This commit is contained in:
2025-10-17 10:11:04 +08:00
commit 9618d5cfa1
2012 changed files with 163764 additions and 0 deletions

View File

@@ -0,0 +1,33 @@
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.jeelowcode</groupId>
<artifactId>jeelowcode-service</artifactId>
<version>${jeelowcode.version}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jeelowcode-service-bpm-api</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>
bpm 模块 API暴露给其它模块调用
</description>
<dependencies>
<dependency>
<groupId>com.jeelowcode</groupId>
<artifactId>tool-common</artifactId>
</dependency>
<!-- 参数校验 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,23 @@
package com.jeelowcode.service.bpm.api;
import com.jeelowcode.service.bpm.dto.BpmProcessInstanceCreateReqDTO;
import javax.validation.Valid;
/**
* 流程实例 Api 接口
*
* @author 芋道源码
*/
public interface IApiBpmProcessInstanceApi {
/**
* 创建流程实例(提供给内部)
*
* @param userId 用户编号
* @param reqDTO 创建信息
* @return 实例的编号
*/
String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO reqDTO);
}

View File

@@ -0,0 +1,27 @@
package com.jeelowcode.service.bpm.api;
import com.jeelowcode.service.bpm.dto.BpmResultListenerRespDTO;
// TODO @芋艿:后续改成支持 RPC
/**
* 业务流程实例的结果发生变化的监听器 Api
*
* @author HUIHUI
*/
public interface IApiBpmResultListenerApi {
/**
* 监听的流程定义 Key
*
* @return 返回监听的流程定义 Key
*/
String getProcessDefinitionKey();
/**
* 处理事件
*
* @param event 事件
*/
void onEvent(BpmResultListenerRespDTO event);
}

View File

@@ -0,0 +1,45 @@
package com.jeelowcode.service.bpm.dto;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import java.util.List;
import java.util.Map;
/**
* 流程实例的创建 Request DTO
*
* @author 芋道源码
*/
@Data
public class BpmProcessInstanceCreateReqDTO {
/**
* 流程定义的标识
*/
@NotEmpty(message = "流程定义的标识不能为空")
private String processDefinitionKey;
/**
* 变量实例
*/
private Map<String, Object> variables;
/**
* 业务的唯一标识
*
* 例如说,请假申请的编号。通过它,可以查询到对应的实例
*/
@NotEmpty(message = "业务的唯一标识")
private String businessKey;
// TODO @haiassignees 复数
/**
* 提前指派的审批人
*
* keytaskKey 任务编码
* value审批人的数组
* 例如: { taskKey1 :[1, 2] },则表示 taskKey1 这个任务,提前设定了,由 userId 为 1,2 的用户进行审批
*/
private Map<String, List<Long>> assignee;
}

View File

@@ -0,0 +1,32 @@
package com.jeelowcode.service.bpm.dto;
import lombok.Data;
// TODO @芋艿:后续改成支持 RPC
/**
* 业务流程实例的结果 Response DTO
*
* @author HUIHUI
*/
@Data
public class BpmResultListenerRespDTO {
/**
* 流程实例的编号
*/
private String id;
/**
* 流程实例的 key
*/
private String processDefinitionKey;
/**
* 流程实例的结果
*/
private Integer result;
/**
* 流程实例对应的业务标识
* 例如说,请假
*/
private String businessKey;
}

View File

@@ -0,0 +1,13 @@
package com.jeelowcode.service.bpm.enums;
/**
* BPM 字典类型的枚举类
*
* @author 芋道源码
*/
public interface DictTypeConstants {
String TASK_ASSIGN_RULE_TYPE = "bpm_task_assign_rule_type"; // 任务分配规则类型
String TASK_ASSIGN_SCRIPT = "bpm_task_assign_script"; // 任务分配自定义脚本
}

View File

@@ -0,0 +1,73 @@
package com.jeelowcode.service.bpm.enums;
import com.jeelowcode.tool.framework.common.exception.ErrorCode;
/**
* Bpm 错误码枚举类
* <p>
* bpm 系统,使用 1-009-000-000 段
*/
public interface ErrorCodeConstants {
// ========== 通用流程处理 模块 1-009-000-000 ==========
ErrorCode HIGHLIGHT_IMG_ERROR = new ErrorCode(1_009_000_002, "获取高亮流程图异常");
// ========== OA 流程模块 1-009-001-000 ==========
ErrorCode OA_LEAVE_NOT_EXISTS = new ErrorCode(1_009_001_001, "请假申请不存在");
ErrorCode OA_PM_POST_NOT_EXISTS = new ErrorCode(1_009_001_002, "项目经理岗位未设置");
ErrorCode OA_DEPART_PM_POST_NOT_EXISTS = new ErrorCode(1_009_001_009, "部门的项目经理不存在");
ErrorCode OA_BM_POST_NOT_EXISTS = new ErrorCode(1_009_001_004, "部门经理岗位未设置");
ErrorCode OA_DEPART_BM_POST_NOT_EXISTS = new ErrorCode(1_009_001_005, "部门的部门经理不存在");
ErrorCode OA_HR_POST_NOT_EXISTS = new ErrorCode(1_009_001_006, "HR岗位未设置");
ErrorCode OA_DAY_LEAVE_ERROR = new ErrorCode(1_009_001_007, "请假天数必须>=1");
// ========== 流程模型 1-009-002-000 ==========
ErrorCode MODEL_KEY_EXISTS = new ErrorCode(1_009_002_000, "已经存在流程标识为【{}】的流程");
ErrorCode MODEL_NOT_EXISTS = new ErrorCode(1_009_002_001, "流程模型不存在");
ErrorCode MODEL_KEY_VALID = new ErrorCode(1_009_002_002, "流程标识格式不正确,需要以字母或下划线开头,后接任意字母、数字、中划线、下划线、句点!");
ErrorCode MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG = new ErrorCode(1_009_002_003, "部署流程失败,原因:流程表单未配置,请点击【修改流程】按钮进行配置");
ErrorCode MODEL_DEPLOY_FAIL_TASK_ASSIGN_RULE_NOT_CONFIG = new ErrorCode(1_009_002_004, "部署流程失败," +
"原因:用户任务({})未配置分配规则,请点击【修改流程】按钮进行配置");
ErrorCode MODEL_DEPLOY_FAIL_TASK_INFO_EQUALS = new ErrorCode(1_009_003_005, "流程定义部署失败,原因:信息未发生变化");
// ========== 流程定义 1-009-003-000 ==========
ErrorCode PROCESS_DEFINITION_KEY_NOT_MATCH = new ErrorCode(1_009_003_000, "流程定义的标识期望是({}),当前是({}),请修改 BPMN 流程图");
ErrorCode PROCESS_DEFINITION_NAME_NOT_MATCH = new ErrorCode(1_009_003_001, "流程定义的名字期望是({}),当前是({}),请修改 BPMN 流程图");
ErrorCode PROCESS_DEFINITION_NOT_EXISTS = new ErrorCode(1_009_003_002, "流程定义不存在");
ErrorCode PROCESS_DEFINITION_IS_SUSPENDED = new ErrorCode(1_009_003_003, "流程定义处于挂起状态");
ErrorCode PROCESS_DEFINITION_BPMN_MODEL_NOT_EXISTS = new ErrorCode(1_009_003_004, "流程定义的模型不存在");
// ========== 流程实例 1-009-004-000 ==========
ErrorCode PROCESS_INSTANCE_NOT_EXISTS = new ErrorCode(1_009_004_000, "流程实例不存在");
ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS = new ErrorCode(1_009_004_001, "流程取消失败,流程不处于运行中");
ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF = new ErrorCode(1_009_004_002, "流程取消失败,该流程不是你发起的");
// ========== 流程任务 1-009-005-000 ==========
ErrorCode TASK_OPERATE_FAIL_ASSIGN_NOT_SELF = new ErrorCode(1_009_005_001, "操作失败,原因:该任务的审批人不是你");
ErrorCode TASK_NOT_EXISTS = new ErrorCode(1_009_005_002, "流程任务不存在");
ErrorCode TASK_IS_PENDING = new ErrorCode(1_009_005_003, "当前任务处于挂起状态,不能操作");
ErrorCode TASK_TARGET_NODE_NOT_EXISTS = new ErrorCode(1_009_005_004, " 目标节点不存在");
ErrorCode TASK_RETURN_FAIL_SOURCE_TARGET_ERROR = new ErrorCode(1_009_005_006, "回退任务失败,目标节点是在并行网关上或非同一路线上,不可跳转");
ErrorCode TASK_DELEGATE_FAIL_USER_REPEAT = new ErrorCode(1_009_005_007, "任务委派失败,委派人和当前审批人为同一人");
ErrorCode TASK_DELEGATE_FAIL_USER_NOT_EXISTS = new ErrorCode(1_009_005_008, "任务委派失败,被委派人不存在");
ErrorCode TASK_ADD_SIGN_USER_NOT_EXIST = new ErrorCode(1_009_005_009, "任务加签:选择的用户不存在");
ErrorCode TASK_ADD_SIGN_TYPE_ERROR = new ErrorCode(1_009_005_010, "任务加签:当前任务已经{},不能{}");
ErrorCode TASK_ADD_SIGN_USER_REPEAT = new ErrorCode(1_009_005_011, "任务加签失败,加签人与现有审批人[{}]重复");
ErrorCode TASK_SUB_SIGN_NO_PARENT = new ErrorCode(1_009_005_011, "任务减签失败,被减签的任务必须是通过加签生成的任务");
// ========== 流程任务分配规则 1-009-006-000 ==========
ErrorCode TASK_ASSIGN_RULE_EXISTS = new ErrorCode(1_009_006_000, "流程({}) 的任务({}) 已经存在分配规则");
ErrorCode TASK_ASSIGN_RULE_NOT_EXISTS = new ErrorCode(1_009_006_001, "流程任务分配规则不存在");
ErrorCode TASK_UPDATE_FAIL_NOT_MODEL = new ErrorCode(1_009_006_002, "只有流程模型的任务分配规则,才允许被修改");
ErrorCode TASK_CREATE_FAIL_NO_CANDIDATE_USER = new ErrorCode(1_009_006_003, "操作失败,原因:找不到任务的审批人!");
ErrorCode TASK_ASSIGN_SCRIPT_NOT_EXISTS = new ErrorCode(1_009_006_004, "操作失败,原因:任务分配脚本({}) 不存在");
// ========== 动态表单模块 1-009-010-000 ==========
ErrorCode FORM_NOT_EXISTS = new ErrorCode(1_009_010_000, "动态表单不存在");
ErrorCode FORM_FIELD_REPEAT = new ErrorCode(1_009_010_001, "表单项({}) 和 ({}) 使用了相同的字段名({})");
// ========== 用户组模块 1-009-011-000 ==========
ErrorCode USER_GROUP_NOT_EXISTS = new ErrorCode(1_009_011_000, "用户组不存在");
ErrorCode USER_GROUP_IS_DISABLE = new ErrorCode(1_009_011_001, "名字为【{}】的用户组已被禁用");
}

View File

@@ -0,0 +1,21 @@
package com.jeelowcode.service.bpm.enums.definition;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* BPM 模型的表单类型的枚举
*
* @author 芋道源码
*/
@Getter
@AllArgsConstructor
public enum BpmModelFormTypeEnum {
NORMAL(10, "流程表单"), // 对应 BpmFormDO
CUSTOM(20, "业务表单") // 业务自己定义的表单,自己进行数据的存储
;
private final Integer type;
private final String desc;
}

View File

@@ -0,0 +1,33 @@
package com.jeelowcode.service.bpm.enums.definition;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* BPM 任务分配规则的类型枚举
*
* @author 芋道源码
*/
@Getter
@AllArgsConstructor
public enum BpmTaskAssignRuleTypeEnum {
ROLE(10, "角色"),
DEPT_MEMBER(20, "部门的成员"), // 包括负责人
DEPT_LEADER(21, "部门的负责人"),
POST(22, "岗位"),
USER(30, "用户"),
USER_GROUP(40, "用户组"),
SCRIPT(50, "自定义脚本"), // 例如说,发起人所在部门的领导、发起人所在部门的领导的领导
;
/**
* 类型
*/
private final Integer type;
/**
* 描述
*/
private final String desc;
}

View File

@@ -0,0 +1,30 @@
package com.jeelowcode.service.bpm.enums.definition;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* BPM 任务规则的脚本枚举
* 目前暂时通过 TODO 芋艿:硬编码,未来可以考虑 Groovy 动态脚本的方式
*
* @author 芋道源码
*/
@Getter
@AllArgsConstructor
public enum BpmTaskRuleScriptEnum {
START_USER(10L, "流程发起人"),
LEADER_X1(20L, "流程发起人的一级领导"),
LEADER_X2(21L, "流程发起人的二级领导");
/**
* 脚本编号
*/
private final Long id;
/**
* 脚本描述
*/
private final String desc;
}

View File

@@ -0,0 +1,26 @@
package com.jeelowcode.service.bpm.enums.message;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* Bpm 消息的枚举
*
* @author 芋道源码
*/
@AllArgsConstructor
@Getter
public enum BpmMessageEnum {
PROCESS_INSTANCE_APPROVE("bpm_process_instance_approve"), // 流程任务被审批通过时,发送给申请人
PROCESS_INSTANCE_REJECT("bpm_process_instance_reject"), // 流程任务被审批不通过时,发送给申请人
TASK_ASSIGNED("bpm_task_assigned"); // 任务被分配时,发送给审批人
/**
* 短信模板的标识
*
* 关联 SmsTemplateDO 的 code 属性
*/
private final String smsTemplateCode;
}

View File

@@ -0,0 +1,35 @@
package com.jeelowcode.service.bpm.enums.task;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 流程任务 -- comment类型枚举
*/
@Getter
@AllArgsConstructor
public enum BpmCommentTypeEnum {
APPROVE(1, "通过", ""),
REJECT(2, "不通过", ""),
CANCEL(3, "已取消", ""),
BACK(4, "退回", ""),
DELEGATE(5, "委派", ""),
ADD_SIGN(6, "加签", "[{}]{}给了[{}],理由为:{}"),
SUB_SIGN(7, "减签", "[{}]操作了【减签】,审批人[{}]的任务被取消"),
;
/**
* 操作类型
*/
private final Integer type;
/**
* 操作名字
*/
private final String name;
/**
* 操作描述
*/
private final String comment;
}

View File

@@ -0,0 +1,58 @@
package com.jeelowcode.service.bpm.enums.task;
import cn.hutool.core.util.StrUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 流程实例的删除原因
*
* @author 芋道源码
*/
@Getter
@AllArgsConstructor
public enum BpmProcessInstanceDeleteReasonEnum {
REJECT_TASK("不通过任务,原因:{}"), // 修改文案时,需要注意 isRejectReason 方法
CANCEL_TASK("主动取消任务,原因:{}"),
// ========== 流程任务的独有原因 ==========
MULTI_TASK_END("系统自动取消,原因:多任务审批已经满足条件,无需审批该任务"), // 多实例满足 condition 而结束时,其它任务实例任务会被取消,对应的删除原因是 MI_END
;
private final String reason;
/**
* 格式化理由
*
* @param args 参数
* @return 理由
*/
public String format(Object... args) {
return StrUtil.format(reason, args);
}
// ========== 逻辑 ==========
public static boolean isRejectReason(String reason) {
return StrUtil.startWith(reason, "不通过任务,原因:");
}
/**
* 将 Flowable 的删除原因,翻译成对应的中文原因
*
* @param reason 原始原因
* @return 原因
*/
public static String translateReason(String reason) {
if (StrUtil.isEmpty(reason)) {
return reason;
}
switch (reason) {
case "MI_END": return MULTI_TASK_END.getReason();
default: return reason;
}
}
}

View File

@@ -0,0 +1,77 @@
package com.jeelowcode.service.bpm.enums.task;
import com.jeelowcode.tool.framework.common.util.object.ObjectUtils;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
import java.util.List;
/**
* 流程实例的结果
*
* @author jason
*/
@Getter
@AllArgsConstructor
public enum BpmProcessInstanceResultEnum {
PROCESS(1, "处理中"),
APPROVE(2, "通过"),
REJECT(3, "不通过"),
CANCEL(4, "已取消"),
// ========== 流程任务独有的状态 ==========
BACK(5, "驳回"), // 退回
DELEGATE(6, "委派"),
/**
* 【加签】源任务已经审批完成,但是它使用了后加签,后加签的任务未完成,源任务就会是这个状态
* 相当于是 通过 APPROVE 的特殊状态
* 例如A审批 A 后加签了 B并且审批通过了任务但是 B 还未审批,则当前任务状态为“待后加签任务完成”
*/
SIGN_AFTER(7, "待后加签任务完成"),
/**
* 【加签】源任务未审批,但是向前加签了,所以源任务状态变为“待前加签任务完成”
* 相当于是 处理中 PROCESS 的特殊状态
* 例如A 审批, A 前加签了 BB 还未审核
*/
SIGN_BEFORE(8, "待前加签任务完成"),
/**
* 【加签】后加签任务被创建时的初始状态
* 相当于是 处理中 PROCESS 的特殊状态
* 因为需要源任务先完成,才能到后加签的人来审批,所以加了一个状态区分
*/
WAIT_BEFORE_TASK(9, "待前置任务完成");
/**
* 能被减签的状态
*/
public static final List<Integer> CAN_SUB_SIGN_STATUS_LIST = Arrays.asList(PROCESS.result, WAIT_BEFORE_TASK.result);
/**
* 结果
* <p>
* 如果新增时,注意 {@link #isEndResult(Integer)} 是否需要变更
*/
private final Integer result;
/**
* 描述
*/
private final String desc;
/**
* 判断该结果是否已经处于 End 最终结果
* <p>
* 主要用于一些结果更新的逻辑,如果已经是最终结果,就不再进行更新
*
* @param result 结果
* @return 是否
*/
public static boolean isEndResult(Integer result) {
return ObjectUtils.equalsAny(result, APPROVE.getResult(), REJECT.getResult(),
CANCEL.getResult(), BACK.getResult(),
SIGN_AFTER.getResult());
}
}

View File

@@ -0,0 +1,27 @@
package com.jeelowcode.service.bpm.enums.task;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 流程实例的状态
*
* @author 芋道源码
*/
@Getter
@AllArgsConstructor
public enum BpmProcessInstanceStatusEnum {
RUNNING(1, "进行中"),
FINISH(2, "已完成");
/**
* 状态
*/
private final Integer status;
/**
* 描述
*/
private final String desc;
}

View File

@@ -0,0 +1,40 @@
package com.jeelowcode.service.bpm.enums.task;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 流程任务 -- 加签类型枚举类型
*/
@Getter
@AllArgsConstructor
public enum BpmTaskAddSignTypeEnum {
/**
* 向前加签,需要前置任务审批完成,才回到原审批人
*/
BEFORE("before", "向前加签"),
/**
* 向后加签,需要后置任务全部审批完,才会通过原审批人节点
*/
AFTER("after", "向后加签"),
/**
* 创建后置加签时的过度状态,用于控制向后加签生成的任务状态
*/
AFTER_CHILDREN_TASK("afterChildrenTask", "向后加签生成的子任务");
private final String type;
private final String desc;
public static String formatDesc(String type) {
for (BpmTaskAddSignTypeEnum value : values()) {
if (value.type.equals(type)) {
return value.desc;
}
}
return null;
}
}

View File

@@ -0,0 +1,86 @@
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.jeelowcode</groupId>
<artifactId>jeelowcode-service</artifactId>
<version>${jeelowcode.version}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jeelowcode-service-bpm-biz</artifactId>
<name>${project.artifactId}</name>
<description>
bpm 包下业务流程管理Business Process Management我们放工作流的功能基于 Flowable 6 版本实现。
例如说:流程定义、表单配置、审核中心(我的申请、我的待办、我的已办)等等
</description>
<dependencies>
<dependency>
<groupId>com.jeelowcode</groupId>
<artifactId>jeelowcode-service-bpm-api</artifactId>
<version>${jeelowcode.version}</version>
</dependency>
<dependency>
<groupId>com.jeelowcode</groupId>
<artifactId>jeelowcode-service-system-api</artifactId>
<version>${jeelowcode.version}</version>
</dependency>
<!-- 业务组件 -->
<dependency>
<groupId>com.jeelowcode</groupId>
<artifactId>tool-spring-boot-starter-biz-operatelog</artifactId>
</dependency>
<dependency>
<groupId>com.jeelowcode</groupId>
<artifactId>tool-spring-boot-starter-biz-data-permission</artifactId>
</dependency>
<dependency>
<groupId>com.jeelowcode</groupId>
<artifactId>tool-spring-boot-starter-biz-tenant</artifactId>
</dependency>
<!-- Web 相关 -->
<dependency>
<groupId>com.jeelowcode</groupId>
<artifactId>tool-spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.jeelowcode</groupId>
<artifactId>tool-spring-boot-starter-security</artifactId>
</dependency>
<!-- DB 相关 -->
<dependency>
<groupId>com.jeelowcode</groupId>
<artifactId>tool-spring-boot-starter-mybatis</artifactId>
</dependency>
<!-- Test 测试相关 -->
<dependency>
<groupId>com.jeelowcode</groupId>
<artifactId>tool-spring-boot-starter-test</artifactId>
</dependency>
<!-- 工作流相关 -->
<dependency>
<groupId>com.jeelowcode</groupId>
<artifactId>tool-spring-boot-starter-flowable</artifactId>
</dependency>
<!--个人业务-->
<dependency>
<groupId>com.jeelowcode</groupId>
<artifactId>jeelowcode-module</artifactId>
<version>2.0.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.jeelowcode</groupId>
<artifactId>jeelowcode-core</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,28 @@
package com.jeelowcode.service.bpm.api;
import com.jeelowcode.service.bpm.dto.BpmProcessInstanceCreateReqDTO;
import com.jeelowcode.service.bpm.service.IBpmProcessInstanceService;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import javax.validation.Valid;
/**
* Flowable 流程实例 Api 实现类
*
* @author 芋道源码
* @author jason
*/
@Service
@Validated
public class ApiBpmProcessInstanceApiImpl implements IApiBpmProcessInstanceApi {
@Resource
private IBpmProcessInstanceService processInstanceService;
@Override
public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO reqDTO) {
return processInstanceService.createProcessInstance(userId, reqDTO);
}
}

