Files
ProjectAGiPrompt/99-项目模板/2-概要详细设计/3-详细设计说明书.md
2026-01-21 16:15:49 +08:00

48 KiB
Raw Permalink Blame History

家庭游戏中心 (Family Game Center) - 产品需求文档 (PRD)

文档版本: 1.0
最后更新: 2024-12-29
状态: 已审批,进入开发阶段
目标平台: Linux (Ubuntu 22.04 LTS) / 开发平台: Windows 11


📋 文档目录

  1. 项目概述
  2. 功能需求
  3. 技术方案
  4. 系统架构
  5. 开发规范
  6. API 设计
  7. 数据库设计
  8. UI/UX 设计规范
  9. 安全需求
  10. 测试计划
  11. 部署方案
  12. 验收标准

项目概述

1.1 项目背景

为父母(年龄 50+ 岁)设计的易用家庭游戏平台。该平台旨在提供一个完全锁定的游戏环境,使非技术用户无法误操作进入系统,只能使用游戏功能。

1.2 项目目标

目标 说明
易用性 父母无计算机知识也能独立使用
安全性 系统级锁定,用户无法退出或破坏系统
稳定性 无中断运行 48+ 小时
游戏性 AI 对手难度可调,赢率 40-60%
维护性 单人可维护和更新

1.3 产品范围

包含:

  • 4 款主要游戏 (斗地主、拖拉机、卡五星、赛车)
  • Linux Kiosk 启动器
  • 响应式菜单 UI
  • 本地 AI 对手
  • 游戏存档管理
  • 基础统计功能

不包含:

  • 网络在线多人对战
  • 云同步和备份
  • 用户账户系统
  • 第三方集成
  • 语音/视频功能

1.4 项目成功标准

✓ MVP 版本 (斗地主 + 菜单): 6-8 周
✓ 完整版 (全部游戏): 12-18 周
✓ 总成本: 硬件 5-15K软件 0 元
✓ 团队规模: 1-3 人
✓ 风险等级: 低-中等


功能需求

2.1 用户故事

故事 1: 启动和菜单

作为 父亲
我想 开机后直接进入游戏菜单
这样 我无需学习系统操作,只需选择想玩的游戏

验收标准:

  • 系统启动后 5 秒内进入菜单
  • 菜单显示 4 个游戏卡片
  • 卡片字体 32px+,易于阅读
  • 按钮 80px+,易于点击
  • 菜单背景颜色高对比度 (WCAG AAA)

故事 2: 启动游戏

作为 母亲
我想 点击游戏卡片启动游戏
这样 我可以开始游玩

验收标准:

  • 点击卡片 3 秒内启动游戏
  • 游戏加载时显示进度提示
  • 游戏结束自动返回菜单
  • 支持按 ESC 返回菜单

故事 3: 无法退出

作为 家人
我想 用户无法通过快捷键或菜单退出游戏
这样 游戏不会被误关闭或系统被破坏

验收标准:

  • Alt+F4 无效
  • Alt+Tab 无效
  • Windows 键无效
  • 右键菜单禁用
  • 任务栏隐藏
  • TTY 无法切换 (Ctrl+Alt+Fx)

故事 4: AI 对手

作为 玩家
我想 和智能电脑对手对战
这样 我可以随时游戏,不需要其他玩家

验收标准:

  • 所有游戏都有 2-4 个 AI 对手
  • AI 难度可调 (简单/中等/困难)
  • AI 出牌合法且有策略性
  • AI 响应时间 1-2 秒

故事 5: 保存进度

作为 玩家
我想 每局游戏的成绩和统计被自动保存
这样 我可以看到我的进度和胜率

验收标准:

  • 游戏结束后自动保存成绩
  • 显示历史成绩和统计数据
  • 统计包括: 总局数、胜率、平均用时
  • 数据持久存储在本地数据库

2.2 功能清单

2.2.1 菜单系统 (Priority: P0 - 必须)

功能 说明 优先级
游戏列表展示 网格布局显示 4 个游戏卡片 P0
游戏卡片 包含海报、名称、简描 P0
启动游戏 点击卡片启动对应游戏 P0
时间显示 菜单顶部显示当前时间 P1
返回菜单 游戏结束自动返回,支持 ESC 返回 P0
响应式布局 适配 1080p 到 4K 分辨率, 以2K分辨率 150%缩放作为基准开发 P0
高对比度 WCAG AAA 标准配色 P0
父母友好 大字体、大按钮 P0

2.2.2 斗地主游戏 (Priority: P0 - 必须)

功能 说明 优先级
基础规则 单张、对、顺、炸、王炸判定 P0
手牌显示 玩家手牌按大小排序显示 P0
出牌交互 鼠标/触屏选牌并出牌 P0
地主判定 系统自动判定地主 P0
AI 对手 2 个 AI 农民对手 P0
游戏流程 发牌→抢地主→轮流出牌→结束 P0
声音效果 出牌音效、胜负音效 P1
游戏统计 保存胜负和用时 P1

牌型规则:

  • 单张: 任何一张牌
  • 对牌: 同点数的两张牌
  • 三张: 同点数的三张牌
  • 顺子: 5 张及以上连续的牌 (2 不能在顺子中)
  • 炸弹: 同点数的四张牌 (可以压任何牌)
  • 王炸: 红黑王 (最大)
  • 其他: 简化版,不实现飞机等复杂牌型

2.2.3 拖拉机游戏 (Priority: P1 - 高)

功能 说明 优先级
基础规则 四人跑牌,拖拉机机制 P1
主牌概念 主花色、主点数 P1
AI 对手 3 个 AI 对手 P1
游戏流程 发牌→选主→轮流出牌→计分→结束 P1

2.2.4 卡五星麻将 (Priority: P2 - 可选)

功能 说明 优先级
基础规则 五人麻将变种 P2
AI 对手 4 个 AI 对手 P2
胡牌类型 简化版5-10 种常见胡牌 P2

2.2.5 赛车游戏 (Priority: P1 - 高)

功能 说明 优先级
基础机制 加速、减速、转向、碰撞 P1
AI 对手 1 个 AI 赛手 P1
赛道 简单环形赛道3 圈 P1
计时 完成赛道的用时和排名 P1

