Files
Genarrative/docs/prd/ACCOUNT_SYSTEM_AND_LOGIN_ENTRY_PRD_2026-04-09.md

23 KiB
Raw Blame History

账号系统与登录入口重构 PRD

更新时间:2026-04-19

2026-04-19 入口策略补充: 平台首页现调整为“未登录也可浏览公开首页”,不再要求用户先登录才能进入平台。 登录拦截点改为“点击作品进入详情/世界”与“选择游戏类型开始创作”等受保护动作触发时再弹出登录弹窗。 本次入口策略与弹窗约束以 docs/design/PLATFORM_HOME_PUBLIC_BROWSE_AND_LOGIN_MODAL_GATING_DESIGN_2026-04-19.md 为准;本 PRD 中“先登录再进入开始界面”的旧表述不再作为当前前台入口实现依据。

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.1.1 密码登录补充方式

密码登录只补充手机号验证码登录,不建立新的账号体系。

落地规则:

  • 入参只允许 phonepassword,不支持邮箱、用户名或叙世号。
  • 手机号不存在时,不创建账号,返回统一的登录失败。
  • 手机号存在但账号未设置过密码时,不允许密码登录。
  • 首次设置密码只能在已登录账号中心内完成;用户必须先通过手机号验证码或已绑定手机号的微信账号进入已登录态。
  • 忘记密码 / 重置密码必须先完成该手机号的短信验证码校验;手机号不存在时不创建账号。

前台约束:

  • 密码页签的账号输入框文案固定为 手机号
  • 密码页签主按钮固定为 登录,不能出现 注册/登录
  • 短信验证码页签可继续承担“手机号不存在时创建正式账号并登录”的能力,但按钮文案不应暗示密码注册。

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. 接口设计

所有接口均由 server-rs 后端承接。

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 会话与账号信息

POST /api/auth/entry

用途:

  • 使用已设置密码的手机号账号登录

入参:

  • phone
  • password

出参:

  • token
  • user

约束:

  • 不支持邮箱、用户名或叙世号。
  • 不承担注册能力。
  • 只有已存在、已验证手机号、且 passwordLoginEnabled=true 的账号可以登录。

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 token2 小时
  • refresh session30

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. 用后端统一承接身份、绑定、会话、存档归属

这样改完之后,玩家进入游戏时感知到的将不再是:

  • “先点开始再说”

而会更接近:

  • “先登录我的正式账号,再继续我的冒险进度。”