View File

@@ -0,0 +1,23 @@
package com.jeelowcode.service.bpm.api;
import com.jeelowcode.service.bpm.dto.BpmResultListenerRespDTO;
import org.springframework.stereotype.Service;
/**
* @author JX
* @create 2024-07-09 11:22
* @dedescription:
*/
@Service
public class ApiBpmResultListenerApiImpl implements IApiBpmResultListenerApi {
@Override
public String getProcessDefinitionKey() {
return null;
}
@Override
public void onEvent(BpmResultListenerRespDTO event) {
}
}

View File

@@ -0,0 +1,4 @@
/**
* bpm API 实现类,定义暴露给其它模块的 API
*/
package com.jeelowcode.service.bpm.api;

View File

@@ -0,0 +1,45 @@
package com.jeelowcode.service.bpm.config.candidate;
import com.jeelowcode.service.bpm.controller.vo.candidate.BpmTaskCandidateRuleVO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.HashSet;
import java.util.Set;
/**
* 获取候选人信息
*/
@AllArgsConstructor
@NoArgsConstructor
@Data
public class BpmCandidateSourceInfo {
@Schema(description = "当前任务ID")
@NotNull
private String taskId;
/**
* 通过这些规则,生成最终需要生成的用户
*/
@Schema(description = "当前任务预选规则")
@NotEmpty(message = "不允许空规则")
private Set<BpmTaskCandidateRuleVO> rules;
@Schema(description = "发起抄送的用户")
private String creator;
@Schema(description = "抄送原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "请帮忙审查下!")
@NotEmpty(message = "抄送原因不能为空")
private String reason;
public void addRule(BpmTaskCandidateRuleVO vo) {
assert vo != null;
if (rules == null) {
rules = new HashSet<>();
}
rules.add(vo);
}
}

View File

@@ -0,0 +1,53 @@
package com.jeelowcode.service.bpm.config.candidate;
import cn.hutool.core.collection.CollUtil;
import com.jeelowcode.service.bpm.controller.vo.candidate.BpmTaskCandidateRuleVO;
import com.jeelowcode.service.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
import org.flowable.engine.delegate.DelegateExecution;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
public interface BpmCandidateSourceInfoProcessor {
/**
* 获取该处理器支持的类型
* 来自 {@link BpmTaskAssignRuleTypeEnum}
*
* @return
*/
Set<Integer> getSupportedTypes();
/**
* 对规则和人员做校验
*
* @param type 规则
* @param options 人员id
*/
void validRuleOptions(Integer type, Set<Long> options);
/**
* 默认的处理
* 如果想去操作所有的规则,则可以覆盖此方法
*
* @param request 原始请求
* @param delegateExecution 审批过程中的对象
* @return 必须包含的是用户ID而不是其他的ID
* @throws Exception
*/
default Set<Long> process(BpmCandidateSourceInfo request, DelegateExecution delegateExecution) throws Exception {
Set<BpmTaskCandidateRuleVO> rules = request.getRules();
Set<Long> results = new HashSet<>();
for (BpmTaskCandidateRuleVO rule : rules) {
// 每个处理器都有机会处理自己支持的事件
if (CollUtil.contains(getSupportedTypes(), rule.getType())) {
results.addAll(doProcess(request, rule, delegateExecution));
}
}
return results;
}
default Set<Long> doProcess(BpmCandidateSourceInfo request, BpmTaskCandidateRuleVO rule, DelegateExecution delegateExecution) {
return Collections.emptySet();
}
}

View File

@@ -0,0 +1,107 @@
package com.jeelowcode.service.bpm.config.candidate;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import com.jeelowcode.tool.framework.common.enums.CommonStatusEnum;
import com.jeelowcode.service.bpm.controller.vo.candidate.BpmTaskCandidateRuleVO;
import com.jeelowcode.service.system.api.IApiAdminUserApi;
import com.jeelowcode.service.system.dto.AdminUserRespDTO;
import org.flowable.engine.delegate.DelegateExecution;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.*;
@Service
public class BpmCandidateSourceInfoProcessorChain {
// 保存处理节点
private List<BpmCandidateSourceInfoProcessor> processorList;
@Resource
private IApiAdminUserApi apiAdminUserApi;
/**
* 可添加其他处理器
*
* @param processorOp
* @return
*/
@Resource
// 动态扩展处理节点
public BpmCandidateSourceInfoProcessorChain addProcessor(ObjectProvider<BpmCandidateSourceInfoProcessor> processorOp) {
List<BpmCandidateSourceInfoProcessor> processor = ListUtil.toList(processorOp.iterator());
if (null == processorList) {
processorList = new ArrayList<>(processor.size());
}
processorList.addAll(processor);
return this;
}
// 获取处理器处理
public Set<Long> process(BpmCandidateSourceInfo sourceInfo, DelegateExecution execution) throws Exception {
// Verify our parameters
if (sourceInfo == null) {
throw new IllegalArgumentException();
}
for (BpmCandidateSourceInfoProcessor processor : processorList) {
try {
for (BpmTaskCandidateRuleVO vo : sourceInfo.getRules()) {
if (CollUtil.contains(processor.getSupportedTypes(), vo.getType())) {
processor.validRuleOptions(vo.getType(), vo.getOptions());
}
}
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
Set<Long> saveResult = Collections.emptySet();
Exception saveException = null;
for (BpmCandidateSourceInfoProcessor processor : processorList) {
try {
saveResult = processor.process(sourceInfo, execution);
if (CollUtil.isNotEmpty(saveResult)) {
removeDisableUsers(saveResult);
break;
}
} catch (Exception e) {
saveException = e;
break;
}
}
// Return the exception or result state from the last execute()
if ((saveException != null)) {
throw saveException;
} else {
return (saveResult);
}
}
public Set<Long> calculateTaskCandidateUsers(DelegateExecution execution, BpmCandidateSourceInfo sourceInfo) {
Set<Long> results = Collections.emptySet();
try {
results = process(sourceInfo, execution);
} catch (Exception e) {
e.printStackTrace();
}
return results;
}
/**
* 移除禁用用户
*
* @param assigneeUserIds
*/
public void removeDisableUsers(Set<Long> assigneeUserIds) {
if (CollUtil.isEmpty(assigneeUserIds)) {
return;
}
Map<Long, AdminUserRespDTO> userMap = apiAdminUserApi.getUserMap(assigneeUserIds);
assigneeUserIds.removeIf(id -> {
AdminUserRespDTO user = userMap.get(id);
return user == null || !CommonStatusEnum.ENABLE.getStatus().equals(user.getStatus());
});
}
}

View File

@@ -0,0 +1,32 @@
package com.jeelowcode.service.bpm.config.candidate.sourceInfoProcessor;
import com.jeelowcode.tool.framework.common.util.collection.SetUtils;
import com.jeelowcode.service.bpm.controller.vo.candidate.BpmTaskCandidateRuleVO;
import com.jeelowcode.service.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
import com.jeelowcode.service.bpm.config.candidate.BpmCandidateSourceInfo;
import com.jeelowcode.service.bpm.config.candidate.BpmCandidateSourceInfoProcessor;
import com.jeelowcode.service.system.api.IApiAdminUserApi;
import org.flowable.engine.delegate.DelegateExecution;
import javax.annotation.Resource;
import java.util.Set;
public class BpmCandidateAdminUserApiSourceInfoProcessor implements BpmCandidateSourceInfoProcessor {
@Resource
private IApiAdminUserApi api;
@Override
public Set<Integer> getSupportedTypes() {
return SetUtils.asSet(BpmTaskAssignRuleTypeEnum.USER.getType());
}
@Override
public void validRuleOptions(Integer type, Set<Long> options) {
api.validateUserList(options);
}
@Override
public Set<Long> doProcess(BpmCandidateSourceInfo request, BpmTaskCandidateRuleVO rule, DelegateExecution delegateExecution) {
return rule.getOptions();
}
}

View File

@@ -0,0 +1,50 @@
package com.jeelowcode.service.bpm.config.candidate.sourceInfoProcessor;
import com.jeelowcode.tool.framework.common.util.collection.SetUtils;
import com.jeelowcode.service.bpm.controller.vo.candidate.BpmTaskCandidateRuleVO;
import com.jeelowcode.service.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
import com.jeelowcode.service.bpm.config.candidate.BpmCandidateSourceInfo;
import com.jeelowcode.service.bpm.config.candidate.BpmCandidateSourceInfoProcessor;
import com.jeelowcode.service.system.api.IApiDeptApi;
import com.jeelowcode.service.system.dto.DeptRespDTO;
import com.jeelowcode.service.system.api.IApiAdminUserApi;
import com.jeelowcode.service.system.dto.AdminUserRespDTO;
import org.flowable.engine.delegate.DelegateExecution;
import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import static com.jeelowcode.tool.framework.common.util.collection.CollectionUtils.convertSet;
public class BpmCandidateDeptApiSourceInfoProcessor implements BpmCandidateSourceInfoProcessor {
@Resource
private IApiDeptApi api;
@Resource
private IApiAdminUserApi apiAdminUserApi;
@Override
public Set<Integer> getSupportedTypes() {
return SetUtils.asSet(BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(),
BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType());
}
@Override
public void validRuleOptions(Integer type, Set<Long> options) {
api.validateDeptList(options);
}
@Override
public Set<Long> doProcess(BpmCandidateSourceInfo request, BpmTaskCandidateRuleVO rule, DelegateExecution delegateExecution) {
if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(), rule.getType())) {
List<AdminUserRespDTO> users = apiAdminUserApi.getUserListByDeptIds(rule.getOptions());
return convertSet(users, AdminUserRespDTO::getId);
} else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType(), rule.getType())) {
List<DeptRespDTO> depts = api.getDeptList(rule.getOptions());
return convertSet(depts, DeptRespDTO::getLeaderUserId);
}
return Collections.emptySet();
}
}

View File

@@ -0,0 +1,40 @@
package com.jeelowcode.service.bpm.config.candidate.sourceInfoProcessor;
import com.jeelowcode.tool.framework.common.util.collection.SetUtils;
import com.jeelowcode.service.bpm.controller.vo.candidate.BpmTaskCandidateRuleVO;
import com.jeelowcode.service.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
import com.jeelowcode.service.bpm.config.candidate.BpmCandidateSourceInfo;
import com.jeelowcode.service.bpm.config.candidate.BpmCandidateSourceInfoProcessor;
import com.jeelowcode.service.system.api.IApiPostApi;
import com.jeelowcode.service.system.api.IApiAdminUserApi;
import com.jeelowcode.service.system.dto.AdminUserRespDTO;
import org.flowable.engine.delegate.DelegateExecution;
import javax.annotation.Resource;
import java.util.List;
import java.util.Set;
import static com.jeelowcode.tool.framework.common.util.collection.CollectionUtils.convertSet;
public class BpmCandidatePostApiSourceInfoProcessor implements BpmCandidateSourceInfoProcessor {
@Resource
private IApiPostApi api;
@Resource
private IApiAdminUserApi apiAdminUserApi;
@Override
public Set<Integer> getSupportedTypes() {
return SetUtils.asSet(BpmTaskAssignRuleTypeEnum.POST.getType());
}
@Override
public void validRuleOptions(Integer type, Set<Long> options) {
api.validPostList(options);
}
@Override
public Set<Long> doProcess(BpmCandidateSourceInfo request, BpmTaskCandidateRuleVO rule, DelegateExecution delegateExecution) {
List<AdminUserRespDTO> users = apiAdminUserApi.getUserListByPostIds(rule.getOptions());
return convertSet(users, AdminUserRespDTO::getId);
}
}

View File

@@ -0,0 +1,37 @@
package com.jeelowcode.service.bpm.config.candidate.sourceInfoProcessor;
import com.jeelowcode.tool.framework.common.util.collection.SetUtils;
import com.jeelowcode.service.bpm.controller.vo.candidate.BpmTaskCandidateRuleVO;
import com.jeelowcode.service.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
import com.jeelowcode.service.bpm.config.candidate.BpmCandidateSourceInfo;
import com.jeelowcode.service.bpm.config.candidate.BpmCandidateSourceInfoProcessor;
import com.jeelowcode.service.system.api.IApiPermissionApi;
import com.jeelowcode.service.system.api.IApiRoleApi;
import org.flowable.engine.delegate.DelegateExecution;
import javax.annotation.Resource;
import java.util.Set;
public class BpmCandidateRoleApiSourceInfoProcessor implements BpmCandidateSourceInfoProcessor {
@Resource
private IApiRoleApi api;
@Resource
private IApiPermissionApi apiPermissionApi;
@Override
public Set<Integer> getSupportedTypes() {
return SetUtils.asSet(BpmTaskAssignRuleTypeEnum.ROLE.getType());
}
@Override
public void validRuleOptions(Integer type, Set<Long> options) {
api.validRoleList(options);
}
@Override
public Set<Long> doProcess(BpmCandidateSourceInfo request, BpmTaskCandidateRuleVO rule, DelegateExecution delegateExecution) {
return apiPermissionApi.getUserRoleIdListByRoleIds(rule.getOptions());
}
}

View File

@@ -0,0 +1,73 @@
package com.jeelowcode.service.bpm.config.candidate.sourceInfoProcessor;
import cn.hutool.core.collection.CollUtil;
import com.jeelowcode.tool.framework.common.util.collection.CollectionUtils;
import com.jeelowcode.tool.framework.common.util.collection.SetUtils;
import com.jeelowcode.service.bpm.controller.vo.candidate.BpmTaskCandidateRuleVO;
import com.jeelowcode.service.bpm.enums.DictTypeConstants;
import com.jeelowcode.service.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
import com.jeelowcode.service.bpm.config.framework.flowable.core.behavior.script.BpmTaskAssignScript;
import com.jeelowcode.service.bpm.config.candidate.BpmCandidateSourceInfo;
import com.jeelowcode.service.bpm.config.candidate.BpmCandidateSourceInfoProcessor;
import com.jeelowcode.service.system.api.IApiDictDataApi;
import org.flowable.engine.delegate.DelegateExecution;
import org.springframework.beans.factory.ObjectProvider;
import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;
import static com.jeelowcode.tool.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.jeelowcode.tool.framework.common.util.collection.CollectionUtils.convertMap;
import static com.jeelowcode.service.bpm.enums.ErrorCodeConstants.TASK_ASSIGN_SCRIPT_NOT_EXISTS;
public class BpmCandidateScriptApiSourceInfoProcessor implements BpmCandidateSourceInfoProcessor {
@Resource
private IApiDictDataApi apiDictDataApi;
/**
* 任务分配脚本
*/
private Map<Long, BpmTaskAssignScript> scriptMap = Collections.emptyMap();
public void setScripts(ObjectProvider<BpmTaskAssignScript> scriptsOp) {
List<BpmTaskAssignScript> scripts = scriptsOp.orderedStream().collect(Collectors.toList());
setScripts(scripts);
}
public void setScripts(List<BpmTaskAssignScript> scripts) {
this.scriptMap = convertMap(scripts, script -> script.getEnum().getId());
}
@Override
public Set<Integer> getSupportedTypes() {
return SetUtils.asSet(BpmTaskAssignRuleTypeEnum.SCRIPT.getType());
}
@Override
public void validRuleOptions(Integer type, Set<Long> options) {
apiDictDataApi.validateDictDataList(DictTypeConstants.TASK_ASSIGN_SCRIPT,
CollectionUtils.convertSet(options, String::valueOf));
}
@Override
public Set<Long> doProcess(BpmCandidateSourceInfo request, BpmTaskCandidateRuleVO rule, DelegateExecution delegateExecution) {
return calculateTaskCandidateUsersByScript(delegateExecution, rule.getOptions());
}
private Set<Long> calculateTaskCandidateUsersByScript(DelegateExecution execution, Set<Long> options) {
// 获得对应的脚本
List<BpmTaskAssignScript> scripts = new ArrayList<>(options.size());
options.forEach(id -> {
BpmTaskAssignScript script = scriptMap.get(id);
if (script == null) {
throw exception(TASK_ASSIGN_SCRIPT_NOT_EXISTS, id);
}
scripts.add(script);
});
// 逐个计算任务
Set<Long> userIds = new HashSet<>();
scripts.forEach(script -> CollUtil.addAll(userIds, script.calculateTaskCandidateUsers(execution)));
return userIds;
}
}

View File

@@ -0,0 +1,41 @@
package com.jeelowcode.service.bpm.config.candidate.sourceInfoProcessor;
import com.jeelowcode.tool.framework.common.util.collection.SetUtils;
import com.jeelowcode.service.bpm.controller.vo.candidate.BpmTaskCandidateRuleVO;
import com.jeelowcode.service.bpm.entity.BpmUserGroupDO;
import com.jeelowcode.service.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
import com.jeelowcode.service.bpm.config.candidate.BpmCandidateSourceInfo;
import com.jeelowcode.service.bpm.config.candidate.BpmCandidateSourceInfoProcessor;
import com.jeelowcode.service.bpm.service.IBpmUserGroupService;
import org.flowable.engine.delegate.DelegateExecution;
import javax.annotation.Resource;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class BpmCandidateUserGroupApiSourceInfoProcessor implements BpmCandidateSourceInfoProcessor {
@Resource
private IBpmUserGroupService api;
@Resource
private IBpmUserGroupService userGroupService;
@Override
public Set<Integer> getSupportedTypes() {
return SetUtils.asSet(BpmTaskAssignRuleTypeEnum.USER_GROUP.getType());
}
@Override
public void validRuleOptions(Integer type, Set<Long> options) {
api.validUserGroups(options);
}
@Override
public Set<Long> doProcess(BpmCandidateSourceInfo request, BpmTaskCandidateRuleVO rule, DelegateExecution delegateExecution) {
List<BpmUserGroupDO> userGroups = userGroupService.getUserGroupList(rule.getOptions());
Set<Long> userIds = new HashSet<>();
userGroups.forEach(group -> userIds.addAll(group.getMemberUserIds()));
return userIds;
}
}

View File

@@ -0,0 +1,40 @@
package com.jeelowcode.service.bpm.config.convert.cc;
import com.jeelowcode.tool.framework.common.pojo.PageResult;
import com.jeelowcode.tool.framework.common.util.collection.MapUtils;
import com.jeelowcode.tool.framework.common.util.object.BeanUtils;
import com.jeelowcode.service.bpm.controller.vo.instance.BpmProcessInstanceCopyPageItemRespVO;
import com.jeelowcode.service.bpm.entity.BpmProcessInstanceCopyDO;
import com.jeelowcode.service.system.dto.AdminUserRespDTO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
import java.util.Map;
/**
* 流程抄送 Convert
*
* @author 芋艿
*/
@Mapper
public interface BpmProcessInstanceCopyConvert {
BpmProcessInstanceCopyConvert INSTANCE = Mappers.getMapper(BpmProcessInstanceCopyConvert.class);
default PageResult<BpmProcessInstanceCopyPageItemRespVO> convertPage(PageResult<BpmProcessInstanceCopyDO> page,
Map<String, String> taskNameMap,
Map<String, String> processInstaneNameMap,
Map<Long, AdminUserRespDTO> userMap) {
List<BpmProcessInstanceCopyPageItemRespVO> list = BeanUtils.toBean(page.getList(),
BpmProcessInstanceCopyPageItemRespVO.class,
copy -> {
MapUtils.findAndThen(userMap, Long.valueOf(copy.getCreator()), user -> user.setNickname(user.getNickname()));
MapUtils.findAndThen(userMap, copy.getStartUserId(), user -> copy.setStartUserNickname(user.getNickname()));
MapUtils.findAndThen(taskNameMap, copy.getTaskId(), copy::setTaskName);
MapUtils.findAndThen(processInstaneNameMap, copy.getProcessInstanceId(), copy::setProcessInstanceName);
});
return new PageResult<>(list, page.getTotal());
}
}

View File

@@ -0,0 +1,34 @@
package com.jeelowcode.service.bpm.config.convert.definition;
import com.jeelowcode.service.bpm.controller.vo.form.BpmFormCreateReqVO;
import com.jeelowcode.service.bpm.controller.vo.form.BpmFormRespVO;
import com.jeelowcode.service.bpm.controller.vo.form.BpmFormSimpleRespVO;
import com.jeelowcode.service.bpm.controller.vo.form.BpmFormUpdateReqVO;
import com.jeelowcode.service.bpm.entity.BpmFormDO;
import com.jeelowcode.tool.framework.common.pojo.PageResult;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* 动态表单 Convert
*
* @author 芋艿
*/
@Mapper
public interface BpmFormConvert {
BpmFormConvert INSTANCE = Mappers.getMapper(BpmFormConvert.class);
BpmFormDO convert(BpmFormCreateReqVO bean);
BpmFormDO convert(BpmFormUpdateReqVO bean);
BpmFormRespVO convert(BpmFormDO bean);
List<BpmFormSimpleRespVO> convertList2(List<BpmFormDO> list);
PageResult<BpmFormRespVO> convertPage(PageResult<BpmFormDO> page);
}

View File

