
本文是「汽修 SaaS 开发连载」第 2 篇。上一篇讲了为什么我们把维修保养记录作为整个系统的数据主轴。这一篇讲系统的心脏维修工单重点是状态流转怎么建模。先说结论维修工单不能用一个 status 字段随便赋值来管。早期我们就是这么干的结果在联调时各种诡异 bug质检还没通过就能交车、返工的单子还能继续领料。后来老老实实写成了显式状态机同类问题再没出现过。9 个状态 1 条回退分支跟着汽修厂跟了两天的实际流程详见本连载第 1 篇我们梳理出工单的 9 个状态预约 → 待进厂 → 已接车 → 维修中 → 待质检 → 已质检 → 待结算 → 已交车 → 已结案 ↑ │ └── 已返工 ──┘每个状态的含义和进入条件状态含义进入条件预约客户约了时间还没来小程序/电话/前台创建预约单待进厂已确认预约等车到店预约确认已接车车到店完成接车登记和预检录入故障描述 预检照片维修中技师开始施工派工完成技师在移动端接单待质检技师报完工技师提交完工已质检质检通过质检员验收可含电子签名待结算车辆可交等客户付款结算单生成已交车客户付款提车收银完成已结案售后回访完成或过保交车后 N 天自动或手动关闭已返工不是第九个半状态而是一条回退边已交车或已质检后发现维修质量问题工单退回维修中同时打上返工标记。这个标记很重要它是技师绩效里返修率的数据来源后面写绩效那篇会用到。为什么必须用显式状态机用散落的 if-else 判断当前能不能执行某个操作代码写起来快烂起来更快。我们踩过的具体坑交车按钮的可用条件散落在 3 个服务类里改需求的时候漏了一处出现了未结算已交车的单子月底对账差了 8000 多块查了一天加一个客户换件需二次确认的流程节点要改 5 处判断逻辑。重构后的做法是把流转规则收敛成一张表// 流转规则允许 [当前状态 x 操作] → 目标状态MapTransition,TargetStateRULESMap.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把维修保养记录做成数据主轴