登录流程文档与实际代码对比
> 注意:本文件是「开发记录 / 博客」用途,不是代码执行规则 AGENTS.md。
注意:本文件是「开发记录 / 博客」用途,不是代码执行规则
AGENTS.md。
博客记录原则:简洁、清晰、直接。
1. 问题背景
本轮在 CloudBase 登录 / 静默恢复 / 手机号与密码 等路径上做了实现收敛与行为修正,并 重写 docs/流程文档/登录流程.md 与代码一致。此前存在三类痛点:实现与文档/旧笔记各说各话、SDK 返回字段与分支判断不一致导致「看似失败」、补设密与多路同步竞态导致误踢或重复请求。本文按问题记录根因链、方案与采纳结果,便于以后排障与改代码时对齐事实。
2. 问题列表
2.1 身份字段与「是否已登录」判断混乱
根因(链式,最多 3 轮)
why1:为什么业务里对「是否有效用户」说法不一?因为文档或旧对比里曾写 uid → user_id → sub 等多源兜底。
why2:为什么多源兜底会误导?因为 当前仓库策略已定为只认 CloudBase 的 uid,混用 sub / user_id 会与真实分支不一致。
why3:为什么必须写死策略?因为否则 Context、isValidCloudbaseUid 与线上日志无法对齐,排障成本高。
方案(每个问题不超过两个)
短期方案:在
AuthContext中统一isValidCloudbaseUid与写入 Context 的user.id只使用user.uid;主文档删除「代码读 sub」类过时描述。- 优点:行为单一、可验证。
- 缺点:若未来 SDK 契约再变,需再改一处并更文档。
长远方案:把「有效用户判定」收口为单一模块 + 单测/契约注释。
- 优点:变更可回归。
- 缺点:本轮未单独拆包。
采纳方案:短期方案;主流程文档已与 仅 uid 为准的实现对齐。
补充用例:冷启动、密码登录、验证码登录后看 Context user.id 与 [Auth] 日志是否一致。
2.2 密码/验证码登录成功却仍判失败(id / uid)
根因
why1:为什么 signIn 有时拿不到「已登录」?因为 signInWithPassword 等成功分支里 await getCurrentUser() 返回的对象常见为 id 而无 uid。
why2:为什么只认 uid 会踩坑?因为若未做 uid ?? id 归一,会把已成功会话误判为失败。
方案
短期方案:成功分支统一
await getCurrentUser();AuthContext.signIn用uid ?? id判断是否真正登录成功。- 优点:与 SDK 实际返回一致。
- 缺点:依赖 SDK 字段命名,需在文档标明。
长远方案:封装
normalizeCloudbaseUser(),单测覆盖仅有id的返回体。- 优点:调用方不再散落
??。 - 缺点:多一层抽象。
- 优点:调用方不再散落
采纳方案:短期方案 + 已写入 登录流程.md(含 getCurrentUser await 与 signIn 校验说明)。
补充用例:邮箱密码、手机号密码、手机验证码登录各走一遍。
2.3 手机号历史账号未设登录密码:补设密与「误踢」
根因
why1:为什么补设密后仍可能被强制登出?因为若在 SIGNED_IN / AppState / syncUserFromSDK 等路径也做「缺密码就踢」,容易撞上 updateUser 设密后元数据尚未刷新 的窗口。
why2:为什么只收紧触发点能缓解?因为 仅在 checkInitialState(含冷启动静默重登后的再次判断) 强制补设密,可避免刚写完密码仍被旧元数据判缺。
方案
短期方案:判定
legacyPhoneUserMissingPassword(hasPassword !== true且有phone/phone_number);强制流程只挂checkInitialState;登录页resetPasswordForPhone+updateUser,成功后scheduleSyncAfterLogin。- 优点:减少竞态误踢。
- 缺点:若未来新增入口绕开
checkInitialState,需复查。
长远方案:服务端或统一 User 元数据版本号,客户端以版本 gate 再判
hasPassword。- 优点:语义最稳。
- 缺点:依赖后端能力。
采纳方案:短期方案;主文档已写清触发面与登录页流程。
补充用例:冷启动命中补设密 → 设密成功 → 再切前后台,确认不再误踢。
2.4 回前台与静默重登:顺序、并发与 credentials_error
根因
why1:为什么会出现重复静默请求或错误登出?因为 refreshSession 与 trySilentReloginWithStoredPassword 若并行或顺序颠倒,会与订阅回调打架;credentials_error 若一律映射为登出,会在 仍有 hasLoginState().user 时误伤。
方案
短期方案:仅当
refreshSession失败 再trySilentReloginWithStoredPassword;cloudbase.ts用silentReloginInFlight合并并发;onAuthStateChange对credentials_error且仍有 user 直接 return,否则与sign_out一样映射SIGNED_OUT。- 优点:行为可预期、减少闪退登录态。
- 缺点:包装层逻辑需随 SDK 事件再验证。
长远方案:把会话刷新策略画成状态机并加集成测试。
- 优点:回归稳定。
- 缺点:成本高。
采纳方案:短期方案;主文档已记先后与 credentials_error 分支。
补充用例:回前台、多 Tab/快速切换 AppState、弱网下 refreshSession 失败路径。
2.5 排查成本高:双源日志与关键前缀
根因
why1:为什么线上难对齐「SDK 里到底是谁」?因为 getLoginState().user 与 getCurrentUser() 可能短暂不一致,若无日志只能猜。
方案
短期方案:
checkInitialState中双源并存时打[Auth] checkInitialState 双源对比(不参与分支);文档列[Auth]常用关键字(与[AuthProbe]区分)。- 优点:低成本可观测。
- 缺点:日志量需控制,避免刷屏。
长远方案:可采样或分级日志。
- 优点:长期可运维。
- 缺点:本轮未做。
采纳方案:短期方案;已写入 登录流程.md「关键日志点」。
补充用例:复现一次双源不一致时 grep [Auth] checkInitialState。
2.6 记住密码仅恢复邮箱,手机号需手填
根因
why1:为什么手机用户重进登录页不省事?因为 REMEMBER_EMAIL_KEY 名义上存「账号」,但 UI 只把值灌进 email,未识别 11 位国内手机号 写入 phoneNumber。
方案
- 短期方案:加载记住账号时,除
email外,若为 11 位手机号则写入phoneNumber。- 优点:改动小、体验立竿见影。
- 缺点:国际号码规则未扩展。
采纳方案:短期方案。
补充用例:勾选记住密码 → 用手机号登录 → 杀进程重开 → 手机号应出现在手机登录流程。
3. 小结
- 主文档
docs/流程文档/登录流程.md已按 本轮修复后的代码 重写并对齐:仅uid有效用户、双源日志、refreshSession 与静默重登顺序、静默重登并发合并、credentials_error包装层、补设密仅checkInitialState强制、getCurrentUserawait 与signIn的uid ?? id、记住密码预填手机号、showInfo 短时去重 等。 - 若旧笔记仍写「代码读取
sub」等,视为过时;以 仓库 + 主流程文档 为准。 - 维护建议:今后再改
AuthContext/cloudbase认证行为时,同步更新docs/流程文档/登录流程.md;若需要,再在根AGENTS.md或zhiyuxing-app/AGENTS.md加一句提醒。