@@ -0,0 +1,140 @@
package com.jeelowcode.service.bpm.config.convert.definition;
import cn.hutool.core.util.StrUtil;
import com.jeelowcode.service.bpm.controller.vo.model.*;
import com.jeelowcode.service.bpm.dto.BpmModelMetaInfoRespDTO;
import com.jeelowcode.service.bpm.dto.BpmProcessDefinitionCreateReqDTO;
import com.jeelowcode.service.bpm.entity.BpmFormDO;
import com.jeelowcode.tool.framework.common.util.collection.CollectionUtils;
import com.jeelowcode.tool.framework.common.util.date.DateUtils;
import com.jeelowcode.tool.framework.common.util.json.JsonUtils;
import org.flowable.common.engine.impl.db.SuspensionState;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.Model;
import org.flowable.engine.repository.ProcessDefinition;
import org.mapstruct.Mapper;
import org.mapstruct.MappingTarget;
import org.mapstruct.factory.Mappers;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* 流程模型 Convert
*
* @author yunlongn
*/
@Mapper
public interface BpmModelConvert {
BpmModelConvert INSTANCE = Mappers.getMapper(BpmModelConvert.class);
default List<BpmModelPageItemRespVO> convertList(List<Model> list, Map<Long, BpmFormDO> formMap,
Map<String, Deployment> deploymentMap,
Map<String, ProcessDefinition> processDefinitionMap) {
return CollectionUtils.convertList(list, model -> {
BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class);
BpmFormDO form = metaInfo != null ? formMap.get(metaInfo.getFormId()) : null;
Deployment deployment = model.getDeploymentId() != null ? deploymentMap.get(model.getDeploymentId()) : null;
ProcessDefinition processDefinition = model.getDeploymentId() != null ? processDefinitionMap.get(model.getDeploymentId()) : null;
return convert(model, form, deployment, processDefinition);
});
}
default BpmModelPageItemRespVO convert(Model model, BpmFormDO form, Deployment deployment, ProcessDefinition processDefinition) {
BpmModelPageItemRespVO modelRespVO = new BpmModelPageItemRespVO();
modelRespVO.setId(model.getId());
modelRespVO.setCreateTime(DateUtils.of(model.getCreateTime()));
// 通用 copy
copyTo(model, modelRespVO);
// Form
if (form != null) {
modelRespVO.setFormId(form.getId());
modelRespVO.setFormName(form.getDesformName());
}
// ProcessDefinition
modelRespVO.setProcessDefinition(this.convert(processDefinition));
if (modelRespVO.getProcessDefinition() != null) {
modelRespVO.getProcessDefinition().setSuspensionState(processDefinition.isSuspended() ?
SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode());
modelRespVO.getProcessDefinition().setDeploymentTime(DateUtils.of(deployment.getDeploymentTime()));
}
return modelRespVO;
}
default BpmModelRespVO convert(Model model) {
BpmModelRespVO modelRespVO = new BpmModelRespVO();
modelRespVO.setId(model.getId());
modelRespVO.setCreateTime(DateUtils.of(model.getCreateTime()));
// 通用 copy
copyTo(model, modelRespVO);
return modelRespVO;
}
default void copyTo(Model model, BpmModelBaseVO to) {
to.setName(model.getName());
to.setKey(model.getKey());
to.setCategory(model.getCategory());
// metaInfo
BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class);
copyTo(metaInfo, to);
}
BpmModelCreateReqVO convert(BpmModeImportReqVO bean);
default BpmProcessDefinitionCreateReqDTO convert2(Model model, BpmFormDO form) {
BpmProcessDefinitionCreateReqDTO createReqDTO = new BpmProcessDefinitionCreateReqDTO();
createReqDTO.setModelId(model.getId());
createReqDTO.setName(model.getName());
createReqDTO.setKey(model.getKey());
createReqDTO.setCategory(model.getCategory());
BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class);
// metaInfo
copyTo(metaInfo, createReqDTO);
// form
if (form != null) {
createReqDTO.setFormConf(form.getDesformJson());
}
return createReqDTO;
}
void copyTo(BpmModelMetaInfoRespDTO from, @MappingTarget BpmProcessDefinitionCreateReqDTO to);
void copyTo(BpmModelMetaInfoRespDTO from, @MappingTarget BpmModelBaseVO to);
BpmModelPageItemRespVO.ProcessDefinition convert(ProcessDefinition bean);
default void copy(Model model, BpmModelCreateReqVO bean) {
model.setName(bean.getName());
model.setKey(bean.getKey());
model.setMetaInfo(buildMetaInfoStr(null, bean.getDescription(), null, null,
null, null));
}
default void copy(Model model, BpmModelUpdateReqVO bean) {
model.setName(bean.getName());
model.setCategory(bean.getCategory());
model.setMetaInfo(buildMetaInfoStr(JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class),
bean.getDescription(), bean.getFormType(), bean.getFormId(),
bean.getFormCustomCreatePath(), bean.getFormCustomViewPath()));
}
default String buildMetaInfoStr(BpmModelMetaInfoRespDTO metaInfo, String description, Integer formType,
Long formId, String formCustomCreatePath, String formCustomViewPath) {
if (metaInfo == null) {
metaInfo = new BpmModelMetaInfoRespDTO();
}
// 只有非空,才进行设置,避免更新时的覆盖
if (StrUtil.isNotEmpty(description)) {
metaInfo.setDescription(description);
}
if (Objects.nonNull(formType)) {
metaInfo.setFormType(formType);
metaInfo.setFormId(formId);
metaInfo.setFormCustomCreatePath(formCustomCreatePath);
metaInfo.setFormCustomViewPath(formCustomViewPath);
}
return JsonUtils.toJsonString(metaInfo);
}
}

View File

@@ -0,0 +1,83 @@
package com.jeelowcode.service.bpm.config.convert.definition;
import cn.hutool.core.date.LocalDateTimeUtil;
import com.jeelowcode.tool.framework.common.util.collection.CollectionUtils;
import com.jeelowcode.service.bpm.controller.vo.process.BpmProcessDefinitionPageItemRespVO;
import com.jeelowcode.service.bpm.controller.vo.process.BpmProcessDefinitionRespVO;
import com.jeelowcode.service.bpm.entity.BpmFormDO;
import com.jeelowcode.service.bpm.entity.BpmProcessDefinitionExtDO;
import com.jeelowcode.service.bpm.dto.BpmProcessDefinitionCreateReqDTO;
import org.flowable.common.engine.impl.db.SuspensionState;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.Named;
import org.mapstruct.factory.Mappers;
import java.util.List;
import java.util.Map;
/**
* Bpm 流程定义的 Convert
*
* @author yunlong.li
*/
@Mapper
public interface BpmProcessDefinitionConvert {
BpmProcessDefinitionConvert INSTANCE = Mappers.getMapper(BpmProcessDefinitionConvert.class);
BpmProcessDefinitionPageItemRespVO convert(ProcessDefinition bean);
BpmProcessDefinitionExtDO convert2(BpmProcessDefinitionCreateReqDTO bean);
default List<BpmProcessDefinitionPageItemRespVO> convertList(List<ProcessDefinition> list, Map<String, Deployment> deploymentMap,
Map<String, BpmProcessDefinitionExtDO> processDefinitionDOMap, Map<Long, BpmFormDO> formMap) {
return CollectionUtils.convertList(list, definition -> {
Deployment deployment = definition.getDeploymentId() != null ? deploymentMap.get(definition.getDeploymentId()) : null;
BpmProcessDefinitionExtDO definitionDO = processDefinitionDOMap.get(definition.getId());
BpmFormDO form = definitionDO != null ? formMap.get(definitionDO.getFormId()) : null;
return convert(definition, deployment, definitionDO, form);
});
}
default List<BpmProcessDefinitionRespVO> convertList3(List<ProcessDefinition> list,
Map<String, BpmProcessDefinitionExtDO> processDefinitionDOMap) {
return CollectionUtils.convertList(list, processDefinition -> {
BpmProcessDefinitionRespVO respVO = convert3(processDefinition);
BpmProcessDefinitionExtDO processDefinitionExtDO = processDefinitionDOMap.get(processDefinition.getId());
// 复制通用属性
copyTo(processDefinitionExtDO, respVO);
return respVO;
});
}
@Mapping(source = "suspended", target = "suspensionState", qualifiedByName = "convertSuspendedToSuspensionState")
BpmProcessDefinitionRespVO convert3(ProcessDefinition bean);
@Named("convertSuspendedToSuspensionState")
default Integer convertSuspendedToSuspensionState(boolean suspended) {
return suspended ? SuspensionState.SUSPENDED.getStateCode() :
SuspensionState.ACTIVE.getStateCode();
}
default BpmProcessDefinitionPageItemRespVO convert(ProcessDefinition bean, Deployment deployment,
BpmProcessDefinitionExtDO processDefinitionExtDO, BpmFormDO form) {
BpmProcessDefinitionPageItemRespVO respVO = convert(bean);
respVO.setSuspensionState(bean.isSuspended() ? SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode());
if (deployment != null) {
respVO.setDeploymentTime(LocalDateTimeUtil.of(deployment.getDeploymentTime()));
}
if (form != null) {
respVO.setFormName(form.getDesformName());
}
// 复制通用属性
copyTo(processDefinitionExtDO, respVO);
return respVO;
}
@Mapping(source = "from.id", target = "to.id", ignore = true)
void copyTo(BpmProcessDefinitionExtDO from, @MappingTarget BpmProcessDefinitionRespVO to);
}

View File

@@ -0,0 +1,40 @@
package com.jeelowcode.service.bpm.config.convert.definition;
import com.jeelowcode.tool.framework.common.util.collection.CollectionUtils;
import com.jeelowcode.service.bpm.controller.vo.rule.BpmTaskAssignRuleCreateReqVO;
import com.jeelowcode.service.bpm.controller.vo.rule.BpmTaskAssignRuleRespVO;
import com.jeelowcode.service.bpm.controller.vo.rule.BpmTaskAssignRuleUpdateReqVO;
import com.jeelowcode.service.bpm.entity.BpmTaskAssignRuleDO;
import org.flowable.bpmn.model.UserTask;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
import java.util.Map;
@Mapper
public interface BpmTaskAssignRuleConvert {
BpmTaskAssignRuleConvert INSTANCE = Mappers.getMapper(BpmTaskAssignRuleConvert.class);
default List<BpmTaskAssignRuleRespVO> convertList(List<UserTask> tasks, List<BpmTaskAssignRuleDO> rules) {
Map<String, BpmTaskAssignRuleDO> ruleMap = CollectionUtils.convertMap(rules, BpmTaskAssignRuleDO::getTaskDefinitionKey);
// 以 UserTask 为主维度,原因是:流程图编辑后,一些规则实际就没用了。
return CollectionUtils.convertList(tasks, task -> {
BpmTaskAssignRuleRespVO respVO = convert(ruleMap.get(task.getId()));
if (respVO == null) {
respVO = new BpmTaskAssignRuleRespVO();
respVO.setTaskDefinitionKey(task.getId());
}
respVO.setTaskDefinitionName(task.getName());
return respVO;
});
}
BpmTaskAssignRuleRespVO convert(BpmTaskAssignRuleDO bean);
BpmTaskAssignRuleDO convert(BpmTaskAssignRuleCreateReqVO bean);
BpmTaskAssignRuleDO convert(BpmTaskAssignRuleUpdateReqVO bean);
List<BpmTaskAssignRuleDO> convertList2(List<BpmTaskAssignRuleRespVO> list);
}

View File

@@ -0,0 +1,38 @@
package com.jeelowcode.service.bpm.config.convert.definition;
import java.util.*;
import com.jeelowcode.service.bpm.controller.vo.group.BpmUserGroupCreateReqVO;
import com.jeelowcode.service.bpm.controller.vo.group.BpmUserGroupRespVO;
import com.jeelowcode.service.bpm.controller.vo.group.BpmUserGroupUpdateReqVO;
import com.jeelowcode.service.bpm.entity.BpmUserGroupDO;
import com.jeelowcode.tool.framework.common.pojo.PageResult;
import org.mapstruct.Mapper;
import org.mapstruct.Named;
import org.mapstruct.factory.Mappers;
/**
* 用户组 Convert
*
* @author 芋道源码
*/
@Mapper
public interface BpmUserGroupConvert {
BpmUserGroupConvert INSTANCE = Mappers.getMapper(BpmUserGroupConvert.class);
BpmUserGroupDO convert(BpmUserGroupCreateReqVO bean);
BpmUserGroupDO convert(BpmUserGroupUpdateReqVO bean);
BpmUserGroupRespVO convert(BpmUserGroupDO bean);
List<BpmUserGroupRespVO> convertList(List<BpmUserGroupDO> list);
PageResult<BpmUserGroupRespVO> convertPage(PageResult<BpmUserGroupDO> page);
@Named("convertList2")
List<BpmUserGroupRespVO> convertList2(List<BpmUserGroupDO> list);
}

View File

@@ -0,0 +1,21 @@
package com.jeelowcode.service.bpm.config.convert.message;
import com.jeelowcode.service.system.dto.SmsSendSingleToUserReqDTO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
import java.util.Map;
@Mapper
public interface BpmMessageConvert {
BpmMessageConvert INSTANCE = Mappers.getMapper(BpmMessageConvert.class);
@Mapping(target = "mobile", ignore = true)
@Mapping(source = "userId", target = "userId")
@Mapping(source = "templateCode", target = "templateCode")
@Mapping(source = "templateParams", target = "templateParams")
SmsSendSingleToUserReqDTO convert(Long userId, String templateCode, Map<String, Object> templateParams);
}

View File

@@ -0,0 +1,30 @@
package com.jeelowcode.service.bpm.config.convert.oa;
import com.jeelowcode.service.bpm.controller.vo.oa.BpmOALeaveCreateReqVO;
import com.jeelowcode.service.bpm.controller.vo.oa.BpmOALeaveRespVO;
import com.jeelowcode.service.bpm.entity.BpmOALeaveDO;
import com.jeelowcode.tool.framework.common.pojo.PageResult;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* 请假申请 Convert
*
* @author 芋艿
*/
@Mapper
public interface BpmOALeaveConvert {
BpmOALeaveConvert INSTANCE = Mappers.getMapper(BpmOALeaveConvert.class);
BpmOALeaveDO convert(BpmOALeaveCreateReqVO bean);
BpmOALeaveRespVO convert(BpmOALeaveDO bean);
List<BpmOALeaveRespVO> convertList(List<BpmOALeaveDO> list);
PageResult<BpmOALeaveRespVO> convertPage(PageResult<BpmOALeaveDO> page);
}

View File

@@ -0,0 +1,6 @@
/**
* 提供 POJO 类的实体转换
*
* 目前使用 MapStruct 框架
*/
package com.jeelowcode.service.bpm.config.convert;

View File

@@ -0,0 +1,30 @@
package com.jeelowcode.service.bpm.config.convert.task;
import com.jeelowcode.tool.framework.common.util.date.DateUtils;
import com.jeelowcode.service.bpm.controller.vo.activity.BpmActivityRespVO;
import org.flowable.engine.history.HistoricActivityInstance;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* BPM 活动 Convert
*
* @author 芋道源码
*/
@Mapper(uses = DateUtils.class)
public interface BpmActivityConvert {
BpmActivityConvert INSTANCE = Mappers.getMapper(BpmActivityConvert.class);
List<BpmActivityRespVO> convertList(List<HistoricActivityInstance> list);
@Mappings({
@Mapping(source = "activityId", target = "key"),
@Mapping(source = "activityType", target = "type")
})
BpmActivityRespVO convert(HistoricActivityInstance bean);
}

View File

@@ -0,0 +1,115 @@
package com.jeelowcode.service.bpm.config.convert.task;
import com.jeelowcode.tool.framework.common.pojo.PageResult;
import com.jeelowcode.tool.framework.common.util.date.DateUtils;
import com.jeelowcode.tool.framework.common.util.number.NumberUtils;
import com.jeelowcode.service.bpm.controller.vo.instance.BpmProcessInstancePageItemRespVO;
import com.jeelowcode.service.bpm.controller.vo.instance.BpmProcessInstanceRespVO;
import com.jeelowcode.service.bpm.entity.BpmProcessDefinitionExtDO;
import com.jeelowcode.service.bpm.entity.BpmProcessInstanceExtDO;
import com.jeelowcode.service.bpm.config.framework.bpm.core.event.BpmProcessInstanceResultEvent;
import com.jeelowcode.service.bpm.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO;
import com.jeelowcode.service.bpm.dto.BpmMessageSendWhenProcessInstanceRejectReqDTO;
import com.jeelowcode.service.system.dto.DeptRespDTO;
import com.jeelowcode.service.system.dto.AdminUserRespDTO;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.factory.Mappers;
import java.util.List;
import java.util.Map;
/**
* 流程实例 Convert
*
* @author 芋道源码
*/
@Mapper(uses = DateUtils.class)
public interface BpmProcessInstanceConvert {
BpmProcessInstanceConvert INSTANCE = Mappers.getMapper(BpmProcessInstanceConvert.class);
default PageResult<BpmProcessInstancePageItemRespVO> convertPage(PageResult<BpmProcessInstanceExtDO> page,
Map<String, List<Task>> taskMap) {
List<BpmProcessInstancePageItemRespVO> list = convertList(page.getList());
list.forEach(respVO -> respVO.setTasks(convertList2(taskMap.get(respVO.getId()))));
return new PageResult<>(list, page.getTotal());
}
List<BpmProcessInstancePageItemRespVO> convertList(List<BpmProcessInstanceExtDO> list);
@Mapping(source = "processInstanceId", target = "id")
BpmProcessInstancePageItemRespVO convert(BpmProcessInstanceExtDO bean);
List<BpmProcessInstancePageItemRespVO.Task> convertList2(List<Task> tasks);
default BpmProcessInstanceRespVO convert2(HistoricProcessInstance processInstance, BpmProcessInstanceExtDO processInstanceExt,
ProcessDefinition processDefinition, BpmProcessDefinitionExtDO processDefinitionExt,
String bpmnXml, AdminUserRespDTO startUser, DeptRespDTO dept) {
BpmProcessInstanceRespVO respVO = convert2(processInstance);
copyTo(processInstanceExt, respVO);
// definition
respVO.setProcessDefinition(convert2(processDefinition));
copyTo(processDefinitionExt, respVO.getProcessDefinition());
respVO.getProcessDefinition().setBpmnXml(bpmnXml);
// user
if (startUser != null) {
respVO.setStartUser(convert2(startUser));
if (dept != null) {
respVO.getStartUser().setDeptName(dept.getName());
}
}
return respVO;
}
BpmProcessInstanceRespVO convert2(HistoricProcessInstance bean);
@Mapping(source = "from.id", target = "to.id", ignore = true)
void copyTo(BpmProcessInstanceExtDO from, @MappingTarget BpmProcessInstanceRespVO to);
BpmProcessInstanceRespVO.ProcessDefinition convert2(ProcessDefinition bean);
@Mapping(source = "from.id", target = "to.id", ignore = true)
void copyTo(BpmProcessDefinitionExtDO from, @MappingTarget BpmProcessInstanceRespVO.ProcessDefinition to);
BpmProcessInstanceRespVO.User convert2(AdminUserRespDTO bean);
default BpmProcessInstanceResultEvent convert(Object source, HistoricProcessInstance instance, Integer result) {
BpmProcessInstanceResultEvent event = new BpmProcessInstanceResultEvent(source);
event.setId(instance.getId());
event.setProcessDefinitionKey(instance.getProcessDefinitionKey());
event.setBusinessKey(instance.getBusinessKey());
event.setResult(result);
return event;
}
default BpmProcessInstanceResultEvent convert(Object source, ProcessInstance instance, Integer result) {
BpmProcessInstanceResultEvent event = new BpmProcessInstanceResultEvent(source);
event.setId(instance.getId());
event.setProcessDefinitionKey(instance.getProcessDefinitionKey());
event.setBusinessKey(instance.getBusinessKey());
event.setResult(result);
return event;
}
default BpmMessageSendWhenProcessInstanceApproveReqDTO convert2ApprovedReq(ProcessInstance instance){
return new BpmMessageSendWhenProcessInstanceApproveReqDTO()
.setStartUserId(NumberUtils.parseLong(instance.getStartUserId()))
.setProcessInstanceId(instance.getId())
.setProcessInstanceName(instance.getName());
}
default BpmMessageSendWhenProcessInstanceRejectReqDTO convert2RejectReq(ProcessInstance instance, String reason) {
return new BpmMessageSendWhenProcessInstanceRejectReqDTO()
.setProcessInstanceName(instance.getName())
.setProcessInstanceId(instance.getId())
.setReason(reason)
.setStartUserId(NumberUtils.parseLong(instance.getStartUserId()));
}
}

View File

