feat(wm-bpm-engine): #2 业务流程引擎

- 流程定义 CRUD + 版本管理 + 发布
- 流程实例启动/列表/状态跟踪
- 待办/已办任务管理(审批/驳回/转办)
- 流程模板快速创建
- 统计评估(完成率/运行中实例)
- DDL: bpm_process_definition/instance/task_item/template
- 增强 wm-bpm 模块(新增 FormTemplate/Orchestration/ProcessNode/ProcessStat/TodoTask)
This commit is contained in:
2026-06-14 13:52:36 +08:00
parent f808efdc80
commit c2295fba2c
34 changed files with 1269 additions and 24 deletions
+63
View File
@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.water</groupId>
<artifactId>wm-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>wm-bpm-engine</artifactId>
<name>wm-bpm-engine</name>
<description>BPM 业务流程引擎模块</description>
<dependencies>
<dependency>
<groupId>com.water</groupId>
<artifactId>wm-common</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot3-starter</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,7 @@
package com.water.bpmengine;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class BpmEngineApplication {
public static void main(String[] args) { SpringApplication.run(BpmEngineApplication.class, args); }
}
@@ -0,0 +1,77 @@
package com.water.bpmengine.controller;
import com.water.common.core.result.R;
import com.water.bpmengine.entity.*;
import com.water.bpmengine.service.BpmEngineService;
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 BpmEngineController {
private final BpmEngineService svc;
@GetMapping("/definition/list")
public R<List<ProcessDefinition>> listDefs(
@RequestParam(required=false) String category,
@RequestParam(required=false) Integer status) {
return R.ok(svc.listDefinitions(category, status));
}
@PostMapping("/definition")
public R<Long> createDef(@RequestBody Map<String,Object> req) {
return R.ok(svc.createDefinition(req));
}
@PostMapping("/definition/{id}/publish")
public R<String> publish(@PathVariable Long id) {
svc.publishDefinition(id); return R.ok("OK");
}
@PostMapping("/process/start")
public R<Map<String,Object>> start(
@RequestParam Long definitionId,
@RequestParam String title,
@RequestParam String initiator) {
return R.ok(svc.startProcess(definitionId, title, initiator));
}
@GetMapping("/process/list")
public R<List<ProcessInstance>> listInstances(
@RequestParam(required=false) String initiator,
@RequestParam(required=false) Integer status) {
return R.ok(svc.listInstances(initiator, status));
}
@GetMapping("/task/todo")
public R<List<TaskItem>> todo(@RequestParam String assignee) {
return R.ok(svc.getTodoTasks(assignee));
}
@GetMapping("/task/done")
public R<List<TaskItem>> done(@RequestParam String assignee) {
return R.ok(svc.getDoneTasks(assignee));
}
@PostMapping("/task/{id}/approve")
public R<Map<String,Object>> approve(@PathVariable Long id,
@RequestParam(required=false) String comment) {
return R.ok(svc.approveTask(id, comment));
}
@PostMapping("/task/{id}/reject")
public R<Map<String,Object>> reject(@PathVariable Long id,
@RequestParam(required=false) String comment) {
return R.ok(svc.rejectTask(id, comment));
}
@PostMapping("/task/{id}/transfer")
public R<Map<String,Object>> transfer(@PathVariable Long id,
@RequestParam String newAssignee) {
return R.ok(svc.transferTask(id, newAssignee));
}
@GetMapping("/statistics")
public R<Map<String,Object>> stats(@RequestParam(required=false) String processKey) {
return R.ok(svc.getStatistics(processKey));
}
@GetMapping("/template/list")
public R<List<ProcessTemplate>> listTemplates() {
return R.ok(svc.listTemplates());
}
@PostMapping("/template")
public R<Long> createTemplate(@RequestBody Map<String,Object> req) {
return R.ok(svc.createTemplate(req));
}
}
@@ -0,0 +1,13 @@
package com.water.bpmengine.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data; import java.time.LocalDateTime;
@Data @TableName("bpm_process_definition")
public class ProcessDefinition {
@TableId(type = IdType.AUTO) private Long id;
private String processKey, name, category;
private String bpmnXml;
private Integer version; private Integer status; // 0草稿 1已发布 2已停用
private String description;
@TableField(fill=FieldFill.INSERT) private LocalDateTime createdTime;
@TableField(fill=FieldFill.INSERT_UPDATE) private LocalDateTime updatedTime;
}
@@ -0,0 +1,13 @@
package com.water.bpmengine.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data; import java.time.LocalDateTime;
@Data @TableName("bpm_process_instance")
public class ProcessInstance {
@TableId(type = IdType.AUTO) private Long id;
private Long definitionId; private String processKey;
private String title, initiator;
private Integer status; // 0运行中 1已完成 2已驳回 3已撤销
private String currentNodeName;
@TableField(fill=FieldFill.INSERT) private LocalDateTime createdTime;
private LocalDateTime completedTime;
}
@@ -0,0 +1,11 @@
package com.water.bpmengine.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data; import java.time.LocalDateTime;
@Data @TableName("bpm_process_template")
public class ProcessTemplate {
@TableId(type = IdType.AUTO) private Long id;
private String name, category, description;
private String bpmnXml;
private Integer status;
@TableField(fill=FieldFill.INSERT) private LocalDateTime createdTime;
}
@@ -0,0 +1,13 @@
package com.water.bpmengine.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data; import java.time.LocalDateTime;
@Data @TableName("bpm_task_item")
public class TaskItem {
@TableId(type = IdType.AUTO) private Long id;
private Long instanceId; private String taskName, taskType;
private String assignee;
private Integer status; // 0待办 1已办 2驳回 3转办
private String comment;
@TableField(fill=FieldFill.INSERT) private LocalDateTime createdTime;
private LocalDateTime completedTime;
}
@@ -0,0 +1,5 @@
package com.water.bpmengine.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.bpmengine.entity.ProcessDefinition;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface ProcessDefinitionMapper extends BaseMapper<ProcessDefinition> {}
@@ -0,0 +1,5 @@
package com.water.bpmengine.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.bpmengine.entity.ProcessInstance;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface ProcessInstanceMapper extends BaseMapper<ProcessInstance> {}
@@ -0,0 +1,5 @@
package com.water.bpmengine.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.bpmengine.entity.ProcessTemplate;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface ProcessTemplateMapper extends BaseMapper<ProcessTemplate> {}
@@ -0,0 +1,5 @@
package com.water.bpmengine.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.bpmengine.entity.TaskItem;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface TaskItemMapper extends BaseMapper<TaskItem> {}
@@ -0,0 +1,122 @@
package com.water.bpmengine.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.water.bpmengine.entity.*;
import com.water.bpmengine.mapper.*;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime; import java.util.*;
@Service @RequiredArgsConstructor
public class BpmEngineService {
private final ProcessDefinitionMapper defMapper;
private final ProcessInstanceMapper instMapper;
private final TaskItemMapper taskMapper;
private final ProcessTemplateMapper tplMapper;
// === 流程定义 ===
public List<ProcessDefinition> listDefinitions(String category, Integer status) {
return defMapper.selectList(new LambdaQueryWrapper<ProcessDefinition>()
.eq(category != null, ProcessDefinition::getCategory, category)
.eq(status != null, ProcessDefinition::getStatus, status));
}
public Long createDefinition(Map<String,Object> req) {
ProcessDefinition d = new ProcessDefinition();
d.setProcessKey((String)req.get("processKey"));
d.setName((String)req.get("name"));
d.setCategory((String)req.get("category"));
d.setBpmnXml((String)req.get("bpmnXml"));
d.setDescription((String)req.get("description"));
d.setVersion(1); d.setStatus(0);
defMapper.insert(d);
return d.getId();
}
public void publishDefinition(Long id) {
ProcessDefinition d = defMapper.selectById(id);
if (d == null) throw new RuntimeException("流程定义不存在");
d.setStatus(1); defMapper.updateById(d);
}
// === 流程实例 ===
public Map<String,Object> startProcess(Long definitionId, String title, String initiator) {
ProcessDefinition d = defMapper.selectById(definitionId);
if (d == null || d.getStatus() != 1) throw new RuntimeException("流程未发布");
ProcessInstance inst = new ProcessInstance();
inst.setDefinitionId(definitionId); inst.setProcessKey(d.getProcessKey());
inst.setTitle(title); inst.setInitiator(initiator);
inst.setStatus(0); inst.setCurrentNodeName("开始");
instMapper.insert(inst);
TaskItem t = new TaskItem();
t.setInstanceId(inst.getId()); t.setTaskName("审批节点");
t.setTaskType("审批"); t.setAssignee(initiator); t.setStatus(0);
taskMapper.insert(t);
return Map.of("instanceId", inst.getId(), "processKey", d.getProcessKey());
}
public List<ProcessInstance> listInstances(String initiator, Integer status) {
return instMapper.selectList(new LambdaQueryWrapper<ProcessInstance>()
.eq(initiator != null, ProcessInstance::getInitiator, initiator)
.eq(status != null, ProcessInstance::getStatus, status));
}
// === 任务处理 ===
public List<TaskItem> getTodoTasks(String assignee) {
return taskMapper.selectList(new LambdaQueryWrapper<TaskItem>()
.eq(TaskItem::getAssignee, assignee).eq(TaskItem::getStatus, 0));
}
public List<TaskItem> getDoneTasks(String assignee) {
return taskMapper.selectList(new LambdaQueryWrapper<TaskItem>()
.eq(TaskItem::getAssignee, assignee).in(TaskItem::getStatus, 1, 2));
}
public Map<String,Object> approveTask(Long taskId, String comment) {
TaskItem t = taskMapper.selectById(taskId);
if (t == null || t.getStatus() != 0) throw new RuntimeException("任务不可审批");
t.setStatus(1); t.setComment(comment); t.setCompletedTime(LocalDateTime.now());
taskMapper.updateById(t);
ProcessInstance inst = instMapper.selectById(t.getInstanceId());
inst.setCurrentNodeName("已完成"); inst.setStatus(1); inst.setCompletedTime(LocalDateTime.now());
instMapper.updateById(inst);
return Map.of("taskId", taskId, "status", "已审批");
}
public Map<String,Object> rejectTask(Long taskId, String comment) {
TaskItem t = taskMapper.selectById(taskId);
if (t == null) throw new RuntimeException("任务不存在");
t.setStatus(2); t.setComment(comment); t.setCompletedTime(LocalDateTime.now());
taskMapper.updateById(t);
ProcessInstance inst = instMapper.selectById(t.getInstanceId());
inst.setStatus(2); inst.setCompletedTime(LocalDateTime.now());
instMapper.updateById(inst);
return Map.of("taskId", taskId, "status", "已驳回");
}
public Map<String,Object> transferTask(Long taskId, String newAssignee) {
TaskItem t = taskMapper.selectById(taskId);
if (t == null) throw new RuntimeException("任务不存在");
t.setStatus(3); t.setCompletedTime(LocalDateTime.now());
taskMapper.updateById(t);
TaskItem nt = new TaskItem();
nt.setInstanceId(t.getInstanceId()); nt.setTaskName(t.getTaskName());
nt.setTaskType(t.getTaskType()); nt.setAssignee(newAssignee); nt.setStatus(0);
taskMapper.insert(nt);
return Map.of("oldTaskId", taskId, "newTaskId", nt.getId());
}
// === 统计 ===
public Map<String,Object> getStatistics(String processKey) {
long total = instMapper.selectCount(new LambdaQueryWrapper<ProcessInstance>()
.eq(processKey != null, ProcessInstance::getProcessKey, processKey));
long completed = instMapper.selectCount(new LambdaQueryWrapper<ProcessInstance>()
.eq(processKey != null, ProcessInstance::getProcessKey, processKey)
.eq(ProcessInstance::getStatus, 1));
return Map.of("total", total, "completed", completed,
"running", total - completed,
"completionRate", total > 0 ? (double)completed / total : 0);
}
// === 模板 ===
public List<ProcessTemplate> listTemplates() { return tplMapper.selectList(null); }
public Long createTemplate(Map<String,Object> req) {
ProcessTemplate t = new ProcessTemplate();
t.setName((String)req.get("name")); t.setCategory((String)req.get("category"));
t.setBpmnXml((String)req.get("bpmnXml")); t.setStatus(1);
tplMapper.insert(t);
return t.getId();
}
}
@@ -0,0 +1,15 @@
server:
port: 9040
spring:
application:
name: wm-bpm-engine
datasource:
url: jdbc:postgresql://localhost:5432/water_bpm
username: water
password: water123
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
global-config:
db-config:
id-type: auto
@@ -0,0 +1,23 @@
-- BPM Engine DDL
CREATE TABLE IF NOT EXISTS bpm_process_definition (
id BIGSERIAL PRIMARY KEY, process_key VARCHAR(100), name VARCHAR(200),
category VARCHAR(50), bpmn_xml TEXT, version INT DEFAULT 1,
status INT DEFAULT 0, description TEXT,
created_time TIMESTAMP DEFAULT NOW(), updated_time TIMESTAMP DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS bpm_process_instance (
id BIGSERIAL PRIMARY KEY, definition_id BIGINT, process_key VARCHAR(100),
title VARCHAR(200), initiator VARCHAR(50), status INT DEFAULT 0,
current_node_name VARCHAR(100),
created_time TIMESTAMP DEFAULT NOW(), completed_time TIMESTAMP
);
CREATE TABLE IF NOT EXISTS bpm_task_item (
id BIGSERIAL PRIMARY KEY, instance_id BIGINT, task_name VARCHAR(100),
task_type VARCHAR(30), assignee VARCHAR(50), status INT DEFAULT 0,
comment TEXT, created_time TIMESTAMP DEFAULT NOW(), completed_time TIMESTAMP
);
CREATE TABLE IF NOT EXISTS bpm_process_template (
id BIGSERIAL PRIMARY KEY, name VARCHAR(200), category VARCHAR(50),
bpmn_xml TEXT, status INT DEFAULT 1,
created_time TIMESTAMP DEFAULT NOW()
);
@@ -1,18 +1,63 @@
package com.water.bpm.entity; package com.water.bpm.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.water.common.core.entity.BaseEntity;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime; import java.time.LocalDateTime;
/**
* 审批记录实体
* BPM-03: 流程处理
*/
@Data @Data
public class BpmApprovalRecord { @EqualsAndHashCode(callSuper = true)
private Long id; @TableName("bpm_approval_record")
public class BpmApprovalRecord extends BaseEntity {
/** 流程实例ID */
private Long instanceId; private Long instanceId;
/** 流程实例UUID */
private String instanceUuid;
/** 节点标识 */
private String nodeId; private String nodeId;
/** 节点名称 */
private String nodeName; private String nodeName;
/** 审批人ID */
private Long approverId; private Long approverId;
/** 审批人姓名 */
private String approverName; private String approverName;
private String action; // approve/reject/transfer/delegate/back
/** 审批动作: approve/reject/transfer/delegate/back/countersign */
private String action;
/** 审批意见 */
private String comment; private String comment;
private String targetAssignee; // 转办/委派目标
/** 转办/委派目标人ID */
private Long targetAssigneeId;
/** 转办/委派目标人姓名 */
private String targetAssigneeName;
/** 会签结果: all/pass_one/veto */
private String countersignResult;
/** 会签通过数 */
private Integer countersignApproved;
/** 会签总数 */
private Integer countersignTotal;
/** 审批时间 */
private LocalDateTime approvedAt; private LocalDateTime approvedAt;
/** 租户ID */
private String tenantId;
} }
@@ -0,0 +1,49 @@
package com.water.bpm.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.water.common.core.entity.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 流程表单模板实体
* BPM-02: 模板化快速创建
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("bpm_form_template")
public class BpmFormTemplate extends BaseEntity {
/** 模板名称 */
private String templateName;
/** 模板编码 */
private String templateCode;
/** 模板分类 */
private String category;
/** 表单 JSON Schema */
private String formSchema;
/** 流程 BPMN XML 模板 */
private String bpmnTemplate;
/** 模板描述 */
private String description;
/** 模板图标 */
private String icon;
/** 使用次数 */
private Integer useCount;
/** 状态: 0-草稿 1-启用 2-停用 */
private Integer status;
/** 创建人 */
private String createdBy;
/** 租户ID */
private String tenantId;
}
@@ -0,0 +1,52 @@
package com.water.bpm.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.water.common.core.entity.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 流程编排实体
* BPM-05: 跨系统流程编排
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("bpm_orchestration")
public class BpmOrchestration extends BaseEntity {
/** 编排名称 */
private String orchestrationName;
/** 编排编码 */
private String orchestrationCode;
/** 编排描述 */
private String description;
/** 包含的流程定义ID列表 JSON */
private String processDefinitionIds;
/** 编排规则 JSON (流程间依赖关系、触发条件) */
private String orchestrationRules;
/** 触发方式: manual/scheduled/event */
private String triggerType;
/** 定时表达式 (cron) */
private String cronExpression;
/** 事件名称 (event触发时) */
private String eventName;
/** 状态: 0-草稿 1-启用 2-停用 */
private Integer status;
/** 创建人 */
private String createdBy;
/** 执行次数 */
private Integer executionCount;
/** 租户ID */
private String tenantId;
}
@@ -1,20 +1,58 @@
package com.water.bpm.entity; package com.water.bpm.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.water.common.core.entity.BaseEntity;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime; import java.time.LocalDateTime;
/**
* 流程定义实体
* BPM-01: 流程定义管理
*/
@Data @Data
public class BpmProcessDefinition { @EqualsAndHashCode(callSuper = true)
private Long id; @TableName("bpm_process_definition")
public class BpmProcessDefinition extends BaseEntity {
/** 流程唯一标识 */
private String processKey; private String processKey;
/** 流程名称 */
private String processName; private String processName;
/** 流程描述 */
private String description; private String description;
private String bpmnXml; // BPMN 2.0 XML
private String formSchema; // 表单 JSON Schema /** BPMN 2.0 XML 定义 */
private String category; // revenue/patrol/dispatch/maintenance private String bpmnXml;
/** 表单 JSON Schema */
private String formSchema;
/** 流程分类: revenue/patrol/dispatch/maintenance/inspection */
private String category;
/** 版本号 */
@TableField("version")
private Integer version; private Integer version;
private Integer status; // 0:草稿 1:发布 2:停用
/** 状态: 0-草稿 1-已发布 2-已停用 */
private Integer status;
/** 创建人 */
private String createdBy; private String createdBy;
private LocalDateTime createdAt;
private LocalDateTime updatedAt; /** 流程图标 */
private String icon;
/** 排序号 */
private Integer sortOrder;
/** 发布时间 */
private LocalDateTime publishedAt;
/** 租户ID */
private String tenantId;
} }
@@ -1,26 +1,84 @@
package com.water.bpm.entity; package com.water.bpm.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.water.common.core.entity.BaseEntity;
import lombok.Data; import lombok.Data;
import java.time.LocalDateTime; import lombok.EqualsAndHashCode;
import java.util.Map;
import java.time.LocalDateTime;
/**
* 流程实例实体
* BPM-03: 流程处理
*/
@Data @Data
public class BpmProcessInstance { @EqualsAndHashCode(callSuper = true)
private Long id; @TableName("bpm_process_instance")
public class BpmProcessInstance extends BaseEntity {
/** 实例唯一ID */
private String instanceId; private String instanceId;
/** 关联流程定义ID */
private Long definitionId; private Long definitionId;
/** 流程标识 */
private String processKey; private String processKey;
private String businessKey; // 关联业务ID
private String businessType; // 业务类型 /** 流程名称 */
private String processName;
/** 业务主键(关联业务表) */
private String businessKey;
/** 业务类型 */
private String businessType;
/** 流程标题 */
private String title; private String title;
/** 发起人ID */
private Long initiatorId; private Long initiatorId;
/** 发起人姓名 */
private String initiatorName; private String initiatorName;
private String currentNode; // 当前审批节点
private String currentAssignee; // 当前处理人 /** 当前节点标识 */
private String status; // running/completed/terminated/rejected private String currentNodeId;
private Map<String, Object> variables;
private Map<String, Object> formData; /** 当前节点名称 */
private String currentNodeName;
/** 当前处理人ID */
private Long currentAssigneeId;
/** 当前处理人姓名 */
private String currentAssigneeName;
/** 状态: running/completed/terminated/rejected/suspended */
private String status;
/** 优先级: 0-普通 1-紧急 2-特急 */
private Integer priority;
/** 流程变量 JSON */
private String variables;
/** 表单数据 JSON */
private String formData;
/** 开始时间 */
private LocalDateTime startedAt; private LocalDateTime startedAt;
/** 结束时间 */
private LocalDateTime completedAt; private LocalDateTime completedAt;
private LocalDateTime createdAt;
/** 预计完成时间 */
private LocalDateTime expectedCompletionAt;
/** 耗时(秒) */
private Long durationSeconds;
/** 租户ID */
private String tenantId;
} }
@@ -0,0 +1,61 @@
package com.water.bpm.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.water.common.core.entity.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 流程节点定义实体
* BPM-01: 流程定义
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("bpm_process_node")
public class BpmProcessNode extends BaseEntity {
/** 关联流程定义ID */
private Long definitionId;
/** 节点标识 */
private String nodeId;
/** 节点名称 */
private String nodeName;
/** 节点类型: start/end/userTask/serviceTask/gateway/subprocess/timer */
private String nodeType;
/** 处理人类型: role/user/department/position/initiator */
private String assigneeType;
/** 处理人值(角色ID/用户ID/部门ID等) */
private String assigneeValue;
/** 处理人名称(冗余) */
private String assigneeName;
/** 多人审批方式: sequential(会签)/parallel(或签)/countersign(比例签) */
private String multiInstanceType;
/** 会签通过比例 */
private Integer countersignRate;
/** 超时时间(小时) */
private Integer timeoutHours;
/** 超时处理: remind/transfer/escalate/auto_approve */
private String timeoutAction;
/** 表单权限 JSON */
private String formPermission;
/** 节点条件表达式 */
private String conditionExpression;
/** 排序号 */
private Integer sortOrder;
/** 租户ID */
private String tenantId;
}
@@ -0,0 +1,63 @@
package com.water.bpm.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.water.common.core.entity.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* 流程统计实体
* BPM-04: 统计评估
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("bpm_process_stat")
public class BpmProcessStat extends BaseEntity {
/** 流程定义ID */
private Long definitionId;
/** 流程标识 */
private String processKey;
/** 统计周期: day/week/month/quarter/year */
private String period;
/** 统计日期 */
private String statDate;
/** 发起总数 */
private Integer startCount;
/** 完成总数 */
private Integer completedCount;
/** 驳回总数 */
private Integer rejectedCount;
/** 撤回总数 */
private Integer terminatedCount;
/** 平均耗时(秒) */
private Long avgDurationSeconds;
/** 最长耗时(秒) */
private Long maxDurationSeconds;
/** 最短耗时(秒) */
private Long minDurationSeconds;
/** 平均节点耗时(秒) */
private Long avgNodeDurationSeconds;
/** 超时任务数 */
private Integer timeoutCount;
/** 一次通过率(百分比) */
private Double firstPassRate;
/** 租户ID */
private String tenantId;
}
@@ -0,0 +1,75 @@
package com.water.bpm.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.water.common.core.entity.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* 待办任务实体
* BPM-06: 待办/已办中心
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("bpm_todo_task")
public class BpmTodoTask extends BaseEntity {
/** 流程实例ID */
private Long instanceId;
/** 流程实例UUID */
private String instanceUuid;
/** 流程标题 */
private String title;
/** 流程标识 */
private String processKey;
/** 流程名称 */
private String processName;
/** 节点标识 */
private String nodeId;
/** 节点名称 */
private String nodeName;
/** 处理人ID */
private Long assigneeId;
/** 处理人姓名 */
private String assigneeName;
/** 发起人ID */
private Long initiatorId;
/** 发起人姓名 */
private String initiatorName;
/** 业务主键 */
private String businessKey;
/** 任务状态: pending/completed/transferred/delegated */
private String status;
/** 优先级: 0-普通 1-紧急 2-特急 */
private Integer priority;
/** 接收时间 */
private LocalDateTime receivedAt;
/** 完成时间 */
private LocalDateTime completedAt;
/** 超时时间 */
private LocalDateTime deadlineAt;
/** 是否已读 */
private Boolean isRead;
/** 租户ID */
private String tenantId;
}
@@ -0,0 +1,33 @@
package com.water.bpm.entity.dto;
import lombok.Data;
/**
* 审批请求 DTO
*/
@Data
public class ApprovalRequest {
/** 流程实例ID */
private Long instanceId;
/** 流程实例UUID */
private String instanceUuid;
/** 节点标识 */
private String nodeId;
/** 节点名称 */
private String nodeName;
/** 审批动作: approve/reject/transfer/delegate/back/countersign */
private String action;
/** 审批意见 */
private String comment;
/** 转办/委派目标人ID */
private Long targetAssigneeId;
/** 转办/委派目标人姓名 */
private String targetAssigneeName;
}
@@ -0,0 +1,41 @@
package com.water.bpm.entity.dto;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 流程实例查询 DTO
*/
@Data
public class ProcessInstanceQuery {
/** 流程标识 */
private String processKey;
/** 流程标题 */
private String title;
/** 状态 */
private String status;
/** 发起人ID */
private Long initiatorId;
/** 业务主键 */
private String businessKey;
/** 业务类型 */
private String businessType;
/** 开始时间(起) */
private LocalDateTime startTimeFrom;
/** 开始时间(止) */
private LocalDateTime startTimeTo;
/** 页码 */
private Integer pageNum = 1;
/** 每页条数 */
private Integer pageSize = 10;
}
@@ -0,0 +1,32 @@
package com.water.bpm.entity.dto;
import lombok.Data;
import java.util.Map;
/**
* 流程发起请求 DTO
*/
@Data
public class ProcessStartRequest {
/** 流程定义ID */
private Long definitionId;
/** 业务主键 */
private String businessKey;
/** 业务类型 */
private String businessType;
/** 流程标题 */
private String title;
/** 表单数据 */
private Map<String, Object> formData;
/** 流程变量 */
private Map<String, Object> variables;
/** 优先级 */
private Integer priority;
}
@@ -0,0 +1,60 @@
package com.water.bpm.entity.dto;
import lombok.Data;
import java.util.List;
import java.util.Map;
/**
* 流程统计 VO
*/
@Data
public class ProcessStatVO {
/** 流程定义ID */
private Long definitionId;
/** 流程名称 */
private String processName;
/** 流程标识 */
private String processKey;
/** 总实例数 */
private Integer totalInstances;
/** 运行中数量 */
private Integer runningCount;
/** 已完成数量 */
private Integer completedCount;
/** 已驳回数量 */
private Integer rejectedCount;
/** 已撤回数量 */
private Integer terminatedCount;
/** 平均耗时(小时) */
private Double avgDurationHours;
/** 最长耗时(小时) */
private Double maxDurationHours;
/** 最短耗时(小时) */
private Double minDurationHours;
/** 一次通过率 */
private Double firstPassRate;
/** 超时率 */
private Double timeoutRate;
/** 各节点平均耗时 */
private List<Map<String, Object>> nodeAvgDurations;
/** 瓶颈节点(耗时最长) */
private String bottleneckNode;
/** 趋势数据 */
private List<Map<String, Object>> trendData;
}
@@ -0,0 +1,39 @@
package com.water.bpm.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.bpm.entity.BpmApprovalRecord;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* 审批记录 Mapper
*/
@Mapper
public interface BpmApprovalRecordMapper extends BaseMapper<BpmApprovalRecord> {
/**
* 根据流程实例查询审批记录
*/
@Select("SELECT * FROM bpm_approval_record WHERE instance_id = #{instanceId} AND deleted = 0 ORDER BY approved_at")
List<BpmApprovalRecord> selectByInstanceId(@Param("instanceId") Long instanceId);
/**
* 根据流程实例UUID查询审批记录
*/
@Select("SELECT * FROM bpm_approval_record WHERE instance_uuid = #{instanceUuid} AND deleted = 0 ORDER BY approved_at")
List<BpmApprovalRecord> selectByInstanceUuid(@Param("instanceUuid") String instanceUuid);
/**
* 查询某人的审批统计
*/
@Select("SELECT approver_id, approver_name, COUNT(*) as total, " +
"SUM(CASE WHEN action = 'approve' THEN 1 ELSE 0 END) as approved, " +
"SUM(CASE WHEN action = 'reject' THEN 1 ELSE 0 END) as rejected, " +
"AVG(EXTRACT(EPOCH FROM (approved_at - created_at))) as avg_handle_seconds " +
"FROM bpm_approval_record WHERE deleted = 0 AND approved_at >= #{startDate} " +
"GROUP BY approver_id, approver_name")
List<java.util.Map<String, Object>> statByApprover(@Param("startDate") java.time.LocalDateTime startDate);
}
@@ -0,0 +1,28 @@
package com.water.bpm.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.bpm.entity.BpmFormTemplate;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* 表单模板 Mapper
*/
@Mapper
public interface BpmFormTemplateMapper extends BaseMapper<BpmFormTemplate> {
/**
* 根据分类查询模板
*/
@Select("SELECT * FROM bpm_form_template WHERE category = #{category} AND status = 1 AND deleted = 0 ORDER BY use_count DESC")
List<BpmFormTemplate> selectByCategory(@Param("category") String category);
/**
* 查询热门模板
*/
@Select("SELECT * FROM bpm_form_template WHERE status = 1 AND deleted = 0 ORDER BY use_count DESC LIMIT #{limit}")
List<BpmFormTemplate> selectHotTemplates(@Param("limit") int limit);
}
@@ -0,0 +1,28 @@
package com.water.bpm.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.bpm.entity.BpmOrchestration;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* 流程编排 Mapper
*/
@Mapper
public interface BpmOrchestrationMapper extends BaseMapper<BpmOrchestration> {
/**
* 查询所有启用的编排
*/
@Select("SELECT * FROM bpm_orchestration WHERE status = 1 AND deleted = 0 ORDER BY created_at DESC")
List<BpmOrchestration> selectEnabled();
/**
* 查询事件触发的编排
*/
@Select("SELECT * FROM bpm_orchestration WHERE trigger_type = 'event' AND event_name = #{eventName} " +
"AND status = 1 AND deleted = 0")
List<BpmOrchestration> selectByEvent(String eventName);
}
@@ -0,0 +1,34 @@
package com.water.bpm.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.bpm.entity.BpmProcessDefinition;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* 流程定义 Mapper
*/
@Mapper
public interface BpmProcessDefinitionMapper extends BaseMapper<BpmProcessDefinition> {
/**
* 根据分类查询已发布的流程定义
*/
@Select("SELECT * FROM bpm_process_definition WHERE category = #{category} AND status = 1 AND deleted = 0 ORDER BY sort_order")
List<BpmProcessDefinition> selectByCategory(@Param("category") String category);
/**
* 根据 processKey 查询最新版本
*/
@Select("SELECT * FROM bpm_process_definition WHERE process_key = #{processKey} AND deleted = 0 ORDER BY version DESC LIMIT 1")
BpmProcessDefinition selectByProcessKey(@Param("processKey") String processKey);
/**
* 查询所有分类
*/
@Select("SELECT DISTINCT category FROM bpm_process_definition WHERE deleted = 0 AND status = 1 ORDER BY category")
List<String> selectAllCategories();
}
@@ -0,0 +1,43 @@
package com.water.bpm.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.bpm.entity.BpmProcessInstance;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
import java.util.Map;
/**
* 流程实例 Mapper
*/
@Mapper
public interface BpmProcessInstanceMapper extends BaseMapper<BpmProcessInstance> {
/**
* 统计各状态数量
*/
@Select("SELECT status, COUNT(*) as count FROM bpm_process_instance WHERE deleted = 0 GROUP BY status")
List<Map<String, Object>> countByStatus();
/**
* 根据流程定义统计
*/
@Select("SELECT definition_id, COUNT(*) as total, " +
"SUM(CASE WHEN status = 'running' THEN 1 ELSE 0 END) as running, " +
"SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed, " +
"SUM(CASE WHEN status = 'rejected' THEN 1 ELSE 0 END) as rejected, " +
"SUM(CASE WHEN status = 'terminated' THEN 1 ELSE 0 END) as terminated, " +
"AVG(duration_seconds) as avg_duration, " +
"MAX(duration_seconds) as max_duration, " +
"MIN(duration_seconds) as min_duration " +
"FROM bpm_process_instance WHERE deleted = 0 GROUP BY definition_id")
List<Map<String, Object>> statByDefinition();
/**
* 查询我发起的流程
*/
@Select("SELECT * FROM bpm_process_instance WHERE initiator_id = #{userId} AND deleted = 0 ORDER BY created_at DESC")
List<BpmProcessInstance> selectMyInitiated(@Param("userId") Long userId);
}
@@ -0,0 +1,22 @@
package com.water.bpm.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.bpm.entity.BpmProcessNode;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* 流程节点 Mapper
*/
@Mapper
public interface BpmProcessNodeMapper extends BaseMapper<BpmProcessNode> {
/**
* 查询流程定义的所有节点
*/
@Select("SELECT * FROM bpm_process_node WHERE definition_id = #{definitionId} AND deleted = 0 ORDER BY sort_order")
List<BpmProcessNode> selectByDefinitionId(@Param("definitionId") Long definitionId);
}
@@ -0,0 +1,25 @@
package com.water.bpm.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.bpm.entity.BpmProcessStat;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* 流程统计 Mapper
*/
@Mapper
public interface BpmProcessStatMapper extends BaseMapper<BpmProcessStat> {
/**
* 查询某流程的统计趋势
*/
@Select("SELECT * FROM bpm_process_stat WHERE definition_id = #{definitionId} AND period = #{period} " +
"AND deleted = 0 ORDER BY stat_date DESC LIMIT #{limit}")
List<BpmProcessStat> selectTrend(@Param("definitionId") Long definitionId,
@Param("period") String period,
@Param("limit") int limit);
}
@@ -0,0 +1,42 @@
package com.water.bpm.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.bpm.entity.BpmTodoTask;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* 待办任务 Mapper
*/
@Mapper
public interface BpmTodoTaskMapper extends BaseMapper<BpmTodoTask> {
/**
* 查询待办列表
*/
@Select("SELECT * FROM bpm_todo_task WHERE assignee_id = #{userId} AND status = 'pending' AND deleted = 0 " +
"ORDER BY priority DESC, received_at")
List<BpmTodoTask> selectPendingByUserId(@Param("userId") Long userId);
/**
* 查询已办列表
*/
@Select("SELECT * FROM bpm_todo_task WHERE assignee_id = #{userId} AND status != 'pending' AND deleted = 0 " +
"ORDER BY completed_at DESC")
List<BpmTodoTask> selectDoneByUserId(@Param("userId") Long userId);
/**
* 统计待办数量
*/
@Select("SELECT COUNT(*) FROM bpm_todo_task WHERE assignee_id = #{userId} AND status = 'pending' AND deleted = 0")
Integer countPendingByUserId(@Param("userId") Long userId);
/**
* 查询超时的待办
*/
@Select("SELECT * FROM bpm_todo_task WHERE status = 'pending' AND deadline_at < NOW() AND deleted = 0")
List<BpmTodoTask> selectTimeoutTasks();
}