news 2026/9/5 6:25:54

维修工单状态机设计:一个汽修系统的心脏该怎么建模

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
维修工单状态机设计:一个汽修系统的心脏该怎么建模

本文是「汽修 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:把维修保养记录做成数据主轴

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/5 6:25:47

电商AI视频创作工具推荐:跨境卖家如何用AI快速制作广告素材?

在过去&#xff0c;电商视频制作往往意味着较高的人力投入。商家需要准备产品样品、安排拍摄、寻找模特、进行剪辑&#xff0c;再根据不同平台调整内容。但随着短视频成为电商营销的重要入口&#xff0c;传统制作方式逐渐难以满足新的需求。尤其对于跨境卖家来说&#xff0c;一…

作者头像 李华
网站建设 2026/9/5 6:25:03

手机端练枪数据复盘:半成装48.8w输出如何拆解与复现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/5 6:24:26

反编译jar包,对ai提问提示词明细

Work app:帮我反编译下*.jar这个jar包。应该是由springboot集成多个module实现的。 每个module可能都打成了jar包都放在了lib里面。 所以你需要判断lib中的哪个jar包是module&#xff0c;然后把整个springboot项目反编译出来给我讲讲反编译后的项目吧&#xff0c;我想了解下&am…

作者头像 李华
网站建设 2026/9/5 6:20:02

两两交换链表中的节点:链表基础与递归思路详解

1. 引言 在 LeetCode 的经典题目中&#xff0c;「两两交换链表中的节点」&#xff08;Swap Nodes in Pairs&#xff09;是一道非常能考察链表基本功和递归思维的题目。很多初学者在面对这道题时&#xff0c;往往会被指针的来回指向绕晕。本文将从链表的基本知识讲起&#xff0c…

作者头像 李华
网站建设 2026/9/5 6:19:37

交互式数字内容项目技术解析:部署、测试与性能评估指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华