本文是「汽修 SaaS 开发连载」第 2 篇。上一篇讲了为什么我们把"维修保养记录"作为整个系统的数据主轴。这一篇讲系统的心脏:维修工单,重点是状态流转怎么建模。
先说结论
维修工单不能用一个 status 字段随便赋值来管。早期我们就是这么干的,结果在联调时各种诡异 bug:质检还没通过就能交车、返工的单子还能继续领料。后来老老实实写成了显式状态机,同类问题再没出现过。
9 个状态 + 1 条回退分支
跟着汽修厂跟了两天的实际流程(详见本连载第 1 篇),我们梳理出工单的 9 个状态:
预约 → 待进厂 → 已接车 → 维修中 → 待质检 → 已质检 → 待结算 → 已交车 → 已结案 ↑ │ └── 已返工 ──┘每个状态的含义和进入条件:
| 状态 | 含义 | 进入条件 |
|---|---|---|
| 预约 | 客户约了时间还没来 | 小程序/电话/前台创建预约单 |
| 待进厂 | 已确认预约,等车到店 | 预约确认 |
| 已接车 | 车到店,完成接车登记和预检 | 录入故障描述 + 预检照片 |
| 维修中 | 技师开始施工 | 派工完成,技师在移动端接单 |
| 待质检 | 技师报完工 | 技师提交完工 |
| 已质检 | 质检通过 | 质检员验收,可含电子签名 |
| 待结算 | 车辆可交,等客户付款 | 结算单生成 |
| 已交车 | 客户付款提车 | 收银完成 |
| 已结案 | 售后回访完成或过保 | 交车后 N 天自动或手动关闭 |
"已返工"不是第九个半状态,而是一条回退边:已交车(或已质检)后发现维修质量问题,工单退回维修中,同时打上返工标记。这个标记很重要,它是技师绩效里"返修率"的数据来源,后面写绩效那篇会用到。
为什么必须用显式状态机
用散落的 if-else 判断"当前能不能执行某个操作",代码写起来快,烂起来更快。我们踩过的具体坑:
- 交车按钮的可用条件散落在 3 个服务类里,改需求的时候漏了一处,出现了"未结算已交车"的单子,月底对账差了 8000 多块,查了一天;
- 加一个"客户换件需二次确认"的流程节点,要改 5 处判断逻辑。
重构后的做法是把流转规则收敛成一张表:
// 流转规则:允许 [当前状态 x 操作] → 目标状态Map<Transition,TargetState>RULES=Map.of(newTransition(ACCEPT_CAR),State.IN_REPAIR,// 接车派工后进入维修newTransition(SUBMIT_WORK),State.PENDING_QC,// 技师报完工newTransition(PASS_QC),State.QC_PASSED,newTransition(SETTLE),State.PENDING_PAY,newTransition(PAY_DONE),State.DELIVERED,newTransition(REWORK),State.IN_REPAIR// 返工回退);任何状态变更必须走统一的transition(orderId, action, operator)入口,入口里做三件事:查规则表、校验操作权限、记录流转日志。三件事任何一件失败,状态就不动。
每个节点都要留下"可追溯链路"
这是汽修场景特有的要求。一次保养不只是记一行"换机油",客户(和平台监管)要求能回答:谁做的、什么时候做的、换的什么件、有没有照片。
所以工单流转日志表是这样设计的:
CREATETABLEwork_order_transition_log(idBIGINTPRIMARYKEY,order_idBIGINTNOTNULL,from_stateVARCHAR(20)NOTNULL,to_stateVARCHAR(20)NOTNULL,operator_idBIGINTNOTNULL,-- 操作人operator_roleVARCHAR(20),-- 前台/技师/质检/财务remarkVARCHAR(500),attachmentsVARCHAR(1000),-- 该节点照片/视频的对象存储 keycreated_atDATETIMENOTNULL);一张工单走完全流程,这张表大概有 10~15 行记录,每行都可以关联照片。车主在小程序里看到的"维修进度",就是把这张日志表按状态映射成对客户友好的文案(“待质检"→"质检中,请稍候”)。
状态守卫 + 权限矩阵
状态机解决"能不能流转",还有一层是"谁能流转"。我们按角色 × 操作做了权限矩阵,几个容易漏的点:
- 改价只能店长操作,前台只能按价目表报价;
- 技师只能对自己被派工的工单报完工;
- 返工操作必须附带返工原因,且记录原质检人(涉及责任划分,店老板很在意这个);
- 已结案的工单任何角色都不能修改,只能追加备注。
权限矩阵同样收敛成配置表,新加门店角色的时候改配置就行,不用改代码。
效果
状态机重构完之后的一个实际收益是"超时预警"变得很简单:每个状态有标准停留时长(比如维修中超过预估工时 20% 就亮黄灯),看板直接按停留时长排序。在此之前,店长想知道哪台车卡住了,得挨个问技师。
下一篇讲"一车一档"的数据模型,会提到我们拿车牌当主键踩的坑——那是我这个项目里返工最彻底的一次设计。
「汽修 SaaS 开发连载」· 02 / 01 从 0 设计汽修 SaaS:把维修保养记录做成数据主轴