2.2.6 系统功能 (Priority: P0 - 必须)

功能 说明 优先级
自动启动 系统启动后自动进入菜单 P0
快捷键禁用 完全禁用系统快捷键 P0
进程监控 游戏异常退出时自动返回菜单 P0
数据持久化 游戏数据本地保存 P0
日志记录 系统和游戏日志记录 P1
故障恢复 自动修复数据损坏和异常状态 P1

2.3 非功能需求

类别 需求 验收标准
性能 启动时间 < 5 秒进入菜单
性能 菜单响应 < 100ms UI 反馈
性能 内存占用** < 500MB (菜单+游戏)
性能 帧率 60fps 稳定
可靠性 运行时长 无中断 48+ 小时
可靠性 故障恢复 自动重启恢复
安全性 系统锁定 三层防护机制
兼容性 分辨率 1080p, 1440p, 2560p, 4K
兼容性 输入 鼠标、触屏、手柄
可维护性 代码** 注释完整,易扩展
可维护性 文档** API、部署、故障排查

技术方案

3.1 技术栈选择

3.1.1 开发平台 (Windows 11)

IDE/编辑器:       VS Code / JetBrains IDE
Golang:           1.20+ (Golang for Windows)
Node.js:          18+ LTS (Windows版)
Godot Engine:     4.1+ (Windows版)
Git:              Git Bash or GitHub Desktop
包管理器:          npm (Node.js), go mod (Golang)

开发优势:

  • 所有开发工具都有完整的 Windows 支持
  • Golang 和 Node.js 可在 Windows 上原生编译
  • Godot 有完整的 Windows 编辑器
  • 跨平台编译: 在 Windows 编译 Linux 二进制

3.1.2 部署平台 (Ubuntu 22.04 LTS)

操作系统:         Ubuntu 22.04 LTS (Server 版本推荐)
系统配置:         Kiosk 模式 + systemd + Openbox
启动器:           Golang 二进制 + zserge/webview
菜单UI:           Vue 3 构建输出
游戏:             Godot 导出的 Linux 64-bit 二进制
运行时:           无需额外依赖 (所有编译为静态二进制)

部署优势:

  • Fedora Silverblue 不可变文件系统,或 Ubuntu Server 最小化
  • Kiosk 配置完全锁定系统
  • 无强制系统更新
  • 极低资源占用

3.2 架构设计

┌─────────────────────────────────────────┐
│         用户交互层 (User Layer)          │
│  鼠标/触屏/键盘                          │
└──────────────────┬──────────────────────┘
                   ↓
┌─────────────────────────────────────────┐
│       应用层 (Application Layer)         │
│                                         │
│  ┌────────────────┐  ┌──────────────┐  │
│  │ Golang Launcher│  │ Process Mgr  │  │
│  │  - Webview     │  │ - Game spawn │  │
│  │  - 全屏模式    │  │ - 监控       │  │
│  └────────┬───────┘  └──────┬───────┘  │
│           │                  │         │
│  ┌────────┴──────────────────┴────┐   │
│  │ HTTP Server (localhost:8888)   │   │
│  └────────────────┬───────────────┘   │
│                   │                   │
│  ┌────────────────┴──────────────┐   │
│  │   Vue 3 前端菜单             │   │
│  │   - 游戏网格                  │   │
│  │   - 响应式布局                │   │
│  └────────────────┬──────────────┘   │
└─────────────────────────────────────────┘
                   ↓
┌─────────────────────────────────────────┐
│         游戏层 (Game Layer)              │
│                                         │
│  ┌──────────┐ ┌──────────┐ ┌────────┐ │
│  │Godot Game│ │Godot Game│ │Pixi.js │ │
│  │ 斗地主   │ │ 拖拉机   │ │ 赛车   │ │
│  └──────────┘ └──────────┘ └────────┘ │
└─────────────────────────────────────────┘
                   ↓
┌─────────────────────────────────────────┐
│      系统层 (System Layer - Linux)       │
│                                         │
│  ┌────────────────────────────────────┐ │
│  │ systemd 系统管理                   │ │
│  │ - 自动登录 kiosk 用户             │ │
│  │ - 启动 game-launcher 服务         │ │
│  │ - 禁用睡眠、更新等                │ │
│  └────────────────────────────────────┘ │
│                                         │
│  ┌────────────────────────────────────┐ │
│  │ X11/Wayland Display Server         │ │
│  │ - Openbox 极简窗口管理            │ │
│  │ - 禁用快捷键 (X11 事件)           │ │
│  │ - 禁用系统菜单                    │ │
│  └────────────────────────────────────┘ │
│                                         │
│  ┌────────────────────────────────────┐ │
│  │ Linux Kernel                       │ │
│  │ - SELinux / AppArmor               │ │
│  │ - kiosk 用户权限管理              │ │
│  └────────────────────────────────────┘ │
└─────────────────────────────────────────┘

3.3 技术决策表

组件 选型 原因
启动器 Golang 内存 20MB, 启动 <100ms, 单二进制
菜单 UI Vue 3 + TypeScript 响应式, 易修改, 开发快
卡牌游戏 Godot 4 + GDScript UI 系统优秀, 易学, 体积小
赛车游戏 Pixi.js (Web 版) 2D 性能最优, 易于参数调整
AI 算法 决策树 + 蒙特卡洛 快速开发, 难度可调, 可解释
数据库 SQLite 轻量级, 无需服务器, 易于备份
配置 JSON 易读易写, 无需解析库

系统架构

4.1 模块划分

