--- title: Elsa Workflows 3 date: 2026-06-17 13:21:19 tags: --- # Elsa Workflows 3 与 Activiti 5.14 对比指南 ## 核心架构差异 | 概念 | Activiti 5.14 (Java) | Elsa Workflows 3 (.NET) | |------|---------------------|------------------------| | **流程定义** | BPMN 2.0 XML | C# 代码 / JSON / 可视化设计器 | | **任务模型** | 内置 UserTask 概念 | 阻塞活动 (Blocking Activity) + 书签 (Bookmark) | | **状态管理** | HistoryService 审计日志 | 自定义审计实现 | | **回滚支持** | 无原生支持,需自定义 | 有 Alterations API | | **API 风格** | Java 服务注入 | .NET 依赖注入 | --- ## 功能逐一对比 ### 1. 流程定义 (Process Definition) **Activiti 5.14:** ```java // 使用 BPMN 2.0 XML 文件 // process.bpmn20.xml // 部署流程定义 repositoryService.createDeployment() .addClasspathResource("process.bpmn20.xml") .deploy(); ``` **Elsa Workflows 3:** ```csharp // 方式1: C# 代码定义 public class ApprovalWorkflow : Workflow { public override void Build(IWorkflowBuilder builder) { builder .HttpEndpoint("/start-approval", HttpMethod.Post) .Then(activity => { activity.ApprovalMessage = new("请审核此申请"); }) .WriteHttpResponse(HttpStatusCode.OK, "流程完成"); } } // 方式2: JSON 定义 { "definitionId": "approval-workflow", "name": "审批流程", "version": 1, "root": { "type": "Elsa.Flowchart", "activities": [ { "id": "start", "type": "Elsa.HttpEndpoint", "path": "/start-approval" } ] } } // 方式3: 使用 Elsa Studio 可视化设计器 // 发布流程定义 await workflowDefinitionsApi.SaveAsync(new SaveWorkflowDefinitionRequest { Model = new WorkflowDefinitionModel { DefinitionId = "approval-workflow", Name = "审批流程", IsPublished = true }, Publish = true }); ``` --- ### 2. 启动一个特定的流程 (Start a Specific Process) **Activiti 5.14:** ```java Map variables = new HashMap<>(); variables.put("applicant", "张三"); variables.put("amount", 5000); ProcessInstance instance = runtimeService .startProcessInstanceByKey("approvalProcess", variables); String processInstanceId = instance.getId(); ``` **Elsa Workflows 3:** ```csharp // 方式1: 直接执行 (同步) var workflowRunner = serviceProvider.GetRequiredService(); var result = await workflowRunner.RunAsync(input: new Dictionary { ["Applicant"] = "张三", ["Amount"] = 5000 }); // 方式2: 异步调度 (推荐用于生产环境) var dispatcher = serviceProvider.GetRequiredService(); await dispatcher.DispatchAsync(new DispatchWorkflowInstanceRequest { DefinitionId = "approval-workflow", Input = new Dictionary { ["Applicant"] = "张三", ["Amount"] = 5000 } }); // 方式3: 通过 HTTP API // POST /api/workflow-definitions/approval-workflow/dispatch ``` --- ### 3. 获取下一流程的流程以及对应审核人列表 (Get Next Tasks and Approvers) **Activiti 5.14:** ```java // 获取当前流程实例的任务 List tasks = taskService.createTaskQuery() .processInstanceId(processInstanceId) .list(); for (Task task : tasks) { System.out.println("任务名称: " + task.getName()); System.out.println("处理人: " + task.getAssignee()); System.out.println("创建时间: " + task.getCreateTime()); } // 获取候选任务 (待认领) List candidateTasks = taskService.createTaskQuery() .taskCandidateUser("userId") .list(); ``` **Elsa Workflows 3:** ```csharp // Elsa 没有内置的"任务"概念,需要通过书签(Bookmarks)实现 // 查询流程实例的书签 (类似当前待处理节点) var bookmarks = await bookmarkFinder .FindByWorkflowInstanceIdAsync(workflowInstanceId); foreach (var bookmark in bookmarks) { Console.WriteLine($"书签名称: {bookmark.Name}"); Console.WriteLine($"活动类型: {bookmark.ActivityTypeName}"); // 自定义: 需要从书签的 Payload 中提取审核人信息 } // 推荐方案: 使用自定义状态管理 public class ApprovalStateTracker { private readonly IWorkflowInstancesApi _workflowInstancesApi; public async Task> GetNextApproversAsync( string workflowInstanceId) { // 从工作流变量中获取当前步骤和审核人 var instance = await _workflowInstancesApi.GetAsync(workflowInstanceId); var currentStep = instance.Variables["CurrentStep"]; var approvers = instance.Variables["NextApprovers"] as List; return new List { new ApprovalStep { StepName = currentStep.ToString(), Approvers = approvers } }; } } ``` --- ### 4. 提交给下一流程至下一个审核人审核 (Submit to Next Approver) **Activiti 5.14:** ```java // 认领任务 taskService.claim(taskId, "userId"); // 完成任务并传递变量 Map variables = new HashMap<>(); variables.put("approved", true); variables.put("comment", "同意申请"); taskService.complete(taskId, variables); ``` **Elsa Workflows 3:** ```csharp // 方式1: 通过书签ID恢复 var input = new Dictionary { ["Decision"] = "Approved", ["ApprovedBy"] = "manager@example.com", ["Comment"] = "同意申请" }; var result = await workflowResumer.ResumeAsync(bookmarkId, input); // 方式2: 通过 Stimulus (匹配书签) var stimulus = new { ApprovalId = approvalId, StepName = "ManagerReview" }; await workflowResumer.ResumeAsync(stimulus, input); // 方式3: 通过 HTTP API (使用令牌URL) // POST /elsa/api/bookmarks/resume?t=ENCRYPTED_TOKEN // Body: { "input": { "Decision": "Approved", "ApprovedBy": "manager@example.com" } } // 自定义审核活动示例 public class WaitForApprovalActivity : Activity { protected override async ValueTask ExecuteAsync( ActivityExecutionContext context) { var bookmarkArgs = new CreateBookmarkArgs { BookmarkName = "WaitForApproval", Payload = new Dictionary { ["ApprovalId"] = context.Get(ApprovalId), ["CurrentStep"] = context.Get(CurrentStep) }, Callback = OnApprovalReceivedAsync, AutoBurn = true // 使用一次后自动销毁 }; context.CreateBookmark(bookmarkArgs); } private async ValueTask OnApprovalReceivedAsync( ActivityExecutionContext context) { var decision = context.WorkflowInput["Decision"]; var approvedBy = context.WorkflowInput["ApprovedBy"]; // 记录审核历史 await RecordApprovalHistoryAsync(decision, approvedBy); // 根据决策决定下一步 if (decision.ToString() == "Approved") { await context.CompleteActivityWithOutcomesAsync("Approved"); } else { await context.CompleteActivityWithOutcomesAsync("Rejected"); } } } ``` --- ### 5. 获取退回到之前流程以及对应审核人列表 (Get Previous Tasks for Rollback) **Activiti 5.14:** ```java // 获取历史任务 List historicTasks = historyService .createHistoricTaskInstanceQuery() .processInstanceId(processInstanceId) .orderByHistoricTaskInstanceEndTime().desc() .list(); for (HistoricTaskInstance task : historicTasks) { System.out.println("任务名称: " + task.getName()); System.out.println("处理人: " + task.getAssignee()); System.out.println("开始时间: " + task.getStartTime()); System.out.println("结束时间: " + task.getEndTime()); } ``` **Elsa Workflows 3:** ```csharp // 需要自定义审计日志实现 public class WorkflowAuditService { private readonly IAuditLogRepository _auditLogRepo; public async Task> GetApprovalHistoryAsync( string workflowInstanceId) { return await _auditLogRepo .GetByWorkflowInstanceIdAsync(workflowInstanceId); } public async Task RecordApprovalAsync( string workflowInstanceId, string stepName, string approver, string decision, string comment) { await _auditLogRepo.AddAsync(new AuditEntry { WorkflowInstanceId = workflowInstanceId, StepName = stepName, Approver = approver, Decision = decision, Comment = comment, Timestamp = DateTime.UtcNow }); } } // 在审核活动的回调中记录历史 private async ValueTask OnApprovalReceivedAsync( ActivityExecutionContext context) { var decision = context.WorkflowInput["Decision"]; var approvedBy = context.WorkflowInput["ApprovedBy"]; // 记录审核历史 await _auditService.RecordApprovalAsync( context.WorkflowExecutionContext.Id, context.Get(CurrentStep), approvedBy.ToString(), decision.ToString(), context.WorkflowInput["Comment"]?.ToString()); // 继续流程... } ``` --- ### 6. 退回到之前流程及其对应的审核人 (Rollback to Previous Task) **Activiti 5.14:** ```java // 方案1: 使用流程变量 + 网关循环 // 在 BPMN 中设计回退路径 // 需要在 BPMN XML 中定义循环逻辑 // 方案2: 使用信号事件 runtimeService.signalEventReceived("rollbackSignal", variables); // 方案3: 自定义命令 (不推荐,侵入性强) ``` **Elsa Workflows 3:** ```csharp // 方案1: 使用 Alterations API (推荐) await workflowAlterationService.AlterAsync(workflowInstanceId, alteration => { // 修改工作流变量 alteration.SetVariable("CurrentStep", "ManagerReview"); alteration.SetVariable("NeedsRollback", true); }); // 方案2: 取消当前书签,创建新书签 public async Task RollbackToStepAsync( string workflowInstanceId, string targetStep, string targetApprover) { // 1. 获取当前书签 var bookmarks = await bookmarkFinder .FindByWorkflowInstanceIdAsync(workflowInstanceId); // 2. 取消当前书签 (通过恢复并传递回退指令) var input = new Dictionary { ["Decision"] = "Rollback", ["TargetStep"] = targetStep, ["TargetApprover"] = targetApprover }; // 3. 恢复工作流,触发回退逻辑 await workflowResumer.ResumeAsync(bookmarks.First().Id, input); } // 方案3: 使用自定义回退活动 public class RollbackActivity : Activity { protected override async ValueTask ExecuteAsync( ActivityExecutionContext context) { var targetStep = context.Get(TargetStep); var targetApprover = context.Get(TargetApprover); // 更新流程状态 context.SetVariable("CurrentStep", targetStep); context.SetVariable("CurrentApprover", targetApprover); // 记录回退历史 await RecordRollbackAsync(targetStep, targetApprover); // 完成活动,流程将根据条件网关回到目标步骤 await context.CompleteActivityAsync(); } } ``` --- ### 7. 流程的完结 (Process Completion) **Activiti 5.14:** ```java // 方式1: 自然结束 (所有任务完成) taskService.complete(lastTaskId); // 方式2: 手动终止 runtimeService.deleteProcessInstance(processInstanceId, "用户取消"); // 方式3: 结束事件触发 // 在 BPMN 中定义 endEvent ``` **Elsa Workflows 3:** ```csharp // 方式1: 自然结束 (所有活动完成) // 当工作流执行到 End 活动时自动结束 // 方式2: 手动取消 var workflowCanceller = serviceProvider.GetRequiredService(); await workflowCanceller.CancelAsync(workflowInstanceId); // 方式3: 通过 API // DELETE /api/workflow-instances/{instanceId} // 方式4: 使用 End 活动 builder .Then() .Then() // 工作流在此结束 .WriteHttpResponse(HttpStatusCode.OK, "流程已完成"); ``` --- ## 迁移建议 ### 设计模式推荐 对于审批流程场景,建议在 Elsa 中采用以下设计: ```csharp public class ApprovalWorkflow : Workflow { public override void Build(IWorkflowBuilder builder) { builder // 开始 .HttpEndpoint("/start", HttpMethod.Post) .Then() // 审核循环 .Then(activity => { activity.ApprovalMessage = new("请审核"); }) .If(context => context.GetInput("Decision") == "Approved", then => then.Then(), @else => @else.Then()) // 结束 .WriteHttpResponse(HttpStatusCode.OK); } } // 状态跟踪服务 public class ApprovalTrackingService { // 记录每个步骤的审核人 public async Task RecordStepAsync( string workflowId, ApprovalStep step) { // 存储到数据库 } // 获取历史步骤 (用于回退) public async Task> GetHistoryAsync( string workflowId) { // 从数据库查询 } // 获取下一个审核人 public async Task> GetNextApproversAsync( string workflowId) { // 根据业务规则计算 } } ``` --- ## 关键差异总结 | 功能 | Activiti | Elsa | 迁移注意事项 | |------|----------|------|-------------| | 任务查询 | 原生支持 | 需自定义 | 需要实现状态跟踪服务 | | 审计日志 | HistoryService | 需自定义 | 需要实现审计日志模块 | | 回滚 | 无原生支持 | Alterations API | Elsa 有更好的扩展性 | | 任务分配 | candidateUser/Group | 需自定义 | 需要在书签中存储分配信息 | --- ## 总结 Elsa Workflows 3 作为 .NET 工作流引擎,虽然在任务管理方面不如 Activiti 成熟,但提供了更灵活的扩展机制。迁移过程中需要: 1. **重新设计任务模型**: 使用阻塞活动 + 书签替代内置 UserTask 2. **实现审计服务**: 自定义审核历史记录 3. **利用 Alterations API**: 实现流程回退功能 4. **设计状态管理**: 通过工作流变量跟踪审批状态 整体而言,Elsa 的架构更适合现代 .NET 应用,但需要更多的自定义实现来满足审批流程的完整需求。