项目中Camunda架构分析 项目中 Camunda 的使用分析基于huindata-rdms-parent-ksxy项目源码分析 该项目是一个基因检测实验室 LIMS 系统慧因数据一、项目背景这是一个什么系统这是一个基因检测实验室信息管理系统LIMS管理从销售签约 → 样本接收 → 实验检测 → 数据分析 → 报告签发的全流程。核心业务链条关键实体实体说明对应的流程RdmsContract合同与客户签订销售合同approve_contractRdmsRequest申请单检测申请含样本信息approve_requestRdmsTest实验具体的实验检测test_xxx(17种)RdmsAnalysis分析数据分析analysis_xxx(4种)RdmsReport报告检测报告approve_report二、项目中 Camunda 的架构总览2.1 模块依赖关系┌─────────────────────────────────────────────────────────────────────┐ │ rdms-starter (启动器) │ │ ┌──────────────────────────────────────────────────────────────┐ │ │ │ camunda-bpm-spring-boot-starter-rest:3.3.4 │ │ │ │ // 提供 REST API 引擎自动注册 /engine-rest/* 端点 │ │ │ └──────────────────────────────────────────────────────────────┘ │ └────────────────────────────┬────────────────────────────────────────┘ │ ┌────────────────────────────▼────────────────────────────────────────┐ │ rdms-core (核心业务层) │ │ ┌──────────────────────────────────────────────────────────────┐ │ │ │ camunda-bpm-spring-boot-starter:3.3.4 │ │ │ │ // 嵌入式引擎自动注册 Camunda Service Bean │ │ │ └──────────────────────────────────────────────────────────────┘ │ │ ┌──────────────────────────────────────────────────────────────┐ │ │ │ camunda-engine:7.11.0 │ │ │ │ // 核心引擎 APIRuntimeService / TaskService / ... │ │ │ └──────────────────────────────────────────────────────────────┘ │ │ │ │ 业务 Service: │ │ RdmsContractService / RdmsRequestService / RdmsTestService / ... │ │ └─ 启动流程、设置变量、关联 businessKey │ └────────────────────────────┬────────────────────────────────────────┘ │ ┌────────────────────────────▼────────────────────────────────────────┐ │ rdms-listener (事件监听层) │ │ WorkflowGlobalEventListener │ │ └─ EventListener 监听 DelegateTask / DelegateExecution │ │ └─ 任务创建时初始化变量、流程结束时更新状态 │ │ │ │ RdmsWorkflowListener (RabbitMQ) │ │ └─ 监听 MQ 消息触发流程启动部分已废弃改用直接调用 │ └────────────────────────────┬────────────────────────────────────────┘ │ ┌────────────────────────────▼────────────────────────────────────────┐ │ rdms-rest (API 层) │ │ DmsWorkflowRest /dms/workflow/* │ │ └─ 审批/挂起/删除/查询流程等操作 │ │ RdmsContractWorkflowRest │ │ RdmsRequestWorkflowRest │ └─────────────────────────────────────────────────────────────────────┘2.2 版本信息组件版本camunda-engine7.11.0camunda-bpm-spring-boot-starter3.3.4Spring Boot2.4.1Java1.82.3 配置详解application.yml(rdms-starter/src/main/resources/env/dev/application.yml):camunda: bpm: admin-user: id: admin # Camunda Web 应用管理员 password: admin firstName: admin filter: create: All task # 默认任务过滤器 eventing: execution: true # 允许 EventListener 监听 DelegateExecution history: true # 允许 EventListener 监听 HistoryEvent task: true # 允许 EventListener 监听 DelegateTaskbootstrap.yml:spring: jersey: application-path: camunda # 把 Camunda REST API 挂载到 /camunda 路径三、三种流程类型详解项目中有三类共 26 个流程定义3.1 审批流程approve/ 目录用途业务单据的多人审核通过/驳回。包含approve_contract、approve_order、approve_quotation、approve_request、approve_reportBPMN 结构以approve_contract为例用 Modeler 打开看到开始 → [商务审核] → 排他网关 ── actionAPPROVED → [财务审核] → 排他网关 ── 通过 → 结束(通过) └─ actionREJECT → 结束(驳回) └─ 驳回 → 结束(驳回)关键机制!-- 审批通过/驳回时通过 SPEL 表达式调用 Spring Bean -- endEvent idEndEvent_1us6mif name通过 extensionElements camunda:executionListener expression${rdmsContractService.approved(data.id)} eventstart / /extensionElements /endEvent ​ endEvent idEndEvent_1xdrk7n name驳回 extensionElements camunda:executionListener expression${rdmsContractService.reject(data.id)} eventstart / /extensionElements /endEvent关键点BPMN 中的${rdmsContractService.approved(data.id)}会直接调用 Spring 容器中的RdmsContractServiceBean无需写任何 JavaDelegate 类。3.2 检测流程test/ 目录用途定义不同检测技术的实验步骤。包含17种test_ngs、test_pcr、test_wes、test_sanger、test_fish、test_ddPcr、test_qPcr、test_steroidHormone等BPMN 结构以test_ngs为例开始 → [提取] → [建库] → [杂交] → [上机] → 结束每一步都是userTask由不同的角色完成提取员→ 样本核酸提取建库员→ 文库构建杂交员→ 目标区域捕获上机员→ 上机测序3.3 分析流程analysis/ 目录用途下机数据后的生物信息分析步骤。包含4种analysis_test、analysis_manual、analysis_anke_test、analysis_medicine_integrationBPMN 结构开始 → [质控] → [比对] → [变异检测] → [注释] → 结束四、Camunda 在项目中的六种使用模式模式 1启动流程实例在哪里RdmsTestService.startWorkflowInstance()、RdmsContractService、RdmsRequestService、RdmsReportService、RdmsAnalysisService// 启动检测流程RdmsTestService public ProcessInstance startWorkflowInstance(TestDto testDto, ...) { VariableMap testVariableMap Variables.createVariables(); testVariableMap.putAll(testDto.getExtendData()); testVariableMap.putValue(testId, testDto.getId()) .putValue(businessKey, testDto.getBusinessKey()) .putValue(teamId, teamId) .putValue(sampleId, inSampleId) .putValue(sampleNo, inSampleNo) .putValue(sampleType, inSampleType); // ↑ 流程变量后续节点可以读取 ​ return runtimeService.startProcessInstanceByKey( testDto.getWorkflowKey(), // 流程Key: test_ngs, test_pcr 等 testDto.getBusinessKey(), // 业务Key: test:{id} testVariableMap // 流程变量 ); }// 启动合同审批流程RdmsContractService ProcessInstance processInstance runtimeService.startProcessInstanceByKey( RdmsConstant.WORKFLOW_APPROVE_CONTRACT, // approve_contract rdmsContract.getBusinessKey(), // contract:{id} variableMap );核心逻辑业务 Service 调用runtimeService.startProcessInstanceByKey()传入workflowKey决定走哪个 BPMN 图如test_ngs传入businessKey格式{实体类型}:{数据库ID}关联业务数据传入流程变量供后续节点使用模式 2完成任务并驱动流程继续在哪里RdmsWorkflowService.approve()Transactional(rollbackFor Exception.class) public void approve(String businessKey, String action, String comment, ListMapString, String nextTaskAssignee, String userId, String teamId) { ​ // 1. Redis 分布式锁防止并发 String lockKey workflow:approve: businessKey; // ... ​ // 2. 查询当前待办任务 Task task taskService.createTaskQuery() .processInstanceBusinessKey(businessKey) .active().singleResult(); ​ // 3. 设置审批变量 MapString, Object varibles Variables.createVariables() .putValue(action, action) // APPROVED / REJECT .putValue(reviewerId, userId); ​ // 4. 记录审批意见 taskService.createComment(task.getId(), task.getProcessInstanceId(), 2026-07-30 张三 通过 同意); ​ // 5. 分配下一节点处理人 taskService.setVariablesLocal(task.getId(), variablesMap); ​ // 6. 完成当前任务 → 流程自动走到下一节点 taskService.complete(task.getId(), varibles); ​ // 7. 审批通过后的业务逻辑 if (APPROVED.equals(action)) { if (businessKey.contains(contract)) { creatProject(businessKey, userId); // 合同通过 → 创建项目 } if (businessKey.contains(request)) { creatRequest(businessKey, userId); // 申请通过 → 创建实验任务 } } }核心逻辑taskService.complete()告诉 Camunda 当前节点已完成引擎自动根据 BPMN 图的流向找到下一个节点如果下一个是排他网关根据action变量走不同的分支如果下一个是用户任务新的待办任务会自动出现在ACT_RU_TASK表模式 3全局事件监听代替 JavaDelegate这是项目中最巧妙的设计 ——没有写任何一个 JavaDelegate 类而是用一个全局监听器统一处理。在哪里WorkflowGlobalEventListener.javaComponent public class WorkflowGlobalEventListener { ​ EventListener public void onTaskEvent(DelegateTask delegateTask) { // 任务创建时 → 初始化实验/分析任务 if (delegateTask.getEventName().equals(create)) { String businessKey delegateTask.getExecution().getBusinessKey(); ​ if (businessKey.startsWith(test:)) { dmsTestTaskService.initTaskVariables(delegateTask); // 在 MongoDB 中创建该步骤的任务记录 } else if (businessKey.startsWith(analysis:)) { dmsAnalysisTaskService.initTaskVariables(delegateTask); } } } ​ EventListener public void onExecutionEvent(DelegateExecution delegateExecution) { // 流程到达结束节点 → 更新状态为完成 if (delegateExecution.getEventName().equals(end)) { String businessKey delegateExecution.getBusinessKey(); ​ if (businessKey.startsWith(test:)) { dmsTestService.updateStatus(id, FINISH, 实验完成); } else if (businessKey.startsWith(analysis:)) { analysisService.updateStatus(id, FINISH, 完成分析); } } } }为什么要这样设计传统的 Camunda 使用方式是每个 Service Task 写一个JavaDelegate类// 传统方式每个节点一个类 public class ExtractDelegate implements JavaDelegate { public void execute(DelegateExecution execution) { // 提取样本逻辑 } } public class LibraryDelegate implements JavaDelegate { public void execute(DelegateExecution execution) { // 建库逻辑 } } // 17种检测流程 × 多步骤 几十个类...而本项目用一个全局监听器统一接管只需要两个方法处理所有流程的所有节点代码量大幅减少。模式 4通过 BusinessKey 关联业务数据这是整个集成最核心的设计Camunda 流程引擎 业务数据库 (MySQL MongoDB) ───────────────── ────────────────────────── ACT_RU_TASK rdms_test (MongoDB) └─ businessKey test:abc123 ─────▶ └─ _id abc123 ACT_HI_PROCINST rdms_contract (MySQL) └─ businessKey contract:def456 ──▶ └─ id def456BusinessKey 格式{实体类型}:{数据库ID}类型格式示例实验test:{id}test:5f8a1b2c3d4e分析analysis:{id}analysis:5f8a1b2c3d4e合同contract:{id}contract:5f8a1b2c3d4e申请单request:{id}request:5f8a1b2c3d4e报告report:{id}report:5f8a1b2c3d4e这样从前端页面看到一条待办任务时通过businessKey就能查到对应的业务数据。模式 5通过角色匹配任务Candidate Groups实验流程的每个步骤都通过camunda:candidateGroups指定谁能做userTask idextract name提取 camunda:candidateGroups提取员 / userTask idlibrary name建库 camunda:candidateGroups建库员 / userTask idhybrid name杂交 camunda:candidateGroups杂交员 / userTask idsequence name上机 camunda:candidateGroups上机员 /查询任务时利用角色筛选// RdmsWorkflowService.queryTask() ListString roles userRoleService.findRoleNameByUserIdAndTeamId(userId, teamId); ListTask taskList taskService.createTaskQuery() .taskDefinitionKey(taskDefinitionKey) .taskCandidateGroupIn(roles) // 用户角色匹配候选组 .taskVariableValueEquals(teamId, teamId) // 多租户隔离 .active() .list();这样实验员登录后只看到自己角色的任务提取员看不到建库的任务。模式 6Camunda REST API 自定义封装项目在 Camunda 自带的 REST API 之上封装了一层业务接口DmsWorkflowRest(/dms/workflow)端点功能对应 Camunda APIGET /definition查看已部署流程定义repositoryServicePUT /approve/{action}审批通过/驳回taskService.complete()PUT /assignee/{businessKey}指派处理人taskService.setAssignee()PUT /suspend/{businessKey}挂起流程runtimeService.suspend()DELETE /{businessKey}删除流程runtimeService.deleteProcessInstance()GET /comment/{businessKey}查询审批意见taskService.getTaskComments()GET /pictask/{businessKey}获取流程图高亮信息historyService五、数据一致性设计5.1 Redis 分布式锁审批操作加了 Redis 锁防止并发String lockKey workflow:approve: businessKey; String token redisLockNew.lock(lockKey, 10000L, 8000L); try { approveInternal(businessKey, ...); } finally { redisLockNew.unlock(lockKey, token); }如果两个人在同一秒点击通过只有一个能成功避免 Camunda 的乐观锁冲突。5.2 事务管理RdmsWorkflowService.approve()加了Transactional审批通过 → 任务完成 → 自动创建项目 → 全部在同一个事务中如果创建项目失败审批也会回滚5.3 RabbitMQ 事件驱动流程状态变更通过 MQ 通知其他模块流程完成 → WorkflowGlobalEventListener ↓ RabbitMQ: huindata.topicexchange.rdms.status ↓ RdmsEmailListener → 发送邮件通知 RdmsRequestListener → 更新申请单状态 RdmsProjectListener → 更新项目进度六、为什么这个项目需要 Camunda6.1 如果不使用 Camunda需要自己实现什么以「检测流程」为例test_ngs有 4 个步骤提取 → 建库 → 杂交 → 上机功能不用 Camunda 要自己写Camunda 做了任务持久化task表 task_assignee表 task_history表写 CRUD✅ ACT_RU_TASK / ACT_HI_ACTINST 自动管理状态流转currentStep字段 条件判断 if/else✅ BPMN 图定义了流向引擎自动执行分支网关if-else 逻辑写死在代码里✅ BPMN 的排他网关根据变量走不同路径候选人匹配角色表 任务分配逻辑✅candidateGroups自动匹配历史轨迹额外建审计表、写日志✅ Cockpit 直接看到完整历史超时提醒定时任务扫描 发消息✅ 边界事件BPMN 中拖一个闹钟图标流程图可视化需要另外开发✅ Cockpit 直接显示6.2 这个项目的核心痛点基因检测流程有两大特点特点一多技术路线有 17 种不同的检测技术每种技术的实验步骤不同技术步骤数步骤NGS4步提取 → 建库 → 杂交 → 上机PCR3步提取 → 扩增 → 检测Sanger3步提取 → PCR → 测序FISH4步制片 → 杂交 → 洗涤 → 观察用传统 if/else 写代码逻辑会纠缠在一起。而用 BPMN每种技术画一张图互不干扰。特点二多人协作实验员只看自己角色的任务审核人看到待审批列表管理层看整体进度跨团队销售→实验→分析→报告Camunda 的待办任务机制和角色匹配天然解决了多人协作问题。6.3 Camunda vs 自己写代码// 假想不用 Camunda手写状态机 class TestService { public void completeStep(String testId, String step) { Test test testRepo.findById(testId); switch (test.getType()) { // 17种技术路线 case NGS: switch (step) { // 每种路线N个步骤 case extract: test.setCurrentStep(library); break; case library: test.setCurrentStep(hybrid); break; // ... } break; case PCR: // 另一套逻辑... break; // 还有15种... } testRepo.save(test); // 手动持久化 createHistory(test, step); // 手动记录历史 notifyNextPerson(test); // 手动通知下一人 } }// 用 Camunda class TestService { public void completeStep(String taskId) { taskService.complete(taskId); // 一句话搞定 // ↑ 引擎自动判断下一步、自动持久化、自动历史记录 } }七、BPMN 文件是怎么被执行的前面的学习笔记说 Camunda 7 引擎启动时会自动读取classpath*:processes/**下的 bpmn 文件部署。但本项目用了一个外部部署的方式bpmn 文件放在rdms-doc/workflow/不在 classpath 中没有走自动部署应该是管理员通过 Camunda Cockpit 或 REST API 手动部署的项目的RdmsWorkflowService.listProcessDefinition()和deleteDeployment()方法提供了管理能力部署方法之前已经验证过了# REST API 部署 curl -X POST http://localhost:8080/engine-rest/deployment/create \ -u demo:demo \ -F deployment-nametest_ngs \ -F filetest_ngs.bpmn八、Camunda 在项目中的演进路线从代码中的注释可以看出演变过程阶段一RabbitMQ 驱动 ← 大量注释掉的 MQ Receiver 通过 MQ 消息触发流程启动 ​ 阶段二直接 API 调用 ← 当前代码状态 Service 中直接调用 runtimeService.startProcessInstanceByKey() RabbitMQ 只做状态通知 ​ 阶段三全嵌入式引擎 ← 当前 Camunda 嵌入在 Spring Boot 中共用 MySQL 数据库 不需要单独部署 Camunda 服务九、总结Camunda 在这个项目中解决的核心问题多技术路线17种检测 × 4种分析 21种流程→ 每种画一张 BPMN 图即可多人协作销售/实验/分析/审核不同角色→ 引擎自动匹配待办任务业务逻辑可视化→ 非技术人员也能看懂流程走到哪了省去大量基础设施代码→ 不用自己写状态机、任务表、历史记录项目中使用 Camunda 的独特之处没有 JavaDelegate全用EventListener全局监听 SPEL 表达式BusinessKey 关联格式{实体}:{ID}统一了流程和业务数据的关联Role → CandidateGroup实验角色和任务角色直接映射一句话总结Camunda 不是给代码配图而是画图即执行。项目中的 BPMN 文件定义了 26 个业务流程的「红绿灯规则」 引擎按图驱动业务流转代码只负责每个节点上的具体业务操作。