project-root/
│
├─ launcher/                          # Golang 启动器 (Module-1)
│  ├─ cmd/main.go                     # 入口点
│  ├─ internal/
│  │  ├─ launcher/launcher.go         # 核心启动器逻辑
│  │  ├─ server/http.go               # HTTP 服务器
│  │  ├─ process/manager.go           # 进程管理
│  │  ├─ config/config.go             # 配置加载
│  │  └─ logger/logger.go             # 日志系统
│  ├─ go.mod
│  ├─ go.sum
│  └─ Makefile                        # 编译脚本
│
├─ frontend/                          # Vue 3 菜单 (Module-2)
│  ├─ src/
│  │  ├─ App.vue                      # 根组件
│  │  ├─ main.ts                      # 入口
│  │  ├─ components/
│  │  │  ├─ GameGrid.vue              # 游戏网格
│  │  │  ├─ GameCard.vue              # 游戏卡片
│  │  │  └─ LoadingSpinner.vue        # 加载提示
│  │  ├─ views/
│  │  │  ├─ MainMenu.vue              # 菜单视图
│  │  │  ├─ StatisticsView.vue        # 统计视图 (可选)
│  │  │  └─ SettingsView.vue          # 设置视图 (可选)
│  │  ├─ services/
│  │  │  └─ api.ts                    # API 通信
│  │  ├─ types/
│  │  │  └─ game.ts                   # 类型定义
│  │  └─ styles/
│  │     └─ main.css                  # 全局样式
│  ├─ public/
│  │  ├─ games.json                   # 游戏配置
│  │  ├─ images/                      # 游戏海报
│  │  └─ fonts/                       # 中文字体
│  ├─ package.json
│  ├─ tsconfig.json
│  ├─ vite.config.ts
│  └─ Makefile
│
├─ games/                             # 游戏项目
│  │
│  ├─ doudizhu/                       # 斗地主 (Module-3)
│  │  ├─ project.godot                # Godot 项目配置
│  │  ├─ scenes/
│  │  │  ├─ Game.tscn                 # 主场景
│  │  │  ├─ Card.tscn                 # 卡牌节点
│  │  │  ├─ Player.tscn               # 玩家区域
│  │  │  └─ UI/
│  │  │     ├─ HandUI.tscn
│  │  │     ├─ TableUI.tscn
│  │  │     └─ InfoPanel.tscn
│  │  ├─ scripts/
│  │  │  ├─ Game.gd                   # 主逻辑 (~800 行)
│  │  │  ├─ CardLogic.gd              # 规则判定 (~300 行)
│  │  │  ├─ AIPlayer.gd               # AI 对手 (~500 行)
│  │  │  └─ UIController.gd           # UI 控制 (~200 行)
│  │  ├─ assets/
│  │  │  ├─ images/
│  │  │  │  ├─ cards/                 # 108 张牌纹理
│  │  │  │  └─ ui/
│  │  │  ├─ sounds/
│  │  │  │  ├─ card_play.wav
│  │  │  │  ├─ win.wav
│  │  │  │  └─ lose.wav
│  │  │  └─ fonts/
│  │  └─ export_presets.cfg           # 导出配置
│  │
│  ├─ tractor/                        # 拖拉机 (Module-4)
│  │  ├─ project.godot
│  │  ├─ scenes/
│  │  ├─ scripts/
│  │  │  ├─ Game.gd
│  │  │  ├─ CardLogic.gd              # 拖拉机规则
│  │  │  └─ AIPlayer.gd
│  │  └─ assets/
│  │
│  ├─ mahjong/                        # 卡五星 (Module-5, 可选)
│  │  ├─ project.godot
│  │  ├─ scenes/
│  │  ├─ scripts/
│  │  │  ├─ Game.gd
│  │  │  ├─ MahjongLogic.gd           # 麻将规则
│  │  │  └─ AIPlayer.gd
│  │  └─ assets/
│  │
│  └─ racing/                         # 赛车 (Module-6, Pixi.js)
│     ├─ src/
│     │  ├─ main.ts
│     │  ├─ Game.ts                   # 游戏主类
│     │  ├─ Car.ts                    # 车辆物理
│     │  ├─ Track.ts                  # 赛道管理
│     │  ├─ AI.ts                     # AI 赛手
│     │  └─ UI.ts                     # UI 管理
│     ├─ assets/
│     │  ├─ images/
│     │  ├─ sounds/
│     │  └─ fonts/
│     ├─ package.json
│     └─ tsconfig.json
│
├─ shared/                            # 共享资源
│  ├─ fonts/
│  │  ├─ SourceHanSansSC-Regular.otf  # 中文字体
│  │  └─ SourceHanSansSC-Bold.otf
│  ├─ images/
│  │  ├─ doudizhu.jpg                 # 游戏海报
│  │  ├─ tractor.jpg
│  │  ├─ mahjong.jpg
│  │  └─ racing.jpg
│  └─ sounds/
│     ├─ ui_click.wav
│     ├─ ui_hover.wav
│     └─ bgm.ogg
│
├─ config/                            # 配置文件
│  ├─ kiosk/
│  │  ├─ gdm-custom.conf              # GDM 自动登录
│  │  ├─ systemd-service.ini          # systemd 服务
│  │  ├─ openbox-rc.xml               # Openbox 配置
│  │  └─ xset-config.sh               # X11 配置脚本
│  └─ app/
│     ├─ launcher.json                # 启动器配置
│     └─ games.json                   # 游戏清单
│
├─ scripts/                           # 构建和部署脚本
│  ├─ build-windows.bat               # Windows 编译脚本
│  ├─ build-linux.sh                  # Linux 编译脚本
│  ├─ build-all.sh                    # 全量编译脚本
│  ├─ cross-compile.sh                # 交叉编译脚本
│  ├─ deploy.sh                       # 部署脚本
│  ├─ setup-kiosk.sh                  # Kiosk 配置脚本
│  └─ verify.sh                       # 验证脚本
│
├─ docs/                              # 文档
│  ├─ PRD.md                          # 本文档
│  ├─ ARCHITECTURE.md                 # 架构详解
│  ├─ DEVELOPMENT.md                  # 开发指南
│  ├─ API.md                          # API 文档
│  ├─ DEPLOYMENT.md                   # 部署指南
│  ├─ TESTING.md                      # 测试指南
│  └─ TROUBLESHOOTING.md              # 故障排查
│
├─ tests/                             # 测试用例
│  ├─ launcher/
│  ├─ frontend/
│  ├─ games/
│  └─ integration/
│
├─ README.md                          # 项目概述
├─ Makefile                           # 顶层编译脚本
├─ docker-compose.yml                 # 可选: Docker 支持
└─ .github/
   └─ workflows/                      # CI/CD 配置

4.2 依赖关系

