Contenido

状态与流转

本文说明系统状态与流转的真实运行方式:状态值以代码枚举为真源,可配置状态机图仅在审批通过和驳回两处被调用,其余流转由代码处理器管控,跨表卡控仍须开发实现。

  • 状态值以代码枚举为真源,字典自动生成,非法值进不来。
  • 状态跃迁时由程序单点回写完工时间、解决时间、生效日期。
  • 状态机图仅在审批通过与驳回两处被引擎调用。
  • 前置条件为纯表达式白名单,跨表卡控须写进程序。
  • 当前流转由代码处理器管控,而非配置驱动。

16.1 一张画得出来的图,和一条真在跑的路

老葛每天干的活,说白了就是把单子从一格推到下一格:草稿变待审、待审变已批、已批变生产、生产变完工。

任何一本讲流程管理的书都会告诉他:这该是一张画出来的图。 谁能推、推到哪儿、推之前要满足什么,全在图上,改图就行,不用改程序。

这套系统里真有这么一张图,也真有填它的界面。第十二章到第十五章讲的很多事,都可以理解为"图上那些格子被填成了什么样"。

但这一章必须先说清一件事,后面才好读:在这套系统里,真正推动单据往前走的力量,主要不来自那张可配置的图,而来自写在程序里的一个个具体规矩。 图是真能画的;它现在只在审批通过、驳回那两处真被叫起来执行,单据往前走的其余每一步,还是程序里那些具体规矩在推。

16.2 状态的码,现在只有一处说得上话

先说已经修好的那部分,因为它决定了"一个状态值到底长什么样"。

以前最坑的一件事,是同一个意思有几种写法:库里存中文的“已处理”,代码里判的是一个英文码,前端下拉里又是第三种叫法。第十五章之前我们讲异常类型时说过,这类不一致最坏的地方不是难看,是统计漏数——漏掉的那几条恰好是没人愿意管的那几条。

现在收口的方式是这样:

  • 状态值与显示名分别在两处定义。 值以代码里的枚举为真源,一套码;显示名、颜色、顺序放在状态字典里。
  • 字典由枚举自动反查生成,不是有人另抄一份。种子跑完,代码枚举和库里的字典两边一致。
  • 前端下拉运行时去拉字典,不再把选项写死在页面上。加一个类型,管理端不用改代码。
  • 写进库的值先归一成码,非法叫法直接拒绝。 这条是硬的:不在枚举里的写法进不来。
  • 历史遗留值做了一次归一迁移,老数据里的中文叫法被换算成码,同时代码保留了旧值的兼容读取。

还有一句要补:移动端目前还是照同一套码手写的一张表,不是运行时拉字典。新增类型时那一头要跟着改一次。这条我们记着,没当作已解决。

16.3 状态一变,跟着自动写的那几格

状态这件事真正的价值不在"标一下",在"标了就带动别的格子"。这套系统里有三处是这样做的,都是本轮补硬的。

  • 工单转进行中,自动写实际开工时间;转完工,自动写实际完工时间。 于是交期达成率、平均周期这两个数第一次有了真口径,而不是拿计划时间凑。没有完工时间的单子不进分母,一个样本都没有时屏上显示两条横线,不显示零。
  • 异常单进入已处理或已关闭,自动写解决时间。 平均处理时长从这一刻起才算得出来。
  • 工艺路线启用时写生效日期、停用时写停用日期。 现场报工选哪一版工序,从此按日期能判;老单子绑住的那一版也不会被新版本顶掉(第九章讲的取版收敛就是这件事)。

这三处的共同点:写它们的动作发生在状态跃迁那一刻,由程序单点负责,不给前端传。 状态格和这些派生格子之间不会出现"一个改了一个没改"。

16.4 那张图长什么样

现在说那张可配置的图。

种子数据里给十二个核心对象各画了一台:订单、报价、询价、工单、派工、请购、采购订单、检验、工艺路线、工装、客户、售后。 每台有一个初始状态。

每一条流转上挂的东西相当齐——这不是个玩具模型:

  • 起始状态、触发事件、目标状态;
  • 前置条件表达式;
  • 所需权限码(比如"提交"要销售建单权限,"批准"要审批权限);
  • 是否触发审批,以及用哪个审批模板;
  • 流转之后要执行的动作:发通知、改某张表的某些字段、创建一张新单据、调一个接口。

订单那一台画得最完整:草稿提交进待审、待审批准或驳回、驳回后重提、批准后转生产、进生产、完工、发货、客户签收,中间还能暂停和恢复。

引擎在执行时确实会查这三样:这条跃迁存不存在、这个人有没有权限、前置条件过不过。全过才写一条状态历史记录,然后执行动作。

光看图,你会觉得这个厂的所有流转都被管住了。

16.5 为什么它没在管

接着上一句往下说,这是全章最难听但也最该写的一段。

这台引擎只在两个地方被叫起来:审批通过和审批驳回之后,各触发一个叫"批准"或"驳回"的事件。除此之外,全厂没有任何一处业务代码在流转前问过它一句"我现在能不能推这一格"。

