技术

主数据状态机不是字段,是一份跨模块公共契约

MES 主数据状态机的设计取舍:六态加停用怎么定、四方口径为什么必须交叉核对,以及 BOM 与编号体系为什么会互相污染。

目录
  1. 一开始的状态机是"抄"出来的
  2. 六态只是起点
  3. 不是所有主数据都该有停用
  4. 引用保护:只在审批通过态开口
  5. 四方一致:落稿前的强制动作
  6. 另一类坑:编号体系会互相污染
  7. 小结

主数据需求设计里最容易返工的部分,不是字段表,而是状态机。

一开始的状态机是"抄"出来的

做 MES 的主数据需求设计时,物料、客户、供应商、BOM 这几类对象的结构高度相似,最省事的做法是先写一份骨架,再逐份复制、改改字段。

复制本身没错,问题在于复制之后没有逐份校验差异。状态迁移表、允许操作矩阵、红线约束、验收项这四处,是四个不同的人(或者同一个人的四个不同时段)写出来的,一旦某份文档只改了其中两处,剩下两处就变成了自相矛盾的证据。

最典型的三处矛盾:

  • 「关闭」和「停用」被当成同一个状态用;
  • 删除允许范围的矩阵和正文对不上;
  • 变更被驳回之后,状态应该回到哪里没有定义。

这三处都不是笔误,是语义漂移:同一个词在不同主数据里承担了不同的含义。

六态只是起点

最终定下来的统一状态机是六态:

状态含义谁触发
草稿已创建未提交,不参与任何引用校验创建人
审批中已提交,审批流实例运行中提交动作
审批通过生效,可被业务单据引用审批完成
已驳回审批未通过,可修改后重提审批人
关闭未审批通过就放弃,等价于"这件事不做了"创建人或管理员
撤回提交后、审批完成前由发起人收回发起人

然后追加第七个:停用(INACTIVE),只对审批通过的对象开放,表示"这条数据还在,但不能再被新单据引用"。

加这个态的理由很直接:一个已经上线的物料,发现录错了,直接删会破坏历史单据的引用完整性;直接改会污染已发生业务。唯一正确的动作是让它退出流转,但保留可查。

这里必须把「关闭」和「停用」彻底分开:

  • 关闭 = 未生效前的放弃,这条记录从此不参与业务;
  • 停用 = 生效后的退场,历史引用依然有效,新引用被阻断。

一个状态承载互斥语义,后面所有矩阵都会写错。

不是所有主数据都该有停用

把这个结论无差别推广到所有对象,就会出问题。两类例外:

BOM 不启用停用。 BOM 的"失效"应该由生效区间和 ECN(工程变更)承担。一个 BOM 版本的退场,本质是某一天之后不再适用,这是一个时间语义,不是一个状态语义。给它加停用,等于允许"某个 BOM 既在生效区间内,又处于停用"这种自相矛盾的数据存在。

AVL(合格供应商名录)由名录状态承担。 名录本身的准入/退出就是它的业务状态,再叠一层停用只会让"这家供应商对这物料还能不能供货"变成两个开关的组合判断。

判断标准可以简化成一句话:如果这个对象的失效可以用时间来精确表达,就不要用状态表达。

引用保护:只在审批通过态开口

状态机定完,真正决定系统能不能用的,是引用规则:

  1. 业务单据只能引用审批通过态的主数据,停用态不可选;
  2. 已被引用的主数据要改关键字段,必须二次确认,并留下影响面提示;
  3. 要停用一条已被引用的主数据,必须先清掉在途引用,否则阻断。

这三条如果不写进红线约束(R-MD 系列),实现阶段一定会被"先让功能跑起来"这个理由绕过去。绕过去之后的代价,是数据出问题以后只能人工修库。

四方一致:落稿前的强制动作

写完一版文档之后,我固定做一遍交叉核对,核对的不是文字,是四张表之间的一致性:

状态迁移表  ↔  操作矩阵  ↔  红线约束(R-MD*)  ↔  验收项(A*)

任何一处新增状态、新增动作、放宽条件,都要同时问四个问题:

  • 迁移表里有没有这条边?
  • 操作矩阵里,哪个角色在哪个状态下能做这个动作?
  • 红线约束需不需要加一条?
  • 验收项怎么验证它?

这一步花的时间,远少于开发阶段发现矛盾后回炉的时间。

另一类坑:编号体系会互相污染

同一份文档里同时存在两套 Q 开头的编号——一套是质检节点 Q01~Q05(来料 IQC、首检、巡检 IPQC、完工 FQC、出货 OQC),另一套是待确认问题 Q1~Q10。

结果就是引用句子互相指错:本来要说"免检策略待确认",实际指到了质检节点;本来要引功能点 F23(数据分发接口),写成了 F22(追溯查询)。

规则是:一份文档里只保留一套编号语义。引用另一套体系时,必须写全名("质检节点 Q01 来料 IQC"),不能只写编号。落稿后再用脚本扫一遍所有 F* A* Q* 引用,确认每个编号都真实存在且指向正确条目。

小结

状态机看起来是几个枚举值,实际上是主数据、业务单据、审批流三方共享的契约。它一旦漂移,下游全部错位。

所以值得在文档阶段较真:把互斥语义拆开、把时间语义还给时间、把引用规则写成硬约束,然后在落稿前做一遍四方交叉核对。这几步不做,省下的时间会在联调时加倍还回来。