启动器 (Golang)
  │
  ├─ 依赖: zserge/webview (嵌入式浏览器)
  │        stdlib (net/http, os/exec, etc.)
  │
  └─→ 启动 HTTP 服务器 (localhost:8888)
        │
        ├─ 提供静态文件: 菜单 UI (Vue)
        │
        ├─ 提供 API 端点: /api/launch
        │   │
        │   └─→ 调用 os/exec 启动游戏进程
        │
        └─ 提供 Webview
            │
            └─→ 显示菜单 UI
                │
                └─→ 用户交互
                    │
                    └─→ 调用 API 启动游戏

游戏进程 (Godot / Pixi.js)
  │
  ├─ 通过 IPC/stdio 接收启动参数
  │
  └─ 独立运行,不需与启动器通信
     (游戏结束时自己主动退出)

菜单 UI (Vue 3)
  │
  ├─ 依赖: Vue 3, TypeScript, Vite
  │
  └─ 运行于 Webview 中
     │
     ├─ 加载 games.json
     │
     └─ 通过 fetch API 调用启动器 HTTP 接口

开发规范

5.1 代码规范

5.1.1 Golang 代码规范

// 文件头注释
// Package main 描述此包的用途
package main

import (
	"fmt"
	"os"
	// 标准库分组
	
	"github.com/zserge/webview"
	// 第三方库分组
)

const (
	// 常量大写 + 下划线
	DEFAULT_WEB_PORT = 8888
	LAUNCHER_VERSION = "1.0.0"
)

type GameLauncher struct {
	// 字段注释
	currentGame *exec.Cmd // 当前运行的游戏进程
	webPort     int        // HTTP 服务器端口
	config      Config     // 配置对象
}

// NewGameLauncher 创建启动器实例
// 返回: *GameLauncher - 启动器指针
func NewGameLauncher() *GameLauncher {
	return &GameLauncher{
		webPort: DEFAULT_WEB_PORT,
	}
}

// Start 启动游戏启动器
// 返回: error - 错误信息
func (gl *GameLauncher) Start() error {
	// 函数实现
	return nil
}

规则:

  • 变量名用驼峰命名 (camelCase)
  • 常量用大写 (CONSTANT_NAME)
  • 每个公共函数必须有注释
  • 每个包必须有注释说明其用途
  • 错误处理不能忽略: if err != nil { return err }
  • 使用 fmt.Sprintf 构建字符串,不要用 +

5.1.2 Vue/TypeScript 代码规范

// 文件头注释
/**
 * GameLauncher.vue - 游戏启动器主组件
 * 
 * 功能:
 * - 显示游戏菜单网格
 * - 处理游戏启动逻辑
 * - 显示加载状态
 */

<template>
  <div class="game-launcher" role="main">
    <!-- 语义化 HTML -->
    <header class="header">
      <h1>家庭游戏中心</h1>
    </header>
    
    <!-- v-for 必须使用 :key -->
    <div class="game-grid">
      <button
        v-for="game in games"
        :key="game.id"
        class="game-card"
        @click="launchGame(game.id)"
        :aria-label="`启动${game.name}游戏`"
      >
        {{ game.name }}
      </button>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, computed } from 'vue'

// 类型定义
interface Game {
  id: string
  name: string
  poster: string
  desc: string
}

// 响应式变量声明
const games = ref<Game[]>([])
const isLoading = ref(false)

// 计算属性
const gameCount = computed(() => games.value.length)

// 生命周期
onMounted(async () => {
  await loadGames()
})

// 函数
async function loadGames(): Promise<void> {
  try {
    const response = await fetch('/games.json')
    games.value = await response.json()
  } catch (error) {
    console.error('加载游戏列表失败:', error)
  }
}

async function launchGame(gameId: string): Promise<void> {
  isLoading.value = true
  try {
    const response = await fetch(`/api/launch?id=${gameId}`)
    const data = await response.json()
    if (data.error) {
      console.error('启动失败:', data.error)
    }
  } finally {
    isLoading.value = false
  }
}
</script>

<style scoped lang="css">
/* BEM 命名法 */
.game-launcher {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

.game-launcher__header {
  padding: 40px;
}

.game-card {
  /* 移动优先,逐级增强 */
  width: 100%;
  /* 触屏友好的最小高度 */
  min-height: 80px;
  font-size: 1.5rem;
}

@media (min-width: 768px) {
  .game-card {
    width: 300px;
    font-size: 1.8rem;
  }
}

@media (min-width: 1920px) {
  .game-card {
    width: 400px;
    font-size: 2.2rem;
  }
}
</style>

规则:

  • 使用 const 优于 let,尽量避免 var
  • 使用 TypeScript不要用 JavaScript
  • 组件名使用 PascalCase (GameCard.vue)
  • 变量名使用 camelCase
  • 使用 BEM 命名法管理 CSS 类名
  • 必须包含 ARIA 无障碍属性
  • 类型定义在每个文件头部

5.1.3 GDScript 代码规范

# 文件头注释
## CardLogic.gd - 卡牌逻辑和规则判定
##
## 实现斗地主的卡牌判定算法
## 包括牌型检查、胜负判定等

class_name CardLogic

# 常量定义
const CARD_RANKS = ["9", "10", "J", "Q", "K", "A", "2"]
const CARD_SUITS = ["♠", "♥", "♦", "♣", "王"]

# 静态函数
static func is_valid_play(cards: Array) -> bool:
	"""检查出牌是否合法"""
	if cards.is_empty():
		return false
	
	# 单张
	if cards.size() == 1:
		return true
	
	# 对牌
	if cards.size() == 2:
		return cards[0].rank == cards[1].rank
	
	return false

# 实例函数
func get_best_play(hand: Array, table_cards: Array) -> Array:
	"""根据手牌和桌面牌,给出最佳出牌"""
	# 实现逻辑
	return []

规则:

  • 类名使用 PascalCase (CardLogic)
  • 函数名使用 snake_case (is_valid_play)
  • 常量使用 SCREAMING_SNAKE_CASE
  • 每个公共函数使用 ## 文档注释
  • 避免全局变量,使用类成员变量

5.2 分支管理

main (生产分支)
  │
  ├─ develop (开发分支)
  │  │
  │  ├─ feature/launcher (启动器功能)
  │  ├─ feature/menu-ui (菜单 UI)
  │  ├─ feature/doudizhu (斗地主)
  │  ├─ feature/ai-algorithm (AI 算法)
  │  └─ bugfix/xxx (bug 修复)
  │
  └─ release/v1.0 (发版分支)

规则:

  • 功能开发: feature/功能名
  • bug 修复: bugfix/bug名
  • 发版准备: release/版本号
  • 每个 PR 必须有描述和关联 issue
  • 至少一人 code review 后才能合并

5.3 提交规范

# 格式: <type>(<scope>): <subject>
# <body>
# <footer>

# 示例
git commit -m "feat(launcher): 实现游戏启动功能

- 支持从菜单启动游戏
- 实现进程监控和自动返回
- 添加错误日志记录

Closes #123"

# type 类型:
# feat: 新功能
# fix: bug 修复
# docs: 文档修改
# style: 代码格式 (不影响功能)
# refactor: 代码重构
# test: 添加测试
# chore: 构建、依赖等

5.4 测试规范

测试文件位置: 与源文件同目录,使用 _test 后缀

launcher/
├─ cmd/main.go
├─ cmd/main_test.go              # 对应的测试文件
├─ internal/launcher/launcher.go
└─ internal/launcher/launcher_test.go

规则:

  • 每个模块必须有单元测试
  • 测试覆盖率目标: > 80%
  • 关键业务逻辑必须有集成测试
  • 使用表驱动测试法组织测试用例
// 表驱动测试示例
func TestIsValidPlay(t *testing.T) {
	tests := []struct {
		name    string
		cards   []Card
		want    bool
	}{
		{
			name:  "单张",
			cards: []Card{{rank: 9}},
			want:  true,
		},
		{
			name:  "对牌",
			cards: []Card{{rank: 9}, {rank: 9}},
			want:  true,
		},
	}
	
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := CardLogic.IsValidPlay(tt.cards)
			if got != tt.want {
				t.Errorf("got %v, want %v", got, tt.want)
			}
		})
	}
}

