Merge branch 'master-jdk17' of https://gitee.com/zhijiantianya/yudao-cloud
# Conflicts: # yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmModelController.java # yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmActivityController.java # yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java # yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java # yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmFormFieldsPermissionReqVO.java # yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java # yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskReturnReqVO.java # yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderMultiStrategy.java # yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderStrategy.java # yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategy.java # yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateAssignEmptyStrategy.java # yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategy.java # yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateStartUserStrategy.java # yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java # yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelService.java # yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java # yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmActivityServiceImpl.java # yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java # yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/vo/DiscountActivityBaseVO.javapull/158/MERGE
commit
14c49629b7
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 种任务分配规则 |
|
||||
| 🚀 | 流程表单 | 拖动表单元素生成相应的工作流表单,覆盖 Element UI 所有的表单组件 |
|
||||
| 🚀 | 用户分组 | 自定义用户分组,可用于工作流的审批分组 |
|
||||
| 🚀 | 我的流程 | 查看我发起的工作流程,支持新建、取消流程等操作,高亮流程图、审批时间线 |
|
||||
| 🚀 | 待办任务 | 查看自己【未】审批的工作任务,支持通过、不通过、转发、委派、退回等操作 |
|
||||
| 🚀 | 已办任务 | 查看自己【已】审批的工作任务,未来会支持回退操作 |
|
||||
| 🚀 | OA 请假 | 作为业务自定义接入工作流的使用示例,只需创建请求对应的工作流程,即可进行审批 |
|
||||
| | 功能 | 描述 |
|
||||
|----|-------|-----------------------------------------|
|
||||
| 🚀 | 流程模型 | 配置工作流的流程模型,支持 BPMN 和仿钉钉/飞书设计器 |
|
||||
| 🚀 | 流程表单 | 拖动表单元素生成相应的工作流表单,覆盖 Element UI 所有的表单组件 |
|
||||
| 🚀 | 用户分组 | 自定义用户分组,可用于工作流的审批分组 |
|
||||
| 🚀 | 我的流程 | 查看我发起的工作流程,支持新建、取消流程等操作,高亮流程图、审批时间线 |
|
||||
| 🚀 | 待办任务 | 查看自己【未】审批的工作任务,支持通过、不通过、转派、委派、退回、加减签等操作 |
|
||||
| 🚀 | 已办任务 | 查看自己【已】审批的工作任务,支持流程预测,展示未来审批人信息 |
|
||||
| 🚀 | OA 请假 | 作为业务自定义接入工作流的使用示例,只需创建请求对应的工作流程,即可进行审批 |
|
||||
|
||||
![功能图](/.image/common/bpm-feature.png)
|
||||
|
||||
| BPMN 设计器 | 钉钉/飞书设计器 |
|
||||
|------------------------------|--------------------------------|
|
||||
| ![](/.image/工作流设计器-bpmn.jpg) | ![](/.image/工作流设计器-simple.jpg) |
|
||||
|
||||
### 支付系统
|
||||
|
||||
| | 功能 | 描述 |
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
Target Server Version : 80200 (8.2.0)
|
||||
File Encoding : 65001
|
||||
|
||||
Date: 09/11/2024 18:16:12
|
||||
Date: 23/11/2024 10:16:46
|
||||
*/
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
|
@ -91,7 +91,7 @@ CREATE TABLE `infra_api_error_log` (
|
|||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
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
|
||||
|
@ -250,7 +250,7 @@ CREATE TABLE `infra_file` (
|
|||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
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
|
||||
|
@ -1044,7 +1044,7 @@ CREATE TABLE `system_login_log` (
|
|||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
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
|
||||
|
@ -2165,7 +2165,7 @@ CREATE TABLE `system_oauth2_access_token` (
|
|||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `idx_access_token`(`access_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
|
||||
|
@ -2287,7 +2287,7 @@ CREATE TABLE `system_oauth2_refresh_token` (
|
|||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
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
|
||||
|
@ -2308,6 +2308,7 @@ CREATE TABLE `system_operate_log` (
|
|||
`sub_type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作名',
|
||||
`biz_id` bigint NOT NULL 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 '拓展字段',
|
||||
`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 '请求地址',
|
||||
|
@ -3303,7 +3304,7 @@ CREATE TABLE `system_sms_code` (
|
|||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
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
|
||||
|
@ -3344,7 +3345,7 @@ CREATE TABLE `system_sms_log` (
|
|||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
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
|
||||
|
@ -3642,7 +3643,7 @@ CREATE TABLE `system_users` (
|
|||
-- Records of system_users
|
||||
-- ----------------------------
|
||||
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 (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);
|
||||
|
|
|
@ -1126,6 +1126,55 @@ INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_t
|
|||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1534, 1, '赢单', '1', 'crm_business_end_status_type', 0, 'success', '', '', '1', '2024-04-13 23:26:57', '1', '2024-04-13 23:26:57', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1535, 2, '输单', '2', 'crm_business_end_status_type', 0, 'primary', '', '', '1', '2024-04-13 23:27:31', '1', '2024-04-13 23:27:31', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1536, 3, '无效', '3', 'crm_business_end_status_type', 0, 'info', '', '', '1', '2024-04-13 23:27:59', '1', '2024-04-13 23:27:59', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1537, 1, 'OpenAI', 'OpenAI', 'ai_platform', 0, '', '', '', '1', '2024-05-09 22:33:47', '1', '2024-05-09 22:58:46', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1538, 2, 'Ollama', 'Ollama', 'ai_platform', 0, '', '', '', '1', '2024-05-17 23:02:55', '1', '2024-05-17 23:02:55', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1539, 3, '文心一言', 'YiYan', 'ai_platform', 0, '', '', '', '1', '2024-05-18 09:24:20', '1', '2024-05-18 09:29:01', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1540, 4, '讯飞星火', 'XingHuo', 'ai_platform', 0, '', '', '', '1', '2024-05-18 10:08:56', '1', '2024-05-18 10:08:56', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1541, 5, '通义千问', 'TongYi', 'ai_platform', 0, '', '', '', '1', '2024-05-18 10:32:29', '1', '2024-07-06 15:42:29', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1542, 6, 'StableDiffusion', 'StableDiffusion', 'ai_platform', 0, '', '', '', '1', '2024-06-01 15:09:31', '1', '2024-06-01 15:10:25', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1543, 10, '进行中', '10', 'ai_image_status', 0, 'primary', '', '', '1', '2024-06-26 20:51:41', '1', '2024-06-26 20:52:48', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1544, 20, '已完成', '20', 'ai_image_status', 0, 'success', '', '', '1', '2024-06-26 20:52:07', '1', '2024-06-26 20:52:41', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1545, 30, '已失败', '30', 'ai_image_status', 0, 'warning', '', '', '1', '2024-06-26 20:52:25', '1', '2024-06-26 20:52:35', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1546, 7, 'Midjourney', 'Midjourney', 'ai_platform', 0, '', '', '', '1', '2024-06-26 22:14:46', '1', '2024-06-26 22:14:46', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1547, 10, '进行中', '10', 'ai_music_status', 0, 'primary', '', '', '1', '2024-06-27 22:45:22', '1', '2024-06-28 00:56:17', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1548, 20, '已完成', '20', 'ai_music_status', 0, 'success', '', '', '1', '2024-06-27 22:45:33', '1', '2024-06-28 00:56:18', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1549, 30, '已失败', '30', 'ai_music_status', 0, 'danger', '', '', '1', '2024-06-27 22:45:44', '1', '2024-06-28 00:56:19', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1550, 1, '歌词模式', '1', 'ai_generate_mode', 0, '', '', '', '1', '2024-06-27 22:46:31', '1', '2024-06-28 01:22:25', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1551, 2, '描述模式', '2', 'ai_generate_mode', 0, '', '', '', '1', '2024-06-27 22:46:37', '1', '2024-06-28 01:22:24', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1552, 8, 'Suno', 'Suno', 'ai_platform', 0, '', '', '', '1', '2024-06-29 09:13:36', '1', '2024-06-29 09:13:41', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1553, 9, 'DeepSeek', 'DeepSeek', 'ai_platform', 0, '', '', '', '1', '2024-07-06 12:04:30', '1', '2024-07-06 12:05:20', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1554, 10, '智谱', 'ZhiPu', 'ai_platform', 0, '', '', '', '1', '2024-07-06 18:00:35', '1', '2024-07-06 18:00:35', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1555, 4, '长', '4', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:49:03', '1', '2024-07-07 15:49:03', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1556, 5, '段落', '5', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:49:54', '1', '2024-07-07 15:49:54', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1557, 6, '文章', '6', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:50:05', '1', '2024-07-07 15:50:05', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1558, 7, '博客文章', '7', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:50:23', '1', '2024-07-07 15:50:23', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1559, 8, '想法', '8', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:50:31', '1', '2024-07-07 15:50:31', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1560, 9, '大纲', '9', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:50:37', '1', '2024-07-07 15:50:37', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1561, 1, '自动', '1', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:51:06', '1', '2024-07-07 15:51:06', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1562, 2, '友善', '2', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:51:19', '1', '2024-07-07 15:51:19', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1563, 3, '随意', '3', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:51:27', '1', '2024-07-07 15:51:27', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1564, 4, '友好', '4', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:51:37', '1', '2024-07-07 15:51:37', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1565, 5, '专业', '5', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:51:49', '1', '2024-07-07 15:52:02', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1566, 6, '诙谐', '6', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:52:15', '1', '2024-07-07 15:52:15', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1567, 7, '有趣', '7', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:52:24', '1', '2024-07-07 15:52:24', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1568, 8, '正式', '8', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:54:33', '1', '2024-07-07 15:54:33', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1569, 5, '段落', '5', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:49:54', '1', '2024-07-07 15:49:54', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1570, 1, '自动', '1', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:19:34', '1', '2024-07-07 15:19:34', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1571, 2, '电子邮件', '2', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:19:50', '1', '2024-07-07 15:49:30', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1572, 3, '消息', '3', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:20:01', '1', '2024-07-07 15:49:38', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1573, 4, '评论', '4', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:20:13', '1', '2024-07-07 15:49:45', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1574, 1, '自动', '1', 'ai_write_language', 0, '', '', '', '1', '2024-07-07 15:44:18', '1', '2024-07-07 15:44:18', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1575, 2, '中文', '2', 'ai_write_language', 0, '', '', '', '1', '2024-07-07 15:44:28', '1', '2024-07-07 15:44:28', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1576, 3, '英文', '3', 'ai_write_language', 0, '', '', '', '1', '2024-07-07 15:44:37', '1', '2024-07-07 15:44:37', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1577, 4, '韩语', '4', 'ai_write_language', 0, '', '', '', '1', '2024-07-07 15:46:28', '1', '2024-07-07 15:46:28', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1578, 5, '日语', '5', 'ai_write_language', 0, '', '', '', '1', '2024-07-07 15:46:44', '1', '2024-07-07 15:46:44', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1579, 1, '自动', '1', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:48:34', '1', '2024-07-07 15:48:34', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1580, 2, '短', '2', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:48:44', '1', '2024-07-07 15:48:44', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1581, 3, '中等', '3', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:48:52', '1', '2024-07-07 15:48:52', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1582, 4, '长', '4', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:49:03', '1', '2024-07-07 15:49:03', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1584, 1, '撰写', '1', 'ai_write_type', 0, '', '', '', '1', '2024-07-10 21:26:00', '1', '2024-07-10 21:26:00', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1585, 2, '回复', '2', 'ai_write_type', 0, '', '', '', '1', '2024-07-10 21:26:06', '1', '2024-07-10 21:26:06', '0');
|
||||
|
||||
COMMIT;
|
||||
-- @formatter:on
|
||||
|
||||
|
@ -2328,6 +2377,54 @@ INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon
|
|||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2755, '删除项目', 'report:go-view-project:delete', 3, 2, 2153, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 20:01:37', '1', '2024-04-24 20:01:37', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2756, '会员等级记录查询', 'member:level-record:query', 3, 10, 2325, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 20:02:32', '1', '2024-04-24 20:02:32', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2757, '会员经验记录查询', 'member:experience-record:query', 3, 11, 2325, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 20:02:51', '1', '2024-04-24 20:02:51', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2758, 'AI 大模型', '', 1, 400, 0, '/ai', 'fa:apple', '', '', 0, '1', '1', '1', '1', '2024-05-07 15:07:56', '1', '2024-05-25 12:36:12', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2759, 'AI 对话', '', 2, 1, 2758, 'chat', 'ep:message', 'ai/chat/index/index.vue', 'AiChat', 0, '1', '1', '1', '1', '2024-05-07 15:09:14', '1', '2024-07-07 17:15:36', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2760, '控制台', '', 1, 100, 2758, 'console', 'ep:setting', '', '', 0, '1', '1', '1', '1', '2024-05-09 22:39:09', '1', '2024-05-24 23:34:21', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2761, 'API 密钥', '', 2, 0, 2760, 'api-key', 'ep:key', 'ai/model/apiKey/index.vue', 'AiApiKey', 0, '1', '1', '1', '', '2024-05-09 14:52:56', '1', '2024-05-10 22:44:08', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2762, 'API 密钥查询', 'ai:api-key:query', 3, 1, 2761, '', '', '', '', 0, '1', '1', '1', '', '2024-05-09 14:52:56', '1', '2024-05-13 20:36:32', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2763, 'API 密钥创建', 'ai:api-key:create', 3, 2, 2761, '', '', '', '', 0, '1', '1', '1', '', '2024-05-09 14:52:56', '1', '2024-05-13 20:36:26', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2764, 'API 密钥更新', 'ai:api-key:update', 3, 3, 2761, '', '', '', '', 0, '1', '1', '1', '', '2024-05-09 14:52:56', '1', '2024-05-13 20:36:42', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2765, 'API 密钥删除', 'ai:api-key:delete', 3, 4, 2761, '', '', '', '', 0, '1', '1', '1', '', '2024-05-09 14:52:56', '1', '2024-05-13 20:36:48', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2767, '聊天模型', '', 2, 0, 2760, 'chat-model', 'fa-solid:abacus', 'ai/model/chatModel/index.vue', 'AiChatModel', 0, '1', '1', '1', '', '2024-05-10 14:42:48', '1', '2024-05-10 22:44:16', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2768, '聊天模型查询', 'ai:chat-model:query', 3, 1, 2767, '', '', '', '', 0, '1', '1', '1', '', '2024-05-10 14:42:48', '1', '2024-05-13 20:37:02', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2769, '聊天模型创建', 'ai:chat-model:create', 3, 2, 2767, '', '', '', '', 0, '1', '1', '1', '', '2024-05-10 14:42:48', '1', '2024-05-13 20:37:12', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2770, '聊天模型更新', 'ai:chat-model:update', 3, 3, 2767, '', '', '', '', 0, '1', '1', '1', '', '2024-05-10 14:42:48', '1', '2024-05-13 20:37:18', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2771, '聊天模型删除', 'ai:chat-model:delete', 3, 4, 2767, '', '', '', '', 0, '1', '1', '1', '', '2024-05-10 14:42:48', '1', '2024-05-13 20:37:23', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2773, '聊天角色', '', 2, 0, 2760, 'chat-role', 'fa:user-secret', 'ai/model/chatRole/index.vue', 'AiChatRole', 0, '1', '1', '1', '', '2024-05-13 12:39:28', '1', '2024-05-13 20:41:45', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2774, '聊天角色查询', 'ai:chat-role:query', 3, 1, 2773, '', '', '', NULL, 0, '1', '1', '1', '', '2024-05-13 12:39:28', '', '2024-05-13 12:39:28', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2775, '聊天角色创建', 'ai:chat-role:create', 3, 2, 2773, '', '', '', NULL, 0, '1', '1', '1', '', '2024-05-13 12:39:28', '', '2024-05-13 12:39:28', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2776, '聊天角色更新', 'ai:chat-role:update', 3, 3, 2773, '', '', '', NULL, 0, '1', '1', '1', '', '2024-05-13 12:39:28', '', '2024-05-13 12:39:28', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2777, '聊天角色删除', 'ai:chat-role:delete', 3, 4, 2773, '', '', '', '', 0, '1', '1', '1', '1', '2024-05-13 21:43:38', '1', '2024-05-13 21:43:38', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2778, '聊天管理', '', 2, 10, 2760, 'chat-conversation', 'ep:chat-square', 'ai/chat/manager/index.vue', 'AiChatManager', 0, '1', '1', '1', '', '2024-05-24 15:39:18', '1', '2024-06-26 21:36:56', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2779, '会话查询', 'ai:chat-conversation:query', 3, 1, 2778, '', '', '', '', 0, '1', '1', '1', '', '2024-05-24 15:39:18', '1', '2024-05-25 08:38:30', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2780, '会话删除', 'ai:chat-conversation:delete', 3, 2, 2778, '', '', '', '', 0, '1', '1', '1', '', '2024-05-24 15:39:18', '1', '2024-05-25 08:38:40', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2781, '消息查询', 'ai:chat-message:query', 3, 11, 2778, '', '', '', '', 0, '1', '1', '1', '1', '2024-05-25 08:38:56', '1', '2024-05-25 08:38:56', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2782, '消息删除', 'ai:chat-message:delete', 3, 12, 2778, '', '', '', '', 0, '1', '1', '1', '1', '2024-05-25 08:39:10', '1', '2024-05-25 08:39:10', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2783, 'AI 绘画', '', 2, 2, 2758, 'image', 'ep:picture-rounded', 'ai/image/index/index.vue', 'AiImage', 0, '1', '1', '1', '1', '2024-05-26 11:45:17', '1', '2024-07-07 17:18:59', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2784, '绘画管理', '', 2, 11, 2760, 'image', 'fa:file-image-o', 'ai/image/manager/index.vue', 'AiImageManager', 0, '1', '1', '1', '', '2024-06-26 13:32:31', '1', '2024-06-26 21:37:13', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2785, '绘画查询', 'ai:image:query', 3, 1, 2784, '', '', '', '', 0, '1', '1', '1', '', '2024-06-26 13:32:31', '1', '2024-06-26 22:21:57', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2786, '绘画删除', 'ai:image:delete', 3, 4, 2784, '', '', '', '', 0, '1', '1', '1', '', '2024-06-26 13:32:31', '1', '2024-06-26 22:22:08', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2787, '绘图更新', 'ai:image:update', 3, 2, 2784, '', '', '', '', 0, '1', '1', '1', '1', '2024-06-26 22:47:56', '1', '2024-08-31 09:21:35', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2788, '音乐管理', '', 2, 12, 2760, 'music', 'fa:music', 'ai/music/manager/index.vue', 'AiMusicManager', 0, '1', '1', '1', '', '2024-06-27 15:03:33', '1', '2024-06-27 23:04:19', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2789, '音乐查询', 'ai:music:query', 3, 1, 2788, '', '', '', NULL, 0, '1', '1', '1', '', '2024-06-27 15:03:33', '', '2024-06-27 15:03:33', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2790, '音乐更新', 'ai:music:update', 3, 3, 2788, '', '', '', NULL, 0, '1', '1', '1', '', '2024-06-27 15:03:33', '', '2024-06-27 15:03:33', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2791, '音乐删除', 'ai:music:delete', 3, 4, 2788, '', '', '', NULL, 0, '1', '1', '1', '', '2024-06-27 15:03:33', '', '2024-06-27 15:03:33', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2792, 'AI 写作', '', 2, 3, 2758, 'write', 'fa-solid:book-reader', 'ai/write/index/index.vue', 'AiWrite', 0, '1', '1', '1', '1', '2024-07-08 09:26:44', '1', '2024-07-16 13:03:06', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2793, '写作管理', '', 2, 13, 2760, 'write', 'fa:bookmark-o', 'ai/write/manager/index.vue', 'AiWriteManager', 0, '1', '1', '1', '', '2024-07-10 13:24:34', '1', '2024-07-10 21:31:59', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2794, 'AI 写作查询', 'ai:write:query', 3, 1, 2793, '', '', '', NULL, 0, '1', '1', '1', '', '2024-07-10 13:24:34', '', '2024-07-10 13:24:34', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2795, 'AI 写作删除', 'ai:write:delete', 3, 4, 2793, '', '', '', NULL, 0, '1', '1', '1', '', '2024-07-10 13:24:34', '', '2024-07-10 13:24:34', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2796, 'AI 音乐', '', 2, 4, 2758, 'music', 'fa:music', 'ai/music/index/index.vue', 'AiMusic', 0, '1', '1', '1', '1', '2024-07-17 09:21:12', '1', '2024-07-29 21:11:52', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2797, '客服中心', '', 2, 100, 2362, 'kefu', 'fa-solid:user-alt', 'mall/promotion/kefu/index', 'KeFu', 0, '1', '1', '1', '1', '2024-07-17 23:49:05', '1', '2024-07-17 23:49:16', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2798, 'AI 思维导图', '', 2, 5, 2758, 'mind-map', 'fa:sitemap', 'ai/mindmap/index/index.vue', 'AiMindMap', 0, '1', '1', '1', '1', '2024-07-29 21:31:59', '1', '2024-07-29 21:33:20', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2799, '导图管理', '', 2, 14, 2760, 'mind-map', 'fa:map', 'ai/mindmap/manager/index', 'AiMindMapManager', 0, '1', '1', '1', '', '2024-08-10 09:15:09', '1', '2024-08-10 17:24:28', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2800, '思维导图查询', 'ai:mind-map:query', 3, 1, 2799, '', '', '', NULL, 0, '1', '1', '1', '', '2024-08-10 09:15:09', '', '2024-08-10 09:15:09', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2801, '思维导图删除', 'ai:mind-map:delete', 3, 4, 2799, '', '', '', NULL, 0, '1', '1', '1', '', '2024-08-10 09:15:09', '', '2024-08-10 09:15:09', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2802, '会话查询', 'promotion:kefu-conversation:query', 3, 1, 2797, '', '', '', '', 0, '1', '1', '1', '1', '2024-08-31 09:17:52', '1', '2024-08-31 09:18:52', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2803, '会话更新', 'promotion:kefu-conversation:update', 3, 2, 2797, '', '', '', '', 0, '1', '1', '1', '1', '2024-08-31 09:18:15', '1', '2024-08-31 09:19:29', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2804, '消息查询', 'promotion:kefu-message:query', 3, 10, 2797, '', '', '', '', 0, '1', '1', '1', '1', '2024-08-31 09:18:42', '1', '2024-08-31 09:18:42', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2805, '会话删除', 'promotion:kefu-conversation:delete', 3, 3, 2797, '', '', '', '', 0, '1', '1', '1', '1', '2024-08-31 09:19:51', '1', '2024-08-31 09:20:32', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2806, '消息发送', 'promotion:kefu-message:send', 3, 12, 2797, '', '', '', '', 0, '1', '1', '1', '1', '2024-08-31 09:20:06', '1', '2024-08-31 09:20:06', '0');
|
||||
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2807, '消息更新', 'promotion:kefu-message:update', 3, 11, 2797, '', '', '', '', 0, '1', '1', '1', '1', '2024-08-31 09:20:22', '1', '2024-08-31 09:20:22', '0');
|
||||
COMMIT;
|
||||
-- @formatter:on
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.common.util.collection;
|
|||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import java.util.*;
|
||||
|
@ -73,6 +74,13 @@ public class CollectionUtils {
|
|||
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,
|
||||
Function<T, ? extends Stream<? extends U>> func) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
|
@ -324,7 +332,7 @@ public class CollectionUtils {
|
|||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
}
|
|
@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil;
|
|||
import cn.iocoder.yudao.framework.tenant.config.TenantProperties;
|
||||
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
||||
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
|
||||
import com.baomidou.mybatisplus.extension.toolkit.SqlParserUtils;
|
||||
import net.sf.jsqlparser.expression.Expression;
|
||||
import net.sf.jsqlparser.expression.LongValue;
|
||||
|
||||
|
@ -37,7 +38,7 @@ public class TenantDatabaseInterceptor implements TenantLineHandler {
|
|||
@Override
|
||||
public boolean ignoreTable(String tableName) {
|
||||
return TenantContextHolder.isIgnore() // 情况一,全局忽略多租户
|
||||
|| CollUtil.contains(ignoreTables, tableName); // 情况二,忽略多租户的表
|
||||
|| CollUtil.contains(ignoreTables, SqlParserUtils.removeWrapperSymbol(tableName)); // 情况二,忽略多租户的表
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -36,8 +36,9 @@ public class MyBatisUtils {
|
|||
Page<T> page = new Page<>(pageParam.getPageNo(), pageParam.getPageSize());
|
||||
// 排序字段
|
||||
if (!CollectionUtil.isEmpty(sortingFields)) {
|
||||
page.addOrder(sortingFields.stream().map(sortingField -> SortingField.ORDER_ASC.equals(sortingField.getOrder()) ?
|
||||
OrderItem.asc(sortingField.getField()) : OrderItem.desc(sortingField.getField()))
|
||||
page.addOrder(sortingFields.stream().map(sortingField -> SortingField.ORDER_ASC.equals(sortingField.getOrder())
|
||||
? OrderItem.asc(StrUtil.toUnderlineCase(sortingField.getField()))
|
||||
: OrderItem.desc(StrUtil.toUnderlineCase(sortingField.getField())))
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
return page;
|
||||
|
|
|
@ -17,7 +17,7 @@ public abstract class AbstractSliderDesensitizationHandler<T extends Annotation>
|
|||
public String desensitize(String origin, T annotation) {
|
||||
// 1. 判断是否禁用脱敏
|
||||
Object disable = SpringExpressionUtils.parseExpression(getDisable(annotation));
|
||||
if (Boolean.FALSE.equals(disable)) {
|
||||
if (Boolean.TRUE.equals(disable)) {
|
||||
return origin;
|
||||
}
|
||||
|
||||
|
|
|
@ -64,8 +64,10 @@ public abstract class AbstractWebSocketMessageSender implements WebSocketMessage
|
|||
sessions = (List<WebSocketSession>) sessionManager.getSessionList(userType);
|
||||
}
|
||||
if (CollUtil.isEmpty(sessions)) {
|
||||
log.info("[send][sessionId({}) userType({}) userId({}) messageType({}) messageContent({}) 未匹配到会话]",
|
||||
sessionId, userType, userId, messageType, messageContent);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("[send][sessionId({}) userType({}) userId({}) messageType({}) messageContent({}) 未匹配到会话]",
|
||||
sessionId, userType, userId, messageType, messageContent);
|
||||
}
|
||||
}
|
||||
// 2. 执行发送
|
||||
doSend(sessions, messageType, messageContent);
|
||||
|
|
|
@ -222,8 +222,8 @@ knife4j:
|
|||
service-name: crm-server
|
||||
url: /admin-api/crm/v3/api-docs
|
||||
- name: ai-server
|
||||
service-name: crm-server
|
||||
url: /admin-api/crm/v3/api-docs
|
||||
service-name: ai-server
|
||||
url: /admin-api/ai/v3/api-docs
|
||||
|
||||
--- #################### 芋道相关配置 ####################
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
|
|||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
||||
import cn.iocoder.yudao.module.ai.controller.admin.image.vo.*;
|
||||
import cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyActionReqVO;
|
||||
import cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyImagineReqVO;
|
||||
|
@ -95,6 +96,7 @@ public class AiImageController {
|
|||
@Operation(summary = "【Midjourney】通知图片进展", description = "由 Midjourney Proxy 回调")
|
||||
@PostMapping("/midjourney/notify") // 必须是 POST 方法,否则会报错
|
||||
@PermitAll
|
||||
@TenantIgnore
|
||||
public CommonResult<Boolean> midjourneyNotify(@Valid @RequestBody MidjourneyApi.Notify notify) {
|
||||
imageService.midjourneyNotify(notify);
|
||||
return success(true);
|
||||
|
|
|
@ -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_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 ==========
|
||||
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_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_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_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_CAN_START = new ErrorCode(1_009_004_005, "发起流程失败,你没有权限发起该流程");
|
||||
|
||||
// ========== 流程任务 1-009-005-000 ==========
|
||||
|
@ -44,7 +44,7 @@ public interface ErrorCodeConstants {
|
|||
ErrorCode TASK_NOT_EXISTS = new ErrorCode(1_009_005_002, "流程任务不存在");
|
||||
ErrorCode TASK_IS_PENDING = new ErrorCode(1_009_005_003, "当前任务处于挂起状态,不能操作");
|
||||
ErrorCode TASK_TARGET_NODE_NOT_EXISTS = new ErrorCode(1_009_005_004, " 目标节点不存在");
|
||||
ErrorCode TASK_RETURN_FAIL_SOURCE_TARGET_ERROR = new ErrorCode(1_009_005_006, "回退任务失败,目标节点是在并行网关上或非同一路线上,不可跳转");
|
||||
ErrorCode TASK_RETURN_FAIL_SOURCE_TARGET_ERROR = new ErrorCode(1_009_005_006, "退回任务失败,目标节点是在并行网关上或非同一路线上,不可跳转");
|
||||
ErrorCode TASK_DELEGATE_FAIL_USER_REPEAT = new ErrorCode(1_009_005_007, "任务委派失败,委派人和当前审批人为同一人");
|
||||
ErrorCode TASK_DELEGATE_FAIL_USER_NOT_EXISTS = new ErrorCode(1_009_005_008, "任务委派失败,被委派人不存在");
|
||||
ErrorCode TASK_SIGN_CREATE_USER_NOT_EXIST = new ErrorCode(1_009_005_009, "任务加签:选择的用户不存在");
|
||||
|
|
|
@ -18,30 +18,26 @@ import java.util.Objects;
|
|||
public enum BpmSimpleModelNodeType implements IntArrayValuable {
|
||||
|
||||
// 0 ~ 1 开始和结束
|
||||
START_NODE(0, "startEvent", "开始节点"),
|
||||
END_NODE(1, "endEvent", "结束节点"),
|
||||
START_NODE(0, "开始", "startEvent"),
|
||||
END_NODE(1, "结束", "endEvent"),
|
||||
|
||||
// 10 ~ 49 各种节点
|
||||
START_USER_NODE(10, "userTask", "发起人节点"), // 发起人节点。前端的开始节点,Id 固定
|
||||
APPROVE_NODE(11, "userTask", "审批人节点"),
|
||||
COPY_NODE(12, "serviceTask", "抄送人节点"),
|
||||
START_USER_NODE(10, "发起人", "userTask"), // 发起人节点。前端的开始节点,Id 固定
|
||||
APPROVE_NODE(11, "审批人", "userTask"),
|
||||
COPY_NODE(12, "抄送人", "serviceTask"),
|
||||
|
||||
// 50 ~ 条件分支
|
||||
CONDITION_NODE(50, "sequenceFlow", "条件节点"), // 用于构建流转条件的表达式
|
||||
CONDITION_BRANCH_NODE(51, " “parallelGateway”", "条件分支节点"), // TODO @jason:是不是改成叫 条件分支?
|
||||
PARALLEL_BRANCH_NODE(52, "exclusiveGateway", "并行分支节点"), // TODO @jason:是不是一个 并行分支 ?就可以啦? 后面是否去掉并行网关。只用包容网关
|
||||
INCLUSIVE_BRANCH_NODE(53, "inclusiveGateway", "包容分支节点"),
|
||||
// TODO @jason:建议整合 join,最终只有 条件分支、并行分支、包容分支,三种~
|
||||
// TODO @芋艿。 感觉还是分开好理解一点,也好处理一点。前端结构中把聚合节点显示并传过来。
|
||||
CONDITION_NODE(50, "条件", "sequenceFlow"), // 用于构建流转条件的表达式
|
||||
CONDITION_BRANCH_NODE(51, "条件分支", "exclusiveGateway"),
|
||||
PARALLEL_BRANCH_NODE(52, "并行分支", "parallelGateway"),
|
||||
INCLUSIVE_BRANCH_NODE(53, "包容分支", "inclusiveGateway"),
|
||||
;
|
||||
|
||||
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 String bpmnType;
|
||||
private final String name;
|
||||
private final String bpmnType;
|
||||
|
||||
/**
|
||||
* 判断是否为分支节点
|
||||
|
@ -54,16 +50,6 @@ public enum BpmSimpleModelNodeType implements IntArrayValuable {
|
|||
|| 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) {
|
||||
return ArrayUtil.firstMatch(nodeType -> nodeType.getType().equals(type), values());
|
||||
}
|
||||
|
|
|
@ -16,20 +16,23 @@ import java.util.Arrays;
|
|||
@AllArgsConstructor
|
||||
public enum BpmUserTaskApproveMethodEnum implements IntArrayValuable {
|
||||
|
||||
RANDOM(1, "随机挑选一人审批"),
|
||||
RATIO(2, "多人会签(按通过比例)"), // 会签(按通过比例)
|
||||
ANY(3, "多人或签(一人通过或拒绝)"), // 或签(通过只需一人,拒绝只需一人)
|
||||
SEQUENTIAL(4, "依次审批"); // 依次审批
|
||||
RANDOM(1, "随机挑选一人审批", null),
|
||||
RATIO(2, "多人会签(按通过比例)", "${ nrOfCompletedInstances/nrOfInstances >= %s}"), // 会签(按通过比例)
|
||||
ANY(3, "多人或签(一人通过或拒绝)", "${ nrOfCompletedInstances > 0 }"), // 或签(通过只需一人,拒绝只需一人)
|
||||
SEQUENTIAL(4, "依次审批", "${ nrOfCompletedInstances >= nrOfInstances }"); // 依次审批
|
||||
|
||||
/**
|
||||
* 审批方式
|
||||
*/
|
||||
private final Integer method;
|
||||
|
||||
/**
|
||||
* 名字
|
||||
*/
|
||||
private final String name;
|
||||
/**
|
||||
* 完成表达式
|
||||
*/
|
||||
private final String completionCondition;
|
||||
|
||||
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmUserTaskApproveMethodEnum::getMethod).toArray();
|
||||
|
||||
|
|
|
@ -38,6 +38,10 @@ public enum BpmProcessInstanceStatusEnum implements IntArrayValuable {
|
|||
return ARRAYS;
|
||||
}
|
||||
|
||||
public static boolean isRejectStatus(Integer status) {
|
||||
return REJECT.getStatus().equals(status);
|
||||
}
|
||||
|
||||
public static boolean isProcessEndStatus(Integer status) {
|
||||
return ObjectUtils.equalsAny(status,
|
||||
APPROVE.getStatus(), REJECT.getStatus(), CANCEL.getStatus());
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package cn.iocoder.yudao.module.bpm.enums.task;
|
||||
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
@ -20,7 +21,6 @@ public enum BpmTaskStatusEnum {
|
|||
CANCEL(4, "已取消"),
|
||||
|
||||
RETURN(5, "已退回"),
|
||||
DELEGATE(6, "委派中"),
|
||||
|
||||
/**
|
||||
* 使用场景:
|
||||
|
@ -45,6 +45,10 @@ public enum BpmTaskStatusEnum {
|
|||
*/
|
||||
private final String name;
|
||||
|
||||
public static boolean isRejectStatus(Integer status) {
|
||||
return REJECT.getStatus().equals(status);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断该状态是否已经处于 End 最终状态
|
||||
* <p>
|
||||
|
@ -59,4 +63,8 @@ public enum BpmTaskStatusEnum {
|
|||
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")
|
||||
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 = "部门编号", 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);
|
||||
}
|
||||
|
||||
@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")
|
||||
@Operation(summary = "删除流程分类")
|
||||
@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.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.simple.BpmSimpleModelNodeVO;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelUpdateReqVO;
|
||||
|
@ -19,6 +17,8 @@ import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
|||
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 jakarta.validation.Valid;
|
||||
import org.flowable.engine.repository.Deployment;
|
||||
import org.flowable.engine.repository.Model;
|
||||
import org.flowable.engine.repository.ProcessDefinition;
|
||||
|
@ -26,9 +26,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
|
|||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import java.util.HashSet;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -56,38 +54,38 @@ public class BpmModelController {
|
|||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
@GetMapping("/page")
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获得模型分页")
|
||||
public CommonResult<PageResult<BpmModelRespVO>> getModelPage(BpmModelPageReqVO pageVO) {
|
||||
PageResult<Model> pageResult = modelService.getModelPage(pageVO);
|
||||
if (CollUtil.isEmpty(pageResult.getList())) {
|
||||
return success(PageResult.empty(pageResult.getTotal()));
|
||||
@Parameter(name = "name", description = "模型名称", example = "芋艿")
|
||||
public CommonResult<List<BpmModelRespVO>> getModelPage(@RequestParam(value = "name", required = false) String name) {
|
||||
List<Model> list = modelService.getModelList(name);
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return success(Collections.emptyList());
|
||||
}
|
||||
|
||||
// 拼接数据
|
||||
// 获得 Form 表单
|
||||
Set<Long> formIds = convertSet(pageResult.getList(), model -> {
|
||||
Set<Long> formIds = convertSet(list, model -> {
|
||||
BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model);
|
||||
return metaInfo != null ? metaInfo.getFormId() : null;
|
||||
});
|
||||
Map<Long, BpmFormDO> formMap = formService.getFormMap(formIds);
|
||||
// 获得 Category Map
|
||||
Map<String, BpmCategoryDO> categoryMap = categoryService.getCategoryMap(
|
||||
convertSet(pageResult.getList(), Model::getCategory));
|
||||
convertSet(list, Model::getCategory));
|
||||
// 获得 Deployment Map
|
||||
Set<String> deploymentIds = new HashSet<>();
|
||||
pageResult.getList().forEach(model -> CollectionUtils.addIfNotNull(deploymentIds, model.getDeploymentId()));
|
||||
Map<String, Deployment> deploymentMap = processDefinitionService.getDeploymentMap(deploymentIds);
|
||||
Map<String, Deployment> deploymentMap = processDefinitionService.getDeploymentMap(
|
||||
convertSet(list, Model::getDeploymentId));
|
||||
// 获得 ProcessDefinition Map
|
||||
List<ProcessDefinition> processDefinitions = processDefinitionService.getProcessDefinitionListByDeploymentIds(deploymentIds);
|
||||
List<ProcessDefinition> processDefinitions = processDefinitionService.getProcessDefinitionListByDeploymentIds(
|
||||
deploymentMap.keySet());
|
||||
Map<String, ProcessDefinition> processDefinitionMap = convertMap(processDefinitions, ProcessDefinition::getDeploymentId);
|
||||
// 获得 User Map
|
||||
Set<Long> userIds = convertSetByFlatMap(pageResult.getList(), model -> {
|
||||
Set<Long> userIds = convertSetByFlatMap(list, model -> {
|
||||
BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model);
|
||||
return metaInfo != null ? metaInfo.getStartUserIds().stream() : Stream.empty();
|
||||
});
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
|
||||
return success(BpmModelConvert.INSTANCE.buildModelPage(pageResult,
|
||||
return success(BpmModelConvert.INSTANCE.buildModelList(list,
|
||||
formMap, categoryMap, deploymentMap, processDefinitionMap, userMap));
|
||||
}
|
||||
|
||||
|
@ -111,6 +109,7 @@ public class BpmModelController {
|
|||
return success(modelService.createModel(createRetVO));
|
||||
}
|
||||
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "修改模型")
|
||||
@PreAuthorize("@ss.hasPermission('bpm:model:update')")
|
||||
|
@ -119,6 +118,14 @@ public class BpmModelController {
|
|||
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")
|
||||
@Operation(summary = "部署模型")
|
||||
@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.BpmFormDO;
|
||||
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.BpmFormService;
|
||||
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
|
||||
|
@ -17,7 +16,6 @@ import io.swagger.v3.oas.annotations.Operation;
|
|||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.bpmn.model.UserTask;
|
||||
import org.flowable.engine.repository.Deployment;
|
||||
import org.flowable.engine.repository.ProcessDefinition;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
|
@ -115,9 +113,8 @@ public class BpmProcessDefinitionController {
|
|||
}
|
||||
BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo(processDefinition.getId());
|
||||
BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(processDefinition.getId());
|
||||
List<UserTask> userTaskList = BpmTaskCandidateStartUserSelectStrategy.getStartUserSelectUserTaskList(bpmnModel);
|
||||
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
|
||||
* 主要用于 { Model#setMetaInfo(String)} 的存储
|
||||
*
|
||||
* 最终,它的字段和 {@link cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO} 是一致的
|
||||
* 最终,它的字段和
|
||||
* {@link cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO}
|
||||
* 是一致的
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
|
@ -40,13 +42,11 @@ public class BpmModelMetaInfoVO {
|
|||
@NotNull(message = "表单类型不能为空")
|
||||
private Integer formType;
|
||||
@Schema(description = "表单编号", example = "1024")
|
||||
private Long formId; // formType 为 NORMAL 使用,必须非空
|
||||
@Schema(description = "自定义表单的提交路径,使用 Vue 的路由地址",
|
||||
example = "/bpm/oa/leave/create")
|
||||
private String formCustomCreatePath; // 表单类型为 CUSTOM 时,必须非空
|
||||
@Schema(description = "自定义表单的查看路径,使用 Vue 的路由地址",
|
||||
example = "/bpm/oa/leave/view")
|
||||
private String formCustomViewPath; // 表单类型为 CUSTOM 时,必须非空
|
||||
private Long formId; // formType 为 NORMAL 使用,必须非空
|
||||
@Schema(description = "自定义表单的提交路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/create")
|
||||
private String formCustomCreatePath; // 表单类型为 CUSTOM 时,必须非空
|
||||
@Schema(description = "自定义表单的查看路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/view")
|
||||
private String formCustomViewPath; // 表单类型为 CUSTOM 时,必须非空
|
||||
|
||||
@Schema(description = "是否可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||
@NotNull(message = "是否可见不能为空")
|
||||
|
@ -59,4 +59,7 @@ public class BpmModelMetaInfoVO {
|
|||
@NotEmpty(message = "可管理用户编号数组不能为空")
|
||||
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.module.bpm.enums.definition.*;
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
@ -31,7 +30,6 @@ public class BpmSimpleModelNodeVO {
|
|||
@Schema(description = "模型节点名称", example = "领导审批")
|
||||
private String name;
|
||||
|
||||
// TODO @jason:和 gpt 大模型对了下这个字段的命名,貌似叫 displayText 合适点。可以等最后我们全局替换下。(优先级:低)
|
||||
@Schema(description = "节点展示内容", example = "指定成员: 芋道源码")
|
||||
private String showText;
|
||||
|
||||
|
@ -79,12 +77,6 @@ public class BpmSimpleModelNodeVO {
|
|||
@Schema(description = "操作按钮设置", example = "[]")
|
||||
private List<OperationButtonSetting> buttonsSetting; // 用于审批节点
|
||||
|
||||
// TODO @jason:看看是不是可以简化;@芋艿: 暂时先放着。不知道后面是否会用到
|
||||
/**
|
||||
* 附加节点 Id, 该节点不从前端传入。 由程序生成. 由于当个节点无法完成功能。 需要附加节点来完成。
|
||||
*/
|
||||
@JsonIgnore
|
||||
private String attachNodeId;
|
||||
/**
|
||||
* 审批节点拒绝处理
|
||||
*/
|
||||
|
|
|
@ -33,6 +33,9 @@ public class BpmProcessDefinitionRespVO {
|
|||
@Schema(description = "流程分类名字", example = "请假")
|
||||
private String categoryName;
|
||||
|
||||
@Schema(description = "流程模型的类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||
private Integer modelType; // 参见 BpmModelTypeEnum 枚举类
|
||||
|
||||
@Schema(description = "表单类型-参见 bpm_model_form_type 数据字典", example = "1")
|
||||
private Integer formType;
|
||||
@Schema(description = "表单编号-在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", example = "1024")
|
||||
|
@ -59,9 +62,12 @@ public class BpmProcessDefinitionRespVO {
|
|||
@Schema(description = "BPMN XML")
|
||||
private String bpmnXml; // 需要从对应的 BpmnModel 读取,非必须返回
|
||||
|
||||
@Schema(description = "发起用户需要选择审批人的任务数组")
|
||||
private List<UserTask> startUserSelectTasks; // 需要从对应的 BpmnModel 读取,非必须返回
|
||||
@Schema(description = "SIMPLE 设计器模型数据 json 格式")
|
||||
private String simpleModel; // 非必须返回
|
||||
|
||||
@Schema(description = "流程定义排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long sort;
|
||||
|
||||
@Schema(description = "BPMN UserTask 用户任务")
|
||||
@Data
|
||||
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 org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
import static 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.dal.dataobject.definition.BpmCategoryDO;
|
||||
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.BpmProcessDefinitionService;
|
||||
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
|
||||
|
@ -128,15 +127,13 @@ public class BpmProcessInstanceController {
|
|||
processInstance.getProcessDefinitionId());
|
||||
BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo(
|
||||
processInstance.getProcessDefinitionId());
|
||||
String bpmnXml = BpmnModelUtils.getBpmnXml(
|
||||
processDefinitionService.getProcessDefinitionBpmnModel(processInstance.getProcessDefinitionId()));
|
||||
AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId())).getCheckedData();
|
||||
DeptRespDTO dept = null;
|
||||
if (startUser != null && startUser.getDeptId() != null) {
|
||||
dept = deptApi.getDept(startUser.getDeptId()).getCheckedData();
|
||||
}
|
||||
return success(BpmProcessInstanceConvert.INSTANCE.buildProcessInstance(processInstance,
|
||||
processDefinition, processDefinitionInfo, bpmnXml, startUser, dept));
|
||||
processDefinition, processDefinitionInfo, startUser, dept));
|
||||
}
|
||||
|
||||
@DeleteMapping("/cancel-by-start-user")
|
||||
|
@ -157,14 +154,6 @@ public class BpmProcessInstanceController {
|
|||
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")
|
||||
@Operation(summary = "获得审批详情")
|
||||
@Parameter(name = "id", description = "流程实例的编号", required = true)
|
||||
|
@ -173,4 +162,11 @@ public class BpmProcessInstanceController {
|
|||
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.date.DateUtils;
|
||||
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.instance.BpmProcessInstanceCopyPageReqVO;
|
||||
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO;
|
||||
|
@ -28,8 +29,7 @@ import java.util.Map;
|
|||
import java.util.stream.Stream;
|
||||
|
||||
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.convertSet;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Tag(name = "管理后台 - 流程实例抄送")
|
||||
|
@ -62,11 +62,15 @@ public class BpmProcessInstanceCopyController {
|
|||
convertSet(pageResult.getList(), BpmProcessInstanceCopyDO::getProcessInstanceId));
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(pageResult.getList(),
|
||||
copy -> Stream.of(copy.getStartUserId(), Long.parseLong(copy.getCreator()))));
|
||||
return success(BeanUtils.toBean(pageResult, BpmProcessInstanceCopyRespVO.class, copyVO -> {
|
||||
MapUtils.findAndThen(userMap, Long.valueOf(copyVO.getCreator()), user -> copyVO.setCreatorName(user.getNickname()));
|
||||
MapUtils.findAndThen(userMap, copyVO.getStartUserId(), user -> copyVO.setStartUserName(user.getNickname()));
|
||||
return success(convertPage(pageResult, copy -> {
|
||||
BpmProcessInstanceCopyRespVO copyVO = BeanUtils.toBean(copy, BpmProcessInstanceCopyRespVO.class);
|
||||
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(),
|
||||
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.dal.dataobject.definition.BpmFormDO;
|
||||
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.BpmTaskService;
|
||||
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||
|
@ -18,7 +17,8 @@ import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
|||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.flowable.bpmn.model.UserTask;
|
||||
import org.flowable.engine.history.HistoricProcessInstance;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
|
@ -28,8 +28,6 @@ import org.springframework.security.access.prepost.PreAuthorize;
|
|||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -52,8 +50,6 @@ public class BpmTaskController {
|
|||
private BpmProcessInstanceService processInstanceService;
|
||||
@Resource
|
||||
private BpmFormService formService;
|
||||
@Resource
|
||||
private BpmProcessDefinitionService bpmProcessDefinitionService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
|
@ -121,27 +117,22 @@ public class BpmTaskController {
|
|||
@PreAuthorize("@ss.hasPermission('bpm:task:query')")
|
||||
public CommonResult<List<BpmTaskRespVO>> getTaskListByProcessInstanceId(
|
||||
@RequestParam("processInstanceId") String processInstanceId) {
|
||||
List<HistoricTaskInstance> taskList = taskService.getTaskListByProcessInstanceId(processInstanceId);
|
||||
List<HistoricTaskInstance> taskList = taskService.getTaskListByProcessInstanceId(processInstanceId, true);
|
||||
if (CollUtil.isEmpty(taskList)) {
|
||||
return success(Collections.emptyList());
|
||||
}
|
||||
|
||||
// 拼接数据
|
||||
HistoricProcessInstance processInstance = processInstanceService.getHistoricProcessInstance(processInstanceId);
|
||||
// 获得 User 和 Dept Map
|
||||
Set<Long> userIds = convertSetByFlatMap(taskList, task ->
|
||||
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, DeptRespDTO> deptMap = deptApi.getDeptMap(
|
||||
convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
|
||||
// 获得 Form Map
|
||||
Map<Long, BpmFormDO> formMap = formService.getFormMap(
|
||||
convertSet(taskList, task -> NumberUtils.parseLong(task.getFormKey())));
|
||||
// 获得 BpmnModel
|
||||
BpmnModel bpmnModel = bpmProcessDefinitionService.getProcessDefinitionBpmnModel(processInstance.getProcessDefinitionId());
|
||||
return success(BpmTaskConvert.INSTANCE.buildTaskListByProcessInstanceId(taskList, processInstance,
|
||||
formMap, userMap, deptMap, bpmnModel));
|
||||
return success(BpmTaskConvert.INSTANCE.buildTaskListByProcessInstanceId(taskList,
|
||||
formMap, userMap, deptMap));
|
||||
}
|
||||
|
||||
@PutMapping("/approve")
|
||||
|
@ -161,7 +152,7 @@ public class BpmTaskController {
|
|||
}
|
||||
|
||||
@GetMapping("/list-by-return")
|
||||
@Operation(summary = "获取所有可回退的节点", description = "用于【流程详情】的【回退】按钮")
|
||||
@Operation(summary = "获取所有可退回的节点", description = "用于【流程详情】的【退回】按钮")
|
||||
@Parameter(name = "taskId", description = "当前任务ID", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
|
||||
public CommonResult<List<BpmTaskRespVO>> getTaskListByReturn(@RequestParam("id") String id) {
|
||||
|
@ -171,7 +162,7 @@ public class BpmTaskController {
|
|||
}
|
||||
|
||||
@PutMapping("/return")
|
||||
@Operation(summary = "回退任务", description = "用于【流程详情】的【回退】按钮")
|
||||
@Operation(summary = "退回任务", description = "用于【流程详情】的【退回】按钮")
|
||||
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
|
||||
public CommonResult<Boolean> returnTask(@Valid @RequestBody BpmTaskReturnReqVO reqVO) {
|
||||
taskService.returnTask(getLoginUserId(), reqVO);
|
||||
|
@ -210,6 +201,14 @@ public class BpmTaskController {
|
|||
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")
|
||||
@Operation(summary = "获得指定父级任务的子任务列表") // 目前用于,减签的时候,获得子任务列表
|
||||
@Parameter(name = "parentTaskId", description = "父级任务编号", required = true)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
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 lombok.Data;
|
||||
|
||||
|
@ -12,31 +13,31 @@ public class BpmProcessInstanceCopyRespVO {
|
|||
@Schema(description = "抄送主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "发起人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "888")
|
||||
private Long startUserId;
|
||||
@Schema(description = "发起人昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
|
||||
private String startUserName;
|
||||
@Schema(description = "发起人", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private UserSimpleBaseVO startUser;
|
||||
|
||||
@Schema(description = "流程实例编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "A233")
|
||||
private String processInstanceId;
|
||||
@Schema(description = "流程实例的名称")
|
||||
@Schema(description = "流程实例的名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "测试")
|
||||
private String processInstanceName;
|
||||
@Schema(description = "流程实例的发起时间")
|
||||
@Schema(description = "流程实例的发起时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime processInstanceStartTime;
|
||||
|
||||
@Schema(description = "抄送的节点的活动编号")
|
||||
@Schema(description = "流程活动的编号", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String activityId;
|
||||
@Schema(description = "发起抄送的任务编号")
|
||||
@Schema(description = "流程活动的名字", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String activityName;
|
||||
|
||||
@Schema(description = "流程活动的编号")
|
||||
private String taskId;
|
||||
@Schema(description = "发起抄送的任务名称")
|
||||
private String taskName;
|
||||
|
||||
@Schema(description = "抄送人")
|
||||
private String creator;
|
||||
@Schema(description = "抄送人昵称")
|
||||
private String creatorName;
|
||||
@Schema(description = "抄送人意见")
|
||||
private String reason;
|
||||
|
||||
@Schema(description = "抄送时间")
|
||||
@Schema(description = "创建人", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private UserSimpleBaseVO createUser;
|
||||
|
||||
@Schema(description = "抄送时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
|
|
|
@ -3,20 +3,30 @@ 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;
|
||||
|
||||
import javax.validation.constraints.AssertTrue;
|
||||
import java.util.Map;
|
||||
|
||||
// TODO @jason:这个可以简化下,使用 @RequestParam。嘿嘿,主要 VO 项不要太多
|
||||
@Schema(description = "管理后台 - 审批详情 Request VO")
|
||||
@Data
|
||||
public class BpmApprovalDetailReqVO {
|
||||
|
||||
@Schema(description = "流程定义的编号", example = "1024")
|
||||
private String processDefinitionId;
|
||||
private String processDefinitionId; // 使用场景:发起流程时,传流程定义 ID
|
||||
|
||||
@Schema(description = "流程变量")
|
||||
private Map<String, Object> processVariables; // 使用场景:同 processDefinitionId,用于流程预测
|
||||
|
||||
@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 = "流程定义的编号和流程实例的编号不能同时为空")
|
||||
@JsonIgnore
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
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 lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
@Schema(description = "管理后台 - 审批详情 Response VO")
|
||||
|
@ -14,12 +19,28 @@ public class BpmApprovalDetailRespVO {
|
|||
@Schema(description = "流程实例的状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer status; // 参见 BpmProcessInstanceStatusEnum 枚举
|
||||
|
||||
@Schema(description = "审批信息列表", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private List<ApprovalNodeInfo> approveNodes;
|
||||
@Schema(description = "活动节点列表", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
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
|
||||
public static class ApprovalNodeInfo {
|
||||
public static class ActivityNode {
|
||||
|
||||
@Schema(description = "节点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "StartUserNode")
|
||||
private String id;
|
||||
|
@ -39,42 +60,40 @@ public class BpmApprovalDetailRespVO {
|
|||
private LocalDateTime endTime;
|
||||
|
||||
@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 = "候选人用户列表")
|
||||
// TODO @jason:candidateUserList => candidateUsers,保持和 tasks 的命名风格一致哈
|
||||
private List<User> candidateUserList; // 用于未运行任务节点
|
||||
private List<UserSimpleBaseVO> candidateUsers; // 只包含未生成 ApprovalTaskInfo 的用户列表
|
||||
|
||||
}
|
||||
|
||||
// TODO @jason:可以替换成 UserSimpleBaseVO。简化下
|
||||
@Schema(description = "用户信息")
|
||||
@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 = "用户头像", example = "https://www.iocoder.cn/1.png")
|
||||
private String avatar;
|
||||
|
||||
}
|
||||
|
||||
@Schema(description = "审批任务信息")
|
||||
@Data
|
||||
public static class ApprovalTaskInfo {
|
||||
public static class ActivityNodeTask {
|
||||
|
||||
@Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private String id;
|
||||
|
||||
@Schema(description = "任务所属人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1818")
|
||||
@JsonIgnore // 不返回,只是方便后续读取,赋值给 ownerUser
|
||||
private Long owner;
|
||||
|
||||
@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")
|
||||
private User assigneeUser;
|
||||
private UserSimpleBaseVO assigneeUser;
|
||||
|
||||
@Schema(description = "任务状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer status; // 参见 BpmTaskStatusEnum 枚举
|
||||
|
|
|
@ -1,38 +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 lombok.Data;
|
||||
|
||||
import javax.validation.constraints.AssertTrue;
|
||||
|
||||
@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;
|
||||
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
@ -44,7 +45,7 @@ public class BpmProcessInstanceRespVO {
|
|||
/**
|
||||
* 发起流程的用户
|
||||
*/
|
||||
private User startUser;
|
||||
private UserSimpleBaseVO startUser;
|
||||
|
||||
@Schema(description = "流程定义的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
|
||||
private String processDefinitionId;
|
||||
|
@ -58,22 +59,6 @@ public class BpmProcessInstanceRespVO {
|
|||
*/
|
||||
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 = "流程任务")
|
||||
@Data
|
||||
public static class Task {
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
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 javax.validation.constraints.NotEmpty;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
@Schema(description = "管理后台 - 通过流程任务的 Request VO")
|
||||
|
@ -19,9 +18,6 @@ public class BpmTaskApproveReqVO {
|
|||
@NotEmpty(message = "审批意见不能为空")
|
||||
private String reason;
|
||||
|
||||
@Schema(description = "抄送的用户编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2")
|
||||
private Collection<Long> copyUserIds;
|
||||
|
||||
@Schema(description = "变量实例(动态表单)", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
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;
|
||||
|
||||
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 lombok.Data;
|
||||
|
||||
|
@ -33,14 +34,21 @@ public class BpmTaskRespVO {
|
|||
@Schema(description = "审批理由", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
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")
|
||||
private String taskDefinitionKey;
|
||||
|
@ -55,7 +63,7 @@ public class BpmTaskRespVO {
|
|||
@Schema(description = "父任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private String parentTaskId;
|
||||
@Schema(description = "子任务列表(由加签生成)", requiredMode = Schema.RequiredMode.REQUIRED, example = "childrenTask")
|
||||
private List<BpmTaskRespVO> children;
|
||||
private List<BpmTaskRespVO> children; // 由加签生成,包含多层子任务
|
||||
|
||||
@Schema(description = "表单编号", example = "1024")
|
||||
private Long formId;
|
||||
|
@ -67,9 +75,6 @@ public class BpmTaskRespVO {
|
|||
private List<String> formFields;
|
||||
@Schema(description = "提交的表单值", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Map<String, Object> formVariables;
|
||||
// @芋艿 都改成了 fieldsPermission。 buttonsSetting。和 BpmSimpleModelNodeVO 统一
|
||||
@Schema(description = "表单字段权限值")
|
||||
private Map<String, String> fieldsPermission;
|
||||
@Schema(description = "操作按钮设置值")
|
||||
private Map<Integer, OperationButtonSetting> buttonsSetting;
|
||||
|
||||
|
@ -92,7 +97,7 @@ public class BpmTaskRespVO {
|
|||
/**
|
||||
* 发起人的用户信息
|
||||
*/
|
||||
private BpmProcessInstanceRespVO.User startUser;
|
||||
private UserSimpleBaseVO startUser;
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
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 javax.validation.constraints.NotEmpty;
|
||||
|
||||
@Schema(description = "管理后台 - 回退流程任务的 Request VO")
|
||||
@Schema(description = "管理后台 - 退回流程任务的 Request VO")
|
||||
@Data
|
||||
public class BpmTaskReturnReqVO {
|
||||
|
||||
|
@ -13,12 +12,12 @@ public class BpmTaskReturnReqVO {
|
|||
@NotEmpty(message = "任务编号不能为空")
|
||||
private String id;
|
||||
|
||||
@Schema(description = "回退到的任务 Key", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotEmpty(message = "回退到的任务 Key 不能为空")
|
||||
@Schema(description = "退回到的任务 Key", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotEmpty(message = "退回到的任务 Key 不能为空")
|
||||
private String targetTaskDefinitionKey;
|
||||
|
||||
@Schema(description = "回退意见", requiredMode = Schema.RequiredMode.REQUIRED, example = "我就是想驳回")
|
||||
@NotEmpty(message = "回退意见不能为空")
|
||||
@Schema(description = "退回意见", requiredMode = Schema.RequiredMode.REQUIRED, example = "我就是想驳回")
|
||||
@NotEmpty(message = "退回意见不能为空")
|
||||
private String reason;
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package cn.iocoder.yudao.module.bpm.convert.definition;
|
||||
|
||||
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.json.JsonUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
|
@ -22,6 +21,7 @@ import org.mapstruct.Mapper;
|
|||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -37,25 +37,28 @@ public interface BpmModelConvert {
|
|||
|
||||
BpmModelConvert INSTANCE = Mappers.getMapper(BpmModelConvert.class);
|
||||
|
||||
default PageResult<BpmModelRespVO> buildModelPage(PageResult<Model> pageResult,
|
||||
Map<Long, BpmFormDO> formMap,
|
||||
Map<String, BpmCategoryDO> categoryMap, Map<String, Deployment> deploymentMap,
|
||||
Map<String, ProcessDefinition> processDefinitionMap,
|
||||
Map<Long, AdminUserRespDTO> userMap) {
|
||||
List<BpmModelRespVO> list = convertList(pageResult.getList(), model -> {
|
||||
default List<BpmModelRespVO> buildModelList(List<Model> list,
|
||||
Map<Long, BpmFormDO> formMap,
|
||||
Map<String, BpmCategoryDO> categoryMap,
|
||||
Map<String, Deployment> deploymentMap,
|
||||
Map<String, ProcessDefinition> processDefinitionMap,
|
||||
Map<Long, AdminUserRespDTO> userMap) {
|
||||
List<BpmModelRespVO> result = convertList(list, model -> {
|
||||
BpmModelMetaInfoVO metaInfo = parseMetaInfo(model);
|
||||
BpmFormDO form = metaInfo != null ? formMap.get(metaInfo.getFormId()) : null;
|
||||
BpmCategoryDO category = categoryMap.get(model.getCategory());
|
||||
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;
|
||||
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,
|
||||
byte[] bpmnBytes) {
|
||||
default BpmModelRespVO buildModel(Model model, byte[] bpmnBytes) {
|
||||
BpmModelMetaInfoVO metaInfo = parseMetaInfo(model);
|
||||
BpmModelRespVO modelVO = buildModel0(model, metaInfo, null, null, null, null, null);
|
||||
if (ArrayUtil.isNotEmpty(bpmnBytes)) {
|
||||
|
@ -112,6 +115,10 @@ public interface BpmModelConvert {
|
|||
if (vo.getStartUserIds() == null) {
|
||||
vo.setStartUserIds(Collections.emptyList());
|
||||
}
|
||||
// 如果为空,兜底处理,使用 createTime 创建时间
|
||||
if (vo.getSort() == null) {
|
||||
vo.setSort(model.getCreateTime().getTime());
|
||||
}
|
||||
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.framework.flowable.core.util.BpmnModelUtils;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.bpmn.model.UserTask;
|
||||
import org.flowable.common.engine.impl.db.SuspensionState;
|
||||
import org.flowable.engine.repository.Deployment;
|
||||
import org.flowable.engine.repository.ProcessDefinition;
|
||||
|
@ -20,6 +19,7 @@ import org.mapstruct.Mapping;
|
|||
import org.mapstruct.MappingTarget;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -47,7 +47,7 @@ public interface BpmProcessDefinitionConvert {
|
|||
Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap,
|
||||
Map<Long, BpmFormDO> formMap,
|
||||
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);
|
||||
BpmProcessDefinitionInfoDO processDefinitionInfo = MapUtil.get(processDefinitionInfoMap, definition.getId(), BpmProcessDefinitionInfoDO.class);
|
||||
BpmFormDO form = null;
|
||||
|
@ -55,8 +55,11 @@ public interface BpmProcessDefinitionConvert {
|
|||
form = MapUtil.get(formMap, processDefinitionInfo.getFormId(), BpmFormDO.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,
|
||||
|
@ -64,8 +67,7 @@ public interface BpmProcessDefinitionConvert {
|
|||
BpmProcessDefinitionInfoDO processDefinitionInfo,
|
||||
BpmFormDO form,
|
||||
BpmCategoryDO category,
|
||||
BpmnModel bpmnModel,
|
||||
List<UserTask> startUserSelectUserTaskList) {
|
||||
BpmnModel bpmnModel) {
|
||||
BpmProcessDefinitionRespVO respVO = BeanUtils.toBean(definition, BpmProcessDefinitionRespVO.class);
|
||||
respVO.setSuspensionState(definition.isSuspended() ? SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode());
|
||||
// Deployment
|
||||
|
@ -87,7 +89,6 @@ public interface BpmProcessDefinitionConvert {
|
|||
// BpmnModel
|
||||
if (bpmnModel != null) {
|
||||
respVO.setBpmnXml(BpmnModelUtils.getBpmnXml(bpmnModel));
|
||||
respVO.setStartUserSelectTasks(BeanUtils.toBean(startUserSelectUserTaskList, BpmProcessDefinitionRespVO.UserTask.class));
|
||||
}
|
||||
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;
|
||||
|
||||
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.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.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.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.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.BpmProcessDefinitionInfoDO;
|
||||
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.service.message.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO;
|
||||
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.user.dto.AdminUserRespDTO;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.engine.history.HistoricProcessInstance;
|
||||
import org.flowable.engine.repository.ProcessDefinition;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
import org.flowable.task.api.Task;
|
||||
import org.flowable.task.api.history.HistoricTaskInstance;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.MappingTarget;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
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
|
||||
|
@ -55,7 +72,7 @@ public interface BpmProcessInstanceConvert {
|
|||
if (userMap != null) {
|
||||
AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(pageResult.getList().get(i).getStartUserId()));
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
@ -65,20 +82,18 @@ public interface BpmProcessInstanceConvert {
|
|||
|
||||
default BpmProcessInstanceRespVO buildProcessInstance(HistoricProcessInstance processInstance,
|
||||
ProcessDefinition processDefinition,
|
||||
BpmProcessDefinitionInfoDO processDefinitionExt,
|
||||
String bpmnXml,
|
||||
BpmProcessDefinitionInfoDO processDefinitionInfo,
|
||||
AdminUserRespDTO startUser,
|
||||
DeptRespDTO dept) {
|
||||
BpmProcessInstanceRespVO respVO = BeanUtils.toBean(processInstance, BpmProcessInstanceRespVO.class);
|
||||
respVO.setStatus(FlowableUtils.getProcessInstanceStatus(processInstance));
|
||||
respVO.setFormVariables(FlowableUtils.getProcessInstanceFormVariable(processInstance));
|
||||
respVO.setStatus(FlowableUtils.getProcessInstanceStatus(processInstance))
|
||||
.setFormVariables(FlowableUtils.getProcessInstanceFormVariable(processInstance));
|
||||
// definition
|
||||
respVO.setProcessDefinition(BeanUtils.toBean(processDefinition, BpmProcessDefinitionRespVO.class));
|
||||
copyTo(processDefinitionExt, respVO.getProcessDefinition());
|
||||
respVO.getProcessDefinition().setBpmnXml(bpmnXml);
|
||||
copyTo(processDefinitionInfo, respVO.getProcessDefinition());
|
||||
// user
|
||||
if (startUser != null) {
|
||||
respVO.setStartUser(BeanUtils.toBean(startUser, BpmProcessInstanceRespVO.User.class));
|
||||
respVO.setStartUser(BeanUtils.toBean(startUser, UserSimpleBaseVO.class));
|
||||
if (dept != null) {
|
||||
respVO.getStartUser().setDeptName(dept.getName());
|
||||
}
|
||||
|
@ -89,7 +104,7 @@ public interface BpmProcessInstanceConvert {
|
|||
@Mapping(source = "from.id", target = "to.id", ignore = true)
|
||||
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)
|
||||
.setProcessDefinitionKey(instance.getProcessDefinitionKey()).setBusinessKey(instance.getBusinessKey());
|
||||
}
|
||||
|
@ -109,4 +124,156 @@ public interface BpmProcessInstanceConvert {
|
|||
.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;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
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.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
|
||||
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.dal.dataobject.definition.BpmFormDO;
|
||||
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.service.message.dto.BpmMessageSendWhenTaskCreatedReqDTO;
|
||||
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
||||
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.runtime.ProcessInstance;
|
||||
import org.flowable.task.api.Task;
|
||||
|
@ -28,7 +26,7 @@ import java.util.Date;
|
|||
import java.util.List;
|
||||
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;
|
||||
|
||||
/**
|
||||
|
@ -51,7 +49,7 @@ public interface BpmTaskConvert {
|
|||
}
|
||||
taskVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmTaskRespVO.ProcessInstance.class));
|
||||
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()));
|
||||
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()));
|
||||
}
|
||||
// 流程实例
|
||||
|
@ -73,7 +71,7 @@ public interface BpmTaskConvert {
|
|||
if (processInstance != null) {
|
||||
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));
|
||||
taskVO.getProcessInstance().setStartUser(BeanUtils.toBean(startUser, UserSimpleBaseVO.class));
|
||||
}
|
||||
return taskVO;
|
||||
});
|
||||
|
@ -81,19 +79,17 @@ public interface BpmTaskConvert {
|
|||
}
|
||||
|
||||
default List<BpmTaskRespVO> buildTaskListByProcessInstanceId(List<HistoricTaskInstance> taskList,
|
||||
HistoricProcessInstance processInstance,
|
||||
Map<Long, BpmFormDO> formMap,
|
||||
Map<Long, AdminUserRespDTO> userMap,
|
||||
Map<Long, DeptRespDTO> deptMap,
|
||||
BpmnModel bpmnModel) {
|
||||
List<BpmTaskRespVO> taskVOList = CollectionUtils.convertList(taskList, task -> {
|
||||
Map<Long, DeptRespDTO> deptMap) {
|
||||
return CollectionUtils.convertList(taskList, task -> {
|
||||
// 特殊:已取消的任务,不返回
|
||||
BpmTaskRespVO taskVO = BeanUtils.toBean(task, BpmTaskRespVO.class);
|
||||
Integer taskStatus = FlowableUtils.getTaskStatus(task);
|
||||
if (BpmTaskStatusEnum.isCancelStatus(taskStatus)) {
|
||||
return null;
|
||||
}
|
||||
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);
|
||||
if (form != null) {
|
||||
|
@ -101,35 +97,10 @@ public interface BpmTaskConvert {
|
|||
.setFormFields(form.getFields()).setFormVariables(FlowableUtils.getTaskFormVariable(task));
|
||||
}
|
||||
// 用户信息
|
||||
AdminUserRespDTO assignUser = userMap.get(NumberUtils.parseLong(task.getAssignee()));
|
||||
if (assignUser != null) {
|
||||
taskVO.setAssigneeUser(BeanUtils.toBean(assignUser, BpmProcessInstanceRespVO.User.class));
|
||||
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;
|
||||
buildTaskAssignee(taskVO, task.getAssignee(), userMap, deptMap);
|
||||
buildTaskOwner(taskVO, task.getOwner(), userMap, deptMap);
|
||||
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,
|
||||
|
@ -138,7 +109,7 @@ public interface BpmTaskConvert {
|
|||
return convertList(taskList, task -> BeanUtils.toBean(task, BpmTaskRespVO.class, taskVO -> {
|
||||
AdminUserRespDTO assignUser = userMap.get(NumberUtils.parseLong(task.getAssignee()));
|
||||
if (assignUser != null) {
|
||||
taskVO.setAssigneeUser(BeanUtils.toBean(assignUser, BpmProcessInstanceRespVO.User.class));
|
||||
taskVO.setAssigneeUser(BeanUtils.toBean(assignUser, UserSimpleBaseVO.class));
|
||||
DeptRespDTO dept = deptMap.get(assignUser.getDeptId());
|
||||
if (dept != null) {
|
||||
taskVO.getAssigneeUser().setDeptName(dept.getName());
|
||||
|
@ -146,12 +117,21 @@ public interface BpmTaskConvert {
|
|||
}
|
||||
AdminUserRespDTO ownerUser = userMap.get(NumberUtils.parseLong(task.getOwner()));
|
||||
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()));
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
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,
|
||||
Task task) {
|
||||
BpmMessageSendWhenTaskCreatedReqDTO reqDTO = new BpmMessageSendWhenTaskCreatedReqDTO();
|
||||
|
@ -162,6 +142,42 @@ public interface BpmTaskConvert {
|
|||
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>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
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.type.LongListTypeHandler;
|
||||
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.BpmModelTypeEnum;
|
||||
|
@ -122,6 +123,10 @@ public class BpmProcessDefinitionInfoDO extends BaseDO {
|
|||
* 目的:如果 false 不可见,则不展示在“发起流程”的列表里
|
||||
*/
|
||||
private Boolean visible;
|
||||
/**
|
||||
* 排序值
|
||||
*/
|
||||
private Long sort;
|
||||
|
||||
/**
|
||||
* 可发起用户编号数组
|
||||
|
@ -134,7 +139,7 @@ public class BpmProcessDefinitionInfoDO extends BaseDO {
|
|||
* 1. {@link #visible} 只是决定是否可见。即使不可见,还是可以发起
|
||||
* 2. startUserIds 决定某个用户是否可以发起。如果该用户不可发起,则他也是不可见的
|
||||
*/
|
||||
@TableField(typeHandler = StringListTypeHandler.class) // 为了可以使用 find_in_set 进行过滤
|
||||
@TableField(typeHandler = LongListTypeHandler.class) // 为了可以使用 find_in_set 进行过滤
|
||||
private List<Long> startUserIds;
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,6 +8,8 @@ import lombok.AllArgsConstructor;
|
|||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.flowable.bpmn.model.FlowNode;
|
||||
import org.flowable.task.api.history.HistoricTaskInstance;
|
||||
|
||||
/**
|
||||
* 流程抄送 DO
|
||||
|
@ -54,23 +56,25 @@ public class BpmProcessInstanceCopyDO extends BaseDO {
|
|||
*/
|
||||
private String category;
|
||||
/**
|
||||
* 流程活动编号
|
||||
* 流程活动的编号
|
||||
* <p/>
|
||||
* 对应 BPMN XML 节点编号,用于查询抄送节点的表单字段权限
|
||||
* 这里冗余的原因:如果是钉钉易搭的抄送节点 (ServiceTask),使用 taskId 可能查不到对应的 activityId
|
||||
*
|
||||
* 冗余 {@link FlowNode#getId()},对应 BPMN XML 节点编号
|
||||
* 原因:用于查询抄送节点的表单字段权限。因为仿钉钉/飞书的抄送节点 (ServiceTask),没有 taskId,只有 activityId
|
||||
*/
|
||||
private String activityId;
|
||||
/**
|
||||
* 任务主键
|
||||
* 关联 Task 的 id 属性
|
||||
* 流程活动的名字
|
||||
*
|
||||
* 冗余 {@link FlowNode#getName()}
|
||||
*/
|
||||
private String activityName;
|
||||
/**
|
||||
* 流程活动的编号
|
||||
*
|
||||
* 关联 {@link HistoricTaskInstance#getId()}
|
||||
*/
|
||||
private String taskId;
|
||||
/**
|
||||
* 任务名称
|
||||
*
|
||||
* 冗余 Task 的 name 属性
|
||||
*/
|
||||
private String taskName;
|
||||
|
||||
/**
|
||||
* 用户编号(被抄送的用户编号)
|
||||
|
@ -79,4 +83,9 @@ public class BpmProcessInstanceCopyDO extends BaseDO {
|
|||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 抄送意见
|
||||
*/
|
||||
private String reason;
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
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.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
|
@ -18,4 +19,9 @@ public interface BpmProcessDefinitionInfoMapper extends BaseMapperX<BpmProcessDe
|
|||
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")
|
||||
Set<Long> assigneeUserIds = (Set<Long>) execution.getVariable(super.collectionVariable, Set.class);
|
||||
if (assigneeUserIds == null) {
|
||||
assigneeUserIds = taskCandidateInvoker.calculateUsers(execution);
|
||||
assigneeUserIds = taskCandidateInvoker.calculateUsersByTask(execution);
|
||||
execution.setVariable(super.collectionVariable, assigneeUserIds);
|
||||
if (CollUtil.isEmpty(assigneeUserIds)) {
|
||||
// 特殊:如果没有处理人的情况下,至少有一个 null 空元素,避免自动通过!
|
||||
|
|
|
@ -46,7 +46,7 @@ public class BpmSequentialMultiInstanceBehavior extends SequentialMultiInstanceB
|
|||
@SuppressWarnings("unchecked")
|
||||
Set<Long> assigneeUserIds = (Set<Long>) execution.getVariable(super.collectionVariable, Set.class);
|
||||
if (assigneeUserIds == null) {
|
||||
assigneeUserIds = taskCandidateInvoker.calculateUsers(execution);
|
||||
assigneeUserIds = taskCandidateInvoker.calculateUsersByTask(execution);
|
||||
execution.setVariable(super.collectionVariable, assigneeUserIds);
|
||||
if (CollUtil.isEmpty(assigneeUserIds)) {
|
||||
// 特殊:如果没有处理人的情况下,至少有一个 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)) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
|||
import com.google.common.annotations.VisibleForTesting;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.bpmn.model.FlowElement;
|
||||
import org.flowable.bpmn.model.UserTask;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
|
@ -89,32 +90,66 @@ public class BpmTaskCandidateInvoker {
|
|||
* @return 用户编号集合
|
||||
*/
|
||||
@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,
|
||||
BpmUserTaskApproveTypeEnum.AUTO_APPROVE.getType(),
|
||||
BpmUserTaskApproveTypeEnum.AUTO_REJECT.getType())) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
|
||||
Integer strategy = BpmnModelUtils.parseCandidateStrategy(execution.getCurrentFlowElement());
|
||||
String param = BpmnModelUtils.parseCandidateParam(execution.getCurrentFlowElement());
|
||||
// 1.1 计算任务的候选人
|
||||
Set<Long> userIds = getCandidateStrategy(strategy).calculateUsers(execution, param);
|
||||
removeDisableUsers(userIds);
|
||||
Integer strategy = BpmnModelUtils.parseCandidateStrategy(flowElement);
|
||||
String param = BpmnModelUtils.parseCandidateParam(flowElement);
|
||||
Set<Long> userIds = getCandidateStrategy(strategy).calculateUsersByTask(execution, param);
|
||||
// 1.2 移除被禁用的用户
|
||||
removeDisableUsers(userIds);
|
||||
|
||||
// 2. 候选人为空时,根据“审批人为空”的配置补充
|
||||
if (CollUtil.isEmpty(userIds)) {
|
||||
userIds = getCandidateStrategy(BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY.getStrategy())
|
||||
.calculateUsers(execution, param);
|
||||
.calculateUsersByTask(execution, param);
|
||||
// ASSIGN_EMPTY 策略,不需要移除被禁用的用户。原因是,再移除,可能会出现更没审批人了!!!
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
@ -126,7 +161,7 @@ public class BpmTaskCandidateInvoker {
|
|||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(assigneeUserIds);
|
||||
assigneeUserIds.removeIf(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 flowElement 当前节点
|
||||
* @param startUserId 发起人
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void removeStartUserIfSkip(DelegateExecution execution, Set<Long> assigneeUserIds) {
|
||||
void removeStartUserIfSkip(Set<Long> assigneeUserIds, FlowElement flowElement, Long startUserId) {
|
||||
if (CollUtil.size(assigneeUserIds) <= 1) {
|
||||
return;
|
||||
}
|
||||
Integer assignStartUserHandlerType = BpmnModelUtils.parseAssignStartUserHandlerType(execution.getCurrentFlowElement());
|
||||
Integer assignStartUserHandlerType = BpmnModelUtils.parseAssignStartUserHandlerType(flowElement);
|
||||
if (ObjectUtil.notEqual(assignStartUserHandlerType, BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType())) {
|
||||
return;
|
||||
}
|
||||
ProcessInstance processInstance = SpringUtil.getBean(BpmProcessInstanceService.class)
|
||||
.getProcessInstance(execution.getProcessInstanceId());
|
||||
Assert.notNull(processInstance, "流程实例({}) 不存在", execution.getProcessInstanceId());
|
||||
assigneeUserIds.remove(Long.valueOf(processInstance.getStartUserId()));
|
||||
assigneeUserIds.remove(startUserId);
|
||||
}
|
||||
|
||||
public BpmTaskCandidateStrategy getCandidateStrategy(Integer strategy) {
|
||||
private BpmTaskCandidateStrategy getCandidateStrategy(Integer strategy) {
|
||||
BpmTaskCandidateStrategyEnum strategyEnum = BpmTaskCandidateStrategyEnum.valueOf(strategy);
|
||||
Assert.notNull(strategyEnum, "策略(%s) 不存在", strategy);
|
||||
BpmTaskCandidateStrategy strategyObj = strategyMap.get(strategyEnum);
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate;
|
||||
|
||||
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.runtime.ProcessInstance;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
|
@ -42,47 +42,44 @@ public interface BpmTaskCandidateStrategy {
|
|||
/**
|
||||
* 基于候选人参数,获得任务的候选用户们
|
||||
*
|
||||
* 注意:实现 calculateUsers 系列方法时,有两种选择:
|
||||
* 1. 只重写 calculateUsers 默认方法
|
||||
* 2. 都重写 calculateUsersByTask 和 calculateUsersByActivity 两个方法
|
||||
*
|
||||
* @param param 执行任务
|
||||
* @return 用户编号集合
|
||||
*/
|
||||
default Set<Long> calculateUsers(String param) {
|
||||
return Collections.emptySet();
|
||||
throw new UnsupportedOperationException("该分配方法未实现,请检查!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 基于执行任务,获得任务的候选用户们
|
||||
* 基于【执行任务】,获得任务的候选用户们
|
||||
*
|
||||
* @param execution 执行任务
|
||||
* @return 用户编号集合
|
||||
*/
|
||||
default Set<Long> calculateUsers(DelegateExecution execution, String param) {
|
||||
Set<Long> users = calculateUsers(param);
|
||||
removeDisableUsers(users);
|
||||
return users;
|
||||
default Set<Long> calculateUsersByTask(DelegateExecution execution, String param) {
|
||||
return calculateUsers(param);
|
||||
}
|
||||
|
||||
/**
|
||||
* 基于流程实例,获得任务的候选用户们
|
||||
* 基于【流程活动】,获得任务的候选用户们
|
||||
* <p>
|
||||
* 目的:用于获取未执行节点的候选用户们
|
||||
*
|
||||
* @param startUserId 流程发起人编号
|
||||
* @param processInstance 流程实例编号
|
||||
* @param activityId 活动 Id (对应 Bpmn XML id)
|
||||
* @param bpmnModel 流程图
|
||||
* @param activityId 活动 ID (对应 Bpmn XML id)
|
||||
* @param param 节点的参数
|
||||
* @param startUserId 流程发起人编号
|
||||
* @param processDefinitionId 流程定义编号
|
||||
* @param processVariables 流程变量
|
||||
* @return 用户编号集合
|
||||
*/
|
||||
default Set<Long> calculateUsers(Long startUserId, ProcessInstance processInstance, String activityId, String param) {
|
||||
Set<Long> users = calculateUsers(param);
|
||||
removeDisableUsers(users);
|
||||
return users;
|
||||
@SuppressWarnings("unused")
|
||||
default Set<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param,
|
||||
Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {
|
||||
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.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.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 java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
|
@ -17,14 +19,12 @@ import java.util.Set;
|
|||
*
|
||||
* @author jason
|
||||
*/
|
||||
public abstract class BpmTaskCandidateAbstractDeptLeaderStrategy extends BpmTaskCandidateAbstractStrategy {
|
||||
public abstract class AbstractBpmTaskCandidateDeptLeaderStrategy implements BpmTaskCandidateStrategy {
|
||||
|
||||
@Resource
|
||||
protected DeptApi deptApi;
|
||||
|
||||
public BpmTaskCandidateAbstractDeptLeaderStrategy(AdminUserApi adminUserApi, DeptApi deptApi) {
|
||||
super(adminUserApi);
|
||||
this.deptApi = deptApi;
|
||||
}
|
||||
@Resource
|
||||
protected AdminUserApi adminUserApi;
|
||||
|
||||
/**
|
||||
* 获得指定层级的部门负责人,只有第 level 的负责人
|
||||
|
@ -78,4 +78,17 @@ public abstract class BpmTaskCandidateAbstractDeptLeaderStrategy extends BpmTas
|
|||
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.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.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 java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
|
@ -16,11 +15,7 @@ import java.util.Set;
|
|||
* @author jason
|
||||
*/
|
||||
@Component
|
||||
public class BpmTaskCandidateDeptLeaderMultiStrategy extends BpmTaskCandidateAbstractDeptLeaderStrategy {
|
||||
|
||||
public BpmTaskCandidateDeptLeaderMultiStrategy(AdminUserApi adminUserApi, DeptApi deptApi) {
|
||||
super(adminUserApi, deptApi);
|
||||
}
|
||||
public class BpmTaskCandidateDeptLeaderMultiStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy {
|
||||
|
||||
@Override
|
||||
public BpmTaskCandidateStrategyEnum getStrategy() {
|
||||
|
@ -32,14 +27,19 @@ public class BpmTaskCandidateDeptLeaderMultiStrategy extends BpmTaskCandidateAbs
|
|||
// 参数格式: | 分隔:1)左边为部门(多个部门用 , 分隔)。2)右边为部门层级
|
||||
String[] params = param.split("\\|");
|
||||
Assert.isTrue(params.length == 2, "参数格式不匹配");
|
||||
deptApi.validateDeptList(StrUtils.splitToLong(params[0], ",")).checkError();
|
||||
Assert.isTrue(Integer.parseInt(params[1]) > 0, "部门层级必须大于 0");
|
||||
List<Long> deptIds = StrUtils.splitToLong(params[0], ",");
|
||||
int level = Integer.parseInt(params[1]);
|
||||
// 校验部门存在
|
||||
deptApi.validateDeptList(deptIds).checkError();
|
||||
Assert.isTrue(level > 0, "部门层级必须大于 0");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Long> calculateUsers(String param) {
|
||||
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.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
|
||||
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.dto.DeptRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -19,14 +19,10 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
|
|||
* @author kyle
|
||||
*/
|
||||
@Component
|
||||
public class BpmTaskCandidateDeptLeaderStrategy extends BpmTaskCandidateAbstractStrategy {
|
||||
public class BpmTaskCandidateDeptLeaderStrategy implements BpmTaskCandidateStrategy {
|
||||
|
||||
private final DeptApi deptApi;
|
||||
|
||||
public BpmTaskCandidateDeptLeaderStrategy(AdminUserApi adminUserApi, DeptApi deptApi) {
|
||||
super(adminUserApi);
|
||||
this.deptApi = deptApi;
|
||||
}
|
||||
@Resource
|
||||
private DeptApi deptApi;
|
||||
|
||||
@Override
|
||||
public BpmTaskCandidateStrategyEnum getStrategy() {
|
||||
|
@ -36,7 +32,7 @@ public class BpmTaskCandidateDeptLeaderStrategy extends BpmTaskCandidateAbstract
|
|||
@Override
|
||||
public void validateParam(String param) {
|
||||
Set<Long> deptIds = StrUtils.splitToLongSet(param);
|
||||
deptApi.validateDeptList(deptIds);
|
||||
deptApi.validateDeptList(deptIds).checkError();
|
||||
}
|
||||
|
||||
@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.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.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -19,14 +20,12 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
|
|||
* @author kyle
|
||||
*/
|
||||
@Component
|
||||
public class BpmTaskCandidateDeptMemberStrategy extends BpmTaskCandidateAbstractStrategy {
|
||||
public class BpmTaskCandidateDeptMemberStrategy implements BpmTaskCandidateStrategy {
|
||||
|
||||
private final DeptApi deptApi;
|
||||
|
||||
public BpmTaskCandidateDeptMemberStrategy(AdminUserApi adminUserApi, DeptApi deptApi) {
|
||||
super(adminUserApi);
|
||||
this.deptApi = deptApi;
|
||||
}
|
||||
@Resource
|
||||
private DeptApi deptApi;
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
@Override
|
||||
public BpmTaskCandidateStrategyEnum getStrategy() {
|
||||
|
@ -36,7 +35,7 @@ public class BpmTaskCandidateDeptMemberStrategy extends BpmTaskCandidateAbstract
|
|||
@Override
|
||||
public void validateParam(String param) {
|
||||
Set<Long> deptIds = StrUtils.splitToLongSet(param);
|
||||
deptApi.validateDeptList(deptIds);
|
||||
deptApi.validateDeptList(deptIds).checkError();
|
||||
}
|
||||
|
||||
@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.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.enums.BpmTaskCandidateStrategyEnum;
|
||||
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 jakarta.annotation.Resource;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.hutool.core.collection.ListUtil.toList;
|
||||
|
@ -26,16 +25,12 @@ import static cn.hutool.core.collection.ListUtil.toList;
|
|||
* @author jason
|
||||
*/
|
||||
@Component
|
||||
public class BpmTaskCandidateStartUserDeptLeaderMultiStrategy extends BpmTaskCandidateAbstractDeptLeaderStrategy {
|
||||
public class BpmTaskCandidateStartUserDeptLeaderMultiStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy {
|
||||
|
||||
@Resource
|
||||
@Lazy
|
||||
private BpmProcessInstanceService processInstanceService;
|
||||
|
||||
public BpmTaskCandidateStartUserDeptLeaderMultiStrategy(AdminUserApi adminUserApi, DeptApi deptApi) {
|
||||
super(adminUserApi, deptApi);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BpmTaskCandidateStrategyEnum getStrategy() {
|
||||
return BpmTaskCandidateStrategyEnum.START_USER_DEPT_LEADER_MULTI;
|
||||
|
@ -43,48 +38,33 @@ public class BpmTaskCandidateStartUserDeptLeaderMultiStrategy extends BpmTaskCan
|
|||
|
||||
@Override
|
||||
public void validateParam(String param) {
|
||||
// 参数是部门的层级
|
||||
Assert.isTrue(Integer.parseInt(param) > 0, "部门的层级必须大于 0");
|
||||
int level = Integer.parseInt(param); // 参数是部门的层级
|
||||
Assert.isTrue(level > 0, "部门的层级必须大于 0");
|
||||
}
|
||||
|
||||
@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());
|
||||
Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId());
|
||||
// 获取发起人的 multi 部门负责人
|
||||
DeptRespDTO dept = getStartUserDept(startUserId);
|
||||
DeptRespDTO dept = super.getStartUserDept(startUserId);
|
||||
if (dept == null) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
Set<Long> users = getMultiLevelDeptLeaderIds(toList(dept.getId()), Integer.valueOf(param)); // 参数是部门的层级
|
||||
// TODO @jason:这里 removeDisableUsers 的原因是啥呀?
|
||||
removeDisableUsers(users);
|
||||
return users;
|
||||
return super.getMultiLevelDeptLeaderIds(toList(dept.getId()), level);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Long> calculateUsers(Long startUserId, ProcessInstance processInstance, String activityId, String param) {
|
||||
DeptRespDTO dept = getStartUserDept(startUserId);
|
||||
public Set<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param,
|
||||
Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {
|
||||
int level = Integer.parseInt(param); // 参数是部门的层级
|
||||
DeptRespDTO dept = super.getStartUserDept(startUserId);
|
||||
if (dept == null) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
Set<Long> users = getMultiLevelDeptLeaderIds(toList(dept.getId()), Integer.valueOf(param)); // 参数是部门的层级
|
||||
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();
|
||||
return super.getMultiLevelDeptLeaderIds(toList(dept.getId()), level);
|
||||
}
|
||||
|
||||
}
|
|
@ -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.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.enums.BpmTaskCandidateStrategyEnum;
|
||||
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 jakarta.annotation.Resource;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
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
|
||||
*/
|
||||
@Component
|
||||
public class BpmTaskCandidateStartUserDeptLeaderStrategy extends BpmTaskCandidateAbstractDeptLeaderStrategy {
|
||||
public class BpmTaskCandidateStartUserDeptLeaderStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy {
|
||||
|
||||
@Resource
|
||||
@Lazy // 避免循环依赖
|
||||
|
@ -37,10 +36,6 @@ public class BpmTaskCandidateStartUserDeptLeaderStrategy extends BpmTaskCandidat
|
|||
return BpmTaskCandidateStrategyEnum.START_USER_DEPT_LEADER;
|
||||
}
|
||||
|
||||
public BpmTaskCandidateStartUserDeptLeaderStrategy(AdminUserApi adminUserApi, DeptApi deptApi) {
|
||||
super(adminUserApi, deptApi);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateParam(String param) {
|
||||
// 参数是部门的层级
|
||||
|
@ -48,44 +43,29 @@ public class BpmTaskCandidateStartUserDeptLeaderStrategy extends BpmTaskCandidat
|
|||
}
|
||||
|
||||
@Override
|
||||
public Set<Long> calculateUsers(DelegateExecution execution, String param) {
|
||||
public Set<Long> calculateUsersByTask(DelegateExecution execution, String param) {
|
||||
// 获得流程发起人
|
||||
ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());
|
||||
Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId());
|
||||
// 获取发起人的部门负责人
|
||||
Set<Long> users = getStartUserDeptLeader(startUserId, param);
|
||||
removeDisableUsers(users);
|
||||
return users;
|
||||
return getStartUserDeptLeader(startUserId, param);
|
||||
}
|
||||
|
||||
@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);
|
||||
removeDisableUsers(users);
|
||||
return users;
|
||||
return getStartUserDeptLeader(startUserId, 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) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
Long deptLeaderId = getAssignLevelDeptLeaderId(dept, Integer.valueOf(param)); // 参数是部门的层级
|
||||
Long deptLeaderId = super.getAssignLevelDeptLeaderId(dept, level);
|
||||
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,20 +1,24 @@
|
|||
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.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.util.BpmnModelUtils;
|
||||
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.system.api.user.AdminUserApi;
|
||||
import com.google.common.collect.Sets;
|
||||
import jakarta.annotation.Resource;
|
||||
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.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
|
@ -23,16 +27,12 @@ import java.util.*;
|
|||
* @author 芋道源码
|
||||
*/
|
||||
@Component
|
||||
public class BpmTaskCandidateStartUserSelectStrategy extends BpmTaskCandidateAbstractStrategy {
|
||||
public class BpmTaskCandidateStartUserSelectStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy {
|
||||
|
||||
@Resource
|
||||
@Lazy // 延迟加载,避免循环依赖
|
||||
private BpmProcessInstanceService processInstanceService;
|
||||
|
||||
public BpmTaskCandidateStartUserSelectStrategy(AdminUserApi adminUserApi) {
|
||||
super(adminUserApi);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BpmTaskCandidateStrategyEnum getStrategy() {
|
||||
return BpmTaskCandidateStrategyEnum.START_USER_SELECT;
|
||||
|
@ -42,7 +42,12 @@ public class BpmTaskCandidateStartUserSelectStrategy extends BpmTaskCandidateAbs
|
|||
public void validateParam(String param) {}
|
||||
|
||||
@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());
|
||||
Assert.notNull(processInstance, "流程实例({})不能为空", execution.getProcessInstanceId());
|
||||
Map<String, List<Long>> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance);
|
||||
|
@ -50,47 +55,43 @@ public class BpmTaskCandidateStartUserSelectStrategy extends BpmTaskCandidateAbs
|
|||
execution.getProcessInstanceId());
|
||||
// 获得审批人
|
||||
List<Long> assignees = startUserSelectAssignees.get(execution.getCurrentActivityId());
|
||||
Set<Long> users = new LinkedHashSet<>(assignees);
|
||||
removeDisableUsers(users);
|
||||
return users;
|
||||
return new LinkedHashSet<>(assignees);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Long> calculateUsers(Long startUserId, ProcessInstance processInstance, String activityId, String param) {
|
||||
if (processInstance == null) {
|
||||
return Collections.emptySet();
|
||||
public LinkedHashSet<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param,
|
||||
Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {
|
||||
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);
|
||||
Set<Long> users = new LinkedHashSet<>(assignees);
|
||||
removeDisableUsers(users);
|
||||
return users;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isParamRequired() {
|
||||
return false;
|
||||
return new LinkedHashSet<>(assignees);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得发起人自选审批人的 UserTask 列表
|
||||
* 获得发起人自选审批人或抄送人的 Task 列表
|
||||
*
|
||||
* @param bpmnModel BPMN 模型
|
||||
* @return UserTask 列表
|
||||
* @return Task 列表
|
||||
*/
|
||||
public static List<UserTask> getStartUserSelectUserTaskList(BpmnModel bpmnModel) {
|
||||
public static List<Task> getStartUserSelectTaskList(BpmnModel bpmnModel) {
|
||||
if (bpmnModel == null) {
|
||||
return null;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<UserTask> userTaskList = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class);
|
||||
if (CollUtil.isEmpty(userTaskList)) {
|
||||
return null;
|
||||
List<Task> tasks = new ArrayList<>();
|
||||
tasks.addAll(BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class));
|
||||
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()));
|
||||
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.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.util.BpmnModelUtils;
|
||||
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.bpmn.model.FlowElement;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -23,16 +25,12 @@ import java.util.Set;
|
|||
* @author kyle
|
||||
*/
|
||||
@Component
|
||||
public class BpmTaskCandidateAssignEmptyStrategy extends BpmTaskCandidateAbstractStrategy {
|
||||
public class BpmTaskCandidateAssignEmptyStrategy implements BpmTaskCandidateStrategy {
|
||||
|
||||
@Resource
|
||||
@Lazy // 延迟加载,避免循环依赖
|
||||
private BpmProcessDefinitionService processDefinitionService;
|
||||
|
||||
public BpmTaskCandidateAssignEmptyStrategy(AdminUserApi adminUserApi) {
|
||||
super(adminUserApi);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BpmTaskCandidateStrategyEnum getStrategy() {
|
||||
return BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY;
|
||||
|
@ -43,19 +41,28 @@ public class BpmTaskCandidateAssignEmptyStrategy extends BpmTaskCandidateAbstrac
|
|||
}
|
||||
|
||||
@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())) {
|
||||
Set<Long> users = new HashSet<>(BpmnModelUtils.parseAssignEmptyHandlerUserIds(execution.getCurrentFlowElement()));
|
||||
removeDisableUsers(users);
|
||||
return users;
|
||||
return new HashSet<>(BpmnModelUtils.parseAssignEmptyHandlerUserIds(flowElement));
|
||||
}
|
||||
|
||||
// 情况二:流程管理员
|
||||
if (Objects.equals(assignEmptyHandlerType, BpmUserTaskAssignEmptyHandlerTypeEnum.ASSIGN_ADMIN.getType())) {
|
||||
BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(execution.getProcessDefinitionId());
|
||||
Assert.notNull(processDefinition, "流程定义({})不存在", execution.getProcessDefinitionId());
|
||||
BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(processDefinitionId);
|
||||
Assert.notNull(processDefinition, "流程定义({})不存在", processDefinitionId);
|
||||
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.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.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.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
|
@ -16,11 +17,7 @@ import java.util.Set;
|
|||
* @author 芋道源码
|
||||
*/
|
||||
@Component
|
||||
public class BpmTaskCandidateExpressionStrategy extends BpmTaskCandidateAbstractStrategy {
|
||||
|
||||
public BpmTaskCandidateExpressionStrategy(AdminUserApi adminUserApi) {
|
||||
super(adminUserApi);
|
||||
}
|
||||
public class BpmTaskCandidateExpressionStrategy implements BpmTaskCandidateStrategy {
|
||||
|
||||
@Override
|
||||
public BpmTaskCandidateStrategyEnum getStrategy() {
|
||||
|
@ -33,11 +30,16 @@ public class BpmTaskCandidateExpressionStrategy extends BpmTaskCandidateAbstract
|
|||
}
|
||||
|
||||
@Override
|
||||
public Set<Long> calculateUsers(DelegateExecution execution, String param) {
|
||||
public Set<Long> calculateUsersByTask(DelegateExecution execution, String param) {
|
||||
Object result = FlowableUtils.getExpressionValue(execution, param);
|
||||
Set<Long> users = Convert.toSet(Long.class, result);
|
||||
removeDisableUsers(users);
|
||||
return users;
|
||||
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 = 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.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.enums.BpmTaskCandidateStrategyEnum;
|
||||
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 java.util.Collection;
|
||||
|
@ -20,14 +20,10 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
|
|||
* @author kyle
|
||||
*/
|
||||
@Component
|
||||
public class BpmTaskCandidateGroupStrategy extends BpmTaskCandidateAbstractStrategy {
|
||||
public class BpmTaskCandidateGroupStrategy implements BpmTaskCandidateStrategy {
|
||||
|
||||
private final BpmUserGroupService userGroupService;
|
||||
|
||||
public BpmTaskCandidateGroupStrategy(AdminUserApi adminUserApi, BpmUserGroupService userGroupService) {
|
||||
super(adminUserApi);
|
||||
this.userGroupService = userGroupService;
|
||||
}
|
||||
@Resource
|
||||
private BpmUserGroupService userGroupService;
|
||||
|
||||
@Override
|
||||
public BpmTaskCandidateStrategyEnum getStrategy() {
|
||||
|
@ -37,7 +33,7 @@ public class BpmTaskCandidateGroupStrategy extends BpmTaskCandidateAbstractStrat
|
|||
@Override
|
||||
public void validateParam(String param) {
|
||||
Set<Long> groupIds = StrUtils.splitToLongSet(param);
|
||||
userGroupService.getUserGroupList(groupIds);
|
||||
userGroupService.validUserGroups(groupIds);
|
||||
}
|
||||
|
||||
@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.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.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -19,14 +20,12 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
|
|||
* @author kyle
|
||||
*/
|
||||
@Component
|
||||
public class BpmTaskCandidatePostStrategy extends BpmTaskCandidateAbstractStrategy {
|
||||
public class BpmTaskCandidatePostStrategy implements BpmTaskCandidateStrategy {
|
||||
|
||||
private final PostApi postApi;
|
||||
|
||||
public BpmTaskCandidatePostStrategy(AdminUserApi adminUserApi, PostApi postApi) {
|
||||
super(adminUserApi);
|
||||
this.postApi = postApi;
|
||||
}
|
||||
@Resource
|
||||
private PostApi postApi;
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
@Override
|
||||
public BpmTaskCandidateStrategyEnum getStrategy() {
|
|
@ -1,14 +1,13 @@
|
|||
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.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
|
||||
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.RoleApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
|
@ -17,17 +16,13 @@ import java.util.Set;
|
|||
* @author kyle
|
||||
*/
|
||||
@Component
|
||||
public class BpmTaskCandidateRoleStrategy extends BpmTaskCandidateAbstractStrategy {
|
||||
public class BpmTaskCandidateRoleStrategy implements BpmTaskCandidateStrategy {
|
||||
|
||||
@Resource
|
||||
private RoleApi roleApi;
|
||||
@Resource
|
||||
private PermissionApi permissionApi;
|
||||
|
||||
public BpmTaskCandidateRoleStrategy(AdminUserApi adminUserApi) {
|
||||
super(adminUserApi);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BpmTaskCandidateStrategyEnum getStrategy() {
|
||||
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.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
|
||||
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.system.api.user.AdminUserApi;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
|
@ -20,16 +22,12 @@ import java.util.Set;
|
|||
* @author jason
|
||||
*/
|
||||
@Component
|
||||
public class BpmTaskCandidateStartUserStrategy extends BpmTaskCandidateAbstractStrategy {
|
||||
public class BpmTaskCandidateStartUserStrategy implements BpmTaskCandidateStrategy {
|
||||
|
||||
@Resource
|
||||
@Lazy // 延迟加载,避免循环依赖
|
||||
private BpmProcessInstanceService processInstanceService;
|
||||
|
||||
public BpmTaskCandidateStartUserStrategy(AdminUserApi adminUserApi) {
|
||||
super(adminUserApi);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BpmTaskCandidateStrategyEnum getStrategy() {
|
||||
return BpmTaskCandidateStrategyEnum.START_USER;
|
||||
|
@ -45,18 +43,15 @@ public class BpmTaskCandidateStartUserStrategy extends BpmTaskCandidateAbstractS
|
|||
}
|
||||
|
||||
@Override
|
||||
public Set<Long> calculateUsers(DelegateExecution execution, String param) {
|
||||
public Set<Long> calculateUsersByTask(DelegateExecution execution, String param) {
|
||||
ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());
|
||||
Set<Long> users = SetUtils.asSet(Long.valueOf(processInstance.getStartUserId()));
|
||||
removeDisableUsers(users);
|
||||
return users;
|
||||
return SetUtils.asSet(Long.valueOf(processInstance.getStartUserId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Long> calculateUsers(Long startUserId, ProcessInstance processInstance, String activityId, String param) {
|
||||
Set<Long> users = SetUtils.asSet(startUserId);
|
||||
removeDisableUsers(users);
|
||||
return users;
|
||||
public Set<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param,
|
||||
Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {
|
||||
return SetUtils.asSet(startUserId);
|
||||
}
|
||||
|
||||
}
|
|
@ -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.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.enums.BpmTaskCandidateStrategyEnum;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 用户 {@link BpmTaskCandidateStrategy} 实现类
|
||||
|
@ -16,11 +16,10 @@ import java.util.Set;
|
|||
* @author kyle
|
||||
*/
|
||||
@Component
|
||||
public class BpmTaskCandidateUserStrategy extends BpmTaskCandidateAbstractStrategy {
|
||||
public class BpmTaskCandidateUserStrategy implements BpmTaskCandidateStrategy {
|
||||
|
||||
public BpmTaskCandidateUserStrategy(AdminUserApi adminUserApi) {
|
||||
super(adminUserApi);
|
||||
}
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
@Override
|
||||
public BpmTaskCandidateStrategyEnum getStrategy() {
|
||||
|
@ -33,7 +32,7 @@ public class BpmTaskCandidateUserStrategy extends BpmTaskCandidateAbstractStrate
|
|||
}
|
||||
|
||||
@Override
|
||||
public Set<Long> calculateUsers(String param) {
|
||||
public LinkedHashSet<Long> calculateUsers(String param) {
|
||||
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.springframework.stereotype.Component;
|
||||
|
||||
// TODO @jason:这个自定义转换的原因是啥呀?
|
||||
/**
|
||||
* 根据流程变量 variable 的类型, 转换参数的值
|
||||
* 根据流程变量 variable 的类型,转换参数的值
|
||||
*
|
||||
* 目前用于 ConditionNodeConvert 的 buildConditionExpression 方法中
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
|
@ -20,11 +21,12 @@ public class VariableConvertByTypeExpressionFunction extends AbstractFlowableVar
|
|||
public static Object convertByType(VariableContainer variableContainer, String variableName, Object parmaValue) {
|
||||
Object variable = variableContainer.getVariable(variableName);
|
||||
if (variable != null && parmaValue != null) {
|
||||
// 如果值不是字符串类型, 流程变量的类型是字符串。 把值转成字符串
|
||||
// 如果值不是字符串类型,流程变量的类型是字符串,把值转成字符串
|
||||
if (!(parmaValue instanceof String) && variable instanceof String ) {
|
||||
return parmaValue.toString();
|
||||
}
|
||||
}
|
||||
return parmaValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -29,6 +29,8 @@ public enum BpmTaskCandidateStrategyEnum implements IntArrayValuable {
|
|||
START_USER_DEPT_LEADER(37, "发起人部门负责人"),
|
||||
START_USER_DEPT_LEADER_MULTI(38, "发起人连续多级部门的负责人"),
|
||||
USER_GROUP(40, "用户组"),
|
||||
FORM_USER(50, "表单内用户字段"),
|
||||
FORM_DEPT_LEADER(51, "表单内部门负责人"),
|
||||
EXPRESSION(60, "流程表达式"), // 表达式 ExpressionManager
|
||||
ASSIGN_EMPTY(1, "审批人为空"),
|
||||
;
|
||||
|
|
|
@ -52,7 +52,7 @@ public interface BpmnModelConstants {
|
|||
*/
|
||||
String USER_TASK_REJECT_HANDLER_TYPE = "rejectHandlerType";
|
||||
/**
|
||||
* BPMN ExtensionElement 的扩展属性,用于标记用户任务拒绝后的回退的任务 Id
|
||||
* BPMN ExtensionElement 的扩展属性,用于标记用户任务拒绝后的退回的任务 Id
|
||||
*/
|
||||
String USER_TASK_REJECT_RETURN_TASK_ID = "rejectReturnTaskId";
|
||||
|
||||
|
@ -104,10 +104,6 @@ public interface BpmnModelConstants {
|
|||
* BPMN Start Event Node Id
|
||||
*/
|
||||
String START_EVENT_NODE_ID = "StartEvent";
|
||||
/**
|
||||
* BPMN Start Event Node Name
|
||||
*/
|
||||
String START_EVENT_NODE_NAME = "开始";
|
||||
|
||||
/**
|
||||
* 发起人节点 ID
|
||||
|
|
|
@ -29,7 +29,12 @@ public class BpmnVariableConstants {
|
|||
* @see ProcessInstance#getProcessVariables()
|
||||
*/
|
||||
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}
|
||||
*
|
||||
|
|
|
@ -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} 的实现类
|
||||
*
|
||||
* 目前只有快搭模式的【抄送节点】使用
|
||||
* <p>
|
||||
* 目前只有仿钉钉/飞书模式的【抄送节点】使用
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
|
@ -34,14 +34,14 @@ public class BpmCopyTaskDelegate implements JavaDelegate {
|
|||
@Override
|
||||
public void execute(DelegateExecution execution) {
|
||||
// 1. 获得抄送人
|
||||
Set<Long> userIds = taskCandidateInvoker.calculateUsers(execution);
|
||||
Set<Long> userIds = taskCandidateInvoker.calculateUsersByTask(execution);
|
||||
if (CollUtil.isEmpty(userIds)) {
|
||||
return;
|
||||
}
|
||||
// 2. 执行抄送
|
||||
FlowElement currentFlowElement = execution.getCurrentFlowElement();
|
||||
processInstanceCopyService.createProcessInstanceCopy(userIds, execution.getProcessInstanceId(),
|
||||
currentFlowElement.getId(), null, currentFlowElement.getName());
|
||||
processInstanceCopyService.createProcessInstanceCopy(userIds, null, execution.getProcessInstanceId(),
|
||||
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.util.BpmnModelUtils;
|
||||
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 com.google.common.collect.ImmutableSet;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -44,9 +43,6 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener {
|
|||
@Resource
|
||||
@Lazy // 解决循环依赖
|
||||
private BpmTaskService taskService;
|
||||
@Resource
|
||||
@Lazy // 解决循环依赖
|
||||
private BpmActivityService activityService;
|
||||
|
||||
public static final Set<FlowableEngineEventType> TASK_EVENTS = ImmutableSet.<FlowableEngineEventType>builder()
|
||||
.add(FlowableEngineEventType.TASK_CREATED)
|
||||
|
@ -72,7 +68,7 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener {
|
|||
|
||||
@Override
|
||||
protected void activityCancelled(FlowableActivityCancelledEvent event) {
|
||||
List<HistoricActivityInstance> activityList = activityService.getHistoricActivityListByExecutionId(event.getExecutionId());
|
||||
List<HistoricActivityInstance> activityList = taskService.getHistoricActivityListByExecutionId(event.getExecutionId());
|
||||
if (CollUtil.isEmpty(activityList)) {
|
||||
log.error("[activityCancelled][使用 executionId({}) 查找不到对应的活动实例]", event.getExecutionId());
|
||||
return;
|
||||
|
@ -87,6 +83,7 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener {
|
|||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("PatternVariableCanBeUsed")
|
||||
protected void timerFired(FlowableEngineEntityEvent event) {
|
||||
// 1.1 只处理 BoundaryEvent 边界计时时间
|
||||
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.map.MapUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
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.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.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.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.model.Process;
|
||||
import org.flowable.bpmn.model.*;
|
||||
import org.flowable.common.engine.api.FlowableException;
|
||||
import org.flowable.common.engine.impl.util.io.BytesStreamSource;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
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_PREFIX;
|
||||
|
||||
/**
|
||||
* 流程模型转操作工具类
|
||||
* BPMN Model 操作工具类。目前分成三部分:
|
||||
*
|
||||
* 1. BPMN 修改 + 解析元素相关的方法
|
||||
* 2. BPMN 简单查找相关的方法
|
||||
* 3. BPMN 复杂遍历相关的方法
|
||||
* 4. BPMN 流程预测相关的方法
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Slf4j
|
||||
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) {
|
||||
Integer candidateStrategy = NumberUtils.parseInt(userTask.getAttributeValue(
|
||||
BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY));
|
||||
|
@ -35,6 +124,12 @@ public class BpmnModelUtils {
|
|||
return candidateStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析候选人参数
|
||||
*
|
||||
* @param userTask 任务节点
|
||||
* @return 候选人参数
|
||||
*/
|
||||
public static String parseCandidateParam(FlowElement userTask) {
|
||||
String candidateParam = userTask.getAttributeValue(
|
||||
BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM);
|
||||
|
@ -45,39 +140,130 @@ public class BpmnModelUtils {
|
|||
return candidateParam;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析审批类型
|
||||
*
|
||||
* @see BpmUserTaskApproveTypeEnum
|
||||
* @param userTask 任务节点
|
||||
* @return 审批类型
|
||||
*/
|
||||
public static Integer parseApproveType(FlowElement userTask) {
|
||||
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) {
|
||||
Integer rejectHandlerType = NumberUtils.parseInt(parseExtensionElement(userTask, USER_TASK_REJECT_HANDLER_TYPE));
|
||||
return BpmUserTaskRejectHandlerType.typeOf(rejectHandlerType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析任务拒绝返回任务节点 ID
|
||||
*
|
||||
* @param flowElement 任务节点
|
||||
* @return 任务拒绝返回任务节点 ID
|
||||
*/
|
||||
public static String parseReturnTaskId(FlowElement flowElement) {
|
||||
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) {
|
||||
return NumberUtils.parseInt(parseExtensionElement(userTask, USER_TASK_ASSIGN_START_USER_HANDLER_TYPE));
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析用户任务的审批人为空时,处理类型枚举
|
||||
*
|
||||
* @param userTask 任务节点
|
||||
* @return 处理类型枚举
|
||||
*/
|
||||
public static Integer parseAssignEmptyHandlerType(FlowElement userTask) {
|
||||
return NumberUtils.parseInt(parseExtensionElement(userTask, USER_TASK_ASSIGN_EMPTY_HANDLER_TYPE));
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析用户任务的审批人为空时,处理用户 ID 数组
|
||||
*
|
||||
* @param userTask 任务节点
|
||||
* @return 处理用户 ID 数组
|
||||
*/
|
||||
public static List<Long> parseAssignEmptyHandlerUserIds(FlowElement userTask) {
|
||||
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) {
|
||||
if (bpmnModel == null || StrUtil.isEmpty(flowElementId)) {
|
||||
return null;
|
||||
|
@ -101,6 +287,29 @@ public class BpmnModelUtils {
|
|||
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) {
|
||||
FlowElement flowElement = getFlowElementById(bpmnModel, flowElementId);
|
||||
if (flowElement == null) {
|
||||
|
@ -110,7 +319,7 @@ public class BpmnModelUtils {
|
|||
if (CollUtil.isEmpty(extensionElements)) {
|
||||
return null;
|
||||
}
|
||||
Map<Integer, BpmTaskRespVO.OperationButtonSetting> buttonSettings = MapUtil.newHashMap(16);
|
||||
Map<Integer, BpmTaskRespVO.OperationButtonSetting> buttonSettings = Maps.newHashMapWithExpectedSize(extensionElements.size());
|
||||
extensionElements.forEach(element -> {
|
||||
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);
|
||||
|
@ -123,6 +332,23 @@ public class BpmnModelUtils {
|
|||
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} 等等
|
||||
* @return 元素们
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends FlowElement> List<T> getBpmnModelElements(BpmnModel model, Class<T> clazz) {
|
||||
List<T> result = new ArrayList<>();
|
||||
model.getProcesses().forEach(process -> {
|
||||
process.getFlowElements().forEach(flowElement -> {
|
||||
if (flowElement.getClass().isAssignableFrom(clazz)) {
|
||||
result.add((T) flowElement);
|
||||
}
|
||||
});
|
||||
});
|
||||
model.getProcesses().forEach(process -> process.getFlowElements().forEach(flowElement -> {
|
||||
if (flowElement.getClass().isAssignableFrom(clazz)) {
|
||||
result.add((T) flowElement);
|
||||
}
|
||||
}));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -193,7 +418,7 @@ public class BpmnModelUtils {
|
|||
|
||||
public static EndEvent getEndEvent(BpmnModel model) {
|
||||
Process process = model.getMainProcess();
|
||||
// 从 flowElementList 找 endEvent. TODO 多个 EndEvent 会有问题
|
||||
// 从 flowElementList 找 endEvent
|
||||
return (EndEvent) CollUtil.findOne(process.getFlowElements(), flowElement -> flowElement instanceof EndEvent);
|
||||
}
|
||||
|
||||
|
@ -221,7 +446,7 @@ public class BpmnModelUtils {
|
|||
return StrUtil.utf8Str(bpmnBytes);
|
||||
}
|
||||
|
||||
// ========== 遍历相关的方法 ==========
|
||||
// ========== BPMN 复杂遍历相关的方法 ==========
|
||||
|
||||
/**
|
||||
* 找到 source 节点之前的所有用户任务节点
|
||||
|
@ -316,16 +541,16 @@ public class BpmnModelUtils {
|
|||
return userTaskList;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 迭代从后向前扫描,判断目标节点相对于当前节点是否是串行
|
||||
* 不存在直接回退到子流程中的情况,但存在从子流程出去到父流程情况
|
||||
* 不存在直接退回到子流程中的情况,但存在从子流程出去到父流程情况
|
||||
*
|
||||
* @param source 起始节点
|
||||
* @param target 目标节点
|
||||
* @param visitedElements 已经经过的连线的 ID,用于判断线路是否重复
|
||||
* @return 结果
|
||||
*/
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
public static boolean isSequentialReachable(FlowElement source, FlowElement target, Set<String> visitedElements) {
|
||||
visitedElements = visitedElements == null ? new HashSet<>() : visitedElements;
|
||||
// 不能是开始事件和子流程
|
||||
|
@ -436,11 +661,136 @@ public class BpmnModelUtils {
|
|||
return userTaskList;
|
||||
}
|
||||
|
||||
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);
|
||||
// ========== BPMN 流程预测相关的方法 ==========
|
||||
|
||||
/**
|
||||
* 流程预测,返回 StartEvent、UserTask、ServiceTask、EndEvent 节点元素,最终是 List 串行结果
|
||||
*
|
||||
* @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;
|
||||
|
||||
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.util.TenantUtils;
|
||||
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.impl.el.ExpressionManager;
|
||||
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.history.HistoricProcessInstance;
|
||||
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
|
||||
|
@ -94,6 +97,27 @@ public class FlowableUtils {
|
|||
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 表单
|
||||
*/
|
||||
public static Map<String, Object> getProcessInstanceFormVariable(HistoricProcessInstance processInstance) {
|
||||
Map<String, Object> formVariables = new HashMap<>(processInstance.getProcessVariables());
|
||||
filterProcessInstanceFormVariable(formVariables);
|
||||
return formVariables;
|
||||
Map<String, Object> processVariables = new HashMap<>(processInstance.getProcessVariables());
|
||||
return filterProcessInstanceFormVariable(processVariables);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -125,9 +148,22 @@ public class FlowableUtils {
|
|||
* @param processInstance 流程实例
|
||||
* @return 发起用户选择的审批人 Map
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -181,13 +217,30 @@ public class FlowableUtils {
|
|||
|
||||
// ========== Expression 相关的工具方法 ==========
|
||||
|
||||
public static Object getExpressionValue(VariableContainer variableContainer, String expressionString) {
|
||||
ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration();
|
||||
assert processEngineConfiguration != null;
|
||||
private static Object getExpressionValue(VariableContainer variableContainer, String expressionString,
|
||||
ProcessEngineConfigurationImpl processEngineConfiguration) {
|
||||
assert processEngineConfiguration!= null;
|
||||
ExpressionManager expressionManager = processEngineConfiguration.getExpressionManager();
|
||||
assert expressionManager != null;
|
||||
assert expressionManager!= null;
|
||||
Expression expression = expressionManager.createExpression(expressionString);
|
||||
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);
|
||||
|
||||
/**
|
||||
* 批量更新流程分类的排序:每个分类的 sort 值,从 0 开始递增
|
||||
*
|
||||
* @param ids 分类编号列表
|
||||
*/
|
||||
void updateCategorySortBatch(List<Long> ids);
|
||||
|
||||
}
|
|
@ -9,12 +9,15 @@ import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category.BpmCa
|
|||
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
|
||||
import cn.iocoder.yudao.module.bpm.dal.mysql.category.BpmCategoryMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
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.module.bpm.enums.ErrorCodeConstants.*;
|
||||
|
@ -56,7 +59,7 @@ public class BpmCategoryServiceImpl implements BpmCategoryService {
|
|||
private void validateCategoryNameUnique(BpmCategorySaveReqVO updateReqVO) {
|
||||
BpmCategoryDO category = bpmCategoryMapper.selectByName(updateReqVO.getName());
|
||||
if (category == null
|
||||
|| ObjUtil.equal(category.getId(), updateReqVO.getId())) {
|
||||
|| ObjUtil.equal(category.getId(), updateReqVO.getId())) {
|
||||
return;
|
||||
}
|
||||
throw exception(CATEGORY_NAME_DUPLICATE, updateReqVO.getName());
|
||||
|
@ -65,7 +68,7 @@ public class BpmCategoryServiceImpl implements BpmCategoryService {
|
|||
private void validateCategoryCodeUnique(BpmCategorySaveReqVO updateReqVO) {
|
||||
BpmCategoryDO category = bpmCategoryMapper.selectByCode(updateReqVO.getCode());
|
||||
if (category == null
|
||||
|| ObjUtil.equal(category.getId(), updateReqVO.getId())) {
|
||||
|| ObjUtil.equal(category.getId(), updateReqVO.getId())) {
|
||||
return;
|
||||
}
|
||||
throw exception(CATEGORY_CODE_DUPLICATE, updateReqVO.getCode());
|
||||
|
@ -108,4 +111,20 @@ public class BpmCategoryServiceImpl implements BpmCategoryService {
|
|||
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,29 +1,28 @@
|
|||
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.simple.BpmSimpleModelNodeVO;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelUpdateReqVO;
|
||||
import jakarta.validation.Valid;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.engine.repository.Model;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Flowable流程模型接口
|
||||
* 流程模型接口
|
||||
*
|
||||
* @author yunlongn
|
||||
*/
|
||||
public interface BpmModelService {
|
||||
|
||||
/**
|
||||
* 获得流程模型分页
|
||||
* 获得流程模型列表
|
||||
*
|
||||
* @param pageVO 分页查询
|
||||
* @return 流程模型分页
|
||||
* @param name 模型名称
|
||||
* @return 流程模型列表
|
||||
*/
|
||||
PageResult<Model> getModelPage(BpmModelPageReqVO pageVO);
|
||||
List<Model> getModelList(String name);
|
||||
|
||||
/**
|
||||
* 创建流程模型
|
||||
|
@ -65,6 +64,14 @@ public interface BpmModelService {
|
|||
*/
|
||||
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.util.ArrayUtil;
|
||||
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.object.PageUtils;
|
||||
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.BpmModelPageReqVO;
|
||||
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.BpmSimpleModelUpdateReqVO;
|
||||
|
@ -35,14 +32,15 @@ import org.springframework.validation.annotation.Validated;
|
|||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
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.*;
|
||||
|
||||
/**
|
||||
* Flowable流程模型实现
|
||||
* 主要进行 Flowable {@link Model} 的维护
|
||||
* 流程模型实现:主要进行 Flowable {@link Model} 的维护
|
||||
*
|
||||
* @author yunlongn
|
||||
* @author 芋道源码
|
||||
|
@ -64,27 +62,12 @@ public class BpmModelServiceImpl implements BpmModelService {
|
|||
private BpmTaskCandidateInvoker taskCandidateInvoker;
|
||||
|
||||
@Override
|
||||
public PageResult<Model> getModelPage(BpmModelPageReqVO pageVO) {
|
||||
public List<Model> getModelList(String name) {
|
||||
ModelQuery modelQuery = repositoryService.createModelQuery();
|
||||
modelQuery.modelTenantId(FlowableUtils.getTenantId());
|
||||
if (StrUtil.isNotBlank(pageVO.getKey())) {
|
||||
modelQuery.modelKey(pageVO.getKey());
|
||||
if (StrUtil.isNotEmpty(name)) {
|
||||
modelQuery.modelNameLike(name);
|
||||
}
|
||||
if (StrUtil.isNotBlank(pageVO.getName())) {
|
||||
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);
|
||||
return modelQuery.list();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -100,6 +83,7 @@ public class BpmModelServiceImpl implements BpmModelService {
|
|||
}
|
||||
|
||||
// 2.1 创建流程定义
|
||||
createReqVO.setSort(System.currentTimeMillis()); // 使用当前时间,作为排序
|
||||
Model model = repositoryService.newModel();
|
||||
BpmModelConvert.INSTANCE.copyToModel(model, createReqVO);
|
||||
model.setTenantId(FlowableUtils.getTenantId());
|
||||
|
@ -120,6 +104,34 @@ public class BpmModelServiceImpl implements BpmModelService {
|
|||
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) {
|
||||
Model model = repositoryService.getModel(id);
|
||||
if (model == null) {
|
||||
|
@ -139,7 +151,7 @@ public class BpmModelServiceImpl implements BpmModelService {
|
|||
Model model = validateModelExists(id);
|
||||
BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model);
|
||||
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;
|
||||
}
|
||||
|
@ -158,10 +170,10 @@ public class BpmModelServiceImpl implements BpmModelService {
|
|||
// 1.4 校验任务分配规则已配置
|
||||
taskCandidateInvoker.validateBpmnConfig(bpmnBytes);
|
||||
// 1.5 获取仿钉钉流程设计器模型数据
|
||||
byte[] simpleBytes = getModelSimpleJson(model.getId());
|
||||
String simpleJson = getModelSimpleJson(model.getId());
|
||||
|
||||
// 2.1 创建流程定义
|
||||
String definitionId = processDefinitionService.createProcessDefinition(model, metaInfo, bpmnBytes, simpleBytes, form);
|
||||
String definitionId = processDefinitionService.createProcessDefinition(model, metaInfo, bpmnBytes, simpleJson, form);
|
||||
|
||||
// 2.2 将老的流程定义进行挂起。也就是说,只有最新部署的流程定义,才可以发起任务。
|
||||
updateProcessDefinitionSuspended(model.getDeploymentId());
|
||||
|
@ -226,8 +238,8 @@ public class BpmModelServiceImpl implements BpmModelService {
|
|||
public BpmSimpleModelNodeVO getSimpleModel(String modelId) {
|
||||
Model model = validateModelExists(modelId);
|
||||
// 通过 ACT_RE_MODEL 表 EDITOR_SOURCE_EXTRA_VALUE_ID_ ,获取仿钉钉快搭模型的 JSON 数据
|
||||
byte[] jsonBytes = getModelSimpleJson(model.getId());
|
||||
return JsonUtils.parseObject(jsonBytes, BpmSimpleModelNodeVO.class);
|
||||
String json = getModelSimpleJson(model.getId());
|
||||
return JsonUtils.parseObject(json, BpmSimpleModelNodeVO.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -240,7 +252,7 @@ public class BpmModelServiceImpl implements BpmModelService {
|
|||
// 2.2 保存 Bpmn XML
|
||||
updateModelBpmnXml(model.getId(), BpmnModelUtils.getBpmnXml(bpmnModel));
|
||||
// 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));
|
||||
}
|
||||
|
||||
private byte[] getModelSimpleJson(String id) {
|
||||
return repositoryService.getModelEditorSourceExtra(id);
|
||||
@SuppressWarnings("JavaExistingMethodCanBeUsed")
|
||||
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) {
|
||||
if (ArrayUtil.isEmpty(jsonBytes)) {
|
||||
private void updateModelSimpleJson(String id, BpmSimpleModelNodeVO node) {
|
||||
if (node == null) {
|
||||
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;
|
||||
|
||||
/**
|
||||
* Flowable流程定义接口
|
||||
* 流程定义接口
|
||||
*
|
||||
* @author yunlong.li
|
||||
* @author ZJQ
|
||||
|
@ -48,12 +48,12 @@ public interface BpmProcessDefinitionService {
|
|||
* @param model 流程模型
|
||||
* @param modelMetaInfo 流程模型元信息
|
||||
* @param bpmnBytes BPMN XML 字节数组
|
||||
* @param simpleBytes SIMPLE Model JSON 字节数组
|
||||
* @param simpleJson SIMPLE Model JSON
|
||||
* @param form 表单
|
||||
* @return 流程编号
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* 更新模型编号
|
||||
*
|
||||
* @param modelId 流程定义编号
|
||||
* @param sort 排序
|
||||
*/
|
||||
void updateProcessDefinitionSortByModelId(String modelId, Long sort);
|
||||
|
||||
/**
|
||||
* 获得流程定义对应的 BPMN
|
||||
*
|
||||
|
|
|
@ -12,6 +12,7 @@ import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitio
|
|||
import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmProcessDefinitionInfoMapper;
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.common.engine.impl.db.SuspensionState;
|
||||
|
@ -23,8 +24,6 @@ import org.flowable.engine.repository.ProcessDefinitionQuery;
|
|||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
|
@ -120,7 +119,7 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
|
|||
|
||||
@Override
|
||||
public String createProcessDefinition(Model model, BpmModelMetaInfoVO modelMetaInfo,
|
||||
byte[] bpmnBytes, byte[] simpleBytes, BpmFormDO form) {
|
||||
byte[] bpmnBytes, String simpleJson, BpmFormDO form) {
|
||||
// 创建 Deployment 部署
|
||||
Deployment deploy = repositoryService.createDeployment()
|
||||
.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)
|
||||
.setModelId(model.getId()).setProcessDefinitionId(definition.getId()).setModelType(modelMetaInfo.getType())
|
||||
.setSimpleModel(StrUtil.str(simpleBytes, StandardCharsets.UTF_8));
|
||||
.setModelId(model.getId()).setProcessDefinitionId(definition.getId())
|
||||
.setModelType(modelMetaInfo.getType()).setSimpleModel(simpleJson);
|
||||
|
||||
if (form != null) {
|
||||
definitionDO.setFormFields(form.getFields()).setFormConf(form.getConf());
|
||||
|
@ -172,6 +171,11 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
|
|||
log.error("[updateProcessDefinitionState][流程定义({}) 修改未知状态({})]", id, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateProcessDefinitionSortByModelId(String modelId, Long sort) {
|
||||
processDefinitionMapper.updateByModelId(modelId, new BpmProcessDefinitionInfoDO().setSort(sort));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BpmnModel getProcessDefinitionBpmnModel(String 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 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 javax.annotation.Resource;
|
||||
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.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO;
|
||||
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.Set;
|
||||
|
||||
/**
|
||||
* 流程抄送 Service 接口
|
||||
|
@ -15,24 +16,29 @@ import java.util.Set;
|
|||
public interface BpmProcessInstanceCopyService {
|
||||
|
||||
/**
|
||||
* 流程实例的抄送
|
||||
* 【管理员】流程实例的抄送
|
||||
*
|
||||
* @param userIds 抄送的用户编号
|
||||
* @param reason 抄送意见
|
||||
* @param taskId 流程任务编号
|
||||
*/
|
||||
void createProcessInstanceCopy(Collection<Long> userIds, String taskId);
|
||||
void createProcessInstanceCopy(Collection<Long> userIds, String reason, String taskId);
|
||||
|
||||
/**
|
||||
* 流程实例的抄送
|
||||
* 【自动抄送】流程实例的抄送
|
||||
*
|
||||
* @param userIds 抄送的用户编号
|
||||
* @param reason 抄送意见
|
||||
* @param processInstanceId 流程编号
|
||||
* @param activityId 流程活动编号 id (对应 BPMN XML 节点 Id)
|
||||
* // TODO 芋艿这个 taskId 是不是可以不要了
|
||||
* @param taskId 任务编号
|
||||
* @param taskName 任务名称
|
||||
* @param activityId 流程活动编号(对应 {@link FlowNode#getId()})
|
||||
* @param activityName 任务编号(对应 {@link FlowNode#getName()})
|
||||
* @param taskId 任务编号,允许空
|
||||
*/
|
||||
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,
|
||||
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.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.dal.dataobject.task.BpmProcessInstanceCopyDO;
|
||||
import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmProcessInstanceCopyMapper;
|
||||
|
@ -19,7 +18,6 @@ import org.springframework.validation.annotation.Validated;
|
|||
import javax.annotation.Resource;
|
||||
import java.util.Collection;
|
||||
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.util.collection.CollectionUtils.convertList;
|
||||
|
@ -49,17 +47,19 @@ public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopy
|
|||
private BpmProcessDefinitionService processDefinitionService;
|
||||
|
||||
@Override
|
||||
public void createProcessInstanceCopy(Collection<Long> userIds, String taskId) {
|
||||
public void createProcessInstanceCopy(Collection<Long> userIds, String reason, String taskId) {
|
||||
Task task = taskService.getTask(taskId);
|
||||
if (ObjectUtil.isNull(task)) {
|
||||
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
|
||||
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 校验流程实例存在
|
||||
ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);
|
||||
if (processInstance == null) {
|
||||
|
@ -74,10 +74,10 @@ public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopy
|
|||
|
||||
// 2. 创建抄送流程
|
||||
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())
|
||||
.setCategory(processDefinition.getCategory()).setActivityId(activityId)
|
||||
.setTaskId(taskId).setTaskName(taskName));
|
||||
.setCategory(processDefinition.getCategory()).setTaskId(taskId)
|
||||
.setActivityId(activityId).setActivityName(activityName));
|
||||
processInstanceCopyMapper.insertBatch(copyList);
|
||||
}
|
||||
|
||||
|
@ -87,10 +87,4 @@ public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopy
|
|||
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,
|
||||
@Valid BpmProcessInstancePageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 获得表单字段权限
|
||||
*
|
||||
* @param reqVO 请求消息
|
||||
* @return 表单字段权限
|
||||
*/
|
||||
Map<String, String> getFormFieldsPermission(@Valid BpmFormFieldsPermissionReqVO reqVO);
|
||||
|
||||
// TODO @芋艿:重点在 review 下
|
||||
/**
|
||||
* 获取审批详情。
|
||||
|
@ -104,6 +96,14 @@ public interface BpmProcessInstanceService {
|
|||
*/
|
||||
BpmApprovalDetailRespVO getApprovalDetail(Long loginUserId, @Valid BpmApprovalDetailReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 获取流程实例的 BPMN 模型视图
|
||||
*
|
||||
* @param id 流程实例的编号
|
||||
* @return BPMN 模型视图
|
||||
*/
|
||||
BpmProcessInstanceBpmnModelViewRespVO getProcessInstanceBpmnModelView(String id);
|
||||
|
||||
// ========== Update 写入相关方法 ==========
|
||||
|
||||
/**
|
||||
|
@ -157,5 +157,4 @@ public interface BpmProcessInstanceService {
|
|||
*/
|
||||
void processProcessInstanceCompleted(ProcessInstance instance);
|
||||
|
||||
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -5,7 +5,9 @@ import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
|||
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
|
||||
import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskTimeoutHandlerTypeEnum;
|
||||
import org.flowable.bpmn.model.UserTask;
|
||||
import org.flowable.engine.history.HistoricActivityInstance;
|
||||
import org.flowable.task.api.Task;
|
||||
import org.flowable.task.api.TaskInfo;
|
||||
import org.flowable.task.api.history.HistoricTaskInstance;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
@ -32,6 +34,15 @@ public interface BpmTaskService {
|
|||
*/
|
||||
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 asc 是否升序
|
||||
* @return 流程任务列表
|
||||
*/
|
||||
List<HistoricTaskInstance> getTaskListByProcessInstanceId(String processInstanceId);
|
||||
List<HistoricTaskInstance> getTaskListByProcessInstanceId(String processInstanceId, Boolean asc);
|
||||
|
||||
/**
|
||||
* 获取任务
|
||||
|
@ -105,21 +117,30 @@ public interface BpmTaskService {
|
|||
* 根据条件查询正在进行中的任务
|
||||
*
|
||||
* @param processInstanceId 流程实例编号,不允许为空
|
||||
* @param assigned 是否分配了审批人,允许空
|
||||
* @param taskDefineKey 任务定义 Key,允许空
|
||||
* @param assigned 是否分配了审批人,允许空
|
||||
* @param taskDefineKey 任务定义 Key,允许空
|
||||
*/
|
||||
List<Task> getRunningTaskListByProcessInstanceId(String processInstanceId,
|
||||
Boolean assigned,
|
||||
String taskDefineKey);
|
||||
|
||||
/**
|
||||
* 获取当前任务的可回退的 UserTask 集合
|
||||
* 获取当前任务的可退回的 UserTask 集合
|
||||
*
|
||||
* @param id 当前的任务 ID
|
||||
* @return 可以回退的节点列表
|
||||
* @return 可以退回的节点列表
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* 通过任务 ID,查询任务名 Map
|
||||
* 获得指定流程实例的活动实例列表
|
||||
*
|
||||
* @param taskIds 任务 ID
|
||||
* @return 任务 ID 与名字的 Map
|
||||
* @param processInstanceId 流程实例的编号
|
||||
* @return 活动实例列表
|
||||
*/
|
||||
Map<String, String> getTaskNameByTaskIds(Collection<String> taskIds);
|
||||
List<HistoricActivityInstance> getActivityListByProcessInstanceId(String processInstanceId);
|
||||
|
||||
/**
|
||||
* 获得执行编号对应的活动实例
|
||||
*
|
||||
* @param executionId 执行编号
|
||||
* @return 活动实例
|
||||
*/
|
||||
List<HistoricActivityInstance> getHistoricActivityListByExecutionId(String executionId);
|
||||
|
||||
// ========== Update 写入相关方法 ==========
|
||||
|
||||
|
@ -170,10 +199,10 @@ public interface BpmTaskService {
|
|||
void moveTaskToEnd(String processInstanceId);
|
||||
|
||||
/**
|
||||
* 将任务回退到指定的 targetDefinitionKey 位置
|
||||
* 将任务退回到指定的 targetDefinitionKey 位置
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param reqVO 回退的任务key和当前所在的任务ID
|
||||
* @param reqVO 退回的任务key和当前所在的任务ID
|
||||
*/
|
||||
void returnTask(Long userId, BpmTaskReturnReqVO reqVO);
|
||||
|
||||
|
@ -201,14 +230,22 @@ public interface BpmTaskService {
|
|||
*/
|
||||
void deleteSignTask(Long userId, BpmTaskSignDeleteReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 抄送任务
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param reqVO 通过请求
|
||||
*/
|
||||
void copyTask(Long userId, @Valid BpmTaskCopyReqVO reqVO);
|
||||
|
||||
// ========== Event 事件相关方法 ==========
|
||||
|
||||
/**
|
||||
* 处理 Task 创建事件,目前是
|
||||
*
|
||||
* <p>
|
||||
* 1. 更新它的状态为审批中
|
||||
* 2. 处理自动通过的情况,例如说:1)无审批人时,是否自动通过、不通过;2)非【人工审核】时,是否自动通过、不通过
|
||||
*
|
||||
* <p>
|
||||
* 注意:它的触发时机,晚于 {@link #processTaskAssigned(Task)} 之后
|
||||
*
|
||||
* @param task 任务实体
|
||||
|
@ -233,8 +270,8 @@ public interface BpmTaskService {
|
|||
* 处理 Task 审批超时事件,可能会处理多个当前审批中的任务
|
||||
*
|
||||
* @param processInstanceId 流程示例编号
|
||||
* @param taskDefineKey 任务 Key
|
||||
* @param handlerType 处理类型,参见 {@link BpmUserTaskTimeoutHandlerTypeEnum}
|
||||
* @param taskDefineKey 任务 Key
|
||||
* @param handlerType 处理类型,参见 {@link BpmUserTaskTimeoutHandlerTypeEnum}
|
||||
*/
|
||||
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.FlowableUtils;
|
||||
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.dto.BpmMessageSendWhenTaskTimeoutReqDTO;
|
||||
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||
|
@ -36,9 +37,11 @@ import org.flowable.engine.HistoryService;
|
|||
import org.flowable.engine.ManagementService;
|
||||
import org.flowable.engine.RuntimeService;
|
||||
import org.flowable.engine.TaskService;
|
||||
import org.flowable.engine.history.HistoricActivityInstance;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
import org.flowable.task.api.DelegationState;
|
||||
import org.flowable.task.api.Task;
|
||||
import org.flowable.task.api.TaskInfo;
|
||||
import org.flowable.task.api.TaskQuery;
|
||||
import org.flowable.task.api.history.HistoricTaskInstance;
|
||||
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
|
||||
|
@ -81,6 +84,8 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
|||
@Resource
|
||||
private BpmProcessInstanceService processInstanceService;
|
||||
@Resource
|
||||
private BpmProcessDefinitionService bpmProcessDefinitionService;
|
||||
@Resource
|
||||
private BpmProcessInstanceCopyService processInstanceCopyService;
|
||||
@Resource
|
||||
private BpmModelService modelService;
|
||||
|
@ -116,6 +121,41 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
|||
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
|
||||
public PageResult<HistoricTaskInstance> getTaskDonePage(Long userId, BpmTaskPageReqVO pageVO) {
|
||||
HistoricTaskInstanceQuery taskQuery = historyService.createHistoricTaskInstanceQuery()
|
||||
|
@ -170,16 +210,16 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<HistoricTaskInstance> getTaskListByProcessInstanceId(String processInstanceId) {
|
||||
List<HistoricTaskInstance> tasks = historyService.createHistoricTaskInstanceQuery()
|
||||
public List<HistoricTaskInstance> getTaskListByProcessInstanceId(String processInstanceId, Boolean asc) {
|
||||
HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery()
|
||||
.includeTaskLocalVariables()
|
||||
.processInstanceId(processInstanceId)
|
||||
.orderByHistoricTaskInstanceStartTime().desc() // 创建时间倒序
|
||||
.list();
|
||||
if (CollUtil.isEmpty(tasks)) {
|
||||
return Collections.emptyList();
|
||||
.processInstanceId(processInstanceId);
|
||||
if (Boolean.TRUE.equals(asc)) {
|
||||
query.orderByHistoricTaskInstanceStartTime().asc();
|
||||
} else {
|
||||
query.orderByHistoricTaskInstanceStartTime().desc();
|
||||
}
|
||||
return tasks;
|
||||
return query.list();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -254,11 +294,43 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
|||
if (CollUtil.isEmpty(previousUserList)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 2.2 过滤:只有串行可到达的节点,才可以回退。类似非串行、子流程无法退回
|
||||
// 2.2 过滤:只有串行可到达的节点,才可以退回。类似非串行、子流程无法退回
|
||||
previousUserList.removeIf(userTask -> !BpmnModelUtils.isSequentialReachable(source, userTask, null));
|
||||
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
|
||||
public Map<String, String> getTaskNameByTaskIds(Collection<String> taskIds) {
|
||||
if (CollUtil.isEmpty(taskIds)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
List<Task> tasks = taskService.createTaskQuery().taskIds(taskIds).list();
|
||||
return convertMap(tasks, Task::getId, Task::getName);
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断指定用户,是否是当前任务的审批人
|
||||
*
|
||||
* @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 写入相关方法 ==========
|
||||
|
@ -352,11 +462,6 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
|||
throw exception(PROCESS_INSTANCE_NOT_EXISTS);
|
||||
}
|
||||
|
||||
// 2. 抄送用户
|
||||
if (CollUtil.isNotEmpty(reqVO.getCopyUserIds())) {
|
||||
processInstanceCopyService.createProcessInstanceCopy(reqVO.getCopyUserIds(), reqVO.getId());
|
||||
}
|
||||
|
||||
// 情况一:被委派的任务,不调用 complete 去完成任务
|
||||
if (DelegationState.PENDING.equals(task.getDelegationState())) {
|
||||
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());
|
||||
// 3.2 添加评论
|
||||
// 2.2 添加评论
|
||||
taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(),
|
||||
BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason()));
|
||||
// 3.3 调用 BPM complete 去完成任务
|
||||
// 2.3 调用 BPM complete 去完成任务
|
||||
// 其中,variables 是存储动态表单到 local 任务级别。过滤一下,避免 ProcessInstance 系统级的变量被占用
|
||||
if (CollUtil.isNotEmpty(reqVO.getVariables())) {
|
||||
Map<String, Object> variables = FlowableUtils.filterTaskFormVariable(reqVO.getVariables());
|
||||
|
@ -519,7 +624,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
|||
BpmUserTaskRejectHandlerType userTaskRejectHandlerType = BpmnModelUtils.parseRejectHandlerType(userTaskElement);
|
||||
if (userTaskRejectHandlerType == BpmUserTaskRejectHandlerType.RETURN_USER_TASK) {
|
||||
String returnTaskId = BpmnModelUtils.parseReturnTaskId(userTaskElement);
|
||||
Assert.notNull(returnTaskId, "回退的节点不能为空");
|
||||
Assert.notNull(returnTaskId, "退回的节点不能为空");
|
||||
returnTask(userId, new BpmTaskReturnReqVO().setId(task.getId())
|
||||
.setTargetTaskDefinitionKey(returnTaskId).setReason(reqVO.getReason()));
|
||||
return;
|
||||
|
@ -563,12 +668,12 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
|||
FlowElement targetElement = validateTargetTaskCanReturn(task.getTaskDefinitionKey(),
|
||||
reqVO.getTargetTaskDefinitionKey(), task.getProcessDefinitionId());
|
||||
|
||||
// 2. 调用 Flowable 框架的回退逻辑
|
||||
// 2. 调用 Flowable 框架的退回逻辑
|
||||
returnTask(task, targetElement, reqVO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 回退流程节点时,校验目标任务节点是否可回退
|
||||
* 退回流程节点时,校验目标任务节点是否可退回
|
||||
*
|
||||
* @param sourceKey 当前任务节点 Key
|
||||
* @param targetKey 目标任务节点 key
|
||||
|
@ -586,7 +691,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
|||
throw exception(TASK_TARGET_NODE_NOT_EXISTS);
|
||||
}
|
||||
|
||||
// 2.2 只有串行可到达的节点,才可以回退。类似非串行、子流程无法退回
|
||||
// 2.2 只有串行可到达的节点,才可以退回。类似非串行、子流程无法退回
|
||||
if (!BpmnModelUtils.isSequentialReachable(source, target, null)) {
|
||||
throw exception(TASK_RETURN_FAIL_SOURCE_TARGET_ERROR);
|
||||
}
|
||||
|
@ -594,10 +699,10 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
|||
}
|
||||
|
||||
/**
|
||||
* 执行回退逻辑
|
||||
* 执行退回逻辑
|
||||
*
|
||||
* @param currentTask 当前回退的任务
|
||||
* @param targetElement 需要回退到的目标任务
|
||||
* @param currentTask 当前退回的任务
|
||||
* @param targetElement 需要退回到的目标任务
|
||||
* @param 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<String> returnTaskKeyList = convertList(returnUserTaskList, UserTask::getId);
|
||||
|
||||
// 2. 给当前要被回退的 task 数组,设置回退意见
|
||||
// 2. 给当前要被退回的 task 数组,设置退回意见
|
||||
taskList.forEach(task -> {
|
||||
// 需要排除掉,不需要设置回退意见的任务
|
||||
// 需要排除掉,不需要设置退回意见的任务
|
||||
if (!returnTaskKeyList.contains(task.getTaskDefinitionKey())) {
|
||||
return;
|
||||
}
|
||||
|
@ -659,9 +764,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
|||
taskService.setOwner(taskId, task.getAssignee());
|
||||
// 3.2 执行委派,将任务委派给 delegateUser
|
||||
taskService.delegateTask(taskId, reqVO.getDelegateUserId().toString());
|
||||
// 3.3 更新 task 状态。
|
||||
// 为什么不更新原因?因为原因目前主要给审批通过、不通过时使用
|
||||
updateTaskStatus(taskId, BpmTaskStatusEnum.DELEGATE.getStatus());
|
||||
// 补充说明:委托不单独设置状态。如果需要,可通过 Task 的 DelegationState 字段,判断是否为 DelegationState.PENDING 委托中
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -868,6 +971,11 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
|||
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)) {
|
||||
return;
|
||||
}
|
||||
// TODO 芋艿:可以后续优化成 getSelf();
|
||||
// 特殊情况一:【人工审核】审批人为空,根据配置是否要自动通过、自动拒绝
|
||||
if (ObjectUtil.equal(approveType, BpmUserTaskApproveTypeEnum.USER.getType())) {
|
||||
// 如果有审批人、或者拥有人,则说明不满足情况一,不自动通过、不自动拒绝
|
||||
if (!ObjectUtil.isAllEmpty(task.getAssignee(), task.getOwner())) {
|
||||
return;
|
||||
}
|
||||
if (ObjectUtil.equal(assignEmptyHandlerType, BpmUserTaskAssignEmptyHandlerTypeEnum.APPROVE.getType())) {
|
||||
SpringUtil.getBean(BpmTaskService.class).approveTask(null, new BpmTaskApproveReqVO()
|
||||
.setId(task.getId()).setReason(BpmReasonEnum.ASSIGN_EMPTY_APPROVE.getReason()));
|
||||
|
@ -987,7 +1100,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
|||
}
|
||||
// 审批人与提交人为同一人时,根据 BpmUserTaskAssignStartUserHandlerTypeEnum 策略进行处理
|
||||
if (StrUtil.equals(task.getAssignee(), processInstance.getStartUserId())) {
|
||||
// 判断是否为回退或者驳回:如果是回退或者驳回不走这个策略
|
||||
// 判断是否为退回或者驳回:如果是退回或者驳回不走这个策略
|
||||
// TODO 芋艿:【优化】未来有没更好的判断方式?!另外,还要考虑清理机制。就是说,下次处理了之后,就移除这个标识
|
||||
Boolean returnTaskFlag = runtimeService.getVariable(processInstance.getProcessInstanceId(),
|
||||
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;
|
||||
|
||||
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.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.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.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.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.Spy;
|
||||
import org.mockito.internal.util.collections.Sets;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
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.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.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* {@link BpmTaskCandidateInvoker} 的单元测试
|
||||
|
@ -39,44 +49,207 @@ public class BpmTaskCandidateInvokerTest extends BaseMockitoUnitTest {
|
|||
@Mock
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
@Spy
|
||||
private BpmTaskCandidateStrategy strategy ;
|
||||
@Mock
|
||||
private BpmProcessInstanceService processInstanceService;
|
||||
|
||||
@Spy
|
||||
private List<BpmTaskCandidateStrategy> strategyList ;
|
||||
private BpmTaskCandidateStrategy userStrategy;
|
||||
@Mock
|
||||
private BpmTaskCandidateAssignEmptyStrategy emptyStrategy;
|
||||
|
||||
@Spy
|
||||
private List<BpmTaskCandidateStrategy> strategyList;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
strategy = new BpmTaskCandidateUserStrategy(adminUserApi); // 创建strategy实例
|
||||
strategyList = Collections.singletonList(strategy); // 创建strategyList
|
||||
userStrategy = new BpmTaskCandidateUserStrategy(); // 创建 strategy 实例
|
||||
when(emptyStrategy.getStrategy()).thenReturn(BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY);
|
||||
strategyList = List.of(userStrategy, emptyStrategy); // 创建 strategyList
|
||||
taskCandidateInvoker = new BpmTaskCandidateInvoker(strategyList, adminUserApi);
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景:成功计算到候选人,但是移除了发起人的用户
|
||||
*/
|
||||
@Test
|
||||
public void testCalculateUsers() {
|
||||
// 准备参数
|
||||
String param = "1,2";
|
||||
DelegateExecution execution = mock(DelegateExecution.class);
|
||||
// mock 方法(DelegateExecution)
|
||||
UserTask userTask = mock(UserTask.class);
|
||||
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.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);
|
||||
public void testCalculateUsersByTask_some() {
|
||||
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.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 移除发起人的用户
|
||||
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);
|
||||
// 断言
|
||||
assertEquals(asSet(1L, 2L), results);
|
||||
// 调用
|
||||
Set<Long> results = taskCandidateInvoker.calculateUsersByTask(execution);
|
||||
// 断言
|
||||
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
|
||||
|
|
|
@ -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.module.system.api.dept.DeptApi;
|
||||
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.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
|
@ -11,14 +12,12 @@ import org.mockito.Mock;
|
|||
import java.util.Set;
|
||||
|
||||
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 java.util.Arrays.asList;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@Disabled // TODO 芋艿:临时注释
|
||||
public class BpmTaskCandidateDeptLeaderStrategyTest extends BaseMockitoUnitTest {
|
||||
|
||||
@InjectMocks
|
||||
|
@ -30,16 +29,16 @@ public class BpmTaskCandidateDeptLeaderStrategyTest extends BaseMockitoUnitTest
|
|||
@Test
|
||||
public void testCalculateUsers() {
|
||||
// 准备参数
|
||||
String param = "1,2";
|
||||
String param = "10,20";
|
||||
// mock 方法
|
||||
DeptRespDTO dept1 = randomPojo(DeptRespDTO.class, o -> o.setLeaderUserId(11L));
|
||||
DeptRespDTO dept2 = randomPojo(DeptRespDTO.class, o -> o.setLeaderUserId(22L));
|
||||
when(deptApi.getDeptList(eq(asSet(1L, 2L)))).thenReturn(success(asList(dept1, dept2)));
|
||||
when(deptApi.getDeptList(eq(SetUtils.asSet(10L, 20L)))).thenReturn(success(asList(
|
||||
randomPojo(DeptRespDTO.class, o -> o.setId(10L).setParentId(10L).setLeaderUserId(11L)),
|
||||
randomPojo(DeptRespDTO.class, o -> o.setId(20L).setParentId(20L).setLeaderUserId(21L)))));
|
||||
|
||||
// 调用
|
||||
Set<Long> results = strategy.calculateUsers(null, param);
|
||||
// 断言
|
||||
assertEquals(asSet(11L, 22L), results);
|
||||
Set<Long> userIds = strategy.calculateUsers(param);
|
||||
// 断言结果
|
||||
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.module.system.api.dept.DeptApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
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.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
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.common.util.collection.SetUtils.asSet;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@Disabled // TODO 芋艿:临时注释
|
||||
public class BpmTaskCandidateDeptMemberStrategyTest extends BaseMockitoUnitTest {
|
||||
|
||||
@InjectMocks
|
||||
private BpmTaskCandidateDeptMemberStrategy strategy;
|
||||
|
||||
@Mock
|
||||
private DeptApi deptApi;
|
||||
@Mock
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
@Test
|
||||
public void testCalculateUsers() {
|
||||
// 准备参数
|
||||
String param = "11,22";
|
||||
String param = "10,20";
|
||||
// mock 方法
|
||||
List<AdminUserRespDTO> users = convertList(asSet(11L, 22L),
|
||||
id -> new AdminUserRespDTO().setId(id));
|
||||
when(adminUserApi.getUserListByDeptIds(eq(asSet(11L, 22L)))).thenReturn(success(users));
|
||||
when(adminUserApi.getUserListByDeptIds(eq(SetUtils.asSet(10L, 20L)))).thenReturn(success(asList(
|
||||
randomPojo(AdminUserRespDTO.class, o -> o.setId(11L)),
|
||||
randomPojo(AdminUserRespDTO.class, o -> o.setId(21L)))));
|
||||
|
||||
// 调用
|
||||
Set<Long> results = strategy.calculateUsers(null, param);
|
||||
// 断言
|
||||
assertEquals(asSet(11L, 22L), results);
|
||||
Set<Long> userIds = strategy.calculateUsers(param);
|
||||
// 断言结果
|
||||
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)));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue