# 多注册流程状态迁移设计 本文是 `docs/多注册流程架构边界.md` 的落地补充,专门解决“现有扁平状态怎么迁到多 flow 架构”这个最容易把旧逻辑拖垮的问题。 ## 1. 先说结论 当前项目不能直接在 `DEFAULT_STATE` 上继续堆字段。要支持多个完全不同的注册流程,必须先把“运行态”“持久配置”“共享服务状态”“flow 私有状态”分层,再保留一层 OpenAI 旧步骤兼容视图。 这次迁移的目标不是一步把全部字段搬完,而是先建立新的状态主模型,让旧代码通过 adapter 继续运行。 ## 2. 当前源码里的真实问题 ### 2.1 `DEFAULT_STATE` 仍然是单流程扁平模型 `background.js` 里的 `DEFAULT_STATE` 目前把这些不同层次的状态混在一起: - 流程进度:`currentStep`、`stepStatuses` - 通用运行态:`automationWindowId`、`tabRegistry`、`logs` - OpenAI OAuth / 平台回调:`oauthUrl`、`localhostUrl`、`sub2apiSessionId`、`codex2apiSessionId` - OpenAI Plus:`plusCheckoutTabId`、`plusCheckoutUrl`、`plusBillingAddress` - OpenAI 接码:`currentPhoneActivation`、`signupPhoneActivation`、`reusablePhoneActivation` - OpenAI LuckMail:`currentLuckmailPurchase` - 共享邮箱运行态:`registrationEmailState` 这会带来两个直接后果: 1. 新 flow 一接进来,就只能继续往全局 state 加字段。 2. 任何 reset / retry / stop 都容易误清理别的业务域状态。 ### 2.2 旧步骤数字模型已经渗透到多个模块 当前不只是 `background.js` 依赖 `currentStep / stepStatuses`,下列模块也在按“Step 1~13”理解流程: - `background/auto-run-controller.js` - `background/logging-status.js` - `background/account-run-history.js` 所以新状态模型不能只改 `background.js`,必须给自动运行、日志、账号记录保留兼容出口。 ### 2.3 `registrationEmailState` 名字看起来通用,内部却仍带 OpenAI 身份假设 `background/registration-email-state.js` 已经把邮箱运行态单独抽出来了,这是好事;但它的 `preserveAccountIdentity` 现在默认保留的是“手机号身份”,也就是: - `accountIdentifierType = phone` - `signupPhoneNumber` - `signupPhoneActivation` - `signupPhoneCompletedActivation` 这对 OpenAI 的 `add-email` 分支是对的,但对未来可能出现的“用户名主身份”“邀请码主身份”“钱包地址主身份”并不通用。 ## 3. 目标状态分层 建议先建立“概念上的标准状态模型”,物理存储可以分阶段迁移。 ```js chrome.storage.session.runtimeState = { activeFlowId: 'openai', activeRunId: 'run_20260512_xxx', currentNodeId: 'submit-signup-email', nodeStatuses: { 'open-chatgpt': 'completed', 'submit-signup-email': 'running', }, shared: { automationWindowId: 123, tabRegistry: {}, sourceLastUrls: {}, logs: [], autoRun: {}, accountArtifacts: {}, }, services: { mail: {}, proxy: {}, accountPools: {}, }, flows: { openai: { auth: {}, platformBinding: {}, plus: {}, phoneVerification: {}, luckmail: {}, identity: {}, }, }, legacyStepCompat: { currentStep: 2, stepStatuses: { 1: 'completed', 2: 'running' }, }, }; chrome.storage.local.settings = { schemaVersion: 2, shared: { autoRunDefaults: {}, logging: {}, }, services: { mailProviders: {}, ipProxy: {}, hotmailPool: {}, mail2925Pool: {}, icloud: {}, customEmailPool: {}, }, flows: { openai: { signupMethod: 'email', phoneVerificationEnabled: false, plusModeEnabled: false, plusPaymentMethod: 'paypal', sub2api: {}, codex2api: {}, luckmail: {}, smsProviders: {}, }, }, }; ``` ## 4. 四层状态边界 ### 4.1 共享运行态 `runtimeState.shared` 只放“运行任何 flow 都需要”的内容: - `activeFlowId / activeRunId / currentNodeId / nodeStatuses` - `automationWindowId` - `tabRegistry` - `sourceLastUrls` - `logs` - 自动运行轮次、暂停、计时器计划 - 账号产物总览,如最终身份、最终邮箱、最终手机号、完成时间 这里不要再放 OpenAI 特有概念,例如 `plusCheckoutTabId`、`currentPhoneActivation`。 ### 4.2 共享服务状态 `runtimeState.services` 放“可被多个 flow 复用的服务运行态”,而不是具体网站流程状态: - 邮箱 provider 当前会话 - 2925 / Hotmail / iCloud / Cloud Mail 等共享邮箱服务上下文 - IP 代理当前应用结果、探测结果、池游标 - 可复用的共享账号池游标 注意:`LuckMail` 目前不进这一层,它仍属于 OpenAI flow 私有能力。 ### 4.3 flow 私有运行态 `runtimeState.flows[flowId]` 每个 flow 自己保管自己的业务运行态。当前 OpenAI 至少应拆成: - `auth`:`oauthUrl`、`localhostUrl` - `platformBinding`:`sub2api*`、`codex2api*` - `plus`:`plusCheckoutTabId`、`plusCheckoutUrl`、`plusBillingAddress` - `phoneVerification`:`currentPhoneActivation`、`signupPhoneActivation`、`reusablePhoneActivation` - `luckmail`:`currentLuckmailPurchase`、`currentLuckmailMailCursor` - `identity`:OpenAI 特有的登录方式冻结结果、`step8VerificationTargetEmail` 等 ### 4.4 持久配置 `settings` 持久配置必须按“共享 / 服务 / flow 私有”拆开,不再把所有配置都平铺到同一个 namespace。 特别注意: - `signupMethod` 先保留在 `flows.openai`,不要急着抽成全局通用枚举。 - `plusModeEnabled`、`plusPaymentMethod`、`gopay*`、`paypal*` 都属于 OpenAI flow 私有配置。 - `phoneVerificationEnabled` 当前也属于 OpenAI flow 私有配置。 ## 5. 旧步骤兼容层怎么做 迁移阶段仍要保留 `currentStep / stepStatuses`,但它们不再是主数据,只是 `legacyStepCompat` 的派生视图。 建议规则: 1. 新的 canonical 状态只写 `activeFlowId / currentNodeId / nodeStatuses` 2. OpenAI flow 通过 node-to-step 映射生成 `legacyStepCompat` 3. `getState()` 对旧模块仍返回: - `currentStep` - `stepStatuses` - OpenAI 旧字段镜像 4. `setStepStatus(step, status)` 内部先更新 node,再回写 legacy step 视图 这样可以保证: - `background/auto-run-controller.js` 先不重写也能跑 - `background/logging-status.js` 先不重写也能继续渲染 - `background/account-run-history.js` 仍能按旧语义落盘 ## 6. 账号记录与自动运行的兼容策略 ### 6.1 自动运行 `background/auto-run-controller.js` 当前按数字步骤推断进度、失败点和恢复点。迁移阶段要增加一层统一 selector: - `getActiveRunProgress(state)` - `getLegacyStepCompat(state)` - `inferResumeNode(state, flowId)` 第一阶段可以继续让 OpenAI flow 通过旧 step 恢复;第二个 flow 接入时再真正改成 node-based resume。 ### 6.2 账号记录 `background/account-run-history.js` 不能只记“失败在第几步”,还要开始记录: - `activeFlowId` - `activeRunId` - `currentNodeId` - `nodeStatuses` 摘要 - `legacyFailedStep`(仅 OpenAI 兼容) 否则未来多 flow 下,单看 `step7_failed` 已经没有意义。 ## 7. 命名约束 仓库里已经存在别的 `flow_id` 语义,用在 GPC / GoPay 远端任务里。为了避免冲突: - 扩展内部运行态统一使用 `activeFlowId` - 当前轮标识统一使用 `activeRunId` - 节点标识统一使用 `currentNodeId` - 不新增裸字段 `flowId` `flow_id` 只保留给外部接口 payload 兼容使用。 ## 8. 第一阶段迁移顺序 1. 新增状态 selector / patch helper,不改业务步骤行为。 2. 给 OpenAI flow 建立 `flowState.openai` 命名空间,同时保留旧字段镜像。 3. 把日志、自动运行、账号记录改为优先读 selector,不直接拼 `stepStatuses`。 4. 把新加字段一律放进 `shared / services / flows.openai`,禁止继续向 `DEFAULT_STATE` 顶层加 OpenAI 字段。 5. 第二个 flow 接入前,再清理旧 step 镜像的写路径。 ## 9. 本文对应解决的缺口 本文主要补齐以下缺口: - `DEFAULT_STATE` 扁平模型没有命名空间 - 旧步骤状态与新 node 状态如何并存 - 自动运行 / 日志 / 账号记录如何不被状态迁移打断 - `registrationEmailState` 当前仍隐含“手机号是唯一可保留主身份”的假设