【功能新增】bpm:仿钉钉、飞书的设计器
parent
91180bc65b
commit
10b2d10fcc
Binary file not shown.
After Width: | Height: | Size: 177 KiB |
Binary file not shown.
After Width: | Height: | Size: 126 KiB |
22
README.md
22
README.md
|
@ -152,18 +152,22 @@
|
||||||
|
|
||||||
### 工作流程
|
### 工作流程
|
||||||
|
|
||||||
| | 功能 | 描述 |
|
| | 功能 | 描述 |
|
||||||
|-----|-------|----------------------------------------|
|
|----|-------|-----------------------------------------|
|
||||||
| 🚀 | 流程模型 | 配置工作流的流程模型,支持文件导入与在线设计流程图,提供 7 种任务分配规则 |
|
| 🚀 | 流程模型 | 配置工作流的流程模型,支持 BPMN 和仿钉钉/飞书设计器 |
|
||||||
| 🚀 | 流程表单 | 拖动表单元素生成相应的工作流表单,覆盖 Element UI 所有的表单组件 |
|
| 🚀 | 流程表单 | 拖动表单元素生成相应的工作流表单,覆盖 Element UI 所有的表单组件 |
|
||||||
| 🚀 | 用户分组 | 自定义用户分组,可用于工作流的审批分组 |
|
| 🚀 | 用户分组 | 自定义用户分组,可用于工作流的审批分组 |
|
||||||
| 🚀 | 我的流程 | 查看我发起的工作流程,支持新建、取消流程等操作,高亮流程图、审批时间线 |
|
| 🚀 | 我的流程 | 查看我发起的工作流程,支持新建、取消流程等操作,高亮流程图、审批时间线 |
|
||||||
| 🚀 | 待办任务 | 查看自己【未】审批的工作任务,支持通过、不通过、转发、委派、退回等操作 |
|
| 🚀 | 待办任务 | 查看自己【未】审批的工作任务,支持通过、不通过、转派、委派、退回、加减签等操作 |
|
||||||
| 🚀 | 已办任务 | 查看自己【已】审批的工作任务,未来会支持回退操作 |
|
| 🚀 | 已办任务 | 查看自己【已】审批的工作任务,支持流程预测,展示未来审批人信息 |
|
||||||
| 🚀 | OA 请假 | 作为业务自定义接入工作流的使用示例,只需创建请求对应的工作流程,即可进行审批 |
|
| 🚀 | OA 请假 | 作为业务自定义接入工作流的使用示例,只需创建请求对应的工作流程,即可进行审批 |
|
||||||
|
|
||||||
![功能图](/.image/common/bpm-feature.png)
|
![功能图](/.image/common/bpm-feature.png)
|
||||||
|
|
||||||
|
| BPMN 设计器 | 钉钉/飞书设计器 |
|
||||||
|
|------------------------------|--------------------------------|
|
||||||
|
| ![](/.image/工作流设计器-bpmn.jpg) | ![](/.image/工作流设计器-simple.jpg) |
|
||||||
|
|
||||||
### 支付系统
|
### 支付系统
|
||||||
|
|
||||||
| | 功能 | 描述 |
|
| | 功能 | 描述 |
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
Target Server Version : 80200 (8.2.0)
|
Target Server Version : 80200 (8.2.0)
|
||||||
File Encoding : 65001
|
File Encoding : 65001
|
||||||
|
|
||||||
Date: 09/11/2024 18:16:12
|
Date: 23/11/2024 10:16:46
|
||||||
*/
|
*/
|
||||||
|
|
||||||
SET NAMES utf8mb4;
|
SET NAMES utf8mb4;
|
||||||
|
@ -91,7 +91,7 @@ CREATE TABLE `infra_api_error_log` (
|
||||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||||
PRIMARY KEY (`id`) USING BTREE
|
PRIMARY KEY (`id`) USING BTREE
|
||||||
) ENGINE = InnoDB AUTO_INCREMENT = 21213 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志';
|
) ENGINE = InnoDB AUTO_INCREMENT = 21220 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志';
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
-- Records of infra_api_error_log
|
-- Records of infra_api_error_log
|
||||||
|
@ -250,7 +250,7 @@ CREATE TABLE `infra_file` (
|
||||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||||
PRIMARY KEY (`id`) USING BTREE
|
PRIMARY KEY (`id`) USING BTREE
|
||||||
) ENGINE = InnoDB AUTO_INCREMENT = 1558 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
|
) ENGINE = InnoDB AUTO_INCREMENT = 1561 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
-- Records of infra_file
|
-- Records of infra_file
|
||||||
|
@ -1044,7 +1044,7 @@ CREATE TABLE `system_login_log` (
|
||||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||||
PRIMARY KEY (`id`) USING BTREE
|
PRIMARY KEY (`id`) USING BTREE
|
||||||
) ENGINE = InnoDB AUTO_INCREMENT = 3370 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录';
|
) ENGINE = InnoDB AUTO_INCREMENT = 3395 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录';
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
-- Records of system_login_log
|
-- Records of system_login_log
|
||||||
|
@ -2165,7 +2165,7 @@ CREATE TABLE `system_oauth2_access_token` (
|
||||||
PRIMARY KEY (`id`) USING BTREE,
|
PRIMARY KEY (`id`) USING BTREE,
|
||||||
INDEX `idx_access_token`(`access_token` ASC) USING BTREE,
|
INDEX `idx_access_token`(`access_token` ASC) USING BTREE,
|
||||||
INDEX `idx_refresh_token`(`refresh_token` ASC) USING BTREE
|
INDEX `idx_refresh_token`(`refresh_token` ASC) USING BTREE
|
||||||
) ENGINE = InnoDB AUTO_INCREMENT = 11308 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌';
|
) ENGINE = InnoDB AUTO_INCREMENT = 11844 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌';
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
-- Records of system_oauth2_access_token
|
-- Records of system_oauth2_access_token
|
||||||
|
@ -2287,7 +2287,7 @@ CREATE TABLE `system_oauth2_refresh_token` (
|
||||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||||
PRIMARY KEY (`id`) USING BTREE
|
PRIMARY KEY (`id`) USING BTREE
|
||||||
) ENGINE = InnoDB AUTO_INCREMENT = 1676 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌';
|
) ENGINE = InnoDB AUTO_INCREMENT = 1696 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌';
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
-- Records of system_oauth2_refresh_token
|
-- Records of system_oauth2_refresh_token
|
||||||
|
@ -2308,6 +2308,7 @@ CREATE TABLE `system_operate_log` (
|
||||||
`sub_type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作名',
|
`sub_type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作名',
|
||||||
`biz_id` bigint NOT NULL COMMENT '操作数据模块编号',
|
`biz_id` bigint NOT NULL COMMENT '操作数据模块编号',
|
||||||
`action` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '操作内容',
|
`action` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '操作内容',
|
||||||
|
`success` bit(1) NOT NULL DEFAULT b'1' COMMENT '操作结果',
|
||||||
`extra` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '拓展字段',
|
`extra` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '拓展字段',
|
||||||
`request_method` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '请求方法名',
|
`request_method` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '请求方法名',
|
||||||
`request_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '请求地址',
|
`request_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '请求地址',
|
||||||
|
@ -3303,7 +3304,7 @@ CREATE TABLE `system_sms_code` (
|
||||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||||
PRIMARY KEY (`id`) USING BTREE,
|
PRIMARY KEY (`id`) USING BTREE,
|
||||||
INDEX `idx_mobile`(`mobile` ASC) USING BTREE COMMENT '手机号'
|
INDEX `idx_mobile`(`mobile` ASC) USING BTREE COMMENT '手机号'
|
||||||
) ENGINE = InnoDB AUTO_INCREMENT = 642 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '手机验证码';
|
) ENGINE = InnoDB AUTO_INCREMENT = 644 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '手机验证码';
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
-- Records of system_sms_code
|
-- Records of system_sms_code
|
||||||
|
@ -3344,7 +3345,7 @@ CREATE TABLE `system_sms_log` (
|
||||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||||
PRIMARY KEY (`id`) USING BTREE
|
PRIMARY KEY (`id`) USING BTREE
|
||||||
) ENGINE = InnoDB AUTO_INCREMENT = 1234 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信日志';
|
) ENGINE = InnoDB AUTO_INCREMENT = 1238 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信日志';
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
-- Records of system_sms_log
|
-- Records of system_sms_log
|
||||||
|
@ -3642,7 +3643,7 @@ CREATE TABLE `system_users` (
|
||||||
-- Records of system_users
|
-- Records of system_users
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
BEGIN;
|
BEGIN;
|
||||||
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 'admin', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '芋道源码', '管理员', 103, '[1,2]', 'aoteman@126.com', '18818260277', 2, 'http://test.yudao.iocoder.cn/bf2002b38950c904243be7c825d3f82e29f25a44526583c3fde2ebdff3a87f75.png', 0, '0:0:0:0:0:0:0:1', '2024-11-08 19:27:07', 'admin', '2021-01-05 17:03:47', NULL, '2024-11-08 19:27:07', b'0', 1);
|
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 'admin', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '芋道源码', '管理员', 103, '[1,2]', 'aoteman@126.com', '18818260277', 2, 'http://test.yudao.iocoder.cn/bf2002b38950c904243be7c825d3f82e29f25a44526583c3fde2ebdff3a87f75.png', 0, '0:0:0:0:0:0:0:1', '2024-11-22 20:11:14', 'admin', '2021-01-05 17:03:47', NULL, '2024-11-22 20:11:14', b'0', 1);
|
||||||
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (100, 'yudao', '$2a$04$IgUse/ibRzAZ3rngCThmtemJeoh15Ux1TQ2hIMe4iwt/K3LcFHEda', '芋道', '不要吓我', 104, '[1]', 'yudao@iocoder.cn', '15601691300', 1, '', 0, '0:0:0:0:0:0:0:1', '2024-11-02 14:00:46', '', '2021-01-07 09:07:17', NULL, '2024-11-02 14:00:46', b'0', 1);
|
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (100, 'yudao', '$2a$04$IgUse/ibRzAZ3rngCThmtemJeoh15Ux1TQ2hIMe4iwt/K3LcFHEda', '芋道', '不要吓我', 104, '[1]', 'yudao@iocoder.cn', '15601691300', 1, '', 0, '0:0:0:0:0:0:0:1', '2024-11-02 14:00:46', '', '2021-01-07 09:07:17', NULL, '2024-11-02 14:00:46', b'0', 1);
|
||||||
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (103, 'yuanma', '$2a$04$fUBSmjKCPYAUmnMzOb6qE.eZCGPhHi1JmAKclODbfS/O7fHOl2bH6', '源码', NULL, 106, NULL, 'yuanma@iocoder.cn', '15601701300', 0, '', 0, '0:0:0:0:0:0:0:1', '2024-08-11 17:48:12', '', '2021-01-13 23:50:35', NULL, '2024-08-11 17:48:12', b'0', 1);
|
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (103, 'yuanma', '$2a$04$fUBSmjKCPYAUmnMzOb6qE.eZCGPhHi1JmAKclODbfS/O7fHOl2bH6', '源码', NULL, 106, NULL, 'yuanma@iocoder.cn', '15601701300', 0, '', 0, '0:0:0:0:0:0:0:1', '2024-08-11 17:48:12', '', '2021-01-13 23:50:35', NULL, '2024-08-11 17:48:12', b'0', 1);
|
||||||
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (104, 'test', '$2a$04$jDFLttgfik0QqJKAbfhMa.2A9xXoZmAIxakdFJUzkX.MgBKT6ddo6', '测试号', NULL, 107, '[1,2]', '111@qq.com', '15601691200', 1, '', 0, '0:0:0:0:0:0:0:1', '2024-09-17 15:05:43', '', '2021-01-21 02:13:53', NULL, '2024-09-17 15:05:43', b'0', 1);
|
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (104, 'test', '$2a$04$jDFLttgfik0QqJKAbfhMa.2A9xXoZmAIxakdFJUzkX.MgBKT6ddo6', '测试号', NULL, 107, '[1,2]', '111@qq.com', '15601691200', 1, '', 0, '0:0:0:0:0:0:0:1', '2024-09-17 15:05:43', '', '2021-01-21 02:13:53', NULL, '2024-09-17 15:05:43', b'0', 1);
|
||||||
|
|
|
@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.common.util.collection;
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.collection.CollectionUtil;
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
import cn.hutool.core.util.ArrayUtil;
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
@ -73,6 +74,13 @@ public class CollectionUtils {
|
||||||
return from.stream().filter(filter).map(func).filter(Objects::nonNull).collect(Collectors.toList());
|
return from.stream().filter(filter).map(func).filter(Objects::nonNull).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T, U> PageResult<U> convertPage(PageResult<T> from, Function<T, U> func) {
|
||||||
|
if (ArrayUtil.isEmpty(from)) {
|
||||||
|
return new PageResult<>(from.getTotal());
|
||||||
|
}
|
||||||
|
return new PageResult<>(convertList(from.getList(), func), from.getTotal());
|
||||||
|
}
|
||||||
|
|
||||||
public static <T, U> List<U> convertListByFlatMap(Collection<T> from,
|
public static <T, U> List<U> convertListByFlatMap(Collection<T> from,
|
||||||
Function<T, ? extends Stream<? extends U>> func) {
|
Function<T, ? extends Stream<? extends U>> func) {
|
||||||
if (CollUtil.isEmpty(from)) {
|
if (CollUtil.isEmpty(from)) {
|
||||||
|
@ -324,7 +332,7 @@ public class CollectionUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> List<T> newArrayList(List<List<T>> list) {
|
public static <T> List<T> newArrayList(List<List<T>> list) {
|
||||||
return list.stream().flatMap(Collection::stream).collect(Collectors.toList());
|
return list.stream().filter(Objects::nonNull).flatMap(Collection::stream).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -23,7 +23,7 @@ public interface ErrorCodeConstants {
|
||||||
"原因:用户任务({})未配置审批人,请点击【流程设计】按钮,选择该它的【任务(审批人)】进行配置");
|
"原因:用户任务({})未配置审批人,请点击【流程设计】按钮,选择该它的【任务(审批人)】进行配置");
|
||||||
ErrorCode MODEL_DEPLOY_FAIL_BPMN_START_EVENT_NOT_EXISTS = new ErrorCode(1_009_002_005, "部署流程失败,原因:BPMN 流程图中,没有开始事件");
|
ErrorCode MODEL_DEPLOY_FAIL_BPMN_START_EVENT_NOT_EXISTS = new ErrorCode(1_009_002_005, "部署流程失败,原因:BPMN 流程图中,没有开始事件");
|
||||||
ErrorCode MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS = new ErrorCode(1_009_002_006, "部署流程失败,原因:BPMN 流程图中,用户任务({})的名字不存在");
|
ErrorCode MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS = new ErrorCode(1_009_002_006, "部署流程失败,原因:BPMN 流程图中,用户任务({})的名字不存在");
|
||||||
ErrorCode MODEL_UPDATE_FAIL_NOT_MANAGER = new ErrorCode(1_009_002_007, "操作流程失败,原因:你不是该流程的管理员");
|
ErrorCode MODEL_UPDATE_FAIL_NOT_MANAGER = new ErrorCode(1_009_002_007, "操作流程失败,原因:你不是该流程({})的管理员");
|
||||||
|
|
||||||
// ========== 流程定义 1-009-003-000 ==========
|
// ========== 流程定义 1-009-003-000 ==========
|
||||||
ErrorCode PROCESS_DEFINITION_KEY_NOT_MATCH = new ErrorCode(1_009_003_000, "流程定义的标识期望是({}),当前是({}),请修改 BPMN 流程图");
|
ErrorCode PROCESS_DEFINITION_KEY_NOT_MATCH = new ErrorCode(1_009_003_000, "流程定义的标识期望是({}),当前是({}),请修改 BPMN 流程图");
|
||||||
|
@ -35,8 +35,8 @@ public interface ErrorCodeConstants {
|
||||||
ErrorCode PROCESS_INSTANCE_NOT_EXISTS = new ErrorCode(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_EXISTS = new ErrorCode(1_009_004_001, "流程取消失败,流程不处于运行中");
|
||||||
ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF = new ErrorCode(1_009_004_002, "流程取消失败,该流程不是你发起的");
|
ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF = new ErrorCode(1_009_004_002, "流程取消失败,该流程不是你发起的");
|
||||||
ErrorCode PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_003, "审批任务({})的审批人未配置");
|
ErrorCode PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_003, "任务({})的候选人未配置");
|
||||||
ErrorCode PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS = new ErrorCode(1_009_004_004, "审批任务({})的审批人({})不存在");
|
ErrorCode PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS = new ErrorCode(1_009_004_004, "任务({})的候选人({})不存在");
|
||||||
ErrorCode PROCESS_INSTANCE_START_USER_CAN_START = new ErrorCode(1_009_004_005, "发起流程失败,你没有权限发起该流程");
|
ErrorCode PROCESS_INSTANCE_START_USER_CAN_START = new ErrorCode(1_009_004_005, "发起流程失败,你没有权限发起该流程");
|
||||||
|
|
||||||
// ========== 流程任务 1-009-005-000 ==========
|
// ========== 流程任务 1-009-005-000 ==========
|
||||||
|
@ -44,7 +44,7 @@ public interface ErrorCodeConstants {
|
||||||
ErrorCode TASK_NOT_EXISTS = new ErrorCode(1_009_005_002, "流程任务不存在");
|
ErrorCode TASK_NOT_EXISTS = new ErrorCode(1_009_005_002, "流程任务不存在");
|
||||||
ErrorCode TASK_IS_PENDING = new ErrorCode(1_009_005_003, "当前任务处于挂起状态,不能操作");
|
ErrorCode TASK_IS_PENDING = new ErrorCode(1_009_005_003, "当前任务处于挂起状态,不能操作");
|
||||||
ErrorCode TASK_TARGET_NODE_NOT_EXISTS = new ErrorCode(1_009_005_004, " 目标节点不存在");
|
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_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_REPEAT = new ErrorCode(1_009_005_007, "任务委派失败,委派人和当前审批人为同一人");
|
||||||
ErrorCode TASK_DELEGATE_FAIL_USER_NOT_EXISTS = new ErrorCode(1_009_005_008, "任务委派失败,被委派人不存在");
|
ErrorCode TASK_DELEGATE_FAIL_USER_NOT_EXISTS = new ErrorCode(1_009_005_008, "任务委派失败,被委派人不存在");
|
||||||
ErrorCode TASK_SIGN_CREATE_USER_NOT_EXIST = new ErrorCode(1_009_005_009, "任务加签:选择的用户不存在");
|
ErrorCode TASK_SIGN_CREATE_USER_NOT_EXIST = new ErrorCode(1_009_005_009, "任务加签:选择的用户不存在");
|
||||||
|
|
|
@ -18,30 +18,26 @@ import java.util.Objects;
|
||||||
public enum BpmSimpleModelNodeType implements IntArrayValuable {
|
public enum BpmSimpleModelNodeType implements IntArrayValuable {
|
||||||
|
|
||||||
// 0 ~ 1 开始和结束
|
// 0 ~ 1 开始和结束
|
||||||
START_NODE(0, "startEvent", "开始节点"),
|
START_NODE(0, "开始", "startEvent"),
|
||||||
END_NODE(1, "endEvent", "结束节点"),
|
END_NODE(1, "结束", "endEvent"),
|
||||||
|
|
||||||
// 10 ~ 49 各种节点
|
// 10 ~ 49 各种节点
|
||||||
START_USER_NODE(10, "userTask", "发起人节点"), // 发起人节点。前端的开始节点,Id 固定
|
START_USER_NODE(10, "发起人", "userTask"), // 发起人节点。前端的开始节点,Id 固定
|
||||||
APPROVE_NODE(11, "userTask", "审批人节点"),
|
APPROVE_NODE(11, "审批人", "userTask"),
|
||||||
COPY_NODE(12, "serviceTask", "抄送人节点"),
|
COPY_NODE(12, "抄送人", "serviceTask"),
|
||||||
|
|
||||||
// 50 ~ 条件分支
|
// 50 ~ 条件分支
|
||||||
CONDITION_NODE(50, "sequenceFlow", "条件节点"), // 用于构建流转条件的表达式
|
CONDITION_NODE(50, "条件", "sequenceFlow"), // 用于构建流转条件的表达式
|
||||||
CONDITION_BRANCH_NODE(51, " “parallelGateway”", "条件分支节点"), // TODO @jason:是不是改成叫 条件分支?
|
CONDITION_BRANCH_NODE(51, "条件分支", "exclusiveGateway"),
|
||||||
PARALLEL_BRANCH_NODE(52, "exclusiveGateway", "并行分支节点"), // TODO @jason:是不是一个 并行分支 ?就可以啦? 后面是否去掉并行网关。只用包容网关
|
PARALLEL_BRANCH_NODE(52, "并行分支", "parallelGateway"),
|
||||||
INCLUSIVE_BRANCH_NODE(53, "inclusiveGateway", "包容分支节点"),
|
INCLUSIVE_BRANCH_NODE(53, "包容分支", "inclusiveGateway"),
|
||||||
// TODO @jason:建议整合 join,最终只有 条件分支、并行分支、包容分支,三种~
|
|
||||||
// TODO @芋艿。 感觉还是分开好理解一点,也好处理一点。前端结构中把聚合节点显示并传过来。
|
|
||||||
;
|
;
|
||||||
|
|
||||||
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmSimpleModelNodeType::getType).toArray();
|
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmSimpleModelNodeType::getType).toArray();
|
||||||
|
|
||||||
public static final String BPMN_USER_TASK_TYPE = "userTask";
|
|
||||||
|
|
||||||
private final Integer type;
|
private final Integer type;
|
||||||
private final String bpmnType;
|
|
||||||
private final String name;
|
private final String name;
|
||||||
|
private final String bpmnType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 判断是否为分支节点
|
* 判断是否为分支节点
|
||||||
|
@ -54,16 +50,6 @@ public enum BpmSimpleModelNodeType implements IntArrayValuable {
|
||||||
|| Objects.equals(INCLUSIVE_BRANCH_NODE.getType(), type);
|
|| Objects.equals(INCLUSIVE_BRANCH_NODE.getType(), type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断是否需要记录的节点
|
|
||||||
*
|
|
||||||
* @param bpmnType bpmn节点类型
|
|
||||||
*/
|
|
||||||
public static boolean isRecordNode(String bpmnType) {
|
|
||||||
return Objects.equals(APPROVE_NODE.getBpmnType(), bpmnType)
|
|
||||||
|| Objects.equals(END_NODE.getBpmnType(), bpmnType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static BpmSimpleModelNodeType valueOf(Integer type) {
|
public static BpmSimpleModelNodeType valueOf(Integer type) {
|
||||||
return ArrayUtil.firstMatch(nodeType -> nodeType.getType().equals(type), values());
|
return ArrayUtil.firstMatch(nodeType -> nodeType.getType().equals(type), values());
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,20 +16,23 @@ import java.util.Arrays;
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public enum BpmUserTaskApproveMethodEnum implements IntArrayValuable {
|
public enum BpmUserTaskApproveMethodEnum implements IntArrayValuable {
|
||||||
|
|
||||||
RANDOM(1, "随机挑选一人审批"),
|
RANDOM(1, "随机挑选一人审批", null),
|
||||||
RATIO(2, "多人会签(按通过比例)"), // 会签(按通过比例)
|
RATIO(2, "多人会签(按通过比例)", "${ nrOfCompletedInstances/nrOfInstances >= %s}"), // 会签(按通过比例)
|
||||||
ANY(3, "多人或签(一人通过或拒绝)"), // 或签(通过只需一人,拒绝只需一人)
|
ANY(3, "多人或签(一人通过或拒绝)", "${ nrOfCompletedInstances > 0 }"), // 或签(通过只需一人,拒绝只需一人)
|
||||||
SEQUENTIAL(4, "依次审批"); // 依次审批
|
SEQUENTIAL(4, "依次审批", "${ nrOfCompletedInstances >= nrOfInstances }"); // 依次审批
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 审批方式
|
* 审批方式
|
||||||
*/
|
*/
|
||||||
private final Integer method;
|
private final Integer method;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 名字
|
* 名字
|
||||||
*/
|
*/
|
||||||
private final String name;
|
private final String name;
|
||||||
|
/**
|
||||||
|
* 完成表达式
|
||||||
|
*/
|
||||||
|
private final String completionCondition;
|
||||||
|
|
||||||
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmUserTaskApproveMethodEnum::getMethod).toArray();
|
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmUserTaskApproveMethodEnum::getMethod).toArray();
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,10 @@ public enum BpmProcessInstanceStatusEnum implements IntArrayValuable {
|
||||||
return ARRAYS;
|
return ARRAYS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isRejectStatus(Integer status) {
|
||||||
|
return REJECT.getStatus().equals(status);
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isProcessEndStatus(Integer status) {
|
public static boolean isProcessEndStatus(Integer status) {
|
||||||
return ObjectUtils.equalsAny(status,
|
return ObjectUtils.equalsAny(status,
|
||||||
APPROVE.getStatus(), REJECT.getStatus(), CANCEL.getStatus());
|
APPROVE.getStatus(), REJECT.getStatus(), CANCEL.getStatus());
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package cn.iocoder.yudao.module.bpm.enums.task;
|
package cn.iocoder.yudao.module.bpm.enums.task;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ObjUtil;
|
||||||
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
|
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
@ -20,7 +21,6 @@ public enum BpmTaskStatusEnum {
|
||||||
CANCEL(4, "已取消"),
|
CANCEL(4, "已取消"),
|
||||||
|
|
||||||
RETURN(5, "已退回"),
|
RETURN(5, "已退回"),
|
||||||
DELEGATE(6, "委派中"),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 使用场景:
|
* 使用场景:
|
||||||
|
@ -45,6 +45,10 @@ public enum BpmTaskStatusEnum {
|
||||||
*/
|
*/
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
|
public static boolean isRejectStatus(Integer status) {
|
||||||
|
return REJECT.getStatus().equals(status);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 判断该状态是否已经处于 End 最终状态
|
* 判断该状态是否已经处于 End 最终状态
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -59,4 +63,8 @@ public enum BpmTaskStatusEnum {
|
||||||
RETURN.getStatus(), APPROVING.getStatus());
|
RETURN.getStatus(), APPROVING.getStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isCancelStatus(Integer status) {
|
||||||
|
return ObjUtil.equal(status, CANCEL.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,11 +9,14 @@ public class UserSimpleBaseVO {
|
||||||
|
|
||||||
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
@Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
|
@Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
|
||||||
private String nickname;
|
private String nickname;
|
||||||
|
|
||||||
@Schema(description = "用户头像", example = "https://www.iocoder.cn/1.png")
|
@Schema(description = "用户头像", example = "https://www.iocoder.cn/1.png")
|
||||||
private String avatar;
|
private String avatar;
|
||||||
|
|
||||||
|
@Schema(description = "部门编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||||
|
private Long deptId;
|
||||||
|
@Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "研发部")
|
||||||
|
private String deptName;
|
||||||
|
|
||||||
}
|
}
|
|
@ -48,6 +48,15 @@ public class BpmCategoryController {
|
||||||
return success(true);
|
return success(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PutMapping("/update-sort-batch")
|
||||||
|
@Operation(summary = "批量更新流程分类的排序")
|
||||||
|
@Parameter(name = "ids", description = "分类编号列表", required = true, example = "1,2,3")
|
||||||
|
@PreAuthorize("@ss.hasPermission('bpm:category:update')")
|
||||||
|
public CommonResult<Boolean> updateCategorySortBatch(@RequestParam("ids") List<Long> ids) {
|
||||||
|
categoryService.updateCategorySortBatch(ids);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
@DeleteMapping("/delete")
|
@DeleteMapping("/delete")
|
||||||
@Operation(summary = "删除流程分类")
|
@Operation(summary = "删除流程分类")
|
||||||
@Parameter(name = "id", description = "编号", required = true)
|
@Parameter(name = "id", description = "编号", required = true)
|
||||||
|
|
|
@ -2,8 +2,6 @@ package cn.iocoder.yudao.module.bpm.controller.admin.definition;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
|
||||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
|
||||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.*;
|
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.*;
|
||||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
|
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
|
||||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelUpdateReqVO;
|
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelUpdateReqVO;
|
||||||
|
@ -28,7 +26,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -56,38 +54,38 @@ public class BpmModelController {
|
||||||
@Resource
|
@Resource
|
||||||
private AdminUserApi adminUserApi;
|
private AdminUserApi adminUserApi;
|
||||||
|
|
||||||
@GetMapping("/page")
|
@GetMapping("/list")
|
||||||
@Operation(summary = "获得模型分页")
|
@Operation(summary = "获得模型分页")
|
||||||
public CommonResult<PageResult<BpmModelRespVO>> getModelPage(BpmModelPageReqVO pageVO) {
|
@Parameter(name = "name", description = "模型名称", example = "芋艿")
|
||||||
PageResult<Model> pageResult = modelService.getModelPage(pageVO);
|
public CommonResult<List<BpmModelRespVO>> getModelPage(@RequestParam(value = "name", required = false) String name) {
|
||||||
if (CollUtil.isEmpty(pageResult.getList())) {
|
List<Model> list = modelService.getModelList(name);
|
||||||
return success(PageResult.empty(pageResult.getTotal()));
|
if (CollUtil.isEmpty(list)) {
|
||||||
|
return success(Collections.emptyList());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 拼接数据
|
|
||||||
// 获得 Form 表单
|
// 获得 Form 表单
|
||||||
Set<Long> formIds = convertSet(pageResult.getList(), model -> {
|
Set<Long> formIds = convertSet(list, model -> {
|
||||||
BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model);
|
BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model);
|
||||||
return metaInfo != null ? metaInfo.getFormId() : null;
|
return metaInfo != null ? metaInfo.getFormId() : null;
|
||||||
});
|
});
|
||||||
Map<Long, BpmFormDO> formMap = formService.getFormMap(formIds);
|
Map<Long, BpmFormDO> formMap = formService.getFormMap(formIds);
|
||||||
// 获得 Category Map
|
// 获得 Category Map
|
||||||
Map<String, BpmCategoryDO> categoryMap = categoryService.getCategoryMap(
|
Map<String, BpmCategoryDO> categoryMap = categoryService.getCategoryMap(
|
||||||
convertSet(pageResult.getList(), Model::getCategory));
|
convertSet(list, Model::getCategory));
|
||||||
// 获得 Deployment Map
|
// 获得 Deployment Map
|
||||||
Set<String> deploymentIds = new HashSet<>();
|
Map<String, Deployment> deploymentMap = processDefinitionService.getDeploymentMap(
|
||||||
pageResult.getList().forEach(model -> CollectionUtils.addIfNotNull(deploymentIds, model.getDeploymentId()));
|
convertSet(list, Model::getDeploymentId));
|
||||||
Map<String, Deployment> deploymentMap = processDefinitionService.getDeploymentMap(deploymentIds);
|
|
||||||
// 获得 ProcessDefinition Map
|
// 获得 ProcessDefinition Map
|
||||||
List<ProcessDefinition> processDefinitions = processDefinitionService.getProcessDefinitionListByDeploymentIds(deploymentIds);
|
List<ProcessDefinition> processDefinitions = processDefinitionService.getProcessDefinitionListByDeploymentIds(
|
||||||
|
deploymentMap.keySet());
|
||||||
Map<String, ProcessDefinition> processDefinitionMap = convertMap(processDefinitions, ProcessDefinition::getDeploymentId);
|
Map<String, ProcessDefinition> processDefinitionMap = convertMap(processDefinitions, ProcessDefinition::getDeploymentId);
|
||||||
// 获得 User Map
|
// 获得 User Map
|
||||||
Set<Long> userIds = convertSetByFlatMap(pageResult.getList(), model -> {
|
Set<Long> userIds = convertSetByFlatMap(list, model -> {
|
||||||
BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model);
|
BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model);
|
||||||
return metaInfo != null ? metaInfo.getStartUserIds().stream() : Stream.empty();
|
return metaInfo != null ? metaInfo.getStartUserIds().stream() : Stream.empty();
|
||||||
});
|
});
|
||||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
|
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
|
||||||
return success(BpmModelConvert.INSTANCE.buildModelPage(pageResult,
|
return success(BpmModelConvert.INSTANCE.buildModelList(list,
|
||||||
formMap, categoryMap, deploymentMap, processDefinitionMap, userMap));
|
formMap, categoryMap, deploymentMap, processDefinitionMap, userMap));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,6 +109,7 @@ public class BpmModelController {
|
||||||
return success(modelService.createModel(createRetVO));
|
return success(modelService.createModel(createRetVO));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@PutMapping("/update")
|
@PutMapping("/update")
|
||||||
@Operation(summary = "修改模型")
|
@Operation(summary = "修改模型")
|
||||||
@PreAuthorize("@ss.hasPermission('bpm:model:update')")
|
@PreAuthorize("@ss.hasPermission('bpm:model:update')")
|
||||||
|
@ -119,6 +118,14 @@ public class BpmModelController {
|
||||||
return success(true);
|
return success(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PutMapping("/update-sort-batch")
|
||||||
|
@Operation(summary = "批量修改模型排序")
|
||||||
|
@Parameter(name = "ids", description = "编号数组", required = true, example = "1,2,3")
|
||||||
|
public CommonResult<Boolean> updateModelSortBatch(@RequestParam("ids") List<String> ids) {
|
||||||
|
modelService.updateModelSortBatch(getLoginUserId(), ids);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping("/deploy")
|
@PostMapping("/deploy")
|
||||||
@Operation(summary = "部署模型")
|
@Operation(summary = "部署模型")
|
||||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||||
|
|
|
@ -9,7 +9,6 @@ import cn.iocoder.yudao.module.bpm.convert.definition.BpmProcessDefinitionConver
|
||||||
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
|
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
|
||||||
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
|
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
|
||||||
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
|
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.BpmTaskCandidateStartUserSelectStrategy;
|
|
||||||
import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService;
|
import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService;
|
||||||
import cn.iocoder.yudao.module.bpm.service.definition.BpmFormService;
|
import cn.iocoder.yudao.module.bpm.service.definition.BpmFormService;
|
||||||
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
|
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
|
||||||
|
@ -18,7 +17,6 @@ import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import org.flowable.bpmn.model.BpmnModel;
|
import org.flowable.bpmn.model.BpmnModel;
|
||||||
import org.flowable.bpmn.model.UserTask;
|
|
||||||
import org.flowable.engine.repository.Deployment;
|
import org.flowable.engine.repository.Deployment;
|
||||||
import org.flowable.engine.repository.ProcessDefinition;
|
import org.flowable.engine.repository.ProcessDefinition;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
@ -115,9 +113,8 @@ public class BpmProcessDefinitionController {
|
||||||
}
|
}
|
||||||
BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo(processDefinition.getId());
|
BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo(processDefinition.getId());
|
||||||
BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(processDefinition.getId());
|
BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(processDefinition.getId());
|
||||||
List<UserTask> userTaskList = BpmTaskCandidateStartUserSelectStrategy.getStartUserSelectUserTaskList(bpmnModel);
|
|
||||||
return success(BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinition(
|
return success(BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinition(
|
||||||
processDefinition, null, processDefinitionInfo, null, null, bpmnModel, userTaskList));
|
processDefinition, null, processDefinitionInfo, null, null, bpmnModel));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,9 @@ import java.util.List;
|
||||||
* BPM 流程 MetaInfo Response DTO
|
* BPM 流程 MetaInfo Response DTO
|
||||||
* 主要用于 { Model#setMetaInfo(String)} 的存储
|
* 主要用于 { Model#setMetaInfo(String)} 的存储
|
||||||
*
|
*
|
||||||
* 最终,它的字段和 {@link cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO} 是一致的
|
* 最终,它的字段和
|
||||||
|
* {@link cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO}
|
||||||
|
* 是一致的
|
||||||
*
|
*
|
||||||
* @author 芋道源码
|
* @author 芋道源码
|
||||||
*/
|
*/
|
||||||
|
@ -40,13 +42,11 @@ public class BpmModelMetaInfoVO {
|
||||||
@NotNull(message = "表单类型不能为空")
|
@NotNull(message = "表单类型不能为空")
|
||||||
private Integer formType;
|
private Integer formType;
|
||||||
@Schema(description = "表单编号", example = "1024")
|
@Schema(description = "表单编号", example = "1024")
|
||||||
private Long formId; // formType 为 NORMAL 使用,必须非空
|
private Long formId; // formType 为 NORMAL 使用,必须非空
|
||||||
@Schema(description = "自定义表单的提交路径,使用 Vue 的路由地址",
|
@Schema(description = "自定义表单的提交路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/create")
|
||||||
example = "/bpm/oa/leave/create")
|
private String formCustomCreatePath; // 表单类型为 CUSTOM 时,必须非空
|
||||||
private String formCustomCreatePath; // 表单类型为 CUSTOM 时,必须非空
|
@Schema(description = "自定义表单的查看路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/view")
|
||||||
@Schema(description = "自定义表单的查看路径,使用 Vue 的路由地址",
|
private String formCustomViewPath; // 表单类型为 CUSTOM 时,必须非空
|
||||||
example = "/bpm/oa/leave/view")
|
|
||||||
private String formCustomViewPath; // 表单类型为 CUSTOM 时,必须非空
|
|
||||||
|
|
||||||
@Schema(description = "是否可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
@Schema(description = "是否可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||||
@NotNull(message = "是否可见不能为空")
|
@NotNull(message = "是否可见不能为空")
|
||||||
|
@ -59,4 +59,7 @@ public class BpmModelMetaInfoVO {
|
||||||
@NotEmpty(message = "可管理用户编号数组不能为空")
|
@NotEmpty(message = "可管理用户编号数组不能为空")
|
||||||
private List<Long> managerUserIds;
|
private List<Long> managerUserIds;
|
||||||
|
|
||||||
|
@Schema(description = "排序", example = "1")
|
||||||
|
private Long sort; // 创建时,后端自动生成
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model;
|
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
|
|
||||||
@Schema(description = "管理后台 - 流程模型分页 Request VO")
|
|
||||||
@Data
|
|
||||||
public class BpmModelPageReqVO extends PageParam {
|
|
||||||
|
|
||||||
@Schema(description = "标识,精准匹配", example = "process1641042089407")
|
|
||||||
private String key;
|
|
||||||
|
|
||||||
@Schema(description = "名字,模糊匹配", example = "芋道")
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
@Schema(description = "流程分类", example = "1")
|
|
||||||
private String category;
|
|
||||||
|
|
||||||
}
|
|
|
@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple;
|
||||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||||
import cn.iocoder.yudao.module.bpm.enums.definition.*;
|
import cn.iocoder.yudao.module.bpm.enums.definition.*;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
|
@ -31,7 +30,6 @@ public class BpmSimpleModelNodeVO {
|
||||||
@Schema(description = "模型节点名称", example = "领导审批")
|
@Schema(description = "模型节点名称", example = "领导审批")
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
// TODO @jason:和 gpt 大模型对了下这个字段的命名,貌似叫 displayText 合适点。可以等最后我们全局替换下。(优先级:低)
|
|
||||||
@Schema(description = "节点展示内容", example = "指定成员: 芋道源码")
|
@Schema(description = "节点展示内容", example = "指定成员: 芋道源码")
|
||||||
private String showText;
|
private String showText;
|
||||||
|
|
||||||
|
@ -79,12 +77,6 @@ public class BpmSimpleModelNodeVO {
|
||||||
@Schema(description = "操作按钮设置", example = "[]")
|
@Schema(description = "操作按钮设置", example = "[]")
|
||||||
private List<OperationButtonSetting> buttonsSetting; // 用于审批节点
|
private List<OperationButtonSetting> buttonsSetting; // 用于审批节点
|
||||||
|
|
||||||
// TODO @jason:看看是不是可以简化;@芋艿: 暂时先放着。不知道后面是否会用到
|
|
||||||
/**
|
|
||||||
* 附加节点 Id, 该节点不从前端传入。 由程序生成. 由于当个节点无法完成功能。 需要附加节点来完成。
|
|
||||||
*/
|
|
||||||
@JsonIgnore
|
|
||||||
private String attachNodeId;
|
|
||||||
/**
|
/**
|
||||||
* 审批节点拒绝处理
|
* 审批节点拒绝处理
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -33,6 +33,9 @@ public class BpmProcessDefinitionRespVO {
|
||||||
@Schema(description = "流程分类名字", example = "请假")
|
@Schema(description = "流程分类名字", example = "请假")
|
||||||
private String categoryName;
|
private String categoryName;
|
||||||
|
|
||||||
|
@Schema(description = "流程模型的类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||||
|
private Integer modelType; // 参见 BpmModelTypeEnum 枚举类
|
||||||
|
|
||||||
@Schema(description = "表单类型-参见 bpm_model_form_type 数据字典", example = "1")
|
@Schema(description = "表单类型-参见 bpm_model_form_type 数据字典", example = "1")
|
||||||
private Integer formType;
|
private Integer formType;
|
||||||
@Schema(description = "表单编号-在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", example = "1024")
|
@Schema(description = "表单编号-在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", example = "1024")
|
||||||
|
@ -59,9 +62,12 @@ public class BpmProcessDefinitionRespVO {
|
||||||
@Schema(description = "BPMN XML")
|
@Schema(description = "BPMN XML")
|
||||||
private String bpmnXml; // 需要从对应的 BpmnModel 读取,非必须返回
|
private String bpmnXml; // 需要从对应的 BpmnModel 读取,非必须返回
|
||||||
|
|
||||||
@Schema(description = "发起用户需要选择审批人的任务数组")
|
@Schema(description = "SIMPLE 设计器模型数据 json 格式")
|
||||||
private List<UserTask> startUserSelectTasks; // 需要从对应的 BpmnModel 读取,非必须返回
|
private String simpleModel; // 非必须返回
|
||||||
|
|
||||||
|
@Schema(description = "流程定义排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||||
|
private Long sort;
|
||||||
|
|
||||||
@Schema(description = "BPMN UserTask 用户任务")
|
@Schema(description = "BPMN UserTask 用户任务")
|
||||||
@Data
|
@Data
|
||||||
public static class UserTask {
|
public static class UserTask {
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
package cn.iocoder.yudao.module.bpm.controller.admin.task;
|
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
|
||||||
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.activity.BpmActivityRespVO;
|
|
||||||
import cn.iocoder.yudao.module.bpm.convert.task.BpmActivityConvert;
|
|
||||||
import cn.iocoder.yudao.module.bpm.service.task.BpmActivityService;
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
||||||
import jakarta.annotation.Resource;
|
|
||||||
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 java.util.List;
|
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
|
||||||
|
|
||||||
@Tag(name = "管理后台 - 流程活动实例")
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/bpm/activity")
|
|
||||||
@Validated
|
|
||||||
public class BpmActivityController {
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private BpmActivityService activityService;
|
|
||||||
|
|
||||||
@GetMapping("/list")
|
|
||||||
@Operation(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(BpmActivityConvert.INSTANCE.convertList(activityService.getActivityListByProcessInstanceId(processInstanceId)));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
### 请求 /bpm/process-instance/get-bpmn 接口 => 成功
|
||||||
|
GET {{baseUrl}}/bpm/process-instance/get-bpmn-model-view?id=1d5fb5a6-85f8-11ef-b717-7e93075f94e3
|
||||||
|
Content-Type: application/json
|
||||||
|
tenant-id: 1
|
||||||
|
Authorization: Bearer {{token}}
|
||||||
|
|
||||||
|
### 请求 /bpm/process-instance/get-bpmn 接口 => 失败
|
||||||
|
#GET {{baseUrl}}/bpm/process-instance/get-approval-detail?processInstanceId=1d5fb5a6-85f8-11ef-b717-7e93075f94e3
|
||||||
|
#GET {{baseUrl}}/bpm/process-instance/get-approval-detail?processInstanceId=3ee5c5ba-904a-11ef-a76e-b2ed5d6ef911
|
||||||
|
#GET {{baseUrl}}/bpm/process-instance/get-approval-detail?processInstanceId=f630dfa2-8f92-11ef-947c-ba5e239a6eb4
|
||||||
|
#GET {{baseUrl}}/bpm/process-instance/get-approval-detail?processInstanceId=9de8bdbf-9133-11ef-ae97-eaf49df1f932
|
||||||
|
#GET {{baseUrl}}/bpm/process-instance/get-approval-detail?processInstanceId=dd2188eb-9394-11ef-a039-7a9ac3d9eb6b
|
||||||
|
GET {{baseUrl}}/bpm/process-instance/get-approval-detail?processDefinitionId=test-auto:1:c70a799a-9394-11ef-a039-7a9ac3d9eb6b
|
||||||
|
Content-Type: application/json
|
||||||
|
tenant-id: 1
|
||||||
|
Authorization: Bearer {{token}}
|
|
@ -8,7 +8,6 @@ import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*;
|
||||||
import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert;
|
import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert;
|
||||||
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
|
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
|
||||||
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
|
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
|
|
||||||
import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService;
|
import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService;
|
||||||
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
|
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
|
||||||
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
|
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
|
||||||
|
@ -128,15 +127,13 @@ public class BpmProcessInstanceController {
|
||||||
processInstance.getProcessDefinitionId());
|
processInstance.getProcessDefinitionId());
|
||||||
BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo(
|
BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo(
|
||||||
processInstance.getProcessDefinitionId());
|
processInstance.getProcessDefinitionId());
|
||||||
String bpmnXml = BpmnModelUtils.getBpmnXml(
|
|
||||||
processDefinitionService.getProcessDefinitionBpmnModel(processInstance.getProcessDefinitionId()));
|
|
||||||
AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId())).getCheckedData();
|
AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId())).getCheckedData();
|
||||||
DeptRespDTO dept = null;
|
DeptRespDTO dept = null;
|
||||||
if (startUser != null && startUser.getDeptId() != null) {
|
if (startUser != null && startUser.getDeptId() != null) {
|
||||||
dept = deptApi.getDept(startUser.getDeptId()).getCheckedData();
|
dept = deptApi.getDept(startUser.getDeptId()).getCheckedData();
|
||||||
}
|
}
|
||||||
return success(BpmProcessInstanceConvert.INSTANCE.buildProcessInstance(processInstance,
|
return success(BpmProcessInstanceConvert.INSTANCE.buildProcessInstance(processInstance,
|
||||||
processDefinition, processDefinitionInfo, bpmnXml, startUser, dept));
|
processDefinition, processDefinitionInfo, startUser, dept));
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/cancel-by-start-user")
|
@DeleteMapping("/cancel-by-start-user")
|
||||||
|
@ -157,14 +154,6 @@ public class BpmProcessInstanceController {
|
||||||
return success(true);
|
return success(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/get-form-fields-permission")
|
|
||||||
@Operation(summary = "获得表单字段权限")
|
|
||||||
@PreAuthorize("@ss.hasPermission('bpm:process-instance:query')")
|
|
||||||
public CommonResult<Map<String, String>> getFormFieldsPermission(
|
|
||||||
@Valid BpmFormFieldsPermissionReqVO reqVO) {
|
|
||||||
return success(processInstanceService.getFormFieldsPermission(reqVO));
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/get-approval-detail")
|
@GetMapping("/get-approval-detail")
|
||||||
@Operation(summary = "获得审批详情")
|
@Operation(summary = "获得审批详情")
|
||||||
@Parameter(name = "id", description = "流程实例的编号", required = true)
|
@Parameter(name = "id", description = "流程实例的编号", required = true)
|
||||||
|
@ -173,4 +162,11 @@ public class BpmProcessInstanceController {
|
||||||
return success(processInstanceService.getApprovalDetail(getLoginUserId(), reqVO));
|
return success(processInstanceService.getApprovalDetail(getLoginUserId(), reqVO));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/get-bpmn-model-view")
|
||||||
|
@Operation(summary = "获取流程实例的 BPMN 模型视图", description = "在【流程详细】界面中,进行调用")
|
||||||
|
@Parameter(name = "id", description = "流程实例的编号", required = true)
|
||||||
|
public CommonResult<BpmProcessInstanceBpmnModelViewRespVO> getProcessInstanceBpmnModelView(@RequestParam(value = "id") String id) {
|
||||||
|
return success(processInstanceService.getProcessInstanceBpmnModelView(id));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||||
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
|
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
|
||||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||||
|
import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;
|
||||||
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.cc.BpmProcessInstanceCopyRespVO;
|
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.cc.BpmProcessInstanceCopyRespVO;
|
||||||
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO;
|
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO;
|
||||||
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO;
|
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO;
|
||||||
|
@ -28,8 +29,7 @@ import java.util.Map;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertListByFlatMap;
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
|
||||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
|
||||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||||
|
|
||||||
@Tag(name = "管理后台 - 流程实例抄送")
|
@Tag(name = "管理后台 - 流程实例抄送")
|
||||||
|
@ -62,11 +62,15 @@ public class BpmProcessInstanceCopyController {
|
||||||
convertSet(pageResult.getList(), BpmProcessInstanceCopyDO::getProcessInstanceId));
|
convertSet(pageResult.getList(), BpmProcessInstanceCopyDO::getProcessInstanceId));
|
||||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(pageResult.getList(),
|
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(pageResult.getList(),
|
||||||
copy -> Stream.of(copy.getStartUserId(), Long.parseLong(copy.getCreator()))));
|
copy -> Stream.of(copy.getStartUserId(), Long.parseLong(copy.getCreator()))));
|
||||||
return success(BeanUtils.toBean(pageResult, BpmProcessInstanceCopyRespVO.class, copyVO -> {
|
return success(convertPage(pageResult, copy -> {
|
||||||
MapUtils.findAndThen(userMap, Long.valueOf(copyVO.getCreator()), user -> copyVO.setCreatorName(user.getNickname()));
|
BpmProcessInstanceCopyRespVO copyVO = BeanUtils.toBean(copy, BpmProcessInstanceCopyRespVO.class);
|
||||||
MapUtils.findAndThen(userMap, copyVO.getStartUserId(), user -> copyVO.setStartUserName(user.getNickname()));
|
MapUtils.findAndThen(userMap, Long.valueOf(copy.getCreator()),
|
||||||
|
user -> copyVO.setStartUser(BeanUtils.toBean(user, UserSimpleBaseVO.class)));
|
||||||
|
MapUtils.findAndThen(userMap, copy.getStartUserId(),
|
||||||
|
user -> copyVO.setCreateUser(BeanUtils.toBean(user, UserSimpleBaseVO.class)));
|
||||||
MapUtils.findAndThen(processInstanceMap, copyVO.getProcessInstanceId(),
|
MapUtils.findAndThen(processInstanceMap, copyVO.getProcessInstanceId(),
|
||||||
processInstance -> copyVO.setProcessInstanceStartTime(DateUtils.of(processInstance.getStartTime())));
|
processInstance -> copyVO.setProcessInstanceStartTime(DateUtils.of(processInstance.getStartTime())));
|
||||||
|
return copyVO;
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@ import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
|
||||||
import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert;
|
import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert;
|
||||||
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
|
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
|
||||||
import cn.iocoder.yudao.module.bpm.service.definition.BpmFormService;
|
import cn.iocoder.yudao.module.bpm.service.definition.BpmFormService;
|
||||||
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
|
|
||||||
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
|
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
|
||||||
import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;
|
import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;
|
||||||
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||||
|
@ -20,7 +19,6 @@ import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import org.flowable.bpmn.model.BpmnModel;
|
|
||||||
import org.flowable.bpmn.model.UserTask;
|
import org.flowable.bpmn.model.UserTask;
|
||||||
import org.flowable.engine.history.HistoricProcessInstance;
|
import org.flowable.engine.history.HistoricProcessInstance;
|
||||||
import org.flowable.engine.runtime.ProcessInstance;
|
import org.flowable.engine.runtime.ProcessInstance;
|
||||||
|
@ -52,8 +50,6 @@ public class BpmTaskController {
|
||||||
private BpmProcessInstanceService processInstanceService;
|
private BpmProcessInstanceService processInstanceService;
|
||||||
@Resource
|
@Resource
|
||||||
private BpmFormService formService;
|
private BpmFormService formService;
|
||||||
@Resource
|
|
||||||
private BpmProcessDefinitionService bpmProcessDefinitionService;
|
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private AdminUserApi adminUserApi;
|
private AdminUserApi adminUserApi;
|
||||||
|
@ -121,27 +117,22 @@ public class BpmTaskController {
|
||||||
@PreAuthorize("@ss.hasPermission('bpm:task:query')")
|
@PreAuthorize("@ss.hasPermission('bpm:task:query')")
|
||||||
public CommonResult<List<BpmTaskRespVO>> getTaskListByProcessInstanceId(
|
public CommonResult<List<BpmTaskRespVO>> getTaskListByProcessInstanceId(
|
||||||
@RequestParam("processInstanceId") String processInstanceId) {
|
@RequestParam("processInstanceId") String processInstanceId) {
|
||||||
List<HistoricTaskInstance> taskList = taskService.getTaskListByProcessInstanceId(processInstanceId);
|
List<HistoricTaskInstance> taskList = taskService.getTaskListByProcessInstanceId(processInstanceId, true);
|
||||||
if (CollUtil.isEmpty(taskList)) {
|
if (CollUtil.isEmpty(taskList)) {
|
||||||
return success(Collections.emptyList());
|
return success(Collections.emptyList());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 拼接数据
|
// 拼接数据
|
||||||
HistoricProcessInstance processInstance = processInstanceService.getHistoricProcessInstance(processInstanceId);
|
|
||||||
// 获得 User 和 Dept Map
|
|
||||||
Set<Long> userIds = convertSetByFlatMap(taskList, task ->
|
Set<Long> userIds = convertSetByFlatMap(taskList, task ->
|
||||||
Stream.of(NumberUtils.parseLong(task.getAssignee()), NumberUtils.parseLong(task.getOwner())));
|
Stream.of(NumberUtils.parseLong(task.getAssignee()), NumberUtils.parseLong(task.getOwner())));
|
||||||
userIds.add(NumberUtils.parseLong(processInstance.getStartUserId()));
|
|
||||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
|
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
|
||||||
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(
|
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(
|
||||||
convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
|
convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
|
||||||
// 获得 Form Map
|
// 获得 Form Map
|
||||||
Map<Long, BpmFormDO> formMap = formService.getFormMap(
|
Map<Long, BpmFormDO> formMap = formService.getFormMap(
|
||||||
convertSet(taskList, task -> NumberUtils.parseLong(task.getFormKey())));
|
convertSet(taskList, task -> NumberUtils.parseLong(task.getFormKey())));
|
||||||
// 获得 BpmnModel
|
return success(BpmTaskConvert.INSTANCE.buildTaskListByProcessInstanceId(taskList,
|
||||||
BpmnModel bpmnModel = bpmProcessDefinitionService.getProcessDefinitionBpmnModel(processInstance.getProcessDefinitionId());
|
formMap, userMap, deptMap));
|
||||||
return success(BpmTaskConvert.INSTANCE.buildTaskListByProcessInstanceId(taskList, processInstance,
|
|
||||||
formMap, userMap, deptMap, bpmnModel));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/approve")
|
@PutMapping("/approve")
|
||||||
|
@ -161,7 +152,7 @@ public class BpmTaskController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/list-by-return")
|
@GetMapping("/list-by-return")
|
||||||
@Operation(summary = "获取所有可回退的节点", description = "用于【流程详情】的【回退】按钮")
|
@Operation(summary = "获取所有可退回的节点", description = "用于【流程详情】的【退回】按钮")
|
||||||
@Parameter(name = "taskId", description = "当前任务ID", required = true)
|
@Parameter(name = "taskId", description = "当前任务ID", required = true)
|
||||||
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
|
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
|
||||||
public CommonResult<List<BpmTaskRespVO>> getTaskListByReturn(@RequestParam("id") String id) {
|
public CommonResult<List<BpmTaskRespVO>> getTaskListByReturn(@RequestParam("id") String id) {
|
||||||
|
@ -171,7 +162,7 @@ public class BpmTaskController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/return")
|
@PutMapping("/return")
|
||||||
@Operation(summary = "回退任务", description = "用于【流程详情】的【回退】按钮")
|
@Operation(summary = "退回任务", description = "用于【流程详情】的【退回】按钮")
|
||||||
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
|
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
|
||||||
public CommonResult<Boolean> returnTask(@Valid @RequestBody BpmTaskReturnReqVO reqVO) {
|
public CommonResult<Boolean> returnTask(@Valid @RequestBody BpmTaskReturnReqVO reqVO) {
|
||||||
taskService.returnTask(getLoginUserId(), reqVO);
|
taskService.returnTask(getLoginUserId(), reqVO);
|
||||||
|
@ -210,6 +201,14 @@ public class BpmTaskController {
|
||||||
return success(true);
|
return success(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PutMapping("/copy")
|
||||||
|
@Operation(summary = "抄送任务")
|
||||||
|
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
|
||||||
|
public CommonResult<Boolean> copyTask(@Valid @RequestBody BpmTaskCopyReqVO reqVO) {
|
||||||
|
taskService.copyTask(getLoginUserId(), reqVO);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/list-by-parent-task-id")
|
@GetMapping("/list-by-parent-task-id")
|
||||||
@Operation(summary = "获得指定父级任务的子任务列表") // 目前用于,减签的时候,获得子任务列表
|
@Operation(summary = "获得指定父级任务的子任务列表") // 目前用于,减签的时候,获得子任务列表
|
||||||
@Parameter(name = "parentTaskId", description = "父级任务编号", required = true)
|
@Parameter(name = "parentTaskId", description = "父级任务编号", required = true)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.cc;
|
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.cc;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
@ -12,31 +13,31 @@ public class BpmProcessInstanceCopyRespVO {
|
||||||
@Schema(description = "抄送主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
@Schema(description = "抄送主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
@Schema(description = "发起人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "888")
|
@Schema(description = "发起人", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private Long startUserId;
|
private UserSimpleBaseVO startUser;
|
||||||
@Schema(description = "发起人昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
|
|
||||||
private String startUserName;
|
|
||||||
|
|
||||||
@Schema(description = "流程实例编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "A233")
|
@Schema(description = "流程实例编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "A233")
|
||||||
private String processInstanceId;
|
private String processInstanceId;
|
||||||
@Schema(description = "流程实例的名称")
|
@Schema(description = "流程实例的名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "测试")
|
||||||
private String processInstanceName;
|
private String processInstanceName;
|
||||||
@Schema(description = "流程实例的发起时间")
|
@Schema(description = "流程实例的发起时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private LocalDateTime processInstanceStartTime;
|
private LocalDateTime processInstanceStartTime;
|
||||||
|
|
||||||
@Schema(description = "抄送的节点的活动编号")
|
@Schema(description = "流程活动的编号", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private String activityId;
|
private String activityId;
|
||||||
@Schema(description = "发起抄送的任务编号")
|
@Schema(description = "流程活动的名字", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
private String activityName;
|
||||||
|
|
||||||
|
@Schema(description = "流程活动的编号")
|
||||||
private String taskId;
|
private String taskId;
|
||||||
@Schema(description = "发起抄送的任务名称")
|
|
||||||
private String taskName;
|
|
||||||
|
|
||||||
@Schema(description = "抄送人")
|
@Schema(description = "抄送人意见")
|
||||||
private String creator;
|
private String reason;
|
||||||
@Schema(description = "抄送人昵称")
|
|
||||||
private String creatorName;
|
|
||||||
|
|
||||||
@Schema(description = "抄送时间")
|
@Schema(description = "创建人", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
private UserSimpleBaseVO createUser;
|
||||||
|
|
||||||
|
@Schema(description = "抄送时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private LocalDateTime createTime;
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,16 +6,27 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import jakarta.validation.constraints.AssertTrue;
|
import jakarta.validation.constraints.AssertTrue;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
// TODO @jason:这个可以简化下,使用 @RequestParam。嘿嘿,主要 VO 项不要太多
|
import java.util.Map;
|
||||||
|
|
||||||
@Schema(description = "管理后台 - 审批详情 Request VO")
|
@Schema(description = "管理后台 - 审批详情 Request VO")
|
||||||
@Data
|
@Data
|
||||||
public class BpmApprovalDetailReqVO {
|
public class BpmApprovalDetailReqVO {
|
||||||
|
|
||||||
@Schema(description = "流程定义的编号", example = "1024")
|
@Schema(description = "流程定义的编号", example = "1024")
|
||||||
private String processDefinitionId;
|
private String processDefinitionId; // 使用场景:发起流程时,传流程定义 ID
|
||||||
|
|
||||||
|
@Schema(description = "流程变量")
|
||||||
|
private Map<String, Object> processVariables; // 使用场景:同 processDefinitionId,用于流程预测
|
||||||
|
|
||||||
@Schema(description = "流程实例的编号", example = "1024")
|
@Schema(description = "流程实例的编号", example = "1024")
|
||||||
private String processInstanceId;
|
private String processInstanceId; // 使用场景:流程已发起时候传流程实例 ID
|
||||||
|
|
||||||
|
// TODO @芋艿:如果未来 BPMN 增加流程图,它没有发起人节点,会有问题。
|
||||||
|
@Schema(description = "流程活动编号", example = "StartUserNode")
|
||||||
|
private String activityId; // 用于获取表单权限。1)发起流程时,传“发起人节点” activityId 可获取发起人的表单权限;2)从抄送列表界面进来时,传抄送的 activityId 可获取抄送人的表单权限;
|
||||||
|
|
||||||
|
@Schema(description = "流程任务编号", example = "95f2f08b-621b-11ef-bf39-00ff4722db8b")
|
||||||
|
private String taskId; // 用于获取表单权限。1)从待审批/已审批界面进来时,传递 taskId 任务编号,可获取任务节点的变得权限
|
||||||
|
|
||||||
@AssertTrue(message = "流程定义的编号和流程实例的编号不能同时为空")
|
@AssertTrue(message = "流程定义的编号和流程实例的编号不能同时为空")
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance;
|
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;
|
||||||
|
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO;
|
||||||
|
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
@Schema(description = "管理后台 - 审批详情 Response VO")
|
@Schema(description = "管理后台 - 审批详情 Response VO")
|
||||||
|
@ -14,12 +19,28 @@ public class BpmApprovalDetailRespVO {
|
||||||
@Schema(description = "流程实例的状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
@Schema(description = "流程实例的状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||||
private Integer status; // 参见 BpmProcessInstanceStatusEnum 枚举
|
private Integer status; // 参见 BpmProcessInstanceStatusEnum 枚举
|
||||||
|
|
||||||
@Schema(description = "审批信息列表", requiredMode = Schema.RequiredMode.REQUIRED)
|
@Schema(description = "活动节点列表", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private List<ApprovalNodeInfo> approveNodes;
|
private List<ActivityNode> activityNodes;
|
||||||
|
|
||||||
@Schema(description = "审批节点信息")
|
@Schema(description = "表单字段权限")
|
||||||
|
private Map<String, String> formFieldsPermission;
|
||||||
|
|
||||||
|
@Schema(description = "待办任务")
|
||||||
|
private BpmTaskRespVO todoTask;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 所属流程定义信息
|
||||||
|
*/
|
||||||
|
private BpmProcessDefinitionRespVO processDefinition;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 所属流程实例信息
|
||||||
|
*/
|
||||||
|
private BpmProcessInstanceRespVO processInstance;
|
||||||
|
|
||||||
|
@Schema(description = "活动节点信息")
|
||||||
@Data
|
@Data
|
||||||
public static class ApprovalNodeInfo {
|
public static class ActivityNode {
|
||||||
|
|
||||||
@Schema(description = "节点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "StartUserNode")
|
@Schema(description = "节点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "StartUserNode")
|
||||||
private String id;
|
private String id;
|
||||||
|
@ -39,42 +60,40 @@ public class BpmApprovalDetailRespVO {
|
||||||
private LocalDateTime endTime;
|
private LocalDateTime endTime;
|
||||||
|
|
||||||
@Schema(description = "审批节点的任务信息")
|
@Schema(description = "审批节点的任务信息")
|
||||||
private List<ApprovalTaskInfo> tasks;
|
private List<ActivityNodeTask> tasks;
|
||||||
|
|
||||||
|
@Schema(description = "候选人策略", example = "35")
|
||||||
|
private Integer candidateStrategy; // 参见 BpmTaskCandidateStrategyEnum 枚举。主要用于发起时,审批节点、抄送节点自选
|
||||||
|
|
||||||
|
@Schema(description = "候选人用户 ID 列表", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1818")
|
||||||
|
@JsonIgnore // 不返回,只是方便后续读取,赋值给 candidateUsers
|
||||||
|
private List<Long> candidateUserIds;
|
||||||
|
|
||||||
@Schema(description = "候选人用户列表")
|
@Schema(description = "候选人用户列表")
|
||||||
// TODO @jason:candidateUserList => candidateUsers,保持和 tasks 的命名风格一致哈
|
private List<UserSimpleBaseVO> candidateUsers; // 只包含未生成 ApprovalTaskInfo 的用户列表
|
||||||
private List<User> candidateUserList; // 用于未运行任务节点
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO @jason:可以替换成 UserSimpleBaseVO。简化下
|
@Schema(description = "活动节点的任务信息")
|
||||||
@Schema(description = "用户信息")
|
|
||||||
@Data
|
@Data
|
||||||
public static class User {
|
public static class ActivityNodeTask {
|
||||||
|
|
||||||
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
@Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
|
|
||||||
private String nickname;
|
|
||||||
|
|
||||||
@Schema(description = "用户头像", example = "https://www.iocoder.cn/1.png")
|
|
||||||
private String avatar;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Schema(description = "审批任务信息")
|
|
||||||
@Data
|
|
||||||
public static class ApprovalTaskInfo {
|
|
||||||
|
|
||||||
@Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
@Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
|
@Schema(description = "任务所属人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1818")
|
||||||
|
@JsonIgnore // 不返回,只是方便后续读取,赋值给 ownerUser
|
||||||
|
private Long owner;
|
||||||
|
|
||||||
@Schema(description = "任务所属人", example = "1024")
|
@Schema(description = "任务所属人", example = "1024")
|
||||||
private User ownerUser;
|
private UserSimpleBaseVO ownerUser;
|
||||||
|
|
||||||
|
@Schema(description = "任务分配人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048")
|
||||||
|
@JsonIgnore // 不返回,只是方便后续读取,赋值给 assigneeUser
|
||||||
|
private Long assignee;
|
||||||
|
|
||||||
@Schema(description = "任务分配人", example = "2048")
|
@Schema(description = "任务分配人", example = "2048")
|
||||||
private User assigneeUser;
|
private UserSimpleBaseVO assigneeUser;
|
||||||
|
|
||||||
@Schema(description = "任务状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
@Schema(description = "任务状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||||
private Integer status; // 参见 BpmTaskStatusEnum 枚举
|
private Integer status; // 参见 BpmTaskStatusEnum 枚举
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance;
|
|
||||||
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
|
||||||
import jakarta.validation.constraints.AssertTrue;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
@Schema(description = "管理后台 - 表单字段权限 Request VO")
|
|
||||||
@Data
|
|
||||||
public class BpmFormFieldsPermissionReqVO {
|
|
||||||
|
|
||||||
@Schema(description = "流程定义的编号", example = "1024")
|
|
||||||
private String processDefinitionId;
|
|
||||||
|
|
||||||
@Schema(description = "流程实例的编号", example = "1024")
|
|
||||||
private String processInstanceId;
|
|
||||||
|
|
||||||
@Schema(description = "流程活动编号", example = "StartUserNode")
|
|
||||||
private String activityId; // 对应 BPMN XML 节点 Id
|
|
||||||
|
|
||||||
@Schema(description = "流程任务编号", example = "95f2f08b-621b-11ef-bf39-00ff4722db8b")
|
|
||||||
private String taskId; // UserTask 对应的Id
|
|
||||||
|
|
||||||
@AssertTrue(message = "流程定义的编号和流程实例的编号不能同时为空")
|
|
||||||
@JsonIgnore
|
|
||||||
public boolean isValidProcessParam() {
|
|
||||||
return StrUtil.isNotEmpty(processDefinitionId) || StrUtil.isNotEmpty(processInstanceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@AssertTrue(message = "流程活动编号和流程任务编号编号不能同时为空")
|
|
||||||
@JsonIgnore
|
|
||||||
public boolean isValidActivityParam() {
|
|
||||||
return StrUtil.isNotEmpty(activityId) || StrUtil.isNotEmpty(taskId);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
|
||||||
|
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - 流程示例的 BPMN 视图 Response VO")
|
||||||
|
@Data
|
||||||
|
public class BpmProcessInstanceBpmnModelViewRespVO {
|
||||||
|
|
||||||
|
// ========== 基本信息 ==========
|
||||||
|
|
||||||
|
@Schema(description = "流程实例信息", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
private BpmProcessInstanceRespVO processInstance;
|
||||||
|
|
||||||
|
@Schema(description = "任务列表", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
private List<BpmTaskRespVO> tasks;
|
||||||
|
|
||||||
|
@Schema(description = "BPMN XML", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
private String bpmnXml;
|
||||||
|
|
||||||
|
@Schema(description = "SIMPLE 模型")
|
||||||
|
private BpmSimpleModelNodeVO simpleModel;
|
||||||
|
|
||||||
|
// ========== 进度信息 ==========
|
||||||
|
|
||||||
|
@Schema(description = "进行中的活动节点编号集合", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
private Set<String> unfinishedTaskActivityIds; // 只包括 UserTask
|
||||||
|
|
||||||
|
@Schema(description = "已经完成的活动节点编号集合", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
private Set<String> finishedTaskActivityIds; // 包括 UserTask、Gateway 等,不包括 SequenceFlow
|
||||||
|
|
||||||
|
@Schema(description = "已经完成的连线节点编号集合", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
private Set<String> finishedSequenceFlowActivityIds; // 只包括 SequenceFlow
|
||||||
|
|
||||||
|
@Schema(description = "已经拒绝的活动节点编号集合", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
private Set<String> rejectedTaskActivityIds; // 只包括 UserTask
|
||||||
|
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance;
|
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;
|
||||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO;
|
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
@ -44,7 +45,7 @@ public class BpmProcessInstanceRespVO {
|
||||||
/**
|
/**
|
||||||
* 发起流程的用户
|
* 发起流程的用户
|
||||||
*/
|
*/
|
||||||
private User startUser;
|
private UserSimpleBaseVO startUser;
|
||||||
|
|
||||||
@Schema(description = "流程定义的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
|
@Schema(description = "流程定义的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
|
||||||
private String processDefinitionId;
|
private String processDefinitionId;
|
||||||
|
@ -58,22 +59,6 @@ public class BpmProcessInstanceRespVO {
|
||||||
*/
|
*/
|
||||||
private List<Task> tasks; // 仅在流程实例分页才返回
|
private List<Task> tasks; // 仅在流程实例分页才返回
|
||||||
|
|
||||||
@Schema(description = "用户信息")
|
|
||||||
@Data
|
|
||||||
public static class User {
|
|
||||||
|
|
||||||
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
|
||||||
private Long id;
|
|
||||||
@Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
|
|
||||||
private String nickname;
|
|
||||||
|
|
||||||
@Schema(description = "部门编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
|
||||||
private Long deptId;
|
|
||||||
@Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "研发部")
|
|
||||||
private String deptName;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Schema(description = "流程任务")
|
@Schema(description = "流程任务")
|
||||||
@Data
|
@Data
|
||||||
public static class Task {
|
public static class Task {
|
||||||
|
|
|
@ -4,7 +4,6 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import jakarta.validation.constraints.NotEmpty;
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@Schema(description = "管理后台 - 通过流程任务的 Request VO")
|
@Schema(description = "管理后台 - 通过流程任务的 Request VO")
|
||||||
|
@ -19,9 +18,6 @@ public class BpmTaskApproveReqVO {
|
||||||
@NotEmpty(message = "审批意见不能为空")
|
@NotEmpty(message = "审批意见不能为空")
|
||||||
private String reason;
|
private String reason;
|
||||||
|
|
||||||
@Schema(description = "抄送的用户编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2")
|
|
||||||
private Collection<Long> copyUserIds;
|
|
||||||
|
|
||||||
@Schema(description = "变量实例(动态表单)", requiredMode = Schema.RequiredMode.REQUIRED)
|
@Schema(description = "变量实例(动态表单)", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private Map<String, Object> variables;
|
private Map<String, Object> variables;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - 抄送流程任务的 Request VO")
|
||||||
|
@Data
|
||||||
|
public class BpmTaskCopyReqVO {
|
||||||
|
|
||||||
|
@Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||||
|
@NotEmpty(message = "任务编号不能为空")
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@Schema(description = "抄送的用户编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1,2]")
|
||||||
|
@NotEmpty(message = "抄送用户不能为空")
|
||||||
|
private Collection<Long> copyUserIds;
|
||||||
|
|
||||||
|
@Schema(description = "抄送意见", example = "帮忙看看!")
|
||||||
|
private String reason;
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task;
|
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task;
|
||||||
|
|
||||||
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceRespVO;
|
import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
@ -33,14 +34,21 @@ public class BpmTaskRespVO {
|
||||||
@Schema(description = "审批理由", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
@Schema(description = "审批理由", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||||
private String reason;
|
private String reason;
|
||||||
|
|
||||||
|
@Schema(description = "任务负责人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048")
|
||||||
|
@JsonIgnore // 不返回,只是方便后续读取,赋值给 ownerUser
|
||||||
|
private Long owner;
|
||||||
/**
|
/**
|
||||||
* 负责人的用户信息
|
* 负责人的用户信息
|
||||||
*/
|
*/
|
||||||
private BpmProcessInstanceRespVO.User ownerUser;
|
private UserSimpleBaseVO ownerUser;
|
||||||
|
|
||||||
|
@Schema(description = "任务分配人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048")
|
||||||
|
@JsonIgnore // 不返回,只是方便后续读取,赋值给 assigneeUser
|
||||||
|
private Long assignee;
|
||||||
/**
|
/**
|
||||||
* 审核的用户信息
|
* 审核的用户信息
|
||||||
*/
|
*/
|
||||||
private BpmProcessInstanceRespVO.User assigneeUser;
|
private UserSimpleBaseVO assigneeUser;
|
||||||
|
|
||||||
@Schema(description = "任务定义的标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "Activity_one")
|
@Schema(description = "任务定义的标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "Activity_one")
|
||||||
private String taskDefinitionKey;
|
private String taskDefinitionKey;
|
||||||
|
@ -55,7 +63,7 @@ public class BpmTaskRespVO {
|
||||||
@Schema(description = "父任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
@Schema(description = "父任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||||
private String parentTaskId;
|
private String parentTaskId;
|
||||||
@Schema(description = "子任务列表(由加签生成)", requiredMode = Schema.RequiredMode.REQUIRED, example = "childrenTask")
|
@Schema(description = "子任务列表(由加签生成)", requiredMode = Schema.RequiredMode.REQUIRED, example = "childrenTask")
|
||||||
private List<BpmTaskRespVO> children;
|
private List<BpmTaskRespVO> children; // 由加签生成,包含多层子任务
|
||||||
|
|
||||||
@Schema(description = "表单编号", example = "1024")
|
@Schema(description = "表单编号", example = "1024")
|
||||||
private Long formId;
|
private Long formId;
|
||||||
|
@ -67,9 +75,6 @@ public class BpmTaskRespVO {
|
||||||
private List<String> formFields;
|
private List<String> formFields;
|
||||||
@Schema(description = "提交的表单值", requiredMode = Schema.RequiredMode.REQUIRED)
|
@Schema(description = "提交的表单值", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private Map<String, Object> formVariables;
|
private Map<String, Object> formVariables;
|
||||||
// @芋艿 都改成了 fieldsPermission。 buttonsSetting。和 BpmSimpleModelNodeVO 统一
|
|
||||||
@Schema(description = "表单字段权限值")
|
|
||||||
private Map<String, String> fieldsPermission;
|
|
||||||
@Schema(description = "操作按钮设置值")
|
@Schema(description = "操作按钮设置值")
|
||||||
private Map<Integer, OperationButtonSetting> buttonsSetting;
|
private Map<Integer, OperationButtonSetting> buttonsSetting;
|
||||||
|
|
||||||
|
@ -92,7 +97,7 @@ public class BpmTaskRespVO {
|
||||||
/**
|
/**
|
||||||
* 发起人的用户信息
|
* 发起人的用户信息
|
||||||
*/
|
*/
|
||||||
private BpmProcessInstanceRespVO.User startUser;
|
private UserSimpleBaseVO startUser;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import jakarta.validation.constraints.NotEmpty;
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@Schema(description = "管理后台 - 回退流程任务的 Request VO")
|
@Schema(description = "管理后台 - 退回流程任务的 Request VO")
|
||||||
@Data
|
@Data
|
||||||
public class BpmTaskReturnReqVO {
|
public class BpmTaskReturnReqVO {
|
||||||
|
|
||||||
|
@ -12,12 +12,12 @@ public class BpmTaskReturnReqVO {
|
||||||
@NotEmpty(message = "任务编号不能为空")
|
@NotEmpty(message = "任务编号不能为空")
|
||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
@Schema(description = "回退到的任务 Key", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
@Schema(description = "退回到的任务 Key", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||||
@NotEmpty(message = "回退到的任务 Key 不能为空")
|
@NotEmpty(message = "退回到的任务 Key 不能为空")
|
||||||
private String targetTaskDefinitionKey;
|
private String targetTaskDefinitionKey;
|
||||||
|
|
||||||
@Schema(description = "回退意见", requiredMode = Schema.RequiredMode.REQUIRED, example = "我就是想驳回")
|
@Schema(description = "退回意见", requiredMode = Schema.RequiredMode.REQUIRED, example = "我就是想驳回")
|
||||||
@NotEmpty(message = "回退意见不能为空")
|
@NotEmpty(message = "退回意见不能为空")
|
||||||
private String reason;
|
private String reason;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package cn.iocoder.yudao.module.bpm.convert.definition;
|
package cn.iocoder.yudao.module.bpm.convert.definition;
|
||||||
|
|
||||||
import cn.hutool.core.util.ArrayUtil;
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
|
||||||
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
|
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
|
||||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||||
|
@ -22,6 +21,7 @@ import org.mapstruct.Mapper;
|
||||||
import org.mapstruct.factory.Mappers;
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -37,25 +37,28 @@ public interface BpmModelConvert {
|
||||||
|
|
||||||
BpmModelConvert INSTANCE = Mappers.getMapper(BpmModelConvert.class);
|
BpmModelConvert INSTANCE = Mappers.getMapper(BpmModelConvert.class);
|
||||||
|
|
||||||
default PageResult<BpmModelRespVO> buildModelPage(PageResult<Model> pageResult,
|
default List<BpmModelRespVO> buildModelList(List<Model> list,
|
||||||
Map<Long, BpmFormDO> formMap,
|
Map<Long, BpmFormDO> formMap,
|
||||||
Map<String, BpmCategoryDO> categoryMap, Map<String, Deployment> deploymentMap,
|
Map<String, BpmCategoryDO> categoryMap,
|
||||||
Map<String, ProcessDefinition> processDefinitionMap,
|
Map<String, Deployment> deploymentMap,
|
||||||
Map<Long, AdminUserRespDTO> userMap) {
|
Map<String, ProcessDefinition> processDefinitionMap,
|
||||||
List<BpmModelRespVO> list = convertList(pageResult.getList(), model -> {
|
Map<Long, AdminUserRespDTO> userMap) {
|
||||||
|
List<BpmModelRespVO> result = convertList(list, model -> {
|
||||||
BpmModelMetaInfoVO metaInfo = parseMetaInfo(model);
|
BpmModelMetaInfoVO metaInfo = parseMetaInfo(model);
|
||||||
BpmFormDO form = metaInfo != null ? formMap.get(metaInfo.getFormId()) : null;
|
BpmFormDO form = metaInfo != null ? formMap.get(metaInfo.getFormId()) : null;
|
||||||
BpmCategoryDO category = categoryMap.get(model.getCategory());
|
BpmCategoryDO category = categoryMap.get(model.getCategory());
|
||||||
Deployment deployment = model.getDeploymentId() != null ? deploymentMap.get(model.getDeploymentId()) : null;
|
Deployment deployment = model.getDeploymentId() != null ? deploymentMap.get(model.getDeploymentId()) : null;
|
||||||
ProcessDefinition processDefinition = model.getDeploymentId() != null ? processDefinitionMap.get(model.getDeploymentId()) : null;
|
ProcessDefinition processDefinition = model.getDeploymentId() != null ?
|
||||||
|
processDefinitionMap.get(model.getDeploymentId()) : null;
|
||||||
List<AdminUserRespDTO> startUsers = metaInfo != null ? convertList(metaInfo.getStartUserIds(), userMap::get) : null;
|
List<AdminUserRespDTO> startUsers = metaInfo != null ? convertList(metaInfo.getStartUserIds(), userMap::get) : null;
|
||||||
return buildModel0(model, metaInfo, form, category, deployment, processDefinition, startUsers);
|
return buildModel0(model, metaInfo, form, category, deployment, processDefinition, startUsers);
|
||||||
});
|
});
|
||||||
return new PageResult<>(list, pageResult.getTotal());
|
// 排序
|
||||||
|
result.sort(Comparator.comparing(BpmModelMetaInfoVO::getSort));
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
default BpmModelRespVO buildModel(Model model,
|
default BpmModelRespVO buildModel(Model model, byte[] bpmnBytes) {
|
||||||
byte[] bpmnBytes) {
|
|
||||||
BpmModelMetaInfoVO metaInfo = parseMetaInfo(model);
|
BpmModelMetaInfoVO metaInfo = parseMetaInfo(model);
|
||||||
BpmModelRespVO modelVO = buildModel0(model, metaInfo, null, null, null, null, null);
|
BpmModelRespVO modelVO = buildModel0(model, metaInfo, null, null, null, null, null);
|
||||||
if (ArrayUtil.isNotEmpty(bpmnBytes)) {
|
if (ArrayUtil.isNotEmpty(bpmnBytes)) {
|
||||||
|
@ -112,6 +115,10 @@ public interface BpmModelConvert {
|
||||||
if (vo.getStartUserIds() == null) {
|
if (vo.getStartUserIds() == null) {
|
||||||
vo.setStartUserIds(Collections.emptyList());
|
vo.setStartUserIds(Collections.emptyList());
|
||||||
}
|
}
|
||||||
|
// 如果为空,兜底处理,使用 createTime 创建时间
|
||||||
|
if (vo.getSort() == null) {
|
||||||
|
vo.setSort(model.getCreateTime().getTime());
|
||||||
|
}
|
||||||
return vo;
|
return vo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,6 @@ import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
|
||||||
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
|
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
|
||||||
import org.flowable.bpmn.model.BpmnModel;
|
import org.flowable.bpmn.model.BpmnModel;
|
||||||
import org.flowable.bpmn.model.UserTask;
|
|
||||||
import org.flowable.common.engine.impl.db.SuspensionState;
|
import org.flowable.common.engine.impl.db.SuspensionState;
|
||||||
import org.flowable.engine.repository.Deployment;
|
import org.flowable.engine.repository.Deployment;
|
||||||
import org.flowable.engine.repository.ProcessDefinition;
|
import org.flowable.engine.repository.ProcessDefinition;
|
||||||
|
@ -20,6 +19,7 @@ import org.mapstruct.Mapping;
|
||||||
import org.mapstruct.MappingTarget;
|
import org.mapstruct.MappingTarget;
|
||||||
import org.mapstruct.factory.Mappers;
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ public interface BpmProcessDefinitionConvert {
|
||||||
Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap,
|
Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap,
|
||||||
Map<Long, BpmFormDO> formMap,
|
Map<Long, BpmFormDO> formMap,
|
||||||
Map<String, BpmCategoryDO> categoryMap) {
|
Map<String, BpmCategoryDO> categoryMap) {
|
||||||
return CollectionUtils.convertList(list, definition -> {
|
List<BpmProcessDefinitionRespVO> result = CollectionUtils.convertList(list, definition -> {
|
||||||
Deployment deployment = MapUtil.get(deploymentMap, definition.getDeploymentId(), Deployment.class);
|
Deployment deployment = MapUtil.get(deploymentMap, definition.getDeploymentId(), Deployment.class);
|
||||||
BpmProcessDefinitionInfoDO processDefinitionInfo = MapUtil.get(processDefinitionInfoMap, definition.getId(), BpmProcessDefinitionInfoDO.class);
|
BpmProcessDefinitionInfoDO processDefinitionInfo = MapUtil.get(processDefinitionInfoMap, definition.getId(), BpmProcessDefinitionInfoDO.class);
|
||||||
BpmFormDO form = null;
|
BpmFormDO form = null;
|
||||||
|
@ -55,8 +55,11 @@ public interface BpmProcessDefinitionConvert {
|
||||||
form = MapUtil.get(formMap, processDefinitionInfo.getFormId(), BpmFormDO.class);
|
form = MapUtil.get(formMap, processDefinitionInfo.getFormId(), BpmFormDO.class);
|
||||||
}
|
}
|
||||||
BpmCategoryDO category = MapUtil.get(categoryMap, definition.getCategory(), BpmCategoryDO.class);
|
BpmCategoryDO category = MapUtil.get(categoryMap, definition.getCategory(), BpmCategoryDO.class);
|
||||||
return buildProcessDefinition(definition, deployment, processDefinitionInfo, form, category, null, null);
|
return buildProcessDefinition(definition, deployment, processDefinitionInfo, form, category, null);
|
||||||
});
|
});
|
||||||
|
// 排序
|
||||||
|
result.sort(Comparator.comparing(BpmProcessDefinitionRespVO::getSort));
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
default BpmProcessDefinitionRespVO buildProcessDefinition(ProcessDefinition definition,
|
default BpmProcessDefinitionRespVO buildProcessDefinition(ProcessDefinition definition,
|
||||||
|
@ -64,8 +67,7 @@ public interface BpmProcessDefinitionConvert {
|
||||||
BpmProcessDefinitionInfoDO processDefinitionInfo,
|
BpmProcessDefinitionInfoDO processDefinitionInfo,
|
||||||
BpmFormDO form,
|
BpmFormDO form,
|
||||||
BpmCategoryDO category,
|
BpmCategoryDO category,
|
||||||
BpmnModel bpmnModel,
|
BpmnModel bpmnModel) {
|
||||||
List<UserTask> startUserSelectUserTaskList) {
|
|
||||||
BpmProcessDefinitionRespVO respVO = BeanUtils.toBean(definition, BpmProcessDefinitionRespVO.class);
|
BpmProcessDefinitionRespVO respVO = BeanUtils.toBean(definition, BpmProcessDefinitionRespVO.class);
|
||||||
respVO.setSuspensionState(definition.isSuspended() ? SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode());
|
respVO.setSuspensionState(definition.isSuspended() ? SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode());
|
||||||
// Deployment
|
// Deployment
|
||||||
|
@ -87,7 +89,6 @@ public interface BpmProcessDefinitionConvert {
|
||||||
// BpmnModel
|
// BpmnModel
|
||||||
if (bpmnModel != null) {
|
if (bpmnModel != null) {
|
||||||
respVO.setBpmnXml(BpmnModelUtils.getBpmnXml(bpmnModel));
|
respVO.setBpmnXml(BpmnModelUtils.getBpmnXml(bpmnModel));
|
||||||
respVO.setStartUserSelectTasks(BeanUtils.toBean(startUserSelectUserTaskList, BpmProcessDefinitionRespVO.UserTask.class));
|
|
||||||
}
|
}
|
||||||
return respVO;
|
return respVO;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
package cn.iocoder.yudao.module.bpm.convert.task;
|
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
|
|
||||||
import cn.iocoder.yudao.module.bpm.controller.admin.task.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);
|
|
||||||
}
|
|
|
@ -1,30 +1,47 @@
|
||||||
package cn.iocoder.yudao.module.bpm.convert.task;
|
package cn.iocoder.yudao.module.bpm.convert.task;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||||
|
import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
|
||||||
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
|
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
|
||||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||||
|
import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;
|
||||||
|
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
|
||||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO;
|
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO;
|
||||||
|
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO;
|
||||||
|
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceBpmnModelViewRespVO;
|
||||||
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceRespVO;
|
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceRespVO;
|
||||||
|
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
|
||||||
|
import cn.iocoder.yudao.module.bpm.convert.definition.BpmProcessDefinitionConvert;
|
||||||
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
|
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
|
||||||
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
|
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
|
||||||
import cn.iocoder.yudao.module.bpm.event.BpmProcessInstanceStatusEvent;
|
import cn.iocoder.yudao.module.bpm.event.BpmProcessInstanceStatusEvent;
|
||||||
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
|
||||||
import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO;
|
import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO;
|
||||||
import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceRejectReqDTO;
|
import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceRejectReqDTO;
|
||||||
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
||||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||||
|
import org.flowable.bpmn.model.BpmnModel;
|
||||||
import org.flowable.engine.history.HistoricProcessInstance;
|
import org.flowable.engine.history.HistoricProcessInstance;
|
||||||
import org.flowable.engine.repository.ProcessDefinition;
|
import org.flowable.engine.repository.ProcessDefinition;
|
||||||
import org.flowable.engine.runtime.ProcessInstance;
|
import org.flowable.engine.runtime.ProcessInstance;
|
||||||
import org.flowable.task.api.Task;
|
import org.flowable.task.api.Task;
|
||||||
|
import org.flowable.task.api.history.HistoricTaskInstance;
|
||||||
import org.mapstruct.Mapper;
|
import org.mapstruct.Mapper;
|
||||||
import org.mapstruct.Mapping;
|
import org.mapstruct.Mapping;
|
||||||
import org.mapstruct.MappingTarget;
|
import org.mapstruct.MappingTarget;
|
||||||
import org.mapstruct.factory.Mappers;
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||||
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 流程实例 Convert
|
* 流程实例 Convert
|
||||||
|
@ -55,7 +72,7 @@ public interface BpmProcessInstanceConvert {
|
||||||
if (userMap != null) {
|
if (userMap != null) {
|
||||||
AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(pageResult.getList().get(i).getStartUserId()));
|
AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(pageResult.getList().get(i).getStartUserId()));
|
||||||
if (startUser != null) {
|
if (startUser != null) {
|
||||||
respVO.setStartUser(BeanUtils.toBean(startUser, BpmProcessInstanceRespVO.User.class));
|
respVO.setStartUser(BeanUtils.toBean(startUser, UserSimpleBaseVO.class));
|
||||||
MapUtils.findAndThen(deptMap, startUser.getDeptId(), dept -> respVO.getStartUser().setDeptName(dept.getName()));
|
MapUtils.findAndThen(deptMap, startUser.getDeptId(), dept -> respVO.getStartUser().setDeptName(dept.getName()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,20 +82,18 @@ public interface BpmProcessInstanceConvert {
|
||||||
|
|
||||||
default BpmProcessInstanceRespVO buildProcessInstance(HistoricProcessInstance processInstance,
|
default BpmProcessInstanceRespVO buildProcessInstance(HistoricProcessInstance processInstance,
|
||||||
ProcessDefinition processDefinition,
|
ProcessDefinition processDefinition,
|
||||||
BpmProcessDefinitionInfoDO processDefinitionExt,
|
BpmProcessDefinitionInfoDO processDefinitionInfo,
|
||||||
String bpmnXml,
|
|
||||||
AdminUserRespDTO startUser,
|
AdminUserRespDTO startUser,
|
||||||
DeptRespDTO dept) {
|
DeptRespDTO dept) {
|
||||||
BpmProcessInstanceRespVO respVO = BeanUtils.toBean(processInstance, BpmProcessInstanceRespVO.class);
|
BpmProcessInstanceRespVO respVO = BeanUtils.toBean(processInstance, BpmProcessInstanceRespVO.class);
|
||||||
respVO.setStatus(FlowableUtils.getProcessInstanceStatus(processInstance));
|
respVO.setStatus(FlowableUtils.getProcessInstanceStatus(processInstance))
|
||||||
respVO.setFormVariables(FlowableUtils.getProcessInstanceFormVariable(processInstance));
|
.setFormVariables(FlowableUtils.getProcessInstanceFormVariable(processInstance));
|
||||||
// definition
|
// definition
|
||||||
respVO.setProcessDefinition(BeanUtils.toBean(processDefinition, BpmProcessDefinitionRespVO.class));
|
respVO.setProcessDefinition(BeanUtils.toBean(processDefinition, BpmProcessDefinitionRespVO.class));
|
||||||
copyTo(processDefinitionExt, respVO.getProcessDefinition());
|
copyTo(processDefinitionInfo, respVO.getProcessDefinition());
|
||||||
respVO.getProcessDefinition().setBpmnXml(bpmnXml);
|
|
||||||
// user
|
// user
|
||||||
if (startUser != null) {
|
if (startUser != null) {
|
||||||
respVO.setStartUser(BeanUtils.toBean(startUser, BpmProcessInstanceRespVO.User.class));
|
respVO.setStartUser(BeanUtils.toBean(startUser, UserSimpleBaseVO.class));
|
||||||
if (dept != null) {
|
if (dept != null) {
|
||||||
respVO.getStartUser().setDeptName(dept.getName());
|
respVO.getStartUser().setDeptName(dept.getName());
|
||||||
}
|
}
|
||||||
|
@ -89,7 +104,7 @@ public interface BpmProcessInstanceConvert {
|
||||||
@Mapping(source = "from.id", target = "to.id", ignore = true)
|
@Mapping(source = "from.id", target = "to.id", ignore = true)
|
||||||
void copyTo(BpmProcessDefinitionInfoDO from, @MappingTarget BpmProcessDefinitionRespVO to);
|
void copyTo(BpmProcessDefinitionInfoDO from, @MappingTarget BpmProcessDefinitionRespVO to);
|
||||||
|
|
||||||
default BpmProcessInstanceStatusEvent buildProcessInstanceStatusEvent(Object source, ProcessInstance instance, Integer status) {;
|
default BpmProcessInstanceStatusEvent buildProcessInstanceStatusEvent(Object source, ProcessInstance instance, Integer status) {
|
||||||
return new BpmProcessInstanceStatusEvent(source).setId(instance.getId()).setStatus(status)
|
return new BpmProcessInstanceStatusEvent(source).setId(instance.getId()).setStatus(status)
|
||||||
.setProcessDefinitionKey(instance.getProcessDefinitionKey()).setBusinessKey(instance.getBusinessKey());
|
.setProcessDefinitionKey(instance.getProcessDefinitionKey()).setBusinessKey(instance.getBusinessKey());
|
||||||
}
|
}
|
||||||
|
@ -109,4 +124,156 @@ public interface BpmProcessInstanceConvert {
|
||||||
.setStartUserId(NumberUtils.parseLong(instance.getStartUserId()));
|
.setStartUserId(NumberUtils.parseLong(instance.getStartUserId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default BpmProcessInstanceBpmnModelViewRespVO buildProcessInstanceBpmnModelView(HistoricProcessInstance processInstance,
|
||||||
|
List<HistoricTaskInstance> taskInstances,
|
||||||
|
BpmnModel bpmnModel,
|
||||||
|
BpmSimpleModelNodeVO simpleModel,
|
||||||
|
Set<String> unfinishedTaskActivityIds,
|
||||||
|
Set<String> finishedTaskActivityIds,
|
||||||
|
Set<String> finishedSequenceFlowActivityIds,
|
||||||
|
Set<String> rejectTaskActivityIds,
|
||||||
|
Map<Long, AdminUserRespDTO> userMap,
|
||||||
|
Map<Long, DeptRespDTO> deptMap) {
|
||||||
|
BpmProcessInstanceBpmnModelViewRespVO respVO = new BpmProcessInstanceBpmnModelViewRespVO();
|
||||||
|
// 基本信息
|
||||||
|
respVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmProcessInstanceRespVO.class, o -> o
|
||||||
|
.setStatus(FlowableUtils.getProcessInstanceStatus(processInstance)))
|
||||||
|
.setStartUser(buildUser(processInstance.getStartUserId(), userMap, deptMap)));
|
||||||
|
respVO.setTasks(convertList(taskInstances, task -> BeanUtils.toBean(task, BpmTaskRespVO.class)
|
||||||
|
.setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task))
|
||||||
|
.setAssigneeUser(buildUser(task.getAssignee(), userMap, deptMap))
|
||||||
|
.setOwnerUser(buildUser(task.getOwner(), userMap, deptMap))));
|
||||||
|
respVO.setBpmnXml(BpmnModelUtils.getBpmnXml(bpmnModel));
|
||||||
|
respVO.setSimpleModel(simpleModel);
|
||||||
|
// 进度信息
|
||||||
|
respVO.setUnfinishedTaskActivityIds(unfinishedTaskActivityIds)
|
||||||
|
.setFinishedTaskActivityIds(finishedTaskActivityIds)
|
||||||
|
.setFinishedSequenceFlowActivityIds(finishedSequenceFlowActivityIds)
|
||||||
|
.setRejectedTaskActivityIds(rejectTaskActivityIds);
|
||||||
|
return respVO;
|
||||||
|
}
|
||||||
|
|
||||||
|
default UserSimpleBaseVO buildUser(String userIdStr,
|
||||||
|
Map<Long, AdminUserRespDTO> userMap,
|
||||||
|
Map<Long, DeptRespDTO> deptMap) {
|
||||||
|
if (StrUtil.isEmpty(userIdStr)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Long userId = NumberUtils.parseLong(userIdStr);
|
||||||
|
return buildUser(userId, userMap, deptMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
default UserSimpleBaseVO buildUser(Long userId,
|
||||||
|
Map<Long, AdminUserRespDTO> userMap,
|
||||||
|
Map<Long, DeptRespDTO> deptMap) {
|
||||||
|
if (userId == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
AdminUserRespDTO user = userMap.get(userId);
|
||||||
|
if (user == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
UserSimpleBaseVO userVO = BeanUtils.toBean(user, UserSimpleBaseVO.class);
|
||||||
|
DeptRespDTO dept = user.getDeptId() != null ? deptMap.get(user.getDeptId()) : null;
|
||||||
|
if (dept != null) {
|
||||||
|
userVO.setDeptName(dept.getName());
|
||||||
|
}
|
||||||
|
return userVO;
|
||||||
|
}
|
||||||
|
|
||||||
|
default BpmApprovalDetailRespVO.ActivityNodeTask buildApprovalTaskInfo(HistoricTaskInstance task) {
|
||||||
|
if (task == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return BeanUtils.toBean(task, BpmApprovalDetailRespVO.ActivityNodeTask.class)
|
||||||
|
.setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task));
|
||||||
|
}
|
||||||
|
|
||||||
|
default Set<Long> parseUserIds(HistoricProcessInstance processInstance,
|
||||||
|
List<BpmApprovalDetailRespVO.ActivityNode> activityNodes,
|
||||||
|
BpmTaskRespVO todoTask) {
|
||||||
|
Set<Long> userIds = new HashSet<>();
|
||||||
|
if (processInstance != null) {
|
||||||
|
userIds.add(NumberUtils.parseLong(processInstance.getStartUserId()));
|
||||||
|
}
|
||||||
|
for (BpmApprovalDetailRespVO.ActivityNode activityNode : activityNodes) {
|
||||||
|
CollUtil.addAll(userIds, convertSet(activityNode.getTasks(), BpmApprovalDetailRespVO.ActivityNodeTask::getAssignee));
|
||||||
|
CollUtil.addAll(userIds, convertSet(activityNode.getTasks(), BpmApprovalDetailRespVO.ActivityNodeTask::getOwner));
|
||||||
|
CollUtil.addAll(userIds, activityNode.getCandidateUserIds());
|
||||||
|
}
|
||||||
|
if (todoTask != null) {
|
||||||
|
CollUtil.addIfAbsent(userIds, todoTask.getAssignee());
|
||||||
|
CollUtil.addIfAbsent(userIds, todoTask.getOwner());
|
||||||
|
if (CollUtil.isNotEmpty(todoTask.getChildren())) {
|
||||||
|
CollUtil.addAll(userIds, convertSet(todoTask.getChildren(), BpmTaskRespVO::getAssignee));
|
||||||
|
CollUtil.addAll(userIds, convertSet(todoTask.getChildren(), BpmTaskRespVO::getOwner));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return userIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
default Set<Long> parseUserIds02(HistoricProcessInstance processInstance,
|
||||||
|
List<HistoricTaskInstance> tasks) {
|
||||||
|
Set<Long> userIds = SetUtils.asSet(Long.valueOf(processInstance.getStartUserId()));
|
||||||
|
tasks.forEach(task -> {
|
||||||
|
CollUtil.addIfAbsent(userIds, NumberUtils.parseLong((task.getAssignee())));
|
||||||
|
CollUtil.addIfAbsent(userIds, NumberUtils.parseLong((task.getOwner())));
|
||||||
|
});
|
||||||
|
return userIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
default BpmApprovalDetailRespVO buildApprovalDetail(BpmnModel bpmnModel,
|
||||||
|
ProcessDefinition processDefinition,
|
||||||
|
BpmProcessDefinitionInfoDO processDefinitionInfo,
|
||||||
|
HistoricProcessInstance processInstance,
|
||||||
|
Integer processInstanceStatus,
|
||||||
|
List<BpmApprovalDetailRespVO.ActivityNode> activityNodes,
|
||||||
|
BpmTaskRespVO todoTask,
|
||||||
|
Map<String, String> formFieldsPermission,
|
||||||
|
Map<Long, AdminUserRespDTO> userMap,
|
||||||
|
Map<Long, DeptRespDTO> deptMap) {
|
||||||
|
// 1.1 流程实例
|
||||||
|
BpmProcessInstanceRespVO processInstanceResp = null;
|
||||||
|
if (processInstance != null) {
|
||||||
|
AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId()));
|
||||||
|
DeptRespDTO dept = startUser != null ? deptMap.get(startUser.getDeptId()) : null;
|
||||||
|
processInstanceResp = buildProcessInstance(processInstance, null, null, startUser, dept);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1.2 流程定义
|
||||||
|
BpmProcessDefinitionRespVO definitionResp = BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinition(
|
||||||
|
processDefinition, null, processDefinitionInfo, null, null, bpmnModel);
|
||||||
|
|
||||||
|
// 1.3 流程节点
|
||||||
|
activityNodes.forEach(approveNode -> {
|
||||||
|
if (approveNode.getTasks() != null) {
|
||||||
|
approveNode.getTasks().forEach(task -> {
|
||||||
|
task.setAssigneeUser(buildUser(task.getAssignee(), userMap, deptMap));
|
||||||
|
task.setOwnerUser(buildUser(task.getOwner(), userMap, deptMap));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
approveNode.setCandidateUsers(convertList(approveNode.getCandidateUserIds(), userId -> buildUser(userId, userMap, deptMap)));
|
||||||
|
});
|
||||||
|
|
||||||
|
// 1.4 待办任务
|
||||||
|
if (todoTask != null) {
|
||||||
|
todoTask.setAssigneeUser(buildUser(todoTask.getAssignee(), userMap, deptMap));
|
||||||
|
todoTask.setOwnerUser(buildUser(todoTask.getOwner(), userMap, deptMap));
|
||||||
|
if (CollUtil.isNotEmpty(todoTask.getChildren())) {
|
||||||
|
todoTask.getChildren().forEach(childTask -> {
|
||||||
|
childTask.setAssigneeUser(buildUser(childTask.getAssignee(), userMap, deptMap));
|
||||||
|
childTask.setOwnerUser(buildUser(childTask.getOwner(), userMap, deptMap));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 拼接起来
|
||||||
|
return new BpmApprovalDetailRespVO().setStatus(processInstanceStatus)
|
||||||
|
.setProcessDefinition(definitionResp)
|
||||||
|
.setProcessInstance(processInstanceResp)
|
||||||
|
.setFormFieldsPermission(formFieldsPermission)
|
||||||
|
.setTodoTask(todoTask)
|
||||||
|
.setActivityNodes(activityNodes);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,19 @@
|
||||||
package cn.iocoder.yudao.module.bpm.convert.task;
|
package cn.iocoder.yudao.module.bpm.convert.task;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.map.MapUtil;
|
import cn.hutool.core.map.MapUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||||
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
|
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
|
||||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||||
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceRespVO;
|
import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;
|
||||||
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
|
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
|
||||||
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
|
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
|
||||||
import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum;
|
import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
|
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
|
||||||
import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenTaskCreatedReqDTO;
|
import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenTaskCreatedReqDTO;
|
||||||
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
||||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||||
import org.flowable.bpmn.model.BpmnModel;
|
|
||||||
import org.flowable.engine.history.HistoricProcessInstance;
|
import org.flowable.engine.history.HistoricProcessInstance;
|
||||||
import org.flowable.engine.runtime.ProcessInstance;
|
import org.flowable.engine.runtime.ProcessInstance;
|
||||||
import org.flowable.task.api.Task;
|
import org.flowable.task.api.Task;
|
||||||
|
@ -28,7 +26,7 @@ import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||||
import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
|
import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -51,7 +49,7 @@ public interface BpmTaskConvert {
|
||||||
}
|
}
|
||||||
taskVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmTaskRespVO.ProcessInstance.class));
|
taskVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmTaskRespVO.ProcessInstance.class));
|
||||||
AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId()));
|
AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId()));
|
||||||
taskVO.getProcessInstance().setStartUser(BeanUtils.toBean(startUser, BpmProcessInstanceRespVO.User.class));
|
taskVO.getProcessInstance().setStartUser(BeanUtils.toBean(startUser, UserSimpleBaseVO.class));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +63,7 @@ public interface BpmTaskConvert {
|
||||||
// 用户信息
|
// 用户信息
|
||||||
AdminUserRespDTO assignUser = userMap.get(NumberUtils.parseLong(task.getAssignee()));
|
AdminUserRespDTO assignUser = userMap.get(NumberUtils.parseLong(task.getAssignee()));
|
||||||
if (assignUser != null) {
|
if (assignUser != null) {
|
||||||
taskVO.setAssigneeUser(BeanUtils.toBean(assignUser, BpmProcessInstanceRespVO.User.class));
|
taskVO.setAssigneeUser(BeanUtils.toBean(assignUser, UserSimpleBaseVO.class));
|
||||||
findAndThen(deptMap, assignUser.getDeptId(), dept -> taskVO.getAssigneeUser().setDeptName(dept.getName()));
|
findAndThen(deptMap, assignUser.getDeptId(), dept -> taskVO.getAssigneeUser().setDeptName(dept.getName()));
|
||||||
}
|
}
|
||||||
// 流程实例
|
// 流程实例
|
||||||
|
@ -73,7 +71,7 @@ public interface BpmTaskConvert {
|
||||||
if (processInstance != null) {
|
if (processInstance != null) {
|
||||||
AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId()));
|
AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId()));
|
||||||
taskVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmTaskRespVO.ProcessInstance.class));
|
taskVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmTaskRespVO.ProcessInstance.class));
|
||||||
taskVO.getProcessInstance().setStartUser(BeanUtils.toBean(startUser, BpmProcessInstanceRespVO.User.class));
|
taskVO.getProcessInstance().setStartUser(BeanUtils.toBean(startUser, UserSimpleBaseVO.class));
|
||||||
}
|
}
|
||||||
return taskVO;
|
return taskVO;
|
||||||
});
|
});
|
||||||
|
@ -81,19 +79,17 @@ public interface BpmTaskConvert {
|
||||||
}
|
}
|
||||||
|
|
||||||
default List<BpmTaskRespVO> buildTaskListByProcessInstanceId(List<HistoricTaskInstance> taskList,
|
default List<BpmTaskRespVO> buildTaskListByProcessInstanceId(List<HistoricTaskInstance> taskList,
|
||||||
HistoricProcessInstance processInstance,
|
|
||||||
Map<Long, BpmFormDO> formMap,
|
Map<Long, BpmFormDO> formMap,
|
||||||
Map<Long, AdminUserRespDTO> userMap,
|
Map<Long, AdminUserRespDTO> userMap,
|
||||||
Map<Long, DeptRespDTO> deptMap,
|
Map<Long, DeptRespDTO> deptMap) {
|
||||||
BpmnModel bpmnModel) {
|
return CollectionUtils.convertList(taskList, task -> {
|
||||||
List<BpmTaskRespVO> taskVOList = CollectionUtils.convertList(taskList, task -> {
|
// 特殊:已取消的任务,不返回
|
||||||
BpmTaskRespVO taskVO = BeanUtils.toBean(task, BpmTaskRespVO.class);
|
BpmTaskRespVO taskVO = BeanUtils.toBean(task, BpmTaskRespVO.class);
|
||||||
Integer taskStatus = FlowableUtils.getTaskStatus(task);
|
Integer taskStatus = FlowableUtils.getTaskStatus(task);
|
||||||
|
if (BpmTaskStatusEnum.isCancelStatus(taskStatus)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
taskVO.setStatus(taskStatus).setReason(FlowableUtils.getTaskReason(task));
|
taskVO.setStatus(taskStatus).setReason(FlowableUtils.getTaskReason(task));
|
||||||
// 流程实例
|
|
||||||
AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId()));
|
|
||||||
taskVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmTaskRespVO.ProcessInstance.class));
|
|
||||||
taskVO.getProcessInstance().setStartUser(BeanUtils.toBean(startUser, BpmProcessInstanceRespVO.User.class));
|
|
||||||
// 表单信息
|
// 表单信息
|
||||||
BpmFormDO form = MapUtil.get(formMap, NumberUtils.parseLong(task.getFormKey()), BpmFormDO.class);
|
BpmFormDO form = MapUtil.get(formMap, NumberUtils.parseLong(task.getFormKey()), BpmFormDO.class);
|
||||||
if (form != null) {
|
if (form != null) {
|
||||||
|
@ -101,35 +97,10 @@ public interface BpmTaskConvert {
|
||||||
.setFormFields(form.getFields()).setFormVariables(FlowableUtils.getTaskFormVariable(task));
|
.setFormFields(form.getFields()).setFormVariables(FlowableUtils.getTaskFormVariable(task));
|
||||||
}
|
}
|
||||||
// 用户信息
|
// 用户信息
|
||||||
AdminUserRespDTO assignUser = userMap.get(NumberUtils.parseLong(task.getAssignee()));
|
buildTaskAssignee(taskVO, task.getAssignee(), userMap, deptMap);
|
||||||
if (assignUser != null) {
|
buildTaskOwner(taskVO, task.getOwner(), userMap, deptMap);
|
||||||
taskVO.setAssigneeUser(BeanUtils.toBean(assignUser, BpmProcessInstanceRespVO.User.class));
|
return taskVO;
|
||||||
findAndThen(deptMap, assignUser.getDeptId(), dept -> taskVO.getAssigneeUser().setDeptName(dept.getName()));
|
|
||||||
}
|
|
||||||
AdminUserRespDTO ownerUser = userMap.get(NumberUtils.parseLong(task.getOwner()));
|
|
||||||
if (ownerUser != null) {
|
|
||||||
taskVO.setOwnerUser(BeanUtils.toBean(ownerUser, BpmProcessInstanceRespVO.User.class));
|
|
||||||
findAndThen(deptMap, ownerUser.getDeptId(), dept -> taskVO.getOwnerUser().setDeptName(dept.getName()));
|
|
||||||
}
|
|
||||||
if (BpmTaskStatusEnum.RUNNING.getStatus().equals(taskStatus)){
|
|
||||||
// 设置表单权限 TODO @芋艿 是不是还要加一个全局的权限 基于 processInstance 的权限;回复:可能不需要,但是发起人,需要有个权限配置
|
|
||||||
// TODO @jason:貌似这么返回,主要解决当前审批 task 的表单权限,但是不同抄送人的表单权限,可能不太对。例如说,对 A 抄送人是隐藏某个字段。
|
|
||||||
// @芋艿 表单权限需要分离开。单独的接口来获取了 BpmProcessInstanceService.getProcessInstanceFormFieldsPermission
|
|
||||||
taskVO.setFieldsPermission(BpmnModelUtils.parseFormFieldsPermission(bpmnModel, task.getTaskDefinitionKey()));
|
|
||||||
// 操作按钮设置
|
|
||||||
taskVO.setButtonsSetting(BpmnModelUtils.parseButtonsSetting(bpmnModel, task.getTaskDefinitionKey()));
|
|
||||||
}
|
|
||||||
return taskVO;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 拼接父子关系
|
|
||||||
Map<String, List<BpmTaskRespVO>> childrenTaskMap = convertMultiMap(
|
|
||||||
filterList(taskVOList, r -> StrUtil.isNotEmpty(r.getParentTaskId())),
|
|
||||||
BpmTaskRespVO::getParentTaskId);
|
|
||||||
for (BpmTaskRespVO taskVO : taskVOList) {
|
|
||||||
taskVO.setChildren(childrenTaskMap.get(taskVO.getId()));
|
|
||||||
}
|
|
||||||
return filterList(taskVOList, r -> StrUtil.isEmpty(r.getParentTaskId()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
default List<BpmTaskRespVO> buildTaskListByParentTaskId(List<Task> taskList,
|
default List<BpmTaskRespVO> buildTaskListByParentTaskId(List<Task> taskList,
|
||||||
|
@ -138,7 +109,7 @@ public interface BpmTaskConvert {
|
||||||
return convertList(taskList, task -> BeanUtils.toBean(task, BpmTaskRespVO.class, taskVO -> {
|
return convertList(taskList, task -> BeanUtils.toBean(task, BpmTaskRespVO.class, taskVO -> {
|
||||||
AdminUserRespDTO assignUser = userMap.get(NumberUtils.parseLong(task.getAssignee()));
|
AdminUserRespDTO assignUser = userMap.get(NumberUtils.parseLong(task.getAssignee()));
|
||||||
if (assignUser != null) {
|
if (assignUser != null) {
|
||||||
taskVO.setAssigneeUser(BeanUtils.toBean(assignUser, BpmProcessInstanceRespVO.User.class));
|
taskVO.setAssigneeUser(BeanUtils.toBean(assignUser, UserSimpleBaseVO.class));
|
||||||
DeptRespDTO dept = deptMap.get(assignUser.getDeptId());
|
DeptRespDTO dept = deptMap.get(assignUser.getDeptId());
|
||||||
if (dept != null) {
|
if (dept != null) {
|
||||||
taskVO.getAssigneeUser().setDeptName(dept.getName());
|
taskVO.getAssigneeUser().setDeptName(dept.getName());
|
||||||
|
@ -146,12 +117,21 @@ public interface BpmTaskConvert {
|
||||||
}
|
}
|
||||||
AdminUserRespDTO ownerUser = userMap.get(NumberUtils.parseLong(task.getOwner()));
|
AdminUserRespDTO ownerUser = userMap.get(NumberUtils.parseLong(task.getOwner()));
|
||||||
if (ownerUser != null) {
|
if (ownerUser != null) {
|
||||||
taskVO.setOwnerUser(BeanUtils.toBean(ownerUser, BpmProcessInstanceRespVO.User.class));
|
taskVO.setOwnerUser(BeanUtils.toBean(ownerUser, UserSimpleBaseVO.class));
|
||||||
findAndThen(deptMap, ownerUser.getDeptId(), dept -> taskVO.getOwnerUser().setDeptName(dept.getName()));
|
findAndThen(deptMap, ownerUser.getDeptId(), dept -> taskVO.getOwnerUser().setDeptName(dept.getName()));
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default BpmTaskRespVO buildTodoTask(Task todoTask, List<Task> childrenTasks,
|
||||||
|
Map<Integer, BpmTaskRespVO.OperationButtonSetting> buttonsSetting) {
|
||||||
|
return BeanUtils.toBean(todoTask, BpmTaskRespVO.class)
|
||||||
|
.setStatus(FlowableUtils.getTaskStatus(todoTask)).setReason(FlowableUtils.getTaskReason(todoTask))
|
||||||
|
.setButtonsSetting(buttonsSetting)
|
||||||
|
.setChildren(convertList(childrenTasks, childTask -> BeanUtils.toBean(childTask, BpmTaskRespVO.class)
|
||||||
|
.setStatus(FlowableUtils.getTaskStatus(childTask))));
|
||||||
|
}
|
||||||
|
|
||||||
default BpmMessageSendWhenTaskCreatedReqDTO convert(ProcessInstance processInstance, AdminUserRespDTO startUser,
|
default BpmMessageSendWhenTaskCreatedReqDTO convert(ProcessInstance processInstance, AdminUserRespDTO startUser,
|
||||||
Task task) {
|
Task task) {
|
||||||
BpmMessageSendWhenTaskCreatedReqDTO reqDTO = new BpmMessageSendWhenTaskCreatedReqDTO();
|
BpmMessageSendWhenTaskCreatedReqDTO reqDTO = new BpmMessageSendWhenTaskCreatedReqDTO();
|
||||||
|
@ -162,6 +142,42 @@ public interface BpmTaskConvert {
|
||||||
return reqDTO;
|
return reqDTO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default void buildTaskOwner(BpmTaskRespVO task, String taskOwner,
|
||||||
|
Map<Long, AdminUserRespDTO> userMap,
|
||||||
|
Map<Long, DeptRespDTO> deptMap) {
|
||||||
|
AdminUserRespDTO ownerUser = userMap.get(NumberUtils.parseLong(taskOwner));
|
||||||
|
if (ownerUser != null) {
|
||||||
|
task.setOwnerUser(BeanUtils.toBean(ownerUser, UserSimpleBaseVO.class));
|
||||||
|
findAndThen(deptMap, ownerUser.getDeptId(), dept -> task.getOwnerUser().setDeptName(dept.getName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default void buildTaskChildren(BpmTaskRespVO task, Map<String, List<Task>> childrenTaskMap,
|
||||||
|
Map<Long, AdminUserRespDTO> userMap, Map<Long, DeptRespDTO> deptMap) {
|
||||||
|
List<Task> childTasks = childrenTaskMap.get(task.getId());
|
||||||
|
if (CollUtil.isNotEmpty(childTasks)) {
|
||||||
|
task.setChildren(
|
||||||
|
convertList(childTasks, childTask -> {
|
||||||
|
BpmTaskRespVO childTaskVO = BeanUtils.toBean(childTask, BpmTaskRespVO.class);
|
||||||
|
childTaskVO.setStatus(FlowableUtils.getTaskStatus(childTask));
|
||||||
|
buildTaskOwner(childTaskVO, childTask.getOwner(), userMap, deptMap);
|
||||||
|
buildTaskAssignee(childTaskVO, childTask.getAssignee(), userMap, deptMap);
|
||||||
|
return childTaskVO;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default void buildTaskAssignee(BpmTaskRespVO task, String taskAssignee,
|
||||||
|
Map<Long, AdminUserRespDTO> userMap,
|
||||||
|
Map<Long, DeptRespDTO> deptMap) {
|
||||||
|
AdminUserRespDTO assignUser = userMap.get(NumberUtils.parseLong(taskAssignee));
|
||||||
|
if (assignUser != null) {
|
||||||
|
task.setAssigneeUser(BeanUtils.toBean(assignUser, UserSimpleBaseVO.class));
|
||||||
|
findAndThen(deptMap, assignUser.getDeptId(), dept -> task.getAssigneeUser().setDeptName(dept.getName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将父任务的属性,拷贝到子任务(加签任务)
|
* 将父任务的属性,拷贝到子任务(加签任务)
|
||||||
* <p>
|
* <p>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package cn.iocoder.yudao.module.bpm.dal.dataobject.definition;
|
package cn.iocoder.yudao.module.bpm.dal.dataobject.definition;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||||
|
import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.type.StringListTypeHandler;
|
import cn.iocoder.yudao.framework.mybatis.core.type.StringListTypeHandler;
|
||||||
import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum;
|
import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum;
|
||||||
import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelTypeEnum;
|
import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelTypeEnum;
|
||||||
|
@ -122,6 +123,10 @@ public class BpmProcessDefinitionInfoDO extends BaseDO {
|
||||||
* 目的:如果 false 不可见,则不展示在“发起流程”的列表里
|
* 目的:如果 false 不可见,则不展示在“发起流程”的列表里
|
||||||
*/
|
*/
|
||||||
private Boolean visible;
|
private Boolean visible;
|
||||||
|
/**
|
||||||
|
* 排序值
|
||||||
|
*/
|
||||||
|
private Long sort;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 可发起用户编号数组
|
* 可发起用户编号数组
|
||||||
|
@ -134,7 +139,7 @@ public class BpmProcessDefinitionInfoDO extends BaseDO {
|
||||||
* 1. {@link #visible} 只是决定是否可见。即使不可见,还是可以发起
|
* 1. {@link #visible} 只是决定是否可见。即使不可见,还是可以发起
|
||||||
* 2. startUserIds 决定某个用户是否可以发起。如果该用户不可发起,则他也是不可见的
|
* 2. startUserIds 决定某个用户是否可以发起。如果该用户不可发起,则他也是不可见的
|
||||||
*/
|
*/
|
||||||
@TableField(typeHandler = StringListTypeHandler.class) // 为了可以使用 find_in_set 进行过滤
|
@TableField(typeHandler = LongListTypeHandler.class) // 为了可以使用 find_in_set 进行过滤
|
||||||
private List<Long> startUserIds;
|
private List<Long> startUserIds;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -8,6 +8,8 @@ import lombok.AllArgsConstructor;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.flowable.bpmn.model.FlowNode;
|
||||||
|
import org.flowable.task.api.history.HistoricTaskInstance;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 流程抄送 DO
|
* 流程抄送 DO
|
||||||
|
@ -54,23 +56,25 @@ public class BpmProcessInstanceCopyDO extends BaseDO {
|
||||||
*/
|
*/
|
||||||
private String category;
|
private String category;
|
||||||
/**
|
/**
|
||||||
* 流程活动编号
|
* 流程活动的编号
|
||||||
* <p/>
|
* <p/>
|
||||||
* 对应 BPMN XML 节点编号,用于查询抄送节点的表单字段权限
|
*
|
||||||
* 这里冗余的原因:如果是钉钉易搭的抄送节点 (ServiceTask),使用 taskId 可能查不到对应的 activityId
|
* 冗余 {@link FlowNode#getId()},对应 BPMN XML 节点编号
|
||||||
|
* 原因:用于查询抄送节点的表单字段权限。因为仿钉钉/飞书的抄送节点 (ServiceTask),没有 taskId,只有 activityId
|
||||||
*/
|
*/
|
||||||
private String activityId;
|
private String activityId;
|
||||||
/**
|
/**
|
||||||
* 任务主键
|
* 流程活动的名字
|
||||||
* 关联 Task 的 id 属性
|
*
|
||||||
|
* 冗余 {@link FlowNode#getName()}
|
||||||
|
*/
|
||||||
|
private String activityName;
|
||||||
|
/**
|
||||||
|
* 流程活动的编号
|
||||||
|
*
|
||||||
|
* 关联 {@link HistoricTaskInstance#getId()}
|
||||||
*/
|
*/
|
||||||
private String taskId;
|
private String taskId;
|
||||||
/**
|
|
||||||
* 任务名称
|
|
||||||
*
|
|
||||||
* 冗余 Task 的 name 属性
|
|
||||||
*/
|
|
||||||
private String taskName;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户编号(被抄送的用户编号)
|
* 用户编号(被抄送的用户编号)
|
||||||
|
@ -79,4 +83,9 @@ public class BpmProcessInstanceCopyDO extends BaseDO {
|
||||||
*/
|
*/
|
||||||
private Long userId;
|
private Long userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 抄送意见
|
||||||
|
*/
|
||||||
|
private String reason;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package cn.iocoder.yudao.module.bpm.dal.mysql.definition;
|
package cn.iocoder.yudao.module.bpm.dal.mysql.definition;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||||
|
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||||
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
|
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@ -18,4 +19,9 @@ public interface BpmProcessDefinitionInfoMapper extends BaseMapperX<BpmProcessDe
|
||||||
return selectOne(BpmProcessDefinitionInfoDO::getProcessDefinitionId, processDefinitionId);
|
return selectOne(BpmProcessDefinitionInfoDO::getProcessDefinitionId, processDefinitionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default void updateByModelId(String modelId, BpmProcessDefinitionInfoDO updateObj) {
|
||||||
|
update(updateObj,
|
||||||
|
new LambdaQueryWrapperX<BpmProcessDefinitionInfoDO>().eq(BpmProcessDefinitionInfoDO::getModelId, modelId));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehav
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
Set<Long> assigneeUserIds = (Set<Long>) execution.getVariable(super.collectionVariable, Set.class);
|
Set<Long> assigneeUserIds = (Set<Long>) execution.getVariable(super.collectionVariable, Set.class);
|
||||||
if (assigneeUserIds == null) {
|
if (assigneeUserIds == null) {
|
||||||
assigneeUserIds = taskCandidateInvoker.calculateUsers(execution);
|
assigneeUserIds = taskCandidateInvoker.calculateUsersByTask(execution);
|
||||||
execution.setVariable(super.collectionVariable, assigneeUserIds);
|
execution.setVariable(super.collectionVariable, assigneeUserIds);
|
||||||
if (CollUtil.isEmpty(assigneeUserIds)) {
|
if (CollUtil.isEmpty(assigneeUserIds)) {
|
||||||
// 特殊:如果没有处理人的情况下,至少有一个 null 空元素,避免自动通过!
|
// 特殊:如果没有处理人的情况下,至少有一个 null 空元素,避免自动通过!
|
||||||
|
|
|
@ -46,7 +46,7 @@ public class BpmSequentialMultiInstanceBehavior extends SequentialMultiInstanceB
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
Set<Long> assigneeUserIds = (Set<Long>) execution.getVariable(super.collectionVariable, Set.class);
|
Set<Long> assigneeUserIds = (Set<Long>) execution.getVariable(super.collectionVariable, Set.class);
|
||||||
if (assigneeUserIds == null) {
|
if (assigneeUserIds == null) {
|
||||||
assigneeUserIds = taskCandidateInvoker.calculateUsers(execution);
|
assigneeUserIds = taskCandidateInvoker.calculateUsersByTask(execution);
|
||||||
execution.setVariable(super.collectionVariable, assigneeUserIds);
|
execution.setVariable(super.collectionVariable, assigneeUserIds);
|
||||||
if (CollUtil.isEmpty(assigneeUserIds)) {
|
if (CollUtil.isEmpty(assigneeUserIds)) {
|
||||||
// 特殊:如果没有处理人的情况下,至少有一个 null 空元素,避免自动通过!
|
// 特殊:如果没有处理人的情况下,至少有一个 null 空元素,避免自动通过!
|
||||||
|
|
|
@ -57,7 +57,7 @@ public class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior {
|
||||||
|
|
||||||
// 情况二,如果非多实例的任务,则计算任务处理人
|
// 情况二,如果非多实例的任务,则计算任务处理人
|
||||||
// 第一步,先计算可处理该任务的处理人们
|
// 第一步,先计算可处理该任务的处理人们
|
||||||
Set<Long> candidateUserIds = taskCandidateInvoker.calculateUsers(execution);
|
Set<Long> candidateUserIds = taskCandidateInvoker.calculateUsersByTask(execution);
|
||||||
if (CollUtil.isEmpty(candidateUserIds)) {
|
if (CollUtil.isEmpty(candidateUserIds)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.flowable.bpmn.model.BpmnModel;
|
import org.flowable.bpmn.model.BpmnModel;
|
||||||
|
import org.flowable.bpmn.model.FlowElement;
|
||||||
import org.flowable.bpmn.model.UserTask;
|
import org.flowable.bpmn.model.UserTask;
|
||||||
import org.flowable.engine.delegate.DelegateExecution;
|
import org.flowable.engine.delegate.DelegateExecution;
|
||||||
import org.flowable.engine.runtime.ProcessInstance;
|
import org.flowable.engine.runtime.ProcessInstance;
|
||||||
|
@ -89,32 +90,66 @@ public class BpmTaskCandidateInvoker {
|
||||||
* @return 用户编号集合
|
* @return 用户编号集合
|
||||||
*/
|
*/
|
||||||
@DataPermission(enable = false) // 忽略数据权限,避免因为过滤,导致找不到候选人
|
@DataPermission(enable = false) // 忽略数据权限,避免因为过滤,导致找不到候选人
|
||||||
public Set<Long> calculateUsers(DelegateExecution execution) {
|
public Set<Long> calculateUsersByTask(DelegateExecution execution) {
|
||||||
// 审批类型非人工审核时,不进行计算候选人。原因是:后续会自动通过、不通过
|
// 审批类型非人工审核时,不进行计算候选人。原因是:后续会自动通过、不通过
|
||||||
Integer approveType = BpmnModelUtils.parseApproveType(execution.getCurrentFlowElement());
|
FlowElement flowElement = execution.getCurrentFlowElement();
|
||||||
|
Integer approveType = BpmnModelUtils.parseApproveType(flowElement);
|
||||||
if (ObjectUtils.equalsAny(approveType,
|
if (ObjectUtils.equalsAny(approveType,
|
||||||
BpmUserTaskApproveTypeEnum.AUTO_APPROVE.getType(),
|
BpmUserTaskApproveTypeEnum.AUTO_APPROVE.getType(),
|
||||||
BpmUserTaskApproveTypeEnum.AUTO_REJECT.getType())) {
|
BpmUserTaskApproveTypeEnum.AUTO_REJECT.getType())) {
|
||||||
return new HashSet<>();
|
return new HashSet<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
Integer strategy = BpmnModelUtils.parseCandidateStrategy(execution.getCurrentFlowElement());
|
|
||||||
String param = BpmnModelUtils.parseCandidateParam(execution.getCurrentFlowElement());
|
|
||||||
// 1.1 计算任务的候选人
|
// 1.1 计算任务的候选人
|
||||||
Set<Long> userIds = getCandidateStrategy(strategy).calculateUsers(execution, param);
|
Integer strategy = BpmnModelUtils.parseCandidateStrategy(flowElement);
|
||||||
removeDisableUsers(userIds);
|
String param = BpmnModelUtils.parseCandidateParam(flowElement);
|
||||||
|
Set<Long> userIds = getCandidateStrategy(strategy).calculateUsersByTask(execution, param);
|
||||||
// 1.2 移除被禁用的用户
|
// 1.2 移除被禁用的用户
|
||||||
removeDisableUsers(userIds);
|
removeDisableUsers(userIds);
|
||||||
|
|
||||||
// 2. 候选人为空时,根据“审批人为空”的配置补充
|
// 2. 候选人为空时,根据“审批人为空”的配置补充
|
||||||
if (CollUtil.isEmpty(userIds)) {
|
if (CollUtil.isEmpty(userIds)) {
|
||||||
userIds = getCandidateStrategy(BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY.getStrategy())
|
userIds = getCandidateStrategy(BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY.getStrategy())
|
||||||
.calculateUsers(execution, param);
|
.calculateUsersByTask(execution, param);
|
||||||
// ASSIGN_EMPTY 策略,不需要移除被禁用的用户。原因是,再移除,可能会出现更没审批人了!!!
|
// ASSIGN_EMPTY 策略,不需要移除被禁用的用户。原因是,再移除,可能会出现更没审批人了!!!
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 移除发起人的用户
|
// 3. 移除发起人的用户
|
||||||
removeStartUserIfSkip(execution, userIds);
|
ProcessInstance processInstance = SpringUtil.getBean(BpmProcessInstanceService.class)
|
||||||
|
.getProcessInstance(execution.getProcessInstanceId());
|
||||||
|
Assert.notNull(processInstance, "流程实例({}) 不存在", execution.getProcessInstanceId());
|
||||||
|
removeStartUserIfSkip(userIds, flowElement, Long.valueOf(processInstance.getStartUserId()));
|
||||||
|
return userIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId,
|
||||||
|
Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {
|
||||||
|
// 审批类型非人工审核时,不进行计算候选人。原因是:后续会自动通过、不通过
|
||||||
|
FlowElement flowElement = BpmnModelUtils.getFlowElementById(bpmnModel, activityId);
|
||||||
|
Integer approveType = BpmnModelUtils.parseApproveType(flowElement);
|
||||||
|
if (ObjectUtils.equalsAny(approveType,
|
||||||
|
BpmUserTaskApproveTypeEnum.AUTO_APPROVE.getType(),
|
||||||
|
BpmUserTaskApproveTypeEnum.AUTO_REJECT.getType())) {
|
||||||
|
return new HashSet<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1.1 计算任务的候选人
|
||||||
|
Integer strategy = BpmnModelUtils.parseCandidateStrategy(flowElement);
|
||||||
|
String param = BpmnModelUtils.parseCandidateParam(flowElement);
|
||||||
|
Set<Long> userIds = getCandidateStrategy(strategy).calculateUsersByActivity(bpmnModel, activityId, param,
|
||||||
|
startUserId, processDefinitionId, processVariables);
|
||||||
|
// 1.2 移除被禁用的用户
|
||||||
|
removeDisableUsers(userIds);
|
||||||
|
|
||||||
|
// 2. 候选人为空时,根据“审批人为空”的配置补充
|
||||||
|
if (CollUtil.isEmpty(userIds)) {
|
||||||
|
userIds = getCandidateStrategy(BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY.getStrategy())
|
||||||
|
.calculateUsersByActivity(bpmnModel, activityId, param, startUserId, processDefinitionId, processVariables);
|
||||||
|
// ASSIGN_EMPTY 策略,不需要移除被禁用的用户。原因是,再移除,可能会出现更没审批人了!!!
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 移除发起人的用户
|
||||||
|
removeStartUserIfSkip(userIds, flowElement, startUserId);
|
||||||
return userIds;
|
return userIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,7 +161,7 @@ public class BpmTaskCandidateInvoker {
|
||||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(assigneeUserIds);
|
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(assigneeUserIds);
|
||||||
assigneeUserIds.removeIf(id -> {
|
assigneeUserIds.removeIf(id -> {
|
||||||
AdminUserRespDTO user = userMap.get(id);
|
AdminUserRespDTO user = userMap.get(id);
|
||||||
return user == null || !CommonStatusEnum.ENABLE.getStatus().equals(user.getStatus());
|
return user == null || CommonStatusEnum.isDisable(user.getStatus());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,25 +170,23 @@ public class BpmTaskCandidateInvoker {
|
||||||
*
|
*
|
||||||
* 注意:如果只有一个候选人,则不处理,避免无法审批
|
* 注意:如果只有一个候选人,则不处理,避免无法审批
|
||||||
*
|
*
|
||||||
* @param execution 执行中的任务
|
|
||||||
* @param assigneeUserIds 当前分配的候选人
|
* @param assigneeUserIds 当前分配的候选人
|
||||||
|
* @param flowElement 当前节点
|
||||||
|
* @param startUserId 发起人
|
||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void removeStartUserIfSkip(DelegateExecution execution, Set<Long> assigneeUserIds) {
|
void removeStartUserIfSkip(Set<Long> assigneeUserIds, FlowElement flowElement, Long startUserId) {
|
||||||
if (CollUtil.size(assigneeUserIds) <= 1) {
|
if (CollUtil.size(assigneeUserIds) <= 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Integer assignStartUserHandlerType = BpmnModelUtils.parseAssignStartUserHandlerType(execution.getCurrentFlowElement());
|
Integer assignStartUserHandlerType = BpmnModelUtils.parseAssignStartUserHandlerType(flowElement);
|
||||||
if (ObjectUtil.notEqual(assignStartUserHandlerType, BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType())) {
|
if (ObjectUtil.notEqual(assignStartUserHandlerType, BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ProcessInstance processInstance = SpringUtil.getBean(BpmProcessInstanceService.class)
|
assigneeUserIds.remove(startUserId);
|
||||||
.getProcessInstance(execution.getProcessInstanceId());
|
|
||||||
Assert.notNull(processInstance, "流程实例({}) 不存在", execution.getProcessInstanceId());
|
|
||||||
assigneeUserIds.remove(Long.valueOf(processInstance.getStartUserId()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public BpmTaskCandidateStrategy getCandidateStrategy(Integer strategy) {
|
private BpmTaskCandidateStrategy getCandidateStrategy(Integer strategy) {
|
||||||
BpmTaskCandidateStrategyEnum strategyEnum = BpmTaskCandidateStrategyEnum.valueOf(strategy);
|
BpmTaskCandidateStrategyEnum strategyEnum = BpmTaskCandidateStrategyEnum.valueOf(strategy);
|
||||||
Assert.notNull(strategyEnum, "策略(%s) 不存在", strategy);
|
Assert.notNull(strategyEnum, "策略(%s) 不存在", strategy);
|
||||||
BpmTaskCandidateStrategy strategyObj = strategyMap.get(strategyEnum);
|
BpmTaskCandidateStrategy strategyObj = strategyMap.get(strategyEnum);
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate;
|
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate;
|
||||||
|
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
|
||||||
|
import org.flowable.bpmn.model.BpmnModel;
|
||||||
import org.flowable.engine.delegate.DelegateExecution;
|
import org.flowable.engine.delegate.DelegateExecution;
|
||||||
import org.flowable.engine.runtime.ProcessInstance;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -42,47 +42,44 @@ public interface BpmTaskCandidateStrategy {
|
||||||
/**
|
/**
|
||||||
* 基于候选人参数,获得任务的候选用户们
|
* 基于候选人参数,获得任务的候选用户们
|
||||||
*
|
*
|
||||||
|
* 注意:实现 calculateUsers 系列方法时,有两种选择:
|
||||||
|
* 1. 只重写 calculateUsers 默认方法
|
||||||
|
* 2. 都重写 calculateUsersByTask 和 calculateUsersByActivity 两个方法
|
||||||
|
*
|
||||||
* @param param 执行任务
|
* @param param 执行任务
|
||||||
* @return 用户编号集合
|
* @return 用户编号集合
|
||||||
*/
|
*/
|
||||||
default Set<Long> calculateUsers(String param) {
|
default Set<Long> calculateUsers(String param) {
|
||||||
return Collections.emptySet();
|
throw new UnsupportedOperationException("该分配方法未实现,请检查!");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 基于执行任务,获得任务的候选用户们
|
* 基于【执行任务】,获得任务的候选用户们
|
||||||
*
|
*
|
||||||
* @param execution 执行任务
|
* @param execution 执行任务
|
||||||
* @return 用户编号集合
|
* @return 用户编号集合
|
||||||
*/
|
*/
|
||||||
default Set<Long> calculateUsers(DelegateExecution execution, String param) {
|
default Set<Long> calculateUsersByTask(DelegateExecution execution, String param) {
|
||||||
Set<Long> users = calculateUsers(param);
|
return calculateUsers(param);
|
||||||
removeDisableUsers(users);
|
|
||||||
return users;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 基于流程实例,获得任务的候选用户们
|
* 基于【流程活动】,获得任务的候选用户们
|
||||||
* <p>
|
* <p>
|
||||||
* 目的:用于获取未执行节点的候选用户们
|
* 目的:用于获取未执行节点的候选用户们
|
||||||
*
|
*
|
||||||
* @param startUserId 流程发起人编号
|
* @param bpmnModel 流程图
|
||||||
* @param processInstance 流程实例编号
|
* @param activityId 活动 ID (对应 Bpmn XML id)
|
||||||
* @param activityId 活动 Id (对应 Bpmn XML id)
|
|
||||||
* @param param 节点的参数
|
* @param param 节点的参数
|
||||||
|
* @param startUserId 流程发起人编号
|
||||||
|
* @param processDefinitionId 流程定义编号
|
||||||
|
* @param processVariables 流程变量
|
||||||
* @return 用户编号集合
|
* @return 用户编号集合
|
||||||
*/
|
*/
|
||||||
default Set<Long> calculateUsers(Long startUserId, ProcessInstance processInstance, String activityId, String param) {
|
@SuppressWarnings("unused")
|
||||||
Set<Long> users = calculateUsers(param);
|
default Set<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param,
|
||||||
removeDisableUsers(users);
|
Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {
|
||||||
return users;
|
return calculateUsers(param);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 移除被禁用的用户
|
|
||||||
*
|
|
||||||
* @param users 用户 Ids
|
|
||||||
*/
|
|
||||||
void removeDisableUsers(Set<Long> users);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
|
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
|
||||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
|
|
||||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
|
||||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link BpmTaskCandidateStrategy} 抽象类
|
|
||||||
*
|
|
||||||
* @author jason
|
|
||||||
*/
|
|
||||||
public abstract class BpmTaskCandidateAbstractStrategy implements BpmTaskCandidateStrategy {
|
|
||||||
|
|
||||||
protected AdminUserApi adminUserApi;
|
|
||||||
|
|
||||||
public BpmTaskCandidateAbstractStrategy(AdminUserApi adminUserApi) {
|
|
||||||
this.adminUserApi = adminUserApi;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeDisableUsers(Set<Long> users) {
|
|
||||||
if (CollUtil.isEmpty(users)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(users);
|
|
||||||
users.removeIf(id -> {
|
|
||||||
AdminUserRespDTO user = userMap.get(id);
|
|
||||||
return user == null || !CommonStatusEnum.ENABLE.getStatus().equals(user.getStatus());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
|
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.lang.Assert;
|
||||||
|
@ -6,6 +6,8 @@ import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCand
|
||||||
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||||
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
||||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||||
|
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
|
@ -17,14 +19,12 @@ import java.util.Set;
|
||||||
*
|
*
|
||||||
* @author jason
|
* @author jason
|
||||||
*/
|
*/
|
||||||
public abstract class BpmTaskCandidateAbstractDeptLeaderStrategy extends BpmTaskCandidateAbstractStrategy {
|
public abstract class AbstractBpmTaskCandidateDeptLeaderStrategy implements BpmTaskCandidateStrategy {
|
||||||
|
|
||||||
|
@Resource
|
||||||
protected DeptApi deptApi;
|
protected DeptApi deptApi;
|
||||||
|
@Resource
|
||||||
public BpmTaskCandidateAbstractDeptLeaderStrategy(AdminUserApi adminUserApi, DeptApi deptApi) {
|
protected AdminUserApi adminUserApi;
|
||||||
super(adminUserApi);
|
|
||||||
this.deptApi = deptApi;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得指定层级的部门负责人,只有第 level 的负责人
|
* 获得指定层级的部门负责人,只有第 level 的负责人
|
||||||
|
@ -78,4 +78,17 @@ public abstract class BpmTaskCandidateAbstractDeptLeaderStrategy extends BpmTas
|
||||||
return deptLeaderIds;
|
return deptLeaderIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取发起人的部门
|
||||||
|
*
|
||||||
|
* @param startUserId 发起人 Id
|
||||||
|
*/
|
||||||
|
protected DeptRespDTO getStartUserDept(Long startUserId) {
|
||||||
|
AdminUserRespDTO startUser = adminUserApi.getUser(startUserId).getCheckedData();
|
||||||
|
if (startUser.getDeptId() == null) { // 找不到部门
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return deptApi.getDept(startUser.getDeptId()).getCheckedData();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,13 +1,12 @@
|
||||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
|
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;
|
||||||
|
|
||||||
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.lang.Assert;
|
||||||
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
|
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
|
||||||
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
|
||||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -16,11 +15,7 @@ import java.util.Set;
|
||||||
* @author jason
|
* @author jason
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class BpmTaskCandidateDeptLeaderMultiStrategy extends BpmTaskCandidateAbstractDeptLeaderStrategy {
|
public class BpmTaskCandidateDeptLeaderMultiStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy {
|
||||||
|
|
||||||
public BpmTaskCandidateDeptLeaderMultiStrategy(AdminUserApi adminUserApi, DeptApi deptApi) {
|
|
||||||
super(adminUserApi, deptApi);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BpmTaskCandidateStrategyEnum getStrategy() {
|
public BpmTaskCandidateStrategyEnum getStrategy() {
|
||||||
|
@ -32,14 +27,19 @@ public class BpmTaskCandidateDeptLeaderMultiStrategy extends BpmTaskCandidateAbs
|
||||||
// 参数格式: | 分隔:1)左边为部门(多个部门用 , 分隔)。2)右边为部门层级
|
// 参数格式: | 分隔:1)左边为部门(多个部门用 , 分隔)。2)右边为部门层级
|
||||||
String[] params = param.split("\\|");
|
String[] params = param.split("\\|");
|
||||||
Assert.isTrue(params.length == 2, "参数格式不匹配");
|
Assert.isTrue(params.length == 2, "参数格式不匹配");
|
||||||
deptApi.validateDeptList(StrUtils.splitToLong(params[0], ",")).checkError();
|
List<Long> deptIds = StrUtils.splitToLong(params[0], ",");
|
||||||
Assert.isTrue(Integer.parseInt(params[1]) > 0, "部门层级必须大于 0");
|
int level = Integer.parseInt(params[1]);
|
||||||
|
// 校验部门存在
|
||||||
|
deptApi.validateDeptList(deptIds).checkError();
|
||||||
|
Assert.isTrue(level > 0, "部门层级必须大于 0");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<Long> calculateUsers(String param) {
|
public Set<Long> calculateUsers(String param) {
|
||||||
String[] params = param.split("\\|");
|
String[] params = param.split("\\|");
|
||||||
return getMultiLevelDeptLeaderIds(StrUtils.splitToLong(params[0], ","), Integer.valueOf(params[1]));
|
List<Long> deptIds = StrUtils.splitToLong(params[0], ",");
|
||||||
|
int level = Integer.parseInt(params[1]);
|
||||||
|
return super.getMultiLevelDeptLeaderIds(deptIds, level);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,11 +1,11 @@
|
||||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
|
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
|
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
|
||||||
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||||
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
||||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
import jakarta.annotation.Resource;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -19,14 +19,10 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
|
||||||
* @author kyle
|
* @author kyle
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class BpmTaskCandidateDeptLeaderStrategy extends BpmTaskCandidateAbstractStrategy {
|
public class BpmTaskCandidateDeptLeaderStrategy implements BpmTaskCandidateStrategy {
|
||||||
|
|
||||||
private final DeptApi deptApi;
|
@Resource
|
||||||
|
private DeptApi deptApi;
|
||||||
public BpmTaskCandidateDeptLeaderStrategy(AdminUserApi adminUserApi, DeptApi deptApi) {
|
|
||||||
super(adminUserApi);
|
|
||||||
this.deptApi = deptApi;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BpmTaskCandidateStrategyEnum getStrategy() {
|
public BpmTaskCandidateStrategyEnum getStrategy() {
|
||||||
|
@ -36,7 +32,7 @@ public class BpmTaskCandidateDeptLeaderStrategy extends BpmTaskCandidateAbstract
|
||||||
@Override
|
@Override
|
||||||
public void validateParam(String param) {
|
public void validateParam(String param) {
|
||||||
Set<Long> deptIds = StrUtils.splitToLongSet(param);
|
Set<Long> deptIds = StrUtils.splitToLongSet(param);
|
||||||
deptApi.validateDeptList(deptIds);
|
deptApi.validateDeptList(deptIds).checkError();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
|
@ -1,4 +1,4 @@
|
||||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
|
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
|
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
|
||||||
|
@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidat
|
||||||
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -19,14 +20,12 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
|
||||||
* @author kyle
|
* @author kyle
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class BpmTaskCandidateDeptMemberStrategy extends BpmTaskCandidateAbstractStrategy {
|
public class BpmTaskCandidateDeptMemberStrategy implements BpmTaskCandidateStrategy {
|
||||||
|
|
||||||
private final DeptApi deptApi;
|
@Resource
|
||||||
|
private DeptApi deptApi;
|
||||||
public BpmTaskCandidateDeptMemberStrategy(AdminUserApi adminUserApi, DeptApi deptApi) {
|
@Resource
|
||||||
super(adminUserApi);
|
private AdminUserApi adminUserApi;
|
||||||
this.deptApi = deptApi;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BpmTaskCandidateStrategyEnum getStrategy() {
|
public BpmTaskCandidateStrategyEnum getStrategy() {
|
||||||
|
@ -36,7 +35,7 @@ public class BpmTaskCandidateDeptMemberStrategy extends BpmTaskCandidateAbstract
|
||||||
@Override
|
@Override
|
||||||
public void validateParam(String param) {
|
public void validateParam(String param) {
|
||||||
Set<Long> deptIds = StrUtils.splitToLongSet(param);
|
Set<Long> deptIds = StrUtils.splitToLongSet(param);
|
||||||
deptApi.validateDeptList(deptIds);
|
deptApi.validateDeptList(deptIds).checkError();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
|
@ -1,21 +1,20 @@
|
||||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
|
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;
|
||||||
|
|
||||||
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.lang.Assert;
|
||||||
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
|
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
|
||||||
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
|
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
|
||||||
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
|
||||||
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
||||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
|
||||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
import org.flowable.bpmn.model.BpmnModel;
|
||||||
import org.flowable.engine.delegate.DelegateExecution;
|
import org.flowable.engine.delegate.DelegateExecution;
|
||||||
import org.flowable.engine.runtime.ProcessInstance;
|
import org.flowable.engine.runtime.ProcessInstance;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import static cn.hutool.core.collection.ListUtil.toList;
|
import static cn.hutool.core.collection.ListUtil.toList;
|
||||||
|
@ -26,16 +25,12 @@ import static cn.hutool.core.collection.ListUtil.toList;
|
||||||
* @author jason
|
* @author jason
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class BpmTaskCandidateStartUserDeptLeaderMultiStrategy extends BpmTaskCandidateAbstractDeptLeaderStrategy {
|
public class BpmTaskCandidateStartUserDeptLeaderMultiStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
@Lazy
|
@Lazy
|
||||||
private BpmProcessInstanceService processInstanceService;
|
private BpmProcessInstanceService processInstanceService;
|
||||||
|
|
||||||
public BpmTaskCandidateStartUserDeptLeaderMultiStrategy(AdminUserApi adminUserApi, DeptApi deptApi) {
|
|
||||||
super(adminUserApi, deptApi);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BpmTaskCandidateStrategyEnum getStrategy() {
|
public BpmTaskCandidateStrategyEnum getStrategy() {
|
||||||
return BpmTaskCandidateStrategyEnum.START_USER_DEPT_LEADER_MULTI;
|
return BpmTaskCandidateStrategyEnum.START_USER_DEPT_LEADER_MULTI;
|
||||||
|
@ -43,48 +38,33 @@ public class BpmTaskCandidateStartUserDeptLeaderMultiStrategy extends BpmTaskCan
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void validateParam(String param) {
|
public void validateParam(String param) {
|
||||||
// 参数是部门的层级
|
int level = Integer.parseInt(param); // 参数是部门的层级
|
||||||
Assert.isTrue(Integer.parseInt(param) > 0, "部门的层级必须大于 0");
|
Assert.isTrue(level > 0, "部门的层级必须大于 0");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<Long> calculateUsers(DelegateExecution execution, String param) {
|
public Set<Long> calculateUsersByTask(DelegateExecution execution, String param) {
|
||||||
|
int level = Integer.parseInt(param); // 参数是部门的层级
|
||||||
// 获得流程发起人
|
// 获得流程发起人
|
||||||
ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());
|
ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());
|
||||||
Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId());
|
Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId());
|
||||||
// 获取发起人的 multi 部门负责人
|
// 获取发起人的 multi 部门负责人
|
||||||
DeptRespDTO dept = getStartUserDept(startUserId);
|
DeptRespDTO dept = super.getStartUserDept(startUserId);
|
||||||
if (dept == null) {
|
if (dept == null) {
|
||||||
return new HashSet<>();
|
return new HashSet<>();
|
||||||
}
|
}
|
||||||
Set<Long> users = getMultiLevelDeptLeaderIds(toList(dept.getId()), Integer.valueOf(param)); // 参数是部门的层级
|
return super.getMultiLevelDeptLeaderIds(toList(dept.getId()), level);
|
||||||
// TODO @jason:这里 removeDisableUsers 的原因是啥呀?
|
|
||||||
removeDisableUsers(users);
|
|
||||||
return users;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<Long> calculateUsers(Long startUserId, ProcessInstance processInstance, String activityId, String param) {
|
public Set<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param,
|
||||||
DeptRespDTO dept = getStartUserDept(startUserId);
|
Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {
|
||||||
|
int level = Integer.parseInt(param); // 参数是部门的层级
|
||||||
|
DeptRespDTO dept = super.getStartUserDept(startUserId);
|
||||||
if (dept == null) {
|
if (dept == null) {
|
||||||
return new HashSet<>();
|
return new HashSet<>();
|
||||||
}
|
}
|
||||||
Set<Long> users = getMultiLevelDeptLeaderIds(toList(dept.getId()), Integer.valueOf(param)); // 参数是部门的层级
|
return super.getMultiLevelDeptLeaderIds(toList(dept.getId()), level);
|
||||||
removeDisableUsers(users);
|
|
||||||
return users;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取发起人的部门
|
|
||||||
*
|
|
||||||
* @param startUserId 发起人 Id
|
|
||||||
*/
|
|
||||||
protected DeptRespDTO getStartUserDept(Long startUserId) {
|
|
||||||
AdminUserRespDTO startUser = adminUserApi.getUser(startUserId).getCheckedData();
|
|
||||||
if (startUser.getDeptId() == null) { // 找不到部门
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return deptApi.getDept(startUser.getDeptId()).getCheckedData();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,21 +1,20 @@
|
||||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
|
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;
|
||||||
|
|
||||||
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.lang.Assert;
|
||||||
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
|
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
|
||||||
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
|
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
|
||||||
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
|
||||||
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
||||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
|
||||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
import org.flowable.bpmn.model.BpmnModel;
|
||||||
import org.flowable.engine.delegate.DelegateExecution;
|
import org.flowable.engine.delegate.DelegateExecution;
|
||||||
import org.flowable.engine.runtime.ProcessInstance;
|
import org.flowable.engine.runtime.ProcessInstance;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
|
import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
|
||||||
|
@ -26,7 +25,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
|
||||||
* @author jason
|
* @author jason
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class BpmTaskCandidateStartUserDeptLeaderStrategy extends BpmTaskCandidateAbstractDeptLeaderStrategy {
|
public class BpmTaskCandidateStartUserDeptLeaderStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
@Lazy // 避免循环依赖
|
@Lazy // 避免循环依赖
|
||||||
|
@ -37,10 +36,6 @@ public class BpmTaskCandidateStartUserDeptLeaderStrategy extends BpmTaskCandidat
|
||||||
return BpmTaskCandidateStrategyEnum.START_USER_DEPT_LEADER;
|
return BpmTaskCandidateStrategyEnum.START_USER_DEPT_LEADER;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BpmTaskCandidateStartUserDeptLeaderStrategy(AdminUserApi adminUserApi, DeptApi deptApi) {
|
|
||||||
super(adminUserApi, deptApi);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void validateParam(String param) {
|
public void validateParam(String param) {
|
||||||
// 参数是部门的层级
|
// 参数是部门的层级
|
||||||
|
@ -48,44 +43,29 @@ public class BpmTaskCandidateStartUserDeptLeaderStrategy extends BpmTaskCandidat
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<Long> calculateUsers(DelegateExecution execution, String param) {
|
public Set<Long> calculateUsersByTask(DelegateExecution execution, String param) {
|
||||||
// 获得流程发起人
|
// 获得流程发起人
|
||||||
ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());
|
ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());
|
||||||
Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId());
|
Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId());
|
||||||
// 获取发起人的部门负责人
|
// 获取发起人的部门负责人
|
||||||
Set<Long> users = getStartUserDeptLeader(startUserId, param);
|
return getStartUserDeptLeader(startUserId, param);
|
||||||
removeDisableUsers(users);
|
|
||||||
return users;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<Long> calculateUsers(Long startUserId, ProcessInstance processInstance, String activityId, String param) {
|
public Set<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param,
|
||||||
|
Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {
|
||||||
// 获取发起人的部门负责人
|
// 获取发起人的部门负责人
|
||||||
Set<Long> users = getStartUserDeptLeader(startUserId, param);
|
return getStartUserDeptLeader(startUserId, param);
|
||||||
removeDisableUsers(users);
|
|
||||||
return users;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<Long> getStartUserDeptLeader(Long startUserId, String param) {
|
private Set<Long> getStartUserDeptLeader(Long startUserId, String param) {
|
||||||
DeptRespDTO dept = getStartUserDept(startUserId);
|
int level = Integer.parseInt(param); // 参数是部门的层级
|
||||||
|
DeptRespDTO dept = super.getStartUserDept(startUserId);
|
||||||
if (dept == null) {
|
if (dept == null) {
|
||||||
return new HashSet<>();
|
return new HashSet<>();
|
||||||
}
|
}
|
||||||
Long deptLeaderId = getAssignLevelDeptLeaderId(dept, Integer.valueOf(param)); // 参数是部门的层级
|
Long deptLeaderId = super.getAssignLevelDeptLeaderId(dept, level);
|
||||||
return deptLeaderId != null ? asSet(deptLeaderId) : new HashSet<>();
|
return deptLeaderId != null ? asSet(deptLeaderId) : new HashSet<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取发起人的部门
|
|
||||||
*
|
|
||||||
* @param startUserId 发起人 Id
|
|
||||||
*/
|
|
||||||
protected DeptRespDTO getStartUserDept(Long startUserId) {
|
|
||||||
AdminUserRespDTO startUser = adminUserApi.getUser(startUserId).getCheckedData();
|
|
||||||
if (startUser.getDeptId() == null) { // 找不到部门
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return deptApi.getDept(startUser.getDeptId()).getCheckedData();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,14 +1,18 @@
|
||||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
|
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.lang.Assert;
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user.BpmTaskCandidateUserStrategy;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
|
||||||
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
|
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
|
||||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
import com.google.common.collect.Sets;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import org.flowable.bpmn.model.BpmnModel;
|
import org.flowable.bpmn.model.BpmnModel;
|
||||||
|
import org.flowable.bpmn.model.ServiceTask;
|
||||||
|
import org.flowable.bpmn.model.Task;
|
||||||
import org.flowable.bpmn.model.UserTask;
|
import org.flowable.bpmn.model.UserTask;
|
||||||
import org.flowable.engine.delegate.DelegateExecution;
|
import org.flowable.engine.delegate.DelegateExecution;
|
||||||
import org.flowable.engine.runtime.ProcessInstance;
|
import org.flowable.engine.runtime.ProcessInstance;
|
||||||
|
@ -23,16 +27,12 @@ import java.util.*;
|
||||||
* @author 芋道源码
|
* @author 芋道源码
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class BpmTaskCandidateStartUserSelectStrategy extends BpmTaskCandidateAbstractStrategy {
|
public class BpmTaskCandidateStartUserSelectStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
@Lazy // 延迟加载,避免循环依赖
|
@Lazy // 延迟加载,避免循环依赖
|
||||||
private BpmProcessInstanceService processInstanceService;
|
private BpmProcessInstanceService processInstanceService;
|
||||||
|
|
||||||
public BpmTaskCandidateStartUserSelectStrategy(AdminUserApi adminUserApi) {
|
|
||||||
super(adminUserApi);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BpmTaskCandidateStrategyEnum getStrategy() {
|
public BpmTaskCandidateStrategyEnum getStrategy() {
|
||||||
return BpmTaskCandidateStrategyEnum.START_USER_SELECT;
|
return BpmTaskCandidateStrategyEnum.START_USER_SELECT;
|
||||||
|
@ -42,7 +42,12 @@ public class BpmTaskCandidateStartUserSelectStrategy extends BpmTaskCandidateAbs
|
||||||
public void validateParam(String param) {}
|
public void validateParam(String param) {}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<Long> calculateUsers(DelegateExecution execution, String param) {
|
public boolean isParamRequired() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LinkedHashSet<Long> calculateUsersByTask(DelegateExecution execution, String param) {
|
||||||
ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());
|
ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());
|
||||||
Assert.notNull(processInstance, "流程实例({})不能为空", execution.getProcessInstanceId());
|
Assert.notNull(processInstance, "流程实例({})不能为空", execution.getProcessInstanceId());
|
||||||
Map<String, List<Long>> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance);
|
Map<String, List<Long>> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance);
|
||||||
|
@ -50,47 +55,43 @@ public class BpmTaskCandidateStartUserSelectStrategy extends BpmTaskCandidateAbs
|
||||||
execution.getProcessInstanceId());
|
execution.getProcessInstanceId());
|
||||||
// 获得审批人
|
// 获得审批人
|
||||||
List<Long> assignees = startUserSelectAssignees.get(execution.getCurrentActivityId());
|
List<Long> assignees = startUserSelectAssignees.get(execution.getCurrentActivityId());
|
||||||
Set<Long> users = new LinkedHashSet<>(assignees);
|
return new LinkedHashSet<>(assignees);
|
||||||
removeDisableUsers(users);
|
|
||||||
return users;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<Long> calculateUsers(Long startUserId, ProcessInstance processInstance, String activityId, String param) {
|
public LinkedHashSet<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param,
|
||||||
if (processInstance == null) {
|
Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {
|
||||||
return Collections.emptySet();
|
if (processVariables == null) {
|
||||||
|
return Sets.newLinkedHashSet();
|
||||||
|
}
|
||||||
|
Map<String, List<Long>> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processVariables);
|
||||||
|
if (startUserSelectAssignees == null) {
|
||||||
|
return Sets.newLinkedHashSet();
|
||||||
}
|
}
|
||||||
Map<String, List<Long>> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance);
|
|
||||||
Assert.notNull(startUserSelectAssignees, "流程实例({}) 的发起人自选审批人不能为空", processInstance.getId());
|
|
||||||
// 获得审批人
|
// 获得审批人
|
||||||
List<Long> assignees = startUserSelectAssignees.get(activityId);
|
List<Long> assignees = startUserSelectAssignees.get(activityId);
|
||||||
Set<Long> users = new LinkedHashSet<>(assignees);
|
return new LinkedHashSet<>(assignees);
|
||||||
removeDisableUsers(users);
|
|
||||||
return users;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isParamRequired() {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得发起人自选审批人的 UserTask 列表
|
* 获得发起人自选审批人或抄送人的 Task 列表
|
||||||
*
|
*
|
||||||
* @param bpmnModel BPMN 模型
|
* @param bpmnModel BPMN 模型
|
||||||
* @return UserTask 列表
|
* @return Task 列表
|
||||||
*/
|
*/
|
||||||
public static List<UserTask> getStartUserSelectUserTaskList(BpmnModel bpmnModel) {
|
public static List<Task> getStartUserSelectTaskList(BpmnModel bpmnModel) {
|
||||||
if (bpmnModel == null) {
|
if (bpmnModel == null) {
|
||||||
return null;
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
List<UserTask> userTaskList = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class);
|
List<Task> tasks = new ArrayList<>();
|
||||||
if (CollUtil.isEmpty(userTaskList)) {
|
tasks.addAll(BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class));
|
||||||
return null;
|
tasks.addAll(BpmnModelUtils.getBpmnModelElements(bpmnModel, ServiceTask.class));
|
||||||
|
if (CollUtil.isEmpty(tasks)) {
|
||||||
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
userTaskList.removeIf(userTask -> !Objects.equals(BpmnModelUtils.parseCandidateStrategy(userTask),
|
tasks.removeIf(task -> ObjectUtil.notEqual(BpmnModelUtils.parseCandidateStrategy(task),
|
||||||
BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy()));
|
BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy()));
|
||||||
return userTaskList;
|
return tasks;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.form;
|
||||||
|
|
||||||
|
import cn.hutool.core.convert.Convert;
|
||||||
|
import cn.hutool.core.lang.Assert;
|
||||||
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
|
||||||
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept.AbstractBpmTaskCandidateDeptLeaderStrategy;
|
||||||
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
|
||||||
|
import org.flowable.bpmn.model.BpmnModel;
|
||||||
|
import org.flowable.engine.delegate.DelegateExecution;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表单内部门负责人 {@link BpmTaskCandidateStrategy} 实现类
|
||||||
|
*
|
||||||
|
* @author jason
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class BpmTaskCandidateFormSDeptLeaderStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BpmTaskCandidateStrategyEnum getStrategy() {
|
||||||
|
return BpmTaskCandidateStrategyEnum.FORM_DEPT_LEADER;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validateParam(String param) {
|
||||||
|
// 参数格式: | 分隔:1)左边为表单内部门字段。2)右边为部门层级
|
||||||
|
String[] params = param.split("\\|");
|
||||||
|
Assert.isTrue(params.length == 2, "参数格式不匹配");
|
||||||
|
Assert.notEmpty(param, "表单内部门字段不能为空");
|
||||||
|
int level = Integer.parseInt(params[1]);
|
||||||
|
Assert.isTrue(level > 0, "部门层级必须大于 0");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Long> calculateUsersByTask(DelegateExecution execution, String param) {
|
||||||
|
String[] params = param.split("\\|");
|
||||||
|
Object result = execution.getVariable(params[0]);
|
||||||
|
int level = Integer.parseInt(params[1]);
|
||||||
|
return super.getMultiLevelDeptLeaderIds(Convert.toList(Long.class, result), level);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId,
|
||||||
|
String param, Long startUserId, String processDefinitionId,
|
||||||
|
Map<String, Object> processVariables) {
|
||||||
|
String[] params = param.split("\\|");
|
||||||
|
Object result = processVariables == null ? null : processVariables.get(params[0]);
|
||||||
|
int level = Integer.parseInt(params[1]);
|
||||||
|
return super.getMultiLevelDeptLeaderIds(Convert.toList(Long.class, result), level);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.form;
|
||||||
|
|
||||||
|
import cn.hutool.core.convert.Convert;
|
||||||
|
import cn.hutool.core.lang.Assert;
|
||||||
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
|
||||||
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user.BpmTaskCandidateUserStrategy;
|
||||||
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
|
||||||
|
import org.flowable.bpmn.model.BpmnModel;
|
||||||
|
import org.flowable.engine.delegate.DelegateExecution;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表单内用户字段 {@link BpmTaskCandidateUserStrategy} 实现类
|
||||||
|
*
|
||||||
|
* @author jason
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class BpmTaskCandidateFormUserStrategy implements BpmTaskCandidateStrategy {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BpmTaskCandidateStrategyEnum getStrategy() {
|
||||||
|
return BpmTaskCandidateStrategyEnum.FORM_USER;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validateParam(String param) {
|
||||||
|
Assert.notEmpty(param, "表单内用户字段不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Long> calculateUsersByTask(DelegateExecution execution, String param) {
|
||||||
|
Object result = execution.getVariable(param);
|
||||||
|
return Convert.toSet(Long.class, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId,
|
||||||
|
String param, Long startUserId, String processDefinitionId,
|
||||||
|
Map<String, Object> processVariables) {
|
||||||
|
Object result = processVariables == null ? null : processVariables.get(param);
|
||||||
|
return Convert.toSet(Long.class, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
|
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.other;
|
||||||
|
|
||||||
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.lang.Assert;
|
||||||
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
|
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
|
||||||
|
@ -7,13 +7,15 @@ import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCand
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
|
||||||
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
|
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
|
||||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
import org.flowable.bpmn.model.BpmnModel;
|
||||||
|
import org.flowable.bpmn.model.FlowElement;
|
||||||
import org.flowable.engine.delegate.DelegateExecution;
|
import org.flowable.engine.delegate.DelegateExecution;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
@ -23,16 +25,12 @@ import java.util.Set;
|
||||||
* @author kyle
|
* @author kyle
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class BpmTaskCandidateAssignEmptyStrategy extends BpmTaskCandidateAbstractStrategy {
|
public class BpmTaskCandidateAssignEmptyStrategy implements BpmTaskCandidateStrategy {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
@Lazy // 延迟加载,避免循环依赖
|
@Lazy // 延迟加载,避免循环依赖
|
||||||
private BpmProcessDefinitionService processDefinitionService;
|
private BpmProcessDefinitionService processDefinitionService;
|
||||||
|
|
||||||
public BpmTaskCandidateAssignEmptyStrategy(AdminUserApi adminUserApi) {
|
|
||||||
super(adminUserApi);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BpmTaskCandidateStrategyEnum getStrategy() {
|
public BpmTaskCandidateStrategyEnum getStrategy() {
|
||||||
return BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY;
|
return BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY;
|
||||||
|
@ -43,19 +41,28 @@ public class BpmTaskCandidateAssignEmptyStrategy extends BpmTaskCandidateAbstrac
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<Long> calculateUsers(DelegateExecution execution, String param) {
|
public Set<Long> calculateUsersByTask(DelegateExecution execution, String param) {
|
||||||
|
return getCandidateUsers(execution.getProcessDefinitionId(), execution.getCurrentFlowElement());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param,
|
||||||
|
Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {
|
||||||
|
FlowElement flowElement = BpmnModelUtils.getFlowElementById(bpmnModel, activityId);
|
||||||
|
return getCandidateUsers(processDefinitionId, flowElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<Long> getCandidateUsers(String processDefinitionId, FlowElement flowElement) {
|
||||||
// 情况一:指定人员审批
|
// 情况一:指定人员审批
|
||||||
Integer assignEmptyHandlerType = BpmnModelUtils.parseAssignEmptyHandlerType(execution.getCurrentFlowElement());
|
Integer assignEmptyHandlerType = BpmnModelUtils.parseAssignEmptyHandlerType(flowElement);
|
||||||
if (Objects.equals(assignEmptyHandlerType, BpmUserTaskAssignEmptyHandlerTypeEnum.ASSIGN_USER.getType())) {
|
if (Objects.equals(assignEmptyHandlerType, BpmUserTaskAssignEmptyHandlerTypeEnum.ASSIGN_USER.getType())) {
|
||||||
Set<Long> users = new HashSet<>(BpmnModelUtils.parseAssignEmptyHandlerUserIds(execution.getCurrentFlowElement()));
|
return new HashSet<>(BpmnModelUtils.parseAssignEmptyHandlerUserIds(flowElement));
|
||||||
removeDisableUsers(users);
|
|
||||||
return users;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 情况二:流程管理员
|
// 情况二:流程管理员
|
||||||
if (Objects.equals(assignEmptyHandlerType, BpmUserTaskAssignEmptyHandlerTypeEnum.ASSIGN_ADMIN.getType())) {
|
if (Objects.equals(assignEmptyHandlerType, BpmUserTaskAssignEmptyHandlerTypeEnum.ASSIGN_ADMIN.getType())) {
|
||||||
BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(execution.getProcessDefinitionId());
|
BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(processDefinitionId);
|
||||||
Assert.notNull(processDefinition, "流程定义({})不存在", execution.getProcessDefinitionId());
|
Assert.notNull(processDefinition, "流程定义({})不存在", processDefinitionId);
|
||||||
return new HashSet<>(processDefinition.getManagerUserIds());
|
return new HashSet<>(processDefinition.getManagerUserIds());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
|
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.other;
|
||||||
|
|
||||||
import cn.hutool.core.convert.Convert;
|
import cn.hutool.core.convert.Convert;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
|
||||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
import org.flowable.bpmn.model.BpmnModel;
|
||||||
import org.flowable.engine.delegate.DelegateExecution;
|
import org.flowable.engine.delegate.DelegateExecution;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -16,11 +17,7 @@ import java.util.Set;
|
||||||
* @author 芋道源码
|
* @author 芋道源码
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class BpmTaskCandidateExpressionStrategy extends BpmTaskCandidateAbstractStrategy {
|
public class BpmTaskCandidateExpressionStrategy implements BpmTaskCandidateStrategy {
|
||||||
|
|
||||||
public BpmTaskCandidateExpressionStrategy(AdminUserApi adminUserApi) {
|
|
||||||
super(adminUserApi);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BpmTaskCandidateStrategyEnum getStrategy() {
|
public BpmTaskCandidateStrategyEnum getStrategy() {
|
||||||
|
@ -33,11 +30,16 @@ public class BpmTaskCandidateExpressionStrategy extends BpmTaskCandidateAbstract
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<Long> calculateUsers(DelegateExecution execution, String param) {
|
public Set<Long> calculateUsersByTask(DelegateExecution execution, String param) {
|
||||||
Object result = FlowableUtils.getExpressionValue(execution, param);
|
Object result = FlowableUtils.getExpressionValue(execution, param);
|
||||||
Set<Long> users = Convert.toSet(Long.class, result);
|
return Convert.toSet(Long.class, result);
|
||||||
removeDisableUsers(users);
|
}
|
||||||
return users;
|
|
||||||
|
@Override
|
||||||
|
public Set<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param,
|
||||||
|
Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {
|
||||||
|
Object result = FlowableUtils.getExpressionValue(processVariables, param);
|
||||||
|
return Convert.toSet(Long.class, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,11 +1,11 @@
|
||||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
|
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
|
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
|
||||||
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;
|
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
|
||||||
import cn.iocoder.yudao.module.bpm.service.definition.BpmUserGroupService;
|
import cn.iocoder.yudao.module.bpm.service.definition.BpmUserGroupService;
|
||||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
import jakarta.annotation.Resource;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -20,14 +20,10 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
|
||||||
* @author kyle
|
* @author kyle
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class BpmTaskCandidateGroupStrategy extends BpmTaskCandidateAbstractStrategy {
|
public class BpmTaskCandidateGroupStrategy implements BpmTaskCandidateStrategy {
|
||||||
|
|
||||||
private final BpmUserGroupService userGroupService;
|
@Resource
|
||||||
|
private BpmUserGroupService userGroupService;
|
||||||
public BpmTaskCandidateGroupStrategy(AdminUserApi adminUserApi, BpmUserGroupService userGroupService) {
|
|
||||||
super(adminUserApi);
|
|
||||||
this.userGroupService = userGroupService;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BpmTaskCandidateStrategyEnum getStrategy() {
|
public BpmTaskCandidateStrategyEnum getStrategy() {
|
||||||
|
@ -37,7 +33,7 @@ public class BpmTaskCandidateGroupStrategy extends BpmTaskCandidateAbstractStrat
|
||||||
@Override
|
@Override
|
||||||
public void validateParam(String param) {
|
public void validateParam(String param) {
|
||||||
Set<Long> groupIds = StrUtils.splitToLongSet(param);
|
Set<Long> groupIds = StrUtils.splitToLongSet(param);
|
||||||
userGroupService.getUserGroupList(groupIds);
|
userGroupService.validUserGroups(groupIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
|
@ -1,4 +1,4 @@
|
||||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
|
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
|
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
|
||||||
|
@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidat
|
||||||
import cn.iocoder.yudao.module.system.api.dept.PostApi;
|
import cn.iocoder.yudao.module.system.api.dept.PostApi;
|
||||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -19,14 +20,12 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
|
||||||
* @author kyle
|
* @author kyle
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class BpmTaskCandidatePostStrategy extends BpmTaskCandidateAbstractStrategy {
|
public class BpmTaskCandidatePostStrategy implements BpmTaskCandidateStrategy {
|
||||||
|
|
||||||
private final PostApi postApi;
|
@Resource
|
||||||
|
private PostApi postApi;
|
||||||
public BpmTaskCandidatePostStrategy(AdminUserApi adminUserApi, PostApi postApi) {
|
@Resource
|
||||||
super(adminUserApi);
|
private AdminUserApi adminUserApi;
|
||||||
this.postApi = postApi;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BpmTaskCandidateStrategyEnum getStrategy() {
|
public BpmTaskCandidateStrategyEnum getStrategy() {
|
|
@ -1,11 +1,10 @@
|
||||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
|
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
|
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
|
||||||
import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
|
import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
|
||||||
import cn.iocoder.yudao.module.system.api.permission.RoleApi;
|
import cn.iocoder.yudao.module.system.api.permission.RoleApi;
|
||||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@ -17,17 +16,13 @@ import java.util.Set;
|
||||||
* @author kyle
|
* @author kyle
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class BpmTaskCandidateRoleStrategy extends BpmTaskCandidateAbstractStrategy {
|
public class BpmTaskCandidateRoleStrategy implements BpmTaskCandidateStrategy {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private RoleApi roleApi;
|
private RoleApi roleApi;
|
||||||
@Resource
|
@Resource
|
||||||
private PermissionApi permissionApi;
|
private PermissionApi permissionApi;
|
||||||
|
|
||||||
public BpmTaskCandidateRoleStrategy(AdminUserApi adminUserApi) {
|
|
||||||
super(adminUserApi);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BpmTaskCandidateStrategyEnum getStrategy() {
|
public BpmTaskCandidateStrategyEnum getStrategy() {
|
||||||
return BpmTaskCandidateStrategyEnum.ROLE;
|
return BpmTaskCandidateStrategyEnum.ROLE;
|
|
@ -1,15 +1,17 @@
|
||||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
|
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
|
import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
|
||||||
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
|
||||||
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
|
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
|
||||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
import org.flowable.bpmn.model.BpmnModel;
|
||||||
import org.flowable.engine.delegate.DelegateExecution;
|
import org.flowable.engine.delegate.DelegateExecution;
|
||||||
import org.flowable.engine.runtime.ProcessInstance;
|
import org.flowable.engine.runtime.ProcessInstance;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -20,16 +22,12 @@ import java.util.Set;
|
||||||
* @author jason
|
* @author jason
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class BpmTaskCandidateStartUserStrategy extends BpmTaskCandidateAbstractStrategy {
|
public class BpmTaskCandidateStartUserStrategy implements BpmTaskCandidateStrategy {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
@Lazy // 延迟加载,避免循环依赖
|
@Lazy // 延迟加载,避免循环依赖
|
||||||
private BpmProcessInstanceService processInstanceService;
|
private BpmProcessInstanceService processInstanceService;
|
||||||
|
|
||||||
public BpmTaskCandidateStartUserStrategy(AdminUserApi adminUserApi) {
|
|
||||||
super(adminUserApi);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BpmTaskCandidateStrategyEnum getStrategy() {
|
public BpmTaskCandidateStrategyEnum getStrategy() {
|
||||||
return BpmTaskCandidateStrategyEnum.START_USER;
|
return BpmTaskCandidateStrategyEnum.START_USER;
|
||||||
|
@ -45,18 +43,15 @@ public class BpmTaskCandidateStartUserStrategy extends BpmTaskCandidateAbstractS
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<Long> calculateUsers(DelegateExecution execution, String param) {
|
public Set<Long> calculateUsersByTask(DelegateExecution execution, String param) {
|
||||||
ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());
|
ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());
|
||||||
Set<Long> users = SetUtils.asSet(Long.valueOf(processInstance.getStartUserId()));
|
return SetUtils.asSet(Long.valueOf(processInstance.getStartUserId()));
|
||||||
removeDisableUsers(users);
|
|
||||||
return users;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<Long> calculateUsers(Long startUserId, ProcessInstance processInstance, String activityId, String param) {
|
public Set<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param,
|
||||||
Set<Long> users = SetUtils.asSet(startUserId);
|
Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {
|
||||||
removeDisableUsers(users);
|
return SetUtils.asSet(startUserId);
|
||||||
return users;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,14 +1,14 @@
|
||||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
|
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user;
|
||||||
|
|
||||||
import cn.hutool.core.text.StrPool;
|
import cn.hutool.core.text.StrPool;
|
||||||
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
|
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
|
||||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户 {@link BpmTaskCandidateStrategy} 实现类
|
* 用户 {@link BpmTaskCandidateStrategy} 实现类
|
||||||
|
@ -16,11 +16,10 @@ import java.util.Set;
|
||||||
* @author kyle
|
* @author kyle
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class BpmTaskCandidateUserStrategy extends BpmTaskCandidateAbstractStrategy {
|
public class BpmTaskCandidateUserStrategy implements BpmTaskCandidateStrategy {
|
||||||
|
|
||||||
public BpmTaskCandidateUserStrategy(AdminUserApi adminUserApi) {
|
@Resource
|
||||||
super(adminUserApi);
|
private AdminUserApi adminUserApi;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BpmTaskCandidateStrategyEnum getStrategy() {
|
public BpmTaskCandidateStrategyEnum getStrategy() {
|
||||||
|
@ -33,7 +32,7 @@ public class BpmTaskCandidateUserStrategy extends BpmTaskCandidateAbstractStrate
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<Long> calculateUsers(String param) {
|
public LinkedHashSet<Long> calculateUsers(String param) {
|
||||||
return new LinkedHashSet<>(StrUtils.splitToLong(param, StrPool.COMMA));
|
return new LinkedHashSet<>(StrUtils.splitToLong(param, StrPool.COMMA));
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,10 @@ import org.flowable.common.engine.api.variable.VariableContainer;
|
||||||
import org.flowable.common.engine.impl.el.function.AbstractFlowableVariableExpressionFunction;
|
import org.flowable.common.engine.impl.el.function.AbstractFlowableVariableExpressionFunction;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
// TODO @jason:这个自定义转换的原因是啥呀?
|
|
||||||
/**
|
/**
|
||||||
* 根据流程变量 variable 的类型, 转换参数的值
|
* 根据流程变量 variable 的类型,转换参数的值
|
||||||
|
*
|
||||||
|
* 目前用于 ConditionNodeConvert 的 buildConditionExpression 方法中
|
||||||
*
|
*
|
||||||
* @author jason
|
* @author jason
|
||||||
*/
|
*/
|
||||||
|
@ -20,11 +21,12 @@ public class VariableConvertByTypeExpressionFunction extends AbstractFlowableVar
|
||||||
public static Object convertByType(VariableContainer variableContainer, String variableName, Object parmaValue) {
|
public static Object convertByType(VariableContainer variableContainer, String variableName, Object parmaValue) {
|
||||||
Object variable = variableContainer.getVariable(variableName);
|
Object variable = variableContainer.getVariable(variableName);
|
||||||
if (variable != null && parmaValue != null) {
|
if (variable != null && parmaValue != null) {
|
||||||
// 如果值不是字符串类型, 流程变量的类型是字符串。 把值转成字符串
|
// 如果值不是字符串类型,流程变量的类型是字符串,把值转成字符串
|
||||||
if (!(parmaValue instanceof String) && variable instanceof String ) {
|
if (!(parmaValue instanceof String) && variable instanceof String ) {
|
||||||
return parmaValue.toString();
|
return parmaValue.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return parmaValue;
|
return parmaValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,8 @@ public enum BpmTaskCandidateStrategyEnum implements IntArrayValuable {
|
||||||
START_USER_DEPT_LEADER(37, "发起人部门负责人"),
|
START_USER_DEPT_LEADER(37, "发起人部门负责人"),
|
||||||
START_USER_DEPT_LEADER_MULTI(38, "发起人连续多级部门的负责人"),
|
START_USER_DEPT_LEADER_MULTI(38, "发起人连续多级部门的负责人"),
|
||||||
USER_GROUP(40, "用户组"),
|
USER_GROUP(40, "用户组"),
|
||||||
|
FORM_USER(50, "表单内用户字段"),
|
||||||
|
FORM_DEPT_LEADER(51, "表单内部门负责人"),
|
||||||
EXPRESSION(60, "流程表达式"), // 表达式 ExpressionManager
|
EXPRESSION(60, "流程表达式"), // 表达式 ExpressionManager
|
||||||
ASSIGN_EMPTY(1, "审批人为空"),
|
ASSIGN_EMPTY(1, "审批人为空"),
|
||||||
;
|
;
|
||||||
|
|
|
@ -52,7 +52,7 @@ public interface BpmnModelConstants {
|
||||||
*/
|
*/
|
||||||
String USER_TASK_REJECT_HANDLER_TYPE = "rejectHandlerType";
|
String USER_TASK_REJECT_HANDLER_TYPE = "rejectHandlerType";
|
||||||
/**
|
/**
|
||||||
* BPMN ExtensionElement 的扩展属性,用于标记用户任务拒绝后的回退的任务 Id
|
* BPMN ExtensionElement 的扩展属性,用于标记用户任务拒绝后的退回的任务 Id
|
||||||
*/
|
*/
|
||||||
String USER_TASK_REJECT_RETURN_TASK_ID = "rejectReturnTaskId";
|
String USER_TASK_REJECT_RETURN_TASK_ID = "rejectReturnTaskId";
|
||||||
|
|
||||||
|
@ -104,10 +104,6 @@ public interface BpmnModelConstants {
|
||||||
* BPMN Start Event Node Id
|
* BPMN Start Event Node Id
|
||||||
*/
|
*/
|
||||||
String START_EVENT_NODE_ID = "StartEvent";
|
String START_EVENT_NODE_ID = "StartEvent";
|
||||||
/**
|
|
||||||
* BPMN Start Event Node Name
|
|
||||||
*/
|
|
||||||
String START_EVENT_NODE_NAME = "开始";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发起人节点 ID
|
* 发起人节点 ID
|
||||||
|
|
|
@ -29,7 +29,12 @@ public class BpmnVariableConstants {
|
||||||
* @see ProcessInstance#getProcessVariables()
|
* @see ProcessInstance#getProcessVariables()
|
||||||
*/
|
*/
|
||||||
public static final String PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES = "PROCESS_START_USER_SELECT_ASSIGNEES";
|
public static final String PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES = "PROCESS_START_USER_SELECT_ASSIGNEES";
|
||||||
|
/**
|
||||||
|
* 流程实例的变量 - 发起用户 ID
|
||||||
|
*
|
||||||
|
* @see ProcessInstance#getProcessVariables()
|
||||||
|
*/
|
||||||
|
public static final String PROCESS_INSTANCE_VARIABLE_START_USER_ID = "PROCESS_START_USER_ID";
|
||||||
/**
|
/**
|
||||||
* 流程实例的变量 - 用于判断流程实例变量节点是否驳回. 格式 RETURN_FLAG_{节点 id}
|
* 流程实例的变量 - 用于判断流程实例变量节点是否驳回. 格式 RETURN_FLAG_{节点 id}
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.enums;
|
|
||||||
|
|
||||||
// TODO @jason:要不合并到 BpmnModelConstants 那
|
|
||||||
/**
|
|
||||||
* 仿钉钉快搭 JSON 常量信息
|
|
||||||
*
|
|
||||||
* @author jason
|
|
||||||
*/
|
|
||||||
public interface SimpleModelConstants {
|
|
||||||
|
|
||||||
// TODO @芋艿:条件表达式的字段名
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 网关节点默认序列流属性
|
|
||||||
*/
|
|
||||||
String DEFAULT_FLOW_ATTRIBUTE = "defaultFlow";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 条件节点的条件类型属性
|
|
||||||
*/
|
|
||||||
String CONDITION_TYPE_ATTRIBUTE = "conditionType";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 条件节点条件表达式属性
|
|
||||||
*/
|
|
||||||
String CONDITION_EXPRESSION_ATTRIBUTE = "conditionExpression";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 条件规则的条件组属性
|
|
||||||
*/
|
|
||||||
String CONDITION_GROUPS_ATTRIBUTE = "conditionGroups";
|
|
||||||
|
|
||||||
}
|
|
|
@ -15,8 +15,8 @@ import static cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmCo
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理抄送用户的 {@link JavaDelegate} 的实现类
|
* 处理抄送用户的 {@link JavaDelegate} 的实现类
|
||||||
*
|
* <p>
|
||||||
* 目前只有快搭模式的【抄送节点】使用
|
* 目前只有仿钉钉/飞书模式的【抄送节点】使用
|
||||||
*
|
*
|
||||||
* @author jason
|
* @author jason
|
||||||
*/
|
*/
|
||||||
|
@ -34,14 +34,14 @@ public class BpmCopyTaskDelegate implements JavaDelegate {
|
||||||
@Override
|
@Override
|
||||||
public void execute(DelegateExecution execution) {
|
public void execute(DelegateExecution execution) {
|
||||||
// 1. 获得抄送人
|
// 1. 获得抄送人
|
||||||
Set<Long> userIds = taskCandidateInvoker.calculateUsers(execution);
|
Set<Long> userIds = taskCandidateInvoker.calculateUsersByTask(execution);
|
||||||
if (CollUtil.isEmpty(userIds)) {
|
if (CollUtil.isEmpty(userIds)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 2. 执行抄送
|
// 2. 执行抄送
|
||||||
FlowElement currentFlowElement = execution.getCurrentFlowElement();
|
FlowElement currentFlowElement = execution.getCurrentFlowElement();
|
||||||
processInstanceCopyService.createProcessInstanceCopy(userIds, execution.getProcessInstanceId(),
|
processInstanceCopyService.createProcessInstanceCopy(userIds, null, execution.getProcessInstanceId(),
|
||||||
currentFlowElement.getId(), null, currentFlowElement.getName());
|
currentFlowElement.getId(), currentFlowElement.getName(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ import cn.iocoder.yudao.module.bpm.enums.definition.BpmBoundaryEventType;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
|
||||||
import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService;
|
import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService;
|
||||||
import cn.iocoder.yudao.module.bpm.service.task.BpmActivityService;
|
|
||||||
import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;
|
import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
@ -44,9 +43,6 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener {
|
||||||
@Resource
|
@Resource
|
||||||
@Lazy // 解决循环依赖
|
@Lazy // 解决循环依赖
|
||||||
private BpmTaskService taskService;
|
private BpmTaskService taskService;
|
||||||
@Resource
|
|
||||||
@Lazy // 解决循环依赖
|
|
||||||
private BpmActivityService activityService;
|
|
||||||
|
|
||||||
public static final Set<FlowableEngineEventType> TASK_EVENTS = ImmutableSet.<FlowableEngineEventType>builder()
|
public static final Set<FlowableEngineEventType> TASK_EVENTS = ImmutableSet.<FlowableEngineEventType>builder()
|
||||||
.add(FlowableEngineEventType.TASK_CREATED)
|
.add(FlowableEngineEventType.TASK_CREATED)
|
||||||
|
@ -72,7 +68,7 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void activityCancelled(FlowableActivityCancelledEvent event) {
|
protected void activityCancelled(FlowableActivityCancelledEvent event) {
|
||||||
List<HistoricActivityInstance> activityList = activityService.getHistoricActivityListByExecutionId(event.getExecutionId());
|
List<HistoricActivityInstance> activityList = taskService.getHistoricActivityListByExecutionId(event.getExecutionId());
|
||||||
if (CollUtil.isEmpty(activityList)) {
|
if (CollUtil.isEmpty(activityList)) {
|
||||||
log.error("[activityCancelled][使用 executionId({}) 查找不到对应的活动实例]", event.getExecutionId());
|
log.error("[activityCancelled][使用 executionId({}) 查找不到对应的活动实例]", event.getExecutionId());
|
||||||
return;
|
return;
|
||||||
|
@ -87,6 +83,7 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SuppressWarnings("PatternVariableCanBeUsed")
|
||||||
protected void timerFired(FlowableEngineEntityEvent event) {
|
protected void timerFired(FlowableEngineEntityEvent event) {
|
||||||
// 1.1 只处理 BoundaryEvent 边界计时时间
|
// 1.1 只处理 BoundaryEvent 边界计时时间
|
||||||
String processDefinitionId = event.getProcessDefinitionId();
|
String processDefinitionId = event.getProcessDefinitionId();
|
||||||
|
|
|
@ -3,27 +3,116 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.util;
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.map.MapUtil;
|
import cn.hutool.core.map.MapUtil;
|
||||||
import cn.hutool.core.util.ArrayUtil;
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
|
import cn.hutool.core.util.ObjUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||||
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
|
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
|
||||||
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
|
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
|
||||||
|
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
|
||||||
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
|
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
|
||||||
|
import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskApproveTypeEnum;
|
||||||
|
import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignEmptyHandlerTypeEnum;
|
||||||
|
import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignStartUserHandlerTypeEnum;
|
||||||
import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskRejectHandlerType;
|
import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskRejectHandlerType;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.flowable.bpmn.converter.BpmnXMLConverter;
|
import org.flowable.bpmn.converter.BpmnXMLConverter;
|
||||||
import org.flowable.bpmn.model.Process;
|
import org.flowable.bpmn.model.Process;
|
||||||
import org.flowable.bpmn.model.*;
|
import org.flowable.bpmn.model.*;
|
||||||
|
import org.flowable.common.engine.api.FlowableException;
|
||||||
import org.flowable.common.engine.impl.util.io.BytesStreamSource;
|
import org.flowable.common.engine.impl.util.io.BytesStreamSource;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*;
|
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*;
|
||||||
import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_NAMESPACE;
|
import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_NAMESPACE;
|
||||||
|
import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_PREFIX;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 流程模型转操作工具类
|
* BPMN Model 操作工具类。目前分成三部分:
|
||||||
|
*
|
||||||
|
* 1. BPMN 修改 + 解析元素相关的方法
|
||||||
|
* 2. BPMN 简单查找相关的方法
|
||||||
|
* 3. BPMN 复杂遍历相关的方法
|
||||||
|
* 4. BPMN 流程预测相关的方法
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
public class BpmnModelUtils {
|
public class BpmnModelUtils {
|
||||||
|
|
||||||
|
// ========== BPMN 修改 + 解析元素相关的方法 ==========
|
||||||
|
|
||||||
|
public static void addExtensionElement(FlowElement element, String name, String value) {
|
||||||
|
if (value == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ExtensionElement extensionElement = new ExtensionElement();
|
||||||
|
extensionElement.setNamespace(FLOWABLE_EXTENSIONS_NAMESPACE);
|
||||||
|
extensionElement.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX);
|
||||||
|
extensionElement.setElementText(value);
|
||||||
|
extensionElement.setName(name);
|
||||||
|
element.addExtensionElement(extensionElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addExtensionElement(FlowElement element, String name, Integer value) {
|
||||||
|
if (value == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
addExtensionElement(element, name, String.valueOf(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addExtensionElement(FlowElement element, String name, Map<String, String> attributes) {
|
||||||
|
if (attributes == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ExtensionElement extensionElement = new ExtensionElement();
|
||||||
|
extensionElement.setNamespace(FLOWABLE_EXTENSIONS_NAMESPACE);
|
||||||
|
extensionElement.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX);
|
||||||
|
extensionElement.setName(name);
|
||||||
|
attributes.forEach((key, value) -> {
|
||||||
|
ExtensionAttribute extensionAttribute = new ExtensionAttribute(key, value);
|
||||||
|
extensionAttribute.setNamespace(FLOWABLE_EXTENSIONS_NAMESPACE);
|
||||||
|
extensionElement.addAttribute(extensionAttribute);
|
||||||
|
});
|
||||||
|
element.addExtensionElement(extensionElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析扩展元素
|
||||||
|
*
|
||||||
|
* @param flowElement 节点
|
||||||
|
* @param elementName 元素名称
|
||||||
|
* @return 扩展元素
|
||||||
|
*/
|
||||||
|
public static String parseExtensionElement(FlowElement flowElement, String elementName) {
|
||||||
|
if (flowElement == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ExtensionElement element = CollUtil.getFirst(flowElement.getExtensionElements().get(elementName));
|
||||||
|
return element != null ? element.getElementText() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 给节点添加候选人元素
|
||||||
|
*
|
||||||
|
* @param candidateStrategy 候选人策略
|
||||||
|
* @param candidateParam 候选人参数,允许空
|
||||||
|
* @param flowElement 节点
|
||||||
|
*/
|
||||||
|
public static void addCandidateElements(Integer candidateStrategy, String candidateParam, FlowElement flowElement) {
|
||||||
|
addExtensionElement(flowElement, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY,
|
||||||
|
candidateStrategy == null ? null : candidateStrategy.toString());
|
||||||
|
addExtensionElement(flowElement, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM, candidateParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析候选人策略
|
||||||
|
*
|
||||||
|
* @param userTask 任务节点
|
||||||
|
* @return 候选人策略
|
||||||
|
*/
|
||||||
public static Integer parseCandidateStrategy(FlowElement userTask) {
|
public static Integer parseCandidateStrategy(FlowElement userTask) {
|
||||||
Integer candidateStrategy = NumberUtils.parseInt(userTask.getAttributeValue(
|
Integer candidateStrategy = NumberUtils.parseInt(userTask.getAttributeValue(
|
||||||
BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY));
|
BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY));
|
||||||
|
@ -35,6 +124,12 @@ public class BpmnModelUtils {
|
||||||
return candidateStrategy;
|
return candidateStrategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析候选人参数
|
||||||
|
*
|
||||||
|
* @param userTask 任务节点
|
||||||
|
* @return 候选人参数
|
||||||
|
*/
|
||||||
public static String parseCandidateParam(FlowElement userTask) {
|
public static String parseCandidateParam(FlowElement userTask) {
|
||||||
String candidateParam = userTask.getAttributeValue(
|
String candidateParam = userTask.getAttributeValue(
|
||||||
BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM);
|
BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM);
|
||||||
|
@ -45,39 +140,130 @@ public class BpmnModelUtils {
|
||||||
return candidateParam;
|
return candidateParam;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析审批类型
|
||||||
|
*
|
||||||
|
* @see BpmUserTaskApproveTypeEnum
|
||||||
|
* @param userTask 任务节点
|
||||||
|
* @return 审批类型
|
||||||
|
*/
|
||||||
public static Integer parseApproveType(FlowElement userTask) {
|
public static Integer parseApproveType(FlowElement userTask) {
|
||||||
return NumberUtils.parseInt(parseExtensionElement(userTask, BpmnModelConstants.USER_TASK_APPROVE_TYPE));
|
return NumberUtils.parseInt(parseExtensionElement(userTask, BpmnModelConstants.USER_TASK_APPROVE_TYPE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加任务拒绝处理元素
|
||||||
|
*
|
||||||
|
* @param rejectHandler 任务拒绝处理
|
||||||
|
* @param userTask 任务节点
|
||||||
|
*/
|
||||||
|
public static void addTaskRejectElements(BpmSimpleModelNodeVO.RejectHandler rejectHandler, UserTask userTask) {
|
||||||
|
if (rejectHandler == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
addExtensionElement(userTask, USER_TASK_REJECT_HANDLER_TYPE, StrUtil.toStringOrNull(rejectHandler.getType()));
|
||||||
|
addExtensionElement(userTask, USER_TASK_REJECT_RETURN_TASK_ID, rejectHandler.getReturnNodeId());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析任务拒绝处理类型
|
||||||
|
*
|
||||||
|
* @param userTask 任务节点
|
||||||
|
* @return 任务拒绝处理类型
|
||||||
|
*/
|
||||||
public static BpmUserTaskRejectHandlerType parseRejectHandlerType(FlowElement userTask) {
|
public static BpmUserTaskRejectHandlerType parseRejectHandlerType(FlowElement userTask) {
|
||||||
Integer rejectHandlerType = NumberUtils.parseInt(parseExtensionElement(userTask, USER_TASK_REJECT_HANDLER_TYPE));
|
Integer rejectHandlerType = NumberUtils.parseInt(parseExtensionElement(userTask, USER_TASK_REJECT_HANDLER_TYPE));
|
||||||
return BpmUserTaskRejectHandlerType.typeOf(rejectHandlerType);
|
return BpmUserTaskRejectHandlerType.typeOf(rejectHandlerType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析任务拒绝返回任务节点 ID
|
||||||
|
*
|
||||||
|
* @param flowElement 任务节点
|
||||||
|
* @return 任务拒绝返回任务节点 ID
|
||||||
|
*/
|
||||||
public static String parseReturnTaskId(FlowElement flowElement) {
|
public static String parseReturnTaskId(FlowElement flowElement) {
|
||||||
return parseExtensionElement(flowElement, USER_TASK_REJECT_RETURN_TASK_ID);
|
return parseExtensionElement(flowElement, USER_TASK_REJECT_RETURN_TASK_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 给节点添加用户任务的审批人与发起人相同时,处理类型枚举
|
||||||
|
*
|
||||||
|
* @see BpmUserTaskAssignStartUserHandlerTypeEnum
|
||||||
|
* @param assignStartUserHandlerType 发起人处理类型
|
||||||
|
* @param userTask 任务节点
|
||||||
|
*/
|
||||||
|
public static void addAssignStartUserHandlerType(Integer assignStartUserHandlerType, UserTask userTask) {
|
||||||
|
if (assignStartUserHandlerType == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
addExtensionElement(userTask, USER_TASK_ASSIGN_START_USER_HANDLER_TYPE, assignStartUserHandlerType.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 给节点添加用户任务的审批人为空时,处理类型枚举
|
||||||
|
*
|
||||||
|
* @see BpmUserTaskAssignEmptyHandlerTypeEnum
|
||||||
|
* @param emptyHandler 空处理
|
||||||
|
* @param userTask 任务节点
|
||||||
|
*/
|
||||||
|
public static void addAssignEmptyHandlerType(BpmSimpleModelNodeVO.AssignEmptyHandler emptyHandler, UserTask userTask) {
|
||||||
|
if (emptyHandler == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
addExtensionElement(userTask, USER_TASK_ASSIGN_EMPTY_HANDLER_TYPE, StrUtil.toStringOrNull(emptyHandler.getType()));
|
||||||
|
addExtensionElement(userTask, USER_TASK_ASSIGN_USER_IDS, StrUtil.join(",", emptyHandler.getUserIds()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析用户任务的审批人与发起人相同时,处理类型枚举
|
||||||
|
*
|
||||||
|
* @param userTask 任务节点
|
||||||
|
* @return 处理类型枚举
|
||||||
|
*/
|
||||||
public static Integer parseAssignStartUserHandlerType(FlowElement userTask) {
|
public static Integer parseAssignStartUserHandlerType(FlowElement userTask) {
|
||||||
return NumberUtils.parseInt(parseExtensionElement(userTask, USER_TASK_ASSIGN_START_USER_HANDLER_TYPE));
|
return NumberUtils.parseInt(parseExtensionElement(userTask, USER_TASK_ASSIGN_START_USER_HANDLER_TYPE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析用户任务的审批人为空时,处理类型枚举
|
||||||
|
*
|
||||||
|
* @param userTask 任务节点
|
||||||
|
* @return 处理类型枚举
|
||||||
|
*/
|
||||||
public static Integer parseAssignEmptyHandlerType(FlowElement userTask) {
|
public static Integer parseAssignEmptyHandlerType(FlowElement userTask) {
|
||||||
return NumberUtils.parseInt(parseExtensionElement(userTask, USER_TASK_ASSIGN_EMPTY_HANDLER_TYPE));
|
return NumberUtils.parseInt(parseExtensionElement(userTask, USER_TASK_ASSIGN_EMPTY_HANDLER_TYPE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析用户任务的审批人为空时,处理用户 ID 数组
|
||||||
|
*
|
||||||
|
* @param userTask 任务节点
|
||||||
|
* @return 处理用户 ID 数组
|
||||||
|
*/
|
||||||
public static List<Long> parseAssignEmptyHandlerUserIds(FlowElement userTask) {
|
public static List<Long> parseAssignEmptyHandlerUserIds(FlowElement userTask) {
|
||||||
return StrUtils.splitToLong(parseExtensionElement(userTask, USER_TASK_ASSIGN_USER_IDS), ",");
|
return StrUtils.splitToLong(parseExtensionElement(userTask, USER_TASK_ASSIGN_USER_IDS), ",");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String parseExtensionElement(FlowElement flowElement, String elementName) {
|
/**
|
||||||
if (flowElement == null) {
|
* 给节点添加表单字段权限元素
|
||||||
return null;
|
*
|
||||||
|
* @param fieldsPermissions 表单字段权限
|
||||||
|
* @param flowElement 节点
|
||||||
|
*/
|
||||||
|
public static void addFormFieldsPermission(List<Map<String, String>> fieldsPermissions, FlowElement flowElement) {
|
||||||
|
if (CollUtil.isNotEmpty(fieldsPermissions)) {
|
||||||
|
fieldsPermissions.forEach(item -> addExtensionElement(flowElement, FORM_FIELD_PERMISSION_ELEMENT, item));
|
||||||
}
|
}
|
||||||
ExtensionElement element = CollUtil.getFirst(flowElement.getExtensionElements().get(elementName));
|
|
||||||
return element != null ? element.getElementText() : null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析表单字段权限
|
||||||
|
*
|
||||||
|
* @param bpmnModel bpmnModel 对象
|
||||||
|
* @param flowElementId 元素 ID
|
||||||
|
* @return 表单字段权限
|
||||||
|
*/
|
||||||
public static Map<String, String> parseFormFieldsPermission(BpmnModel bpmnModel, String flowElementId) {
|
public static Map<String, String> parseFormFieldsPermission(BpmnModel bpmnModel, String flowElementId) {
|
||||||
if (bpmnModel == null || StrUtil.isEmpty(flowElementId)) {
|
if (bpmnModel == null || StrUtil.isEmpty(flowElementId)) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -101,6 +287,29 @@ public class BpmnModelUtils {
|
||||||
return fieldsPermission;
|
return fieldsPermission;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 给节点添加操作按钮设置元素
|
||||||
|
*/
|
||||||
|
public static void addButtonsSetting(List<BpmSimpleModelNodeVO.OperationButtonSetting> buttonsSetting, UserTask userTask) {
|
||||||
|
if (CollUtil.isNotEmpty(buttonsSetting)) {
|
||||||
|
List<Map<String, String>> list = CollectionUtils.convertList(buttonsSetting, item -> {
|
||||||
|
Map<String, String> settingMap = Maps.newHashMapWithExpectedSize(3);
|
||||||
|
settingMap.put(BUTTON_SETTING_ELEMENT_ID_ATTRIBUTE, String.valueOf(item.getId()));
|
||||||
|
settingMap.put(BUTTON_SETTING_ELEMENT_DISPLAY_NAME_ATTRIBUTE, item.getDisplayName());
|
||||||
|
settingMap.put(BUTTON_SETTING_ELEMENT_ENABLE_ATTRIBUTE, String.valueOf(item.getEnable()));
|
||||||
|
return settingMap;
|
||||||
|
});
|
||||||
|
list.forEach(item -> addExtensionElement(userTask, BUTTON_SETTING_ELEMENT, item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析操作按钮设置
|
||||||
|
*
|
||||||
|
* @param bpmnModel bpmnModel 对象
|
||||||
|
* @param flowElementId 元素 ID
|
||||||
|
* @return 操作按钮设置
|
||||||
|
*/
|
||||||
public static Map<Integer, BpmTaskRespVO.OperationButtonSetting> parseButtonsSetting(BpmnModel bpmnModel, String flowElementId) {
|
public static Map<Integer, BpmTaskRespVO.OperationButtonSetting> parseButtonsSetting(BpmnModel bpmnModel, String flowElementId) {
|
||||||
FlowElement flowElement = getFlowElementById(bpmnModel, flowElementId);
|
FlowElement flowElement = getFlowElementById(bpmnModel, flowElementId);
|
||||||
if (flowElement == null) {
|
if (flowElement == null) {
|
||||||
|
@ -110,7 +319,7 @@ public class BpmnModelUtils {
|
||||||
if (CollUtil.isEmpty(extensionElements)) {
|
if (CollUtil.isEmpty(extensionElements)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Map<Integer, BpmTaskRespVO.OperationButtonSetting> buttonSettings = MapUtil.newHashMap(16);
|
Map<Integer, BpmTaskRespVO.OperationButtonSetting> buttonSettings = Maps.newHashMapWithExpectedSize(extensionElements.size());
|
||||||
extensionElements.forEach(element -> {
|
extensionElements.forEach(element -> {
|
||||||
String id = element.getAttributeValue(FLOWABLE_EXTENSIONS_NAMESPACE, BUTTON_SETTING_ELEMENT_ID_ATTRIBUTE);
|
String id = element.getAttributeValue(FLOWABLE_EXTENSIONS_NAMESPACE, BUTTON_SETTING_ELEMENT_ID_ATTRIBUTE);
|
||||||
String displayName = element.getAttributeValue(FLOWABLE_EXTENSIONS_NAMESPACE, BUTTON_SETTING_ELEMENT_DISPLAY_NAME_ATTRIBUTE);
|
String displayName = element.getAttributeValue(FLOWABLE_EXTENSIONS_NAMESPACE, BUTTON_SETTING_ELEMENT_DISPLAY_NAME_ATTRIBUTE);
|
||||||
|
@ -123,6 +332,23 @@ public class BpmnModelUtils {
|
||||||
return buttonSettings;
|
return buttonSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析边界事件扩展元素
|
||||||
|
*
|
||||||
|
* @param boundaryEvent 边界事件
|
||||||
|
* @param customElement 元素
|
||||||
|
* @return 扩展元素
|
||||||
|
*/
|
||||||
|
public static String parseBoundaryEventExtensionElement(BoundaryEvent boundaryEvent, String customElement) {
|
||||||
|
if (boundaryEvent == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ExtensionElement extensionElement = CollUtil.getFirst(boundaryEvent.getExtensionElements().get(customElement));
|
||||||
|
return Optional.ofNullable(extensionElement).map(ExtensionElement::getElementText).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== BPM 简单查找相关的方法 ==========
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据节点,获取入口连线
|
* 根据节点,获取入口连线
|
||||||
*
|
*
|
||||||
|
@ -168,15 +394,14 @@ public class BpmnModelUtils {
|
||||||
* @param clazz 指定元素。例如说,{@link UserTask}、{@link Gateway} 等等
|
* @param clazz 指定元素。例如说,{@link UserTask}、{@link Gateway} 等等
|
||||||
* @return 元素们
|
* @return 元素们
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public static <T extends FlowElement> List<T> getBpmnModelElements(BpmnModel model, Class<T> clazz) {
|
public static <T extends FlowElement> List<T> getBpmnModelElements(BpmnModel model, Class<T> clazz) {
|
||||||
List<T> result = new ArrayList<>();
|
List<T> result = new ArrayList<>();
|
||||||
model.getProcesses().forEach(process -> {
|
model.getProcesses().forEach(process -> process.getFlowElements().forEach(flowElement -> {
|
||||||
process.getFlowElements().forEach(flowElement -> {
|
if (flowElement.getClass().isAssignableFrom(clazz)) {
|
||||||
if (flowElement.getClass().isAssignableFrom(clazz)) {
|
result.add((T) flowElement);
|
||||||
result.add((T) flowElement);
|
}
|
||||||
}
|
}));
|
||||||
});
|
|
||||||
});
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,7 +418,7 @@ public class BpmnModelUtils {
|
||||||
|
|
||||||
public static EndEvent getEndEvent(BpmnModel model) {
|
public static EndEvent getEndEvent(BpmnModel model) {
|
||||||
Process process = model.getMainProcess();
|
Process process = model.getMainProcess();
|
||||||
// 从 flowElementList 找 endEvent. TODO 多个 EndEvent 会有问题
|
// 从 flowElementList 找 endEvent
|
||||||
return (EndEvent) CollUtil.findOne(process.getFlowElements(), flowElement -> flowElement instanceof EndEvent);
|
return (EndEvent) CollUtil.findOne(process.getFlowElements(), flowElement -> flowElement instanceof EndEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -221,7 +446,7 @@ public class BpmnModelUtils {
|
||||||
return StrUtil.utf8Str(bpmnBytes);
|
return StrUtil.utf8Str(bpmnBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== 遍历相关的方法 ==========
|
// ========== BPMN 复杂遍历相关的方法 ==========
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 找到 source 节点之前的所有用户任务节点
|
* 找到 source 节点之前的所有用户任务节点
|
||||||
|
@ -316,16 +541,16 @@ public class BpmnModelUtils {
|
||||||
return userTaskList;
|
return userTaskList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 迭代从后向前扫描,判断目标节点相对于当前节点是否是串行
|
* 迭代从后向前扫描,判断目标节点相对于当前节点是否是串行
|
||||||
* 不存在直接回退到子流程中的情况,但存在从子流程出去到父流程情况
|
* 不存在直接退回到子流程中的情况,但存在从子流程出去到父流程情况
|
||||||
*
|
*
|
||||||
* @param source 起始节点
|
* @param source 起始节点
|
||||||
* @param target 目标节点
|
* @param target 目标节点
|
||||||
* @param visitedElements 已经经过的连线的 ID,用于判断线路是否重复
|
* @param visitedElements 已经经过的连线的 ID,用于判断线路是否重复
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||||
public static boolean isSequentialReachable(FlowElement source, FlowElement target, Set<String> visitedElements) {
|
public static boolean isSequentialReachable(FlowElement source, FlowElement target, Set<String> visitedElements) {
|
||||||
visitedElements = visitedElements == null ? new HashSet<>() : visitedElements;
|
visitedElements = visitedElements == null ? new HashSet<>() : visitedElements;
|
||||||
// 不能是开始事件和子流程
|
// 不能是开始事件和子流程
|
||||||
|
@ -436,11 +661,136 @@ public class BpmnModelUtils {
|
||||||
return userTaskList;
|
return userTaskList;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String parseBoundaryEventExtensionElement(BoundaryEvent boundaryEvent, String customElement) {
|
// ========== BPMN 流程预测相关的方法 ==========
|
||||||
if (boundaryEvent == null) {
|
|
||||||
return null;
|
/**
|
||||||
}
|
* 流程预测,返回 StartEvent、UserTask、ServiceTask、EndEvent 节点元素,最终是 List 串行结果
|
||||||
ExtensionElement extensionElement = CollUtil.getFirst(boundaryEvent.getExtensionElements().get(customElement));
|
*
|
||||||
return Optional.ofNullable(extensionElement).map(ExtensionElement::getElementText).orElse(null);
|
* @param bpmnModel BPMN 图
|
||||||
|
* @param variables 变量
|
||||||
|
* @return 节点元素数组
|
||||||
|
*/
|
||||||
|
public static List<FlowElement> simulateProcess(BpmnModel bpmnModel, Map<String, Object> variables) {
|
||||||
|
List<FlowElement> resultElements = new ArrayList<>();
|
||||||
|
Set<FlowElement> visitElements = new HashSet<>();
|
||||||
|
|
||||||
|
// 从 StartEvent 开始遍历
|
||||||
|
StartEvent startEvent = getStartEvent(bpmnModel);
|
||||||
|
simulateNextFlowElements(startEvent, variables, resultElements, visitElements);
|
||||||
|
|
||||||
|
// 将 EndEvent 放在末尾。原因是,DFS 遍历,可能 EndEvent 在 resultElements 中
|
||||||
|
List<FlowElement> endEvents = CollUtil.removeWithAddIf(resultElements,
|
||||||
|
flowElement -> flowElement instanceof EndEvent);
|
||||||
|
resultElements.addAll(endEvents);
|
||||||
|
return resultElements;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("PatternVariableCanBeUsed")
|
||||||
|
private static void simulateNextFlowElements(FlowElement currentElement, Map<String, Object> variables,
|
||||||
|
List<FlowElement> resultElements, Set<FlowElement> visitElements) {
|
||||||
|
// 如果为空,或者已经遍历过,则直接结束
|
||||||
|
if (currentElement == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (visitElements.contains(currentElement)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
visitElements.add(currentElement);
|
||||||
|
|
||||||
|
// 情况:StartEvent/EndEvent/UserTask/ServiceTask
|
||||||
|
if (currentElement instanceof StartEvent
|
||||||
|
|| currentElement instanceof EndEvent
|
||||||
|
|| currentElement instanceof UserTask
|
||||||
|
|| currentElement instanceof ServiceTask) {
|
||||||
|
// 添加元素
|
||||||
|
FlowNode flowNode = (FlowNode) currentElement;
|
||||||
|
resultElements.add(flowNode);
|
||||||
|
// 遍历子节点
|
||||||
|
flowNode.getOutgoingFlows().forEach(
|
||||||
|
nextElement -> simulateNextFlowElements(nextElement.getTargetFlowElement(), variables, resultElements, visitElements));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 情况:ExclusiveGateway 排它,只有一个满足条件的。如果没有,就走默认的
|
||||||
|
if (currentElement instanceof ExclusiveGateway) {
|
||||||
|
// 查找满足条件的 SequenceFlow 路径
|
||||||
|
Gateway gateway = (Gateway) currentElement;
|
||||||
|
SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(),
|
||||||
|
flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId())
|
||||||
|
&& evalConditionExpress(variables, flow.getConditionExpression()));
|
||||||
|
if (matchSequenceFlow == null) {
|
||||||
|
matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(),
|
||||||
|
flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()));
|
||||||
|
// 特殊:没有默认的情况下,并且只有 1 个条件,则认为它是默认的
|
||||||
|
if (matchSequenceFlow == null && gateway.getOutgoingFlows().size() == 1) {
|
||||||
|
matchSequenceFlow = gateway.getOutgoingFlows().get(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 遍历满足条件的 SequenceFlow 路径
|
||||||
|
if (matchSequenceFlow != null) {
|
||||||
|
simulateNextFlowElements(matchSequenceFlow.getTargetFlowElement(), variables, resultElements, visitElements);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 情况:InclusiveGateway 包容,多个满足条件的。如果没有,就走默认的
|
||||||
|
if (currentElement instanceof InclusiveGateway) {
|
||||||
|
// 查找满足条件的 SequenceFlow 路径
|
||||||
|
Gateway gateway = (Gateway) currentElement;
|
||||||
|
Collection<SequenceFlow> matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(),
|
||||||
|
flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId())
|
||||||
|
&& evalConditionExpress(variables, flow.getConditionExpression()));
|
||||||
|
if (CollUtil.isEmpty(matchSequenceFlows)) {
|
||||||
|
matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(),
|
||||||
|
flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()));
|
||||||
|
// 特殊:没有默认的情况下,并且只有 1 个条件,则认为它是默认的
|
||||||
|
if (CollUtil.isEmpty(matchSequenceFlows) && gateway.getOutgoingFlows().size() == 1) {
|
||||||
|
matchSequenceFlows = gateway.getOutgoingFlows();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 遍历满足条件的 SequenceFlow 路径
|
||||||
|
matchSequenceFlows.forEach(
|
||||||
|
flow -> simulateNextFlowElements(flow.getTargetFlowElement(), variables, resultElements, visitElements));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 情况:ParallelGateway 并行,都满足,都走
|
||||||
|
if (currentElement instanceof ParallelGateway) {
|
||||||
|
Gateway gateway = (Gateway) currentElement;
|
||||||
|
// 遍历子节点
|
||||||
|
gateway.getOutgoingFlows().forEach(
|
||||||
|
nextElement -> simulateNextFlowElements(nextElement.getTargetFlowElement(), variables, resultElements, visitElements));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算条件表达式是否为 true 满足条件
|
||||||
|
*
|
||||||
|
* @param variables 流程实例
|
||||||
|
* @param express 条件表达式
|
||||||
|
* @return 是否满足条件
|
||||||
|
*/
|
||||||
|
public static boolean evalConditionExpress(Map<String, Object> variables, String express) {
|
||||||
|
if (express == null) {
|
||||||
|
return Boolean.FALSE;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Object result = FlowableUtils.getExpressionValue(variables, express);
|
||||||
|
return Boolean.TRUE.equals(result);
|
||||||
|
} catch (FlowableException ex) {
|
||||||
|
log.error("[evalConditionExpress][条件表达式({}) 变量({}) 解析报错", express, variables, ex);
|
||||||
|
return Boolean.FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("PatternVariableCanBeUsed")
|
||||||
|
public static boolean isSequentialUserTask(FlowElement flowElement) {
|
||||||
|
if (!(flowElement instanceof UserTask)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
UserTask userTask = (UserTask) flowElement;
|
||||||
|
MultiInstanceLoopCharacteristics loopCharacteristics = userTask.getLoopCharacteristics();
|
||||||
|
return loopCharacteristics != null && loopCharacteristics.isSequential();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.util;
|
package cn.iocoder.yudao.module.bpm.framework.flowable.core.util;
|
||||||
|
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import cn.hutool.extra.spring.SpringUtil;
|
||||||
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
||||||
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
|
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants;
|
||||||
|
@ -8,6 +9,8 @@ import org.flowable.common.engine.api.delegate.Expression;
|
||||||
import org.flowable.common.engine.api.variable.VariableContainer;
|
import org.flowable.common.engine.api.variable.VariableContainer;
|
||||||
import org.flowable.common.engine.impl.el.ExpressionManager;
|
import org.flowable.common.engine.impl.el.ExpressionManager;
|
||||||
import org.flowable.common.engine.impl.identity.Authentication;
|
import org.flowable.common.engine.impl.identity.Authentication;
|
||||||
|
import org.flowable.common.engine.impl.variable.MapDelegateVariableContainer;
|
||||||
|
import org.flowable.engine.ManagementService;
|
||||||
import org.flowable.engine.ProcessEngineConfiguration;
|
import org.flowable.engine.ProcessEngineConfiguration;
|
||||||
import org.flowable.engine.history.HistoricProcessInstance;
|
import org.flowable.engine.history.HistoricProcessInstance;
|
||||||
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
|
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
|
||||||
|
@ -94,6 +97,27 @@ public class FlowableUtils {
|
||||||
return (Integer) processVariables.get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS);
|
return (Integer) processVariables.get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得流程实例的审批原因
|
||||||
|
*
|
||||||
|
* @param processInstance 流程实例
|
||||||
|
* @return 审批原因
|
||||||
|
*/
|
||||||
|
public static String getProcessInstanceReason(HistoricProcessInstance processInstance) {
|
||||||
|
return (String) processInstance.getProcessVariables().get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得流程实例的表单
|
||||||
|
*
|
||||||
|
* @param processInstance 流程实例
|
||||||
|
* @return 表单
|
||||||
|
*/
|
||||||
|
public static Map<String, Object> getProcessInstanceFormVariable(ProcessInstance processInstance) {
|
||||||
|
Map<String, Object> processVariables = new HashMap<>(processInstance.getProcessVariables());
|
||||||
|
return filterProcessInstanceFormVariable(processVariables);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得流程实例的表单
|
* 获得流程实例的表单
|
||||||
*
|
*
|
||||||
|
@ -101,9 +125,8 @@ public class FlowableUtils {
|
||||||
* @return 表单
|
* @return 表单
|
||||||
*/
|
*/
|
||||||
public static Map<String, Object> getProcessInstanceFormVariable(HistoricProcessInstance processInstance) {
|
public static Map<String, Object> getProcessInstanceFormVariable(HistoricProcessInstance processInstance) {
|
||||||
Map<String, Object> formVariables = new HashMap<>(processInstance.getProcessVariables());
|
Map<String, Object> processVariables = new HashMap<>(processInstance.getProcessVariables());
|
||||||
filterProcessInstanceFormVariable(formVariables);
|
return filterProcessInstanceFormVariable(processVariables);
|
||||||
return formVariables;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -125,9 +148,22 @@ public class FlowableUtils {
|
||||||
* @param processInstance 流程实例
|
* @param processInstance 流程实例
|
||||||
* @return 发起用户选择的审批人 Map
|
* @return 发起用户选择的审批人 Map
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public static Map<String, List<Long>> getStartUserSelectAssignees(ProcessInstance processInstance) {
|
public static Map<String, List<Long>> getStartUserSelectAssignees(ProcessInstance processInstance) {
|
||||||
return (Map<String, List<Long>>) processInstance.getProcessVariables().get(
|
return processInstance != null ? getStartUserSelectAssignees(processInstance.getProcessVariables()) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得流程实例的发起用户选择的审批人 Map
|
||||||
|
*
|
||||||
|
* @param processVariables 流程变量
|
||||||
|
* @return 发起用户选择的审批人 Map
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static Map<String, List<Long>> getStartUserSelectAssignees(Map<String, Object> processVariables) {
|
||||||
|
if (processVariables == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (Map<String, List<Long>>) processVariables.get(
|
||||||
BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES);
|
BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,13 +217,30 @@ public class FlowableUtils {
|
||||||
|
|
||||||
// ========== Expression 相关的工具方法 ==========
|
// ========== Expression 相关的工具方法 ==========
|
||||||
|
|
||||||
public static Object getExpressionValue(VariableContainer variableContainer, String expressionString) {
|
private static Object getExpressionValue(VariableContainer variableContainer, String expressionString,
|
||||||
ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration();
|
ProcessEngineConfigurationImpl processEngineConfiguration) {
|
||||||
assert processEngineConfiguration != null;
|
assert processEngineConfiguration!= null;
|
||||||
ExpressionManager expressionManager = processEngineConfiguration.getExpressionManager();
|
ExpressionManager expressionManager = processEngineConfiguration.getExpressionManager();
|
||||||
assert expressionManager != null;
|
assert expressionManager!= null;
|
||||||
Expression expression = expressionManager.createExpression(expressionString);
|
Expression expression = expressionManager.createExpression(expressionString);
|
||||||
return expression.getValue(variableContainer);
|
return expression.getValue(variableContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Object getExpressionValue(VariableContainer variableContainer, String expressionString) {
|
||||||
|
ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration();
|
||||||
|
if (processEngineConfiguration != null) {
|
||||||
|
return getExpressionValue(variableContainer, expressionString, processEngineConfiguration);
|
||||||
|
}
|
||||||
|
// 如果 ProcessEngineConfigurationImpl 获取不到,则需要通过 ManagementService 来获取
|
||||||
|
ManagementService managementService = SpringUtil.getBean(ManagementService.class);
|
||||||
|
assert managementService != null;
|
||||||
|
return managementService.executeCommand(context ->
|
||||||
|
getExpressionValue(variableContainer, expressionString, CommandContextUtil.getProcessEngineConfiguration()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object getExpressionValue(Map<String, Object> variable, String expressionString) {
|
||||||
|
VariableContainer variableContainer = new MapDelegateVariableContainer(variable, VariableContainer.empty());
|
||||||
|
return getExpressionValue(variableContainer, expressionString);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -82,4 +82,11 @@ public interface BpmCategoryService {
|
||||||
*/
|
*/
|
||||||
List<BpmCategoryDO> getCategoryListByStatus(Integer status);
|
List<BpmCategoryDO> getCategoryListByStatus(Integer status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量更新流程分类的排序:每个分类的 sort 值,从 0 开始递增
|
||||||
|
*
|
||||||
|
* @param ids 分类编号列表
|
||||||
|
*/
|
||||||
|
void updateCategorySortBatch(List<Long> ids);
|
||||||
|
|
||||||
}
|
}
|
|
@ -10,11 +10,14 @@ import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
|
||||||
import cn.iocoder.yudao.module.bpm.dal.mysql.category.BpmCategoryMapper;
|
import cn.iocoder.yudao.module.bpm.dal.mysql.category.BpmCategoryMapper;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||||
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
|
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
|
||||||
|
@ -56,7 +59,7 @@ public class BpmCategoryServiceImpl implements BpmCategoryService {
|
||||||
private void validateCategoryNameUnique(BpmCategorySaveReqVO updateReqVO) {
|
private void validateCategoryNameUnique(BpmCategorySaveReqVO updateReqVO) {
|
||||||
BpmCategoryDO category = bpmCategoryMapper.selectByName(updateReqVO.getName());
|
BpmCategoryDO category = bpmCategoryMapper.selectByName(updateReqVO.getName());
|
||||||
if (category == null
|
if (category == null
|
||||||
|| ObjUtil.equal(category.getId(), updateReqVO.getId())) {
|
|| ObjUtil.equal(category.getId(), updateReqVO.getId())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw exception(CATEGORY_NAME_DUPLICATE, updateReqVO.getName());
|
throw exception(CATEGORY_NAME_DUPLICATE, updateReqVO.getName());
|
||||||
|
@ -65,7 +68,7 @@ public class BpmCategoryServiceImpl implements BpmCategoryService {
|
||||||
private void validateCategoryCodeUnique(BpmCategorySaveReqVO updateReqVO) {
|
private void validateCategoryCodeUnique(BpmCategorySaveReqVO updateReqVO) {
|
||||||
BpmCategoryDO category = bpmCategoryMapper.selectByCode(updateReqVO.getCode());
|
BpmCategoryDO category = bpmCategoryMapper.selectByCode(updateReqVO.getCode());
|
||||||
if (category == null
|
if (category == null
|
||||||
|| ObjUtil.equal(category.getId(), updateReqVO.getId())) {
|
|| ObjUtil.equal(category.getId(), updateReqVO.getId())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw exception(CATEGORY_CODE_DUPLICATE, updateReqVO.getCode());
|
throw exception(CATEGORY_CODE_DUPLICATE, updateReqVO.getCode());
|
||||||
|
@ -108,4 +111,20 @@ public class BpmCategoryServiceImpl implements BpmCategoryService {
|
||||||
return bpmCategoryMapper.selectListByStatus(status);
|
return bpmCategoryMapper.selectListByStatus(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void updateCategorySortBatch(List<Long> ids) {
|
||||||
|
// 校验分类都存在
|
||||||
|
List<BpmCategoryDO> categories = bpmCategoryMapper.selectByIds(ids);
|
||||||
|
if (categories.size() != ids.size()) {
|
||||||
|
throw exception(CATEGORY_NOT_EXISTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量更新排序
|
||||||
|
List<BpmCategoryDO> updateList = IntStream.range(0, ids.size())
|
||||||
|
.mapToObj(index -> new BpmCategoryDO().setId(ids.get(index)).setSort(index))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
bpmCategoryMapper.updateBatch(updateList);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,7 +1,5 @@
|
||||||
package cn.iocoder.yudao.module.bpm.service.definition;
|
package cn.iocoder.yudao.module.bpm.service.definition;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
|
||||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelPageReqVO;
|
|
||||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelSaveReqVO;
|
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelSaveReqVO;
|
||||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
|
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
|
||||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelUpdateReqVO;
|
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelUpdateReqVO;
|
||||||
|
@ -9,20 +7,22 @@ import jakarta.validation.Valid;
|
||||||
import org.flowable.bpmn.model.BpmnModel;
|
import org.flowable.bpmn.model.BpmnModel;
|
||||||
import org.flowable.engine.repository.Model;
|
import org.flowable.engine.repository.Model;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flowable流程模型接口
|
* 流程模型接口
|
||||||
*
|
*
|
||||||
* @author yunlongn
|
* @author yunlongn
|
||||||
*/
|
*/
|
||||||
public interface BpmModelService {
|
public interface BpmModelService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得流程模型分页
|
* 获得流程模型列表
|
||||||
*
|
*
|
||||||
* @param pageVO 分页查询
|
* @param name 模型名称
|
||||||
* @return 流程模型分页
|
* @return 流程模型列表
|
||||||
*/
|
*/
|
||||||
PageResult<Model> getModelPage(BpmModelPageReqVO pageVO);
|
List<Model> getModelList(String name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建流程模型
|
* 创建流程模型
|
||||||
|
@ -64,6 +64,14 @@ public interface BpmModelService {
|
||||||
*/
|
*/
|
||||||
void updateModel(Long userId, @Valid BpmModelSaveReqVO updateReqVO);
|
void updateModel(Long userId, @Valid BpmModelSaveReqVO updateReqVO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量更新模型排序
|
||||||
|
*
|
||||||
|
* @param userId 用户编号
|
||||||
|
* @param ids 编号列表
|
||||||
|
*/
|
||||||
|
void updateModelSortBatch(Long userId, List<String> ids);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将流程模型,部署成一个流程定义
|
* 将流程模型,部署成一个流程定义
|
||||||
*
|
*
|
||||||
|
|
|
@ -3,12 +3,9 @@ package cn.iocoder.yudao.module.bpm.service.definition;
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.util.ArrayUtil;
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
|
||||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||||
import cn.iocoder.yudao.framework.common.util.object.PageUtils;
|
|
||||||
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
|
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
|
||||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO;
|
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO;
|
||||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelPageReqVO;
|
|
||||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelSaveReqVO;
|
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelSaveReqVO;
|
||||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
|
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
|
||||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelUpdateReqVO;
|
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelUpdateReqVO;
|
||||||
|
@ -35,14 +32,15 @@ import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||||
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||||
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
|
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flowable流程模型实现
|
* 流程模型实现:主要进行 Flowable {@link Model} 的维护
|
||||||
* 主要进行 Flowable {@link Model} 的维护
|
|
||||||
*
|
*
|
||||||
* @author yunlongn
|
* @author yunlongn
|
||||||
* @author 芋道源码
|
* @author 芋道源码
|
||||||
|
@ -64,27 +62,12 @@ public class BpmModelServiceImpl implements BpmModelService {
|
||||||
private BpmTaskCandidateInvoker taskCandidateInvoker;
|
private BpmTaskCandidateInvoker taskCandidateInvoker;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PageResult<Model> getModelPage(BpmModelPageReqVO pageVO) {
|
public List<Model> getModelList(String name) {
|
||||||
ModelQuery modelQuery = repositoryService.createModelQuery();
|
ModelQuery modelQuery = repositoryService.createModelQuery();
|
||||||
modelQuery.modelTenantId(FlowableUtils.getTenantId());
|
if (StrUtil.isNotEmpty(name)) {
|
||||||
if (StrUtil.isNotBlank(pageVO.getKey())) {
|
modelQuery.modelNameLike(name);
|
||||||
modelQuery.modelKey(pageVO.getKey());
|
|
||||||
}
|
}
|
||||||
if (StrUtil.isNotBlank(pageVO.getName())) {
|
return modelQuery.list();
|
||||||
modelQuery.modelNameLike("%" + pageVO.getName() + "%"); // 模糊匹配
|
|
||||||
}
|
|
||||||
if (StrUtil.isNotBlank(pageVO.getCategory())) {
|
|
||||||
modelQuery.modelCategory(pageVO.getCategory());
|
|
||||||
}
|
|
||||||
// 执行查询
|
|
||||||
long count = modelQuery.count();
|
|
||||||
if (count == 0) {
|
|
||||||
return PageResult.empty(count);
|
|
||||||
}
|
|
||||||
List<Model> models = modelQuery
|
|
||||||
.orderByCreateTime().desc()
|
|
||||||
.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize());
|
|
||||||
return new PageResult<>(models, count);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -100,6 +83,7 @@ public class BpmModelServiceImpl implements BpmModelService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2.1 创建流程定义
|
// 2.1 创建流程定义
|
||||||
|
createReqVO.setSort(System.currentTimeMillis()); // 使用当前时间,作为排序
|
||||||
Model model = repositoryService.newModel();
|
Model model = repositoryService.newModel();
|
||||||
BpmModelConvert.INSTANCE.copyToModel(model, createReqVO);
|
BpmModelConvert.INSTANCE.copyToModel(model, createReqVO);
|
||||||
model.setTenantId(FlowableUtils.getTenantId());
|
model.setTenantId(FlowableUtils.getTenantId());
|
||||||
|
@ -120,6 +104,34 @@ public class BpmModelServiceImpl implements BpmModelService {
|
||||||
repositoryService.saveModel(model);
|
repositoryService.saveModel(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void updateModelSortBatch(Long userId, List<String> ids) {
|
||||||
|
// 1.1 校验流程模型存在
|
||||||
|
List<Model> models = repositoryService.createModelQuery()
|
||||||
|
.modelTenantId(FlowableUtils.getTenantId()).list();
|
||||||
|
models.removeIf(model ->!ids.contains(model.getId()));
|
||||||
|
if (ids.size() != models.size()) {
|
||||||
|
throw exception(MODEL_NOT_EXISTS);
|
||||||
|
}
|
||||||
|
Map<String, Model> modelMap = convertMap(models, Model::getId);
|
||||||
|
// 1.2 校验是否为管理员
|
||||||
|
ids.forEach(id -> validateModelManager(id, userId));
|
||||||
|
|
||||||
|
// 保存排序
|
||||||
|
long sort = System.currentTimeMillis(); // 使用时间戳 - i 作为排序
|
||||||
|
for (int i = ids.size() - 1; i > 0; i--) {
|
||||||
|
Model model = modelMap.get(ids.get(i));
|
||||||
|
// 更新模型
|
||||||
|
BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model).setSort(sort);
|
||||||
|
model.setMetaInfo(JsonUtils.toJsonString(metaInfo));
|
||||||
|
repositoryService.saveModel(model);
|
||||||
|
// 更新排序
|
||||||
|
processDefinitionService.updateProcessDefinitionSortByModelId(model.getId(), sort);
|
||||||
|
sort--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Model validateModelExists(String id) {
|
private Model validateModelExists(String id) {
|
||||||
Model model = repositoryService.getModel(id);
|
Model model = repositoryService.getModel(id);
|
||||||
if (model == null) {
|
if (model == null) {
|
||||||
|
@ -139,7 +151,7 @@ public class BpmModelServiceImpl implements BpmModelService {
|
||||||
Model model = validateModelExists(id);
|
Model model = validateModelExists(id);
|
||||||
BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model);
|
BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model);
|
||||||
if (metaInfo == null || !CollUtil.contains(metaInfo.getManagerUserIds(), userId)) {
|
if (metaInfo == null || !CollUtil.contains(metaInfo.getManagerUserIds(), userId)) {
|
||||||
throw exception(MODEL_UPDATE_FAIL_NOT_MANAGER);
|
throw exception(MODEL_UPDATE_FAIL_NOT_MANAGER, model.getName());
|
||||||
}
|
}
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
@ -158,10 +170,10 @@ public class BpmModelServiceImpl implements BpmModelService {
|
||||||
// 1.4 校验任务分配规则已配置
|
// 1.4 校验任务分配规则已配置
|
||||||
taskCandidateInvoker.validateBpmnConfig(bpmnBytes);
|
taskCandidateInvoker.validateBpmnConfig(bpmnBytes);
|
||||||
// 1.5 获取仿钉钉流程设计器模型数据
|
// 1.5 获取仿钉钉流程设计器模型数据
|
||||||
byte[] simpleBytes = getModelSimpleJson(model.getId());
|
String simpleJson = getModelSimpleJson(model.getId());
|
||||||
|
|
||||||
// 2.1 创建流程定义
|
// 2.1 创建流程定义
|
||||||
String definitionId = processDefinitionService.createProcessDefinition(model, metaInfo, bpmnBytes, simpleBytes, form);
|
String definitionId = processDefinitionService.createProcessDefinition(model, metaInfo, bpmnBytes, simpleJson, form);
|
||||||
|
|
||||||
// 2.2 将老的流程定义进行挂起。也就是说,只有最新部署的流程定义,才可以发起任务。
|
// 2.2 将老的流程定义进行挂起。也就是说,只有最新部署的流程定义,才可以发起任务。
|
||||||
updateProcessDefinitionSuspended(model.getDeploymentId());
|
updateProcessDefinitionSuspended(model.getDeploymentId());
|
||||||
|
@ -226,8 +238,8 @@ public class BpmModelServiceImpl implements BpmModelService {
|
||||||
public BpmSimpleModelNodeVO getSimpleModel(String modelId) {
|
public BpmSimpleModelNodeVO getSimpleModel(String modelId) {
|
||||||
Model model = validateModelExists(modelId);
|
Model model = validateModelExists(modelId);
|
||||||
// 通过 ACT_RE_MODEL 表 EDITOR_SOURCE_EXTRA_VALUE_ID_ ,获取仿钉钉快搭模型的 JSON 数据
|
// 通过 ACT_RE_MODEL 表 EDITOR_SOURCE_EXTRA_VALUE_ID_ ,获取仿钉钉快搭模型的 JSON 数据
|
||||||
byte[] jsonBytes = getModelSimpleJson(model.getId());
|
String json = getModelSimpleJson(model.getId());
|
||||||
return JsonUtils.parseObject(jsonBytes, BpmSimpleModelNodeVO.class);
|
return JsonUtils.parseObject(json, BpmSimpleModelNodeVO.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -240,7 +252,7 @@ public class BpmModelServiceImpl implements BpmModelService {
|
||||||
// 2.2 保存 Bpmn XML
|
// 2.2 保存 Bpmn XML
|
||||||
updateModelBpmnXml(model.getId(), BpmnModelUtils.getBpmnXml(bpmnModel));
|
updateModelBpmnXml(model.getId(), BpmnModelUtils.getBpmnXml(bpmnModel));
|
||||||
// 2.3 保存 JSON 数据
|
// 2.3 保存 JSON 数据
|
||||||
saveModelSimpleJson(model.getId(), JsonUtils.toJsonByte(reqVO.getSimpleModel()));
|
updateModelSimpleJson(model.getId(), reqVO.getSimpleModel());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -279,15 +291,21 @@ public class BpmModelServiceImpl implements BpmModelService {
|
||||||
repositoryService.addModelEditorSource(id, StrUtil.utf8Bytes(bpmnXml));
|
repositoryService.addModelEditorSource(id, StrUtil.utf8Bytes(bpmnXml));
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] getModelSimpleJson(String id) {
|
@SuppressWarnings("JavaExistingMethodCanBeUsed")
|
||||||
return repositoryService.getModelEditorSourceExtra(id);
|
private String getModelSimpleJson(String id) {
|
||||||
|
byte[] bytes = repositoryService.getModelEditorSourceExtra(id);
|
||||||
|
if (ArrayUtil.isEmpty(bytes)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return StrUtil.utf8Str(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveModelSimpleJson(String id, byte[] jsonBytes) {
|
private void updateModelSimpleJson(String id, BpmSimpleModelNodeVO node) {
|
||||||
if (ArrayUtil.isEmpty(jsonBytes)) {
|
if (node == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
repositoryService.addModelEditorSourceExtra(id, jsonBytes);
|
byte[] bytes = JsonUtils.toJsonByte(node);
|
||||||
|
repositoryService.addModelEditorSourceExtra(id, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -18,7 +18,7 @@ import java.util.Set;
|
||||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flowable流程定义接口
|
* 流程定义接口
|
||||||
*
|
*
|
||||||
* @author yunlong.li
|
* @author yunlong.li
|
||||||
* @author ZJQ
|
* @author ZJQ
|
||||||
|
@ -48,12 +48,12 @@ public interface BpmProcessDefinitionService {
|
||||||
* @param model 流程模型
|
* @param model 流程模型
|
||||||
* @param modelMetaInfo 流程模型元信息
|
* @param modelMetaInfo 流程模型元信息
|
||||||
* @param bpmnBytes BPMN XML 字节数组
|
* @param bpmnBytes BPMN XML 字节数组
|
||||||
* @param simpleBytes SIMPLE Model JSON 字节数组
|
* @param simpleJson SIMPLE Model JSON
|
||||||
* @param form 表单
|
* @param form 表单
|
||||||
* @return 流程编号
|
* @return 流程编号
|
||||||
*/
|
*/
|
||||||
String createProcessDefinition(Model model, BpmModelMetaInfoVO modelMetaInfo,
|
String createProcessDefinition(Model model, BpmModelMetaInfoVO modelMetaInfo,
|
||||||
byte[] bpmnBytes, byte[] simpleBytes, BpmFormDO form);
|
byte[] bpmnBytes, String simpleJson, BpmFormDO form);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新流程定义状态
|
* 更新流程定义状态
|
||||||
|
@ -63,6 +63,14 @@ public interface BpmProcessDefinitionService {
|
||||||
*/
|
*/
|
||||||
void updateProcessDefinitionState(String id, Integer state);
|
void updateProcessDefinitionState(String id, Integer state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新模型编号
|
||||||
|
*
|
||||||
|
* @param modelId 流程定义编号
|
||||||
|
* @param sort 排序
|
||||||
|
*/
|
||||||
|
void updateProcessDefinitionSortByModelId(String modelId, Long sort);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得流程定义对应的 BPMN
|
* 获得流程定义对应的 BPMN
|
||||||
*
|
*
|
||||||
|
|
|
@ -24,7 +24,6 @@ import org.flowable.engine.repository.ProcessDefinitionQuery;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||||
|
@ -120,7 +119,7 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String createProcessDefinition(Model model, BpmModelMetaInfoVO modelMetaInfo,
|
public String createProcessDefinition(Model model, BpmModelMetaInfoVO modelMetaInfo,
|
||||||
byte[] bpmnBytes, byte[] simpleBytes, BpmFormDO form) {
|
byte[] bpmnBytes, String simpleJson, BpmFormDO form) {
|
||||||
// 创建 Deployment 部署
|
// 创建 Deployment 部署
|
||||||
Deployment deploy = repositoryService.createDeployment()
|
Deployment deploy = repositoryService.createDeployment()
|
||||||
.key(model.getKey()).name(model.getName()).category(model.getCategory())
|
.key(model.getKey()).name(model.getName()).category(model.getCategory())
|
||||||
|
@ -145,8 +144,8 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
|
||||||
|
|
||||||
// 插入拓展表
|
// 插入拓展表
|
||||||
BpmProcessDefinitionInfoDO definitionDO = BeanUtils.toBean(modelMetaInfo, BpmProcessDefinitionInfoDO.class)
|
BpmProcessDefinitionInfoDO definitionDO = BeanUtils.toBean(modelMetaInfo, BpmProcessDefinitionInfoDO.class)
|
||||||
.setModelId(model.getId()).setProcessDefinitionId(definition.getId()).setModelType(modelMetaInfo.getType())
|
.setModelId(model.getId()).setProcessDefinitionId(definition.getId())
|
||||||
.setSimpleModel(StrUtil.str(simpleBytes, StandardCharsets.UTF_8));
|
.setModelType(modelMetaInfo.getType()).setSimpleModel(simpleJson);
|
||||||
|
|
||||||
if (form != null) {
|
if (form != null) {
|
||||||
definitionDO.setFormFields(form.getFields()).setFormConf(form.getConf());
|
definitionDO.setFormFields(form.getFields()).setFormConf(form.getConf());
|
||||||
|
@ -172,6 +171,11 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
|
||||||
log.error("[updateProcessDefinitionState][流程定义({}) 修改未知状态({})]", id, state);
|
log.error("[updateProcessDefinitionState][流程定义({}) 修改未知状态({})]", id, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateProcessDefinitionSortByModelId(String modelId, Long sort) {
|
||||||
|
processDefinitionMapper.updateByModelId(modelId, new BpmProcessDefinitionInfoDO().setSort(sort));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BpmnModel getProcessDefinitionBpmnModel(String id) {
|
public BpmnModel getProcessDefinitionBpmnModel(String id) {
|
||||||
return repositoryService.getBpmnModel(id);
|
return repositoryService.getBpmnModel(id);
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
package cn.iocoder.yudao.module.bpm.service.task;
|
|
||||||
|
|
||||||
import org.flowable.engine.history.HistoricActivityInstance;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* BPM 活动实例 Service 接口
|
|
||||||
*
|
|
||||||
* @author 芋道源码
|
|
||||||
*/
|
|
||||||
public interface BpmActivityService {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得指定流程实例的活动实例列表
|
|
||||||
*
|
|
||||||
* @param processInstanceId 流程实例的编号
|
|
||||||
* @return 活动实例列表
|
|
||||||
*/
|
|
||||||
List<HistoricActivityInstance> getActivityListByProcessInstanceId(String processInstanceId);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得执行编号对应的活动实例
|
|
||||||
*
|
|
||||||
* @param executionId 执行编号
|
|
||||||
* @return 活动实例
|
|
||||||
*/
|
|
||||||
List<HistoricActivityInstance> getHistoricActivityListByExecutionId(String executionId);
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
package cn.iocoder.yudao.module.bpm.service.task;
|
|
||||||
|
|
||||||
import jakarta.annotation.Resource;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.flowable.engine.HistoryService;
|
|
||||||
import org.flowable.engine.history.HistoricActivityInstance;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import org.springframework.validation.annotation.Validated;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* BPM 活动实例 Service 实现类
|
|
||||||
*
|
|
||||||
* @author 芋道源码
|
|
||||||
*/
|
|
||||||
@Service
|
|
||||||
@Slf4j
|
|
||||||
@Validated
|
|
||||||
public class BpmActivityServiceImpl implements BpmActivityService {
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private HistoryService historyService;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<HistoricActivityInstance> getActivityListByProcessInstanceId(String processInstanceId) {
|
|
||||||
return historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId)
|
|
||||||
.orderByHistoricActivityInstanceStartTime().asc().list();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<HistoricActivityInstance> getHistoricActivityListByExecutionId(String executionId) {
|
|
||||||
return historyService.createHistoricActivityInstanceQuery().executionId(executionId).list();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -3,9 +3,10 @@ package cn.iocoder.yudao.module.bpm.service.task;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO;
|
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO;
|
||||||
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO;
|
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO;
|
||||||
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
|
import org.flowable.bpmn.model.FlowNode;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 流程抄送 Service 接口
|
* 流程抄送 Service 接口
|
||||||
|
@ -15,24 +16,29 @@ import java.util.Set;
|
||||||
public interface BpmProcessInstanceCopyService {
|
public interface BpmProcessInstanceCopyService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 流程实例的抄送
|
* 【管理员】流程实例的抄送
|
||||||
*
|
*
|
||||||
* @param userIds 抄送的用户编号
|
* @param userIds 抄送的用户编号
|
||||||
|
* @param reason 抄送意见
|
||||||
* @param taskId 流程任务编号
|
* @param taskId 流程任务编号
|
||||||
*/
|
*/
|
||||||
void createProcessInstanceCopy(Collection<Long> userIds, String taskId);
|
void createProcessInstanceCopy(Collection<Long> userIds, String reason, String taskId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 流程实例的抄送
|
* 【自动抄送】流程实例的抄送
|
||||||
*
|
*
|
||||||
* @param userIds 抄送的用户编号
|
* @param userIds 抄送的用户编号
|
||||||
|
* @param reason 抄送意见
|
||||||
* @param processInstanceId 流程编号
|
* @param processInstanceId 流程编号
|
||||||
* @param activityId 流程活动编号 id (对应 BPMN XML 节点 Id)
|
* @param activityId 流程活动编号(对应 {@link FlowNode#getId()})
|
||||||
* // TODO 芋艿这个 taskId 是不是可以不要了
|
* @param activityName 任务编号(对应 {@link FlowNode#getName()})
|
||||||
* @param taskId 任务编号
|
* @param taskId 任务编号,允许空
|
||||||
* @param taskName 任务名称
|
|
||||||
*/
|
*/
|
||||||
void createProcessInstanceCopy(Collection<Long> userIds, String processInstanceId, String activityId, String taskId, String taskName);
|
void createProcessInstanceCopy(Collection<Long> userIds, String reason,
|
||||||
|
@NotEmpty(message = "流程实例编号不能为空") String processInstanceId,
|
||||||
|
@NotEmpty(message = "流程活动编号不能为空") String activityId,
|
||||||
|
@NotEmpty(message = "流程活动名字不能为空") String activityName,
|
||||||
|
String taskId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得抄送的流程的分页
|
* 获得抄送的流程的分页
|
||||||
|
@ -43,14 +49,5 @@ public interface BpmProcessInstanceCopyService {
|
||||||
*/
|
*/
|
||||||
PageResult<BpmProcessInstanceCopyDO> getProcessInstanceCopyPage(Long userId,
|
PageResult<BpmProcessInstanceCopyDO> getProcessInstanceCopyPage(Long userId,
|
||||||
BpmProcessInstanceCopyPageReqVO pageReqVO);
|
BpmProcessInstanceCopyPageReqVO pageReqVO);
|
||||||
// TODO @芋艿:重点在 review 下
|
|
||||||
/**
|
|
||||||
* 通过流程实例和流程活动编号获取抄送人的 Id
|
|
||||||
*
|
|
||||||
* @param processInstanceId 流程实例 Id
|
|
||||||
* @param activityId 流程活动编号 Id
|
|
||||||
* @return 抄送人 Ids
|
|
||||||
*/
|
|
||||||
Set<Long> getCopyUserIds(String processInstanceId, String activityId);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.bpm.service.task;
|
||||||
|
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
|
||||||
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO;
|
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO;
|
||||||
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO;
|
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO;
|
||||||
import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmProcessInstanceCopyMapper;
|
import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmProcessInstanceCopyMapper;
|
||||||
|
@ -19,7 +18,6 @@ import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||||
|
@ -49,17 +47,19 @@ public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopy
|
||||||
private BpmProcessDefinitionService processDefinitionService;
|
private BpmProcessDefinitionService processDefinitionService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createProcessInstanceCopy(Collection<Long> userIds, String taskId) {
|
public void createProcessInstanceCopy(Collection<Long> userIds, String reason, String taskId) {
|
||||||
Task task = taskService.getTask(taskId);
|
Task task = taskService.getTask(taskId);
|
||||||
if (ObjectUtil.isNull(task)) {
|
if (ObjectUtil.isNull(task)) {
|
||||||
throw exception(ErrorCodeConstants.TASK_NOT_EXISTS);
|
throw exception(ErrorCodeConstants.TASK_NOT_EXISTS);
|
||||||
}
|
}
|
||||||
String processInstanceId = task.getProcessInstanceId();
|
// 执行抄送
|
||||||
createProcessInstanceCopy(userIds, processInstanceId, task.getTaskDefinitionKey(), task.getId(), task.getName());
|
createProcessInstanceCopy(userIds, reason,
|
||||||
|
task.getProcessInstanceId(), task.getTaskDefinitionKey(), task.getId(), task.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createProcessInstanceCopy(Collection<Long> userIds, String processInstanceId, String activityId, String taskId, String taskName) {
|
public void createProcessInstanceCopy(Collection<Long> userIds, String reason, String processInstanceId,
|
||||||
|
String activityId, String activityName, String taskId) {
|
||||||
// 1.1 校验流程实例存在
|
// 1.1 校验流程实例存在
|
||||||
ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);
|
ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);
|
||||||
if (processInstance == null) {
|
if (processInstance == null) {
|
||||||
|
@ -74,10 +74,10 @@ public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopy
|
||||||
|
|
||||||
// 2. 创建抄送流程
|
// 2. 创建抄送流程
|
||||||
List<BpmProcessInstanceCopyDO> copyList = convertList(userIds, userId -> new BpmProcessInstanceCopyDO()
|
List<BpmProcessInstanceCopyDO> copyList = convertList(userIds, userId -> new BpmProcessInstanceCopyDO()
|
||||||
.setUserId(userId).setStartUserId(Long.valueOf(processInstance.getStartUserId()))
|
.setUserId(userId).setReason(reason).setStartUserId(Long.valueOf(processInstance.getStartUserId()))
|
||||||
.setProcessInstanceId(processInstanceId).setProcessInstanceName(processInstance.getName())
|
.setProcessInstanceId(processInstanceId).setProcessInstanceName(processInstance.getName())
|
||||||
.setCategory(processDefinition.getCategory()).setActivityId(activityId)
|
.setCategory(processDefinition.getCategory()).setTaskId(taskId)
|
||||||
.setTaskId(taskId).setTaskName(taskName));
|
.setActivityId(activityId).setActivityName(activityName));
|
||||||
processInstanceCopyMapper.insertBatch(copyList);
|
processInstanceCopyMapper.insertBatch(copyList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,10 +87,4 @@ public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopy
|
||||||
return processInstanceCopyMapper.selectPage(userId, pageReqVO);
|
return processInstanceCopyMapper.selectPage(userId, pageReqVO);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<Long> getCopyUserIds(String processInstanceId, String activityId) {
|
|
||||||
return CollectionUtils.convertSet(processInstanceCopyMapper.selectListByProcessInstanceIdAndActivityId(processInstanceId, activityId),
|
|
||||||
BpmProcessInstanceCopyDO::getUserId);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,14 +84,6 @@ public interface BpmProcessInstanceService {
|
||||||
PageResult<HistoricProcessInstance> getProcessInstancePage(Long userId,
|
PageResult<HistoricProcessInstance> getProcessInstancePage(Long userId,
|
||||||
@Valid BpmProcessInstancePageReqVO pageReqVO);
|
@Valid BpmProcessInstancePageReqVO pageReqVO);
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得表单字段权限
|
|
||||||
*
|
|
||||||
* @param reqVO 请求消息
|
|
||||||
* @return 表单字段权限
|
|
||||||
*/
|
|
||||||
Map<String, String> getFormFieldsPermission(@Valid BpmFormFieldsPermissionReqVO reqVO);
|
|
||||||
|
|
||||||
// TODO @芋艿:重点在 review 下
|
// TODO @芋艿:重点在 review 下
|
||||||
/**
|
/**
|
||||||
* 获取审批详情。
|
* 获取审批详情。
|
||||||
|
@ -104,6 +96,14 @@ public interface BpmProcessInstanceService {
|
||||||
*/
|
*/
|
||||||
BpmApprovalDetailRespVO getApprovalDetail(Long loginUserId, @Valid BpmApprovalDetailReqVO reqVO);
|
BpmApprovalDetailRespVO getApprovalDetail(Long loginUserId, @Valid BpmApprovalDetailReqVO reqVO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取流程实例的 BPMN 模型视图
|
||||||
|
*
|
||||||
|
* @param id 流程实例的编号
|
||||||
|
* @return BPMN 模型视图
|
||||||
|
*/
|
||||||
|
BpmProcessInstanceBpmnModelViewRespVO getProcessInstanceBpmnModelView(String id);
|
||||||
|
|
||||||
// ========== Update 写入相关方法 ==========
|
// ========== Update 写入相关方法 ==========
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -157,5 +157,4 @@ public interface BpmProcessInstanceService {
|
||||||
*/
|
*/
|
||||||
void processProcessInstanceCompleted(ProcessInstance instance);
|
void processProcessInstanceCompleted(ProcessInstance instance);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -6,7 +6,9 @@ import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
|
||||||
import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskTimeoutHandlerTypeEnum;
|
import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskTimeoutHandlerTypeEnum;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import org.flowable.bpmn.model.UserTask;
|
import org.flowable.bpmn.model.UserTask;
|
||||||
|
import org.flowable.engine.history.HistoricActivityInstance;
|
||||||
import org.flowable.task.api.Task;
|
import org.flowable.task.api.Task;
|
||||||
|
import org.flowable.task.api.TaskInfo;
|
||||||
import org.flowable.task.api.history.HistoricTaskInstance;
|
import org.flowable.task.api.history.HistoricTaskInstance;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -32,6 +34,15 @@ public interface BpmTaskService {
|
||||||
*/
|
*/
|
||||||
PageResult<Task> getTaskTodoPage(Long userId, BpmTaskPageReqVO pageReqVO);
|
PageResult<Task> getTaskTodoPage(Long userId, BpmTaskPageReqVO pageReqVO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得用户在指定流程下,首个需要处理(待办)的任务
|
||||||
|
*
|
||||||
|
* @param userId 用户编号
|
||||||
|
* @param processInstanceId 流程实例编号
|
||||||
|
* @return 待办任务
|
||||||
|
*/
|
||||||
|
BpmTaskRespVO getFirstTodoTask(Long userId, String processInstanceId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得已办的流程任务分页
|
* 获得已办的流程任务分页
|
||||||
*
|
*
|
||||||
|
@ -73,9 +84,10 @@ public interface BpmTaskService {
|
||||||
* 获得指定流程实例的流程任务列表,包括所有状态的
|
* 获得指定流程实例的流程任务列表,包括所有状态的
|
||||||
*
|
*
|
||||||
* @param processInstanceId 流程实例的编号
|
* @param processInstanceId 流程实例的编号
|
||||||
|
* @param asc 是否升序
|
||||||
* @return 流程任务列表
|
* @return 流程任务列表
|
||||||
*/
|
*/
|
||||||
List<HistoricTaskInstance> getTaskListByProcessInstanceId(String processInstanceId);
|
List<HistoricTaskInstance> getTaskListByProcessInstanceId(String processInstanceId, Boolean asc);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取任务
|
* 获取任务
|
||||||
|
@ -105,21 +117,30 @@ public interface BpmTaskService {
|
||||||
* 根据条件查询正在进行中的任务
|
* 根据条件查询正在进行中的任务
|
||||||
*
|
*
|
||||||
* @param processInstanceId 流程实例编号,不允许为空
|
* @param processInstanceId 流程实例编号,不允许为空
|
||||||
* @param assigned 是否分配了审批人,允许空
|
* @param assigned 是否分配了审批人,允许空
|
||||||
* @param taskDefineKey 任务定义 Key,允许空
|
* @param taskDefineKey 任务定义 Key,允许空
|
||||||
*/
|
*/
|
||||||
List<Task> getRunningTaskListByProcessInstanceId(String processInstanceId,
|
List<Task> getRunningTaskListByProcessInstanceId(String processInstanceId,
|
||||||
Boolean assigned,
|
Boolean assigned,
|
||||||
String taskDefineKey);
|
String taskDefineKey);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取当前任务的可回退的 UserTask 集合
|
* 获取当前任务的可退回的 UserTask 集合
|
||||||
*
|
*
|
||||||
* @param id 当前的任务 ID
|
* @param id 当前的任务 ID
|
||||||
* @return 可以回退的节点列表
|
* @return 可以退回的节点列表
|
||||||
*/
|
*/
|
||||||
List<UserTask> getUserTaskListByReturn(String id);
|
List<UserTask> getUserTaskListByReturn(String id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定任务的子任务列表(多层)
|
||||||
|
*
|
||||||
|
* @param parentTaskId 父任务 ID
|
||||||
|
* @param tasks 任务列表
|
||||||
|
* @return 子任务列表
|
||||||
|
*/
|
||||||
|
<T extends TaskInfo> List<T> getAllChildrenTaskListByParentTaskId(String parentTaskId, List<T> tasks);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取指定任务的子任务列表
|
* 获取指定任务的子任务列表
|
||||||
*
|
*
|
||||||
|
@ -129,12 +150,20 @@ public interface BpmTaskService {
|
||||||
List<Task> getTaskListByParentTaskId(String parentTaskId);
|
List<Task> getTaskListByParentTaskId(String parentTaskId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通过任务 ID,查询任务名 Map
|
* 获得指定流程实例的活动实例列表
|
||||||
*
|
*
|
||||||
* @param taskIds 任务 ID
|
* @param processInstanceId 流程实例的编号
|
||||||
* @return 任务 ID 与名字的 Map
|
* @return 活动实例列表
|
||||||
*/
|
*/
|
||||||
Map<String, String> getTaskNameByTaskIds(Collection<String> taskIds);
|
List<HistoricActivityInstance> getActivityListByProcessInstanceId(String processInstanceId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得执行编号对应的活动实例
|
||||||
|
*
|
||||||
|
* @param executionId 执行编号
|
||||||
|
* @return 活动实例
|
||||||
|
*/
|
||||||
|
List<HistoricActivityInstance> getHistoricActivityListByExecutionId(String executionId);
|
||||||
|
|
||||||
// ========== Update 写入相关方法 ==========
|
// ========== Update 写入相关方法 ==========
|
||||||
|
|
||||||
|
@ -170,10 +199,10 @@ public interface BpmTaskService {
|
||||||
void moveTaskToEnd(String processInstanceId);
|
void moveTaskToEnd(String processInstanceId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将任务回退到指定的 targetDefinitionKey 位置
|
* 将任务退回到指定的 targetDefinitionKey 位置
|
||||||
*
|
*
|
||||||
* @param userId 用户编号
|
* @param userId 用户编号
|
||||||
* @param reqVO 回退的任务key和当前所在的任务ID
|
* @param reqVO 退回的任务key和当前所在的任务ID
|
||||||
*/
|
*/
|
||||||
void returnTask(Long userId, BpmTaskReturnReqVO reqVO);
|
void returnTask(Long userId, BpmTaskReturnReqVO reqVO);
|
||||||
|
|
||||||
|
@ -201,14 +230,22 @@ public interface BpmTaskService {
|
||||||
*/
|
*/
|
||||||
void deleteSignTask(Long userId, BpmTaskSignDeleteReqVO reqVO);
|
void deleteSignTask(Long userId, BpmTaskSignDeleteReqVO reqVO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 抄送任务
|
||||||
|
*
|
||||||
|
* @param userId 用户编号
|
||||||
|
* @param reqVO 通过请求
|
||||||
|
*/
|
||||||
|
void copyTask(Long userId, @Valid BpmTaskCopyReqVO reqVO);
|
||||||
|
|
||||||
// ========== Event 事件相关方法 ==========
|
// ========== Event 事件相关方法 ==========
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理 Task 创建事件,目前是
|
* 处理 Task 创建事件,目前是
|
||||||
*
|
* <p>
|
||||||
* 1. 更新它的状态为审批中
|
* 1. 更新它的状态为审批中
|
||||||
* 2. 处理自动通过的情况,例如说:1)无审批人时,是否自动通过、不通过;2)非【人工审核】时,是否自动通过、不通过
|
* 2. 处理自动通过的情况,例如说:1)无审批人时,是否自动通过、不通过;2)非【人工审核】时,是否自动通过、不通过
|
||||||
*
|
* <p>
|
||||||
* 注意:它的触发时机,晚于 {@link #processTaskAssigned(Task)} 之后
|
* 注意:它的触发时机,晚于 {@link #processTaskAssigned(Task)} 之后
|
||||||
*
|
*
|
||||||
* @param task 任务实体
|
* @param task 任务实体
|
||||||
|
@ -233,8 +270,8 @@ public interface BpmTaskService {
|
||||||
* 处理 Task 审批超时事件,可能会处理多个当前审批中的任务
|
* 处理 Task 审批超时事件,可能会处理多个当前审批中的任务
|
||||||
*
|
*
|
||||||
* @param processInstanceId 流程示例编号
|
* @param processInstanceId 流程示例编号
|
||||||
* @param taskDefineKey 任务 Key
|
* @param taskDefineKey 任务 Key
|
||||||
* @param handlerType 处理类型,参见 {@link BpmUserTaskTimeoutHandlerTypeEnum}
|
* @param handlerType 处理类型,参见 {@link BpmUserTaskTimeoutHandlerTypeEnum}
|
||||||
*/
|
*/
|
||||||
void processTaskTimeout(String processInstanceId, String taskDefineKey, Integer handlerType);
|
void processTaskTimeout(String processInstanceId, String taskDefineKey, Integer handlerType);
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableCon
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
|
||||||
import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService;
|
import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService;
|
||||||
|
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
|
||||||
import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService;
|
import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService;
|
||||||
import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenTaskTimeoutReqDTO;
|
import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenTaskTimeoutReqDTO;
|
||||||
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||||
|
@ -38,9 +39,11 @@ import org.flowable.engine.HistoryService;
|
||||||
import org.flowable.engine.ManagementService;
|
import org.flowable.engine.ManagementService;
|
||||||
import org.flowable.engine.RuntimeService;
|
import org.flowable.engine.RuntimeService;
|
||||||
import org.flowable.engine.TaskService;
|
import org.flowable.engine.TaskService;
|
||||||
|
import org.flowable.engine.history.HistoricActivityInstance;
|
||||||
import org.flowable.engine.runtime.ProcessInstance;
|
import org.flowable.engine.runtime.ProcessInstance;
|
||||||
import org.flowable.task.api.DelegationState;
|
import org.flowable.task.api.DelegationState;
|
||||||
import org.flowable.task.api.Task;
|
import org.flowable.task.api.Task;
|
||||||
|
import org.flowable.task.api.TaskInfo;
|
||||||
import org.flowable.task.api.TaskQuery;
|
import org.flowable.task.api.TaskQuery;
|
||||||
import org.flowable.task.api.history.HistoricTaskInstance;
|
import org.flowable.task.api.history.HistoricTaskInstance;
|
||||||
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
|
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
|
||||||
|
@ -81,6 +84,8 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||||
@Resource
|
@Resource
|
||||||
private BpmProcessInstanceService processInstanceService;
|
private BpmProcessInstanceService processInstanceService;
|
||||||
@Resource
|
@Resource
|
||||||
|
private BpmProcessDefinitionService bpmProcessDefinitionService;
|
||||||
|
@Resource
|
||||||
private BpmProcessInstanceCopyService processInstanceCopyService;
|
private BpmProcessInstanceCopyService processInstanceCopyService;
|
||||||
@Resource
|
@Resource
|
||||||
private BpmModelService modelService;
|
private BpmModelService modelService;
|
||||||
|
@ -116,6 +121,41 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||||
return new PageResult<>(tasks, count);
|
return new PageResult<>(tasks, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BpmTaskRespVO getFirstTodoTask(Long userId, String processInstanceId) {
|
||||||
|
if (processInstanceId == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// 1. 查询所有任务
|
||||||
|
List<Task> tasks = taskService.createTaskQuery()
|
||||||
|
.active()
|
||||||
|
.processInstanceId(processInstanceId)
|
||||||
|
.includeTaskLocalVariables()
|
||||||
|
.includeProcessVariables()
|
||||||
|
.orderByTaskCreateTime().asc() // 按创建时间升序
|
||||||
|
.list();
|
||||||
|
if (CollUtil.isEmpty(tasks)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.1 查询我的首个任务
|
||||||
|
Task todoTask = CollUtil.findOne(tasks, task -> {
|
||||||
|
return isAssignUserTask(userId, task) // 当前用户为审批人
|
||||||
|
|| isAddSignUserTask(userId, task); // 当前用户为加签人(为了减签)
|
||||||
|
});
|
||||||
|
if (todoTask == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// 2.2 查询该任务的子任务
|
||||||
|
List<Task> childrenTasks = getAllChildrenTaskListByParentTaskId(todoTask.getId(), tasks);
|
||||||
|
|
||||||
|
// 3. 转换返回
|
||||||
|
BpmnModel bpmnModel = bpmProcessDefinitionService.getProcessDefinitionBpmnModel(todoTask.getProcessDefinitionId());
|
||||||
|
Map<Integer, BpmTaskRespVO.OperationButtonSetting> buttonsSetting = BpmnModelUtils.parseButtonsSetting(
|
||||||
|
bpmnModel, todoTask.getTaskDefinitionKey());
|
||||||
|
return BpmTaskConvert.INSTANCE.buildTodoTask(todoTask, childrenTasks, buttonsSetting);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PageResult<HistoricTaskInstance> getTaskDonePage(Long userId, BpmTaskPageReqVO pageVO) {
|
public PageResult<HistoricTaskInstance> getTaskDonePage(Long userId, BpmTaskPageReqVO pageVO) {
|
||||||
HistoricTaskInstanceQuery taskQuery = historyService.createHistoricTaskInstanceQuery()
|
HistoricTaskInstanceQuery taskQuery = historyService.createHistoricTaskInstanceQuery()
|
||||||
|
@ -170,16 +210,16 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<HistoricTaskInstance> getTaskListByProcessInstanceId(String processInstanceId) {
|
public List<HistoricTaskInstance> getTaskListByProcessInstanceId(String processInstanceId, Boolean asc) {
|
||||||
List<HistoricTaskInstance> tasks = historyService.createHistoricTaskInstanceQuery()
|
HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery()
|
||||||
.includeTaskLocalVariables()
|
.includeTaskLocalVariables()
|
||||||
.processInstanceId(processInstanceId)
|
.processInstanceId(processInstanceId);
|
||||||
.orderByHistoricTaskInstanceStartTime().desc() // 创建时间倒序
|
if (Boolean.TRUE.equals(asc)) {
|
||||||
.list();
|
query.orderByHistoricTaskInstanceStartTime().asc();
|
||||||
if (CollUtil.isEmpty(tasks)) {
|
} else {
|
||||||
return Collections.emptyList();
|
query.orderByHistoricTaskInstanceStartTime().desc();
|
||||||
}
|
}
|
||||||
return tasks;
|
return query.list();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -254,11 +294,43 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||||
if (CollUtil.isEmpty(previousUserList)) {
|
if (CollUtil.isEmpty(previousUserList)) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
// 2.2 过滤:只有串行可到达的节点,才可以回退。类似非串行、子流程无法退回
|
// 2.2 过滤:只有串行可到达的节点,才可以退回。类似非串行、子流程无法退回
|
||||||
previousUserList.removeIf(userTask -> !BpmnModelUtils.isSequentialReachable(source, userTask, null));
|
previousUserList.removeIf(userTask -> !BpmnModelUtils.isSequentialReachable(source, userTask, null));
|
||||||
return previousUserList;
|
return previousUserList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends TaskInfo> List<T> getAllChildrenTaskListByParentTaskId(String parentTaskId, List<T> tasks) {
|
||||||
|
if (CollUtil.isEmpty(tasks)) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
Map<String, List<T>> parentTaskMap = convertMultiMap(
|
||||||
|
filterList(tasks, task -> StrUtil.isNotEmpty(task.getParentTaskId())), TaskInfo::getParentTaskId);
|
||||||
|
if (CollUtil.isEmpty(parentTaskMap)) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<T> result = new ArrayList<>();
|
||||||
|
// 1. 递归获取子级
|
||||||
|
Stack<String> stack = new Stack<>();
|
||||||
|
stack.push(parentTaskId);
|
||||||
|
// 2. 递归遍历
|
||||||
|
for (int i = 0; i < Short.MAX_VALUE; i++) {
|
||||||
|
if (stack.isEmpty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// 2.1 获取子任务们
|
||||||
|
String taskId = stack.pop();
|
||||||
|
List<T> childTaskList = filterList(tasks, task -> StrUtil.equals(task.getParentTaskId(), taskId));
|
||||||
|
// 2.2 如果非空,则添加到 stack 进一步递归
|
||||||
|
if (CollUtil.isNotEmpty(childTaskList)) {
|
||||||
|
stack.addAll(convertList(childTaskList, TaskInfo::getId));
|
||||||
|
result.addAll(childTaskList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得所有子任务列表
|
* 获得所有子任务列表
|
||||||
*
|
*
|
||||||
|
@ -331,12 +403,50 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, String> getTaskNameByTaskIds(Collection<String> taskIds) {
|
public List<HistoricActivityInstance> getActivityListByProcessInstanceId(String processInstanceId) {
|
||||||
if (CollUtil.isEmpty(taskIds)) {
|
return historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId)
|
||||||
return Collections.emptyMap();
|
.orderByHistoricActivityInstanceStartTime().asc().list();
|
||||||
}
|
}
|
||||||
List<Task> tasks = taskService.createTaskQuery().taskIds(taskIds).list();
|
|
||||||
return convertMap(tasks, Task::getId, Task::getName);
|
@Override
|
||||||
|
public List<HistoricActivityInstance> getHistoricActivityListByExecutionId(String executionId) {
|
||||||
|
return historyService.createHistoricActivityInstanceQuery().executionId(executionId).list();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断指定用户,是否是当前任务的审批人
|
||||||
|
*
|
||||||
|
* @param userId 用户编号
|
||||||
|
* @param task 任务
|
||||||
|
* @return 是否
|
||||||
|
*/
|
||||||
|
private boolean isAssignUserTask(Long userId, Task task) {
|
||||||
|
Long assignee = NumberUtil.parseLong(task.getAssignee(), null);
|
||||||
|
return ObjectUtil.equals(userId, assignee);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断指定用户,是否是当前任务的拥有人
|
||||||
|
*
|
||||||
|
* @param userId 用户编号
|
||||||
|
* @param task 任务
|
||||||
|
* @return 是否
|
||||||
|
*/
|
||||||
|
private boolean isOwnerUserTask(Long userId, Task task) {
|
||||||
|
Long assignee = NumberUtil.parseLong(task.getOwner(), null);
|
||||||
|
return ObjectUtil.equal(userId, assignee);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断指定用户,是否是当前任务的加签人
|
||||||
|
*
|
||||||
|
* @param userId 用户 Id
|
||||||
|
* @param task 任务
|
||||||
|
* @return 是否
|
||||||
|
*/
|
||||||
|
private boolean isAddSignUserTask(Long userId, Task task) {
|
||||||
|
return (isAssignUserTask(userId, task) || isOwnerUserTask(userId, task))
|
||||||
|
&& BpmTaskSignTypeEnum.of(task.getScopeType()) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== Update 写入相关方法 ==========
|
// ========== Update 写入相关方法 ==========
|
||||||
|
@ -352,11 +462,6 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||||
throw exception(PROCESS_INSTANCE_NOT_EXISTS);
|
throw exception(PROCESS_INSTANCE_NOT_EXISTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 抄送用户
|
|
||||||
if (CollUtil.isNotEmpty(reqVO.getCopyUserIds())) {
|
|
||||||
processInstanceCopyService.createProcessInstanceCopy(reqVO.getCopyUserIds(), reqVO.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 情况一:被委派的任务,不调用 complete 去完成任务
|
// 情况一:被委派的任务,不调用 complete 去完成任务
|
||||||
if (DelegationState.PENDING.equals(task.getDelegationState())) {
|
if (DelegationState.PENDING.equals(task.getDelegationState())) {
|
||||||
approveDelegateTask(reqVO, task);
|
approveDelegateTask(reqVO, task);
|
||||||
|
@ -370,12 +475,12 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 情况三:审批普通的任务。大多数情况下,都是这样
|
// 情况三:审批普通的任务。大多数情况下,都是这样
|
||||||
// 3.1 更新 task 状态、原因
|
// 2.1 更新 task 状态、原因
|
||||||
updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.APPROVE.getStatus(), reqVO.getReason());
|
updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.APPROVE.getStatus(), reqVO.getReason());
|
||||||
// 3.2 添加评论
|
// 2.2 添加评论
|
||||||
taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(),
|
taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(),
|
||||||
BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason()));
|
BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason()));
|
||||||
// 3.3 调用 BPM complete 去完成任务
|
// 2.3 调用 BPM complete 去完成任务
|
||||||
// 其中,variables 是存储动态表单到 local 任务级别。过滤一下,避免 ProcessInstance 系统级的变量被占用
|
// 其中,variables 是存储动态表单到 local 任务级别。过滤一下,避免 ProcessInstance 系统级的变量被占用
|
||||||
if (CollUtil.isNotEmpty(reqVO.getVariables())) {
|
if (CollUtil.isNotEmpty(reqVO.getVariables())) {
|
||||||
Map<String, Object> variables = FlowableUtils.filterTaskFormVariable(reqVO.getVariables());
|
Map<String, Object> variables = FlowableUtils.filterTaskFormVariable(reqVO.getVariables());
|
||||||
|
@ -519,7 +624,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||||
BpmUserTaskRejectHandlerType userTaskRejectHandlerType = BpmnModelUtils.parseRejectHandlerType(userTaskElement);
|
BpmUserTaskRejectHandlerType userTaskRejectHandlerType = BpmnModelUtils.parseRejectHandlerType(userTaskElement);
|
||||||
if (userTaskRejectHandlerType == BpmUserTaskRejectHandlerType.RETURN_USER_TASK) {
|
if (userTaskRejectHandlerType == BpmUserTaskRejectHandlerType.RETURN_USER_TASK) {
|
||||||
String returnTaskId = BpmnModelUtils.parseReturnTaskId(userTaskElement);
|
String returnTaskId = BpmnModelUtils.parseReturnTaskId(userTaskElement);
|
||||||
Assert.notNull(returnTaskId, "回退的节点不能为空");
|
Assert.notNull(returnTaskId, "退回的节点不能为空");
|
||||||
returnTask(userId, new BpmTaskReturnReqVO().setId(task.getId())
|
returnTask(userId, new BpmTaskReturnReqVO().setId(task.getId())
|
||||||
.setTargetTaskDefinitionKey(returnTaskId).setReason(reqVO.getReason()));
|
.setTargetTaskDefinitionKey(returnTaskId).setReason(reqVO.getReason()));
|
||||||
return;
|
return;
|
||||||
|
@ -563,12 +668,12 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||||
FlowElement targetElement = validateTargetTaskCanReturn(task.getTaskDefinitionKey(),
|
FlowElement targetElement = validateTargetTaskCanReturn(task.getTaskDefinitionKey(),
|
||||||
reqVO.getTargetTaskDefinitionKey(), task.getProcessDefinitionId());
|
reqVO.getTargetTaskDefinitionKey(), task.getProcessDefinitionId());
|
||||||
|
|
||||||
// 2. 调用 Flowable 框架的回退逻辑
|
// 2. 调用 Flowable 框架的退回逻辑
|
||||||
returnTask(task, targetElement, reqVO);
|
returnTask(task, targetElement, reqVO);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 回退流程节点时,校验目标任务节点是否可回退
|
* 退回流程节点时,校验目标任务节点是否可退回
|
||||||
*
|
*
|
||||||
* @param sourceKey 当前任务节点 Key
|
* @param sourceKey 当前任务节点 Key
|
||||||
* @param targetKey 目标任务节点 key
|
* @param targetKey 目标任务节点 key
|
||||||
|
@ -586,7 +691,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||||
throw exception(TASK_TARGET_NODE_NOT_EXISTS);
|
throw exception(TASK_TARGET_NODE_NOT_EXISTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2.2 只有串行可到达的节点,才可以回退。类似非串行、子流程无法退回
|
// 2.2 只有串行可到达的节点,才可以退回。类似非串行、子流程无法退回
|
||||||
if (!BpmnModelUtils.isSequentialReachable(source, target, null)) {
|
if (!BpmnModelUtils.isSequentialReachable(source, target, null)) {
|
||||||
throw exception(TASK_RETURN_FAIL_SOURCE_TARGET_ERROR);
|
throw exception(TASK_RETURN_FAIL_SOURCE_TARGET_ERROR);
|
||||||
}
|
}
|
||||||
|
@ -594,10 +699,10 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行回退逻辑
|
* 执行退回逻辑
|
||||||
*
|
*
|
||||||
* @param currentTask 当前回退的任务
|
* @param currentTask 当前退回的任务
|
||||||
* @param targetElement 需要回退到的目标任务
|
* @param targetElement 需要退回到的目标任务
|
||||||
* @param reqVO 前端参数封装
|
* @param reqVO 前端参数封装
|
||||||
*/
|
*/
|
||||||
public void returnTask(Task currentTask, FlowElement targetElement, BpmTaskReturnReqVO reqVO) {
|
public void returnTask(Task currentTask, FlowElement targetElement, BpmTaskReturnReqVO reqVO) {
|
||||||
|
@ -610,9 +715,9 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||||
List<UserTask> returnUserTaskList = BpmnModelUtils.iteratorFindChildUserTasks(targetElement, runTaskKeyList, null, null);
|
List<UserTask> returnUserTaskList = BpmnModelUtils.iteratorFindChildUserTasks(targetElement, runTaskKeyList, null, null);
|
||||||
List<String> returnTaskKeyList = convertList(returnUserTaskList, UserTask::getId);
|
List<String> returnTaskKeyList = convertList(returnUserTaskList, UserTask::getId);
|
||||||
|
|
||||||
// 2. 给当前要被回退的 task 数组,设置回退意见
|
// 2. 给当前要被退回的 task 数组,设置退回意见
|
||||||
taskList.forEach(task -> {
|
taskList.forEach(task -> {
|
||||||
// 需要排除掉,不需要设置回退意见的任务
|
// 需要排除掉,不需要设置退回意见的任务
|
||||||
if (!returnTaskKeyList.contains(task.getTaskDefinitionKey())) {
|
if (!returnTaskKeyList.contains(task.getTaskDefinitionKey())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -659,9 +764,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||||
taskService.setOwner(taskId, task.getAssignee());
|
taskService.setOwner(taskId, task.getAssignee());
|
||||||
// 3.2 执行委派,将任务委派给 delegateUser
|
// 3.2 执行委派,将任务委派给 delegateUser
|
||||||
taskService.delegateTask(taskId, reqVO.getDelegateUserId().toString());
|
taskService.delegateTask(taskId, reqVO.getDelegateUserId().toString());
|
||||||
// 3.3 更新 task 状态。
|
// 补充说明:委托不单独设置状态。如果需要,可通过 Task 的 DelegationState 字段,判断是否为 DelegationState.PENDING 委托中
|
||||||
// 为什么不更新原因?因为原因目前主要给审批通过、不通过时使用
|
|
||||||
updateTaskStatus(taskId, BpmTaskStatusEnum.DELEGATE.getStatus());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -868,6 +971,11 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||||
handleParentTaskIfSign(task.getParentTaskId());
|
handleParentTaskIfSign(task.getParentTaskId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void copyTask(Long userId, BpmTaskCopyReqVO reqVO) {
|
||||||
|
processInstanceCopyService.createProcessInstanceCopy(reqVO.getCopyUserIds(), reqVO.getReason(), reqVO.getId());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 校验任务是否能被减签
|
* 校验任务是否能被减签
|
||||||
*
|
*
|
||||||
|
@ -920,8 +1028,13 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||||
if (ObjectUtil.notEqual(transactionStatus, TransactionSynchronization.STATUS_COMMITTED)) {
|
if (ObjectUtil.notEqual(transactionStatus, TransactionSynchronization.STATUS_COMMITTED)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// TODO 芋艿:可以后续优化成 getSelf();
|
||||||
// 特殊情况一:【人工审核】审批人为空,根据配置是否要自动通过、自动拒绝
|
// 特殊情况一:【人工审核】审批人为空,根据配置是否要自动通过、自动拒绝
|
||||||
if (ObjectUtil.equal(approveType, BpmUserTaskApproveTypeEnum.USER.getType())) {
|
if (ObjectUtil.equal(approveType, BpmUserTaskApproveTypeEnum.USER.getType())) {
|
||||||
|
// 如果有审批人、或者拥有人,则说明不满足情况一,不自动通过、不自动拒绝
|
||||||
|
if (!ObjectUtil.isAllEmpty(task.getAssignee(), task.getOwner())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (ObjectUtil.equal(assignEmptyHandlerType, BpmUserTaskAssignEmptyHandlerTypeEnum.APPROVE.getType())) {
|
if (ObjectUtil.equal(assignEmptyHandlerType, BpmUserTaskAssignEmptyHandlerTypeEnum.APPROVE.getType())) {
|
||||||
SpringUtil.getBean(BpmTaskService.class).approveTask(null, new BpmTaskApproveReqVO()
|
SpringUtil.getBean(BpmTaskService.class).approveTask(null, new BpmTaskApproveReqVO()
|
||||||
.setId(task.getId()).setReason(BpmReasonEnum.ASSIGN_EMPTY_APPROVE.getReason()));
|
.setId(task.getId()).setReason(BpmReasonEnum.ASSIGN_EMPTY_APPROVE.getReason()));
|
||||||
|
@ -987,7 +1100,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||||
}
|
}
|
||||||
// 审批人与提交人为同一人时,根据 BpmUserTaskAssignStartUserHandlerTypeEnum 策略进行处理
|
// 审批人与提交人为同一人时,根据 BpmUserTaskAssignStartUserHandlerTypeEnum 策略进行处理
|
||||||
if (StrUtil.equals(task.getAssignee(), processInstance.getStartUserId())) {
|
if (StrUtil.equals(task.getAssignee(), processInstance.getStartUserId())) {
|
||||||
// 判断是否为回退或者驳回:如果是回退或者驳回不走这个策略
|
// 判断是否为退回或者驳回:如果是退回或者驳回不走这个策略
|
||||||
// TODO 芋艿:【优化】未来有没更好的判断方式?!另外,还要考虑清理机制。就是说,下次处理了之后,就移除这个标识
|
// TODO 芋艿:【优化】未来有没更好的判断方式?!另外,还要考虑清理机制。就是说,下次处理了之后,就移除这个标识
|
||||||
Boolean returnTaskFlag = runtimeService.getVariable(processInstance.getProcessInstanceId(),
|
Boolean returnTaskFlag = runtimeService.getVariable(processInstance.getProcessInstanceId(),
|
||||||
String.format(PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, task.getTaskDefinitionKey()), Boolean.class);
|
String.format(PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, task.getTaskDefinitionKey()), Boolean.class);
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
package cn.iocoder.yudao.module.bpm.service.task.bo;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import static cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO.ApprovalNodeInfo;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 已经进行中的审批节点 Response BO
|
|
||||||
*
|
|
||||||
* @author jason
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class AlreadyRunApproveNodeRespBO {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 审批节点信息数组
|
|
||||||
*/
|
|
||||||
private List<ApprovalNodeInfo> approveNodes;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 已运行的节点 ID 数组 (对应 Bpmn XML 节点 id)
|
|
||||||
*/
|
|
||||||
private Set<String> runNodeIds;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 正在运行的节点的审批信息(key: activityId, value: 审批信息)
|
|
||||||
* <p>
|
|
||||||
* 用于依次审批,需要加上候选人信息
|
|
||||||
*/
|
|
||||||
private Map<String, ApprovalNodeInfo> runningApprovalNodes;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,31 +1,41 @@
|
||||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate;
|
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate;
|
||||||
|
|
||||||
import cn.hutool.core.map.MapUtil;
|
import cn.hutool.core.map.MapUtil;
|
||||||
|
import cn.hutool.extra.spring.SpringUtil;
|
||||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.BpmTaskCandidateUserStrategy;
|
import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignStartUserHandlerTypeEnum;
|
||||||
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.other.BpmTaskCandidateAssignEmptyStrategy;
|
||||||
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user.BpmTaskCandidateUserStrategy;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
|
||||||
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
|
||||||
|
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
|
||||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||||
|
import org.flowable.bpmn.model.BpmnModel;
|
||||||
|
import org.flowable.bpmn.model.ExtensionElement;
|
||||||
|
import org.flowable.bpmn.model.FlowElement;
|
||||||
import org.flowable.bpmn.model.UserTask;
|
import org.flowable.bpmn.model.UserTask;
|
||||||
import org.flowable.engine.delegate.DelegateExecution;
|
import org.flowable.engine.delegate.DelegateExecution;
|
||||||
|
import org.flowable.engine.runtime.ProcessInstance;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockedStatic;
|
||||||
import org.mockito.Spy;
|
import org.mockito.Spy;
|
||||||
|
import org.mockito.internal.util.collections.Sets;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
|
import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
|
||||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||||
|
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
|
||||||
|
import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_NAMESPACE;
|
||||||
|
import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_PREFIX;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.*;
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link BpmTaskCandidateInvoker} 的单元测试
|
* {@link BpmTaskCandidateInvoker} 的单元测试
|
||||||
|
@ -39,44 +49,207 @@ public class BpmTaskCandidateInvokerTest extends BaseMockitoUnitTest {
|
||||||
@Mock
|
@Mock
|
||||||
private AdminUserApi adminUserApi;
|
private AdminUserApi adminUserApi;
|
||||||
|
|
||||||
@Spy
|
@Mock
|
||||||
private BpmTaskCandidateStrategy strategy ;
|
private BpmProcessInstanceService processInstanceService;
|
||||||
|
|
||||||
@Spy
|
@Spy
|
||||||
private List<BpmTaskCandidateStrategy> strategyList ;
|
private BpmTaskCandidateStrategy userStrategy;
|
||||||
|
@Mock
|
||||||
|
private BpmTaskCandidateAssignEmptyStrategy emptyStrategy;
|
||||||
|
|
||||||
|
@Spy
|
||||||
|
private List<BpmTaskCandidateStrategy> strategyList;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
strategy = new BpmTaskCandidateUserStrategy(adminUserApi); // 创建strategy实例
|
userStrategy = new BpmTaskCandidateUserStrategy(); // 创建 strategy 实例
|
||||||
strategyList = Collections.singletonList(strategy); // 创建strategyList
|
when(emptyStrategy.getStrategy()).thenReturn(BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY);
|
||||||
|
strategyList = List.of(userStrategy, emptyStrategy); // 创建 strategyList
|
||||||
taskCandidateInvoker = new BpmTaskCandidateInvoker(strategyList, adminUserApi);
|
taskCandidateInvoker = new BpmTaskCandidateInvoker(strategyList, adminUserApi);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 场景:成功计算到候选人,但是移除了发起人的用户
|
||||||
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testCalculateUsers() {
|
public void testCalculateUsersByTask_some() {
|
||||||
// 准备参数
|
try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
|
||||||
String param = "1,2";
|
// 准备参数
|
||||||
DelegateExecution execution = mock(DelegateExecution.class);
|
String param = "1,2";
|
||||||
// mock 方法(DelegateExecution)
|
DelegateExecution execution = mock(DelegateExecution.class);
|
||||||
UserTask userTask = mock(UserTask.class);
|
// mock 方法(DelegateExecution)
|
||||||
when(execution.getCurrentFlowElement()).thenReturn(userTask);
|
UserTask userTask = mock(UserTask.class);
|
||||||
when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY)))
|
String processInstanceId = randomString();
|
||||||
.thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy().toString());
|
when(execution.getProcessInstanceId()).thenReturn(processInstanceId);
|
||||||
when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_PARAM)))
|
when(execution.getCurrentFlowElement()).thenReturn(userTask);
|
||||||
.thenReturn(param);
|
when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY)))
|
||||||
// mock 方法(adminUserApi)
|
.thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy().toString());
|
||||||
AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L)
|
when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_PARAM)))
|
||||||
.setStatus(CommonStatusEnum.ENABLE.getStatus()));
|
.thenReturn(param);
|
||||||
AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L)
|
// mock 方法(adminUserApi)
|
||||||
.setStatus(CommonStatusEnum.ENABLE.getStatus()));
|
AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L)
|
||||||
Map<Long, AdminUserRespDTO> userMap = MapUtil.builder(user1.getId(), user1)
|
.setStatus(CommonStatusEnum.ENABLE.getStatus()));
|
||||||
.put(user2.getId(), user2).build();
|
AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L)
|
||||||
when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap);
|
.setStatus(CommonStatusEnum.ENABLE.getStatus()));
|
||||||
|
Map<Long, AdminUserRespDTO> userMap = MapUtil.builder(user1.getId(), user1)
|
||||||
|
.put(user2.getId(), user2).build();
|
||||||
|
when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap);
|
||||||
|
// mock 移除发起人的用户
|
||||||
|
springUtilMockedStatic.when(() -> SpringUtil.getBean(BpmProcessInstanceService.class))
|
||||||
|
.thenReturn(processInstanceService);
|
||||||
|
ProcessInstance processInstance = mock(ProcessInstance.class);
|
||||||
|
when(processInstanceService.getProcessInstance(eq(processInstanceId))).thenReturn(processInstance);
|
||||||
|
when(processInstance.getStartUserId()).thenReturn("1");
|
||||||
|
mockFlowElementExtensionElement(userTask, BpmnModelConstants.USER_TASK_ASSIGN_START_USER_HANDLER_TYPE,
|
||||||
|
String.valueOf(BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType()));
|
||||||
|
|
||||||
// 调用
|
// 调用
|
||||||
Set<Long> results = taskCandidateInvoker.calculateUsers(execution);
|
Set<Long> results = taskCandidateInvoker.calculateUsersByTask(execution);
|
||||||
// 断言
|
// 断言
|
||||||
assertEquals(asSet(1L, 2L), results);
|
assertEquals(asSet(2L), results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 场景:没有计算到候选人,但是被禁用移除,最终通过 empty 进行分配
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testCalculateUsersByTask_none() {
|
||||||
|
try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
|
||||||
|
// 准备参数
|
||||||
|
String param = "1,2";
|
||||||
|
DelegateExecution execution = mock(DelegateExecution.class);
|
||||||
|
// mock 方法(DelegateExecution)
|
||||||
|
UserTask userTask = mock(UserTask.class);
|
||||||
|
String processInstanceId = randomString();
|
||||||
|
when(execution.getProcessInstanceId()).thenReturn(processInstanceId);
|
||||||
|
when(execution.getCurrentFlowElement()).thenReturn(userTask);
|
||||||
|
when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY)))
|
||||||
|
.thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy().toString());
|
||||||
|
when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_PARAM)))
|
||||||
|
.thenReturn(param);
|
||||||
|
// mock 方法(adminUserApi)
|
||||||
|
AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L)
|
||||||
|
.setStatus(CommonStatusEnum.DISABLE.getStatus()));
|
||||||
|
AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L)
|
||||||
|
.setStatus(CommonStatusEnum.DISABLE.getStatus()));
|
||||||
|
Map<Long, AdminUserRespDTO> userMap = MapUtil.builder(user1.getId(), user1)
|
||||||
|
.put(user2.getId(), user2).build();
|
||||||
|
when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap);
|
||||||
|
// mock 方法(empty)
|
||||||
|
when(emptyStrategy.calculateUsersByTask(same(execution), same(param)))
|
||||||
|
.thenReturn(Sets.newSet(2L));
|
||||||
|
// mock 移除发起人的用户
|
||||||
|
springUtilMockedStatic.when(() -> SpringUtil.getBean(BpmProcessInstanceService.class))
|
||||||
|
.thenReturn(processInstanceService);
|
||||||
|
ProcessInstance processInstance = mock(ProcessInstance.class);
|
||||||
|
when(processInstanceService.getProcessInstance(eq(processInstanceId))).thenReturn(processInstance);
|
||||||
|
when(processInstance.getStartUserId()).thenReturn("1");
|
||||||
|
|
||||||
|
// 调用
|
||||||
|
Set<Long> results = taskCandidateInvoker.calculateUsersByTask(execution);
|
||||||
|
// 断言
|
||||||
|
assertEquals(asSet(2L), results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 场景:没有计算到候选人,但是被禁用移除,最终通过 empty 进行分配
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testCalculateUsersByActivity_some() {
|
||||||
|
try (MockedStatic<BpmnModelUtils> bpmnModelUtilsMockedStatic = mockStatic(BpmnModelUtils.class)) {
|
||||||
|
// 准备参数
|
||||||
|
String param = "1,2";
|
||||||
|
BpmnModel bpmnModel = mock(BpmnModel.class);
|
||||||
|
String activityId = randomString();
|
||||||
|
Long startUserId = 1L;
|
||||||
|
String processDefinitionId = randomString();
|
||||||
|
Map<String, Object> processVariables = new HashMap<>();
|
||||||
|
// mock 方法(DelegateExecution)
|
||||||
|
UserTask userTask = mock(UserTask.class);
|
||||||
|
bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseCandidateStrategy(same(userTask)))
|
||||||
|
.thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy());
|
||||||
|
bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseCandidateParam(same(userTask)))
|
||||||
|
.thenReturn(param);
|
||||||
|
bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.getFlowElementById(same(bpmnModel), eq(activityId))).thenReturn(userTask);
|
||||||
|
// mock 方法(adminUserApi)
|
||||||
|
AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L)
|
||||||
|
.setStatus(CommonStatusEnum.ENABLE.getStatus()));
|
||||||
|
AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L)
|
||||||
|
.setStatus(CommonStatusEnum.ENABLE.getStatus()));
|
||||||
|
Map<Long, AdminUserRespDTO> userMap = MapUtil.builder(user1.getId(), user1)
|
||||||
|
.put(user2.getId(), user2).build();
|
||||||
|
when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap);
|
||||||
|
// mock 移除发起人的用户
|
||||||
|
bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseAssignStartUserHandlerType(same(userTask)))
|
||||||
|
.thenReturn(BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType());
|
||||||
|
|
||||||
|
// 调用
|
||||||
|
Set<Long> results = taskCandidateInvoker.calculateUsersByActivity(bpmnModel, activityId,
|
||||||
|
startUserId, processDefinitionId, processVariables);
|
||||||
|
// 断言
|
||||||
|
assertEquals(asSet(2L), results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 场景:成功计算到候选人,但是移除了发起人的用户
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testCalculateUsersByActivity_none() {
|
||||||
|
try (MockedStatic<BpmnModelUtils> bpmnModelUtilsMockedStatic = mockStatic(BpmnModelUtils.class)) {
|
||||||
|
// 准备参数
|
||||||
|
String param = "1,2";
|
||||||
|
BpmnModel bpmnModel = mock(BpmnModel.class);
|
||||||
|
String activityId = randomString();
|
||||||
|
Long startUserId = 1L;
|
||||||
|
String processDefinitionId = randomString();
|
||||||
|
Map<String, Object> processVariables = new HashMap<>();
|
||||||
|
// mock 方法(DelegateExecution)
|
||||||
|
UserTask userTask = mock(UserTask.class);
|
||||||
|
bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseCandidateStrategy(same(userTask)))
|
||||||
|
.thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy());
|
||||||
|
bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseCandidateParam(same(userTask)))
|
||||||
|
.thenReturn(param);
|
||||||
|
bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.getFlowElementById(same(bpmnModel), eq(activityId))).thenReturn(userTask);
|
||||||
|
// mock 方法(adminUserApi)
|
||||||
|
AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L)
|
||||||
|
.setStatus(CommonStatusEnum.DISABLE.getStatus()));
|
||||||
|
AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L)
|
||||||
|
.setStatus(CommonStatusEnum.DISABLE.getStatus()));
|
||||||
|
Map<Long, AdminUserRespDTO> userMap = MapUtil.builder(user1.getId(), user1)
|
||||||
|
.put(user2.getId(), user2).build();
|
||||||
|
when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap);
|
||||||
|
// mock 方法(empty)
|
||||||
|
when(emptyStrategy.calculateUsersByActivity(same(bpmnModel), eq(activityId),
|
||||||
|
eq(param), same(startUserId), same(processDefinitionId), same(processVariables)))
|
||||||
|
.thenReturn(Sets.newSet(2L));
|
||||||
|
|
||||||
|
// 调用
|
||||||
|
Set<Long> results = taskCandidateInvoker.calculateUsersByActivity(bpmnModel, activityId,
|
||||||
|
startUserId, processDefinitionId, processVariables);
|
||||||
|
// 断言
|
||||||
|
assertEquals(asSet(2L), results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void mockFlowElementExtensionElement(FlowElement element, String name, String value) {
|
||||||
|
if (value == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ExtensionElement extensionElement = new ExtensionElement();
|
||||||
|
extensionElement.setNamespace(FLOWABLE_EXTENSIONS_NAMESPACE);
|
||||||
|
extensionElement.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX);
|
||||||
|
extensionElement.setElementText(value);
|
||||||
|
extensionElement.setName(name);
|
||||||
|
// mock
|
||||||
|
Map<String, List<ExtensionElement>> extensionElements = element.getExtensionElements();
|
||||||
|
if (extensionElements == null) {
|
||||||
|
extensionElements = new LinkedHashMap<>();
|
||||||
|
}
|
||||||
|
extensionElements.put(name, Collections.singletonList(extensionElement));
|
||||||
|
when(element.getExtensionElements()).thenReturn(extensionElements);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||||
|
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||||
|
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||||
|
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
||||||
|
import org.assertj.core.util.Sets;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.stubbing.Answer;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||||
|
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
public class BpmTaskCandidateDeptLeaderMultiStrategyTest extends BaseMockitoUnitTest {
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private BpmTaskCandidateDeptLeaderMultiStrategy strategy;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private DeptApi deptApi;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCalculateUsers() {
|
||||||
|
// 准备参数
|
||||||
|
String param = "10,20|2";
|
||||||
|
// mock 方法
|
||||||
|
when(deptApi.getDept(any())).thenAnswer((Answer< CommonResult<DeptRespDTO>>) invocationOnMock -> {
|
||||||
|
Long deptId = invocationOnMock.getArgument(0);
|
||||||
|
return success(randomPojo(DeptRespDTO.class, o -> o.setId(deptId).setParentId(deptId * 100).setLeaderUserId(deptId + 1)));
|
||||||
|
});
|
||||||
|
|
||||||
|
// 调用
|
||||||
|
Set<Long> userIds = strategy.calculateUsers(param);
|
||||||
|
// 断言结果
|
||||||
|
assertEquals(Sets.newLinkedHashSet(11L, 1001L, 21L, 2001L), userIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,9 +1,10 @@
|
||||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
|
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
|
||||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||||
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||||
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
||||||
import org.junit.jupiter.api.Disabled;
|
import org.assertj.core.util.Sets;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
|
@ -11,14 +12,12 @@ import org.mockito.Mock;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||||
import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
|
|
||||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
@Disabled // TODO 芋艿:临时注释
|
|
||||||
public class BpmTaskCandidateDeptLeaderStrategyTest extends BaseMockitoUnitTest {
|
public class BpmTaskCandidateDeptLeaderStrategyTest extends BaseMockitoUnitTest {
|
||||||
|
|
||||||
@InjectMocks
|
@InjectMocks
|
||||||
|
@ -30,16 +29,16 @@ public class BpmTaskCandidateDeptLeaderStrategyTest extends BaseMockitoUnitTest
|
||||||
@Test
|
@Test
|
||||||
public void testCalculateUsers() {
|
public void testCalculateUsers() {
|
||||||
// 准备参数
|
// 准备参数
|
||||||
String param = "1,2";
|
String param = "10,20";
|
||||||
// mock 方法
|
// mock 方法
|
||||||
DeptRespDTO dept1 = randomPojo(DeptRespDTO.class, o -> o.setLeaderUserId(11L));
|
when(deptApi.getDeptList(eq(SetUtils.asSet(10L, 20L)))).thenReturn(success(asList(
|
||||||
DeptRespDTO dept2 = randomPojo(DeptRespDTO.class, o -> o.setLeaderUserId(22L));
|
randomPojo(DeptRespDTO.class, o -> o.setId(10L).setParentId(10L).setLeaderUserId(11L)),
|
||||||
when(deptApi.getDeptList(eq(asSet(1L, 2L)))).thenReturn(success(asList(dept1, dept2)));
|
randomPojo(DeptRespDTO.class, o -> o.setId(20L).setParentId(20L).setLeaderUserId(21L)))));
|
||||||
|
|
||||||
// 调用
|
// 调用
|
||||||
Set<Long> results = strategy.calculateUsers(null, param);
|
Set<Long> userIds = strategy.calculateUsers(param);
|
||||||
// 断言
|
// 断言结果
|
||||||
assertEquals(asSet(11L, 22L), results);
|
assertEquals(Sets.newLinkedHashSet(11L, 21L), userIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,45 +1,47 @@
|
||||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
|
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
|
||||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||||
|
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||||
import org.junit.jupiter.api.Disabled;
|
import org.assertj.core.util.Sets;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||||
import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
|
import static java.util.Arrays.asList;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
@Disabled // TODO 芋艿:临时注释
|
|
||||||
public class BpmTaskCandidateDeptMemberStrategyTest extends BaseMockitoUnitTest {
|
public class BpmTaskCandidateDeptMemberStrategyTest extends BaseMockitoUnitTest {
|
||||||
|
|
||||||
@InjectMocks
|
@InjectMocks
|
||||||
private BpmTaskCandidateDeptMemberStrategy strategy;
|
private BpmTaskCandidateDeptMemberStrategy strategy;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private DeptApi deptApi;
|
||||||
@Mock
|
@Mock
|
||||||
private AdminUserApi adminUserApi;
|
private AdminUserApi adminUserApi;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCalculateUsers() {
|
public void testCalculateUsers() {
|
||||||
// 准备参数
|
// 准备参数
|
||||||
String param = "11,22";
|
String param = "10,20";
|
||||||
// mock 方法
|
// mock 方法
|
||||||
List<AdminUserRespDTO> users = convertList(asSet(11L, 22L),
|
when(adminUserApi.getUserListByDeptIds(eq(SetUtils.asSet(10L, 20L)))).thenReturn(success(asList(
|
||||||
id -> new AdminUserRespDTO().setId(id));
|
randomPojo(AdminUserRespDTO.class, o -> o.setId(11L)),
|
||||||
when(adminUserApi.getUserListByDeptIds(eq(asSet(11L, 22L)))).thenReturn(success(users));
|
randomPojo(AdminUserRespDTO.class, o -> o.setId(21L)))));
|
||||||
|
|
||||||
// 调用
|
// 调用
|
||||||
Set<Long> results = strategy.calculateUsers(null, param);
|
Set<Long> userIds = strategy.calculateUsers(param);
|
||||||
// 断言
|
// 断言结果
|
||||||
assertEquals(asSet(11L, 22L), results);
|
assertEquals(Sets.newLinkedHashSet(11L, 21L), userIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||||
|
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||||
|
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
|
||||||
|
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||||
|
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
||||||
|
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||||
|
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||||
|
import org.assertj.core.util.Sets;
|
||||||
|
import org.flowable.engine.delegate.DelegateExecution;
|
||||||
|
import org.flowable.engine.runtime.ProcessInstance;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.stubbing.Answer;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||||
|
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
public class BpmTaskCandidateStartUserDeptLeaderMultiStrategyTest extends BaseMockitoUnitTest {
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private BpmTaskCandidateStartUserDeptLeaderMultiStrategy strategy;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private BpmProcessInstanceService processInstanceService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private AdminUserApi adminUserApi;
|
||||||
|
@Mock
|
||||||
|
private DeptApi deptApi;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCalculateUsersByTask() {
|
||||||
|
// 准备参数
|
||||||
|
String param = "2";
|
||||||
|
// mock 方法(获得流程发起人)
|
||||||
|
Long startUserId = 1L;
|
||||||
|
ProcessInstance processInstance = mock(ProcessInstance.class);
|
||||||
|
DelegateExecution execution = mock(DelegateExecution.class);
|
||||||
|
when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))).thenReturn(processInstance);
|
||||||
|
when(processInstance.getStartUserId()).thenReturn(startUserId.toString());
|
||||||
|
// mock 方法(获取发起人的 multi 部门负责人)
|
||||||
|
mockGetStartUserDept(startUserId);
|
||||||
|
|
||||||
|
// 调用
|
||||||
|
Set<Long> userIds = strategy.calculateUsersByTask(execution, param);
|
||||||
|
// 断言
|
||||||
|
assertEquals(Sets.newLinkedHashSet(11L, 1001L), userIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCalculateUsersByActivity() {
|
||||||
|
// 准备参数
|
||||||
|
String param = "2";
|
||||||
|
// mock 方法
|
||||||
|
Long startUserId = 1L;
|
||||||
|
mockGetStartUserDept(startUserId);
|
||||||
|
|
||||||
|
// 调用
|
||||||
|
Set<Long> userIds = strategy.calculateUsersByActivity(null, null, param,
|
||||||
|
startUserId, null, null);
|
||||||
|
// 断言
|
||||||
|
assertEquals(Sets.newLinkedHashSet(11L, 1001L), userIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mockGetStartUserDept(Long startUserId) {
|
||||||
|
when(adminUserApi.getUser(eq(startUserId))).thenReturn(
|
||||||
|
success(randomPojo(AdminUserRespDTO.class, o -> o.setId(startUserId).setDeptId(10L))));
|
||||||
|
when(deptApi.getDept(any())).thenAnswer((Answer< CommonResult<DeptRespDTO>>) invocationOnMock -> {
|
||||||
|
Long deptId = invocationOnMock.getArgument(0);
|
||||||
|
return success(randomPojo(DeptRespDTO.class, o -> o.setId(deptId).setParentId(deptId * 100).setLeaderUserId(deptId + 1)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||||
|
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||||
|
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
|
||||||
|
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||||
|
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
||||||
|
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||||
|
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||||
|
import org.assertj.core.util.Sets;
|
||||||
|
import org.flowable.engine.delegate.DelegateExecution;
|
||||||
|
import org.flowable.engine.runtime.ProcessInstance;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.stubbing.Answer;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||||
|
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
public class BpmTaskCandidateStartUserDeptLeaderStrategyTest extends BaseMockitoUnitTest {
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private BpmTaskCandidateStartUserDeptLeaderStrategy strategy;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private BpmProcessInstanceService processInstanceService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private AdminUserApi adminUserApi;
|
||||||
|
@Mock
|
||||||
|
private DeptApi deptApi;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCalculateUsersByTask() {
|
||||||
|
// 准备参数
|
||||||
|
String param = "2";
|
||||||
|
// mock 方法(获得流程发起人)
|
||||||
|
Long startUserId = 1L;
|
||||||
|
ProcessInstance processInstance = mock(ProcessInstance.class);
|
||||||
|
DelegateExecution execution = mock(DelegateExecution.class);
|
||||||
|
when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))).thenReturn(processInstance);
|
||||||
|
when(processInstance.getStartUserId()).thenReturn(startUserId.toString());
|
||||||
|
// mock 方法(获取发起人的部门负责人)
|
||||||
|
mockGetStartUserDeptLeader(startUserId);
|
||||||
|
|
||||||
|
// 调用
|
||||||
|
Set<Long> userIds = strategy.calculateUsersByTask(execution, param);
|
||||||
|
// 断言
|
||||||
|
assertEquals(Sets.newLinkedHashSet(1001L), userIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetStartUserDeptLeader() {
|
||||||
|
// 准备参数
|
||||||
|
String param = "2";
|
||||||
|
// mock 方法
|
||||||
|
Long startUserId = 1L;
|
||||||
|
mockGetStartUserDeptLeader(startUserId);
|
||||||
|
|
||||||
|
// 调用
|
||||||
|
Set<Long> userIds = strategy.calculateUsersByActivity(null, null, param,
|
||||||
|
startUserId, null, null);
|
||||||
|
// 断言
|
||||||
|
assertEquals(Sets.newLinkedHashSet(1001L), userIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mockGetStartUserDeptLeader(Long startUserId) {
|
||||||
|
when(adminUserApi.getUser(eq(startUserId))).thenReturn(
|
||||||
|
success(randomPojo(AdminUserRespDTO.class, o -> o.setId(startUserId).setDeptId(10L))));
|
||||||
|
when(deptApi.getDept(any())).thenAnswer((Answer< CommonResult<DeptRespDTO>>) invocationOnMock -> {
|
||||||
|
Long deptId = invocationOnMock.getArgument(0);
|
||||||
|
return success(randomPojo(DeptRespDTO.class, o -> o.setId(deptId).setParentId(deptId * 100).setLeaderUserId(deptId + 1)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;
|
||||||
|
|
||||||
|
import cn.hutool.core.map.MapUtil;
|
||||||
|
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||||
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants;
|
||||||
|
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
|
||||||
|
import org.assertj.core.util.Sets;
|
||||||
|
import org.flowable.engine.delegate.DelegateExecution;
|
||||||
|
import org.flowable.engine.runtime.ProcessInstance;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
public class BpmTaskCandidateStartUserSelectStrategyTest extends BaseMockitoUnitTest {
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private BpmTaskCandidateStartUserSelectStrategy strategy;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private BpmProcessInstanceService processInstanceService;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCalculateUsersByTask() {
|
||||||
|
// 准备参数
|
||||||
|
String param = "2";
|
||||||
|
// mock 方法(获得流程发起人)
|
||||||
|
ProcessInstance processInstance = mock(ProcessInstance.class);
|
||||||
|
DelegateExecution execution = mock(DelegateExecution.class);
|
||||||
|
when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))).thenReturn(processInstance);
|
||||||
|
when(execution.getCurrentActivityId()).thenReturn("activity_001");
|
||||||
|
// mock 方法(FlowableUtils)
|
||||||
|
Map<String, Object> processVariables = new HashMap<>();
|
||||||
|
processVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES,
|
||||||
|
MapUtil.of("activity_001", List.of(1L, 2L)));
|
||||||
|
when(processInstance.getProcessVariables()).thenReturn(processVariables);
|
||||||
|
|
||||||
|
// 调用
|
||||||
|
Set<Long> userIds = strategy.calculateUsersByTask(execution, param);
|
||||||
|
// 断言
|
||||||
|
assertEquals(Sets.newLinkedHashSet(1L, 2L), userIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCalculateUsersByActivity() {
|
||||||
|
// 准备参数
|
||||||
|
String activityId = "activity_001";
|
||||||
|
Map<String, Object> processVariables = new HashMap<>();
|
||||||
|
processVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES,
|
||||||
|
MapUtil.of("activity_001", List.of(1L, 2L)));
|
||||||
|
|
||||||
|
// 调用
|
||||||
|
Set<Long> userIds = strategy.calculateUsersByActivity(null, activityId, null,
|
||||||
|
null, null, processVariables);
|
||||||
|
// 断言
|
||||||
|
assertEquals(Sets.newLinkedHashSet(1L, 2L), userIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.other;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.ListUtil;
|
||||||
|
import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
|
||||||
|
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||||
|
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
|
||||||
|
import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignEmptyHandlerTypeEnum;
|
||||||
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
|
||||||
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
|
||||||
|
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
|
||||||
|
import org.flowable.bpmn.model.BpmnModel;
|
||||||
|
import org.flowable.bpmn.model.FlowElement;
|
||||||
|
import org.flowable.engine.delegate.DelegateExecution;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockedStatic;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||||
|
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
public class BpmTaskCandidateAssignEmptyStrategyTest extends BaseMockitoUnitTest {
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private BpmTaskCandidateAssignEmptyStrategy strategy;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private BpmProcessDefinitionService processDefinitionService;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCalculateUsersByTask() {
|
||||||
|
try (MockedStatic<FlowableUtils> flowableUtilMockedStatic = mockStatic(FlowableUtils.class);
|
||||||
|
MockedStatic<BpmnModelUtils> bpmnModelUtilsMockedStatic = mockStatic(BpmnModelUtils.class)) {
|
||||||
|
// 准备参数
|
||||||
|
DelegateExecution execution = mock(DelegateExecution.class);
|
||||||
|
String param = randomString();
|
||||||
|
// mock 方法(execution)
|
||||||
|
String processDefinitionId = randomString();
|
||||||
|
when(execution.getProcessDefinitionId()).thenReturn(processDefinitionId);
|
||||||
|
FlowElement flowElement = mock(FlowElement.class);
|
||||||
|
when(execution.getCurrentFlowElement()).thenReturn(flowElement);
|
||||||
|
// mock 方法(parseAssignEmptyHandlerType)
|
||||||
|
bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseAssignEmptyHandlerType(same(flowElement)))
|
||||||
|
.thenReturn(BpmUserTaskAssignEmptyHandlerTypeEnum.ASSIGN_USER.getType());
|
||||||
|
bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseAssignEmptyHandlerUserIds(same(flowElement)))
|
||||||
|
.thenReturn(ListUtil.of(1L, 2L));
|
||||||
|
|
||||||
|
// 调用
|
||||||
|
Set<Long> userIds = strategy.calculateUsersByTask(execution, param);
|
||||||
|
// 断言
|
||||||
|
assertEquals(SetUtils.asSet(1L, 2L), userIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCalculateUsersByActivity() {
|
||||||
|
try (MockedStatic<BpmnModelUtils> bpmnModelUtilsMockedStatic = mockStatic(BpmnModelUtils.class)) {
|
||||||
|
// 准备参数
|
||||||
|
String processDefinitionId = randomString();
|
||||||
|
String activityId = randomString();
|
||||||
|
String param = randomString();
|
||||||
|
// mock 方法(getFlowElementById)
|
||||||
|
FlowElement flowElement = mock(FlowElement.class);
|
||||||
|
BpmnModel bpmnModel = mock(BpmnModel.class);
|
||||||
|
bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.getFlowElementById(same(bpmnModel), eq(activityId)))
|
||||||
|
.thenReturn(flowElement);
|
||||||
|
// mock 方法(parseAssignEmptyHandlerType)
|
||||||
|
bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseAssignEmptyHandlerType(same(flowElement)))
|
||||||
|
.thenReturn(BpmUserTaskAssignEmptyHandlerTypeEnum.ASSIGN_ADMIN.getType());
|
||||||
|
// mock 方法(getProcessDefinitionInfo)
|
||||||
|
BpmProcessDefinitionInfoDO processDefinition = randomPojo(BpmProcessDefinitionInfoDO.class,
|
||||||
|
o -> o.setManagerUserIds(ListUtil.of(1L, 2L)));
|
||||||
|
when(processDefinitionService.getProcessDefinitionInfo(eq(processDefinitionId))).thenReturn(processDefinition);
|
||||||
|
|
||||||
|
// 调用
|
||||||
|
Set<Long> userIds = strategy.calculateUsersByActivity(bpmnModel, activityId, param,
|
||||||
|
null, processDefinitionId, null);
|
||||||
|
// 断言
|
||||||
|
assertEquals(SetUtils.asSet(1L, 2L), userIds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
|
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.other;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
|
||||||
|
@ -8,6 +8,8 @@ import org.junit.jupiter.api.Test;
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
import org.mockito.MockedStatic;
|
import org.mockito.MockedStatic;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
|
import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
|
||||||
|
@ -22,7 +24,7 @@ public class BpmTaskCandidateExpressionStrategyTest extends BaseMockitoUnitTest
|
||||||
private BpmTaskCandidateExpressionStrategy strategy;
|
private BpmTaskCandidateExpressionStrategy strategy;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCalculateUsers() {
|
public void testCalculateUsersByTask() {
|
||||||
try (MockedStatic<FlowableUtils> flowableUtilMockedStatic = mockStatic(FlowableUtils.class)) {
|
try (MockedStatic<FlowableUtils> flowableUtilMockedStatic = mockStatic(FlowableUtils.class)) {
|
||||||
// 准备参数
|
// 准备参数
|
||||||
String param = "1,2";
|
String param = "1,2";
|
||||||
|
@ -32,7 +34,25 @@ public class BpmTaskCandidateExpressionStrategyTest extends BaseMockitoUnitTest
|
||||||
.thenReturn(asSet(1L, 2L));
|
.thenReturn(asSet(1L, 2L));
|
||||||
|
|
||||||
// 调用
|
// 调用
|
||||||
Set<Long> results = strategy.calculateUsers(execution, param);
|
Set<Long> results = strategy.calculateUsersByTask(execution, param);
|
||||||
|
// 断言
|
||||||
|
assertEquals(asSet(1L, 2L), results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCalculateUsersByActivity() {
|
||||||
|
try (MockedStatic<FlowableUtils> flowableUtilMockedStatic = mockStatic(FlowableUtils.class)) {
|
||||||
|
// 准备参数
|
||||||
|
String param = "1,2";
|
||||||
|
Map<String, Object> processVariables = new HashMap<>();
|
||||||
|
// mock 方法
|
||||||
|
flowableUtilMockedStatic.when(() -> FlowableUtils.getExpressionValue(same(processVariables), eq(param)))
|
||||||
|
.thenReturn(asSet(1L, 2L));
|
||||||
|
|
||||||
|
// 调用
|
||||||
|
Set<Long> results = strategy.calculateUsersByActivity(null, null, param,
|
||||||
|
null, null, processVariables);
|
||||||
// 断言
|
// 断言
|
||||||
assertEquals(asSet(1L, 2L), results);
|
assertEquals(asSet(1L, 2L), results);
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
|
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||||
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;
|
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;
|
||||||
|
@ -36,9 +36,9 @@ public class BpmTaskCandidateGroupStrategyTest extends BaseMockitoUnitTest {
|
||||||
when(userGroupService.getUserGroupList(eq(asSet(1L, 2L)))).thenReturn(Arrays.asList(userGroup1, userGroup2));
|
when(userGroupService.getUserGroupList(eq(asSet(1L, 2L)))).thenReturn(Arrays.asList(userGroup1, userGroup2));
|
||||||
|
|
||||||
// 调用
|
// 调用
|
||||||
Set<Long> results = strategy.calculateUsers(null, param);
|
Set<Long> userIds = strategy.calculateUsersByTask(null, param);
|
||||||
// 断言
|
// 断言
|
||||||
assertEquals(asSet(11L, 12L, 21L, 22L), results);
|
assertEquals(asSet(11L, 12L, 21L, 22L), userIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
|
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||||
import cn.iocoder.yudao.module.system.api.dept.PostApi;
|
import cn.iocoder.yudao.module.system.api.dept.PostApi;
|
||||||
|
@ -40,9 +40,9 @@ public class BpmTaskCandidatePostStrategyTest extends BaseMockitoUnitTest {
|
||||||
when(adminUserApi.getUserListByPostIds(eq(asSet(1L, 2L)))).thenReturn(success(users));
|
when(adminUserApi.getUserListByPostIds(eq(asSet(1L, 2L)))).thenReturn(success(users));
|
||||||
|
|
||||||
// 调用
|
// 调用
|
||||||
Set<Long> results = strategy.calculateUsers(null, param);
|
Set<Long> userIds = strategy.calculateUsersByTask(null, param);
|
||||||
// 断言
|
// 断言
|
||||||
assertEquals(asSet(11L, 22L), results);
|
assertEquals(asSet(11L, 22L), userIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
|
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||||
import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
|
import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
|
||||||
|
@ -36,9 +36,9 @@ public class BpmTaskCandidateRoleStrategyTest extends BaseMockitoUnitTest {
|
||||||
.thenReturn(success(asSet(11L, 22L)));
|
.thenReturn(success(asSet(11L, 22L)));
|
||||||
|
|
||||||
// 调用
|
// 调用
|
||||||
Set<Long> results = strategy.calculateUsers(null, param);
|
Set<Long> userIds = strategy.calculateUsersByTask(null, param);
|
||||||
// 断言
|
// 断言
|
||||||
assertEquals(asSet(11L, 22L), results);
|
assertEquals(asSet(11L, 22L), userIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue