Workflow 的核心设计是“引擎语义与产品接入分离”。Framework 只认识定义、运行对象和端口;Platform.Workflow 把当前用户、统一授权、平台通知和 Endpoint 接进来;Infrastructure 决定数据库;Host 决定是否启用以及运行在哪个进程。
1. 分层与依赖方向
Engine 不引用 Platform、Host、EF Core、SqlSugar 或 Dapper。Abstractions 当前仍引用 Domain 和 Persistence.Metadata,用于结果模型与编译期持久化元数据;“零 ORM”不等于“零框架依赖”。
2. 四类核心模型
| 模型 | 作用 |
|---|---|
| WorkflowDefinition | 不可变 JSON DSL 快照,含节点、边、变量、Checksum |
| WorkflowDeployment | DefinitionKey + Tenant + Office 到 Active/Previous DefinitionId 的绑定 |
| ProcessInstance / Execution | 业务流程快照与 Token 执行位置 |
| WorkflowTask / ActivityRecord / TimerJob | 人工工作、轨迹证据和到期动作 |
实例锁定 DefinitionId,Execution 表示并行路径和挂起点,Task 是人机交互入口。不能只更新 Instance.CurrentNodeId 来“跳流程”,因为 Execution、Task、FlowState、轨迹、Timer 和缓存会失去一致性。
3. 聚焦服务端口
RuntimeTransition 负责主状态推进;Handoff 负责转办、委派、加签;Migration 负责指定节点发起和申请人转交;BatchOperation 负责终止、取消、挂起、催办、代批和优先级。兼容门面仍存在,但新增用例不应继续把所有方法注入一个巨型服务。
4. 命令与查询存储
IPersistenceStore 是写模型和部分简单查询的完整端口,包含定义、部署、实例、Execution、Task、候选、轨迹、Timer、历史和统计。IWorkflowQueryStore 面向待办分页、Timeline 和报表原始集合。
5. 一次完成任务的事务边界
具体回调位置随用例不同,不能假定所有外部副作用都严格在 commit 之后。评审必须打开对应 partial 文件核对,而不是依赖上图替代源码。
通知与监听器的失败语义需要特别强调:平台 INotificationChannel 实现逐接收人写入站内信与 CAP Outbox,任一接收人失败即计数并在结尾抛出;分发器记录日志后重抛,EventDispatcher 同样把监听器异常向上传播——因此一次提交要么连同通知事实一起成功,要么整体失败回滚。“流程成功但通知悄悄丢了”不再是合法状态;换来的是通知通道故障会阻断审批动作本身。
6. 乐观并发
实例 Version 是同一流程写操作的共享并发边界。即使转办、委派、加签或代批只改 Task,运行时也先推进实例 Version。EF Core 把 expectedVersion 设为 OriginalValue;SqlSugar 通过带版本谓词更新。Engine 捕获 WorkflowConcurrencyException、DBConcurrencyException 和 EF 并发异常并返回 ConcurrencyConflict。
Result result = await transitions.CompleteTaskAsync( taskId, currentUserId, "同意", variables: null, cancellationToken);
// 并发冲突说明当前页面看到的任务版本已经过期。if (result.IsFailure && result.Error.Code == "Workflow.Runtime.ConcurrencyConflict"){ // 重新读取任务和进度;不要盲目循环重试写操作。 return Result.Failure("任务已由其他操作改变,请刷新后确认。");}并发控制不提供幂等响应缓存。两次相同完成请求中,一次成功,另一次可能得到 TaskNotPending 或 ConcurrencyConflict。
7. 缓存
ExecutionGraphCache 是引擎实例内编译图缓存。IWorkflowCache 缓存定义、部署和待办计数;Noop 是默认实现。平台 AddWorkflowFusionCache 始终启用 L1,发现 Redis Multiplexer 和 IDistributedCache 时追加 L2 与 backplane。
缓存失效属于正确性路径:发布/回滚失效部署绑定,任务变化失效相关用户待办。新增候选人、申请人转交或批量操作时必须验证所有受影响用户,而不仅是原 Assignee。
8. 租户解析
定义部署按 Office 精确、租户 ALL、PLATFORM/ALL 解析;运行记录带 TenantId。平台 Handler 从 ICurrentUser 取租户。JobHost 通过 IWorkflowTimerJobTenantResolver 忽略普通查询过滤器查到父实例租户,再建立系统 CurrentUser 与 ORM filter scope。
历史的部分公开端口只接收 businessKey 或 instanceId,没有显式 tenantId;隔离依赖 Adapter 当前上下文过滤。独立宿主若没有等价租户过滤器,必须在端口实现补充强制谓词。
9. 组合模式
API Host 先注册持久化 Adapter,再调用 AddBitzOrcasWorkflowEngine;没有 IPersistenceStore 时该扩展 no-op,WorkflowEndpointGroup 也不会映射手写路由。生成端点通过 RequireService 做同类条件注册。
JobHost 只在 hasSqlSugar 时注册 Timer 处理链:WorkflowBackgroundJobsExtensions.AddWorkflowBackgroundJobs 登记 SqlSugarWorkflowPersistenceStore、计时器租户执行作用域、系统权限检查器和 INotificationChannel → WorkflowNotificationAdapter——后台催办通知与业务动作、Outbox、Fired 状态在同一执行作用域内原子提交。定时器处理器 WorkflowTimerProcessor 以 ILicenseGate 为必选依赖,推进到期 Timer、超时升级(escalate)与超时转办(transfer)。
10. 配置与许可门禁的当前真实状态
WorkflowEngineConfiguration 声明 EnableHistory、EnableActivityRecord、EnableListeners、DefaultPriority、EnableMultiTenancy,但除保存到 Engine.Configuration 外没有消费。设置 false 不会关闭对应行为,DefaultPriority 也不会改变实例默认优先级——不要把它当作可用开关。
workflow.runtime Feature 则已被强制执行:WorkflowRuntimeLicenseGuard 在 RepositoryService、ManagementService、WorkflowHistoryCore 的每次写入前以及定时器处理链的每轮扫描前通过 ILicenseGate 评估它,失败关闭。引入新工作流消费方(嵌入式宿主)时必须提供正式 Runtime License 配置,否则引擎在第一个写命令上就会拒绝服务。
11. 架构测试重点
- Engine 不得引用 ORM 或 Platform。
- Platform Handler 依赖聚焦端口,不回退巨型兼容门面。
- EF Core 与 SqlSugar 实现完整 IPersistenceStore。
- 更新方法都带 expectedVersion。
- 生成元数据、表名、租户过滤和事务行为双 Provider 对齐。
- API Shell 缺少 Store 时不会半组合引擎。
- JobHost 可信租户作用域在异常后正确恢复。
12. 源码核查
# 项目依赖与端口分布。rg -n "ProjectReference|PackageReference" src/Framework/BitzOrcas.Workflow/**/**.csproj
# 并发与事务覆盖面。rg -n "ExecuteInTransactionAsync|ExecuteWithConcurrencyGuardAsync|expectedVersion" src/Framework/BitzOrcas.Workflow src/Framework/BitzOrcas.Infrastructure.* -g '*.cs'
# 组合根差异。rg -n "AddBitzOrcasWorkflowEngine|AddWorkflowBackgroundJobs|INotificationChannel" src/Hosts -g '*.cs'