@@ -0,0 +1,201 @@
package com.jeelowcode.service.bpm.config.convert.task;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.jeelowcode.service.bpm.controller.vo.task.*;
import com.jeelowcode.service.bpm.dto.BpmMessageSendWhenTaskCreatedReqDTO;
import com.jeelowcode.service.bpm.entity.BpmTaskExtDO;
import com.jeelowcode.service.system.dto.AdminUserRespDTO;
import com.jeelowcode.service.system.dto.DeptRespDTO;
import com.jeelowcode.tool.framework.common.util.collection.CollectionUtils;
import com.jeelowcode.tool.framework.common.util.collection.MapUtils;
import com.jeelowcode.tool.framework.common.util.date.DateUtils;
import com.jeelowcode.tool.framework.common.util.number.NumberUtils;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.common.engine.impl.db.SuspensionState;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl;
import org.mapstruct.*;
import org.mapstruct.factory.Mappers;
import java.util.Date;
import java.util.List;
import java.util.Map;
import static com.jeelowcode.tool.framework.common.util.collection.CollectionUtils.convertMultiMap;
import static com.jeelowcode.tool.framework.common.util.collection.CollectionUtils.filterList;
/**
* Bpm 任务 Convert
*
* @author 芋道源码
*/
@Mapper(uses = DateUtils.class)
public interface BpmTaskConvert {
BpmTaskConvert INSTANCE = Mappers.getMapper(BpmTaskConvert.class);
default List<BpmTaskTodoPageItemRespVO> convertList1(List<Task> tasks,
Map<String, ProcessInstance> processInstanceMap,
Map<Long, AdminUserRespDTO> userMap) {
return CollectionUtils.convertList(tasks, task -> {
BpmTaskTodoPageItemRespVO respVO = convert1(task);
ProcessInstance processInstance = processInstanceMap.get(task.getProcessInstanceId());
if (processInstance != null) {
AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId()));
respVO.setProcessInstance(convert(processInstance, startUser));
}
return respVO;
});
}
@Mapping(source = "suspended", target = "suspensionState", qualifiedByName = "convertSuspendedToSuspensionState")
BpmTaskTodoPageItemRespVO convert1(Task bean);
@Named("convertSuspendedToSuspensionState")
default Integer convertSuspendedToSuspensionState(boolean suspended) {
return suspended ? SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode();
}
default List<BpmTaskDonePageItemRespVO> convertList2(List<HistoricTaskInstance> tasks,
Map<String, BpmTaskExtDO> bpmTaskExtDOMap, Map<String, HistoricProcessInstance> historicProcessInstanceMap,
Map<Long, AdminUserRespDTO> userMap) {
return CollectionUtils.convertList(tasks, task -> {
BpmTaskDonePageItemRespVO respVO = convert2(task);
BpmTaskExtDO taskExtDO = bpmTaskExtDOMap.get(task.getId());
copyTo(taskExtDO, respVO);
HistoricProcessInstance processInstance = historicProcessInstanceMap.get(task.getProcessInstanceId());
if (processInstance != null) {
AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId()));
respVO.setProcessInstance(convert(processInstance, startUser));
}
return respVO;
});
}
BpmTaskDonePageItemRespVO convert2(HistoricTaskInstance bean);
@Mappings({
@Mapping(source = "processInstance.id", target = "id"),
@Mapping(source = "processInstance.name", target = "name"),
@Mapping(source = "processInstance.startUserId", target = "startUserId"),
@Mapping(source = "processInstance.processDefinitionId", target = "processDefinitionId"),
@Mapping(source = "startUser.nickname", target = "startUserNickname")
})
BpmTaskTodoPageItemRespVO.ProcessInstance convert(ProcessInstance processInstance, AdminUserRespDTO startUser);
default List<BpmTaskRespVO> convertList3(List<HistoricTaskInstance> tasks,
Map<String, BpmTaskExtDO> bpmTaskExtDOMap, HistoricProcessInstance processInstance,
Map<Long, AdminUserRespDTO> userMap, Map<Long, DeptRespDTO> deptMap) {
return CollectionUtils.convertList(tasks, task -> {
BpmTaskRespVO respVO = convert3(task);
BpmTaskExtDO taskExtDO = bpmTaskExtDOMap.get(task.getId());
copyTo(taskExtDO, respVO);
if (processInstance != null) {
AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId()));
respVO.setProcessInstance(convert(processInstance, startUser));
}
AdminUserRespDTO assignUser = userMap.get(NumberUtils.parseLong(task.getAssignee()));
if (assignUser != null) {
respVO.setAssigneeUser(convert3(assignUser));
DeptRespDTO dept = deptMap.get(assignUser.getDeptId());
if (dept != null) {
respVO.getAssigneeUser().setDeptName(dept.getName());
}
}
return respVO;
});
}
@Mapping(source = "taskDefinitionKey", target = "definitionKey")
BpmTaskRespVO convert3(HistoricTaskInstance bean);
BpmTaskRespVO.User convert3(AdminUserRespDTO bean);
@Mapping(target = "id", ignore = true)
void copyTo(BpmTaskExtDO from, @MappingTarget BpmTaskDonePageItemRespVO to);
@Mappings({@Mapping(source = "processInstance.id", target = "id"),
@Mapping(source = "processInstance.name", target = "name"),
@Mapping(source = "processInstance.startUserId", target = "startUserId"),
@Mapping(source = "processInstance.processDefinitionId", target = "processDefinitionId"),
@Mapping(source = "startUser.nickname", target = "startUserNickname")})
BpmTaskTodoPageItemRespVO.ProcessInstance convert(HistoricProcessInstance processInstance,
AdminUserRespDTO startUser);
default BpmTaskExtDO convert2TaskExt(Task task) {
BpmTaskExtDO taskExtDO = new BpmTaskExtDO().setTaskId(task.getId())
.setAssigneeUserId(NumberUtils.parseLong(task.getAssignee())).setName(task.getName())
.setProcessDefinitionId(task.getProcessDefinitionId()).setProcessInstanceId(task.getProcessInstanceId());
taskExtDO.setCreateTime(LocalDateTimeUtil.of(task.getCreateTime()));
return taskExtDO;
}
default BpmMessageSendWhenTaskCreatedReqDTO convert(ProcessInstance processInstance, AdminUserRespDTO startUser,
Task task) {
BpmMessageSendWhenTaskCreatedReqDTO reqDTO = new BpmMessageSendWhenTaskCreatedReqDTO();
reqDTO.setProcessInstanceId(processInstance.getProcessInstanceId())
.setProcessInstanceName(processInstance.getName()).setStartUserId(startUser.getId())
.setStartUserNickname(startUser.getNickname()).setTaskId(task.getId()).setTaskName(task.getName())
.setAssigneeUserId(NumberUtils.parseLong(task.getAssignee()));
return reqDTO;
}
default List<BpmTaskSimpleRespVO> convertList(List<? extends FlowElement> elementList) {
return CollectionUtils.convertList(elementList, element -> new BpmTaskSimpleRespVO()
.setName(element.getName())
.setDefinitionKey(element.getId()));
}
//此处不用 mapstruct 映射,因为 TaskEntityImpl 还有很多其他属性,这里我们只设置我们需要的
//使用 mapstruct 会将里面嵌套的各个属性值都设置进去,会出现意想不到的问题
default TaskEntityImpl convert(TaskEntityImpl task,TaskEntityImpl parentTask){
task.setCategory(parentTask.getCategory());
task.setDescription(parentTask.getDescription());
task.setTenantId(parentTask.getTenantId());
task.setName(parentTask.getName());
task.setParentTaskId(parentTask.getId());
task.setProcessDefinitionId(parentTask.getProcessDefinitionId());
task.setProcessInstanceId(parentTask.getProcessInstanceId());
task.setTaskDefinitionKey(parentTask.getTaskDefinitionKey());
task.setTaskDefinitionId(parentTask.getTaskDefinitionId());
task.setPriority(parentTask.getPriority());
task.setCreateTime(new Date());
return task;
}
default List<BpmTaskSubSignRespVO> convertList(List<BpmTaskExtDO> bpmTaskExtDOList,
Map<Long, AdminUserRespDTO> userMap,
Map<String, Task> idTaskMap){
return CollectionUtils.convertList(bpmTaskExtDOList, task -> {
BpmTaskSubSignRespVO bpmTaskSubSignRespVO = new BpmTaskSubSignRespVO()
.setId(task.getTaskId()).setName(task.getName());
// 后加签任务不会直接设置 assignee ,所以不存在 assignee 的情况,则去取 owner
Task sourceTask = idTaskMap.get(task.getTaskId());
String assignee = ObjectUtil.defaultIfBlank(sourceTask.getOwner(),sourceTask.getAssignee());
MapUtils.findAndThen(userMap,NumberUtils.parseLong(assignee),
assignUser-> bpmTaskSubSignRespVO.setAssigneeUser(convert3(assignUser)));
return bpmTaskSubSignRespVO;
});
}
/**
* 转换任务为父子级
*
* @param sourceList 原始数据
* @return 转换后的父子级数组
*/
default List<BpmTaskRespVO> convertChildrenList(List<BpmTaskRespVO> sourceList) {
List<BpmTaskRespVO> childrenTaskList = filterList(sourceList, r -> StrUtil.isNotEmpty(r.getParentTaskId()));
Map<String, List<BpmTaskRespVO>> parentChildrenTaskListMap = convertMultiMap(childrenTaskList, BpmTaskRespVO::getParentTaskId);
for (BpmTaskRespVO bpmTaskRespVO : sourceList) {
bpmTaskRespVO.setChildren(parentChildrenTaskListMap.get(bpmTaskRespVO.getId()));
}
return filterList(sourceList, r -> StrUtil.isEmpty(r.getParentTaskId()));
}
}

View File

@@ -0,0 +1 @@
package com.jeelowcode.service.bpm.config.convert.task;

View File

@@ -0,0 +1,51 @@
package com.jeelowcode.service.bpm.config.framework.bpm.config;
import com.jeelowcode.service.bpm.config.candidate.sourceInfoProcessor.*;
import com.jeelowcode.service.bpm.config.framework.flowable.core.behavior.script.BpmTaskAssignScript;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* BPM 通用的 Configuration 配置类,提供给 Activiti 和 Flowable
* @author kyle
*/
@Configuration(proxyBeanMethods = false)
public class BpmCandidateProcessorConfiguration {
@Bean
public BpmCandidateAdminUserApiSourceInfoProcessor bpmCandidateAdminUserApiSourceInfoProcessor() {
return new BpmCandidateAdminUserApiSourceInfoProcessor();
}
@Bean
public BpmCandidateDeptApiSourceInfoProcessor bpmCandidateDeptApiSourceInfoProcessor() {
return new BpmCandidateDeptApiSourceInfoProcessor();
}
@Bean
public BpmCandidatePostApiSourceInfoProcessor bpmCandidatePostApiSourceInfoProcessor() {
return new BpmCandidatePostApiSourceInfoProcessor();
}
@Bean
public BpmCandidateRoleApiSourceInfoProcessor bpmCandidateRoleApiSourceInfoProcessor() {
return new BpmCandidateRoleApiSourceInfoProcessor();
}
@Bean
public BpmCandidateUserGroupApiSourceInfoProcessor bpmCandidateUserGroupApiSourceInfoProcessor() {
return new BpmCandidateUserGroupApiSourceInfoProcessor();
}
/**
* 可以自己定制脚本,然后通过这里设置到处理器里面去
* @param scriptsOp 脚本包装对象
* @return
*/
@Bean
public BpmCandidateScriptApiSourceInfoProcessor bpmCandidateScriptApiSourceInfoProcessor(ObjectProvider<BpmTaskAssignScript> scriptsOp) {
BpmCandidateScriptApiSourceInfoProcessor bpmCandidateScriptApiSourceInfoProcessor = new BpmCandidateScriptApiSourceInfoProcessor();
bpmCandidateScriptApiSourceInfoProcessor.setScripts(scriptsOp);
return bpmCandidateScriptApiSourceInfoProcessor;
}
}

View File

@@ -0,0 +1,19 @@
package com.jeelowcode.service.bpm.config.framework.bpm.config;
import com.jeelowcode.service.bpm.config.framework.bpm.core.event.BpmProcessInstanceResultEventPublisher;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* BPM 通用的 Configuration 配置类,提供给 Activiti 和 Flowable
*/
@Configuration(proxyBeanMethods = false)
public class BpmCommonConfiguration {
@Bean
public BpmProcessInstanceResultEventPublisher processInstanceResultEventPublisher(ApplicationEventPublisher publisher) {
return new BpmProcessInstanceResultEventPublisher(publisher);
}
}

View File

@@ -0,0 +1,44 @@
package com.jeelowcode.service.bpm.config.framework.bpm.core.event;
import com.jeelowcode.service.bpm.entity.BpmProcessInstanceExtDO;
import lombok.Data;
import org.springframework.context.ApplicationEvent;
import javax.validation.constraints.NotNull;
/**
* 流程实例的结果发生变化的 Event
* 定位:由于额外增加了 {@link BpmProcessInstanceExtDO#getResult()} 结果,所以增加该事件
*
* @author 芋道源码
*/
@SuppressWarnings("ALL")
@Data
public class BpmProcessInstanceResultEvent extends ApplicationEvent {
/**
* 流程实例的编号
*/
@NotNull(message = "流程实例的编号不能为空")
private String id;
/**
* 流程实例的 key
*/
@NotNull(message = "流程实例的 key 不能为空")
private String processDefinitionKey;
/**
* 流程实例的结果
*/
@NotNull(message = "流程实例的结果不能为空")
private Integer result;
/**
* 流程实例对应的业务标识
* 例如说,请假
*/
private String businessKey;
public BpmProcessInstanceResultEvent(Object source) {
super(source);
}
}

View File

@@ -0,0 +1,34 @@
package com.jeelowcode.service.bpm.config.framework.bpm.core.event;
import cn.hutool.core.util.StrUtil;
import org.springframework.context.ApplicationListener;
/**
* {@link BpmProcessInstanceResultEvent} 的监听器
*
* @author 芋道源码
*/
public abstract class BpmProcessInstanceResultEventListener
implements ApplicationListener<BpmProcessInstanceResultEvent> {
@Override
public final void onApplicationEvent(BpmProcessInstanceResultEvent event) {
if (!StrUtil.equals(event.getProcessDefinitionKey(), getProcessDefinitionKey())) {
return;
}
onEvent(event);
}
/**
* @return 返回监听的流程定义 Key
*/
protected abstract String getProcessDefinitionKey();
/**
* 处理事件
*
* @param event 事件
*/
protected abstract void onEvent(BpmProcessInstanceResultEvent event);
}

View File

@@ -0,0 +1,24 @@
package com.jeelowcode.service.bpm.config.framework.bpm.core.event;
import lombok.AllArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.validation.annotation.Validated;
import javax.validation.Valid;
/**
* {@link BpmProcessInstanceResultEvent} 的生产者
*
* @author 芋道源码
*/
@AllArgsConstructor
@Validated
public class BpmProcessInstanceResultEventPublisher {
private final ApplicationEventPublisher publisher;
public void sendProcessInstanceResultEvent(@Valid BpmProcessInstanceResultEvent event) {
publisher.publishEvent(event);
}
}

View File

@@ -0,0 +1,6 @@
/**
* 自定义 Event 实现,提供方便业务接入的 Listener
*
* @author 芋道源码
*/
package com.jeelowcode.service.bpm.config.framework.bpm.core.event;

View File

@@ -0,0 +1,4 @@
/**
* 占位
*/
package com.jeelowcode.service.bpm.config.framework.bpm.core;

View File

@@ -0,0 +1,36 @@
package com.jeelowcode.service.bpm.config.framework.bpm.listener;
import cn.hutool.core.util.StrUtil;
import com.jeelowcode.tool.framework.common.util.object.BeanUtils;
import com.jeelowcode.service.bpm.api.IApiBpmResultListenerApi;
import com.jeelowcode.service.bpm.dto.BpmResultListenerRespDTO;
import com.jeelowcode.service.bpm.config.framework.bpm.core.event.BpmProcessInstanceResultEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
// TODO @芋艿:后续改成支持 RPC
/**
* 业务流程结果监听器实现类
*
* @author HUIHUI
*/
@Component
public class BpmServiceResultListener implements ApplicationListener<BpmProcessInstanceResultEvent> {
@Resource
private List<IApiBpmResultListenerApi> apiBpmResultListenerApis;
@Override
public final void onApplicationEvent(BpmProcessInstanceResultEvent event) {
apiBpmResultListenerApis.forEach(apiBpmResultListenerApi -> {
if (!StrUtil.equals(event.getProcessDefinitionKey(), apiBpmResultListenerApi.getProcessDefinitionKey())) {
return;
}
apiBpmResultListenerApi.onEvent(BeanUtils.toBean(event, BpmResultListenerRespDTO.class));
});
}
}

View File

@@ -0,0 +1,6 @@
/**
* 提供给 Activiti 和 Flowable 的通用封装
*
* @author 芋道源码
*/
package com.jeelowcode.service.bpm.config.framework.bpm;

View File

@@ -0,0 +1,46 @@
package com.jeelowcode.service.bpm.config.framework.flowable.config;
import cn.hutool.core.collection.ListUtil;
import com.jeelowcode.service.bpm.config.framework.flowable.core.behavior.BpmActivityBehaviorFactory;
import com.jeelowcode.service.bpm.service.IBpmTaskAssignRuleService;
import org.flowable.common.engine.api.delegate.event.FlowableEventListener;
import org.flowable.spring.SpringProcessEngineConfiguration;
import org.flowable.spring.boot.EngineConfigurationConfigurer;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* BPM 模块的 Flowable 配置类
*
* @author jason
*/
@Configuration(proxyBeanMethods = false)
public class BpmFlowableConfiguration {
/**
* BPM 模块的 ProcessEngineConfigurationConfigurer 实现类:
*
* 1. 设置各种监听器
* 2. 设置自定义的 ActivityBehaviorFactory 实现
*/
@Bean
public EngineConfigurationConfigurer<SpringProcessEngineConfiguration> bpmProcessEngineConfigurationConfigurer(
ObjectProvider<FlowableEventListener> listeners,
BpmActivityBehaviorFactory bpmActivityBehaviorFactory) {
return configuration -> {
// 注册监听器,例如说 BpmActivityEventListener
configuration.setEventListeners(ListUtil.toList(listeners.iterator()));
// 设置 ActivityBehaviorFactory 实现类,用于流程任务的审核人的自定义
configuration.setActivityBehaviorFactory(bpmActivityBehaviorFactory);
};
}
@Bean
public BpmActivityBehaviorFactory bpmActivityBehaviorFactory(IBpmTaskAssignRuleService taskRuleService) {
BpmActivityBehaviorFactory bpmActivityBehaviorFactory = new BpmActivityBehaviorFactory();
bpmActivityBehaviorFactory.setBpmTaskRuleService(taskRuleService);
return bpmActivityBehaviorFactory;
}
}

View File

@@ -0,0 +1,44 @@
package com.jeelowcode.service.bpm.config.framework.flowable.core.behavior;
import com.jeelowcode.service.bpm.service.IBpmTaskAssignRuleService;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Setter;
import lombok.ToString;
import org.flowable.bpmn.model.Activity;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior;
import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
import org.flowable.engine.impl.bpmn.behavior.UserTaskActivityBehavior;
import org.flowable.engine.impl.bpmn.parser.factory.DefaultActivityBehaviorFactory;
/**
* 自定义的 ActivityBehaviorFactory 实现类,目的如下:
* 1. 自定义 {@link #createUserTaskActivityBehavior(UserTask)}:实现自定义的流程任务的 assignee 负责人的分配
*
* @author 芋道源码
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class BpmActivityBehaviorFactory extends DefaultActivityBehaviorFactory {
@Setter
private IBpmTaskAssignRuleService bpmTaskRuleService;
@Override
public UserTaskActivityBehavior createUserTaskActivityBehavior(UserTask userTask) {
return new BpmUserTaskActivityBehavior(userTask)
.setBpmTaskRuleService(bpmTaskRuleService);
}
@Override
public ParallelMultiInstanceBehavior createParallelMultiInstanceBehavior(Activity activity,
AbstractBpmnActivityBehavior innerActivityBehavior) {
return new BpmParallelMultiInstanceBehavior(activity, innerActivityBehavior)
.setBpmTaskRuleService(bpmTaskRuleService);
}
// TODO @keSequentialMultiInstanceBehavior 这个抽空也可以看看
}

View File

@@ -0,0 +1,58 @@
package com.jeelowcode.service.bpm.config.framework.flowable.core.behavior;
import com.jeelowcode.tool.framework.flowable.core.util.FlowableUtils;
import com.jeelowcode.service.bpm.service.IBpmTaskAssignRuleService;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.Activity;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior;
import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
import java.util.Set;
/**
* 自定义的【并行】的【多个】流程任务的 assignee 负责人的分配
* 第一步,基于分配规则,计算出分配任务的【多个】候选人们。
* 第二步,将【多个】任务候选人们,设置到 DelegateExecution 的 collectionVariable 变量中,以便 BpmUserTaskActivityBehavior 使用它
*
* @author kemengkai
* @date 2022-04-21 16:57
*/
@Slf4j
public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehavior {
@Setter
private IBpmTaskAssignRuleService bpmTaskRuleService;
public BpmParallelMultiInstanceBehavior(Activity activity,
AbstractBpmnActivityBehavior innerActivityBehavior) {
super(activity, innerActivityBehavior);
}
/**
* 重写该方法,主要实现两个功能:
* 1. 忽略原有的 collectionVariable、collectionElementVariable 表达式,而是采用自己定义的
* 2. 获得任务的处理人,并设置到 collectionVariable 中,用于 BpmUserTaskActivityBehavior 从中可以获取任务的处理人
*
* 注意,多个任务实例,每个任务实例对应一个处理人,所以返回的数量就是任务处理人的数量
*
* @param execution 执行任务
* @return 数量
*/
@Override
protected int resolveNrOfInstances(DelegateExecution execution) {
// 第一步,设置 collectionVariable 和 CollectionVariable
// 从 execution.getVariable() 读取所有任务处理人的 key
super.collectionExpression = null; // collectionExpression 和 collectionVariable 是互斥的
super.collectionVariable = FlowableUtils.formatCollectionVariable(execution.getCurrentActivityId());
// 从 execution.getVariable() 读取当前所有任务处理的人的 key
super.collectionElementVariable = FlowableUtils.formatCollectionElementVariable(execution.getCurrentActivityId());
// 第二步,获取任务的所有处理人
Set<Long> assigneeUserIds = bpmTaskRuleService.calculateTaskCandidateUsers(execution);
execution.setVariable(super.collectionVariable, assigneeUserIds);
return assigneeUserIds.size();
}
}

View File