API 设计

6.1 HTTP API 规范

6.1.1 基础信息

基础 URL:    http://localhost:8888
版本:        v1
认证:        无 (本地应用)
错误格式:    JSON

6.1.2 API 端点

1. 启动游戏 (POST /api/launch)
请求:
  GET /api/launch?id=doudizhu&difficulty=normal
  
响应 (200 OK):
{
  "status": "launched",
  "gameId": "doudizhu",
  "timestamp": 1704028800000
}

响应 (400 Bad Request):
{
  "error": "game not found",
  "gameId": "invalid_game"
}

响应 (500 Internal Error):
{
  "error": "failed to launch game",
  "details": "process spawn failed"
}

参数:

  • id (string, required): 游戏 ID (doudizhu, tractor, mahjong, racing)
  • difficulty (string, optional): 难度 (easy, normal, hard),默认 normal

业务逻辑:

  1. 验证游戏 ID 是否存在
  2. 如果有游戏在运行,先关闭它
  3. 使用 os/exec 启动游戏进程
  4. 通过 goroutine 监听游戏退出事件
  5. 游戏退出后自动返回菜单

6.1.3 游戏状态 (GET /api/status)

请求:
  GET /api/status
  
响应 (200 OK):
{
  "status": "ok",
  "currentGame": "doudizhu",
  "uptime": 3600,
  "memory": 256,
  "version": "1.0.0"
}

6.1.4 获取游戏列表 (GET /games.json)

请求:
  GET /games.json
  
响应 (200 OK):
[
  {
    "id": "doudizhu",
    "name": "🎴 斗地主",
    "desc": "经典三人卡牌游戏",
    "poster": "/images/doudizhu.jpg",
    "players": 3,
    "difficulty": "easy"
  },
  ...
]

6.2 IPC 通信 (可选)

游戏进程可通过标准输入接收启动参数:

./doudizhu --difficulty normal --players 3

游戏进程通过标准输出向启动器报告状态:

{"status": "loaded", "game": "doudizhu"}
{"status": "game_over", "winner": "player1", "score": 100}

数据库设计

7.1 SQLite 数据库

文件位置: ~/.game-center/data/game.db

7.1.1 表结构

表 1: games (游戏元数据)

