v0.2.30 · 生产在用 · 零 moo-system 依赖
流程交给用户画,
环节交给引擎跑
流程定义存库并整图版本化,实例 morph 挂到任意业务单上跑 —— 用户在设计器里画流程,业务单照着走,而不是把流转写死在代码里。
One engine, seven tables
PHP 8.2+ / Laravel 10–12 · 命名空间 Mooeen\Process · 结构由 scaffold YAML 生成
Engine vs lifecycle
环节属于引擎,生命周期属于业务
同样叫「流转」,生态里其实分工两层:一层管代码里声明的业务生命周期,一层管用户画出来的审批流程。两者零 composer 依赖,唯一接缝是 host 胶水里的事件桥。
① 声明式内核
moo-flow
状态机内核。host 写一份 FlowDefinition 声明「状态字段 + 一组 Transition」,受理、拒绝、完成、评价全走同一执行器。
纯逻辑库 · 无路由无迁移
② 通用引擎 · 本页
moo-process
流程定义存库、整图版本化、实例挂任意业务单。节点分办理与审批,参与人按组织规则解析,用户画什么就跑什么。
依赖 moo-scaffold 与 moo-contract
③ 审批应用 · 配套
moo-process-application
以动态表单提交作为流程业务主体:审批申请发起、状态投影与实例详情扩展。扩展包单向依赖引擎,引擎不反向认识它。
v0.1.17 · 产品包
接缝只有一处:本包发领域事件 → host listener 调 FlowExecutor::apply。所以「审批走到某一步,业务单状态跟着变」这件事是可选的加法,而不是引擎里写死的耦合。
Data model
七张表,一条在途实例
定义与整图快照分离,每次发布新建不可变版本;实例锁定发起时的版本,同定义同业务单最多一条在途,靠数据库生成列唯一索引保证。
一次流转的四个动作
- 1发布定义 —— 新建不可变版本,实例锁定发起时的版本。
- 2发起实例 —— morph 挂业务单,按定义范围复检发起资格。
- 3生成待办 —— 一批任务精确标识一次节点激活,并行任务按分支隔离。
- 4留痕 —— 人工动作与系统事件统一入表,append-only。
| 表(前缀 moo_process_) | 职责 |
|---|---|
| definitions | 流程身份:名称、适用业务、启停、发起范围、当前发布版本指针 |
| versions | 不可变版本快照:节点 + 连线整图 JSON,append-only |
| delegations | 审批代理:全部或指定流程范围 + 生效期,不改写待办候选人 |
| instances | 运行实例:morph 挂单 + 发起快照 + 锁定版本 + 表单成果 |
| tasks | 待办:节点快照、候选人、实际操作人、截止与升级配置 |
| logs | 留痕:人工 / 系统统一入表,按待办反查有专用索引 |
| receipts | 生命周期回执:消费者 + 日志 + 事件唯一,重放不重复投递 |
刻意不建的表:notifications(用 host 既有通知体系)、audit_logs(行为审计用 moo-trail)、timers(超时靠扫描任务表,不另存定时器)、EAV 变量表(发起快照进实例 JSON)。少四张表,就少四处会漂移的口径。
Capability
设计、预检、执行、留痕,四段都齐
不是「有节点就算流程引擎」:发布前的预检、在途版本的隔离、参与人的组织解析、审批动作的边角情况,都写进了同一套规则里。
流程定义与版本
定义和整图快照分离;每次发布新建不可变版本,在途实例仍跑旧版本,历史版本与会话不自动改写。
版本化动态表单
表单 JSON 随流程版本冻结;开始 / 办理 / 审批节点各自声明 all / selected / none 填写范围,实例按节点累计结构化成果。
发起范围
按全部在职人员、指定人员、岗位、部门(可含下级)授权;列表先过滤,事务内执行前再复检一次。
条件与网关
字段类型决定运算符与控件;枚举用编码、引用用系统 ID。预检、发布、试运行共用同一套校验。
试运行
用当前设计图和真实业务单只读计算:发起资格、业务快照、预计分支与首批处理人 —— 零实例、零待办、零通知。
执行与审批动作
办理、审批、快捷意见、驳回、转交、催办、前加签后加签与本人减签、或签、会签、依次、直属与逐级主管、排他网关、挂起安全网。
一级子流程
父流程节点引用同业务模型的另一条已发布定义;发布父版本时锁定子版本,父流程终止会级联终止在途子实例。
实例、待办与抄送
管理读面与个人关系读面分开,「我的流程」五视图、代理审批、独立评论与完整留痕;抄送与 cc 节点不建待办,超时默认只提醒。
Safety
运行态没有通用 CRUD
流程数据一旦能被直接增删改,留痕和状态机就都失去意义。所以引擎在这件事上是刻意收窄的。
01
写动作事务内锁行复检
办理、审批、转交都在事务里锁住当前待办再复检,竞态下不会重复推进。
02
个人与管理接口不混用
发起人撤回和管理员强制终止是两条独立契约,走不同入口与不同授权。
03
组织数据走公共契约
人员、部门、岗位经 moo-contract 的 OrgDirectory 读取,缺接线显式失败,不静默降级成空列表。
Extension
引擎不认识产品,产品来接引擎
扩展包单向依赖引擎,引擎不反向认识任何产品模型、表、路由或状态 —— 这是它能被不同业务复用的前提。
产品接入点
DefinitionProfileRegistry—— 声明某类流程要预置什么SubjectContractRegistry—— 业务单怎么被引擎读懂SubjectFormDataBridge—— 表单数据怎么进出InstanceDetailContributor/InstanceDocumentContributor—— 详情页与文书由谁补
Host 责任
- 组织数据:由 System 或 Host 提供 OrgDirectory 实现
- 业务归属、业务字段、展示名称:窄契约或事件桥
- 通知文案与渠道:用 Host 既有通知体系
- 业务状态如何随审批变化:listener 里调 FlowExecutor
What it needs
用起来要满足的条件
- · 必须先接 org 契约(OrgDirectory 的实现),否则参与人解析直接失败。
- · 流程定义由用户在宿主后台的设计器里画,引擎不自带一套前端设计器。
- · 结构性挂起(图本身有问题)需要修图后重新发起,不会自动恢复。
- · 升级要 Host、Admin 与常驻 worker 一起升,禁止新旧引擎混跑。
What it is not
刻意不承诺的事
- · 不是开箱即用的审批产品;审批应用是单独的 moo-process-application。
- · 不管业务政策:谁能发起、字段怎么填、通知发给谁,都由 host 定义。
- · 不存通知、审计与定时器,这三件事各自有更合适的包。
- · 源码非开源,按商业授权提供;接入与升级以实际发布版本为准。