@@ -0,0 +1,66 @@
package com.jeelowcode.service.bpm.config.framework.flowable.core.behavior;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.RandomUtil;
import com.jeelowcode.service.bpm.service.IBpmTaskAssignRuleService;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.UserTask;
import org.flowable.common.engine.impl.el.ExpressionManager;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.impl.bpmn.behavior.UserTaskActivityBehavior;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.TaskHelper;
import org.flowable.task.service.TaskService;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import java.util.List;
import java.util.Set;
/**
* 自定义的【单个】流程任务的 assignee 负责人的分配
* 第一步,基于分配规则,计算出分配任务的【单个】候选人。如果找不到,则直接报业务异常,不继续执行后续的流程;
* 第二步,随机选择一个候选人,则选择作为 assignee 负责人。
*
* @author 芋道源码
*/
@Slf4j
public class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior {
@Setter
private IBpmTaskAssignRuleService bpmTaskRuleService;
public BpmUserTaskActivityBehavior(UserTask userTask) {
super(userTask);
}
@Override
protected void handleAssignments(TaskService taskService, String assignee, String owner,
List<String> candidateUsers, List<String> candidateGroups, TaskEntity task, ExpressionManager expressionManager,
DelegateExecution execution, ProcessEngineConfigurationImpl processEngineConfiguration) {
// 第一步,获得任务的候选用户
Long assigneeUserId = calculateTaskCandidateUsers(execution);
Assert.notNull(assigneeUserId, "任务处理人不能为空");
// 第二步,设置作为负责人
TaskHelper.changeTaskAssignee(task, String.valueOf(assigneeUserId));
}
private Long calculateTaskCandidateUsers(DelegateExecution execution) {
// 情况一,如果是多实例的任务,例如说会签、或签等情况,则从 Variable 中获取。它的任务处理人在 BpmParallelMultiInstanceBehavior 中已经被分配了
if (super.multiInstanceActivityBehavior != null) {
return execution.getVariable(super.multiInstanceActivityBehavior.getCollectionElementVariable(), Long.class);
}
// 情况二,如果非多实例的任务,则计算任务处理人
// 第一步,先计算可处理该任务的处理人们
Set<Long> candidateUserIds = bpmTaskRuleService.calculateTaskCandidateUsers(execution);
// 第二步,后随机选择一个任务的处理人
// 疑问:为什么一定要选择一个任务处理人?
// 解答:项目对 bpm 的任务是责任到人,所以每个任务有且仅有一个处理人。
// 如果希望一个任务可以同时被多个人处理,可以考虑使用 BpmParallelMultiInstanceBehavior 实现的会签 or 或签。
int index = RandomUtil.randomInt(candidateUserIds.size());
return CollUtil.get(candidateUserIds, index);
}
}

View File

@@ -0,0 +1,34 @@
package com.jeelowcode.service.bpm.config.framework.flowable.core.behavior.script;
import com.jeelowcode.service.bpm.enums.definition.BpmTaskRuleScriptEnum;
import org.flowable.engine.delegate.DelegateExecution;
import java.util.Set;
/**
* Bpm 任务分配的自定义 Script 脚本
* 使用场景:
* 1. 设置审批人为发起人
* 2. 设置审批人为发起人的 Leader
* 3. 甚至审批人为发起人的 Leader 的 Leader
*
* @author 芋道源码
*/
public interface BpmTaskAssignScript {
/**
* 基于执行任务,获得任务的候选用户们
*
* @param execution 执行任务
* @return 候选人用户的编号数组
*/
Set<Long> calculateTaskCandidateUsers(DelegateExecution execution);
/**
* 获得枚举值
*
* @return 枚举值
*/
BpmTaskRuleScriptEnum getEnum();
}

View File

@@ -0,0 +1,70 @@
package com.jeelowcode.service.bpm.config.framework.flowable.core.behavior.script.impl;
import com.jeelowcode.tool.framework.common.util.number.NumberUtils;
import com.jeelowcode.service.bpm.config.framework.flowable.core.behavior.script.BpmTaskAssignScript;
import com.jeelowcode.service.bpm.service.IBpmProcessInstanceService;
import com.jeelowcode.service.system.api.IApiDeptApi;
import com.jeelowcode.service.system.dto.DeptRespDTO;
import com.jeelowcode.service.system.api.IApiAdminUserApi;
import com.jeelowcode.service.system.dto.AdminUserRespDTO;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.context.annotation.Lazy;
import org.springframework.util.Assert;
import javax.annotation.Resource;
import java.util.Set;
import static com.jeelowcode.tool.framework.common.util.collection.SetUtils.asSet;
import static java.util.Collections.emptySet;
/**
* 分配给发起人的 Leader 审批的 Script 实现类
* 目前 Leader 的定义是,
*
* @author 芋道源码
*/
public abstract class BpmTaskAssignLeaderAbstractScript implements BpmTaskAssignScript {
@Resource
private IApiAdminUserApi apiAdminUserApi;
@Resource
private IApiDeptApi apiDeptApi;
@Resource
@Lazy // 解决循环依赖
private IBpmProcessInstanceService bpmProcessInstanceService;
protected Set<Long> calculateTaskCandidateUsers(DelegateExecution execution, int level) {
Assert.isTrue(level > 0, "level 必须大于 0");
// 获得发起人
ProcessInstance processInstance = bpmProcessInstanceService.getProcessInstance(execution.getProcessInstanceId());
Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId());
// 获得对应 leve 的部门
DeptRespDTO dept = null;
for (int i = 0; i < level; i++) {
// 获得 level 对应的部门
if (dept == null) {
dept = getStartUserDept(startUserId);
if (dept == null) { // 找不到发起人的部门,所以无法使用该规则
return emptySet();
}
} else {
DeptRespDTO parentDept = apiDeptApi.getDept(dept.getParentId());
if (parentDept == null) { // 找不到父级部门,所以只好结束寻找。原因是:例如说,级别比较高的人,所在部门层级比较少
break;
}
dept = parentDept;
}
}
return dept.getLeaderUserId() != null ? asSet(dept.getLeaderUserId()) : emptySet();
}
private DeptRespDTO getStartUserDept(Long startUserId) {
AdminUserRespDTO startUser = apiAdminUserApi.getUser(startUserId);
if (startUser.getDeptId() == null) { // 找不到部门,所以无法使用该规则
return null;
}
return apiDeptApi.getDept(startUser.getDeptId());
}
}

View File

@@ -0,0 +1,27 @@
package com.jeelowcode.service.bpm.config.framework.flowable.core.behavior.script.impl;
import com.jeelowcode.service.bpm.enums.definition.BpmTaskRuleScriptEnum;
import org.flowable.engine.delegate.DelegateExecution;
import org.springframework.stereotype.Component;
import java.util.Set;
/**
* 分配给发起人的一级 Leader 审批的 Script 实现类
*
* @author 芋道源码
*/
@Component
public class BpmTaskAssignLeaderX1Script extends BpmTaskAssignLeaderAbstractScript {
@Override
public Set<Long> calculateTaskCandidateUsers(DelegateExecution execution) {
return calculateTaskCandidateUsers(execution, 1);
}
@Override
public BpmTaskRuleScriptEnum getEnum() {
return BpmTaskRuleScriptEnum.LEADER_X1;
}
}

View File

@@ -0,0 +1,27 @@
package com.jeelowcode.service.bpm.config.framework.flowable.core.behavior.script.impl;
import com.jeelowcode.service.bpm.enums.definition.BpmTaskRuleScriptEnum;
import org.flowable.engine.delegate.DelegateExecution;
import org.springframework.stereotype.Component;
import java.util.Set;
/**
* 分配给发起人的二级 Leader 审批的 Script 实现类
*
* @author 芋道源码
*/
@Component
public class BpmTaskAssignLeaderX2Script extends BpmTaskAssignLeaderAbstractScript {
@Override
public Set<Long> calculateTaskCandidateUsers(DelegateExecution execution) {
return calculateTaskCandidateUsers(execution, 2);
}
@Override
public BpmTaskRuleScriptEnum getEnum() {
return BpmTaskRuleScriptEnum.LEADER_X2;
}
}

View File

@@ -0,0 +1,40 @@
package com.jeelowcode.service.bpm.config.framework.flowable.core.behavior.script.impl;
import com.jeelowcode.tool.framework.common.util.collection.SetUtils;
import com.jeelowcode.tool.framework.common.util.number.NumberUtils;
import com.jeelowcode.service.bpm.enums.definition.BpmTaskRuleScriptEnum;
import com.jeelowcode.service.bpm.config.framework.flowable.core.behavior.script.BpmTaskAssignScript;
import com.jeelowcode.service.bpm.service.IBpmProcessInstanceService;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Set;
/**
* 分配给发起人审批的 Script 实现类
*
* @author 芋道源码
*/
@Component
public class BpmTaskAssignStartUserScript implements BpmTaskAssignScript {
@Resource
@Lazy // 解决循环依赖
private IBpmProcessInstanceService bpmProcessInstanceService;
@Override
public Set<Long> calculateTaskCandidateUsers(DelegateExecution execution) {
ProcessInstance processInstance = bpmProcessInstanceService.getProcessInstance(execution.getProcessInstanceId());
Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId());
return SetUtils.asSet(startUserId);
}
@Override
public BpmTaskRuleScriptEnum getEnum() {
return BpmTaskRuleScriptEnum.START_USER;
}
}

View File

@@ -0,0 +1,77 @@
package com.jeelowcode.service.bpm.config.framework.flowable.core.handler;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.jeelowcode.tool.framework.flowable.core.enums.BpmnModelConstants;
import com.jeelowcode.service.system.api.IApiPermissionApi;
import com.jeelowcode.service.system.api.IApiAdminUserApi;
import com.jeelowcode.service.system.dto.AdminUserRespDTO;
import lombok.AllArgsConstructor;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.delegate.DelegateExecution;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static com.jeelowcode.tool.framework.common.util.collection.CollectionUtils.convertList;
// TODO @芋艿bpmn 分配人融合时,需要搞下这块;
/**
* 多实例处理类
*/
@AllArgsConstructor
@Component("multiInstanceHandler")
public class MultiInstanceHandler {
@Resource
private IApiAdminUserApi userApi;
@Resource
private IApiPermissionApi apiPermissionApi;
/**
* 流程发起人那种情况不需要处理,
* 由 flowable 完成
*
* @param execution flowable的执行对象
* @return 用户ID
*/
public Set<String> getUserIds(DelegateExecution execution) {
Set<String> candidateUserIds = new LinkedHashSet<>();
FlowElement flowElement = execution.getCurrentFlowElement();
if (ObjectUtil.isNotEmpty(flowElement) && flowElement instanceof UserTask) {
UserTask userTask = (UserTask) flowElement;
String dataType = userTask.getAttributeValue(BpmnModelConstants.NAMESPACE, BpmnModelConstants.PROCESS_CUSTOM_DATA_TYPE);
if ("USERS".equals(dataType) && CollUtil.isNotEmpty(userTask.getCandidateUsers())) {
// 添加候选用户id
candidateUserIds.addAll(userTask.getCandidateUsers());
} else if (CollUtil.isNotEmpty(userTask.getCandidateGroups())) {
// 获取组的ID角色ID集合或部门ID集合
List<Long> groups = userTask.getCandidateGroups().stream()
// 例如部门DEPT100100才是部门id
.map(item -> Long.parseLong(item.substring(4)))
.collect(Collectors.toList());
List<Long> userIds = new ArrayList<>();
if ("ROLES".equals(dataType)) {
// 通过角色id获取所有用户id集合
Set<Long> userRoleIdListByRoleIds = apiPermissionApi.getUserRoleIdListByRoleIds(groups);
userIds = new ArrayList<>(userRoleIdListByRoleIds);
} else if ("DEPTS".equals(dataType)) {
// 通过部门id获取所有用户id集合
List<AdminUserRespDTO> userListByDeptIds = userApi.getUserListByDeptIds(groups);
userIds = convertList(userListByDeptIds, AdminUserRespDTO::getId);
}
// 添加候选用户id
userIds.forEach(id -> candidateUserIds.add(String.valueOf(id)));
}
}
return candidateUserIds;
}
}

View File

@@ -0,0 +1,388 @@
package com.jeelowcode.service.bpm.config.framework.flowable.core.listener;
import cn.hutool.core.date.DateUtil;
import com.google.common.collect.ImmutableSet;
import com.jeelowcode.framework.utils.tool.spring.SpringUtils;
import com.jeelowcode.service.bpm.config.framework.portal.core.PortalRequest;
import com.jeelowcode.service.bpm.config.framework.portal.core.dto.DeleteRequestInfoDTO;
import com.jeelowcode.service.bpm.config.framework.portal.core.dto.DeleteUserRequestInfoDTO;
import com.jeelowcode.service.bpm.config.framework.portal.core.dto.PortalTodoResponseDTO;
import com.jeelowcode.service.bpm.config.framework.portal.core.dto.ReceiveRequestInfoDTO;
import com.jeelowcode.service.bpm.controller.vo.instance.BpmProcessInstanceRespVO;
import com.jeelowcode.service.bpm.controller.vo.task.BpmTaskRespVO;
import com.jeelowcode.service.bpm.entity.BpmProcessDefinitionExtDO;
import com.jeelowcode.service.bpm.service.IBpmProcessDefinitionService;
import com.jeelowcode.service.bpm.service.IBpmProcessInstanceService;
import com.jeelowcode.service.bpm.service.IBpmTaskService;
import com.jeelowcode.service.system.api.IApiAdminUserApi;
import com.jeelowcode.service.system.dto.AdminUserRespDTO;
import com.jeelowcode.tool.framework.common.util.date.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent;
import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType;
import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener;
import org.flowable.engine.delegate.event.FlowableCancelledEvent;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import static cn.hutool.core.collection.CollUtil.getLast;
import static com.jeelowcode.tool.framework.common.util.json.JsonUtils.toJsonString;
/**
* 监听一些事件推送到门户待办模块
*
* @author shelly
*/
@Slf4j
@Component
public class BpmPortalTodoEventListener extends AbstractFlowableEngineEventListener {
@Lazy
@Resource
private IBpmTaskService bpmTaskService;
@Lazy
@Resource
private IBpmProcessInstanceService bpmProcessInstanceService;
@Lazy
@Resource
private IBpmProcessDefinitionService bpmProcessDefinitionService;
@Lazy
@Resource
private IApiAdminUserApi apiAdminUserApi;
/**
* 需要本监听器监听的事件
*/
public static final Set<FlowableEngineEventType> TASK_EVENTS = ImmutableSet.<FlowableEngineEventType>builder()
.add(FlowableEngineEventType.PROCESS_CREATED)
.add(FlowableEngineEventType.PROCESS_CANCELLED)
.add(FlowableEngineEventType.PROCESS_COMPLETED)
.add(FlowableEngineEventType.TASK_CREATED)
.add(FlowableEngineEventType.TASK_ASSIGNED)
.add(FlowableEngineEventType.TASK_COMPLETED)
.add(FlowableEngineEventType.ACTIVITY_CANCELLED)
.build();
/**
* 构造函数,把需要监听的事件传给父构造器
*/
public BpmPortalTodoEventListener() {
super(TASK_EVENTS);
}
@Override
protected void processCreated(FlowableEngineEntityEvent event) {
}
@Override
protected void processCancelled(FlowableCancelledEvent event) {
// 流程取消事件,这里对应调用流程删除接口
PortalRequest portalRequest = SpringUtils.getBean(PortalRequest.class);
if (Objects.isNull(portalRequest)) {
// 如果没有配置待办平台,则结束这个监听任务的执行
return;
}
try {
ProcessInstance processInstance = bpmProcessInstanceService.getProcessInstance(event.getProcessInstanceId());
DeleteRequestInfoDTO requestDTO = new DeleteRequestInfoDTO()
.setFlowId(event.getProcessInstanceId());
PortalTodoResponseDTO response = portalRequest.deleteRequestInfo(requestDTO);
log.info("[processCancelled][删除待办信息成功,参数:{},结果:{}]", toJsonString(requestDTO), toJsonString(response));
} catch (Exception e) {
log.error("[processCancelled][删除待办信息失败,参数]", e);
}
}
@Override
protected void processCompleted(FlowableEngineEntityEvent event) {
// 流程完成事件,对应流程办结
PortalRequest portalRequest = SpringUtils.getBean(PortalRequest.class);
if (portalRequest == null) {
// 如果没有配置待办平台,则结束这个监听任务的执行
return;
}
ProcessInstance processInstance = bpmProcessInstanceService.getProcessInstance(event.getProcessInstanceId());
BpmProcessDefinitionExtDO processDefinition = bpmProcessDefinitionService.getProcessDefinitionExt(event.getProcessDefinitionId());
BpmTaskRespVO task = getLast(bpmTaskService.getTaskListByProcessInstanceId(event.getProcessInstanceId()));
BpmProcessInstanceRespVO processInstanceVO = bpmProcessInstanceService.getProcessInstanceVO(processInstance.getId());
String processInstanceId = event.getProcessInstanceId();
String processName = processInstance.getName();
String processDefinitionName = processInstance.getProcessDefinitionName();
String activityName = task.getName();
String pcPath = "/process-instance/detail?id=" + processInstanceId;
String processStatus = "4";
String viewStatus = "1";
String startUserId = processInstance.getStartUserId();
AdminUserRespDTO startUser = apiAdminUserApi.getUser(Long.valueOf(startUserId));
Date startTime = processInstance.getStartTime();
String assignee = String.valueOf(task.getAssigneeUser().getId());
AdminUserRespDTO assignUser = apiAdminUserApi.getUser(Long.valueOf(assignee));
LocalDateTime assignTime = task.getCreateTime();
// 当前时间戳毫秒级时间戳13位
String timestamp = String.valueOf(System.currentTimeMillis());
String title = processInstance.getName() + "-" + startUser.getNickname() + "-" + DateUtil.formatDateTime(processInstance.getStartTime());
// 接收请求参数
ReceiveRequestInfoDTO requestDTO = new ReceiveRequestInfoDTO()
.setFlowId(processInstanceId)
.setRequestName(title)
.setWorkflowName(processDefinitionName)
.setNodeName(activityName)
.setPcUrl(pcPath)
.setIsRemark(processStatus)
.setViewType(viewStatus)
.setCreator(startUser.getUsername())
.setCreateDateTime(startTime)
.setReceiver(assignUser.getUsername())
.setReceiveDateTime(DateUtils.of(assignTime))
.setReceiveTs(timestamp);
// 调用待办平台
try {
PortalTodoResponseDTO response = portalRequest.receiveRequestInfo(requestDTO);
log.info("[processCompleted][接收待办信息成功,参数({})]", toJsonString(response));
} catch (Exception e) {
log.error("接收异构系统流程失败", e);
}
}
@Override
protected void taskCreated(FlowableEngineEntityEvent event) {
}
@Override
protected void taskAssigned(FlowableEngineEntityEvent event) {
// 任务分配事件,对应待办
PortalRequest portalRequest = SpringUtils.getBean(PortalRequest.class);
if (portalRequest == null) {
// 如果没有配置待办平台,则结束这个监听任务的执行
return;
}
Task task = (Task) event.getEntity();
ProcessInstance processInstance = bpmProcessInstanceService.getProcessInstance(event.getProcessInstanceId());
BpmProcessDefinitionExtDO processDefinition = bpmProcessDefinitionService.getProcessDefinitionExt(event.getProcessDefinitionId());
BpmProcessInstanceRespVO processInstanceVO = bpmProcessInstanceService.getProcessInstanceVO(event.getProcessInstanceId());
// 获取流程实例id
String processInstanceId = event.getProcessInstanceId();
// 流程类型名称
String processDefinitionName = processInstance.getProcessDefinitionName();
// 步骤名称
String activityName = task.getName();
// pc url 地址
String pcPath = "/process-instance/detail?id=" + processInstanceId;
// 流程处理状态: 待办
String processStatus = "0";
// 流程查看状态: 未读
String viewStatus = "1";
// 创建人
String startUserId = processInstance.getStartUserId();
AdminUserRespDTO startUser = apiAdminUserApi.getUser(Long.valueOf(startUserId));
// 创建时间
Date startTime = processInstance.getStartTime();
// 接收人
String assignee = task.getAssignee();
AdminUserRespDTO assigneeUser = apiAdminUserApi.getUser(Long.valueOf(assignee));
// 接收时间
Date assigneeTime = task.getCreateTime();
// 当前时间戳毫秒级时间戳13位
String timestamp = String.valueOf(System.currentTimeMillis());
// 待办标题
String title = processInstance.getName() + "-" + startUser.getNickname() + "-" + DateUtil.formatDateTime(processInstance.getStartTime());
// 接收请求参数
ReceiveRequestInfoDTO requestDTO = new ReceiveRequestInfoDTO()
.setFlowId(processInstanceId)
.setRequestName(title)
.setWorkflowName(processDefinitionName)
.setNodeName(activityName)
.setPcUrl(pcPath)
.setIsRemark(processStatus)
.setViewType(viewStatus)
.setCreator(startUser.getUsername())
.setCreateDateTime(startTime)
.setReceiver(assigneeUser.getUsername())
.setReceiveDateTime(assigneeTime)
.setReceiveTs(timestamp);
// 调用待办平台
try {
log.error("接收异构系统流程参数({})", toJsonString(requestDTO));
PortalTodoResponseDTO response = portalRequest.receiveRequestInfo(requestDTO);
log.error("接收异构系统流程成功({})", toJsonString(response));
} catch (Exception e) {
log.error("接收异构系统流程失败", e);
}
}
@Override
protected void taskCompleted(FlowableEngineEntityEvent event) {
// 任务完成事件,这里处理任务已办
PortalRequest portalRequest = SpringUtils.getBean(PortalRequest.class);
if (portalRequest == null) {
// 如果没有配置待办平台,则结束这个监听任务的执行
return;
}
Task task = (Task) event.getEntity();
ProcessInstance processInstance = bpmProcessInstanceService.getProcessInstance(event.getProcessInstanceId());
BpmProcessDefinitionExtDO processDefinition = bpmProcessDefinitionService.getProcessDefinitionExt(event.getProcessDefinitionId());
BpmProcessInstanceRespVO processInstanceVO = bpmProcessInstanceService.getProcessInstanceVO(event.getProcessInstanceId());
// 获取流程实例id
String processInstanceId = event.getProcessInstanceId();
// 流程类型名称
String processDefinitionName = processInstance.getProcessDefinitionName();
// 步骤名称
String activityName = task.getName();
// pc url 地址
String pcPath = "/process-instance/detail?id=" + processInstanceId;
// 流程处理状态: 已办
String processStatus = "2";
// 流程查看状态: 未读
String viewStatus = "1";
// 创建人
String startUserId = processInstance.getStartUserId();
AdminUserRespDTO startUser = apiAdminUserApi.getUser(Long.valueOf(startUserId));
// 创建时间
Date startTime = processInstance.getStartTime();
// 接收人
String assignee = task.getAssignee();
AdminUserRespDTO assigneeUser = apiAdminUserApi.getUser(Long.valueOf(assignee));
// 接收时间
Date assigneeTime = task.getCreateTime();
// 当前时间戳毫秒级时间戳13位
String timestamp = String.valueOf(System.currentTimeMillis());
// 待办标题
String title = processInstance.getName() + "-" + startUser.getNickname() + "-" + DateUtil.formatDateTime(processInstance.getStartTime());
// 接收请求参数
ReceiveRequestInfoDTO requestDTO = new ReceiveRequestInfoDTO()
.setFlowId(processInstanceId)
.setRequestName(title)
.setWorkflowName(processDefinitionName)
.setNodeName(activityName)
.setPcUrl(pcPath)
.setIsRemark(processStatus)
.setViewType(viewStatus)
.setCreator(startUser.getUsername())
.setCreateDateTime(startTime)
.setReceiver(assigneeUser.getUsername())
.setReceiveDateTime(assigneeTime)
.setReceiveTs(timestamp);
// 调用待办平台
try {
PortalTodoResponseDTO response = portalRequest.receiveRequestInfo(requestDTO);
log.info("[taskCompleted][请求待办平台成功 response({})]", toJsonString(response));
} catch (Exception e) {
log.error("接收异构系统流程失败", e);
}
// 判断是否为多实例任务(会签任务)
if (isMultiInstanceTask(task)) {
// 检查会签任务是否全部完成
if (isMultiInstanceCompleted(task)) {
log.info("[会签任务完成] 流程实例ID: {}, 任务定义Key: {}",
event.getProcessInstanceId(), task.getTaskDefinitionKey());
// 在这里添加会签完成后的业务逻辑
handleMultiInstanceCompletion(event.getProcessInstanceId(), task, portalRequest);
} else {
log.info("[会签任务进行中] 流程实例ID: {}, 任务定义Key: {}",
event.getProcessInstanceId(), task.getTaskDefinitionKey());
}
}
}
/**
* 判断是否为多实例任务
*
* @param task 任务实例
* @return 是否为多实例任务
*/
private boolean isMultiInstanceTask(Task task) {
// 通过任务变量判断是否为多实例任务
return task.getProcessVariables().containsKey("nrOfInstances") &&
task.getProcessVariables().containsKey("nrOfCompletedInstances");
}
/**
* 判断多实例任务是否已完成
*
* @param task 任务实例
* @return 是否已完成
*/
private boolean isMultiInstanceCompleted(Task task) {
// 获取多实例相关变量
Integer nrOfInstances = (Integer) task.getProcessVariables().get("nrOfInstances");
Integer nrOfCompletedInstances = (Integer) task.getProcessVariables().get("nrOfCompletedInstances");
// 如果实例总数等于已完成实例数,则表示会签完成
return nrOfInstances != null && nrOfCompletedInstances != null &&
nrOfInstances.compareTo(nrOfCompletedInstances) == 0;
}
/**
* 处理会签完成事件
*
* @param processInstanceId 流程实例ID
* @param task 任务实例
*/
private void handleMultiInstanceCompletion(String processInstanceId, Task task, PortalRequest portalRequest) {
// 这里可以添加会签完成后的业务逻辑
log.info("会签任务完成处理: 流程实例ID={}, 任务名称={}, 任务Key={}",
processInstanceId, task.getName(), task.getTaskDefinitionKey());
// 查询目前谁还没有完成会签任务,将没有完成的人的任务发送到待办平台取消掉
List<String> candidateUsers = bpmTaskService.getIdentityLinksForTask(task.getId());
List<String> completedUsers = bpmTaskService.getCompletedUsers(processInstanceId, task.getTaskDefinitionKey());
List<String> pendingUsers = candidateUsers.stream()
.filter(userId -> !completedUsers.contains(userId))
.collect(Collectors.toList());
// 计算出了哪些用户没有完成会签任务,此时需要将这些用户的任务取消掉
if (pendingUsers != null && !pendingUsers.isEmpty()) {
try {
List<DeleteUserRequestInfoDTO> requestInfoDTOList = pendingUsers.stream()
.map(userId -> {
AdminUserRespDTO pendingUser = apiAdminUserApi.getUser(Long.valueOf(userId));
return new DeleteUserRequestInfoDTO()
.setFlowId(processInstanceId).setUserId(pendingUser.getUsername());
})
.collect(Collectors.toList());
PortalTodoResponseDTO response = portalRequest.batchDeleteUserRequestInfo(requestInfoDTOList);
log.info("[handleMultiInstanceCompletion][取消待办成功 response({})]", toJsonString(response));
} catch (Exception e) {
log.error("取消待办失败", e);
}
}
}
}