CREATE TABLE games (
  id TEXT PRIMARY KEY,           -- doudizhu, tractor, etc.
  name TEXT NOT NULL,            -- 显示名称
  description TEXT,              -- 游戏描述
  version TEXT,                  -- 游戏版本
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

表 2: game_stats (游戏统计)

CREATE TABLE game_stats (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  game_id TEXT NOT NULL,         -- 外键: games.id
  player_name TEXT DEFAULT '玩家',
  result TEXT NOT NULL,          -- win, loss, draw
  score INTEGER DEFAULT 0,
  duration_seconds INTEGER,      -- 游戏时长
  play_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  
  FOREIGN KEY (game_id) REFERENCES games(id)
);

-- 索引优化查询
CREATE INDEX idx_game_stats_game_id ON game_stats(game_id);
CREATE INDEX idx_game_stats_play_date ON game_stats(play_date);

表 3: ai_profiles (AI 配置档)

CREATE TABLE ai_profiles (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  game_id TEXT NOT NULL,         -- 外键: games.id
  difficulty TEXT NOT NULL,      -- easy, normal, hard
  strategy_version INTEGER DEFAULT 1,
  win_rate REAL DEFAULT 0.5,     -- 当前胜率
  last_updated TIMESTAMP,
  
  FOREIGN KEY (game_id) REFERENCES games(id)
);

7.1.2 初始化脚本

-- init.sql
PRAGMA foreign_keys = ON;

INSERT INTO games (id, name, description, version) VALUES
  ('doudizhu', '🎴 斗地主', '经典三人卡牌游戏', '1.0.0'),
  ('tractor', '🎯 拖拉机', '四人跑牌游戏', '1.0.0'),
  ('mahjong', '🀄 卡五星', '五人麻将变种', '1.0.0'),
  ('racing', '🏎️ 赛车', '简单竞速游戏', '1.0.0');

INSERT INTO ai_profiles (game_id, difficulty) VALUES
  ('doudizhu', 'easy'),
  ('doudizhu', 'normal'),
  ('doudizhu', 'hard');

7.2 配置文件 (JSON)

文件位置: ~/.game-center/config/launcher.json

{
  "version": "1.0.0",
  "launcher": {
    "port": 8888,
    "host": "127.0.0.1",
    "auto_launch": true,
    "fullscreen": true
  },
  "ui": {
    "theme": "dark",
    "language": "zh-CN",
    "font_size": 32,
    "button_size": 80
  },
  "system": {
    "log_level": "INFO",
    "log_file": "/home/kiosk/.game-center/logs/launcher.log",
    "max_log_size_mb": 100,
    "auto_restart": true,
    "restart_delay_seconds": 10
  },
  "games": {
    "doudizhu": {
      "enabled": true,
      "difficulty": "normal",
      "ai_count": 2
    },
    "tractor": {
      "enabled": true,
      "difficulty": "normal",
      "ai_count": 3
    }
  }
}

UI/UX 设计规范

8.1 响应式设计

断点定义:
- 移动设备 (1080p):     最小 (grid: 1 列)
- 平板/小屏 (1440p):    中等 (grid: 2 列)
- 桌面/大屏 (1920p):    标准 (grid: 3-4 列)
- 超大屏 (2560p+):      超大 (grid: 4-6 列)

布局优化:
- 游戏卡片宽度: 300-400px
- 卡片间距: 30px (响应式调整)
- 按钮最小高度: 80px (触屏友好)
- 文字最小大小: 32px (易读)

8.2 颜色方案

主色调:        #667eea (紫色)
辅色调:        #764ba2 (深紫色)
背景色:        #f5f5f5 (浅灰)
文字色:        #333333 (深灰)
强调色:        #FFD700 (金色,焦点)

高对比度检查:
- 文本 vs 背景: 7:1 (WCAG AAA 标准)
- 焦点指示器: 金色边框4px + 外阴影

8.3 字体规范

中文字体:     SourceHanSansSC (思源黑体)
英文字体:     -apple-system, BlinkMacSystemFont, Segoe UI

字体大小阶梯:
- 菜单标题:   3em (48px)
- 游戏名称:   1.8em (32px 基础)
- 描述文本:   1em (14-16px)
- 小提示:     0.875em (12px)

8.4 交互反馈

鼠标悬停:     
- 缩放: scale(1.08)
- 阴影增强: 0 12px 24px
- 过渡: 0.3s ease

点击按钮:
- 按下: scale(0.95)
- 释放: scale(1.0)
- 反馈: 音效 + 视觉效果

加载状态:
- 显示加载动画 (旋转圈/进度条)
- 禁用按钮交互
- 提示文字: "游戏启动中..."

焦点管理:
- Tab 键导航支持
- 焦点圈清晰可见 (金色, 4px)
- 焦点陷阱: 最后一个卡片按 Tab 回到第一个

安全需求

9.1 系统级防护

9.1.1 BIOS/UEFI 级别

配置项:
- 启用 Secure Boot (可选,增强安全)
- 设置管理员密码
- 禁用 USB 启动
- 禁用光驱启动

9.1.2 Bootloader 级别 (GRUB)

配置:
/boot/grub/grub.cfg

# 自动启动,隐藏菜单
set timeout=0

# 禁用编辑功能
CLASS="--class gnu-linux --class os"

9.1.3 Linux 内核级别

# SELinux 强制模式
selinux=1
enforcing=1

# kiosk 用户权限限制
# /etc/sudoers
kiosk ALL=(ALL) NOPASSWD: /bin/false  # 拒绝所有 sudo

9.2 应用级防护

9.2.1 快捷键拦截

# X11 级别禁用快捷键
xset r 133         # 禁用 Windows 键 (keycode 133)
xset r 108         # 禁用 Menu 键 (keycode 108)

# GTK 级别
gsettings set org.gnome.desktop.session idle-delay 0
gsettings set org.gnome.desktop.lockdown disable-lock-screen true
gsettings set org.gnome.desktop.lockdown disable-user-switching true

9.2.2 进程级防护

// Golang 启动器
- 不能 fork  shell (os/exec 设置 Stdout/Stderr)
- 不能修改文件权限 (运行于受限用户)
- 定期检查子进程 (监听 SIGCHLD)
- 自动重启崩溃的进程

9.3 数据安全

9.3.1 存档保护

# 文件权限
~/.game-center/data/     644  # 用户可读写,组不可写
~/.game-center/data/game.db  600  # 仅所有者可读写

# 定期备份
cron: 每天 00:00 自动备份到 ~/.game-center/backup/
备份保留: 7 天轮转

# 损坏恢复
检测到数据库损坏时:
1. 恢复最新的完整备份
2. 删除损坏的数据
3. 清空日志并重启

9.3.2 日志安全

日志位置: ~/.game-center/logs/
权限: 644 (仅所有者可写)
日志级别:
  - ERROR: 错误信息
  - WARN: 警告信息
  - INFO: 启动/停止事件
  - DEBUG: 详细过程 (仅开发环境)

日志内容不包含:
  - 用户隐私信息
  - 系统配置敏感信息
  - 完整错误堆栈 (用户环境)

9.4 网络安全

限制条件:
- 启动器仅监听 localhost (127.0.0.1:8888)
- Webview 无法访问外部 URL
- 游戏进程无网络权限

跨域限制:
- Content-Security-Policy 头严格
- 禁止 <iframe> 嵌入
- 禁止 fetch 外部资源

测试计划

10.1 测试分层

单元测试 (Unit Tests)
  ├─ Golang 启动器单元测试
  ├─ Vue 菜单组件测试
  └─ Godot 游戏逻辑测试

集成测试 (Integration Tests)
  ├─ 启动器 + 菜单 UI 集成
  ├─ 菜单 UI + 游戏启动
  └─ 多个游戏顺序启动

系统测试 (System Tests)
  ├─ 完整流程测试
  ├─ 长时间稳定性测试 (24+ 小时)
  └─ 异常恢复测试

用户验收测试 (UAT)
  ├─ 真实用户操作验证
  ├─ 易用性评估
  └─ 性能满足度评估

10.2 测试用例

10.2.1 菜单系统测试

TC-MENU-001: 菜单启动和显示
  步骤: 1. 系统启动
       2. 等待菜单显示
  预期: 5 秒内显示菜单,显示 4 个游戏卡片
  
TC-MENU-002: 游戏启动
  步骤: 1. 菜单显示
       2. 点击"斗地主"卡片
       3. 等待游戏加载
  预期: 3 秒内启动游戏,显示加载提示
  
TC-MENU-003: 无法通过 Alt+F4 退出
  步骤: 1. 菜单显示
       2. 按下 Alt+F4
  预期: 菜单继续显示,不退出
  
TC-MENU-004: 响应式布局
  步骤: 1. 菜单显示
       2. 改变窗口大小或分辨率
  预期: 布局自适应,卡片大小调整,文字清晰

10.2.2 游戏逻辑测试

TC-GAME-001: 斗地主发牌
  步骤: 1. 启动斗地主
       2. 游戏初始化
  预期: 每个玩家 17 张牌,地主额外 3 张
  
TC-GAME-002: 无法出非法牌
  步骤: 1. 游戏进行中
       2. 尝试出非法牌型
  预期: 系统提示"非法出牌",不执行操作
  
TC-GAME-003: AI 出牌合法
  步骤: 1. AI 轮到时
       2. AI 自动出牌
  预期: AI 出牌合法,不低于 1 秒延迟
  
TC-GAME-004: 游戏结束和返回
  步骤: 1. 游戏进行到结束
       2. 胜者产生
  预期: 显示胜者信息2 秒后自动返回菜单

10.2.3 安全性测试

TC-SEC-001: 快捷键禁用
  步骤: 1. 菜单/游戏显示
       2. 尝试按 Alt+Tab, Alt+F4, Windows 键等
  预期: 所有快捷键无效,界面不变
  
TC-SEC-002: 无法访问系统文件
  步骤: 1. 尝试通过菜单/游戏访问系统文件
  预期: 无法进入文件管理器或系统设置

TC-SEC-003: 24 小时稳定运行
  步骤: 1. 启动系统
       2. 自动化循环启动所有游戏,每 30 分钟换一个
       3. 持续 24 小时
  预期: 无崩溃、无内存泄漏、无响应问题

10.3 性能指标

指标                 目标值           验收标准
─────────────────────────────────────────────
系统启动时间          < 5 秒          进入菜单
菜单响应时间          < 100ms         点击到反馈
游戏启动时间          < 3 秒          游戏显示
菜单内存占用          < 300MB         启动器 + UI
游戏内存占用          < 300MB         单个游戏
总内存占用            < 600MB         启动器 + 菜单 + 游戏
菜单帧率              60fps           稳定渲染
游戏帧率              60fps           卡牌游戏
AI 响应时间           1-2 秒          出牌决策
CPU 占用 (空闲)       < 5%            菜单显示,无交互

部署方案

11.1 开发环境 (Windows 11)

11.1.1 环境配置

# 安装 Golang (Windows)
下载: https://golang.org/dl/
版本: 1.20 LTS
设置 GOPATH 和 GOROOT 环境变量
验证: go version

# 安装 Node.js (Windows)
下载: https://nodejs.org/
版本: 18 LTS
验证: node --version, npm --version

# 安装 Godot (Windows)
下载: https://godotengine.org/
版本: 4.1 LTS
设置可执行文件路径

# 安装 Git
下载: https://git-scm.com/
使用 Git Bash 或 GitHub Desktop

11.1.2 项目初始化 (Windows)

# 克隆项目
git clone https://github.com/xxx/game-center.git
cd game-center

# 安装依赖
cd launcher && go mod download
cd ../frontend && npm install
cd ../games/racing && npm install

# 编译启动器 (针对 Windows)
cd launcher
go build -o launcher-windows.exe

# 编译菜单 UI
cd ../frontend
npm run build
# 输出到 dist/ 目录

# Godot 项目在 VS Code 中打开
# 在 Godot 编辑器中导入 games/doudizhu 项目

11.1.3 Windows 本地测试

# 启动菜单 (需要 Node.js)
cd frontend
npm run dev
# 访问 http://localhost:5173

# 启动 Golang 启动器 (Windows)
cd launcher
go run cmd/main.go
# 启动 localhost:8888 HTTP 服务器

# 测试 API
curl http://localhost:8888/api/status
curl http://localhost:8888/games.json

# 在 Godot 中运行游戏
# 在编辑器中点击"播放"按钮运行

11.2 生产环境 (Ubuntu 22.04 LTS)

11.2.1 系统安装

# 基础系统安装
1. 下载 Ubuntu 22.04 Server ISO
2. 最小化安装 (仅命令行)
3. 设置主机名: game-center
4. 设置网络: 有线网络
5. 创建用户: root (设置强密码)
6. 安装完成后重启

# 初始更新
sudo apt-get update
sudo apt-get upgrade -y
sudo apt-get install -y \
  git curl wget \
  gcc make pkg-config \
  libxcb-1-0 libxcb-devel \
  xorg-x11-server xorg-x11-utils \
  openbox \
  pulseaudio alsa-utils

11.2.2 Kiosk 配置

# 创建 kiosk 用户
sudo useradd -m -s /bin/bash kiosk
sudo passwd kiosk  # 设置简单密码 (如: 1234)

# 编辑 /etc/gdm/custom.conf
[daemon]
AutomaticLoginEnable=true
AutomaticLogin=kiosk

# 禁用不必要服务
sudo systemctl disable cups
sudo systemctl disable ssh
sudo systemctl disable bluetooth

# 禁用睡眠/休眠
sudo systemctl mask sleep.target suspend.target hibernate.target

# 隐藏 GRUB 菜单
sudo vi /etc/default/grub
# 修改: GRUB_TIMEOUT=0
# 修改: GRUB_TIMEOUT_STYLE=hidden
sudo update-grub

11.2.3 部署应用

# 创建应用目录
sudo mkdir -p /opt/game-center
sudo mkdir -p /opt/games

# 复制已编译的启动器 (从 Windows 交叉编译得到)
sudo cp launcher-linux /opt/game-center/launcher
sudo chmod +x /opt/game-center/launcher

# 复制菜单 UI
sudo cp -r frontend/dist/* /opt/game-center/html/
sudo chmod -R 755 /opt/game-center/html

# 复制游戏二进制 (从 Godot 导出)
sudo cp games/doudizhu/doudizhu.x86_64 /opt/games/
sudo cp games/tractor/tractor.x86_64 /opt/games/
sudo cp games/racing/racing.x86_64 /opt/games/
sudo chmod +x /opt/games/*.x86_64

# 创建 systemd 服务
sudo cat > /etc/systemd/system/game-launcher.service << 'EOF'
[Unit]
Description=Game Launcher Service
After=network.target

[Service]
Type=simple
User=kiosk
WorkingDirectory=/opt/game-center
ExecStart=/opt/game-center/launcher
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
EOF

# 启用和启动服务
sudo systemctl enable game-launcher.service
sudo systemctl start game-launcher.service
sudo systemctl status game-launcher.service

11.2.4 验证部署

# 检查服务状态
sudo systemctl status game-launcher.service

# 查看日志
sudo journalctl -u game-launcher.service -f

# 测试 HTTP 接口
curl http://localhost:8888/
curl http://localhost:8888/api/status
curl http://localhost:8888/games.json

# 检查进程
ps aux | grep launcher
ps aux | grep doudizhu

# 检查内存使用
free -h
top

# 测试快捷键禁用
# 在显示器上尝试按 Alt+F4, Alt+Tab 等
# 验证无法退出或切换窗口

11.3 交叉编译指南

11.3.1 在 Windows 编译 Linux 二进制

# PowerShell (Windows)

# 设置交叉编译环境变量
$env:GOOS = "linux"
$env:GOARCH = "amd64"
$env:CGO_ENABLED = "0"

# 编译 Golang 启动器
cd launcher
go build -o launcher-linux -ldflags="-w -s"
# 输出: launcher-linux (无扩展名)

# 验证生成的二进制
file launcher-linux  # 应显示 ELF 64-bit

# 复制到部署目录
Copy-Item launcher-linux ../deploy/

11.3.2 Godot 导出配置

# project.godot 中的导出设置

[preset.0]
name="Linux/X11"
platform="Linux/X11"
runnable=true
custom_features=""
export_filter="all_resources"
export_files=[]
script_export_mode=1
script_encryption_key=""

[preset.0.options]
binary_format/64_bits=true
binary_format/embed_pck=true
texture_format/vram_compression="ETC2"

导出步骤:

  1. Godot 编辑器 → 项目 → 导出项目
  2. 选择 Linux/X11 预设
  3. 点击"导出项目"
  4. 输出文件: doudizhu.x86_64

验收标准

12.1 功能验收

功能 验收标准 优先级
菜单显示 5秒内显示4个卡片清晰可见点击可启动 P0
游戏启动 3秒内启动游戏显示加载提示 P0
快捷键禁用 Alt+F4, Alt+Tab, Win 键等全部无效 P0
AI 对手 合法出牌,响应时间 1-2 秒 P0
游戏结束 自动返回菜单,保存成绩 P0
数据持久化 成绩保存到数据库,重启后可查询 P1
故障恢复 游戏崩溃自动返回菜单 P1
响应式布局 适配 1080p 到 4K自动调整 P0

12.2 性能验收

指标 目标 验收标准
启动时间 < 5s 进入菜单显示
菜单响应 < 100ms 点击到视觉反馈
游戏启动 < 3s 游戏窗口显示
内存占用 < 600MB 启动器+菜单+游戏总计
帧率稳定 60fps 无卡顿掉帧
24小时稳定 无崩溃 连续运行无异常

12.3 安全验收

项目 验收标准
快捷键 Alt+F4, Alt+Tab, Win, Ctrl+Alt+Fx 全部禁用
菜单 右键菜单禁用,任务栏隐藏
文件访问 无法进入文件管理器或系统设置
进程管理 用户无法修改或杀死游戏进程
数据安全 游戏数据加密存储,自动备份

12.4 用户验收

项目 验收标准
易用性 无计算机知识的用户能独立操作
可读性 字体大小 32px+,易于老年人阅读
按钮大小 80px+ 易于点击,触屏友好
游戏规则 规则简洁明确,易于理解
反馈及时 点击、加载、结果等有明确反馈

12.5 交付清单

□ 源代码 (GitHub 仓库)
  ├─ launcher/ (Golang)
  ├─ frontend/ (Vue 3)
  ├─ games/ (Godot + Pixi.js)
  ├─ config/ (系统配置)
  ├─ scripts/ (部署脚本)
  └─ docs/ (文档)

□ 编译产物
  ├─ launcher-linux (Golang 二进制)
  ├─ launcher-windows.exe (Windows 二进制,用于本地测试)
  ├─ frontend/dist/ (Vue 构建输出)
  ├─ games/doudizhu/doudizhu.x86_64 (Godot 导出)
  ├─ games/tractor/tractor.x86_64
  ├─ games/racing/racing.x86_64
  └─ games/mahjong/mahjong.x86_64 (可选)

□ 文档
  ├─ PRD.md (本文档)
  ├─ ARCHITECTURE.md (架构详解)
  ├─ DEVELOPMENT.md (开发指南)
  ├─ DEPLOYMENT.md (部署指南)
  ├─ API.md (API 文档)
  ├─ TESTING.md (测试指南)
  └─ TROUBLESHOOTING.md (故障排查)

□ 系统镜像 (可选)
  ├─ game-center-base.iso (基础 Ubuntu 镜像)
  ├─ game-center-complete.img (完整部署镜像)
  └─ backup.tar.gz (系统备份)

□ 支持材料
  ├─ 用户手册 (PDF)
  ├─ 快速开始指南
  ├─ 故障排查表
  └─ 维护清单

附录 A: 术语表

术语 说明
Kiosk 模式 系统锁定模式,用户只能运行指定应用
启动器 Golang 应用,负责管理菜单和游戏进程
Webview 嵌入式浏览器,显示 Vue 菜单
AI 对手 游戏中的电脑玩家
卡牌游戏 基于卡牌规则的游戏 (斗地主、拖拉机等)
赛车游戏 基于物理引擎的竞速游戏
决策树 AI 算法,通过规则树进行决策
蒙特卡洛树搜索 AI 算法,通过模拟进行决策优化
交叉编译 在一个平台编译另一个平台的可执行文件
systemd Linux 系统服务管理器
Openbox 极简窗口管理器

附录 B: 参考资源

官方文档

开发工具

第三方库