手机分期订单状态机
单一事实源(SoT):跨 SA App / 客户 App(PocketBuy)/ 运营后台 / 风控 / 信贷核心的状态机契约。
任何涉及订单状态的设计、接口、PRD、代码注释,应引用本文档,不得自行定义新的状态名 / 枚举值 / 流转规则。
如需新增状态或调整流转,先改本文档再同步下游。
适用业务线:手机分期(PocketBuy)。现金贷(RocketCash)等其它业务线的订单状态机应独立分册(避免直接套用)。
1. 文档定位与适用范围
| 项 | 说明 |
|---|---|
| 适用业务 | 手机分期(PocketBuy)— SA 代客办单的完整生命周期 |
| 适用端 | SA App(PocketBuy SA)/ 客户 App(PocketBuy Client)/ 运营后台 / 风控决策 / 信贷核心 |
| 状态语义 | 一级状态全部用完成式命名(描述当前所处状态而非”待做什么”) |
| 子状态语义 | 二级标签不进一级状态机,作为附加字段描述当前一级状态的细分原因或属性 |
| 工程命名规范 | 一级状态:PascalCase(如 Drafting、FixRequired);二级枚举值:snake_case(如 identity_mismatch、declined) |
2. 一级状态(8 个)
| 状态 | 业务含义 | SA 下一动作 | UI 标签(i18n value) | UI Tab 归属 | 二级字段(如有) |
|---|---|---|---|---|---|
Drafting | 进件中:客户资料填写未完成;订单已创建有 order_id,可中途暂停后续录 | 继续录单 → Review 提交 | Drafting | Pending(pending tab) | — |
Reviewing | 客户已提交,风控审批中 | 等审批结果 | Reviewing | Reviewing(in_approval tab) | — |
FixRequired | 风控要求补正资料 | 跳回标红 step 补齐后重提 | Fix Required | Pending(pending tab) | fix_reason + fix_notes |
Approved | 已审批通过,待签约 | 跳 Sign 流程 | Approved | Pending(pending tab) | offer_type |
Signed | 已签约,待 SA 收首付 | 跳 Pay | Signed | Pending(pending tab) | offer_type |
Paid | 已收首付,待 SA 交付设备 + 锁机激活 | 跳 Deliver | Paid | Pending(pending tab) | offer_type |
Completed | 已交付 + 锁机激活完成;订单生效 | 无 | Completed | Completed | offer_type |
Closed | 已关闭(拒贷 / 用户放弃 / 过期) | 无 | Closed | Closed | close_reason |
UI Tab 与一级状态的映射(4 Tab):
| UI Tab 标签 | tab_id(技术字段) | 包含状态 |
|---|---|---|
| Pending | pending | Drafting / FixRequired / Approved / Signed / Paid |
| Reviewing | in_approval | Reviewing |
| Completed | completed | Completed |
| Closed | closed | Closed |
tab_id
in_approval是历史技术命名(早期叫 In Approval),UI 显示Reviewing;改名风险高(破坏 URL section param / 后端枚举),保留不动。
3. 状态流转图
stateDiagram-v2 [*] --> Drafting: OTP 通过 + 创建订单 Drafting --> Reviewing: Review 页 SA 帮客户提交 Reviewing --> Approved: 风控审批通过(含 offer_type=original 或 revised) Reviewing --> FixRequired: 风控要求补资料(带 fix_reason) Reviewing --> Closed: 风控拒贷 → close_reason=declined FixRequired --> Reviewing: SA 原单补齐后重提 FixRequired --> Closed: SLA 超时 → close_reason=returned Approved --> Signed: Sign 流程子阶段 3 客户签字提交 Approved --> Closed: 24h 未推进 → close_reason=expired Signed --> Paid: Pay 页 SA 确认收到首付 Signed --> Closed: 24h 未推进 → close_reason=expired Paid --> Completed: Deliver 页 IMEI 校验通过 + 锁机激活成功 Paid --> Closed: 24h 未闭环 → close_reason=expired Completed --> [*] Closed --> [*]
关键说明:
Drafting不会自动 Closed:客户已落库但未提交,运营可手动归档但状态机不自动转。Reviewing没有 24h 超时:风控审批时长由风控侧 SLA 控制,不在状态机层超时。Approved / Signed / Paid均 24h 超时自动 Closed(expired):给”SA 推进窗口”加上限,避免库存锁定 / 客户决策窗口拖长。- 没有
Drafting → Approved等跳步 — 必须经过Reviewing。 - 没有
Closed → 任何— 关闭后不可重开(如要重新办单需新建订单)。
4. 二级状态字段(不进一级状态机)
二级字段是附加在一级状态上的细分原因或属性标签,作为 OrderData 的可选字段返回。不改变状态机流转。
4.1 fix_reason(仅 status = FixRequired 时有意义)
| 取值 | 含义 | 触发场景 |
|---|---|---|
identity_mismatch | BVN / NIN 核验信息与 SA 录入不符 | 风控核身阶段发现不一致 |
income_insufficient | 收入不足 / 流水异常 | 风控收入评估不通过 |
guarantor_invalid | 担保人信息核验失败 | 担保人手机号无效 / BVN 不可达等 |
address_unverified | 居住地址无法核实 | 地址核验失败 |
other | 其它原因(须配合 fix_notes 自由文本说明) | 不在上述枚举内的补正方向 |
配套字段:
fix_notes: string(可选,≤ 500 字符):自由文本,用于补充说明(特别是other取值时)
4.2 offer_type(仅 status ∈ {Approved, Signed, Paid, Completed} 时有意义)
| 取值 | 含义 | 业务影响 |
|---|---|---|
original | 按 SA 试算方案审批通过 | 合同方案 = 试算方案 |
revised | 风控调价通过(如降额 / 改期数) | 合同方案 ≠ 试算方案;详情见 approval_result 字段;UI 端订单卡显示 “Offer Revised” 黄标 |
注:早期 prototype 用过 revised_offer: boolean 字段表达同一语义;v2.0 起统一改为 offer_type 枚举。
4.3 close_reason(仅 status = Closed 时有意义)
| 取值 | 含义 | 触发场景 |
|---|---|---|
declined | 风控拒贷 | Reviewing → Closed(风控审批不通过) |
returned | 客户主动放弃 / 三次联系不上 | FixRequired SLA 超时;或运营三次联系不上 |
expired | 长时间未推进过期 | Approved / Signed / Paid 24h 未推进;自动转 Closed |
5. Sign 流程子阶段(非二级状态,前端推断)
Sign 流程内部 3 子阶段(订单 status 整个 Sign 流程保持 Approved):
| 子阶段 | 客户端 signStep | 触发条件 | 完成时写入字段 |
|---|---|---|---|
| Confirm Plan | 1 | 进入 Sign 页时默认 | — |
| Group Photo | 2 | SA 点 Confirm Plan & Continue | order.group_photo_url(合影 URL) |
| Sign Agreement | 3 | SA 拍完合影点 Next | order.signed_at(提交时同时推进 status 到 Signed) |
Continue 路径恢复逻辑(前端按字段推断当前子阶段):
if (!order.group_photo_url) → signStep = 1(Confirm Plan)
else if (!order.signed_at) → signStep = 3(Sign Agreement)
else → 已经是 Signed 状态,跳 Pay
重要约定:
- ❌ 不给
OrderData加sign_substage: enum字段 - ❌ 不进入订单状态机
- ✅ 前端按已有字段(
group_photo_url/signed_at)推断 - ✅ 后端只需要正确接收
saveStep('group_photo', ...)和saveStep('review', { signed_at })(或saveStep('sign', ...))两个事件
为什么这样设计:
- Sign 子阶段是流程内部步骤,不是状态机层面的”状态”
- 信息已经在
group_photo_url字段里隐含(有 = 进过 Step 2;没有 = 还在 Step 1) - 业务上 Sign 流程是 SA 连续操作,不像 Drafting 那种”长时间挂起”需要监控进度
- 如未来需要追踪”SA 在合影环节的流失率”等运营数据,用埋点分析,不动状态机
6. 状态推进触发条件
谁触发 → 状态变化 → 调用什么接口 → 关键字段
| From → To | 触发方 | 接口 | 写入字段 | |
|---|---|---|---|---|
(初始) → Drafting | SA(Verify OTP 通过) | 校验 OTP + 创建订单([[接口需求/SA App/03-客户身份验证与订单创建#2 | 03 §2]]) | status='Drafting' |
Drafting → Reviewing | SA(Review 提交) | 进件正式提交([[接口需求/SA App/04-进件资料采集#5 | 04 §5]]) | status='Reviewing'、customer_authorized=true、submitted_at |
Reviewing → Approved | 风控 | 风控决策事件 | status='Approved'、offer_type ∈ {original, revised}、approval_result | |
Reviewing → FixRequired | 风控 | 风控决策事件 | status='FixRequired'、fix_reason、fix_notes? | |
Reviewing → Closed(declined) | 风控 | 风控决策事件 | status='Closed'、close_reason='declined' | |
FixRequired → Reviewing | SA(补齐后再提) | 同 Drafting → Reviewing | — | |
FixRequired → Closed(returned) | 后台(SLA 超时) | 定时任务 | status='Closed'、close_reason='returned' | |
Approved → Signed | SA(客户签字提交) | 客户签字提交([[接口需求/SA App/06-审批后子流程#2 | 06 §2]]) | status='Signed'、signed_at、signature_image_url |
Approved → Closed(expired) | 后台(24h 未推进) | 定时任务 | status='Closed'、close_reason='expired' | |
Signed → Paid | SA(确认首付到账) | 首付收款提交([[接口需求/SA App/06-审批后子流程#3 | 06 §3]]) | status='Paid'、confirmed_at |
Signed → Closed(expired) | 后台 | 定时任务 | 同上 | |
Paid → Completed | SA(IMEI 二次校验通过 + 锁机激活成功) | 交付确认([[接口需求/SA App/06-审批后子流程#4 | 06 §4]]) | status='Completed'、delivered_at、lock_activation_status='succeeded' |
Paid → Closed(expired) | 后台 | 定时任务 | 同上 |
Sign 流程子阶段(Confirm Plan → Group Photo → Sign Agreement)不在本表,因 status 全程保持
Approved。
7. 时限规则(推进窗口期)
| 状态 | 推进窗口默认值 | 红字阈值(前端 SA 端订单卡) | 超时后状态 | 业务含义 | 运营可配置 |
|---|---|---|---|---|---|
Drafting | 无超时 | — | — | SA 可中途暂停后续录 | — |
Reviewing | 由风控 SLA 控制 | — | — | 不在订单状态机超时层 | — |
FixRequired | 由运营 SLA 配置 | — | Closed(returned) | 客户补资料窗口 | ✅ |
Approved | 24h | < 12h | Closed(expired) | 客户决策签约窗口 | ✅ 独立配置 |
Signed | 24h | < 12h | Closed(expired) | 等待首付到账窗口 | ✅ 独立配置 |
Paid | 24h | < 12h | Closed(expired) + 首付退款 | SA 交付设备 + 激活窗口 | ✅ 独立配置 |
Completed | 终态 | — | — | — | — |
Closed | 终态 | — | — | — | — |
SA App 订单卡
expires_at字段呈现倒计时;< 红字阈值时改用text-stateDanger颜色。
8. 状态相关的异常码
接口层面与状态机相关的异常码命名规则:ORDER_NOT_<状态> — 表示当前订单状态不允许该操作。
| 异常码 | 含义 | 使用场景 |
|---|---|---|
ORDER_NOT_FOUND | 订单不存在 | 任何拉单 / 改单接口 |
ORDER_NOT_AUTHORIZED | 订单不属于当前 SA | 同上(多租户隔离) |
ORDER_STATUS_INVALID | 当前订单状态不允许该操作(通用兜底) | 进件资料采集 / saveStep |
ORDER_NOT_APPROVED | 当前不是 Approved 状态 | Sign 子流程相关接口 |
ORDER_NOT_SIGNED | 当前不是 Signed 状态 | 首付收款接口 |
ORDER_NOT_PAID | 当前不是 Paid 状态 | 交付确认接口 |
9. 其它文档引用关系
本文档为 SoT,下列文档应引用本文(不应自行定义状态名 / 枚举):
| 文档 | 引用方式 |
|---|---|
| 04-进件资料采集 §2 OrderData 出参 | 引用本文 §2 一级状态 + §4 二级字段 |
| 06-审批后子流程 | 引用本文 §6 推进规则 + §7 时限规则 + §8 异常码 |
| 07-列表与汇总查询 | 引用本文 §2 UI Tab 映射 + §4.3 close_reason 取值 |
kangaroo_prototype/.../docs/application/overview_prd.md §3.2.2 状态机 | 引用本文(替代原 prototype 自行维护的状态机图) |
kangaroo_prototype/.../api/order.ts 类型注释 | 引用本文 §2 / §4 |
如未来加入 客户 App(PocketBuy Client)/ 运营后台 / 风控 / 信贷核心 相关文档涉及订单状态,应直接引用本文,避免分叉。
10. 修改记录
- [2026-06-01] v2.0:首版(从 prototype overview_prd.md §3.2.2 + 接口需求 04/06/07 整合);一级状态全完成式命名;新增 3 个二级字段(fix_reason / offer_type / close_reason);Sign 流程子阶段不入状态机的设计决策落档。