View File

@@ -0,0 +1,54 @@
package com.jeelowcode.service.bpm.config.framework.flowable.core.listener;
import com.jeelowcode.service.bpm.entity.BpmProcessInstanceExtDO;
import com.jeelowcode.service.bpm.service.IBpmProcessInstanceService;
import com.google.common.collect.ImmutableSet;
import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent;
import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType;
import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener;
import org.flowable.engine.delegate.event.FlowableCancelledEvent;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Set;
/**
* 监听 {@link ProcessInstance} 的开始与完成,创建与更新对应的 {@link BpmProcessInstanceExtDO} 记录
*
* @author jason
*/
@Component
public class BpmProcessInstanceEventListener extends AbstractFlowableEngineEventListener {
@Resource
@Lazy
private IBpmProcessInstanceService processInstanceService;
public static final Set<FlowableEngineEventType> PROCESS_INSTANCE_EVENTS = ImmutableSet.<FlowableEngineEventType>builder()
.add(FlowableEngineEventType.PROCESS_CREATED)
.add(FlowableEngineEventType.PROCESS_CANCELLED)
.add(FlowableEngineEventType.PROCESS_COMPLETED)
.build();
public BpmProcessInstanceEventListener(){
super(PROCESS_INSTANCE_EVENTS);
}
@Override
protected void processCreated(FlowableEngineEntityEvent event) {
processInstanceService.createProcessInstanceExt((ProcessInstance)event.getEntity());
}
@Override
protected void processCancelled(FlowableCancelledEvent event) {
processInstanceService.updateProcessInstanceExtCancel(event);
}
@Override
protected void processCompleted(FlowableEngineEntityEvent event) {
processInstanceService.updateProcessInstanceExtComplete((ProcessInstance)event.getEntity());
}
}

View File

@@ -0,0 +1,82 @@
package com.jeelowcode.service.bpm.config.framework.flowable.core.listener;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.jeelowcode.service.bpm.entity.BpmTaskExtDO;
import com.jeelowcode.service.bpm.service.IBpmActivityService;
import com.jeelowcode.service.bpm.service.IBpmTaskService;
import com.google.common.collect.ImmutableSet;
import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent;
import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType;
import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener;
import org.flowable.engine.delegate.event.FlowableActivityCancelledEvent;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.task.api.Task;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
import java.util.Set;
/**
* 监听 {@link Task} 的开始与完成,创建与更新对应的 {@link BpmTaskExtDO} 记录
*
* @author jason
*/
@Component
@Slf4j
public class BpmTaskEventListener extends AbstractFlowableEngineEventListener {
@Resource
@Lazy // 解决循环依赖
private IBpmTaskService taskService;
@Resource
@Lazy // 解决循环依赖
private IBpmActivityService activityService;
public static final Set<FlowableEngineEventType> TASK_EVENTS = ImmutableSet.<FlowableEngineEventType>builder()
.add(FlowableEngineEventType.TASK_CREATED)
.add(FlowableEngineEventType.TASK_ASSIGNED)
.add(FlowableEngineEventType.TASK_COMPLETED)
.add(FlowableEngineEventType.ACTIVITY_CANCELLED)
.build();
public BpmTaskEventListener(){
super(TASK_EVENTS);
}
@Override
protected void taskCreated(FlowableEngineEntityEvent event) {
taskService.createTaskExt((Task) event.getEntity());
}
@Override
protected void taskCompleted(FlowableEngineEntityEvent event) {
taskService.updateTaskExtComplete((Task)event.getEntity());
}
@Override
protected void taskAssigned(FlowableEngineEntityEvent event) {
taskService.updateTaskExtAssign((Task)event.getEntity());
}
@Override
protected void activityCancelled(FlowableActivityCancelledEvent event) {
List<HistoricActivityInstance> activityList = activityService.getHistoricActivityListByExecutionId(event.getExecutionId());
if (CollUtil.isEmpty(activityList)) {
log.error("[activityCancelled][使用 executionId({}) 查找不到对应的活动实例]", event.getExecutionId());
return;
}
// 遍历处理
activityList.forEach(activity -> {
if (StrUtil.isEmpty(activity.getTaskId())) {
return;
}
taskService.updateTaskExtCancel(activity.getTaskId());
});
}
}

View File

@@ -0,0 +1,30 @@
package com.jeelowcode.service.bpm.config.framework.portal.config;
import com.jeelowcode.service.bpm.config.framework.portal.core.PortalRequest;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
/**
* 门户待办配置
*
* @author shelly
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(PortalTodoProperties.class)
public class PortalTodoConfiguration {
@Resource
private PortalTodoProperties portalTodoProperties;
@Bean
public PortalRequest getPortalRequest() {
return new PortalRequest(
portalTodoProperties.getSysCode(),
portalTodoProperties.getDomain(),
portalTodoProperties.getProtocol());
}
}

View File

@@ -0,0 +1,30 @@
package com.jeelowcode.service.bpm.config.framework.portal.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* 门户待办配置
*
* @author shelly
*/
@Data
@ConfigurationProperties(prefix = "jeelowcode.portal.todo")
public class PortalTodoProperties {
/**
* 异构系统标识
*/
private String sysCode;
/**
* 门户域名,例如 xops.chinasatnet.com.cn 或者 192.168.1.1:8083
*/
private String domain;
/**
* 门户协议http 或者 https
*/
private String protocol;
}

View File

@@ -0,0 +1,162 @@
package com.jeelowcode.service.bpm.config.framework.portal.core;
import cn.hutool.http.Header;
import cn.hutool.http.HttpRequest;
import com.jeelowcode.service.bpm.config.framework.portal.core.dto.*;
import com.jeelowcode.tool.framework.common.util.json.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
import static com.jeelowcode.service.bpm.config.framework.portal.core.PortalRequestConstant.*;
/**
* 描述:请求门户信息
*
* @author shelly
*/
@Slf4j
public class PortalRequest {
/**
* 门户域名,例如 xops.chinasatnet.com.cn 或者 192.168.1.1:8083
*/
private final String domain;
/**
* 门户协议http 或者 https
*/
private final String protocol;
/**
* 系统编码
*/
private final String sysCode;
/**
* 构造函数
*
* @param domain 门户域名
* @param protocol 门户协议
*/
public PortalRequest(String sysCode, String domain, String protocol) {
this.sysCode = sysCode;
this.domain = domain;
this.protocol = protocol;
}
/**
* 获取门户基础URL地址
*
* @return 基础URL地址格式为"协议://域名"
*/
public String getBaseUrl() {
return protocol + "://" + domain;
}
/**
* 获取门户请求URL地址
*
* @param path 请求路径
* @return 请求URL地址
*/
public String getRequestUrl(String path) {
return getBaseUrl() + path;
}
/**
* 初始化Http请求
*
* @param url 请求URL地址
* @return Http请求
*/
private static HttpRequest initHttpRequest(String url, Object data) {
return HttpRequest.post(url)
.header(Header.ACCEPT, "*/*")
.header(Header.CONNECTION, "Keep-Alive")
.header(Header.CONTENT_TYPE, "application/json")
.body(JsonUtils.toJsonString(data), "application/json");
}
/**
* 接收待办流程
*/
public PortalTodoResponseDTO receiveTodoRequest(ReceiveTodoRequestDTO requestDTO) {
requestDTO.setSysCode(this.sysCode);
HttpRequest httpRequest = initHttpRequest(getRequestUrl(PATH_RECEIVE_TODO_REQUEST_BY_JSON), requestDTO);
return JsonUtils.parseObject(httpRequest.execute().body(), PortalTodoResponseDTO.class);
}
/**
* 接收待阅流程
*/
public PortalTodoResponseDTO receiveCCRequest(ReceiveCCRequestDTO requestDTO) {
requestDTO.setSysCode(this.sysCode);
HttpRequest httpRequest = initHttpRequest(getRequestUrl(PATH_RECEIVE_CC_REQUEST_BY_JSON), requestDTO);
return JsonUtils.parseObject(httpRequest.execute().body(), PortalTodoResponseDTO.class);
}
/**
* 处理待办流程,变成已办
*/
public PortalTodoResponseDTO processDoneRequest(ProcessDoneRequestDTO requestDTO) {
requestDTO.setSysCode(this.sysCode);
HttpRequest httpRequest = initHttpRequest(getRequestUrl(PATH_PROCESS_DONE_REQUEST_BY_JSON), requestDTO);
return JsonUtils.parseObject(httpRequest.execute().body(), PortalTodoResponseDTO.class);
}
/**
* 处理办结流程,变成办结
*/
public PortalTodoResponseDTO processOverRequest(ProcessOverRequestDTO requestDTO) {
requestDTO.setSysCode(this.sysCode);
HttpRequest httpRequest = initHttpRequest(getRequestUrl(PATH_PROCESS_OVER_REQUEST_BY_JSON), requestDTO);
return JsonUtils.parseObject(httpRequest.execute().body(), PortalTodoResponseDTO.class);
}
/**
* 接收异构系统流程
*/
public PortalTodoResponseDTO receiveRequestInfo(ReceiveRequestInfoDTO requestDTO) {
requestDTO.setSysCode(this.sysCode);
HttpRequest httpRequest = initHttpRequest(getRequestUrl(PATH_RECEIVE_REQUEST_INFO_BY_JSON), requestDTO);
return JsonUtils.parseObject(httpRequest.execute().body(), PortalTodoResponseDTO.class);
}
/**
* 删除异构系统流程
*/
public PortalTodoResponseDTO deleteRequestInfo(DeleteRequestInfoDTO requestDTO) {
requestDTO.setSysCode(this.sysCode);
HttpRequest httpRequest = initHttpRequest(getRequestUrl(PATH_DELETE_REQUEST_INFO_BY_JSON), requestDTO);
return JsonUtils.parseObject(httpRequest.execute().body(), PortalTodoResponseDTO.class);
}
/**
* 批量删除异构系统流程
*/
public PortalTodoResponseDTO deleteUserRequestInfo(DeleteUserRequestInfoDTO requestDTO) {
requestDTO.setSysCode(this.sysCode);
HttpRequest httpRequest = initHttpRequest(getRequestUrl(PATH_DELETE_USER_REQUEST_INFO_BY_JSON), requestDTO);
return JsonUtils.parseObject(httpRequest.execute().body(), PortalTodoResponseDTO.class);
}
/**
* 批量删除异构系统流程
*/
public PortalTodoResponseDTO batchDeleteUserRequestInfo(List<DeleteUserRequestInfoDTO> requestDTO) {
requestDTO.forEach(request -> request.setSysCode(this.sysCode));
HttpRequest httpRequest = initHttpRequest(getRequestUrl(PATH_BATCH_DELETE_USER_REQUEST_INFO_BY_JSON), requestDTO);
return JsonUtils.parseObject(httpRequest.execute().body(), PortalTodoResponseDTO.class);
}
/**
* 批量接收异构系统流程
*/
public PortalTodoResponseDTO batchReceiveRequestInfo(List<ReceiveRequestInfoDTO> requestDTO) {
requestDTO.forEach(request -> request.setSysCode(this.sysCode));
HttpRequest httpRequest = initHttpRequest(getRequestUrl(PATH_BATCH_RECEIVE_REQUEST_INFO_BY_JSON), requestDTO);
return JsonUtils.parseObject(httpRequest.execute().body(), PortalTodoResponseDTO.class);
}
}

View File

@@ -0,0 +1,29 @@
package com.jeelowcode.service.bpm.config.framework.portal.core;
/**
* 门户请求常量
*
* @author shelly
*/
public interface PortalRequestConstant {
// 接收待办流程
String PATH_RECEIVE_TODO_REQUEST_BY_JSON = "/rest/ofs/ReceiveTodoRequestByJson";
// 处理待办流程
String PATH_PROCESS_DONE_REQUEST_BY_JSON = "/rest/ofs/ProcessDoneRequestByJson";
// 处理办结流程
String PATH_PROCESS_OVER_REQUEST_BY_JSON = "/rest/ofs/ProcessOverRequestByJson";
// 接收异构系统流程
String PATH_RECEIVE_REQUEST_INFO_BY_JSON = "/rest/ofs/ReceiveRequestInfoByJson";
// 删除异构系统所有待办流程
String PATH_DELETE_REQUEST_INFO_BY_JSON = "/rest/ofs/deleteRequestInfoByJson";
// 删除异构系统指定人员待办流程
String PATH_DELETE_USER_REQUEST_INFO_BY_JSON = "/rest/ofs/deleteUserRequestInfoByJson";
// 批量删除异构系统流程
String PATH_BATCH_DELETE_USER_REQUEST_INFO_BY_JSON = "/rest/ofs/batchDeleteUserRequestInfoByJson";
// 批量接收异构系统流程
String PATH_BATCH_RECEIVE_REQUEST_INFO_BY_JSON = "/rest/ofs/batchReceiveRequestInfoByJson";
// 接收待阅流程
String PATH_RECEIVE_CC_REQUEST_BY_JSON = "/rest/ofs/ReceiveCCRequestByJson";
}

View File

@@ -0,0 +1,25 @@
package com.jeelowcode.service.bpm.config.framework.portal.core.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
* 描述:删除异构系统所有待办流程请求参数
*
* @author shelly
*/
@Data
public class DeleteRequestInfoDTO {
/**
* 系统编码
*/
@JsonProperty("syscode")
private String sysCode;
/**
* 流程实例ID
*/
@JsonProperty("flowid")
private String flowId;
}

View File

@@ -0,0 +1,30 @@
package com.jeelowcode.service.bpm.config.framework.portal.core.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
* 描述:删除用户待办流程请求参数
*
* @author shelly
*/
@Data
public class DeleteUserRequestInfoDTO {
/**
* 系统编码
*/
@JsonProperty("syscode")
private String sysCode;
/**
* 流程实例ID
*/
@JsonProperty("flowid")
private String flowId;
/**
* 用户ID
*/
@JsonProperty("userid")
private String userId;
}

View File

@@ -0,0 +1,51 @@
package com.jeelowcode.service.bpm.config.framework.portal.core.dto;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
* 描述:门户待办响应信息
*
* @author shelly
*/
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class PortalTodoResponseDTO {
/**
* 系统编码
*/
@JsonProperty("syscode")
private String sysCode;
/**
* 数据类型
*/
private String dataType;
/**
* 操作类型
*/
private String operType;
/**
* 操作结果
*/
private Integer operResult;
/**
* 消息
*/
private String message;
/**
* 是否成功
*
* @return true/false
*/
public boolean success() {
return operResult == 1;
}
}

View File

@@ -0,0 +1,51 @@
package com.jeelowcode.service.bpm.config.framework.portal.core.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 描述:流程处理完成请求参数
*
* @author shelly
*/
@Data
public class ProcessDoneRequestDTO {
/**
* 系统编码
*/
@JsonProperty("syscode")
private String sysCode;
/**
* 流程实例ID
*/
@JsonProperty("flowid")
private String flowId;
/**
* 标题
*/
@JsonProperty("requestname")
private String requestName;
/**
* 流程类型名称
*/
@JsonProperty("workflowname")
private String workflowName;
/**
* 节点名称
*/
@JsonProperty("nodename")
private String nodeName;
/**
* 接收人
*/
private String receiver;
/**
* 接收时间戳
*/
@JsonProperty("receivets")
private Long receiveTs;
}

View File

@@ -0,0 +1,49 @@
package com.jeelowcode.service.bpm.config.framework.portal.core.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
* 描述:处理办结流程请求参数
*
* @author shelly
*/
@Data
public class ProcessOverRequestDTO {
/**
* 系统编码
*/
@JsonProperty("syscode")
private String sysCode;
/**
* 流程实例ID
*/
@JsonProperty("flowid")
private String flowId;
/**
* 标题
*/
@JsonProperty("requestname")
private String requestName;
/**
* 流程类型名称
*/
@JsonProperty("workflowname")
private String workflowName;
/**
* 节点名称
*/
@JsonProperty("nodename")
private String nodeName;
/**
* 接收人
*/
private String receiver;
/**
* 接收时间戳
*/
@JsonProperty("receivets")
private Long receiveTs;
}

View File

