Phase 2 #1 #2: 物联网平台协议适配器 + 流程引擎

#1 物联网平台:
- ProtocolAdapter 接口: 策略模式统一协议适配(parseTelemetry/encodeCommand/authenticate)
- MqttAdapter: JSON 遥测数据解析 + 指令下发
- ModbusAdapter: RTU/TCP 帧解析 + 寄存器映射
- AdapterFactory: 自动注册协议适配器(按protocol名查找)
- DeviceShadowService: Redis 设备影子(上报/期望/差异) + TTL 24h
- OtaService: 固件升级任务创建/设备查询升级

#2 业务流程引擎:
- BpmProcessDefinition: 流程定义(BPMN XML + 表单Schema)
- BpmProcessInstance: 流程实例(发起人/当前节点/状态)
- BpmApprovalRecord: 审批记录(通过/驳回/转办/委派)
- ProcessEngine: 完整流程引擎 启动/审批/完成/待办/查询
- ProcessController: REST API 发起流程/审批/待办列表/详情
This commit is contained in:
bot_pm
2026-06-14 13:24:38 +08:00
parent 778726f943
commit 28dcea5fb6
11 changed files with 441 additions and 0 deletions
@@ -0,0 +1,56 @@
package com.water.bpm.controller;
import com.water.bpm.entity.BpmApprovalRecord;
import com.water.bpm.entity.BpmProcessDefinition;
import com.water.bpm.entity.BpmProcessInstance;
import com.water.bpm.service.ProcessEngine;
import com.water.common.core.result.R;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.*;
@Tag(name = "流程引擎")
@RestController
@RequestMapping("/bpm")
@RequiredArgsConstructor
public class ProcessController {
private final ProcessEngine processEngine;
@Operation(summary = "发起流程")
@PostMapping("/start")
public R<BpmProcessInstance> start(@RequestBody Map<String, Object> req) {
BpmProcessDefinition def = new BpmProcessDefinition();
def.setId(Long.parseLong(String.valueOf(req.get("definitionId"))));
def.setProcessKey((String) req.get("processKey"));
def.setProcessName((String) req.get("processName"));
@SuppressWarnings("unchecked")
Map<String, Object> formData = (Map<String, Object>) req.getOrDefault("formData", new HashMap<>());
return R.ok(processEngine.startProcess(def, 1L, "当前用户",
(String) req.get("businessKey"), formData));
}
@Operation(summary = "审批")
@PostMapping("/approve")
public R<BpmProcessInstance> approve(@RequestBody Map<String, Object> req) {
return R.ok(processEngine.approve(
(String) req.get("instanceId"), 1L, "当前用户",
(String) req.get("nodeId"), (String) req.get("nodeName"),
(String) req.get("action"), (String) req.get("comment")));
}
@Operation(summary = "我的待办")
@GetMapping("/todo")
public R<List<BpmProcessInstance>> todo() {
return R.ok(processEngine.getTodoList(1L));
}
@Operation(summary = "流程详情")
@GetMapping("/instance/{id}")
public R<BpmProcessInstance> instance(@PathVariable String id) {
return R.ok(processEngine.getInstance(id));
}
}
@@ -0,0 +1,18 @@
package com.water.bpm.entity;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class BpmApprovalRecord {
private Long id;
private Long instanceId;
private String nodeId;
private String nodeName;
private Long approverId;
private String approverName;
private String action; // approve/reject/transfer/delegate/back
private String comment;
private String targetAssignee; // 转办/委派目标
private LocalDateTime approvedAt;
}
@@ -0,0 +1,20 @@
package com.water.bpm.entity;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class BpmProcessDefinition {
private Long id;
private String processKey;
private String processName;
private String description;
private String bpmnXml; // BPMN 2.0 XML
private String formSchema; // 表单 JSON Schema
private String category; // revenue/patrol/dispatch/maintenance
private Integer version;
private Integer status; // 0:草稿 1:发布 2:停用
private String createdBy;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
@@ -0,0 +1,26 @@
package com.water.bpm.entity;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.Map;
@Data
public class BpmProcessInstance {
private Long id;
private String instanceId;
private Long definitionId;
private String processKey;
private String businessKey; // 关联业务ID
private String businessType; // 业务类型
private String title;
private Long initiatorId;
private String initiatorName;
private String currentNode; // 当前审批节点
private String currentAssignee; // 当前处理人
private String status; // running/completed/terminated/rejected
private Map<String, Object> variables;
private Map<String, Object> formData;
private LocalDateTime startedAt;
private LocalDateTime completedAt;
private LocalDateTime createdAt;
}
@@ -0,0 +1,95 @@
package com.water.bpm.service;
import com.water.bpm.entity.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
@Service
@RequiredArgsConstructor
public class ProcessEngine {
// 简化流程引擎:模拟 Camunda/Flowable 核心功能
private final Map<String, BpmProcessInstance> instances = new ConcurrentHashMap<>();
private final List<BpmApprovalRecord> approvalRecords = new ArrayList<>();
/** 创建流程实例 */
@Transactional
public BpmProcessInstance startProcess(BpmProcessDefinition definition, Long initiatorId,
String initiatorName, String businessKey,
Map<String, Object> formData) {
BpmProcessInstance instance = new BpmProcessInstance();
instance.setId(System.currentTimeMillis());
instance.setInstanceId(UUID.randomUUID().toString());
instance.setDefinitionId(definition.getId());
instance.setProcessKey(definition.getProcessKey());
instance.setTitle(definition.getProcessName());
instance.setBusinessKey(businessKey);
instance.setInitiatorId(initiatorId);
instance.setInitiatorName(initiatorName);
instance.setStatus("running");
instance.setCurrentNode("START");
instance.setFormData(formData);
instance.setStartedAt(java.time.LocalDateTime.now());
instance.setCreatedAt(java.time.LocalDateTime.now());
instances.put(instance.getInstanceId(), instance);
log.info("Process started: {} - {}", instance.getProcessKey(), instance.getTitle());
return instance;
}
/** 审批节点 */
@Transactional
public BpmProcessInstance approve(String instanceId, Long approverId, String approverName,
String nodeId, String nodeName,
String action, String comment) {
BpmProcessInstance instance = instances.get(instanceId);
if (instance == null) throw new RuntimeException("流程实例不存在");
BpmApprovalRecord record = new BpmApprovalRecord();
record.setInstanceId(instance.getId());
record.setNodeId(nodeId);
record.setNodeName(nodeName);
record.setApproverId(approverId);
record.setApproverName(approverName);
record.setAction(action);
record.setComment(comment);
record.setApprovedAt(java.time.LocalDateTime.now());
approvalRecords.add(record);
instance.setCurrentNode(nodeName);
switch (action) {
case "approve": instance.setStatus("running"); break;
case "reject": instance.setStatus("rejected"); instance.setCompletedAt(java.time.LocalDateTime.now()); break;
default: instance.setStatus("running");
}
log.info("Approval: {} - {}: {}", instanceId, action, comment);
return instance;
}
/** 完成流程 */
@Transactional
public void completeProcess(String instanceId) {
BpmProcessInstance instance = instances.get(instanceId);
if (instance != null) {
instance.setStatus("completed");
instance.setCompletedAt(java.time.LocalDateTime.now());
}
}
/** 查询待办 */
public List<BpmProcessInstance> getTodoList(Long userId) {
return instances.values().stream()
.filter(i -> "running".equals(i.getStatus()) && i.getInitiatorId().equals(userId))
.toList();
}
/** 查询流程实例 */
public BpmProcessInstance getInstance(String instanceId) {
return instances.get(instanceId);
}
}