纯 Go 通用工作流:文件、文档与业务流程
# 纯 Go 通用工作流:文件、文档与业务流程
版本 v1.4,2026-10-22。返回总览。
# 1. 已确定的目标
自研后端和工作流运行时只使用 Go,不部署 Flowable/Java bridge。以 Flowable 的流程、表单、任务和审批业务能力为对标,文件/文档审批与业务单据使用同一个引擎。功能对标不等于兼容旧 Java API、引擎表或全部历史流程实例。
M0 已实现单结构化激活的持久化/并发 PoC,并在 MySQL/PostgreSQL 验证跨进程恢复、AND/OR、命令幂等、timer fencing 及双资源事务;尚非生产执行器。实现和证据见 M0 实施,不宣称达到完整等价。对标功能逐项列出实现阶段、支持语义和验收,不用“支持 BPMN”概括所有构件。
# 2. 现有组件评估与复用原则
| 候选 | 已查官方材料显示的定位 | 与本项目的差距/尚需验证 | 处置 |
|---|---|---|---|
| olive-io/bpmn | 可嵌入 Go 的 BPMN runtime 和 schema 模块 | 本次未验证七库持久化恢复、企业审批、通用资源授权和整套设计器 | M0 评估 schema/parser 与基础执行;未过恢复和事务 PoC 不绑定核心 |
| go-workflows | Go 代码定义的持久工作流,提供多个存储 backend | 仍需可视化图编译、审批任务、表单、资源 ACL,以及其他目标数据库适配 | 借鉴/评估 durable 调度,不把它直接等同审批平台 |
| Temporal | 代码定义和历史重放的持久执行平台 | 需另建在线模型解释、审批/表单/文件平台,且增加独立基础设施与存储约束 | 保留未来长时自动化编排 adapter,首期不作为必选运行时 |
来源:olive-io/bpmn (opens new window)、go-workflows (opens new window)、Temporal (opens new window)。这是基于公开资料的初筛,未做源码全审计、性能比较或生产认证;不据此断言市场上不存在满足条件的产品。
目前没有经本项目验证可直接覆盖全部目标的组件,因此方案采用“自有公开契约 + Go 通用核心 + 可复用库”的路线。缺少的持久化、审批、资源绑定、表单设计和运维功能自研。若 PoC 找到满足契约的现成核心,可通过 RuntimeBackend 接口替换,不影响文件/业务资源合同。
复用条件:依赖无 JVM、许可证和维护状态明确、流程语义通过测试、可在目标数据库正确恢复、可纳入租户和事务边界。不能为保留第三方引擎而绕过这些条件。
# 3. 模块与依赖
workflow-core 定义/编译/执行 token、节点状态、幂等、持久化、定时器
workflow-approval 人工任务、候选策略、会签/或签、委派转办、审计
workflow-resource ResourceRef、ResourceAdapter、版本和授权端口
bpm 流程与表单设计/发布、管理 API、实例视图
forms 可先作为 bpm 内包;版本化 schema、验证和渲染协议
documents 文件/文档、版本、ACL、元数据、审批状态
business modules 请假、采购、合同等资源适配器
core 不依赖 documents/system/具体业务。候选解析、资源授权、受控自动动作由上层注入。API 和 worker 可共享 Go 模块和数据库;以后独立部署也仍是 Go 服务。
流程不把文件路径、用户输入的 tableName 或任意 URL 当执行目标。资源适配器按 resourceType 白名单注册,实例只能绑定当前租户有效资源。
# 4. 通用资源合同
每次发起绑定 ResourceRef:tenantId、resourceType、resourceId、resourceVersion、contentHash(适用于文件字节)、displayName,以及可选的关联资源集合。
| 资源类型 | 例子 | 必须固定的内容 |
|---|---|---|
| file | 上传 PDF/图片的审阅、归档审批 | 对象版本 ID、hash、大小、归属和附件 ACL |
| document | 合同文档、制度、发文 | 文档版本、正文/附件快照、元数据版本 |
| business | 请假、采购、报销、合同单据 | 业务单据版本、用于决策的字段快照 |
| form-instance | 在线设计的请假、申请等独立行政表单 | 内置表单实例 ID、提交版本、formVersion、字段快照与附件引用 |
| collection | 一组文档与业务关联资源 | 各成员资源与版本清单、组内容摘要 |
ResourceAdapter 提供 LoadSnapshot、Authorize、ValidateTransition、ApplyOutcome、ResolveView。快照用于审核和决策,不意味着引擎获准绕过 ACL 读取所有字段;字段可见性仍由 adapter 和节点规则计算。
同一个定义可声明允许的资源类型集合;业务模板只配置表单、节点与状态映射,不复制审批引擎。自定义状态由 adapter 映射到 pending/running/approved/rejected/cancelled 等通用结果。
# 独立表单资源
bpm/forms 内置 FormInstanceResourceAdapter,注册 resourceType: form-instance,不要求用户为每张在线表单编写 business adapter。在线设计并发布表单后,POST /workflow/form-instances 创建当前租户、当前用户的草稿,返回 resourceId/resourceVersion;草稿修改以 version 做乐观锁。草稿修订版本与引用的 formVersion 分开记录,提交版本保存完整字段快照和附件引用并冻结,审批节点的合法字段写入形成新的、可追溯的节点数据记录,不覆盖最初提交快照。
发起仍调用统一 POST /workflow/instances,引用该表单的 resourceId/resourceVersion。引擎事务内校验表单版本、可发起定义、提交人/租户/字段权限与附件授权,将草稿修订转为不可变提交版本,同时写入资源占位和流程实例。相同 commandId 返回原结果;同一资源版本/用途的并发发起由活动占位约束拒绝。退回重填或重新提交创建新的资源版本,旧实例继续绑定原版本。
内置 adapter 实现相同的 LoadSnapshot/Authorize/ValidateTransition/ApplyOutcome/ResolveView:本人及合法流程参与人按节点规则查看字段,审批结果更新该提交版本的业务状态,状态和内容快照分开存储;附件复用文件 ACL 与临时 grant。此类型和 file/document/business 一样有 tenant_id、访问检查与留存约束,不能通过省略 ResourceRef 绕过资源授权。
# 文件和文档语义
提交审批锁定一个不可变版本,底层对象使用不可覆盖的 storage key/版本并记录 hash。新上传内容创建新版本,旧流程继续审旧版;若必须换版则取消并重提,或走已定义的显式修订流程,不能让批准结果自动覆盖最新版。
引擎 task permission 与文件 ACL 双重验证。允许为候选/审批人签发限定实例、任务、版本、时效的临时 read grant;默认不扩展到整个目录或其他版本。任务结束/转办/流程取消后回收 grant;短时下载 URL 仍有残余有效窗口,要按文件敏感级别控制有效期,严格撤销场景通过鉴权下载代理。
文件元数据、版本和引用计数在业务库;字节在对象存储。流程结束不自动删除文件,清理遵循保留/归档/legal hold 策略,存在活动流程或留存约束时拒绝物理删除。审批意见、附件、下载和版本切换保留审计。
同一租户、资源版本、流程用途默认只允许一个活动实例,靠持久化唯一占位/资源锁确保;确需并行多个不同审批目的,由 purposeKey 显式区分,不能靠前端防重复。
# 5. 定义、编译与设计器
采用版本化 WorkflowDefinition JSON 作为规范执行模型,包括节点、边、作用域、条件、候选策略、表单/字段权限、超时与重试。简易审批设计器和 BPMN 设计器编译到同一模型,发布后保存规范模型、原始设计、编译器版本和摘要。
Vben 集成 JSON 表单设计/预览/渲染;BPMN 在线建模建议使用 bpmn-js,增加本项目扩展属性面板。导入只接受明确支持的 BPMN 子集;不支持的元素、脚本、表达式或命名空间在发布时给出节点级错误,不静默忽略。导出携带支持范围说明和本项目扩展。bpmn-js (opens new window)
编译校验:开始/结束可达、节点和边引用、分支与 join 匹配、合法循环和 scope、空候选策略、超时、表单字段、资源类型和表达式类型。网关采用受控条件 DSL,禁止任意 Go/JS/Java 代码、反射调用或不受控网络访问。
发布版本不可改。定义版本保存 definitionVersion、formVersion、compilerVersion、requiredEngineVersion 与编译产物摘要;实例发起时固定这些值及 engineVersion。compilerVersion 标识图编译规则,engineVersion 标识节点/表达式/审批/join/定时器的运行语义,两者不能互相替代。新发布不改变旧实例绑定;历史渲染使用原 schema。停用定义阻止新发起,不自动终止已有流程。
# 运行时版本与滚动升级
engineVersion 对应不可变的语义处理器集合,和 Go 二进制发布版本分开。处理器行为或持久化含义发生变化时分配新 engineVersion;同一二进制可以同时携带新旧集合,但不能以新处理器替换已有版本号下的行为。实例、异步 job、timer 和命令记录绑定 engineVersion,资源/候选等扩展契约也声明所支持的语义版本。
新实例必须使用所选定义的 requiredEngineVersion,不能用当前二进制或全局默认版本覆盖。切换默认版本只影响后续编译/发布的新定义;旧定义若继续开放发起,就必须保留其精确匹配的运行时。更换语义时重新编译并发布新的 definitionVersion,旧定义与旧实例的绑定均不修改。
| 操作 | 必须满足的条件 | 未满足时 |
|---|---|---|
| 恢复/领取旧实例任务 | API/worker 注册了精确匹配的 engineVersion 与所需处理器,能读取该 schema | 不领取/不执行;记录缺失运行时告警,不使用最新版兜底 |
| 滚动部署 | 先 expand schema;保留活动引用及仍可发起定义要求的旧处理器;所有相关 API/执行池支持新版本后才允许以新默认编译和启用新定义 | 阻止默认值/定义准入切换,旧定义和实例继续按原版本执行 |
| 删除旧处理器 | 已关闭该版本的新发起准入,所有引用它的定义准入均已停用;无活动实例、已受理未完成命令、job/timer 和在途执行租约;历史仍可读取 | 任一引用或开放入口存在就阻止退役,保留旧处理器/执行池 |
| 应用回滚 | 目标构建支持活动引用与仍可发起定义所需的全部 engineVersion、默认编译版本及当前兼容 schema;不兼容版本的准入已关闭且引用已排空 | 禁止直接回滚;保留兼容运行池,不隐式改绑定义或降低实例版本 |
| 实例语义迁移 | 独立迁移命令,校验目标版本、节点/token/任务/timer/变量映射,固定检查点并审计 | 保持原绑定,不能靠重启或发布定义隐式迁移 |
实例迁移属于 R3.4;在此之前采用新旧运行时并存和旧版本排空,不以“迁移功能未做”替代基础升级保护。worker 领取按支持版本过滤,提交仍校验实例版本与 fencing,避免滚动期间旧 worker 完成新语义任务。部署记录活动版本清单、处理器清单与回滚兼容性;SQL expand/contract 本身不能证明流程语义兼容。
退役协议使用数据库中的版本级准入状态 enabled → draining → retired,不能只依赖进程内配置或一次无锁计数:
- 关闭准入:受控部署服务锁定 wf_runtime_versions 中该 engineVersion 的记录,写入 draining 并递增 admission_epoch 后提交。draining 拒绝新受理的 StartInstance、旧定义重新启用,以及发布/启用仍要求该运行时的新定义;现有实例和此前已受理命令仍可由匹配处理器执行。
- 处理定义与入口:通过可变的 wf_definition_admissions 停用所有引用该版本的定义准入,并停用其定时/消息发起入口;需要继续使用的流程应先发布、启用指向受支持版本的新定义,再切换入口。已发布的图、requiredEngineVersion 和旧实例保持不变。直接按旧 definitionVersion 发起也要检查准入,不能绕过停用状态。
- 排空并原子确认:等待所有租户的活动实例、已受理未完成命令、job/timer 和在途执行租约结束;再锁定同一版本记录,确认定义准入全关闭且引用为零,写 retired 并递增 epoch。未完成则保持 draining,不卸载处理器。只有 retired 后才允许移除处理器或最后一个兼容执行池。
发布/启用定义、首次受理发起、退役检查使用固定锁顺序:版本准入记录 → 定义准入记录 → 资源/实例。StartInstance 的准入复查、commandId 首次受理记录及实例/待执行命令写入在同一数据库事务完成,保存所核验的 admission_epoch;检查不能在事务外完成后再无条件提交。关闭准入若先提交,新的发起被拒绝;发起若先提交,其持久化实例或 accepted 命令必然计入排空。因此“计数为零”与关闭准入之间不能插入未登记的新实例。定义版本不可变与可变准入状态分表保存,重新启用也必须经过同一版本锁。
已受理命令重投不算新准入:验证调用主体/租户和载荷一致后返回原结果或继续原 accepted 命令,其运行时引用一直保留到终态;未被引擎受理的外部消息到达后按当前准入检查,不能凭旧消息绕过 draining。retired 不自动重开;若确需恢复旧定义,须先重新部署并验证兼容处理器,经审计将版本切回 enabled、递增 epoch,再显式开放定义准入。
# 6. 持久化执行核心
采用数据库状态机 + 追加事件日志,活跃实例不能只存在 goroutine/内存图中。运行状态、token、任务、join 和定时器为权威持久化状态;事件用于审计与恢复校验,不在 v1 同时承诺任意版本代码的全历史重放。
命令步骤:校验 Actor/tenant/resource → 取得实例/任务 version → 同事务插入唯一 commandId → 更新 token/节点/任务/资源状态 → 追加事件与 outbox → 提交后触发后续异步任务。重复 commandId 返回原结果,载荷不同返回冲突;commandId 与租户/调用主体绑定,不能用他人的 ID探测结果。
并行 fork 为每次分叉分配 scopeId、activationId 和分支集合;join 根据同次激活的预期分支 token 合并,不能只数节点完成次数。循环每轮创建独立激活范围,避免上轮 token 与本轮误合并。首版限制为结构化并行图;复杂非结构化和 inclusive join 在实现、验证后才开放。
定时器存 dueAt、状态、leaseOwner、leaseUntil、fencingToken、version。worker 抢占后在提交前校验最新租约和 fencing;旧 worker 恢复后不能重复触发超时/完成任务。超时和人工审批竞争以同一状态与版本更新裁决,失败方返回已结束/冲突。终止/取消任务同时撤销相关定时器与自动动作授权。
service task 仅调用注册的 ActionHandler。数据库内动作可参与 UnitOfWork;外部副作用通过持久化 job/outbox、业务幂等键、有限重试和人工补偿执行,不在引擎数据库事务内发 HTTP 后假定可以回滚。
# 7. 审批与任务分配
候选策略包括指定用户、角色、部门成员、部门负责人、岗位、发起人本人/主管、发起时自选和业务 adapter 计算。候选必须同租户且有效;空候选默认进入分配异常,不自动通过。候选在节点激活时快照化;人员停用时当前权限仍拒绝审批,管理员按规则转办并审计。
| 模式 | 决策语义 |
|---|---|
| 单人/候选领取 | 有效候选原子 claim 后处理,同一任务只允许一个处理人 |
| 会签 AND | 全部同意才通过,任一拒绝按配置进入拒绝分支 |
| 或签 OR | 任一同意即通过并取消剩余任务;全部拒绝才拒绝 |
| 串行 | 按确定候选顺序逐人处理,拒绝按显式路线执行 |
| 比例/票签 | 阈值、分母、弃权与拒绝规则必须明确,作为后续模式实现 |
不能用“已完成任务数”代替同意人数。或签通过后,迟到的另一审批请求不能覆盖结果。自审批、重复审批、代理身份、转办后的文件权限和候选资格都需要检查。
动作包含同意、拒绝、取消、转办、委派/归还、抄送、催办;驳回、加减签、撤回、跳转和迁移在定义严格语义后分批开放。所有动作写入操作者、原处理人、原因、资源版本、commandId 和时间,不改写历史意见。
# 8. 动态表单
JSON schema 定义字段、数据类型、校验、布局、选项源、节点可见/只读/必填。可视化设计器支持拖拽、属性配置和预览;Go 做权威校验,前端保持同一语义。字段扩展通过组件注册表,不接受任意脚本。
独立行政流程使用内置 form-instance 资源,无需额外业务模块;文件审批说明绑定对应 file/document 资源及表单快照,业务表单通过业务 ResourceAdapter 指向单据。决策变量来自受控快照,客户端不能修改只读字段或伪造用于审批的金额/部门。附件使用正式资源引用而非裸 URL。
# 9. Flowable 功能对标与分期
| 功能 | 自研计划 | 验收重点 |
|---|---|---|
| 定义/版本/发布/停用、实例/历史/审计 | R3.1 | 不可变版本、隔离、崩溃恢复与幂等 |
| 人工任务、服务任务、条件、结构化并行 | R3.1 | token/scope/join 和外部动作可靠性 |
| 会签/或签/串行及多种分配 | R3.1 | 决策计数、候选边界、并发与空候选 |
| 动态表单、在线流程设计 | R3.2 | 双设计器同一模型、后端校验、历史版本 |
| 独立表单资源与内置 adapter | R3.2 | 无业务模块即可设计/发起/审批;提交版本、字段和附件授权可追溯 |
| 文件/文档/业务资源共用引擎 | R3.2 | 固定版本、ACL、临时 grant、结果应用与留存 |
| 运行时语义版本及滚动/回滚保护 | R3.1 起 | 旧实例按固定版本恢复,缺失处理器阻断执行,新旧池协作不改变结果 |
| 定时器、暂停恢复、消息/信号、子流程 | R3.3 | 持久化等待、唯一关联、父子传播和重复投递 |
| 驳回/撤回/加减签、票签、委派转办 | 基础转办/委派 R3.1,其余 R3.3 | 明确边界,取消旧 token/任务/定时器 |
| BPMN inclusive/event gateway、边界事件、补偿等 | R3.4 扩展认证 | 每个构件独立语义/恢复/组合测试,未通过不开放 |
| 运行实例迁移、复杂非结构化流程 | R3.4 专项 | 可迁移节点映射、变量/任务/定时器一致性与回滚 |
| DMN 决策/规则与 CMMN 案例管理 | R5 独立组件对标 | 另建语义、设计器、模型和验收;不是 BPMN 节点自动附送 |
| Flowable REST/数据库/历史实例直接兼容 | 不作为默认兼容合同 | 需要存量迁移时按具体版本另做 importer |
Flowable 文档用于功能语义参照,不作为运行依赖。BPMN 构件参考 (opens new window)。完整对等需继续闭合上述差距;首版不能宣传已支持 Flowable 全部 BPMN/DMN/CMMN 功能。
# 10. 数据、接口与事务
工作流表包含 wf_definitions/versions、wf_definition_admissions、wf_runtime_versions、wf_form_instances/versions、wf_instances、wf_tokens、wf_node_activations、wf_join_states、wf_tasks、wf_task_candidates、wf_decisions、wf_timers、wf_commands、wf_events、wf_resource_bindings、wf_resource_claims、wf_jobs、outbox/inbox。实例、job、timer 和命令保存 engineVersion;首次受理命令同时记录 admission_epoch。定义记录 requiredEngineVersion 与 compilerVersion,可变发起状态存 wf_definition_admissions。wf_runtime_versions 是受控全局运维表,记录版本准入状态、epoch 和乐观锁版本;跨租户引用检查仅由专用部署仓库执行,不向租户 API 暴露免隔离能力。部署/执行池维护运行时支持版本清单与存活状态。documents 包含 document_versions、file_versions、resource_acl、temporary_grants。每个租户表有 tenant_id、必要版本和复合约束。
API 使用 /workflow/definitions、/workflow/forms、/workflow/form-instances、/workflow/instances、/workflow/tasks、/workflow/commands/{id}、/documents/{id}/versions/{version}/submit;独立表单和其他业务发起最终调用同一 StartInstance 命令,不提供另一套表单或文件审批引擎。
同库用户审批结果可直接原子更新文件/单据状态,通常返回 200/201;需外部动作时返回 202 + commandId。未来拆成服务后改用持久化命令与结果事件,并保留资源版本的 CompareAndSet 和对账,不宣称跨库原子。
# 11. 验收与研发闸口
M0 先验证持久化恢复、结构化分支 join、OR并发、定时器 fencing、同库审批事务和两个资源类型,未通过不得先堆设计器页面。
R3 必须演示同一个审批定义分别审批 PDF 固定版本和采购单:在线设计 → 发布 → 发起 → 候选领取/会签/或签 → 通过/拒绝 → 固定版本查看 → 状态回写 → 完整审计。验证跨租户资源、文件换版、任务转办回收权限、删除/留存、审批与超时竞争、旧 worker 恢复、重复命令、节点循环和并行重入。
重启 API/worker 后实例、人工任务和定时器继续正确执行;同一资源用途只存在一个活动实例;旧租约不能提交结果;未支持 BPMN 构件在发布时明确拒绝。七数据库均使用相同工作流合同套件,数据库和消息后端切换不改变审批语义。
另验证无业务 adapter 的独立行政表单:设计发布 → 创建草稿 → 冻结提交版本并发起 → 改动草稿/附件不能改变审批快照 → 结果回写 → 跨租户访问拒绝。数据库事务失败时不留下已提交表单但无实例的半成品。
升级验收覆盖旧版本上存在人工等待、并行 join、循环重入和已排定 timer 的实例:部署新二进制后旧实例仍按原 engineVersion 得到原结果;旧定义的新实例仍使用其 requiredEngineVersion,新发布的定义才使用新语义;不兼容 worker 不领取;缺少必需处理器的发布或回滚被阻止。R3.4 的显式迁移另测映射、检查点和失败恢复,不能代替基础升级用例。
退役/回滚还必须覆盖:没有活动实例但旧定义仍开放时,移除/不兼容回滚被拒绝;停用入口、关闭版本准入并排空后可退役;发布/启用旧定义与退役竞争时只能有一方成功;并发 StartInstance 要么先提交并计入排空,要么收到准入关闭错误,不能产生无处理器实例;已受理命令重投仍返回原结果/继续执行,未受理消息不能越过关闭入口;重新开放 retired 版本必须先恢复兼容池。七数据库均验证相同并发边界。2026-11-02的schema v42已在MySQL/PostgreSQL专项验证持续池和核心六类引用的原子退役基础;schema v43再加入独立外部动作未结算引用、原响应收据和受控补偿。正式外部动作配置和第四类worker进程已接入并有真实双库子进程恢复证据;实际API池及首批HTTP已接入,完整产品查询/历史/页面和滚动/容量仍待R3后续验收,达梦与其他目标库未由此获得认证。见M6实施记录。M0 PoC只作为历史精确版本匹配证据,不视为完整生产引擎。
# 参与人历史与固定原提交读取
schema v45的只读服务独立于执行bundle:当前history权限与参与证据同快照复核,发起人、实际人工操作人和精确活动任务候选分别判定。 候选快照/接收指派/管理补分配不能直接成为历史主体;当前候选仍校验原来源,不能Resolve扩大旧任务。 时间线按时间、来源、原ID合并,元数据不带私有意见/原因/输出;私有条目只向发起人或条目actor开放。
原提交使用独立workflow-original-resource-v1合同及当前bpm:form:history-read,只认原所有者或精确投票/代理归还版本。
按原定义ReadFields投影不可变提交与原schema,不读当前变量、最终汇总或最新草稿,不复活grant/slot,不推进版本。
当前持有人、领取/转办/委派或管理权限不授予永久历史正文;没有ReadFields的旧档案拒绝处理人正文而非默认全字段。
目录退役后可用持久摘要及已实现的纯编译器解释历史,不能因此执行旧模型。未知解释版本明确拒绝,不猜最新语义。
当前没有完整节点输入快照;投票/归还只记录各次输出,不能把原提交、当前变量或最终Outcome冒充节点当时完整输入。 这些后端合同不替代真实审批页面或完整跨进程/滚动/容量验收,见工作流API和M6证据。