@@ -0,0 +1,73 @@
package com.jeelowcode.service.bpm.config.framework.portal.core.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 描述:接收待阅流程请求参数
*/
@Data
public class ReceiveCCRequestDTO {
/**
* 系统编码
*/
@JsonProperty("syscode")
private String sysCode;
/**
* 流程实例ID
*/
@JsonProperty("flowid")
private String flowId;
/**
* 标题
*/
@JsonProperty("requestname")
private String requestName;
/**
* 流程类型名称
*/
@JsonProperty("workflowname")
private String workflowName;
/**
* 节点名称
*/
@JsonProperty("nodename")
private String nodeName;
/**
* PC端URL
*/
@JsonProperty("pcurl")
private String pcUrl;
/**
* APP端URL
*/
@JsonProperty("appurl")
private String appUrl;
/**
* 创建人
*/
private String creator;
/**
* 创建时间
*/
@JsonProperty("createdatetime")
private LocalDateTime createDateTime;
/**
* 接收人
*/
private String receiver;
/**
* 接收时间
*/
@JsonProperty("receivedatetime")
private LocalDateTime receiveDateTime;
/**
* 接收时间戳
*/
@JsonProperty("receivets")
private Long receiveTs;
}

View File

@@ -0,0 +1,93 @@
package com.jeelowcode.service.bpm.config.framework.portal.core.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.Date;
import static com.jeelowcode.tool.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
import static com.jeelowcode.tool.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT;
/**
* 描述:接收异构系统流程请求参数
*
* @author shelly
*/
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ReceiveRequestInfoDTO {
/**
* 系统编码
*/
@JsonProperty("syscode")
private String sysCode;
/**
* 流程实例ID
*/
@JsonProperty("flowid")
private String flowId;
/**
* 标题
*/
@JsonProperty("requestname")
private String requestName;
/**
* 流程类型名称
*/
@JsonProperty("workflowname")
private String workflowName;
/**
* 节点名称
*/
@JsonProperty("nodename")
private String nodeName;
/**
* PC端URL
*/
@JsonProperty("pcurl")
private String pcUrl;
/**
* APP端URL
*/
@JsonProperty("appurl")
private String appUrl;
/**
* 流程处理状态,0:待办,2:已办,4:办结
*/
@JsonProperty("isremark")
private String isRemark;
/**
* 流程查看状态,0:待处理,1:处理中,2:处理完成
*/
@JsonProperty("viewtype")
private String viewType;
/**
* 创建人
*/
private String creator;
/**
* 创建时间
*/
@JsonProperty("createdatetime")
@JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)
private Date createDateTime;
/**
* 接收人
*/
private String receiver;
/**
* 接收时间
*/
@JsonProperty("receivedatetime")
@JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)
private Date receiveDateTime;
/**
* 接收时间戳
*/
@JsonProperty("receivets")
private String receiveTs;
}

View File

@@ -0,0 +1,75 @@
package com.jeelowcode.service.bpm.config.framework.portal.core.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 描述:接收待办流程请求参数
*
* @author shelly
*/
@Data
public class ReceiveTodoRequestDTO {
/**
* 系统编码
*/
@JsonProperty("syscode")
private String sysCode;
/**
* 流程实例ID
*/
@JsonProperty("flowid")
private String flowId;
/**
* 标题
*/
@JsonProperty("requestname")
private String requestName;
/**
* 流程类型名称
*/
@JsonProperty("workflowname")
private String workflowName;
/**
* 节点名称
*/
@JsonProperty("nodename")
private String nodeName;
/**
* PC端URL
*/
@JsonProperty("pcurl")
private String pcUrl;
/**
* APP端URL
*/
@JsonProperty("appurl")
private String appUrl;
/**
* 创建人
*/
private String creator;
/**
* 创建时间
*/
@JsonProperty("createdatetime")
private LocalDateTime createDateTime;
/**
* 接收人
*/
private String receiver;
/**
* 接收时间
*/
@JsonProperty("receivedatetime")
private LocalDateTime receiveDateTime;
/**
* 接收时间戳
*/
@JsonProperty("receivets")
private Long receiveTs;
}

View File

@@ -0,0 +1,24 @@
package com.jeelowcode.service.bpm.config.framework.web.config;
import com.jeelowcode.tool.framework.swagger.config.SwaggerAutoConfiguration;
import org.springdoc.core.GroupedOpenApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* bpm 模块的 web 组件的 Configuration
*
* @author 芋道源码
*/
@Configuration(proxyBeanMethods = false)
public class BpmWebConfiguration {
/**
* bpm 模块的 API 分组
*/
@Bean
public GroupedOpenApi bpmGroupedOpenApi() {
return SwaggerAutoConfiguration.buildGroupedOpenApi("bpm");
}
}

View File

@@ -0,0 +1,4 @@
/**
* bpm 模块的 web 配置
*/
package com.jeelowcode.service.bpm.config.framework.web;

View File

@@ -0,0 +1,32 @@
package com.jeelowcode.service.bpm.config.listener;
import com.jeelowcode.service.bpm.config.framework.bpm.core.event.BpmProcessInstanceResultEvent;
import com.jeelowcode.service.bpm.config.framework.bpm.core.event.BpmProcessInstanceResultEventListener;
import com.jeelowcode.service.bpm.service.IBpmOALeaveService;
import com.jeelowcode.service.bpm.service.impl.BpmOALeaveServiceImpl;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* OA 请假单的结果的监听器实现类
*
* @author 芋道源码
*/
@Component
public class BpmOALeaveResultListener extends BpmProcessInstanceResultEventListener {
@Resource
private IBpmOALeaveService leaveService;
@Override
protected String getProcessDefinitionKey() {
return BpmOALeaveServiceImpl.PROCESS_KEY;
}
@Override
protected void onEvent(BpmProcessInstanceResultEvent event) {
leaveService.updateLeaveResult(Long.parseLong(event.getBusinessKey()), event.getResult());
}
}

View File

@@ -0,0 +1,39 @@
package com.jeelowcode.service.bpm.controller;
import com.jeelowcode.tool.framework.common.pojo.CommonResult;
import com.jeelowcode.service.bpm.controller.vo.activity.BpmActivityRespVO;
import com.jeelowcode.service.bpm.service.IBpmActivityService;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
import static com.jeelowcode.tool.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 流程活动实例")
@RestController
@RequestMapping("/bpm/activity")
@Validated
public class BpmActivityController {
@Resource
private IBpmActivityService activityService;
@GetMapping("/list")
@Operation(tags = "流程管理",summary = "生成指定流程实例的高亮流程图",
description = "只高亮进行中的任务。不过要注意,该接口暂时没用,通过前端的 ProcessViewer.vue 界面的 highlightDiagram 方法生成")
@Parameter(name = "processInstanceId", description = "流程实例的编号", required = true)
@PreAuthorize("@ss.hasPermission('bpm:task:query')")
public CommonResult<List<BpmActivityRespVO>> getActivityList(
@RequestParam("processInstanceId") String processInstanceId) {
return success(activityService.getActivityListByProcessInstanceId(processInstanceId));
}
}

View File

