BitzOrcas.Workflow.Migrator 是平台级 Tooling,只迁移流程定义。它按 source 读取 BitzOrcas 五表或 Saury T_WorkflowDefinition* 一组表,转换成新引擎 JSON DSL。非 dry run 时通过真实 Workflow IRepositoryService 执行 DeployAsync 与 PublishDeploymentAsync。Saury 表结构知识只留在 Tooling,不得进入 Framework 或 Platform 运行时。
迁移阶段
读图:配置批准后先定来源,再分两路读表。BitzOrcas 走既有五表转换器,行为保持不变。Saury 走步骤树转换器并额外产出 sidecar 报告。output-dir 只影响是否落盘;DryRun 决定是否部署。警告不增加失败计数,转换成功也不等于业务语义等价。
准备
- 源库是只读 snapshot,记录 snapshot 时间与 schema 版本。
- 目标是隔离迁移库,不是生产租户数据库。
- 选定
source。不传时自动探测:存在FlowDefine用bitzorcas,存在T_WorkflowDefinition用saury;两张表都在时回退bitzorcas;都没有则抛错退出 99。 flow-names写成精确列表。BitzOrcas 过滤FlowDefine.Name;Saury 过滤T_WorkflowDefinition.Name。空值会迁移全部启用定义。- Saury 若要按分所收窄权限和租户绑定,准备整数
office-id。 - 冻结旧定义编辑窗口;本工具不锁源表。
- 明确实例切换策略;本工具不搬迁
T_Workflow或 BitzOrcas 在途实例。
# 构建工具与 Workflow 相关项目,先暴露 metadata/adapter 漂移。dotnet build src/Tooling/BitzOrcas.Workflow.Migrator \ --configuration Release
# 转换器与 DSL 编译单测。dotnet test tests/BitzOrcas.Workflow.Migrator.Tests \ --configuration Release缺少任一连接串时打印 usage 并返回 1。没有独立 help 开关。
配置接线
程序加载 appsettings.json、appsettings.local.json、MIGRATOR_ 环境变量和命令行,绑定 Migrator section,再由 MigratorCli.ApplyOverrides 叠加 kebab-case 开关。优先级是:命令行开关大于已绑定的 JSON 与环境变量。
{ "Migrator": { "SourceConnectionString": "Server=legacy-staging;Database=WorkflowSnapshot;User Id=wf_migrator;Password=Secr3t!P@ss;TrustServerCertificate=True", "TargetConnectionString": "Server=target-isolated;Database=WorkflowMigration;User Id=wf_migrator;Password=Secr3t!P@ss;TrustServerCertificate=True", "TenantId": "PLATFORM", "DryRun": true, "FlowNames": "MatterApproval,SealApply", "Source": "saury", "OfficeId": "1", "OutputDir": "/tmp/wf-migrator-out" }}把含密码的文件保存为项目目录下的 appsettings.local.json,确认被 Git 忽略。连接串中的 Password/Pwd 在摘要日志会掩码,但异常、shell、驱动日志仍按敏感制品处理。
| 开关 | 配置键 | 含义 |
|---|---|---|
source-connection / target-connection | SourceConnectionString / TargetConnectionString | 源库与目标库 |
source | Source | bitzorcas 或 saury;空则探测 |
office-id | OfficeId | 仅 saury:过滤权限与 T_TenantWorkflowDefinition |
output-dir | OutputDir | 写出 {key}.v{version}.json 与 sidecar 报告 |
flow-names | FlowNames | 逗号分隔流程名 |
tenant-id | TenantId | 目标租户,默认 PLATFORM |
dry-run | DryRun | 只转换不部署 |
实施
在工具项目目录运行,确保 local 配置被加载。
cd src/Tooling/BitzOrcas.Workflow.Migrator
# 既有 BitzOrcas 调用:不传 source,行为仍走五表路径。dotnet run --configuration Release -- \ --dry-run \ --output-dir /tmp/wf-bitzorcas
# Saury 产品库:显式来源,可按流程名与分所收窄。dotnet run --configuration Release -- \ --source saury \ --dry-run \ --office-id 1 \ --flow-names MatterApproval \ --output-dir /tmp/wf-sauryoutput-dir 对两种来源都写 {key}.v{version}.json、{key}.v{version}.migration-report.md,并再写一份 {key}.migration-report.md。不传则只打印控制台。
BitzOrcas 五表路径
FlowDefine 读取 IsDeleted = 0 OR IsDeleted IS NULL。随后按定义 ID 读取按 NodeStep 排序的节点、LaneType = 0 的连线、经 FlowControl join 的条件,以及全部审批人。FlowDefinitionConverter 的节点、会签、退回、条件映射保持原行为,不因 Saury 来源改写。
| Legacy NodeType | JSON DSL type | 说明 |
|---|---|---|
Start | startEvent | 起点 |
Approver | userTask | 审批任务 |
Finish | endEvent | 终点 |
| 其他值 | userTask | 仅 warning,并默认降级 |
条件支持 GreaterThan、GreaterThanOrEqual、LessThan、LessThanOrEqual、Equal、NotEqual。Contains 返回空,边不带 condition。
Saury 定义表路径
SauryFlowReader 读取 T_WorkflowDefinition、T_WorkflowDefinitionStep、T_WorkflowDefinitionState、T_WorkflowPermission、T_WorkflowOrganizationUnits、T_TenantWorkflowDefinition。T_Workflow 实例表不读。只取 IsActive = 1 的定义,按 Name、Version 排序。
版本解析对齐 DefinitionManager.GetDefinitionState:优先 T_TenantWorkflowDefinition 指定的 DefinitionId,否则回退 Version = 1,再按 Version 降序。发布时:无绑定则把最高 Version 发布到 officeId 为空的租户绑定;有绑定则按 OfficeId 发布对应版本。
| Saury | 新 DSL | 规则 |
|---|---|---|
Name / Version / 显示名 | key / version / name | key = SanitizeKey(Name),与 BitzOrcas 清理规则一致 |
| 顶层步骤链 | nodes + edges | 链首补 start,链尾补 end |
WaitFor | userTask | 权限按 WaitForId 映射;提供 office-id 时按 OfficeId 过滤;无权限记录不设 participants 并记 warning |
T_WorkflowOrganizationUnits | dataScope | OfficeLevel 1/2/3 对应 Tenant/Office/Department;非空组织机构列表对应 Custom |
If | exclusiveGateway | 连续 If 收成一个网关;Condition 做机械转译 |
类名为 Return / Redo 的动作 | rollback | Return 倾向 mode: start,Redo 用 prev |
类名为 Approve / Confirm / End 等 | 折叠为边 | 业务副作用不进 DSL,StateId 进报告 |
链首 Apply | 折叠 | 申请人提交属于发起方逻辑 |
While / ForEach / Schedule / Recur | 不迁移 | 记 warning |
T_WorkflowDefinitionState | sidecar 状态映射表 | 不进 DSL |
WorkflowCore.Primitives. 前缀判定控制原语,兼容有无程序集后缀。Condition 只转译 Data.X / data.X、比较与逻辑运算符、数字/引号字符串/true/false/null。方法调用如 .Contains(value) 不转译:该边不带 condition,原文进 warning 与 sidecar。
sidecar 迁移报告
Saury 每条定义产出一份 Markdown,供业务模块实现 IBusinessIntegrationCallback.OnStatusChangedAsync 时对照 BusinessStatusChange.ToFlowState 与 ToNodePrefix:
- 状态映射表:老 StateId / Name / DisplayName / IsAudit / Category 对照新 NodePrefix 与建议 FlowState。
- Hook 清单:每个业务步骤的 Name、DisplayName、StepType 全名、IsAudit、所在环节、建议写回状态。StepType 直接来自库,不解析 Saury 源码。
- 转译失败清单:未转译 Condition、未迁移控制原语、无 participants 的 userTask、降级的 dataScope。
BitzOrcas 路径在指定 output-dir 时也会写报告,但状态映射与 Hook 清单为空,失败清单只收 converter warnings。
部署与发布
非 dry run 通过 SqlSugar Workflow store 构建引擎。DeployAsync(key, name, jsonDsl, "migrator", tenantId) 创建不可变版本;再用返回的 DefinitionId 调用 PublishDeploymentAsync。BitzOrcas 的 office 参数仍传 null。Saury 对每个 Office 绑定传入该分所字符串。
这两步不是本工具显式包裹的原子事务。Deploy 成功而 Publish 失败时,目标可能留下未发布版本;重跑前必须查询目标版本与 binding。
# 确认隔离目标后关闭 DryRun。dotnet run --configuration Release -- \ --source saury \ --output-dir /tmp/wf-saury \ > "$MIGRATION_EVIDENCE/workflow-deploy.log" 2>&1
rg -n '部署成功|部署失败|发布部署绑定失败|失败:' \ "$MIGRATION_EVIDENCE/workflow-deploy.log"验证
- dry-run 每个 JSON 能被
DefinitionCompiler反序列化并编译;见tests/BitzOrcas.Workflow.Migrator.Tests。 - sidecar 三节齐全,Hook 的 StepType 与库中类型全名一致。
- 同一输入两次运行,JSON 与报告逐字节一致。
- 不带
source的既有调用仍走 BitzOrcas 五表路径。 - 无权限 WaitFor 只有 warning,不伪造审批人。
- 方法调用条件没有被写进边。
- 目标租户能按 published binding 启动一条新实例。
# 转换器单测与 Workflow 相关架构合同。dotnet test tests/BitzOrcas.Workflow.Migrator.Tests \ --configuration Releasedotnet test tests/BitzOrcas.Architecture.Tests \ --configuration Release \ --filter 'FullyQualifiedName~Workflow'回滚
- 保留 dry-run 日志、JSON 与 sidecar 作为审批制品。
- 隔离目标上的未发布 Definition 可删除或停用;不要在生产直接重跑覆盖。
- 已 Publish 的绑定用
RollbackDeploymentAsync回到PreviousDefinitionId,已发起实例仍锁定旧快照。 - 源库保持只读 snapshot,不在本工具内回写 Saury 或 BitzOrcas 旧表。
- 业务回调代码属于业务模块,不随本工具回滚。
退出码
| Code | 当前含义 | 自动化处理 |
|---|---|---|
| 0 | 所有流程未抛异常;可能仍有 warnings | 继续语义审查,不自动放行 |
| 1 | 连接串配置缺失 | 阻塞,修配置接线 |
| 2 | 至少一个流程转换或部署失败 | 阻塞,对账部分成功 |
| 3 | Ctrl+C / cancellation | 阻塞,检查部分部署 |
| 99 | 未处理异常或来源探测失败 | 阻塞,保存掩码日志 |
批量运行遇到单个流程失败会继续处理其他流程,因此 code 2 代表可能部分成功。
交付清单
- snapshot 源和隔离目标身份可追溯;
- 显式或探测后的
source已记录; - dry-run 每个 warning 都有接受、修复或阻塞结论;
- Saury sidecar 三节已交给业务模块做回调实现;
- definition key、版本、Deploy 与 Publish binding 在目标核对;
- 声明不迁移实例,并完成新旧流量切换方案;
- 日志、JSON 与报告按敏感制品存储。