This commit is contained in:
2026-04-10 15:37:02 +08:00
parent 161cd32277
commit f19e482c8f
233 changed files with 43987 additions and 5127 deletions

View File

@@ -0,0 +1,963 @@
# 账号系统与登录入口重构 PRD
更新时间:`2026-04-09`
## 0. 目标
这份 PRD 面向当前仓库,要解决的是一个已经非常明确的产品问题:
**游戏开始界面目前还是“开始游戏”导向,但项目已经进入后端账号模式,真正缺的是一套正式可运营的账号登录入口和账号体系。**
这次要完成的不是单纯把按钮文案改掉,而是把当前入口、鉴权、存档归属和跨设备使用体验一起重构成完整闭环:
1. 游戏开始界面从“开始游戏”切换为“账号登录”
2. 支持 `手机号验证码登录`
3. 支持 `微信登录`
4. 微信登录后必须先绑定手机号,绑定完成后才能进入游戏
5. 账号与存档、自定义世界、运行时设置统一绑定到后端用户体系
6. 整体体验保持移动端优先、界面清爽、规则不堆满前台
一句话目标:
**让“登录账号”成为进入游戏的第一步,让手机号和微信成为正式身份入口,并把账号与游戏数据归属真正接到 Express 后端。**
---
## 1. 当前现状
## 1.1 当前仓库已经有基础鉴权,但不是正式账号系统
从当前代码看:
- `src/components/auth/AuthGate.tsx`
- 当前会在无 token 时自动创建或恢复匿名账号
- `src/services/authService.ts`
- 当前支持“用户名 + 密码”直进,且带自动生成游客凭据逻辑
- `server-node/src/routes/authRoutes.ts`
- 当前只有 `/api/auth/entry``/api/auth/me``/api/auth/logout`
- `server-node/src/auth/authService.ts`
- 当前后端仍是“用户名不存在就自动创建,存在就校验密码”的轻量方案
- `src/components/game-shell/PreGameSelectionFlow.tsx`
- 开始页前台主语仍然是“开始游戏 / 新游戏 / 继续游戏”
这说明当前系统已经有“用户 ID + JWT + 用户隔离存档”,但还没有正式运营可用的:
- 手机号体系
- 微信身份体系
- 绑定关系体系
- 验证码体系
- 会话过期与刷新体系
- 账号归并与换绑规则
## 1.2 当前方案存在的直接问题
### 1.2.1 入口主语错位
当前开始页告诉玩家的是:
- 开始游戏
但真实系统需要的是:
- 先确认你是谁
- 再把你的存档和世界数据挂到你的账号下
也就是说,前台入口和后端真实数据归属已经不一致。
### 1.2.2 匿名自动账号不适合正式用户体系
当前自动生成随机账号的方案适合开发期快速联调,但不适合正式版本,因为它会带来:
- 用户无法理解自己的账号是什么
- 跨设备登录和找回几乎不可用
- 微信登录无法落地
- 手机号绑定没有锚点
- 用户会误以为“换设备 = 进度丢失”
### 1.2.3 微信登录缺少落点
如果只做“微信授权成功即进入游戏”,会出现两个问题:
1. 账号恢复能力弱
- 一旦微信环境变化,恢复和找回仍然不稳。
2. 数据归属不稳定
- 仅靠微信身份,不利于后续账号运营、客服排查、风控与跨端同步。
因此:
**微信登录不是最终归属,手机号绑定才是正式账号落点。**
### 1.2.4 当前永久 JWT 不适合正式登录系统
现状文档已经说明当前 JWT 是永久签发。
这对开发期方便,但对正式账号系统有明显风险:
- token 泄露后难以及时失效
- 多设备会话管理能力不足
- 换绑手机号和安全操作缺少会话边界
---
## 2. 设计原则
## 2.1 账号先于游戏
正式版本中,玩家进入游戏主流程前,应该先完成账号确认。
前台语义改为:
- 未登录:账号登录
- 已登录:继续冒险 / 选择世界 / 开始新档
而不是一上来先点“开始游戏”。
## 2.2 移动端优先,前台保持清爽
开始界面和登录面板要继续遵循当前项目已经沉淀出的 UI 经验:
- 手机竖屏优先
- 入口动作少而清晰
- 不堆大段规则说明
- 主按钮只保留关键动作
所以登录前台默认只呈现:
- 手机号登录
- 微信登录
- 开发团队(次级)
## 2.3 前端只负责表现,账号逻辑全部进 Express 后端
必须严格遵守当前项目约束:
- 前端只渲染登录状态、错误态、输入态、成功态
- 验证码发送、验证码校验、微信 OAuth、绑定决策、账号归并、token 签发全部由 Express 后端处理
前端不能自行决定:
- 账号是否新建
- 是否允许绑定
- 是否归并到已有账号
- 是否允许进入游戏
## 2.4 微信登录不是免绑定通行证
微信登录后,如果账号没有绑定手机号,则:
- 允许展示绑定手机号页
- 不允许进入游戏主流程
- 不允许创建正式存档
- 不允许进入世界选择和冒险流程
一句话约束:
**微信授权成功 != 可以开始游戏,绑定手机号成功后才算正式激活账号。**
## 2.5 一个手机号对应一个正式主账号
MVP 阶段建议采用最稳妥规则:
- 一个手机号只能绑定一个正式账号
- 一个微信身份只能绑定一个正式账号
- 一个正式账号必须有已验证手机号
- 微信是可选登录方式,手机号是正式归属方式
---
## 3. 产品范围
## 3.1 本期要做
1. 开始界面改成账号登录入口
2. 手机号验证码登录
3. 微信登录
4. 微信后强制绑定手机号
5. 账号会话管理
6. 账号与存档/自定义世界/运行时设置统一绑定
7. 基础账号中心与退出登录
## 3.2 本期不做
1. 用户名密码注册
2. 游客正式入口
3. 账号密码找回
4. 实名认证
5. 社交好友体系
6. 多微信绑定同一账号
说明:
当前用户名密码模式可仅保留为开发环境兜底能力,不作为正式前台入口。
---
## 4. 用户状态设计
建议把账号状态统一为下面 5 类:
## 4.1 未登录
特征:
- 本地无有效会话
- 前台只显示登录入口
允许动作:
- 手机号登录
- 微信登录
- 查看开发团队
不允许动作:
- 开始游戏
- 选择世界
- 进入已有存档
## 4.2 手机号已登录
特征:
- 已完成手机号验证码校验
- 已有正式账号
允许动作:
- 继续冒险
- 开始新档
- 选择世界
- 查看账号信息
- 退出登录
## 4.3 微信已授权但未绑定手机号
特征:
- 已拿到微信身份
- 后端已创建待激活账号壳
- 账号状态为 `pending_bind_phone`
允许动作:
- 绑定手机号
- 切换其他登录方式
- 退出当前授权态
不允许动作:
- 进入游戏
- 创建正式存档
- 使用正式运行时能力
## 4.4 微信已授权且已绑定手机号
特征:
- 微信身份已归属到正式账号
- 手机号已验证
允许动作:
- 与手机号登录用户相同
## 4.5 会话过期或失效
特征:
- access token 过期
- refresh session 无效
- 或账号状态被强制失效
表现:
- 前台回到登录入口
- 如果是绑定中状态失效,则回到重新登录
---
## 5. 开始界面重构方案
## 5.1 未登录状态下的开始界面
当前开始界面的主按钮“开始游戏”应被替换为“账号登录”语义。
建议界面结构:
### 顶部
- 游戏名
- 一句极短副标题
副标题只保留类似:
- 登录后同步你的冒险进度
不要再出现规则说明式长文案。
### 中部主按钮
只保留两个一级主按钮:
1. `手机号登录`
2. `微信登录`
按钮样式要求:
- 手机端纵向堆叠
- 桌面端仍以单列为主,不做复杂双栏表单
- 点击目标区域足够大
- 视觉主语明确是“登录方式”,不是“功能菜单”
### 底部次级入口
- `开发团队`
联系方式面板不建议再作为开始页主信息块长期占据核心位置,避免稀释登录主动作。
## 5.2 微信未绑定状态下的开始界面
微信授权成功但未绑定手机号时,开始界面应切成“待激活账号”形态。
只展示:
- 微信头像/昵称(可选)
- 当前提示:请绑定手机号后进入游戏
- 绑定手机号按钮
- 返回其他登录方式按钮
这个状态下不要显示:
- 继续游戏
- 新游戏
- 世界选择
## 5.3 已登录状态下的开始界面
当账号已正式激活后,开始界面才恢复游戏入口动作,但按钮文案要重新组织。
建议一级动作:
- `继续冒险`(存在存档时)
- `开始新档`
- `选择世界`
建议次级动作:
- `账号设置`
- `退出登录`
- `开发团队`
也就是说:
**“开始游戏”不再是未登录态按钮,而是登录后的游戏内动作集合。**
---
## 6. 核心功能设计
## 6.1 手机号验证码登录
### 交互
用户输入手机号后:
1. 点击发送验证码
2. 收到 6 位短信验证码
3. 输入验证码完成登录
### 规则
- 验证码有效期:`5` 分钟
- 重新发送冷却:`60`
- 单手机号日发送上限:需配置
- 单 IP / 单设备分钟级频控:需配置
- 验证成功后自动登录
### 账号策略
- 手机号已存在:登录已有账号
- 手机号不存在:创建正式账号并登录
MVP 阶段不需要单独设置密码。
## 6.2 微信登录
微信登录按终端拆分:
### 微信内 H5
- 走微信 OAuth 授权
- 后端换取 `code`
- 优先获取 `unionid`
### 桌面网页
- 走微信扫码登录
- 用户扫码确认后由后端完成登录回调
### 普通手机浏览器
如果不在微信环境内,不强行做复杂跳转,建议:
- 优先提示使用手机号登录
- 或提示“请在微信内打开以使用微信登录”
这样比做一套体验不稳定的浏览器跳转更稳。
### 微信账号策略
- 若微信身份已绑定正式账号:直接登录
- 若微信身份首次出现:创建 `pending_bind_phone` 账号壳,并进入绑定手机号流程
## 6.3 微信后绑定手机号
这是本期最关键规则。
### 绑定目标
把微信身份最终归属到一个已验证手机号的正式账号上。
### 绑定流程
1. 用户完成微信授权
2. 前台进入“绑定手机号”页
3. 输入手机号
4. 获取短信验证码
5. 后端校验验证码并处理账号归属
### 绑定结果分两类
#### 情况 A手机号未被使用
结果:
- 将该手机号绑定到当前微信待激活账号
- 账号转为正式激活
- 登录成功,进入游戏开始界面已登录态
#### 情况 B手机号已绑定已有正式账号
建议采用最稳的归并策略:
1. 短信验证码校验通过
2. 后端确认该手机号已有正式账号
3. 将当前微信身份直接绑定到该正式账号
4. 废弃当前待激活账号壳
5. 用户登录到已有手机号账号
这样做的好处是:
- 规则简单
- 不需要做复杂数据合并
- 因为未绑定手机号前禁止进入游戏,所以待激活账号壳上不会挂正式存档数据
## 6.4 账号中心
MVP 阶段建议至少提供一个轻量账号中心,包含:
- 当前登录方式
- 已绑定手机号(脱敏展示)
- 微信绑定状态
- 最近登录时间
- 退出登录
二期可以再补:
- 更换手机号
- 退出全部设备
- 查看登录设备
## 6.5 存档与数据归属
当前项目已有:
- 存档快照
- 自定义世界库
- 运行时设置
这些数据已经在后端按用户隔离。
本期要求是把它们统一稳定归属到正式账号,而不是匿名随机用户。
也就是说:
- 手机号登录后,数据归属到该账号
- 微信登录并绑定手机号后,数据归属到绑定后的正式账号
- 同一账号跨设备登录,应读取同一份用户数据
---
## 7. 后端账号模型设计
## 7.1 设计思路
建议保留当前 `users` 主表,但把“登录方式”和“账号归属”拆到独立身份表中。
正式模型应区分:
- 用户主实体
- 登录身份
- 验证码记录
- 会话记录
## 7.2 建议数据表
### `users`
建议保留并扩展:
- `id`
- `display_name`
- `account_status`
- `primary_phone`
- `phone_verified_at`
- `token_version`
- `created_at`
- `updated_at`
- `last_login_at`
其中:
- `account_status` 需至少支持:
- `active`
- `pending_bind_phone`
- `disabled`
### `auth_identities`
用于记录登录身份。
建议字段:
- `id`
- `user_id`
- `provider`
- `provider_uid`
- `provider_unionid`
- `phone_e164`
- `is_verified`
- `is_primary`
- `bound_at`
- `last_login_at`
- `meta_json`
其中 `provider` 至少支持:
- `phone`
- `wechat`
说明:
- 手机号登录身份存 `phone_e164`
- 微信登录身份优先存 `unionid`
- 若极端场景拿不到 `unionid`,需明确风险并做降级策略,但正式上线应优先确保 `unionid`
### `phone_verification_codes`
用于短信验证码管理。
建议字段:
- `id`
- `phone_e164`
- `scene`
- `code_hash`
- `expires_at`
- `consumed_at`
- `send_ip`
- `device_id`
- `created_at`
`scene` 建议至少区分:
- `login`
- `bind_phone`
- `change_phone`
### `user_sessions`
用于会话刷新和设备管理。
建议字段:
- `id`
- `user_id`
- `refresh_token_hash`
- `client_type`
- `user_agent`
- `ip`
- `expires_at`
- `revoked_at`
- `created_at`
- `last_seen_at`
## 7.3 与当前仓库的关系
当前仓库已有:
- `users`
- JWT 鉴权
- `token_version`
因此本期不是推翻重做,而是:
1. 保留 `users` 作为账号主表
2. 废弃“用户名密码自动注册”作为正式入口
3. 增加手机号与微信身份层
4. 增加验证码表与会话表
---
## 8. 接口设计
所有接口均由 Express 后端承接。
## 8.1 手机号登录相关
### `POST /api/auth/phone/send-code`
用途:
- 发送登录验证码
入参:
- `phone`
- `scene`
出参:
- `ok`
- `cooldownSeconds`
### `POST /api/auth/phone/login`
用途:
- 使用手机号 + 验证码登录或注册
入参:
- `phone`
- `code`
出参:
- `accessToken`
- `accessTokenExpiresAt`
- `user`
- `bindingStatus`
## 8.2 微信登录相关
### `GET /api/auth/wechat/start`
用途:
- 获取微信授权跳转地址或扫码会话
### `GET /api/auth/wechat/callback`
用途:
- 微信授权完成后的后端回调
回调结果分两类:
1. 已绑定正式账号
- 直接签发会话
2. 未绑定手机号
- 签发临时登录态,并标记 `pending_bind_phone`
### `POST /api/auth/wechat/bind-phone`
用途:
- 微信授权后绑定手机号
入参:
- `phone`
- `code`
出参:
- `accessToken`
- `accessTokenExpiresAt`
- `user`
- `bindingStatus: active`
## 8.3 会话与账号信息
### `GET /api/auth/me`
返回建议扩展为:
- `user`
- `bindingStatus`
- `boundPhoneMasked`
- `wechatBound`
- `availableLoginMethods`
### `POST /api/auth/logout`
用途:
- 当前设备退出登录
### `POST /api/auth/refresh`
用途:
- 刷新 access token
说明:
当前仓库已有 Bearer token 调用链MVP 阶段可继续保留 Bearer access token 的前端接法,但不应再使用永久 token。
---
## 9. 会话与安全设计
## 9.1 token 策略
建议从“永久 JWT”切为
- `access token`:短期有效
- `refresh session`:中期有效
建议时长:
- access token`2` 小时
- refresh session`30`
## 9.2 失效策略
- 退出登录:失效当前 refresh session并提升 `token_version`
- 账号异常:可强制失效全部 session
- 换绑手机号:旧高风险会话应重新校验
## 9.3 验证码风控
至少要有:
- 发送频控
- 校验次数限制
- 图形验证码扩展点
- 日志审计
## 9.4 微信安全要求
- 校验 `state`
- 回调只能由后端处理
- 不在前端直接换 token
- 优先使用 `unionid` 作为跨应用稳定身份标识
---
## 10. 详细用户流程
## 10.1 手机号登录流程
1. 用户打开游戏开始界面
2. 点击 `手机号登录`
3. 输入手机号
4. 点击 `获取验证码`
5. 输入验证码
6. 后端校验验证码
7. 若账号存在则登录,若不存在则创建正式账号
8. 返回已登录开始界面
9. 用户再选择 `继续冒险 / 开始新档 / 选择世界`
## 10.2 微信登录流程
1. 用户打开游戏开始界面
2. 点击 `微信登录`
3. 进入微信授权或扫码授权
4. 后端拿到微信身份
5. 若该微信已绑定正式账号,则直接登录
6. 若该微信未绑定正式账号,则进入 `绑定手机号`
7. 用户完成手机号验证码校验
8. 后端完成绑定或归并
9. 返回已登录开始界面
## 10.3 微信绑定已有手机号账号流程
1. 用户微信首次登录
2. 系统要求绑定手机号
3. 用户输入一个已注册手机号
4. 用户完成短信验证码校验
5. 后端识别该手机号已有正式账号
6. 系统将当前微信身份绑定到该正式账号
7. 当前待激活账号壳废弃
8. 用户直接登录到已有正式账号
---
## 11. 前端实现要求
## 11.1 前端只维护这些状态
前端主要只需要表现这些状态:
- `idle`
- `sending_code`
- `code_sent`
- `wechat_authorizing`
- `pending_bind_phone`
- `authenticated`
- `session_expired`
- `error`
## 11.2 前端不做业务裁决
前端不能自行决定:
- 当前手机号是新账号还是老账号
- 微信是否允许直进
- 当前绑定是“绑定”还是“归并”
- 当前账号是否已经正式激活
前端只根据后端返回的:
- `bindingStatus`
- `availableLoginMethods`
- `user`
- `errorCode`
来切换页面表现。
## 11.3 与当前代码结构的建议映射
建议优先改造这些区域:
- `src/components/auth/AuthGate.tsx`
- 去掉正式环境的匿名自动登录逻辑
- `src/components/auth/LoginScreen.tsx`
- 重做为手机号 / 微信双入口
- `src/components/game-shell/PreGameSelectionFlow.tsx`
- 将未登录态入口从“开始游戏”调整为“账号登录后进入”
- `src/services/authService.ts`
- 改为手机号、微信、绑定手机号、刷新会话等服务层
- `src/services/apiClient.ts`
- 增加 refresh / 过期处理
- `packages/shared/src/contracts/auth.ts`
- 扩展新的鉴权 contract
后端建议优先改造:
- `server-node/src/routes/authRoutes.ts`
- `server-node/src/auth/authService.ts`
- `server-node/src/repositories/userRepository.ts`
- `server-node/src/middleware/auth.ts`
---
## 12. 分阶段落地建议
## 阶段 A登录入口重构 + 手机号登录 MVP
目标:
- 开始界面从“开始游戏”切为“账号登录”
- 手机号验证码登录可用
- 正式环境关闭匿名自动账号
建议同时保留一个仅开发环境可开的兜底开关:
- `AUTH_ALLOW_DEV_GUEST=true`
但默认不开,不进入正式前台。
## 阶段 B微信登录 + 强制绑定手机号
目标:
- 微信授权可用
- 微信首次登录后强制绑定手机号
- 已有手机号账号与微信身份可自动归并
## 阶段 C会话完善 + 账号中心
目标:
- access/refresh 机制稳定
- 账号中心可查看绑定状态
- 支持更稳的退出登录与过期恢复
## 阶段 D安全与运营补强
目标:
- 更细的风控策略
- 登录审计
- 换绑手机号
- 退出全部设备
---
## 13. 验收标准
做到以下几点,才说明这套账号系统真正成立。
## 13.1 入口验收
1. 未登录时,开始界面不再出现“开始游戏”作为主按钮。
2. 未登录时,前台主动作只剩手机号登录和微信登录。
3. 已登录后,才出现继续冒险、开始新档、选择世界等游戏动作。
## 13.2 功能验收
1. 手机号验证码登录可稳定完成注册/登录。
2. 微信首次登录后不能直接进入游戏,必须绑定手机号。
3. 微信绑定一个已存在手机号时,能够归并到已有正式账号。
4. 同一正式账号在不同设备登录后能看到同一份用户数据。
## 13.3 安全验收
1. 不再使用永久有效 access token。
2. 验证码具备有效期、冷却和频控。
3. 退出登录后旧会话不能继续访问受保护接口。
## 13.4 体验验收
1. 手机竖屏下登录入口一屏内就能看懂并操作。
2. 登录页文案简洁,不堆规则说明。
3. 微信未绑定状态下,用户能清楚知道下一步就是绑定手机号。
---
## 14. 为什么这套方案适合当前仓库
这套 PRD 不是凭空换一套系统,而是顺着当前仓库现状做升级:
1. 当前后端已经具备 `users + JWT + 用户隔离存档`
- 所以正式账号体系不是从零开始。
2. 当前前端已经有 `AuthGate + authService`
- 所以登录入口和登录态切换已有承载点。
3. 当前项目是移动端优先
- 所以“两个主按钮 + 一个绑定流程”的轻量方案比厚重注册页更适合。
4. 当前项目明确要求前端只做表现、逻辑下沉 Express
- 所以这套方案把所有裁决都压到后端,和现有工程原则一致。
---
## 15. 最后结论
当前项目真正需要的,不是把“开始游戏”按钮换成另一个文案,而是把进入游戏的第一步从“点开始”升级成“确认正式账号身份”。
最合理的产品路径是:
1. 用手机号验证码建立正式账号主入口
2. 用微信登录补充更低摩擦的登录方式
3. 用“微信后必须绑定手机号”保证账号归属稳定
4. 用后端统一承接身份、绑定、会话、存档归属
这样改完之后,玩家进入游戏时感知到的将不再是:
- “先点开始再说”
而会更接近:
- “先登录我的正式账号,再继续我的冒险进度。”

File diff suppressed because it is too large Load Diff