而被叫起来那两次,以前基本落不了地——三堵墙叠在一起,如今这三堵都收了:

  1. 编码对不上——收了。 引擎用审批实例上的"业务模块"当机器编码去找图。种子里请购那台是缩写式、发起审批传的却是全拼式;订单那台单数、审批复数,十二台里以前只有采购订单和工艺路线偶然对得上。

现在中间加了一张收口映射表:先把业务模块名解析成规范机器编码再找图。真没对应状态机的(报销这类),明确跳过并记一笔,不再拿原名查表必然落空还当作没事。 2. 报错被吞——收了。 那一段调用整个包在异常捕获里,以前失败只往日志写一行警告、审批照常通过。现在升级了:无对应状态机是正常跳过;可一旦机器在、流转却没通过或抛异常,就从一行警告升成错误级日志、并给管理员发一条可观测告警。审批主流程仍不阻断,但**"图没执行"这件事不再是静默的**。 3. 和单据两张皮——收了。 引擎判断"现在是什么状态",以前只读自己那张流水本,没记录就用初始状态;而单子上那格状态是被各服务直接改的、从没写进这张流水。于是第一次触发时它以为单子还在草稿,而"批准"那条跃迁要求的当前状态对不上。现在首触发会回读业务单那一格当前状态补基线(拿不到、或不在已知状态词表里,才退回初始态),两边不再各说各话。

一句话总结:三堵墙收了之后,图在被叫起的那两次,是真能推着单据上那格状态走了。

那真正回写业务状态的是什么?是审批引擎里那张处理器注册表:每种业务单据注册一个自己的处理器,审批通过时把单子改成什么、驳回时改回什么,全是人写死的一段代码。第十一章讲过请购和采购单在驳回时取舍不同——那种"不同"正是手写出来的。

这套系统的流转,目前是代码管的,不是配置管的。

16.6 顺带说两个能看见的坑

图能执行的那两处修了,但界面上配它这件事还留过一个坑。两处实证,一处已经填了:

  • 同一组接口以前存在两套。 一套挂在配置模块下,字段用得对,有查软删、有查重、有权限码;另一套挂在审批模块下,也在提供增删改和手工触发事件,界面用的是前者。后者那套重复的旧接口,已经删了。
  • 删的就是那个走不通的。 旧那套的删除入口按一个不存在的字段名去查活跃记录、按不存在的属性名去改名字——只要有人真去点删除,那次请求必失败。它不是数据问题,是这段代码从没被走通过。

如今全仓状态机配置统一走配置模块那一条。改一处就是改那一份,不再有两张皮的接口。

16.7 前置条件为什么写不了太复杂的事

跃迁上那一格"前置条件"看着很诱人:库存够不够、上一步做完没做完、金额超没超线,好像填个表达式就行。

它跑不通复杂判断的原因是设计层面的:条件求值用的是一个纯表达式白名单,只做字面比较和逻辑组合,不许调用函数、不许访问库。这是故意的——一个能被单据数据影响的表达式如果什么都能干,那就是别人往你系统里塞代码的门。

代价就是:凡是要跨表看一眼的条件,在图上配不出来。 库存够不够、有没有生效的工艺路线、这个月还有没有信用额度——这些只能写进程序。第 11 章说请购进审批时金额传空、按金额分级看不见钱,也是同一个根子上的事。

所以"改流程不找厂商"这句话要分两半说:显示名、颜色、选项、编号格式,厂里自己改;跨表卡控,仍然是一次开发。 第十五章会把这条分界线完整划一遍。

16.8 这一章不做的事

  • 不做流程建模器。 没有拖拽画流程图、没有版本对比、没有草稿与发布两套定义;界面上改的就是生效的那一份。
  • 不做业务动作的全流程自动编排。 跃迁上能配"发通知、改字段、建单据、调接口"四类动作,可这台图只在审批通过、驳回两处被叫起(上一节收了那两处的落地);把全部流转都交给引擎编排,是明写不做的事,目前主路径仍靠代码处理器。
  • 不宣称支持体系审核里的流程图证据。 那张图能导出给人看,但客户问"这张单上周三为什么从待审回到草稿",答得出来的是审批历史和状态记录,不是图。
  • 不做状态回滚。 单据状态推错了,只能人工往回改(并留下修改痕迹),系统不提供"退回上一步"的操作。

16.9 今晚能定的两条

  1. 流转规则先只认一处。 在把那张图接通之前,厂里定一句:谁有权改单子的状态、改成什么要有几条依据。别看流程图,看审批历史和单据上那格状态——那才是真在用的两份记录。
  2. 状态相关的空值别硬凑。 现在这套口径已经明确:没有实际完工时间的工单不进达成率分母,一个样本都没有就显示两条横线。这条纪律要守住——把空白补成零或者补成计划值,等于把 16.3 那三处自动化省下来的可信度全花光。

这一章能定的事就一句:能被配置改变的东西,和真在管着流转的东西,目前不在一处。 知道这一点,厂里才不会把"图上已经画了"当成"现场已经卡住了"。