@@ -0,0 +1,81 @@
package com.jeelowcode.service.bpm.controller;
import com.jeelowcode.service.bpm.config.convert.definition.BpmFormConvert;
import com.jeelowcode.service.bpm.controller.vo.form.*;
import com.jeelowcode.service.bpm.entity.BpmFormDO;
import com.jeelowcode.service.bpm.service.IBpmFormService;
import com.jeelowcode.tool.framework.common.pojo.CommonResult;
import com.jeelowcode.tool.framework.common.pojo.PageResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
import static com.jeelowcode.tool.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 动态表单")
@RestController
@RequestMapping("/bpm/form")
@Validated
public class BpmFormController {
@Resource
private IBpmFormService formService;
@PostMapping("/create")
@Operation(tags = "流程管理",summary = "创建动态表单")
@PreAuthorize("@ss.hasPermission('bpm:form:create')")
public CommonResult<Long> createForm(@Valid @RequestBody BpmFormCreateReqVO createReqVO) {
return success(formService.createForm(createReqVO));
}
@PutMapping("/update")
@Operation(tags = "流程管理",summary = "更新动态表单")
@PreAuthorize("@ss.hasPermission('bpm:form:update')")
public CommonResult<Boolean> updateForm(@Valid @RequestBody BpmFormUpdateReqVO updateReqVO) {
formService.updateForm(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(tags = "流程管理",summary = "删除动态表单")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('bpm:form:delete')")
public CommonResult<Boolean> deleteForm(@RequestParam("id") Long id) {
formService.deleteForm(id);
return success(true);
}
@GetMapping("/get")
@Operation(tags = "流程管理",summary = "获得动态表单")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('bpm:form:query')")
public CommonResult<BpmFormRespVO> getForm(@RequestParam("id") Long id) {
BpmFormDO form = formService.getForm(id);
return success(BpmFormConvert.INSTANCE.convert(form));
}
@GetMapping("/list-all-simple")
@Operation(tags = "流程管理",summary = "获得动态表单的精简列表", description = "用于表单下拉框")
public CommonResult<List<BpmFormSimpleRespVO>> getSimpleForms() {
List<BpmFormDO> list = formService.getFormList();
return success(BpmFormConvert.INSTANCE.convertList2(list));
}
@GetMapping("/page")
@Operation(tags = "流程管理",summary = "获得动态表单分页")
@PreAuthorize("@ss.hasPermission('bpm:form:query')")
public CommonResult<PageResult<BpmFormRespVO>> getFormPage(@Valid BpmFormPageReqVO pageVO) {
PageResult<BpmFormDO> pageResult = formService.getFormPage(pageVO);
return success(BpmFormConvert.INSTANCE.convertPage(pageResult));
}
}

View File

@@ -0,0 +1,96 @@
package com.jeelowcode.service.bpm.controller;
import com.jeelowcode.service.bpm.config.convert.definition.BpmModelConvert;
import com.jeelowcode.service.bpm.controller.vo.model.*;
import com.jeelowcode.service.bpm.service.IBpmModelService;
import com.jeelowcode.tool.framework.common.pojo.CommonResult;
import com.jeelowcode.tool.framework.common.pojo.PageResult;
import com.jeelowcode.tool.framework.common.util.io.IoUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.io.IOException;
import static com.jeelowcode.tool.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 流程模型")
@RestController
@RequestMapping("/bpm/model")
@Validated
public class BpmModelController {
@Resource
private IBpmModelService modelService;
@GetMapping("/page")
@Operation(tags = "流程管理",summary = "获得模型分页")
public CommonResult<PageResult<BpmModelPageItemRespVO>> getModelPage(BpmModelPageReqVO pageVO) {
return success(modelService.getModelPage(pageVO));
}
@GetMapping("/get")
@Operation(tags = "流程管理",summary = "获得模型")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('bpm:model:query')")
public CommonResult<BpmModelRespVO> getModel(@RequestParam("id") String id) {
BpmModelRespVO model = modelService.getModel(id);
return success(model);
}
@PostMapping("/create")
@Operation(tags = "流程管理",summary = "新建模型")
@PreAuthorize("@ss.hasPermission('bpm:model:create')")
public CommonResult<String> createModel(@Valid @RequestBody BpmModelCreateReqVO createRetVO) {
return success(modelService.createModel(createRetVO, null));
}
@PutMapping("/update")
@Operation(tags = "流程管理",summary = "修改模型")
@PreAuthorize("@ss.hasPermission('bpm:model:update')")
public CommonResult<Boolean> updateModel(@Valid @RequestBody BpmModelUpdateReqVO modelVO) {
modelService.updateModel(modelVO);
return success(true);
}
@PostMapping("/import")
@Operation(tags = "流程管理",summary = "导入模型")
@PreAuthorize("@ss.hasPermission('bpm:model:import')")
public CommonResult<String> importModel(@Valid BpmModeImportReqVO importReqVO) throws IOException {
BpmModelCreateReqVO createReqVO = BpmModelConvert.INSTANCE.convert(importReqVO);
// 读取文件
String bpmnXml = IoUtils.readUtf8(importReqVO.getBpmnFile().getInputStream(), false);
return success(modelService.createModel(createReqVO, bpmnXml));
}
@PostMapping("/deploy")
@Operation(tags = "流程管理",summary = "部署模型")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('bpm:model:deploy')")
public CommonResult<Boolean> deployModel(@RequestParam("id") String id) {
modelService.deployModel(id);
return success(true);
}
@PutMapping("/update-state")
@Operation(tags = "流程管理",summary = "修改模型的状态", description = "实际更新的部署的流程定义的状态")
@PreAuthorize("@ss.hasPermission('bpm:model:update')")
public CommonResult<Boolean> updateModelState(@Valid @RequestBody BpmModelUpdateStateReqVO reqVO) {
modelService.updateModelState(reqVO.getId(), reqVO.getState());
return success(true);
}
@DeleteMapping("/delete")
@Operation(tags = "流程管理",summary = "删除模型")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('bpm:model:delete')")
public CommonResult<Boolean> deleteModel(@RequestParam("id") String id) {
modelService.deleteModel(id);
return success(true);
}
}

View File

@@ -0,0 +1,63 @@
package com.jeelowcode.service.bpm.controller;
import com.jeelowcode.service.bpm.controller.vo.oa.BpmOALeaveCreateReqVO;
import com.jeelowcode.service.bpm.controller.vo.oa.BpmOALeavePageReqVO;
import com.jeelowcode.service.bpm.controller.vo.oa.BpmOALeaveRespVO;
import com.jeelowcode.service.bpm.config.convert.oa.BpmOALeaveConvert;
import com.jeelowcode.service.bpm.entity.BpmOALeaveDO;
import com.jeelowcode.tool.framework.common.pojo.CommonResult;
import com.jeelowcode.tool.framework.common.pojo.PageResult;
import com.jeelowcode.service.bpm.service.IBpmOALeaveService;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import static com.jeelowcode.tool.framework.common.pojo.CommonResult.success;
import static com.jeelowcode.tool.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
/**
* OA 请假申请 Controller用于演示自己存储数据接入工作流的例子
*
* @author jason
* @author 芋道源码
*/
@Tag(name = "管理后台 - OA 请假申请")
@RestController
@RequestMapping("/bpm/oa/leave")
@Validated
public class BpmOALeaveController {
@Resource
private IBpmOALeaveService leaveService;
@PostMapping("/create")
@PreAuthorize("@ss.hasPermission('bpm:oa-leave:create')")
@Operation(tags = "流程管理",summary = "创建请求申请")
public CommonResult<Long> createLeave(@Valid @RequestBody BpmOALeaveCreateReqVO createReqVO) {
return success(leaveService.createLeave(getLoginUserId(), createReqVO));
}
@GetMapping("/get")
@PreAuthorize("@ss.hasPermission('bpm:oa-leave:query')")
@Operation(tags = "流程管理",summary = "获得请假申请")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
public CommonResult<BpmOALeaveRespVO> getLeave(@RequestParam("id") Long id) {
BpmOALeaveDO leave = leaveService.getLeave(id);
return success(BpmOALeaveConvert.INSTANCE.convert(leave));
}
@GetMapping("/page")
@PreAuthorize("@ss.hasPermission('bpm:oa-leave:query')")
@Operation(tags = "流程管理",summary = "获得请假申请分页")
public CommonResult<PageResult<BpmOALeaveRespVO>> getLeavePage(@Valid BpmOALeavePageReqVO pageVO) {
PageResult<BpmOALeaveDO> pageResult = leaveService.getLeavePage(getLoginUserId(), pageVO);
return success(BpmOALeaveConvert.INSTANCE.convertPage(pageResult));
}
}

View File

@@ -0,0 +1,89 @@
package com.jeelowcode.service.bpm.controller;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.jeelowcode.core.framework.controller.BaseController;
import com.jeelowcode.core.framework.entity.FormEntity;
import com.jeelowcode.core.framework.service.IFormService;
import com.jeelowcode.framework.exception.JeeLowCodeException;
import com.jeelowcode.framework.utils.model.ResultDataModel;
import com.jeelowcode.framework.utils.tool.CollectionUtil;
import com.jeelowcode.framework.utils.tool.NumberUtil;
import com.jeelowcode.tool.framework.common.pojo.CommonResult;
import com.jeelowcode.tool.framework.common.pojo.PageResult;
import com.jeelowcode.service.bpm.controller.vo.process.BpmProcessDefinitionListReqVO;
import com.jeelowcode.service.bpm.controller.vo.process.BpmProcessDefinitionPageItemRespVO;
import com.jeelowcode.service.bpm.controller.vo.process.BpmProcessDefinitionPageReqVO;
import com.jeelowcode.service.bpm.controller.vo.process.BpmProcessDefinitionRespVO;
import com.jeelowcode.service.bpm.service.IBpmProcessDefinitionService;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.security.acl.LastOwnerException;
import java.util.*;
import static com.jeelowcode.tool.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 流程定义")
@RestController
@RequestMapping("/bpm/process-definition")
public class BpmProcessDefinitionController extends BaseController {
@Resource
private IBpmProcessDefinitionService bpmDefinitionService;
@Resource
private IFormService dbFormService;
@GetMapping("/page")
@Operation(tags = "流程管理",summary = "获得流程定义分页")
@PreAuthorize("@ss.hasPermission('bpm:process-definition:query')")
public CommonResult<PageResult<BpmProcessDefinitionPageItemRespVO>> getProcessDefinitionPage(
BpmProcessDefinitionPageReqVO pageReqVO) {
return success(bpmDefinitionService.getProcessDefinitionPage(pageReqVO));
}
@GetMapping ("/list")
@Operation(tags = "流程管理",summary = "获得流程定义列表")
@PreAuthorize("@ss.hasPermission('bpm:process-definition:query')")
public CommonResult<List<BpmProcessDefinitionRespVO>> getProcessDefinitionList(
BpmProcessDefinitionListReqVO listReqVO) {
return success(bpmDefinitionService.getProcessDefinitionList(listReqVO));
}
@GetMapping ("/formlist")
@ApiOperationSupport(order = 4)
@Operation(tags = "流程管理",summary = "获得表单流程定义列表")
public CommonResult<List<BpmProcessDefinitionRespVO>> getProcessDefinitionList(String formId) {
return success(bpmDefinitionService.getProcessDefinitionFormList(NumberUtil.toLong(formId)));
}
@GetMapping ("/dbformlist")
@ApiOperationSupport(order = 4)
@Operation(tags = "流程管理",summary = "获得表单流程定义列表")
public CommonResult<List<BpmProcessDefinitionRespVO>> getProcessDefinitionListUseDbFormId(String dbFormId) {
FormEntity formEntity = dbFormService.getFormEntityById(NumberUtil.toLong(dbFormId));
if(Objects.isNull(formEntity)){
return success(new ArrayList<>());
}
List<BpmProcessDefinitionRespVO> list = bpmDefinitionService.getProcessDefinitionFormList(formEntity.getDesformWebId());
return success(list);
}
@GetMapping ("/get-bpmn-xml")
@Operation(tags = "流程管理",summary = "获得流程定义的 BPMN XML")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
//@PreAuthorize("@ss.hasPermission('bpm:process-definition:query')")
public CommonResult<String> getProcessDefinitionBpmnXML(@RequestParam("id") String id) {
String bpmnXML = bpmDefinitionService.getProcessDefinitionBpmnXML(id);
return success(bpmnXML);
}
}

View File

@@ -0,0 +1,93 @@
package com.jeelowcode.service.bpm.controller;
import com.jeelowcode.core.framework.controller.BaseController;
import com.jeelowcode.core.framework.enums.ApproveStatusEnum;
import com.jeelowcode.framework.utils.model.ResultDataModel;
import com.jeelowcode.framework.utils.tool.CollectionUtil;
import com.jeelowcode.framework.utils.tool.NumberUtil;
import com.jeelowcode.service.bpm.controller.vo.instance.*;
import com.jeelowcode.service.bpm.controller.vo.process.BpmProcessDefinitionRespVO;
import com.jeelowcode.service.bpm.service.IBpmProcessInstanceService;
import com.jeelowcode.tool.framework.common.pojo.CommonResult;
import com.jeelowcode.tool.framework.common.pojo.PageResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import static com.jeelowcode.tool.framework.common.pojo.CommonResult.success;
import static com.jeelowcode.tool.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - 流程实例") // 流程实例,通过流程定义创建的一次“申请”
@RestController
@RequestMapping("/bpm/process-instance")
@Validated
public class BpmProcessInstanceController extends BaseController {
@Resource
private IBpmProcessInstanceService processInstanceService;
@GetMapping("/my-page")
@Operation(tags = "流程管理",summary = "获得我的实例分页列表", description = "在【我的流程】菜单中,进行调用")
@PreAuthorize("@ss.hasPermission('bpm:process-instance:query')")
public CommonResult<PageResult<BpmProcessInstancePageItemRespVO>> getMyProcessInstancePage(
@Valid BpmProcessInstanceMyPageReqVO pageReqVO) {
return success(processInstanceService.getMyProcessInstancePage(getLoginUserId(), pageReqVO));
}
@PostMapping("/create")
@Operation(tags = "流程管理",summary = "新建流程实例")
@PreAuthorize("@ss.hasPermission('bpm:process-instance:query')")
public CommonResult<String> createProcessInstance(@Valid @RequestBody BpmProcessInstanceCreateReqVO createReqVO) {
return success(processInstanceService.createProcessInstance(getLoginUserId(), createReqVO));
}
@PostMapping("/listCreate")
@Operation(tags = "流程管理",summary = "新建流程实例")
@PreAuthorize("@ss.hasPermission('bpm:process-instance:query')")
public CommonResult<String> createListProcessInstance(@Valid @RequestBody BpmProcessInstanceCreateReqVO createReqVO) {
ResultDataModel dataDetail = super.getDataDetail(NumberUtil.toLong(createReqVO.getDbFormId()),NumberUtil.toLong(createReqVO.getDataId()),new HashMap<String,Object>());
if(Objects.nonNull(dataDetail)){
dataDetail.getRecords().get(0).put("approveStatusName","");
createReqVO.setVariables(dataDetail.getRecords().get(0));
}
return success(processInstanceService.createProcessInstance(getLoginUserId(), createReqVO));
}
@PostMapping("/createV2")
@Operation(tags = "流程管理",summary = "新建流程实例(自定义流程发起使用)")
@PreAuthorize("@ss.hasPermission('bpm:process-instance:query')")
public CommonResult<String> createProcessInstanceV2(@Valid @RequestBody BpmProcessInstanceCreateReqVO createReqVO) {
return success(processInstanceService.createProcessInstanceV2(getLoginUserId(), createReqVO));
}
@GetMapping("/get")
@Operation(tags = "流程管理",summary = "获得指定流程实例", description = "在【流程详细】界面中,进行调用")
@Parameter(name = "id", description = "流程实例的编号", required = true)
@PreAuthorize("@ss.hasPermission('bpm:process-instance:query')")
public CommonResult<BpmProcessInstanceRespVO> getProcessInstance(@RequestParam("id") String id) {
return success(processInstanceService.getProcessInstanceVO(id));
}
@DeleteMapping("/cancel")
@Operation(tags = "流程管理",summary = "取消流程实例", description = "撤回发起的流程")
@PreAuthorize("@ss.hasPermission('bpm:process-instance:cancel')")
public CommonResult<Boolean> cancelProcessInstance(@Valid @RequestBody BpmProcessInstanceCancelReqVO cancelReqVO) {
processInstanceService.cancelProcessInstance(getLoginUserId(), cancelReqVO);
return success(true);
}
}

View File

@@ -0,0 +1,77 @@
package com.jeelowcode.service.bpm.controller;
import cn.hutool.core.collection.CollUtil;
import com.jeelowcode.tool.framework.common.pojo.CommonResult;
import com.jeelowcode.tool.framework.common.pojo.PageResult;
import com.jeelowcode.service.bpm.controller.vo.instance.BpmProcessInstanceCopyCreateReqVO;
import com.jeelowcode.service.bpm.controller.vo.instance.BpmProcessInstanceCopyMyPageReqVO;
import com.jeelowcode.service.bpm.controller.vo.instance.BpmProcessInstanceCopyPageItemRespVO;
import com.jeelowcode.service.bpm.config.convert.cc.BpmProcessInstanceCopyConvert;
import com.jeelowcode.service.bpm.entity.BpmProcessInstanceCopyDO;
import com.jeelowcode.service.bpm.service.IBpmProcessInstanceService;
import com.jeelowcode.service.bpm.service.IBpmTaskService;
import com.jeelowcode.service.bpm.service.IBpmProcessInstanceCopyService;
import com.jeelowcode.service.system.api.IApiAdminUserApi;
import com.jeelowcode.service.system.dto.AdminUserRespDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.Map;
import java.util.stream.Stream;
import static com.jeelowcode.tool.framework.common.pojo.CommonResult.success;
import static com.jeelowcode.tool.framework.common.util.collection.CollectionUtils.convertListByFlatMap;
import static com.jeelowcode.tool.framework.common.util.collection.CollectionUtils.convertSet;
import static com.jeelowcode.tool.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - 流程实例抄送")
@RestController
@RequestMapping("/bpm/process-instance/cc")
@Validated
public class BpmProcessInstanceCopyController {
@Resource
private IBpmProcessInstanceCopyService processInstanceCopyService;
@Resource
private IBpmProcessInstanceService bpmProcessInstanceService;
@Resource
private IApiAdminUserApi apiAdminUserApi;
@Resource
private IBpmTaskService bpmTaskService;
@PostMapping("/create")
@Operation(tags = "流程管理",summary = "抄送流程")
// @PreAuthorize("@ss.hasPermission('bpm:process-instance-cc:create')")
public CommonResult<Boolean> createProcessInstanceCopy(@Valid @RequestBody BpmProcessInstanceCopyCreateReqVO createReqVO) {
processInstanceCopyService.createProcessInstanceCopy(getLoginUserId(), createReqVO);
return success(true);
}
@GetMapping("/my-page")
@Operation(tags = "流程管理",summary = "获得抄送流程分页列表")
// @PreAuthorize("@ss.hasPermission('bpm:process-instance-cc:query')")
public CommonResult<PageResult<BpmProcessInstanceCopyPageItemRespVO>> getProcessInstanceCopyPage(
@Valid BpmProcessInstanceCopyMyPageReqVO pageReqVO) {
PageResult<BpmProcessInstanceCopyDO> pageResult = processInstanceCopyService.getMyProcessInstanceCopyPage(getLoginUserId(), pageReqVO);
if (CollUtil.isEmpty(pageResult.getList())) {
return success(new PageResult<>(pageResult.getTotal()));
}
// 拼接返回
Map<String, String> taskNameMap = bpmTaskService.getTaskNameByTaskIds(
convertSet(pageResult.getList(), BpmProcessInstanceCopyDO::getTaskId));
Map<String, String> processNameMap = bpmProcessInstanceService.getProcessInstanceNameMap(
convertSet(pageResult.getList(), BpmProcessInstanceCopyDO::getProcessInstanceId));
Map<Long, AdminUserRespDTO> userMap = apiAdminUserApi.getUserMap(convertListByFlatMap(pageResult.getList(),
copy -> Stream.of(copy.getStartUserId(), Long.parseLong(copy.getCreator()))));
return success(BpmProcessInstanceCopyConvert.INSTANCE.convertPage(pageResult, taskNameMap, processNameMap, userMap));
}
}

View File

@@ -0,0 +1,58 @@
package com.jeelowcode.service.bpm.controller;
import com.jeelowcode.tool.framework.common.pojo.CommonResult;
import com.jeelowcode.service.bpm.controller.vo.rule.BpmTaskAssignRuleCreateReqVO;
import com.jeelowcode.service.bpm.controller.vo.rule.BpmTaskAssignRuleRespVO;
import com.jeelowcode.service.bpm.controller.vo.rule.BpmTaskAssignRuleUpdateReqVO;
import com.jeelowcode.service.bpm.service.IBpmTaskAssignRuleService;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
import static com.jeelowcode.tool.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 任务分配规则")
@RestController
@RequestMapping("/bpm/task-assign-rule")
@Validated
public class BpmTaskAssignRuleController {
@Resource
private IBpmTaskAssignRuleService taskAssignRuleService;
@GetMapping("/list")
@Operation(tags = "流程管理",summary = "获得任务分配规则列表")
@Parameters({
@Parameter(name = "modelId", description = "模型编号", example = "1024"),
@Parameter(name = "processDefinitionId", description = "流程定义的编号", example = "2048")
})
@PreAuthorize("@ss.hasPermission('bpm:task-assign-rule:query')")
public CommonResult<List<BpmTaskAssignRuleRespVO>> getTaskAssignRuleList(
@RequestParam(value = "modelId", required = false) String modelId,
@RequestParam(value = "processDefinitionId", required = false) String processDefinitionId) {
return success(taskAssignRuleService.getTaskAssignRuleList(modelId, processDefinitionId));
}
@PostMapping("/create")
@Operation(tags = "流程管理",summary = "创建任务分配规则")
@PreAuthorize("@ss.hasPermission('bpm:task-assign-rule:create')")
public CommonResult<Long> createTaskAssignRule(@Valid @RequestBody BpmTaskAssignRuleCreateReqVO reqVO) {
return success(taskAssignRuleService.createTaskAssignRule(reqVO));
}
@PutMapping("/update")
@Operation(tags = "流程管理",summary = "更新任务分配规则")
@PreAuthorize("@ss.hasPermission('bpm:task-assign-rule:update')")
public CommonResult<Boolean> updateTaskAssignRule(@Valid @RequestBody BpmTaskAssignRuleUpdateReqVO reqVO) {
taskAssignRuleService.updateTaskAssignRule(reqVO);
return success(true);
}
}

View File

@@ -0,0 +1,125 @@
package com.jeelowcode.service.bpm.controller;
import com.jeelowcode.service.bpm.controller.vo.task.*;
import com.jeelowcode.service.bpm.service.IBpmTaskService;
import com.jeelowcode.tool.framework.common.pojo.CommonResult;
import com.jeelowcode.tool.framework.common.pojo.PageResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
import static com.jeelowcode.tool.framework.common.pojo.CommonResult.success;
import static com.jeelowcode.tool.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - 流程任务实例")
@RestController
@RequestMapping("/bpm/task")
@Validated
public class BpmTaskController {
@Resource
private IBpmTaskService taskService;
@GetMapping("todo-page")
@Operation(tags = "流程管理",summary = "获取 Todo 待办任务分页")
@PreAuthorize("@ss.hasPermission('bpm:task:query')")
public CommonResult<PageResult<BpmTaskTodoPageItemRespVO>> getTodoTaskPage(@Valid BpmTaskTodoPageReqVO pageVO) {
return success(taskService.getTodoTaskPage(getLoginUserId(), pageVO));
}
@GetMapping("done-page")
@Operation(tags = "流程管理",summary = "获取 Done 已办任务分页")
@PreAuthorize("@ss.hasPermission('bpm:task:query')")
public CommonResult<PageResult<BpmTaskDonePageItemRespVO>> getDoneTaskPage(@Valid BpmTaskDonePageReqVO pageVO) {
return success(taskService.getDoneTaskPage(getLoginUserId(), pageVO));
}
@GetMapping("/list-by-process-instance-id")
@Operation(tags = "流程管理",summary = "获得指定流程实例的任务列表", description = "包括完成的、未完成的")
@Parameter(name = "processInstanceId", description = "流程实例的编号", required = true)
@PreAuthorize("@ss.hasPermission('bpm:task:query')")
public CommonResult<List<BpmTaskRespVO>> getTaskListByProcessInstanceId(
@RequestParam("processInstanceId") String processInstanceId) {
return success(taskService.getTaskListByProcessInstanceId(processInstanceId));
}
@PutMapping("/approve")
@Operation(tags = "流程管理",summary = "通过任务")
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
public CommonResult<Boolean> approveTask(@Valid @RequestBody BpmTaskApproveReqVO reqVO) {
taskService.approveTask(getLoginUserId(), reqVO);
return success(true);
}
@PutMapping("/reject")
@Operation(tags = "流程管理",summary = "不通过任务")
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
public CommonResult<Boolean> rejectTask(@Valid @RequestBody BpmTaskRejectReqVO reqVO) {
taskService.rejectTask(getLoginUserId(), reqVO);
return success(true);
}
@PutMapping("/update-assignee")
@Operation(tags = "流程管理",summary = "更新任务的负责人", description = "用于【流程详情】的【转派】按钮")
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
public CommonResult<Boolean> updateTaskAssignee(@Valid @RequestBody BpmTaskUpdateAssigneeReqVO reqVO) {
taskService.updateTaskAssignee(getLoginUserId(), reqVO);
return success(true);
}
@GetMapping("/return-list")
@Operation(tags = "流程管理",summary = "获取所有可回退的节点", description = "用于【流程详情】的【回退】按钮")
@Parameter(name = "taskId", description = "当前任务ID", required = true)
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
public CommonResult<List<BpmTaskSimpleRespVO>> getReturnList(@RequestParam("taskId") String taskId) {
return success(taskService.getReturnTaskList(taskId));
}
@PutMapping("/return")
@Operation(tags = "流程管理",summary = "回退任务", description = "用于【流程详情】的【回退】按钮")
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
public CommonResult<Boolean> returnTask(@Valid @RequestBody BpmTaskReturnReqVO reqVO) {
taskService.returnTask(getLoginUserId(), reqVO);
return success(true);
}
@PutMapping("/delegate")
@Operation(tags = "流程管理",summary = "委派任务", description = "用于【流程详情】的【委派】按钮。和向前【加签】有点像,唯一区别是【委托】没有单独创立任务")
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
public CommonResult<Boolean> delegateTask(@Valid @RequestBody BpmTaskDelegateReqVO reqVO) {
taskService.delegateTask(getLoginUserId(), reqVO);
return success(true);
}
@PutMapping("/create-sign")
@Operation(tags = "流程管理",summary = "加签", description = "before 前加签after 后加签")
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
public CommonResult<Boolean> createSignTask(@Valid @RequestBody BpmTaskAddSignReqVO reqVO) {
taskService.createSignTask(getLoginUserId(), reqVO);
return success(true);
}
@DeleteMapping("/delete-sign")
@Operation(tags = "流程管理",summary = "减签")
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
public CommonResult<Boolean> deleteSignTask(@Valid @RequestBody BpmTaskSubSignReqVO reqVO) {
taskService.deleteSignTask(getLoginUserId(), reqVO);
return success(true);
}
@GetMapping("children-list")
@Operation(tags = "流程管理",summary = "获取能被减签的任务")
@Parameter(name = "parentId", description = "父级任务 ID", required = true)
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
public CommonResult<List<BpmTaskSubSignRespVO>> getChildrenTaskList(@RequestParam("parentId") String parentId) {
return success(taskService.getChildrenTaskList(parentId));
}
}

View File

@@ -0,0 +1,85 @@
package com.jeelowcode.service.bpm.controller;
import com.jeelowcode.service.bpm.controller.vo.group.BpmUserGroupCreateReqVO;
import com.jeelowcode.service.bpm.controller.vo.group.BpmUserGroupPageReqVO;
import com.jeelowcode.service.bpm.controller.vo.group.BpmUserGroupRespVO;
import com.jeelowcode.service.bpm.controller.vo.group.BpmUserGroupUpdateReqVO;
import com.jeelowcode.service.bpm.config.convert.definition.BpmUserGroupConvert;
import com.jeelowcode.service.bpm.entity.BpmUserGroupDO;
import com.jeelowcode.service.bpm.service.IBpmUserGroupService;
import com.jeelowcode.tool.framework.common.enums.CommonStatusEnum;
import com.jeelowcode.tool.framework.common.pojo.CommonResult;
import com.jeelowcode.tool.framework.common.pojo.PageResult;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
import static com.jeelowcode.tool.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 用户组")
@RestController
@RequestMapping("/bpm/user-group")
@Validated
public class BpmUserGroupController {
@Resource
private IBpmUserGroupService userGroupService;
@PostMapping("/create")
@Operation(tags = "流程管理",summary = "创建用户组")
@PreAuthorize("@ss.hasPermission('bpm:user-group:create')")
public CommonResult<Long> createUserGroup(@Valid @RequestBody BpmUserGroupCreateReqVO createReqVO) {
return success(userGroupService.createUserGroup(createReqVO));
}
@PutMapping("/update")
@Operation(tags = "流程管理",summary = "更新用户组")
@PreAuthorize("@ss.hasPermission('bpm:user-group:update')")
public CommonResult<Boolean> updateUserGroup(@Valid @RequestBody BpmUserGroupUpdateReqVO updateReqVO) {
userGroupService.updateUserGroup(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(tags = "流程管理",summary = "删除用户组")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('bpm:user-group:delete')")
public CommonResult<Boolean> deleteUserGroup(@RequestParam("id") Long id) {
userGroupService.deleteUserGroup(id);
return success(true);
}
@GetMapping("/get")
@Operation(tags = "流程管理",summary = "获得用户组")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('bpm:user-group:query')")
public CommonResult<BpmUserGroupRespVO> getUserGroup(@RequestParam("id") Long id) {
BpmUserGroupDO userGroup = userGroupService.getUserGroup(id);
return success(BpmUserGroupConvert.INSTANCE.convert(userGroup));
}
@GetMapping("/page")
@Operation(tags = "流程管理",summary = "获得用户组分页")
@PreAuthorize("@ss.hasPermission('bpm:user-group:query')")
public CommonResult<PageResult<BpmUserGroupRespVO>> getUserGroupPage(@Valid BpmUserGroupPageReqVO pageVO) {
PageResult<BpmUserGroupDO> pageResult = userGroupService.getUserGroupPage(pageVO);
return success(BpmUserGroupConvert.INSTANCE.convertPage(pageResult));
}
@GetMapping("/list-all-simple")
@Operation(tags = "流程管理",summary = "获取用户组精简信息列表", description = "只包含被开启的用户组,主要用于前端的下拉选项")
public CommonResult<List<BpmUserGroupRespVO>> getSimpleUserGroups() {
// 获用户门列表,只要开启状态的
List<BpmUserGroupDO> list = userGroupService.getUserGroupListByStatus(CommonStatusEnum.ENABLE.getStatus());
// 排序后,返回给前端
return success(BpmUserGroupConvert.INSTANCE.convertList2(list));
}
}

View File

@@ -0,0 +1,25 @@
package com.jeelowcode.service.bpm.controller.vo.activity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 流程活动的 Response VO")
@Data
public class BpmActivityRespVO {
@Schema(description = "流程活动的标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private String key;
@Schema(description = "流程活动的类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "StartEvent")
private String type;
@Schema(description = "流程活动的开始时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime startTime;
@Schema(description = "流程活动的结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime endTime;
@Schema(description = "关联的流程任务的编号-关联的流程任务,只有 UserTask 等类型才有", example = "2048")
private String taskId;
}

View File

@@ -0,0 +1,27 @@
package com.jeelowcode.service.bpm.controller.vo.candidate;
import com.jeelowcode.service.bpm.controller.vo.rule.BpmTaskAssignRuleBaseVO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.util.Set;
/**
* 流程任务分配规则 Base VO提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*
* @see BpmTaskAssignRuleBaseVO
*/
@Data
public class BpmTaskCandidateRuleVO {
@Schema(description = "规则类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "bpm_task_assign_rule_type")
@NotNull(message = "规则类型不能为空")
private Integer type;
@Schema(description = "规则值数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3")
@NotNull(message = "规则值数组不能为空")
private Set<Long> options;
}

View File

@@ -0,0 +1,28 @@
package com.jeelowcode.service.bpm.controller.vo.enu;
public enum BpmFormType {
ZUOYE("1958710228903837697","sc_zuoye","c1c87f9d-8198-11f0-ad7a-00ff09f9c682");
private final String formId;
private final String formTable;
private final String bpmId;
BpmFormType(String formId, String formTable,String bpmId) {
this.formId = formId;
this.formTable = formTable;
this.bpmId = bpmId;
}
public String getFormId() { return formId; }
public String getFormTable() { return formTable; }
public String getBpmId(){
return bpmId;
}
}

View File

@@ -0,0 +1,24 @@
package com.jeelowcode.service.bpm.controller.vo.form;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import javax.validation.constraints.*;
/**
* 动态表单 Base VO提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*/
@Data
public class BpmFormBaseVO {
@Schema(description = "表单名称", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "表单名称不能为空")
private String name;
@Schema(description = "表单状态-参见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "表单状态不能为空")
private Integer status;
@Schema(description = "备注", example = "我是备注")
private String remark;
}

View File

@@ -0,0 +1,22 @@
package com.jeelowcode.service.bpm.controller.vo.form;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import javax.validation.constraints.NotNull;
import java.util.List;
@Schema(description = "管理后台 - 动态表单创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class BpmFormCreateReqVO extends BpmFormBaseVO {
@Schema(description = "表单的配置-JSON 字符串", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "表单的配置不能为空")
private String conf;
@Schema(description = "表单项的数组-JSON 字符串的数组", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "表单项的数组不能为空")
private List<String> fields;
}

View File

@@ -0,0 +1,18 @@
package com.jeelowcode.service.bpm.controller.vo.form;
import com.jeelowcode.tool.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 动态表单分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class BpmFormPageReqVO extends PageParam {
@Schema(description = "表单名称")
private String name;
}

View File

@@ -0,0 +1,31 @@
package com.jeelowcode.service.bpm.controller.vo.form;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - 动态表单 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class BpmFormRespVO extends BpmFormBaseVO {
@Schema(description = "表单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "表单的配置-JSON 字符串", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "表单的配置不能为空")
private String conf;
@Schema(description = "表单项的数组-JSON 字符串的数组", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "表单项的数组不能为空")
private List<String> fields;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,16 @@
package com.jeelowcode.service.bpm.controller.vo.form;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 流程表单精简 Response VO")
@Data
public class BpmFormSimpleRespVO {
@Schema(description = "表单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "表单名称", requiredMode = Schema.RequiredMode.REQUIRED)
private String name;
}

View File

@@ -0,0 +1,25 @@
package com.jeelowcode.service.bpm.controller.vo.form;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import javax.validation.constraints.*;
import java.util.List;
@Schema(description = "管理后台 - 动态表单更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class BpmFormUpdateReqVO extends BpmFormBaseVO {
@Schema(description = "表单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "表单编号不能为空")
private Long id;
@Schema(description = "表单的配置-JSON 字符串", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "表单的配置不能为空")
private String conf;
@Schema(description = "表单项的数组-JSON 字符串的数组", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "表单项的数组不能为空")
private List<String> fields;
}

View File

@@ -0,0 +1,32 @@
package com.jeelowcode.service.bpm.controller.vo.group;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.util.Set;
/**
* 用户组 Base VO提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*/
@Data
public class BpmUserGroupBaseVO {
@Schema(description = "组名", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "组名不能为空")
private String name;
@Schema(description = "描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码")
@NotNull(message = "描述不能为空")
private String description;
@Schema(description = "成员编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3")
@NotNull(message = "成员编号数组不能为空")
private Set<Long> memberUserIds;
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "状态不能为空")
private Integer status;
}

View File

@@ -0,0 +1,11 @@
package com.jeelowcode.service.bpm.controller.vo.group;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
@Schema(description = "管理后台 - 用户组创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class BpmUserGroupCreateReqVO extends BpmUserGroupBaseVO {
}

Some files were not shown because too many files have changed in this diff Show More