ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

基于Spring Boot与Camunda构建全链路自动化工厂系统实战

基于Spring Boot与Camunda构建全链路自动化工厂系统实战 最近在开发自动化工厂系统时经常遇到一个痛点从蓝图设计到实际部署中间涉及大量的手动配置、资源核对和环境适配工作一个环节出错就可能导致整个生产线停摆。本文将分享一套基于“全自动超级安山工厂 2.0版本”理念的完整实战方案它不仅仅是一个概念升级更是一套可落地的、从设计到运维的自动化工程体系。无论你是正在规划智能工厂的架构师还是需要实现具体自动化流程的开发工程师都能从本文中找到从环境搭建、核心模块实现到生产部署的全流程代码与避坑指南。1. 核心概念什么是“全自动超级安山工厂 2.0”在深入代码之前我们首先要厘清概念。所谓的“全自动超级安山工厂”并非指某个特定的物理工厂而是一个高度抽象的数字孪生与自动化管理框架。它旨在通过软件定义生产Software-Defined Manufacturing将工厂的物理设备、工艺流程、物料流转和质量管理全部数字化、模型化并通过中央调度系统实现无人干预的全自动运行。1.0 到 2.0 的升级核心在于1.0 版本侧重于单点自动化例如一条传送带的控制、一个机械臂的编程。各系统间是“烟囱式”的联动依赖硬编码或简单的脚本扩展性和灵活性差。2.0 版本强调“全链路”与“自适应”。全链路从订单下达、原料入库、生产排程、设备调度、质量检测到成品出库整个流程在数字世界有完整的映射和闭环控制。自适应系统能够根据实时数据如设备状态、物料库存、订单优先级动态调整生产计划具备一定的自愈和优化能力。因此构建 2.0 版本系统的关键技术栈通常包括物联网IoT平台用于设备接入与数据采集微服务架构实现业务解耦规则引擎或机器学习模型用于智能决策以及一个强大的工作流引擎来编排整个生产流程。本文将用一个简化的模拟系统来演示核心思想的实现。2. 环境准备与项目结构我们使用 Spring Boot 作为后端框架它能够快速集成各类中间件非常适合构建此类复杂的业务系统。同时我们会引入工作流引擎 Camunda 来编排核心业务流程。2.1 基础环境要求JDK: 版本 11 或 17本文示例使用 JDK 17Maven: 3.6 及以上IDE: IntelliJ IDEA 或 Eclipse数据库: MySQL 8.0用于业务数据和工作流引擎数据存储消息队列可选: RabbitMQ 或 Kafka用于模块间异步通信本文为简化使用 Spring Event 模拟。Camunda Modeler: 用于绘制 BPMN 工作流图的桌面工具 官网下载 。2.2 初始化 Spring Boot 项目使用 Spring Initializr 或 IDE 创建项目选择以下依赖Spring Web: 提供 RESTful API 支持。Spring Data JPA: 简化数据库操作。MySQL Driver: 数据库连接。Camunda Spring Boot Starter: 集成工作流引擎。Lombok: 简化实体类代码可选但推荐。生成项目后pom.xml关键依赖部分如下dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-jpa/artifactId /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency !-- Camunda 工作流引擎 -- dependency groupIdorg.camunda.bpm.springboot/groupId artifactIdcamunda-bpm-spring-boot-starter/artifactId version7.19.0/version !-- 请使用与Spring Boot兼容的最新版本 -- /dependency dependency groupIdorg.camunda.bpm.springboot/groupId artifactIdcamunda-bpm-spring-boot-starter-rest/artifactId version7.19.0/version /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies2.3 项目结构概览创建完成后你的项目结构应类似如下我们后续的代码将按此组织src/main/java/com/example/superfactory/ ├── SuperFactoryApplication.java # 启动类 ├── config/ # 配置类 ├── controller/ # REST API 控制器 ├── service/ # 业务逻辑层 │ ├── impl/ │ └── workflow/ # 工作流相关服务 ├── repository/ # 数据访问层JPA ├── entity/ # 实体类对应数据库表 ├── dto/ # 数据传输对象 └── bpmn/ # 存放 BPMN 工作流定义文件2.4 数据库与基础配置在application.yml或application.properties中配置数据库和 Camunda# application.yml spring: datasource: url: jdbc:mysql://localhost:3306/super_factory_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: yourpassword driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: ddl-auto: update # 首次启动用update自动建表生产环境应使用validate或none show-sql: true properties: hibernate: dialect: org.hibernate.dialect.MySQL8Dialect # Camunda 配置 camunda.bpm: admin-user: id: admin password: admin firstName: Admin # 自动部署 resources/bpmn 目录下的流程定义 auto-deployment-enabled: true # 生成历史数据便于监控和查询 history-level: full启动应用后Camunda 会自动在super_factory_db库中创建数十张表用于存储流程定义、实例、任务和历史数据。3. 核心模块设计与实现我们的模拟工厂包含几个核心业务实体生产订单(ProductionOrder)、物料(Material)、设备(Device)、工艺路线(ProcessRoute)。全自动的核心在于一个主工作流它驱动订单走完整个生命周期。3.1 定义业务实体首先我们创建 JPA 实体。// 文件路径src/main/java/com/example/superfactory/entity/ProductionOrder.java package com.example.superfactory.entity; import lombok.Data; import javax.persistence.*; import java.time.LocalDateTime; import java.util.List; Entity Data Table(name production_order) public class ProductionOrder { Id GeneratedValue(strategy GenerationType.IDENTITY) private Long id; private String orderNumber; // 订单号 private String productCode; // 产品编码 private Integer quantity; // 订单数量 private String status; // 状态NEW, SCHEDULED, IN_PROGRESS, COMPLETED, CANCELLED private Integer priority; // 优先级 private LocalDateTime createTime; private LocalDateTime updateTime; // 关联的工艺路线简化实际可能有多条 ManyToOne JoinColumn(name process_route_id) private ProcessRoute processRoute; // 关联的流程实例IDCamunda private String processInstanceId; PrePersist protected void onCreate() { createTime LocalDateTime.now(); status NEW; } PreUpdate protected void onUpdate() { updateTime LocalDateTime.now(); } }// 文件路径src/main/java/com/example/superfactory/entity/Device.java package com.example.superfactory.entity; import lombok.Data; import javax.persistence.*; Entity Data Table(name device) public class Device { Id GeneratedValue(strategy GenerationType.IDENTITY) private Long id; private String deviceId; // 设备唯一标识 private String name; // 设备名称 private String type; // 类型CNC, ASSEMBLY, TEST, PACKING private String status; // 状态IDLE, RUNNING, FAULT, MAINTENANCE private String ipAddress; // 设备IP用于模拟通讯 private Integer capability; // 单位时间产能 }3.2 设计核心工作流BPMN 2.0这是“全自动”的灵魂。我们使用 Camunda Modeler 绘制一个简化的生产订单流程。打开 Camunda Modeler新建一个 BPMN 图。从左侧面板拖拽元素构建如下流程开始事件 (Start Event): 订单创建。用户任务 (User Task): “订单审核”。模拟人工介入点服务任务 (Service Task): “自动排程”。调用我们的 Java 服务方法并行网关 (Parallel Gateway): 拆分出物料准备和设备准备两条并行线。服务任务: “检查物料库存”。服务任务: “调度设备”。并行网关: 汇聚并行分支。服务任务: “执行生产”。服务任务: “质量检测”。独占网关 (Exclusive Gateway): 判断检测结果。结果合格 -服务任务: “成品入库”。结果不合格 -用户任务: “人工处理异常”。结束事件 (End Event): 流程结束。为每个服务任务和用户任务设置属性。服务任务在Implementation标签页设置Type为Delegate Expression并在Delegate Expression框中填入一个 Spring Bean 的名字例如${autoSchedulingDelegate}。这表示该任务将委托给这个 Bean 执行。用户任务在Assignee或Candidate Groups中设置处理人/组例如assignee: reviewer。将绘制好的 BPMN 文件保存到项目的src/main/resources/bpmn/目录下命名为production-order-process.bpmn。Spring Boot 启动时会自动部署它。3.3 实现工作流委托类Delegate这些类负责执行 BPMN 图中服务任务的实际业务逻辑。它们必须实现org.camunda.bpm.engine.delegate.JavaDelegate接口。// 文件路径src/main/java/com/example/superfactory/service/workflow/delegate/AutoSchedulingDelegate.java package com.example.superfactory.service.workflow.delegate; import com.example.superfactory.entity.ProductionOrder; import com.example.superfactory.repository.ProductionOrderRepository; import com.example.superfactory.service.SchedulingService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.camunda.bpm.engine.delegate.DelegateExecution; import org.camunda.bpm.engine.delegate.JavaDelegate; import org.springframework.stereotype.Component; Slf4j Component(autoSchedulingDelegate) // Bean名称必须与BPMN中Delegate Expression一致 RequiredArgsConstructor public class AutoSchedulingDelegate implements JavaDelegate { private final SchedulingService schedulingService; private final ProductionOrderRepository orderRepository; Override public void execute(DelegateExecution execution) throws Exception { // 从流程变量中获取业务数据 String orderId (String) execution.getVariable(orderId); log.info(【自动排程】开始处理订单: {}, orderId); ProductionOrder order orderRepository.findByOrderNumber(orderId) .orElseThrow(() - new RuntimeException(订单不存在: orderId)); // 调用排程服务计算最优生产开始时间、分配设备等 boolean scheduleSuccess schedulingService.scheduleOrder(order); if (scheduleSuccess) { order.setStatus(SCHEDULED); orderRepository.save(order); execution.setVariable(schedulingResult, SUCCESS); log.info(【自动排程】订单 {} 排程成功, orderId); } else { execution.setVariable(schedulingResult, FAILED); // 可以设置一个错误码引导流程走向异常处理分支 execution.setVariable(errorCode, SCHEDULE_FAILED); log.error(【自动排程】订单 {} 排程失败资源不足, orderId); } } }按照同样的模式实现CheckMaterialDelegate、DispatchDeviceDelegate、ExecuteProductionDelegate、QualityCheckDelegate、WarehouseInDelegate等。它们将分别注入不同的业务 Service 来完成具体操作。3.4 实现业务服务层以SchedulingService为例它包含核心的排程算法这里用简单模拟。// 文件路径src/main/java/com/example/superfactory/service/impl/SchedulingServiceImpl.java package com.example.superfactory.service.impl; import com.example.superfactory.entity.Device; import com.example.superfactory.entity.ProductionOrder; import com.example.superfactory.repository.DeviceRepository; import com.example.superfactory.service.SchedulingService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; Slf4j Service RequiredArgsConstructor public class SchedulingServiceImpl implements SchedulingService { private final DeviceRepository deviceRepository; Override Transactional public boolean scheduleOrder(ProductionOrder order) { // 1. 根据订单产品类型确定需要的设备类型 String requiredDeviceType determineDeviceType(order.getProductCode()); // 2. 查询当前空闲且可用的该类型设备 ListDevice availableDevices deviceRepository .findByTypeAndStatus(requiredDeviceType, IDLE); if (availableDevices.isEmpty()) { log.warn(没有找到空闲的 {} 类型设备, requiredDeviceType); return false; } // 3. 简单的调度策略选择第一台空闲设备实际可能考虑负载、位置等 Device selectedDevice availableDevices.get(0); selectedDevice.setStatus(RUNNING); deviceRepository.save(selectedDevice); // 4. 模拟计算生产耗时实际应根据订单数量、设备产能计算 long estimatedDuration order.getQuantity() * 60L; // 假设每件产品60秒 // 5. 将分配结果关联到订单或存入上下文这里简化实际可能存到关联表 log.info(订单 {} 已分配设备 {}预计耗时 {} 秒, order.getOrderNumber(), selectedDevice.getDeviceId(), estimatedDuration); // 可以将设备ID和预计时间设置为流程变量供后续任务使用 // execution.setVariable(assignedDeviceId, selectedDevice.getDeviceId()); // execution.setVariable(estimatedDuration, estimatedDuration); return true; } private String determineDeviceType(String productCode) { // 模拟一个产品-设备类型的映射规则 if (productCode.startsWith(CNC)) { return CNC; } else if (productCode.startsWith(ASM)) { return ASSEMBLY; } return GENERAL; } }3.5 实现流程启动与任务处理的 REST API我们需要提供接口来触发流程和管理人工任务。// 文件路径src/main/java/com/example/superfactory/controller/WorkflowController.java package com.example.superfactory.controller; import com.example.superfactory.entity.ProductionOrder; import com.example.superfactory.service.ProductionOrderService; import com.example.superfactory.service.WorkflowService; import lombok.RequiredArgsConstructor; import org.camunda.bpm.engine.task.Task; import org.springframework.web.bind.annotation.*; import java.util.HashMap; import java.util.List; import java.util.Map; RestController RequestMapping(/api/workflow) RequiredArgsConstructor public class WorkflowController { private final WorkflowService workflowService; private final ProductionOrderService orderService; /** * 创建新生产订单并启动流程 */ PostMapping(/order/start) public MapString, Object startProductionOrder(RequestBody ProductionOrder order) { // 1. 保存业务订单 ProductionOrder savedOrder orderService.createOrder(order); // 2. 启动工作流实例 String processInstanceId workflowService.startProductionProcess(savedOrder.getOrderNumber()); // 3. 关联流程实例ID到业务订单 savedOrder.setProcessInstanceId(processInstanceId); orderService.updateOrder(savedOrder); MapString, Object result new HashMap(); result.put(orderId, savedOrder.getOrderNumber()); result.put(processInstanceId, processInstanceId); result.put(message, 生产订单已创建流程已启动); return result; } /** * 查询待办任务例如给“订单审核”人员 */ GetMapping(/tasks) public ListTask getTasks(RequestParam String assignee) { return workflowService.getTasksByAssignee(assignee); } /** * 完成一个任务例如完成审核 */ PostMapping(/task/{taskId}/complete) public void completeTask(PathVariable String taskId, RequestBody(required false) MapString, Object variables) { workflowService.completeTask(taskId, variables); } }对应的WorkflowService实现// 文件路径src/main/java/com/example/superfactory/service/impl/WorkflowServiceImpl.java package com.example.superfactory.service.impl; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.camunda.bpm.engine.RuntimeService; import org.camunda.bpm.engine.TaskService; import org.camunda.bpm.engine.task.Task; import org.camunda.bpm.engine.runtime.ProcessInstance; import org.springframework.stereotype.Service; import java.util.HashMap; import java.util.List; import java.util.Map; Slf4j Service RequiredArgsConstructor public class WorkflowServiceImpl implements com.example.superfactory.service.WorkflowService { private final RuntimeService runtimeService; private final TaskService taskService; Override public String startProductionProcess(String orderNumber) { MapString, Object variables new HashMap(); variables.put(orderId, orderNumber); // 将业务ID传递给流程 variables.put(initiator, system); // 可以设置更多初始变量如优先级等 ProcessInstance instance runtimeService.startProcessInstanceByKey( ProductionOrderProcess, // BPMN流程的ID variables ); log.info(启动生产订单流程成功订单号{}流程实例ID{}, orderNumber, instance.getId()); return instance.getId(); } Override public ListTask getTasksByAssignee(String assignee) { return taskService.createTaskQuery() .taskAssignee(assignee) .active() .list(); } Override public void completeTask(String taskId, MapString, Object variables) { if (variables null) { variables new HashMap(); } taskService.complete(taskId, variables); log.info(任务 {} 已完成, taskId); } }4. 运行与验证全流程4.1 启动应用与初始化数据确保 MySQL 服务运行并创建好super_factory_db数据库。运行SuperFactoryApplication的 main 方法。应用启动后Camunda 会自动创建表并部署bpmn目录下的流程定义。可以在日志中看到类似Deployment of ‘production-order-process.bpmn’ succeeded的信息。通过简单的 SQL 或编写一个初始化脚本向device表插入几条设备数据状态为IDLE。4.2 模拟端到端流程我们可以使用 Postman 或 curl 来模拟整个自动化流程。步骤1创建并启动订单POST http://localhost:8080/api/workflow/order/start Content-Type: application/json { orderNumber: PO-20240527-001, productCode: CNC-001, quantity: 100, priority: 1 }响应会返回processInstanceId。步骤2处理“订单审核”任务人工查询审核员的待办任务GET http://localhost:8080/api/workflow/tasks?assigneereviewer系统会返回一个任务列表包含taskId和name例如“订单审核”。审核员完成任务可以附带审核意见变量POST http://localhost:8080/api/workflow/task/{taskId}/complete Content-Type: application/json { reviewResult: approved, reviewComment: 订单合规准予生产 }步骤3观察自动执行审核完成后流程会自动进入“自动排程”、“检查物料”、“调度设备”等服务任务。观察应用控制台日志可以看到各个Delegate类被依次调用并打印相应的业务日志。步骤4处理“质量检测”后的分支当流程执行到“质量检测”服务任务后会根据我们代码中设置的检测结果变量例如qualityPass: false决定流程走向。如果合格自动执行“成品入库”。如果不合格流程会创建一个新的“人工处理异常”用户任务等待相关人员介入。4.3 监控流程状态Camunda 自带了一个功能强大的管理界面默认端口 8080与 Spring Boot 应用端口冲突需要配置或使用独立版本。更简单的方式是通过 API 或查询数据库来监控。查询流程实例状态可以通过RuntimeService查询。查看历史Camunda 的ACT_HI_*系列表记录了完整的流程历史便于追溯和报表生成。5. 常见问题与排查思路在实现和运行此类系统时你可能会遇到以下典型问题问题现象可能原因排查思路与解决方案流程启动失败报ProcessDefinitionNotFoundException1. BPMN 文件未正确部署。2.startProcessInstanceByKey中的流程定义 Key 错误。1. 检查src/main/resources/bpmn/目录下文件是否存在启动日志是否有部署成功信息。2. 用repositoryService.createProcessDefinitionQuery().list()查询已部署流程的 Key确保一致。服务任务Delegate未执行1. Delegate Expression 的 Bean 名称与 Spring 容器中的名称不匹配。2. Delegate 类未被 Spring 管理缺少Component。3. 方法抛出未捕获异常导致流程中断。1. 检查 BPMN 文件中服务任务的Delegate Expression属性确保是${beanName}格式且beanName与Component(“beanName”)一致。2. 确认 Delegate 类在 Spring 扫描路径下并添加了Component。3. 在 Delegate 的execute方法中添加详细的 try-catch 和日志查看异常信息。用户任务找不到处理人1. BPMN 用户任务的assignee或candidateGroups设置错误或为空。2. 任务查询时使用的assignee参数与设置不符。1. 用 Camunda Modeler 打开 BPMN 文件检查用户任务的Assignee属性。2. 通过taskService.createTaskQuery().taskUnassigned().list()查询未分配的任务。业务数据与流程实例丢失关联启动流程时未将业务主键如订单号作为流程变量传递或后续任务未正确读取。1. 在启动流程的variablesMap 中务必放入关键业务ID。2. 在后续的 Delegate 中使用execution.getVariable(“orderId”)获取并转换为业务对象。并行网关后流程卡住并行分支中的某个服务任务执行失败或耗时极长导致网关无法汇聚。1. 检查每个并行分支的服务任务日志确认是否都成功执行完毕。2. 对于可能失败的任务考虑增加重试机制或超时处理并在失败时设置流程变量引导至异常处理路径。数据库死锁高并发下多个流程实例同时更新同一条设备状态记录。1. 在排程等关键业务方法上使用Transactional并考虑隔离级别。2. 优化调度算法避免热点数据竞争或引入分布式锁如 Redis Lock。3. 对设备状态更新使用乐观锁JPA 的Version注解。6. 最佳实践与工程建议将“全自动超级工厂”从演示推向生产环境需要关注以下工程实践1. 流程设计原则幂等性所有服务任务Delegate的实现应保证幂等。因为网络超时等原因引擎可能会重试任务。确保你的execute方法即使被多次调用也不会产生副作用如重复扣减库存。补偿机制对于“执行生产”这类长时任务要考虑失败后的补偿。例如生产任务失败后应触发一个补偿处理器来释放已占用的设备、回滚物料预留等。Camunda 支持 BPMN 的“事务子流程”和“补偿边界事件”。超时与重试为可能阻塞的服务任务设置异步处理和超时BPMN 的asyncBefore和定时器边界事件避免单个任务挂起导致整个流程实例僵死。2. 数据一致性业务与流程数据最终一致本文示例在业务事务中启动流程是简单做法。更严谨的模式是使用“发件箱模式”Outbox Pattern将“创建订单”和“发送流程启动命令”放在一个本地事务中通过可靠消息如 Debezium或定时任务来保证流程引擎最终能收到命令。这避免了分布式事务的复杂性。关键状态可追溯重要的业务状态变更如订单状态从SCHEDULED变为IN_PROGRESS不仅要在业务表中更新也应作为流程变量记录方便通过流程历史反查业务链路。3. 可观测性与运维结构化日志在所有 Delegate 和 Service 中使用 SLF4J 记录结构化日志JSON 格式包含orderId,processInstanceId,taskId等关键字段便于通过 ELK 等工具进行聚合查询和链路追踪。监控与告警监控 Camunda 的指标如活动流程实例数、任务积压数、作业执行失败率。对长时间运行的任务实例和失败的任务设置告警。版本管理对 BPMN 流程定义进行版本控制Camunda 支持。当修改流程后部署新版本旧的流程实例会继续运行在原有版本上新实例使用新版本。务必在修改前评估对运行中实例的影响。4. 扩展性设计微服务化将“设备管理”、“物料管理”、“排程算法”、“质量检测”等模块拆分为独立的微服务。工作流引擎中的服务任务通过 HTTP/RPC 调用这些服务而非本地 JavaDelegate。这使系统更容易水平扩展。规则外置将排程策略、质量检测规则等动态部分抽取到规则引擎如 Drools或配置中心中。这样可以在不重启流程、不重新部署 BPMN 的情况下调整业务规则。事件驱动用消息队列如 Kafka连接工作流引擎与外部系统。流程可以发布“生产任务就绪”事件由设备控制系统消费并执行设备完成任务后再发布“生产完成”事件触发流程继续。这极大提升了系统的解耦度和响应能力。5. 安全与权限流程数据隔离如果系统是多租户的需要使用 Camunda 的多租户功能确保不同工厂/客户的数据完全隔离。任务权限控制结合企业现有的 RBAC 系统动态设置用户任务的candidateGroups实现基于角色和部门的任务分配。API 安全对所有 REST API 实施认证如 JWT和授权防止未授权用户启动流程或操作任务。构建“全自动超级安山工厂 2.0”是一个持续的迭代过程。从本文的最小可行系统出发你可以逐步引入更复杂的流程模式如子流程、调用活动、更智能的决策集成机器学习模型、更实时的数据看板通过 Camunda Cockpit 或自研驾驶舱最终形成一个真正能够自适应、自优化的智能制造中枢。
返回列表