Commit a5dc4e787654b1adac667ca29cb995f4cb703f94

Authored by 芋道源码
Committed by Gitee
2 parents 0abc618a 3720a648

!839 流程增加抄送功能

Merge pull request !839 from 云开/feature/BPM_CC_develop
Showing 31 changed files with 1966 additions and 2 deletions
sql/mysql/ruoyi-vue-pro.sql
@@ -2479,6 +2479,15 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i @@ -2479,6 +2479,15 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
2479 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 (2523, '客户限制配置导出', 'crm:customer-limit-config:export', 3, 5, 2518, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-18 13:33:53', '', '2023-11-18 13:33:53', b'0'); 2479 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 (2523, '客户限制配置导出', 'crm:customer-limit-config:export', 3, 5, 2518, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-18 13:33:53', '', '2023-11-18 13:33:53', b'0');
2480 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 (2524, '系统配置', '', 1, 99, 2397, 'config', 'ep:connection', '', '', 0, b'1', b'1', b'1', '1', '2023-11-18 21:58:00', '1', '2023-11-18 21:58:00', b'0'); 2480 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 (2524, '系统配置', '', 1, 99, 2397, 'config', 'ep:connection', '', '', 0, b'1', b'1', b'1', '1', '2023-11-18 21:58:00', '1', '2023-11-18 21:58:00', b'0');
2481 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 (2525, 'WebSocket 测试', '', 2, 7, 2, 'websocket', 'ep:connection', 'infra/webSocket/index', 'InfraWebSocket', 0, b'1', b'1', b'1', '1', '2023-11-23 19:41:55', '1', '2023-11-24 19:22:30', b'0'); 2481 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 (2525, 'WebSocket 测试', '', 2, 7, 2, 'websocket', 'ep:connection', 'infra/webSocket/index', 'InfraWebSocket', 0, b'1', b'1', b'1', '1', '2023-11-23 19:41:55', '1', '2023-11-24 19:22:30', b'0');
  2482 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`,
  2483 + `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`,
  2484 + `updater`, `update_time`, `deleted`)
  2485 +VALUES (2526, '抄送流程', '', 2, 21, 1200, 'processInstanceCC', 'eye', '/bpm/task/cc/index',
  2486 + 'BpmCCProcessInstance', 0, true, true, true, 1, '2023-01-13 16:14:00', '1', '2023-01-13 16:14:00', false),
  2487 + (2527, '抄送流程查询', 'bpm:process-instance-cc:query', 3, 1, 2526, '', '', '', NULL, 0, b'1', b'1', b'1', '1',
  2488 + '2023-01-13 16:14:00', '1', '2023-01-13 16:14:00', b'0'),
  2489 + (2528, '抄送流程创建', 'bpm:process-instance-cc:create', 3, 2, 2526, '', '', '', NULL, 0, b'1', b'1', b'1', '1',
  2490 + '2023-01-13 16:14:00', '1', '2023-01-13 16:14:00', b'0');
2482 COMMIT; 2491 COMMIT;
2483 2492
2484 -- ---------------------------- 2493 -- ----------------------------
yudao-framework/yudao-spring-boot-starter-flowable/src/main/java/cn/iocoder/yudao/framework/flowable/config/YudaoFlowableConfiguration.java
@@ -17,8 +17,8 @@ public class YudaoFlowableConfiguration { @@ -17,8 +17,8 @@ public class YudaoFlowableConfiguration {
17 * 17 *
18 * 如果不创建,会导致项目启动时,Flowable 报错的问题 18 * 如果不创建,会导致项目启动时,Flowable 报错的问题
19 */ 19 */
20 - @Bean  
21 - @ConditionalOnMissingBean 20 + @Bean(name = "applicationTaskExecutor")
  21 + @ConditionalOnMissingBean(name = "applicationTaskExecutor")
22 public AsyncListenableTaskExecutor taskExecutor() { 22 public AsyncListenableTaskExecutor taskExecutor() {
23 ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); 23 ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
24 executor.setCorePoolSize(8); 24 executor.setCorePoolSize(8);
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/candidate/vo/BpmTaskCandidateRuleVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo;
  2 +
  3 +import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleBaseVO;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import jakarta.validation.constraints.NotNull;
  6 +import lombok.Data;
  7 +
  8 +import java.util.Set;
  9 +
  10 +/**
  11 + * 流程任务分配规则 Base VO,提供给添加、修改、详细的子 VO 使用
  12 + * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
  13 + *
  14 + * @see BpmTaskAssignRuleBaseVO
  15 + */
  16 +@Data
  17 +public class BpmTaskCandidateRuleVO {
  18 +
  19 + @Schema(description = "规则类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "bpm_task_assign_rule_type")
  20 + @NotNull(message = "规则类型不能为空")
  21 + private Integer type;
  22 +
  23 + @Schema(description = "规则值数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3")
  24 + @NotNull(message = "规则值数组不能为空")
  25 + private Set<Long> options;
  26 +
  27 +}
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java
@@ -2,7 +2,9 @@ package cn.iocoder.yudao.module.bpm.controller.admin.task; @@ -2,7 +2,9 @@ package cn.iocoder.yudao.module.bpm.controller.admin.task;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
5 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*; 6 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*;
  7 +import cn.iocoder.yudao.module.bpm.service.cc.BpmProcessInstanceCopyService;
6 import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; 8 import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
7 import io.swagger.v3.oas.annotations.tags.Tag; 9 import io.swagger.v3.oas.annotations.tags.Tag;
8 import io.swagger.v3.oas.annotations.Parameter; 10 import io.swagger.v3.oas.annotations.Parameter;
@@ -26,6 +28,9 @@ public class BpmProcessInstanceController { @@ -26,6 +28,9 @@ public class BpmProcessInstanceController {
26 @Resource 28 @Resource
27 private BpmProcessInstanceService processInstanceService; 29 private BpmProcessInstanceService processInstanceService;
28 30
  31 + @Resource
  32 + private BpmProcessInstanceCopyService processInstanceCopyService;
  33 +
29 @GetMapping("/my-page") 34 @GetMapping("/my-page")
30 @Operation(summary = "获得我的实例分页列表", description = "在【我的流程】菜单中,进行调用") 35 @Operation(summary = "获得我的实例分页列表", description = "在【我的流程】菜单中,进行调用")
31 @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") 36 @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')")
@@ -56,4 +61,20 @@ public class BpmProcessInstanceController { @@ -56,4 +61,20 @@ public class BpmProcessInstanceController {
56 processInstanceService.cancelProcessInstance(getLoginUserId(), cancelReqVO); 61 processInstanceService.cancelProcessInstance(getLoginUserId(), cancelReqVO);
57 return success(true); 62 return success(true);
58 } 63 }
  64 +
  65 +
  66 + @PostMapping("/cc/create")
  67 + @Operation(summary = "抄送流程")
  68 + @PreAuthorize("@ss.hasPermission('bpm:process-instance-cc:create')")
  69 + public CommonResult<Boolean> createProcessInstanceCC(@Valid @RequestBody BpmProcessInstanceCCReqVO createReqVO) {
  70 + return success(processInstanceCopyService.ccProcessInstance(SecurityFrameworkUtils.getLoginUserId(), createReqVO));
  71 + }
  72 +
  73 + @GetMapping("/cc/my-page")
  74 + @Operation(summary = "获得抄送流程分页列表")
  75 + @PreAuthorize("@ss.hasPermission('bpm:process-instance-cc:query')")
  76 + public CommonResult<PageResult<BpmProcessInstanceCCPageItemRespVO>> getProcessInstanceCCPage(@Valid BpmProcessInstanceCCMyPageReqVO pageReqVO) {
  77 + return success(processInstanceCopyService.getMyProcessInstanceCCPage(SecurityFrameworkUtils.getLoginUserId(), pageReqVO));
  78 + }
  79 +
59 } 80 }
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCCMyPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +import org.springframework.format.annotation.DateTimeFormat;
  9 +
  10 +import java.time.LocalDateTime;
  11 +
  12 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  13 +
  14 +@Schema(description = "管理后台 - 流程实例抄送的分页 Item Response VO")
  15 +@Data
  16 +@EqualsAndHashCode(callSuper = true)
  17 +@ToString(callSuper = true)
  18 +public class BpmProcessInstanceCCMyPageReqVO extends PageParam {
  19 +
  20 + @Schema(description = "流程名称", example = "芋道")
  21 + private String processInstanceName;
  22 +
  23 + @Schema(description = "流程编号", example = "123456768")
  24 + private String processInstanceId;
  25 +
  26 + @Schema(description = "创建时间")
  27 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  28 + private LocalDateTime[] createTime;
  29 +
  30 +}
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCCPageItemRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +
  8 +@Schema(description = "管理后台 - 流程实例抄送的分页 Item Response VO")
  9 +@Data
  10 +public class BpmProcessInstanceCCPageItemRespVO {
  11 +
  12 + /**
  13 + * 编号
  14 + */
  15 + @Schema(description = "抄送主键")
  16 + private Long id;
  17 +
  18 + /**
  19 + * 发起人Id
  20 + */
  21 + @Schema(description = "发起人Id")
  22 + private Long startUserId;
  23 +
  24 + @Schema(description = "发起人别名")
  25 + private String startUserNickname;
  26 +
  27 + /**
  28 + * 流程主键
  29 + */
  30 + @Schema(description = "流程实例的主键")
  31 + private String processInstanceId;
  32 +
  33 + @Schema(description = "流程实例的名称")
  34 + private String processInstanceName;
  35 + /**
  36 + * 任务主键
  37 + */
  38 + @Schema(description = "发起抄送的任务编号")
  39 + private String taskId;
  40 +
  41 + @Schema(description = "发起抄送的任务名称")
  42 + private String taskName;
  43 +
  44 + @Schema(description = "抄送原因")
  45 + private String reason;
  46 +
  47 + @Schema(description = "抄送人")
  48 + private String creator;
  49 +
  50 + @Schema(description = "抄送人别名")
  51 + private String creatorNickname;
  52 +
  53 + @Schema(description = "抄送时间")
  54 + private LocalDateTime createTime;
  55 +}
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCCReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance;
  2 +
  3 +import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateRuleVO;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import jakarta.validation.constraints.NotBlank;
  6 +import jakarta.validation.constraints.NotEmpty;
  7 +import jakarta.validation.constraints.NotNull;
  8 +import lombok.Data;
  9 +import lombok.EqualsAndHashCode;
  10 +import lombok.ToString;
  11 +
  12 +import java.util.Set;
  13 +
  14 +@Schema(description = "管理后台 - 流程实例的抄送 Request VO")
  15 +@Data
  16 +@EqualsAndHashCode(callSuper = true)
  17 +@ToString(callSuper = true)
  18 +public class BpmProcessInstanceCCReqVO extends BpmTaskCandidateRuleVO {
  19 +
  20 + @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  21 + @NotEmpty(message = "任务编号不能为空")
  22 + private String taskKey;
  23 +
  24 + @Schema(description = "任务名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  25 + @NotEmpty(message = "任务名称不能为空")
  26 + private String taskName;
  27 +
  28 + @Schema(description = "流程实例的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  29 + @NotEmpty(message = "流程实例的编号不能为空")
  30 + private String processInstanceKey;
  31 +
  32 + @Schema(description = "发起流程的用户的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  33 + @NotNull(message = "发起流程的用户的编号不能为空")
  34 + private Long startUserId;
  35 +
  36 +
  37 + @Schema(description = "任务实例名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  38 + @NotEmpty(message = "任务实例名称不能为空")
  39 + private String processInstanceName;
  40 +
  41 + @Schema(description = "抄送原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "请帮忙审查下!")
  42 + @NotBlank(message = "抄送原因不能为空")
  43 + private String reason;
  44 +}
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java
1 package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task; 1 package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task;
2 2
  3 +import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateRuleVO;
3 import io.swagger.v3.oas.annotations.media.Schema; 4 import io.swagger.v3.oas.annotations.media.Schema;
4 import lombok.Data; 5 import lombok.Data;
5 6
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/cc/BpmProcessInstanceCopyConvert.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.convert.cc;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
  5 +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCCPageItemRespVO;
  6 +import cn.iocoder.yudao.module.bpm.dal.dataobject.cc.BpmProcessInstanceCopyDO;
  7 +import cn.iocoder.yudao.module.bpm.service.cc.BpmProcessInstanceCopyVO;
  8 +import org.mapstruct.Mapper;
  9 +import org.mapstruct.factory.Mappers;
  10 +
  11 +import java.util.List;
  12 +import java.util.Map;
  13 +
  14 +/**
  15 + * 动态表单 Convert
  16 + *
  17 + * @author 芋艿
  18 + */
  19 +@Mapper
  20 +public interface BpmProcessInstanceCopyConvert {
  21 +
  22 + BpmProcessInstanceCopyConvert INSTANCE = Mappers.getMapper(BpmProcessInstanceCopyConvert.class);
  23 +
  24 + BpmProcessInstanceCopyDO copy(BpmProcessInstanceCopyDO bean);
  25 +
  26 + BpmProcessInstanceCopyVO convert(BpmProcessInstanceCopyDO bean);
  27 +
  28 + List<BpmProcessInstanceCopyVO> convertList2(List<BpmProcessInstanceCopyDO> list);
  29 +
  30 + List<BpmProcessInstanceCCPageItemRespVO> convertList(List<BpmProcessInstanceCopyDO> list);
  31 +
  32 + default PageResult<BpmProcessInstanceCCPageItemRespVO> convertPage(PageResult<BpmProcessInstanceCopyDO> page
  33 + , Map<String/* taskId */, String/* taskName */> taskMap
  34 + , Map<String/* processInstaneId */, String/* processInstaneName */> processInstaneMap
  35 + , Map<Long/* userId */, String/* userName */> userMap
  36 + ) {
  37 + List<BpmProcessInstanceCCPageItemRespVO> list = convertList(page.getList());
  38 + for (BpmProcessInstanceCCPageItemRespVO vo : list) {
  39 + MapUtils.findAndThen(userMap, Long.valueOf(vo.getCreator()),
  40 + vo::setCreatorNickname);
  41 + MapUtils.findAndThen(userMap, vo.getStartUserId(),
  42 + vo::setStartUserNickname);
  43 + MapUtils.findAndThen(taskMap, vo.getTaskId(),
  44 + vo::setTaskName);
  45 + MapUtils.findAndThen(processInstaneMap, vo.getProcessInstanceId(),
  46 + vo::setProcessInstanceName);
  47 + }
  48 + return new PageResult<>(list, page.getTotal());
  49 + }
  50 +
  51 +}
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/cc/BpmProcessInstanceCopyDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.dal.dataobject.cc;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import com.baomidou.mybatisplus.annotation.TableId;
  5 +import com.baomidou.mybatisplus.annotation.TableName;
  6 +import lombok.*;
  7 +
  8 +/**
  9 + * 流程抄送对象
  10 + *
  11 + * @author kyle
  12 + * @date 2022-05-19
  13 + */
  14 +@TableName(value = "bpm_process_instance_copy", autoResultMap = true)
  15 +@Data
  16 +@EqualsAndHashCode(callSuper = true)
  17 +@ToString(callSuper = true)
  18 +@Builder
  19 +@NoArgsConstructor
  20 +@AllArgsConstructor
  21 +public class BpmProcessInstanceCopyDO extends BaseDO {
  22 +
  23 + /**
  24 + * 编号
  25 + */
  26 + @TableId
  27 + private Long id;
  28 +
  29 + /**
  30 + * 发起人Id
  31 + */
  32 + private Long startUserId;
  33 + /**
  34 + * 流程名
  35 + */
  36 + private String processInstanceName;
  37 + /**
  38 + * 流程主键
  39 + */
  40 + private String processInstanceId;
  41 +
  42 + /**
  43 + * 任务主键
  44 + */
  45 + private String taskId;
  46 +
  47 + /**
  48 + * 任务名称
  49 + */
  50 + private String taskName;
  51 +
  52 + /**
  53 + * 用户主键
  54 + */
  55 + private Long userId;
  56 +
  57 + /**
  58 + * 抄送原因
  59 + */
  60 + private String reason;
  61 +
  62 + /**
  63 + * 流程分类
  64 + */
  65 + private String processDefinitionCategory;
  66 +
  67 +}
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/cc/BpmProcessInstanceCopyMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.dal.mysql.cc;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCCMyPageReqVO;
  7 +import cn.iocoder.yudao.module.bpm.dal.dataobject.cc.BpmProcessInstanceCopyDO;
  8 +import org.apache.ibatis.annotations.Mapper;
  9 +
  10 +@Mapper
  11 +public interface BpmProcessInstanceCopyMapper extends BaseMapperX<BpmProcessInstanceCopyDO> {
  12 + default PageResult<BpmProcessInstanceCopyDO> selectPage(Long loginUserId, BpmProcessInstanceCCMyPageReqVO reqVO){
  13 + return selectPage(reqVO, new LambdaQueryWrapperX<BpmProcessInstanceCopyDO>()
  14 + .eqIfPresent(BpmProcessInstanceCopyDO::getUserId, loginUserId)
  15 + .eqIfPresent(BpmProcessInstanceCopyDO::getProcessInstanceId, reqVO.getProcessInstanceId())
  16 + .likeIfPresent(BpmProcessInstanceCopyDO::getProcessInstanceName, reqVO.getProcessInstanceName())
  17 + .betweenIfPresent(BpmProcessInstanceCopyDO::getCreateTime, reqVO.getCreateTime())
  18 + .orderByDesc(BpmProcessInstanceCopyDO::getId));
  19 + }
  20 +}
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/bpm/config/BpmCandidateProcessorConfiguration.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.framework.bpm.config;
  2 +
  3 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript;
  4 +import cn.iocoder.yudao.module.bpm.service.candidate.sourceInfoProcessor.*;
  5 +import org.springframework.beans.factory.ObjectProvider;
  6 +import org.springframework.context.annotation.Bean;
  7 +import org.springframework.context.annotation.Configuration;
  8 +
  9 +import java.util.List;
  10 +
  11 +/**
  12 + * BPM 通用的 Configuration 配置类,提供给 Activiti 和 Flowable
  13 + * @author kyle
  14 + */
  15 +@Configuration(proxyBeanMethods = false)
  16 +public class BpmCandidateProcessorConfiguration {
  17 + @Bean
  18 + public BpmCandidateAdminUserApiSourceInfoProcessor bpmCandidateAdminUserApiSourceInfoProcessor() {
  19 + return new BpmCandidateAdminUserApiSourceInfoProcessor();
  20 + }
  21 +
  22 + @Bean
  23 + public BpmCandidateDeptApiSourceInfoProcessor bpmCandidateDeptApiSourceInfoProcessor() {
  24 + return new BpmCandidateDeptApiSourceInfoProcessor();
  25 + }
  26 +
  27 + @Bean
  28 + public BpmCandidatePostApiSourceInfoProcessor bpmCandidatePostApiSourceInfoProcessor() {
  29 + return new BpmCandidatePostApiSourceInfoProcessor();
  30 + }
  31 +
  32 + @Bean
  33 + public BpmCandidateRoleApiSourceInfoProcessor bpmCandidateRoleApiSourceInfoProcessor() {
  34 + return new BpmCandidateRoleApiSourceInfoProcessor();
  35 + }
  36 +
  37 + @Bean
  38 + public BpmCandidateUserGroupApiSourceInfoProcessor bpmCandidateUserGroupApiSourceInfoProcessor() {
  39 + return new BpmCandidateUserGroupApiSourceInfoProcessor();
  40 + }
  41 +
  42 + /**
  43 + * 可以自己定制脚本,然后通过这里设置到处理器里面去
  44 + * @param scriptsOp 脚本包装对象
  45 + * @return
  46 + */
  47 + @Bean
  48 + public BpmCandidateScriptApiSourceInfoProcessor bpmCandidateScriptApiSourceInfoProcessor(ObjectProvider<BpmTaskAssignScript> scriptsOp) {
  49 + BpmCandidateScriptApiSourceInfoProcessor bpmCandidateScriptApiSourceInfoProcessor = new BpmCandidateScriptApiSourceInfoProcessor();
  50 + bpmCandidateScriptApiSourceInfoProcessor.setScripts(scriptsOp);
  51 + return bpmCandidateScriptApiSourceInfoProcessor;
  52 + }
  53 +}
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/BpmCandidateSourceInfo.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.service.candidate;
  2 +
  3 +import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateRuleVO;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.AllArgsConstructor;
  6 +import lombok.Data;
  7 +import lombok.NoArgsConstructor;
  8 +
  9 +import jakarta.validation.constraints.NotEmpty;
  10 +import jakarta.validation.constraints.NotNull;
  11 +import java.util.HashSet;
  12 +import java.util.Set;
  13 +
  14 +/**
  15 + * 获取候选人信息
  16 + */
  17 +@AllArgsConstructor
  18 +@NoArgsConstructor
  19 +@Data
  20 +public class BpmCandidateSourceInfo {
  21 +
  22 + @Schema(description = "流程id")
  23 + @NotNull
  24 + private String processInstanceId;
  25 +
  26 + @Schema(description = "当前任务ID")
  27 + @NotNull
  28 + private String taskId;
  29 + /**
  30 + * 通过这些规则,生成最终需要生成的用户
  31 + */
  32 + @Schema(description = "当前任务预选规则")
  33 + @NotEmpty(message = "不允许空规则")
  34 + private Set<BpmTaskCandidateRuleVO> rules;
  35 +
  36 + @Schema(description = "发起抄送的用户")
  37 + private String creator;
  38 +
  39 + @Schema(description = "抄送原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "请帮忙审查下!")
  40 + @NotEmpty(message = "抄送原因不能为空")
  41 + private String reason;
  42 +
  43 + public void addRule(BpmTaskCandidateRuleVO vo) {
  44 + assert vo != null;
  45 + if (rules == null) {
  46 + rules = new HashSet<>();
  47 + }
  48 + rules.add(vo);
  49 + }
  50 +}
0 \ No newline at end of file 51 \ No newline at end of file
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/BpmCandidateSourceInfoProcessor.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.service.candidate;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateRuleVO;
  5 +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
  6 +import org.flowable.engine.delegate.DelegateExecution;
  7 +
  8 +import java.util.Collections;
  9 +import java.util.HashSet;
  10 +import java.util.Set;
  11 +
  12 +public interface BpmCandidateSourceInfoProcessor {
  13 + /**
  14 + * 获取该处理器支持的类型
  15 + * 来自 {@link BpmTaskAssignRuleTypeEnum}
  16 + *
  17 + * @return
  18 + */
  19 + Set<Integer> getSupportedTypes();
  20 +
  21 + /**
  22 + * 对规则和人员做校验
  23 + *
  24 + * @param type 规则
  25 + * @param options 人员id
  26 + */
  27 + void validRuleOptions(Integer type, Set<Long> options);
  28 +
  29 + /**
  30 + * 默认的处理
  31 + * 如果想去操作所有的规则,则可以覆盖此方法
  32 + *
  33 + * @param request 原始请求
  34 + * @param delegateExecution 审批过程中的对象
  35 + * @return 必须包含的是用户ID,而不是其他的ID
  36 + * @throws Exception
  37 + */
  38 + default Set<Long> process(BpmCandidateSourceInfo request, DelegateExecution delegateExecution) throws Exception {
  39 + Set<BpmTaskCandidateRuleVO> rules = request.getRules();
  40 + Set<Long> results = new HashSet<>();
  41 + for (BpmTaskCandidateRuleVO rule : rules) {
  42 + // 每个处理器都有机会处理自己支持的事件
  43 + if (CollUtil.contains(getSupportedTypes(), rule.getType())) {
  44 + results.addAll(doProcess(request, rule, delegateExecution));
  45 + }
  46 + }
  47 + return results;
  48 + }
  49 +
  50 + default Set<Long> doProcess(BpmCandidateSourceInfo request, BpmTaskCandidateRuleVO rule, DelegateExecution delegateExecution) {
  51 + return Collections.emptySet();
  52 + }
  53 +}
0 \ No newline at end of file 54 \ No newline at end of file
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/BpmCandidateSourceInfoProcessorChain.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.service.candidate;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.collection.ListUtil;
  5 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  6 +import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateRuleVO;
  7 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  8 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  9 +import org.flowable.engine.delegate.DelegateExecution;
  10 +import org.springframework.beans.factory.ObjectProvider;
  11 +import org.springframework.stereotype.Service;
  12 +
  13 +import jakarta.annotation.Resource;
  14 +import java.util.*;
  15 +
  16 +@Service
  17 +public class BpmCandidateSourceInfoProcessorChain {
  18 +
  19 + // 保存处理节点
  20 +
  21 + private List<BpmCandidateSourceInfoProcessor> processorList;
  22 + @Resource
  23 + private AdminUserApi adminUserApi;
  24 +
  25 + /**
  26 + * 可添加其他处理器
  27 + *
  28 + * @param processorOp
  29 + * @return
  30 + */
  31 + @Resource
  32 + // 动态扩展处理节点
  33 + public BpmCandidateSourceInfoProcessorChain addProcessor(ObjectProvider<BpmCandidateSourceInfoProcessor> processorOp) {
  34 + List<BpmCandidateSourceInfoProcessor> processor = ListUtil.toList(processorOp.iterator());
  35 + if (null == processorList) {
  36 + processorList = new ArrayList<>(processor.size());
  37 + }
  38 + processorList.addAll(processor);
  39 + return this;
  40 + }
  41 +
  42 + // 获取处理器处理
  43 + public Set<Long> process(BpmCandidateSourceInfo sourceInfo, DelegateExecution execution) throws Exception {
  44 + // Verify our parameters
  45 + if (sourceInfo == null) {
  46 + throw new IllegalArgumentException();
  47 + }
  48 + for (BpmCandidateSourceInfoProcessor processor : processorList) {
  49 + try {
  50 + for (BpmTaskCandidateRuleVO vo : sourceInfo.getRules()) {
  51 + if (CollUtil.contains(processor.getSupportedTypes(), vo.getType())) {
  52 + processor.validRuleOptions(vo.getType(), vo.getOptions());
  53 + }
  54 + }
  55 + } catch (Exception e) {
  56 + e.printStackTrace();
  57 + throw e;
  58 + }
  59 + }
  60 + Set<Long> saveResult = Collections.emptySet();
  61 + Exception saveException = null;
  62 + for (BpmCandidateSourceInfoProcessor processor : processorList) {
  63 + try {
  64 + saveResult = processor.process(sourceInfo, execution);
  65 + if (CollUtil.isNotEmpty(saveResult)) {
  66 + removeDisableUsers(saveResult);
  67 + break;
  68 + }
  69 + } catch (Exception e) {
  70 + saveException = e;
  71 + break;
  72 + }
  73 + }
  74 + // Return the exception or result state from the last execute()
  75 + if ((saveException != null)) {
  76 + throw saveException;
  77 + } else {
  78 + return (saveResult);
  79 + }
  80 + }
  81 +
  82 + public Set<Long> calculateTaskCandidateUsers(DelegateExecution execution, BpmCandidateSourceInfo sourceInfo) {
  83 + Set<Long> results = Collections.emptySet();
  84 + try {
  85 + results = process(sourceInfo, execution);
  86 + } catch (Exception e) {
  87 + e.printStackTrace();
  88 + }
  89 + return results;
  90 + }
  91 +
  92 + /**
  93 + * 移除禁用用户
  94 + *
  95 + * @param assigneeUserIds
  96 + */
  97 + public void removeDisableUsers(Set<Long> assigneeUserIds) {
  98 + if (CollUtil.isEmpty(assigneeUserIds)) {
  99 + return;
  100 + }
  101 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(assigneeUserIds);
  102 + assigneeUserIds.removeIf(id -> {
  103 + AdminUserRespDTO user = userMap.get(id);
  104 + return user == null || !CommonStatusEnum.ENABLE.getStatus().equals(user.getStatus());
  105 + });
  106 + }
  107 +}
0 \ No newline at end of file 108 \ No newline at end of file
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidateAdminUserApiSourceInfoProcessor.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.service.candidate.sourceInfoProcessor;
  2 +
  3 +import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
  4 +import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateRuleVO;
  5 +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
  6 +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo;
  7 +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfoProcessor;
  8 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  9 +import org.flowable.engine.delegate.DelegateExecution;
  10 +
  11 +import jakarta.annotation.Resource;
  12 +import java.util.Set;
  13 +
  14 +public class BpmCandidateAdminUserApiSourceInfoProcessor implements BpmCandidateSourceInfoProcessor {
  15 + @Resource
  16 + private AdminUserApi api;
  17 +
  18 + @Override
  19 + public Set<Integer> getSupportedTypes() {
  20 + return SetUtils.asSet(BpmTaskAssignRuleTypeEnum.USER.getType());
  21 + }
  22 +
  23 + @Override
  24 + public void validRuleOptions(Integer type, Set<Long> options) {
  25 + api.validateUserList(options);
  26 + }
  27 +
  28 + @Override
  29 + public Set<Long> doProcess(BpmCandidateSourceInfo request, BpmTaskCandidateRuleVO rule, DelegateExecution delegateExecution) {
  30 + return rule.getOptions();
  31 + }
  32 +}
0 \ No newline at end of file 33 \ No newline at end of file
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidateDeptApiSourceInfoProcessor.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.service.candidate.sourceInfoProcessor;
  2 +
  3 +import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
  4 +import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateRuleVO;
  5 +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
  6 +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo;
  7 +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfoProcessor;
  8 +import cn.iocoder.yudao.module.system.api.dept.DeptApi;
  9 +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
  10 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  11 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  12 +import org.flowable.engine.delegate.DelegateExecution;
  13 +
  14 +import jakarta.annotation.Resource;
  15 +import java.util.Collections;
  16 +import java.util.List;
  17 +import java.util.Objects;
  18 +import java.util.Set;
  19 +
  20 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  21 +
  22 +public class BpmCandidateDeptApiSourceInfoProcessor implements BpmCandidateSourceInfoProcessor {
  23 + @Resource
  24 + private DeptApi api;
  25 + @Resource
  26 + private AdminUserApi adminUserApi;
  27 +
  28 + @Override
  29 + public Set<Integer> getSupportedTypes() {
  30 + return SetUtils.asSet(BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(),
  31 + BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType());
  32 + }
  33 +
  34 + @Override
  35 + public void validRuleOptions(Integer type, Set<Long> options) {
  36 + api.validateDeptList(options);
  37 + }
  38 +
  39 + @Override
  40 + public Set<Long> doProcess(BpmCandidateSourceInfo request, BpmTaskCandidateRuleVO rule, DelegateExecution delegateExecution) {
  41 + if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(), rule.getType())) {
  42 + List<AdminUserRespDTO> users = adminUserApi.getUserListByDeptIds(rule.getOptions());
  43 + return convertSet(users, AdminUserRespDTO::getId);
  44 + } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType(), rule.getType())) {
  45 + List<DeptRespDTO> depts = api.getDeptList(rule.getOptions());
  46 + return convertSet(depts, DeptRespDTO::getLeaderUserId);
  47 + }
  48 + return Collections.emptySet();
  49 + }
  50 +}
0 \ No newline at end of file 51 \ No newline at end of file
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidatePostApiSourceInfoProcessor.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.service.candidate.sourceInfoProcessor;
  2 +
  3 +import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
  4 +import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateRuleVO;
  5 +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
  6 +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo;
  7 +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfoProcessor;
  8 +import cn.iocoder.yudao.module.system.api.dept.PostApi;
  9 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  10 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  11 +import org.flowable.engine.delegate.DelegateExecution;
  12 +
  13 +import jakarta.annotation.Resource;
  14 +import java.util.List;
  15 +import java.util.Set;
  16 +
  17 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  18 +
  19 +public class BpmCandidatePostApiSourceInfoProcessor implements BpmCandidateSourceInfoProcessor {
  20 + @Resource
  21 + private PostApi api;
  22 + @Resource
  23 + private AdminUserApi adminUserApi;
  24 +
  25 + @Override
  26 + public Set<Integer> getSupportedTypes() {
  27 + return SetUtils.asSet(BpmTaskAssignRuleTypeEnum.POST.getType());
  28 + }
  29 +
  30 + @Override
  31 + public void validRuleOptions(Integer type, Set<Long> options) {
  32 + api.validPostList(options);
  33 + }
  34 +
  35 + @Override
  36 + public Set<Long> doProcess(BpmCandidateSourceInfo request, BpmTaskCandidateRuleVO rule, DelegateExecution delegateExecution) {
  37 + List<AdminUserRespDTO> users = adminUserApi.getUserListByPostIds(rule.getOptions());
  38 + return convertSet(users, AdminUserRespDTO::getId);
  39 + }
  40 +}
0 \ No newline at end of file 41 \ No newline at end of file
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidateRoleApiSourceInfoProcessor.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.service.candidate.sourceInfoProcessor;
  2 +
  3 +import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
  4 +import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateRuleVO;
  5 +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
  6 +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo;
  7 +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfoProcessor;
  8 +import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
  9 +import cn.iocoder.yudao.module.system.api.permission.RoleApi;
  10 +import org.flowable.engine.delegate.DelegateExecution;
  11 +
  12 +import jakarta.annotation.Resource;
  13 +import java.util.Set;
  14 +
  15 +public class BpmCandidateRoleApiSourceInfoProcessor implements BpmCandidateSourceInfoProcessor {
  16 + @Resource
  17 + private RoleApi api;
  18 +
  19 + @Resource
  20 + private PermissionApi permissionApi;
  21 +
  22 + @Override
  23 + public Set<Integer> getSupportedTypes() {
  24 + return SetUtils.asSet(BpmTaskAssignRuleTypeEnum.ROLE.getType());
  25 + }
  26 +
  27 + @Override
  28 + public void validRuleOptions(Integer type, Set<Long> options) {
  29 + api.validRoleList(options);
  30 + }
  31 +
  32 + @Override
  33 + public Set<Long> doProcess(BpmCandidateSourceInfo request, BpmTaskCandidateRuleVO rule, DelegateExecution delegateExecution) {
  34 + return permissionApi.getUserRoleIdListByRoleIds(rule.getOptions());
  35 + }
  36 +
  37 +}
0 \ No newline at end of file 38 \ No newline at end of file
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidateScriptApiSourceInfoProcessor.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.service.candidate.sourceInfoProcessor;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
  5 +import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
  6 +import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateRuleVO;
  7 +import cn.iocoder.yudao.module.bpm.enums.DictTypeConstants;
  8 +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
  9 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript;
  10 +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo;
  11 +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfoProcessor;
  12 +import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
  13 +import org.flowable.engine.delegate.DelegateExecution;
  14 +import org.springframework.beans.factory.ObjectProvider;
  15 +
  16 +import jakarta.annotation.Resource;
  17 +import java.util.*;
  18 +import java.util.stream.Collectors;
  19 +
  20 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  21 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
  22 +import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.TASK_ASSIGN_SCRIPT_NOT_EXISTS;
  23 +
  24 +public class BpmCandidateScriptApiSourceInfoProcessor implements BpmCandidateSourceInfoProcessor {
  25 + @Resource
  26 + private DictDataApi dictDataApi;
  27 +
  28 + /**
  29 + * 任务分配脚本
  30 + */
  31 + private Map<Long, BpmTaskAssignScript> scriptMap = Collections.emptyMap();
  32 +
  33 + public void setScripts(ObjectProvider<BpmTaskAssignScript> scriptsOp) {
  34 + List<BpmTaskAssignScript> scripts = scriptsOp.orderedStream().collect(Collectors.toList());
  35 + setScripts(scripts);
  36 + }
  37 +
  38 + public void setScripts(List<BpmTaskAssignScript> scripts) {
  39 + this.scriptMap = convertMap(scripts, script -> script.getEnum().getId());
  40 + }
  41 +
  42 + @Override
  43 + public Set<Integer> getSupportedTypes() {
  44 + return SetUtils.asSet(BpmTaskAssignRuleTypeEnum.SCRIPT.getType());
  45 + }
  46 +
  47 + @Override
  48 + public void validRuleOptions(Integer type, Set<Long> options) {
  49 + dictDataApi.validateDictDataList(DictTypeConstants.TASK_ASSIGN_SCRIPT,
  50 + CollectionUtils.convertSet(options, String::valueOf));
  51 + }
  52 +
  53 + @Override
  54 + public Set<Long> doProcess(BpmCandidateSourceInfo request, BpmTaskCandidateRuleVO rule, DelegateExecution delegateExecution) {
  55 + return calculateTaskCandidateUsersByScript(delegateExecution, rule.getOptions());
  56 + }
  57 +
  58 + private Set<Long> calculateTaskCandidateUsersByScript(DelegateExecution execution, Set<Long> options) {
  59 + // 获得对应的脚本
  60 + List<BpmTaskAssignScript> scripts = new ArrayList<>(options.size());
  61 + options.forEach(id -> {
  62 + BpmTaskAssignScript script = scriptMap.get(id);
  63 + if (script == null) {
  64 + throw exception(TASK_ASSIGN_SCRIPT_NOT_EXISTS, id);
  65 + }
  66 + scripts.add(script);
  67 + });
  68 + // 逐个计算任务
  69 + Set<Long> userIds = new HashSet<>();
  70 + scripts.forEach(script -> CollUtil.addAll(userIds, script.calculateTaskCandidateUsers(execution)));
  71 + return userIds;
  72 + }
  73 +}
0 \ No newline at end of file 74 \ No newline at end of file
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidateUserGroupApiSourceInfoProcessor.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.service.candidate.sourceInfoProcessor;
  2 +
  3 +import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
  4 +import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateRuleVO;
  5 +import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;
  6 +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
  7 +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo;
  8 +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfoProcessor;
  9 +import cn.iocoder.yudao.module.bpm.service.definition.BpmUserGroupService;
  10 +import org.flowable.engine.delegate.DelegateExecution;
  11 +
  12 +import jakarta.annotation.Resource;
  13 +import java.util.HashSet;
  14 +import java.util.List;
  15 +import java.util.Set;
  16 +
  17 +public class BpmCandidateUserGroupApiSourceInfoProcessor implements BpmCandidateSourceInfoProcessor {
  18 + @Resource
  19 + private BpmUserGroupService api;
  20 + @Resource
  21 + private BpmUserGroupService userGroupService;
  22 +
  23 + @Override
  24 + public Set<Integer> getSupportedTypes() {
  25 + return SetUtils.asSet(BpmTaskAssignRuleTypeEnum.USER_GROUP.getType());
  26 + }
  27 +
  28 + @Override
  29 + public void validRuleOptions(Integer type, Set<Long> options) {
  30 + api.validUserGroups(options);
  31 + }
  32 +
  33 + @Override
  34 + public Set<Long> doProcess(BpmCandidateSourceInfo request, BpmTaskCandidateRuleVO rule, DelegateExecution delegateExecution) {
  35 + List<BpmUserGroupDO> userGroups = userGroupService.getUserGroupList(rule.getOptions());
  36 + Set<Long> userIds = new HashSet<>();
  37 + userGroups.forEach(group -> userIds.addAll(group.getMemberUserIds()));
  38 + return userIds;
  39 + }
  40 +
  41 +}
0 \ No newline at end of file 42 \ No newline at end of file
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/cc/BpmProcessInstanceCopyService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.service.cc;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCCMyPageReqVO;
  5 +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCCPageItemRespVO;
  6 +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCCReqVO;
  7 +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo;
  8 +
  9 +/**
  10 + * 流程抄送Service接口
  11 + *
  12 + * 现在是在审批的时候进行流程抄送
  13 + */
  14 +public interface BpmProcessInstanceCopyService {
  15 +
  16 + /**
  17 + * 查询流程抄送
  18 + *
  19 + * @param copyId 流程抄送主键
  20 + * @return 流程抄送
  21 + */
  22 + BpmProcessInstanceCopyVO queryById(Long copyId);
  23 +
  24 + /**
  25 + * 抄送
  26 + * @param sourceInfo 抄送源信息,方便抄送处理
  27 + * @return
  28 + */
  29 + boolean makeCopy(BpmCandidateSourceInfo sourceInfo);
  30 +
  31 + /**
  32 + * 流程实例的抄送
  33 + * @param loginUserId 当前登录用户
  34 + * @param createReqVO 创建的抄送请求
  35 + * @return 是否抄送成功,抄送成功则返回true
  36 + */
  37 + boolean ccProcessInstance(Long loginUserId, BpmProcessInstanceCCReqVO createReqVO);
  38 +
  39 + /**
  40 + * 抄送的流程
  41 + * @param loginUserId 登录用户id
  42 + * @param pageReqVO 分页请求
  43 + * @return 抄送的分页结果
  44 + */
  45 + PageResult<BpmProcessInstanceCCPageItemRespVO> getMyProcessInstanceCCPage(Long loginUserId, BpmProcessInstanceCCMyPageReqVO pageReqVO);
  46 +}
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/cc/BpmProcessInstanceCopyServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.service.cc;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCCMyPageReqVO;
  6 +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCCPageItemRespVO;
  7 +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCCReqVO;
  8 +import cn.iocoder.yudao.module.bpm.convert.cc.BpmProcessInstanceCopyConvert;
  9 +import cn.iocoder.yudao.module.bpm.dal.dataobject.cc.BpmProcessInstanceCopyDO;
  10 +import cn.iocoder.yudao.module.bpm.dal.mysql.cc.BpmProcessInstanceCopyMapper;
  11 +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo;
  12 +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfoProcessorChain;
  13 +import cn.iocoder.yudao.module.bpm.service.cc.dto.BpmDelegateExecutionDTO;
  14 +import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;
  15 +import cn.iocoder.yudao.module.bpm.util.FlowableUtils;
  16 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  17 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.flowable.engine.RuntimeService;
  20 +import org.flowable.engine.delegate.DelegateExecution;
  21 +import org.flowable.engine.repository.ProcessDefinition;
  22 +import org.flowable.engine.runtime.ProcessInstance;
  23 +import org.springframework.context.annotation.Lazy;
  24 +import org.springframework.stereotype.Service;
  25 +import org.springframework.validation.annotation.Validated;
  26 +
  27 +import jakarta.annotation.Resource;
  28 +import java.time.LocalDateTime;
  29 +import java.util.*;
  30 +import java.util.stream.Collectors;
  31 +
  32 +@Slf4j
  33 +@Service
  34 +@Validated
  35 +public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopyService {
  36 + @Resource
  37 + private BpmProcessInstanceCopyMapper processInstanceCopyMapper;
  38 +
  39 + /**
  40 + * 和flowable有关的,查询流程名用的
  41 + */
  42 + @Resource
  43 + private RuntimeService runtimeService;
  44 +
  45 + /**
  46 + * 找抄送人用的
  47 + */
  48 + @Resource
  49 + private BpmCandidateSourceInfoProcessorChain processorChain;
  50 +
  51 + @Resource
  52 + @Lazy // 解决循环依赖
  53 + private BpmTaskService taskService;
  54 + @Resource
  55 + private AdminUserApi adminUserApi;
  56 +
  57 + @Override
  58 + public BpmProcessInstanceCopyVO queryById(Long copyId) {
  59 + BpmProcessInstanceCopyDO bpmProcessInstanceCopyDO = processInstanceCopyMapper.selectById(copyId);
  60 + return BpmProcessInstanceCopyConvert.INSTANCE.convert(bpmProcessInstanceCopyDO);
  61 + }
  62 +
  63 + @Override
  64 + public boolean makeCopy(BpmCandidateSourceInfo sourceInfo) {
  65 + if (null == sourceInfo) {
  66 + return false;
  67 + }
  68 +
  69 + DelegateExecution executionEntity = new BpmDelegateExecutionDTO(sourceInfo.getProcessInstanceId());
  70 + Set<Long> ccCandidates = processorChain.calculateTaskCandidateUsers(executionEntity, sourceInfo);
  71 + if (CollUtil.isEmpty(ccCandidates)) {
  72 + log.warn("相关抄送人不存在 {}", sourceInfo.getTaskId());
  73 + return false;
  74 + } else {
  75 + BpmProcessInstanceCopyDO copyDO = new BpmProcessInstanceCopyDO();
  76 + // 调用
  77 + // 设置任务id
  78 + copyDO.setTaskId(sourceInfo.getTaskId());
  79 + copyDO.setTaskName(FlowableUtils.getTaskNameByTaskId(sourceInfo.getTaskId()));
  80 + copyDO.setProcessInstanceId(sourceInfo.getProcessInstanceId());
  81 + ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
  82 + .processInstanceId(sourceInfo.getProcessInstanceId())
  83 + .singleResult();
  84 + if (null == processInstance) {
  85 + log.warn("相关流程实例不存在 {}", sourceInfo.getTaskId());
  86 + return false;
  87 + }
  88 + copyDO.setStartUserId(FlowableUtils.getStartUserIdFromProcessInstance(processInstance));
  89 + copyDO.setProcessInstanceName(processInstance.getName());
  90 + ProcessDefinition processDefinition = FlowableUtils.getProcessDefinition(processInstance.getProcessDefinitionId());
  91 + copyDO.setProcessDefinitionCategory(processDefinition.getCategory());
  92 + copyDO.setReason(sourceInfo.getReason());
  93 + copyDO.setCreator(sourceInfo.getCreator());
  94 + copyDO.setCreateTime(LocalDateTime.now());
  95 + List<BpmProcessInstanceCopyDO> copyList = new ArrayList<>(ccCandidates.size());
  96 + for (Long userId : ccCandidates) {
  97 + BpmProcessInstanceCopyDO copy = BpmProcessInstanceCopyConvert.INSTANCE.copy(copyDO);
  98 + copy.setUserId(userId);
  99 + copyList.add(copy);
  100 + }
  101 + return processInstanceCopyMapper.insertBatch(copyList);
  102 + }
  103 + }
  104 +
  105 + @Override
  106 + public boolean ccProcessInstance(Long loginUserId, BpmProcessInstanceCCReqVO reqVO) {
  107 + // 在能正常审批的情况下抄送流程
  108 + BpmCandidateSourceInfo sourceInfo = new BpmCandidateSourceInfo();
  109 + sourceInfo.setTaskId(reqVO.getTaskKey());
  110 + sourceInfo.setProcessInstanceId(reqVO.getProcessInstanceKey());
  111 + sourceInfo.addRule(reqVO);
  112 + sourceInfo.setCreator(String.valueOf(loginUserId));
  113 + sourceInfo.setReason(reqVO.getReason());
  114 + if (!makeCopy(sourceInfo)) {
  115 + throw new RuntimeException("抄送任务失败");
  116 + }
  117 + return false;
  118 + }
  119 +
  120 + //获取流程抄送分页
  121 + @Override
  122 + public PageResult<BpmProcessInstanceCCPageItemRespVO> getMyProcessInstanceCCPage(Long loginUserId, BpmProcessInstanceCCMyPageReqVO pageReqVO) {
  123 + // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页
  124 + PageResult<BpmProcessInstanceCopyDO> pageResult = processInstanceCopyMapper.selectPage(loginUserId, pageReqVO);
  125 + if (CollUtil.isEmpty(pageResult.getList())) {
  126 + return new PageResult<>(pageResult.getTotal());
  127 + }
  128 +
  129 + Set<String/* taskId */> taskIds = new HashSet<>();
  130 + Set<String/* processInstaneId */> processInstaneIds = new HashSet<>();
  131 + Set<Long/* userId */> userIds = new HashSet<>();
  132 + for (BpmProcessInstanceCopyDO doItem : pageResult.getList()) {
  133 + taskIds.add(doItem.getTaskId());
  134 + processInstaneIds.add(doItem.getProcessInstanceId());
  135 + userIds.add(doItem.getStartUserId());
  136 + Long userId = Long.valueOf(doItem.getCreator());
  137 + userIds.add(userId);
  138 + }
  139 +
  140 + Map<String, String> taskNameByTaskIds = FlowableUtils.getTaskNameByTaskIds(taskIds);
  141 + Map<String, String> processInstanceNameByTaskIds = FlowableUtils.getProcessInstanceNameByTaskIds(processInstaneIds);
  142 +
  143 + Map<Long, String> userMap = adminUserApi.getUserList(userIds).stream().collect(Collectors.toMap(
  144 + AdminUserRespDTO::getId, AdminUserRespDTO::getNickname));
  145 +
  146 + // 转换返回
  147 + return BpmProcessInstanceCopyConvert.INSTANCE.convertPage(pageResult, taskNameByTaskIds, processInstanceNameByTaskIds, userMap);
  148 + }
  149 +
  150 +}
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/cc/BpmProcessInstanceCopyVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.service.cc;
  2 +
  3 +import com.baomidou.mybatisplus.annotation.TableId;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +
  7 +import java.time.LocalDateTime;
  8 +import java.util.Date;
  9 +
  10 +
  11 +/**
  12 + * 流程抄送视图对象 wf_copy
  13 + *
  14 + * @author ruoyi
  15 + * @date 2022-05-19
  16 + */
  17 +@Data
  18 +public class BpmProcessInstanceCopyVO {
  19 +
  20 + /**
  21 + * 编号
  22 + */
  23 + @Schema(description = "抄送主键")
  24 + private Long id;
  25 +
  26 + /**
  27 + * 发起人Id
  28 + */
  29 + @Schema(description = "发起人Id")
  30 + private Long startUserId;
  31 +
  32 + @Schema(description = "发起人别名")
  33 + private String startUserNickname;
  34 +
  35 + /**
  36 + * 流程主键
  37 + */
  38 + @Schema(description = "流程实例的主键")
  39 + private String processInstanceId;
  40 +
  41 + @Schema(description = "流程实例的名字")
  42 + private String processInstanceName;
  43 +
  44 + /**
  45 + * 任务主键
  46 + */
  47 + @Schema(description = "发起抄送的任务编号")
  48 + private String taskId;
  49 +
  50 + @Schema(description = "发起抄送的任务名称")
  51 + private String taskName;
  52 + /**
  53 + * 用户主键
  54 + */
  55 + @Schema(description = "用户编号")
  56 + private Long userId;
  57 +
  58 + @Schema(description = "用户别名")
  59 + private Long userNickname;
  60 +
  61 + @Schema(description = "抄送原因")
  62 + private String reason;
  63 +
  64 + @Schema(description = "抄送人")
  65 + private String creator;
  66 +
  67 + @Schema(description = "抄送时间")
  68 + private LocalDateTime createTime;
  69 +}
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/cc/dto/BpmDelegateExecutionDTO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.service.cc.dto;
  2 +
  3 +import org.flowable.bpmn.model.FlowElement;
  4 +import org.flowable.bpmn.model.FlowableListener;
  5 +import org.flowable.engine.delegate.DelegateExecution;
  6 +import org.flowable.engine.delegate.ReadOnlyDelegateExecution;
  7 +import org.flowable.variable.api.persistence.entity.VariableInstance;
  8 +
  9 +import java.util.Collection;
  10 +import java.util.List;
  11 +import java.util.Map;
  12 +import java.util.Set;
  13 +
  14 +/**
  15 + * 仅为了传输processInstanceId
  16 + */
  17 +public class BpmDelegateExecutionDTO implements DelegateExecution {
  18 +
  19 + public BpmDelegateExecutionDTO(String getProcessInstanceId) {
  20 + this.getProcessInstanceId = getProcessInstanceId;
  21 + }
  22 +
  23 + private final String getProcessInstanceId;
  24 +
  25 + @Override
  26 + public String getId() {
  27 + return null;
  28 + }
  29 +
  30 + @Override
  31 + public String getProcessInstanceId() {
  32 + return null;
  33 + }
  34 +
  35 + @Override
  36 + public String getRootProcessInstanceId() {
  37 + return null;
  38 + }
  39 +
  40 + @Override
  41 + public String getEventName() {
  42 + return null;
  43 + }
  44 +
  45 + @Override
  46 + public void setEventName(String eventName) {
  47 +
  48 + }
  49 +
  50 + @Override
  51 + public String getProcessInstanceBusinessKey() {
  52 + return null;
  53 + }
  54 +
  55 + @Override
  56 + public String getProcessInstanceBusinessStatus() {
  57 + return null;
  58 + }
  59 +
  60 + @Override
  61 + public String getProcessDefinitionId() {
  62 + return null;
  63 + }
  64 +
  65 + @Override
  66 + public String getPropagatedStageInstanceId() {
  67 + return null;
  68 + }
  69 +
  70 + @Override
  71 + public String getParentId() {
  72 + return null;
  73 + }
  74 +
  75 + @Override
  76 + public String getSuperExecutionId() {
  77 + return null;
  78 + }
  79 +
  80 + @Override
  81 + public String getCurrentActivityId() {
  82 + return null;
  83 + }
  84 +
  85 + @Override
  86 + public String getTenantId() {
  87 + return null;
  88 + }
  89 +
  90 + @Override
  91 + public FlowElement getCurrentFlowElement() {
  92 + return null;
  93 + }
  94 +
  95 + @Override
  96 + public void setCurrentFlowElement(FlowElement flowElement) {
  97 +
  98 + }
  99 +
  100 + @Override
  101 + public FlowableListener getCurrentFlowableListener() {
  102 + return null;
  103 + }
  104 +
  105 + @Override
  106 + public void setCurrentFlowableListener(FlowableListener currentListener) {
  107 +
  108 + }
  109 +
  110 + @Override
  111 + public ReadOnlyDelegateExecution snapshotReadOnly() {
  112 + return null;
  113 + }
  114 +
  115 + @Override
  116 + public DelegateExecution getParent() {
  117 + return null;
  118 + }
  119 +
  120 + @Override
  121 + public List<? extends DelegateExecution> getExecutions() {
  122 + return null;
  123 + }
  124 +
  125 + @Override
  126 + public void setActive(boolean isActive) {
  127 +
  128 + }
  129 +
  130 + @Override
  131 + public boolean isActive() {
  132 + return false;
  133 + }
  134 +
  135 + @Override
  136 + public boolean isEnded() {
  137 + return false;
  138 + }
  139 +
  140 + @Override
  141 + public void setConcurrent(boolean isConcurrent) {
  142 +
  143 + }
  144 +
  145 + @Override
  146 + public boolean isConcurrent() {
  147 + return false;
  148 + }
  149 +
  150 + @Override
  151 + public boolean isProcessInstanceType() {
  152 + return false;
  153 + }
  154 +
  155 + @Override
  156 + public void inactivate() {
  157 +
  158 + }
  159 +
  160 + @Override
  161 + public boolean isScope() {
  162 + return false;
  163 + }
  164 +
  165 + @Override
  166 + public void setScope(boolean isScope) {
  167 +
  168 + }
  169 +
  170 + @Override
  171 + public boolean isMultiInstanceRoot() {
  172 + return false;
  173 + }
  174 +
  175 + @Override
  176 + public void setMultiInstanceRoot(boolean isMultiInstanceRoot) {
  177 +
  178 + }
  179 +
  180 + @Override
  181 + public Map<String, Object> getVariables() {
  182 + return null;
  183 + }
  184 +
  185 + @Override
  186 + public Map<String, VariableInstance> getVariableInstances() {
  187 + return null;
  188 + }
  189 +
  190 + @Override
  191 + public Map<String, Object> getVariables(Collection<String> collection) {
  192 + return null;
  193 + }
  194 +
  195 + @Override
  196 + public Map<String, VariableInstance> getVariableInstances(Collection<String> collection) {
  197 + return null;
  198 + }
  199 +
  200 + @Override
  201 + public Map<String, Object> getVariables(Collection<String> collection, boolean b) {
  202 + return null;
  203 + }
  204 +
  205 + @Override
  206 + public Map<String, VariableInstance> getVariableInstances(Collection<String> collection, boolean b) {
  207 + return null;
  208 + }
  209 +
  210 + @Override
  211 + public Map<String, Object> getVariablesLocal() {
  212 + return null;
  213 + }
  214 +
  215 + @Override
  216 + public Map<String, VariableInstance> getVariableInstancesLocal() {
  217 + return null;
  218 + }
  219 +
  220 + @Override
  221 + public Map<String, Object> getVariablesLocal(Collection<String> collection) {
  222 + return null;
  223 + }
  224 +
  225 + @Override
  226 + public Map<String, VariableInstance> getVariableInstancesLocal(Collection<String> collection) {
  227 + return null;
  228 + }
  229 +
  230 + @Override
  231 + public Map<String, Object> getVariablesLocal(Collection<String> collection, boolean b) {
  232 + return null;
  233 + }
  234 +
  235 + @Override
  236 + public Map<String, VariableInstance> getVariableInstancesLocal(Collection<String> collection, boolean b) {
  237 + return null;
  238 + }
  239 +
  240 + @Override
  241 + public Object getVariable(String s) {
  242 + return null;
  243 + }
  244 +
  245 + @Override
  246 + public VariableInstance getVariableInstance(String s) {
  247 + return null;
  248 + }
  249 +
  250 + @Override
  251 + public Object getVariable(String s, boolean b) {
  252 + return null;
  253 + }
  254 +
  255 + @Override
  256 + public VariableInstance getVariableInstance(String s, boolean b) {
  257 + return null;
  258 + }
  259 +
  260 + @Override
  261 + public Object getVariableLocal(String s) {
  262 + return null;
  263 + }
  264 +
  265 + @Override
  266 + public VariableInstance getVariableInstanceLocal(String s) {
  267 + return null;
  268 + }
  269 +
  270 + @Override
  271 + public Object getVariableLocal(String s, boolean b) {
  272 + return null;
  273 + }
  274 +
  275 + @Override
  276 + public VariableInstance getVariableInstanceLocal(String s, boolean b) {
  277 + return null;
  278 + }
  279 +
  280 + @Override
  281 + public <T> T getVariable(String s, Class<T> aClass) {
  282 + return null;
  283 + }
  284 +
  285 + @Override
  286 + public <T> T getVariableLocal(String s, Class<T> aClass) {
  287 + return null;
  288 + }
  289 +
  290 + @Override
  291 + public Set<String> getVariableNames() {
  292 + return null;
  293 + }
  294 +
  295 + @Override
  296 + public Set<String> getVariableNamesLocal() {
  297 + return null;
  298 + }
  299 +
  300 + @Override
  301 + public void setVariable(String s, Object o) {
  302 +
  303 + }
  304 +
  305 + @Override
  306 + public void setVariable(String s, Object o, boolean b) {
  307 +
  308 + }
  309 +
  310 + @Override
  311 + public Object setVariableLocal(String s, Object o) {
  312 + return null;
  313 + }
  314 +
  315 + @Override
  316 + public Object setVariableLocal(String s, Object o, boolean b) {
  317 + return null;
  318 + }
  319 +
  320 + @Override
  321 + public void setVariables(Map<String, ?> map) {
  322 +
  323 + }
  324 +
  325 + @Override
  326 + public void setVariablesLocal(Map<String, ?> map) {
  327 +
  328 + }
  329 +
  330 + @Override
  331 + public boolean hasVariables() {
  332 + return false;
  333 + }
  334 +
  335 + @Override
  336 + public boolean hasVariablesLocal() {
  337 + return false;
  338 + }
  339 +
  340 + @Override
  341 + public boolean hasVariable(String s) {
  342 + return false;
  343 + }
  344 +
  345 + @Override
  346 + public boolean hasVariableLocal(String s) {
  347 + return false;
  348 + }
  349 +
  350 + @Override
  351 + public void removeVariable(String s) {
  352 +
  353 + }
  354 +
  355 + @Override
  356 + public void removeVariableLocal(String s) {
  357 +
  358 + }
  359 +
  360 + @Override
  361 + public void removeVariables(Collection<String> collection) {
  362 +
  363 + }
  364 +
  365 + @Override
  366 + public void removeVariablesLocal(Collection<String> collection) {
  367 +
  368 + }
  369 +
  370 + @Override
  371 + public void removeVariables() {
  372 +
  373 + }
  374 +
  375 + @Override
  376 + public void removeVariablesLocal() {
  377 +
  378 + }
  379 +
  380 + @Override
  381 + public void setTransientVariable(String s, Object o) {
  382 +
  383 + }
  384 +
  385 + @Override
  386 + public void setTransientVariableLocal(String s, Object o) {
  387 +
  388 + }
  389 +
  390 + @Override
  391 + public void setTransientVariables(Map<String, Object> map) {
  392 +
  393 + }
  394 +
  395 + @Override
  396 + public Object getTransientVariable(String s) {
  397 + return null;
  398 + }
  399 +
  400 + @Override
  401 + public Map<String, Object> getTransientVariables() {
  402 + return null;
  403 + }
  404 +
  405 + @Override
  406 + public void setTransientVariablesLocal(Map<String, Object> map) {
  407 +
  408 + }
  409 +
  410 + @Override
  411 + public Object getTransientVariableLocal(String s) {
  412 + return null;
  413 + }
  414 +
  415 + @Override
  416 + public Map<String, Object> getTransientVariablesLocal() {
  417 + return null;
  418 + }
  419 +
  420 + @Override
  421 + public void removeTransientVariableLocal(String s) {
  422 +
  423 + }
  424 +
  425 + @Override
  426 + public void removeTransientVariable(String s) {
  427 +
  428 + }
  429 +
  430 + @Override
  431 + public void removeTransientVariables() {
  432 +
  433 + }
  434 +
  435 + @Override
  436 + public void removeTransientVariablesLocal() {
  437 +
  438 + }
  439 +}
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
@@ -19,6 +19,8 @@ import cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum; @@ -19,6 +19,8 @@ import cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum;
19 import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum; 19 import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum;
20 import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum; 20 import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum;
21 import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskAddSignTypeEnum; 21 import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskAddSignTypeEnum;
  22 +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo;
  23 +import cn.iocoder.yudao.module.bpm.service.cc.BpmProcessInstanceCopyService;
22 import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService; 24 import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService;
23 import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; 25 import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService;
24 import cn.iocoder.yudao.module.system.api.dept.DeptApi; 26 import cn.iocoder.yudao.module.system.api.dept.DeptApi;
@@ -94,6 +96,9 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -94,6 +96,9 @@ public class BpmTaskServiceImpl implements BpmTaskService {
94 @Resource 96 @Resource
95 private ManagementService managementService; 97 private ManagementService managementService;
96 98
  99 + @Resource
  100 + private BpmProcessInstanceCopyService processInstanceCopyService;
  101 +
97 @Override 102 @Override
98 public PageResult<BpmTaskTodoPageItemRespVO> getTodoTaskPage(Long userId, BpmTaskTodoPageReqVO pageVO) { 103 public PageResult<BpmTaskTodoPageItemRespVO> getTodoTaskPage(Long userId, BpmTaskTodoPageReqVO pageVO) {
99 // 查询待办任务 104 // 查询待办任务
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/util/FlowableUtils.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.util;
  2 +
  3 +
  4 +import cn.hutool.core.collection.CollUtil;
  5 +import cn.hutool.extra.spring.SpringUtil;
  6 +import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
  7 +import org.flowable.bpmn.model.BpmnModel;
  8 +import org.flowable.bpmn.model.ExtensionElement;
  9 +import org.flowable.bpmn.model.FlowElement;
  10 +import org.flowable.bpmn.model.FlowNode;
  11 +import org.flowable.engine.RepositoryService;
  12 +import org.flowable.engine.RuntimeService;
  13 +import org.flowable.engine.TaskService;
  14 +import org.flowable.engine.repository.ProcessDefinition;
  15 +import org.flowable.engine.runtime.ProcessInstance;
  16 +import org.flowable.task.api.Task;
  17 +
  18 +import java.util.*;
  19 +
  20 +/**
  21 + * 流程引擎工具类封装
  22 + *
  23 + * @author: linjinp
  24 + * @create: 2019-12-24 13:51
  25 + **/
  26 +public class FlowableUtils {
  27 +
  28 + /**
  29 + * 获取流程名称
  30 + *
  31 + * @param processDefinitionId
  32 + * @return
  33 + */
  34 + public static String getProcessDefinitionName(String processDefinitionId) {
  35 + RepositoryService repositoryService = SpringUtil.getBean(RepositoryService.class);
  36 + ProcessDefinition processDefinition = repositoryService.getProcessDefinition(processDefinitionId);
  37 + return processDefinition.getName();
  38 + }
  39 +
  40 + public static ProcessDefinition getProcessDefinition(String processDefinitionId) {
  41 + RepositoryService repositoryService = SpringUtil.getBean(RepositoryService.class);
  42 + return repositoryService.getProcessDefinition(processDefinitionId);
  43 + }
  44 +
  45 + /**
  46 + * 获取节点数据
  47 + *
  48 + * @param processInstanceId
  49 + * @param nodeId
  50 + * @return
  51 + */
  52 + public static FlowNode getFlowNode(String processInstanceId, String nodeId) {
  53 +
  54 + RuntimeService runtimeService = SpringUtil.getBean(RuntimeService.class);
  55 + RepositoryService repositoryService = SpringUtil.getBean(RepositoryService.class);
  56 +
  57 + String definitionld = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult().getProcessDefinitionId(); // 获取bpm(模型)对象
  58 + BpmnModel bpmnModel = repositoryService.getBpmnModel(definitionld);
  59 + // 传节点定义key获取当前节点
  60 + FlowNode flowNode = (FlowNode) bpmnModel.getFlowElement(nodeId);
  61 + return flowNode;
  62 + }
  63 +
  64 + public static ExtensionElement generateFlowNodeIdExtension(String nodeId) {
  65 + ExtensionElement extensionElement = new ExtensionElement();
  66 + extensionElement.setElementText(nodeId);
  67 + extensionElement.setName("nodeId");
  68 + extensionElement.setNamespacePrefix("flowable");
  69 + extensionElement.setNamespace("nodeId");
  70 + return extensionElement;
  71 + }
  72 +
  73 + public static String getNodeIdFromExtension(FlowElement flowElement) {
  74 + Map<String, List<ExtensionElement>> extensionElements = flowElement.getExtensionElements();
  75 + return extensionElements.get("nodeId").get(0).getElementText();
  76 + }
  77 +
  78 + public static Long getStartUserIdFromProcessInstance(ProcessInstance instance) {
  79 + if (null == instance) {
  80 + return null;
  81 + }
  82 + return NumberUtils.parseLong(instance.getStartUserId());
  83 + }
  84 +
  85 + public static String getTaskNameByTaskId(String taskId) {
  86 + TaskService taskService = SpringUtil.getBean(TaskService.class);
  87 + Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
  88 + return task.getName();
  89 + }
  90 +
  91 + public static Map<String/* taskId */, String/* taskName */> getTaskNameByTaskIds(Collection<String> taskIds) {
  92 + TaskService taskService = SpringUtil.getBean(TaskService.class);
  93 + List<Task> tasks = taskService.createTaskQuery().taskIds(taskIds).list();
  94 + if (CollUtil.isNotEmpty(tasks)) {
  95 + Map<String/* taskId */, String/* taskName */> taskMap = new HashMap<>(tasks.size());
  96 + for (Task task : tasks) {
  97 + taskMap.putIfAbsent(task.getId(), task.getName());
  98 + }
  99 + return taskMap;
  100 + }
  101 + return Collections.emptyMap();
  102 + }
  103 +
  104 + public static String getProcessInstanceNameByTaskId(String processInstanceId) {
  105 + RuntimeService runtimeService = SpringUtil.getBean(RuntimeService.class);
  106 + ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
  107 + .processInstanceId(processInstanceId)
  108 + .singleResult();
  109 + return processInstance.getName();
  110 + }
  111 +
  112 + public static Map<String/* processInstaneId */, String/* processInstaneName */> getProcessInstanceNameByTaskIds(Set<String> taskIds) {
  113 + RuntimeService runtimeService = SpringUtil.getBean(RuntimeService.class);
  114 + List<ProcessInstance> processInstances = runtimeService.createProcessInstanceQuery().processInstanceIds(taskIds).list();
  115 + if (CollUtil.isNotEmpty(processInstances)) {
  116 + Map<String/* processInstaneId */, String/* processInstaneName */> processInstaneMap = new HashMap<>(processInstances.size());
  117 + for (ProcessInstance processInstance : processInstances) {
  118 + processInstaneMap.putIfAbsent(processInstance.getId(), processInstance.getName());
  119 + }
  120 + return processInstaneMap;
  121 + }
  122 + return Collections.emptyMap();
  123 + }
  124 +
  125 +}
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/candidate/BpmCandidateSourceInfoProcessorChainTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.service.candidate;
  2 +
  3 +import cn.hutool.core.map.MapUtil;
  4 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  5 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
  6 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  7 +import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateRuleVO;
  8 +import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;
  9 +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
  10 +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskRuleScriptEnum;
  11 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript;
  12 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.impl.BpmTaskAssignLeaderX1Script;
  13 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.impl.BpmTaskAssignLeaderX2Script;
  14 +import cn.iocoder.yudao.module.bpm.service.candidate.sourceInfoProcessor.BpmCandidateScriptApiSourceInfoProcessor;
  15 +import cn.iocoder.yudao.module.bpm.service.definition.BpmUserGroupService;
  16 +import cn.iocoder.yudao.module.system.api.dept.DeptApi;
  17 +import cn.iocoder.yudao.module.system.api.dept.PostApi;
  18 +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
  19 +import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
  20 +import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
  21 +import cn.iocoder.yudao.module.system.api.permission.RoleApi;
  22 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  23 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  24 +import jakarta.annotation.Resource;
  25 +import org.flowable.engine.delegate.DelegateExecution;
  26 +import org.junit.jupiter.api.Test;
  27 +import org.springframework.boot.test.mock.mockito.MockBean;
  28 +import org.springframework.context.annotation.Import;
  29 +
  30 +import java.util.Arrays;
  31 +import java.util.List;
  32 +import java.util.Map;
  33 +import java.util.Set;
  34 +
  35 +import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
  36 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
  37 +import static java.util.Collections.singleton;
  38 +import static org.junit.jupiter.api.Assertions.assertEquals;
  39 +import static org.mockito.ArgumentMatchers.eq;
  40 +import static org.mockito.Mockito.when;
  41 +
  42 +@Import({BpmCandidateSourceInfoProcessorChain.class,
  43 + BpmCandidateScriptApiSourceInfoProcessor.class, BpmTaskAssignLeaderX1Script.class,
  44 + BpmTaskAssignLeaderX2Script.class})
  45 +public class BpmCandidateSourceInfoProcessorChainTest extends BaseDbUnitTest {
  46 + @Resource
  47 + private BpmCandidateSourceInfoProcessorChain processorChain;
  48 +
  49 + @MockBean
  50 + private BpmUserGroupService userGroupService;
  51 + @MockBean
  52 + private DeptApi deptApi;
  53 + @MockBean
  54 + private AdminUserApi adminUserApi;
  55 + @MockBean
  56 + private PermissionApi permissionApi;
  57 + @MockBean
  58 + private RoleApi roleApi;
  59 + @MockBean
  60 + private PostApi postApi;
  61 + @MockBean
  62 + private DictDataApi dictDataApi;
  63 + @Resource
  64 + private BpmCandidateScriptApiSourceInfoProcessor bpmCandidateScriptApiSourceInfoProcessor;
  65 +
  66 + @Test
  67 + public void testCalculateTaskCandidateUsers_Role() {
  68 + // 准备参数
  69 + BpmTaskCandidateRuleVO rule = new BpmTaskCandidateRuleVO().setOptions(asSet(1L, 2L))
  70 + .setType(BpmTaskAssignRuleTypeEnum.ROLE.getType());
  71 + // mock 方法
  72 + when(permissionApi.getUserRoleIdListByRoleIds(eq(rule.getOptions())))
  73 + .thenReturn(asSet(11L, 22L));
  74 + mockGetUserMap(asSet(11L, 22L));
  75 +
  76 + // 调用
  77 + BpmCandidateSourceInfo sourceInfo = new BpmCandidateSourceInfo();
  78 + sourceInfo.addRule(rule);
  79 + Set<Long> results = processorChain.calculateTaskCandidateUsers(null, sourceInfo);
  80 + // 断言
  81 + assertEquals(asSet(11L, 22L), results);
  82 + }
  83 +
  84 + @Test
  85 + public void testCalculateTaskCandidateUsers_DeptMember() {
  86 + // 准备参数
  87 + BpmTaskCandidateRuleVO rule = new BpmTaskCandidateRuleVO().setOptions(asSet(1L, 2L))
  88 + .setType(BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType());
  89 + // mock 方法
  90 + List<AdminUserRespDTO> users = CollectionUtils.convertList(asSet(11L, 22L),
  91 + id -> new AdminUserRespDTO().setId(id));
  92 + when(adminUserApi.getUserListByDeptIds(eq(rule.getOptions()))).thenReturn(users);
  93 + mockGetUserMap(asSet(11L, 22L));
  94 +
  95 + // 调用
  96 + BpmCandidateSourceInfo sourceInfo = new BpmCandidateSourceInfo();
  97 + sourceInfo.addRule(rule);
  98 + Set<Long> results = processorChain.calculateTaskCandidateUsers(null, sourceInfo);
  99 + // 断言
  100 + assertEquals(asSet(11L, 22L), results);
  101 + }
  102 +
  103 + @Test
  104 + public void testCalculateTaskCandidateUsers_DeptLeader() {
  105 + // 准备参数
  106 + BpmTaskCandidateRuleVO rule = new BpmTaskCandidateRuleVO().setOptions(asSet(1L, 2L))
  107 + .setType(BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType());
  108 + // mock 方法
  109 + DeptRespDTO dept1 = randomPojo(DeptRespDTO.class, o -> o.setLeaderUserId(11L));
  110 + DeptRespDTO dept2 = randomPojo(DeptRespDTO.class, o -> o.setLeaderUserId(22L));
  111 + when(deptApi.getDeptList(eq(rule.getOptions()))).thenReturn(Arrays.asList(dept1, dept2));
  112 + mockGetUserMap(asSet(11L, 22L));
  113 +
  114 + // 调用
  115 + BpmCandidateSourceInfo sourceInfo = new BpmCandidateSourceInfo();
  116 + sourceInfo.addRule(rule);
  117 + Set<Long> results = processorChain.calculateTaskCandidateUsers(null, sourceInfo);
  118 + // 断言
  119 + assertEquals(asSet(11L, 22L), results);
  120 + }
  121 +
  122 + @Test
  123 + public void testCalculateTaskCandidateUsers_Post() {
  124 + // 准备参数
  125 + BpmTaskCandidateRuleVO rule = new BpmTaskCandidateRuleVO().setOptions(asSet(1L, 2L))
  126 + .setType(BpmTaskAssignRuleTypeEnum.POST.getType());
  127 + // mock 方法
  128 + List<AdminUserRespDTO> users = CollectionUtils.convertList(asSet(11L, 22L),
  129 + id -> new AdminUserRespDTO().setId(id));
  130 + when(adminUserApi.getUserListByPostIds(eq(rule.getOptions()))).thenReturn(users);
  131 + mockGetUserMap(asSet(11L, 22L));
  132 +
  133 + // 调用
  134 + BpmCandidateSourceInfo sourceInfo = new BpmCandidateSourceInfo();
  135 + sourceInfo.addRule(rule);
  136 + Set<Long> results = processorChain.calculateTaskCandidateUsers(null, sourceInfo);
  137 + // 断言
  138 + assertEquals(asSet(11L, 22L), results);
  139 + }
  140 +
  141 + @Test
  142 + public void testCalculateTaskCandidateUsers_User() {
  143 + // 准备参数
  144 + BpmTaskCandidateRuleVO rule = new BpmTaskCandidateRuleVO().setOptions(asSet(1L, 2L))
  145 + .setType(BpmTaskAssignRuleTypeEnum.USER.getType());
  146 + // mock 方法
  147 + mockGetUserMap(asSet(1L, 2L));
  148 +
  149 + // 调用
  150 + BpmCandidateSourceInfo sourceInfo = new BpmCandidateSourceInfo();
  151 + sourceInfo.addRule(rule);
  152 + Set<Long> results = processorChain.calculateTaskCandidateUsers(null, sourceInfo);
  153 + // 断言
  154 + assertEquals(asSet(1L, 2L), results);
  155 + }
  156 +
  157 + @Test
  158 + public void testCalculateTaskCandidateUsers_UserGroup() {
  159 + // 准备参数
  160 + BpmTaskCandidateRuleVO rule = new BpmTaskCandidateRuleVO().setOptions(asSet(1L, 2L))
  161 + .setType(BpmTaskAssignRuleTypeEnum.USER_GROUP.getType());
  162 + // mock 方法
  163 + BpmUserGroupDO userGroup1 = randomPojo(BpmUserGroupDO.class, o -> o.setMemberUserIds(asSet(11L, 12L)));
  164 + BpmUserGroupDO userGroup2 = randomPojo(BpmUserGroupDO.class, o -> o.setMemberUserIds(asSet(21L, 22L)));
  165 + when(userGroupService.getUserGroupList(eq(rule.getOptions()))).thenReturn(Arrays.asList(userGroup1, userGroup2));
  166 + mockGetUserMap(asSet(11L, 12L, 21L, 22L));
  167 +
  168 + // 调用
  169 + BpmCandidateSourceInfo sourceInfo = new BpmCandidateSourceInfo();
  170 + sourceInfo.addRule(rule);
  171 + Set<Long> results = processorChain.calculateTaskCandidateUsers(null, sourceInfo);
  172 + // 断言
  173 + assertEquals(asSet(11L, 12L, 21L, 22L), results);
  174 + }
  175 +
  176 + private void mockGetUserMap(Set<Long> assigneeUserIds) {
  177 + Map<Long, AdminUserRespDTO> userMap = CollectionUtils.convertMap(assigneeUserIds, id -> id,
  178 + id -> new AdminUserRespDTO().setId(id).setStatus(CommonStatusEnum.ENABLE.getStatus()));
  179 + when(adminUserApi.getUserMap(eq(assigneeUserIds))).thenReturn(userMap);
  180 + }
  181 +
  182 + @Test
  183 + public void testCalculateTaskCandidateUsers_Script() {
  184 + // 准备参数
  185 + BpmTaskCandidateRuleVO rule = new BpmTaskCandidateRuleVO().setOptions(asSet(20L, 21L))
  186 + .setType(BpmTaskAssignRuleTypeEnum.SCRIPT.getType());
  187 + // mock 方法
  188 + BpmTaskAssignScript script1 = new BpmTaskAssignScript() {
  189 +
  190 + @Override
  191 + public Set<Long> calculateTaskCandidateUsers(DelegateExecution task) {
  192 + return singleton(11L);
  193 + }
  194 +
  195 + @Override
  196 + public BpmTaskRuleScriptEnum getEnum() {
  197 + return BpmTaskRuleScriptEnum.LEADER_X1;
  198 + }
  199 + };
  200 + BpmTaskAssignScript script2 = new BpmTaskAssignScript() {
  201 +
  202 + @Override
  203 + public Set<Long> calculateTaskCandidateUsers(DelegateExecution task) {
  204 + return singleton(22L);
  205 + }
  206 +
  207 + @Override
  208 + public BpmTaskRuleScriptEnum getEnum() {
  209 + return BpmTaskRuleScriptEnum.LEADER_X2;
  210 + }
  211 + };
  212 + bpmCandidateScriptApiSourceInfoProcessor.setScripts(Arrays.asList(script1, script2));
  213 + mockGetUserMap(asSet(11L, 22L));
  214 +
  215 + // 调用
  216 + BpmCandidateSourceInfo sourceInfo = new BpmCandidateSourceInfo();
  217 + sourceInfo.addRule(rule);
  218 + Set<Long> results = processorChain.calculateTaskCandidateUsers(null, sourceInfo);
  219 + // 断言
  220 + assertEquals(asSet(11L, 22L), results);
  221 + }
  222 +
  223 + @Test
  224 + public void testRemoveDisableUsers() {
  225 + // 准备参数. 1L 可以找到;2L 是禁用的;3L 找不到
  226 + Set<Long> assigneeUserIds = asSet(1L, 2L, 3L);
  227 + // mock 方法
  228 + AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L)
  229 + .setStatus(CommonStatusEnum.ENABLE.getStatus()));
  230 + AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L)
  231 + .setStatus(CommonStatusEnum.DISABLE.getStatus()));
  232 + Map<Long, AdminUserRespDTO> userMap = MapUtil.builder(user1.getId(), user1)
  233 + .put(user2.getId(), user2).build();
  234 + when(adminUserApi.getUserMap(eq(assigneeUserIds))).thenReturn(userMap);
  235 +
  236 + // 调用
  237 + processorChain.removeDisableUsers(assigneeUserIds);
  238 + // 断言
  239 + assertEquals(asSet(1L), assigneeUserIds);
  240 + }
  241 +
  242 +}
0 \ No newline at end of file 243 \ No newline at end of file
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/cc/BpmProcessInstanceCopyServiceTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.service.cc;
  2 +
  3 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  4 +import jakarta.annotation.Resource;
  5 +import org.junit.jupiter.api.Test;
  6 +import org.springframework.context.annotation.Import;
  7 +
  8 +@Import({BpmProcessInstanceCopyServiceImpl.class})
  9 +class BpmProcessInstanceCopyServiceTest extends BaseDbUnitTest {
  10 + @Resource
  11 + private BpmProcessInstanceCopyServiceImpl service;
  12 +
  13 + @Test
  14 + void queryById() {
  15 + }
  16 +}
0 \ No newline at end of file 17 \ No newline at end of file
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserBindReqVO.java
@@ -31,4 +31,7 @@ public class SocialUserBindReqVO { @@ -31,4 +31,7 @@ public class SocialUserBindReqVO {
31 @NotEmpty(message = "state 不能为空") 31 @NotEmpty(message = "state 不能为空")
32 private String state; 32 private String state;
33 33
  34 + public Integer getType() {
  35 + return type;
  36 + }
34 } 37 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/ip/AreaConvert.java
@@ -17,4 +17,12 @@ public interface AreaConvert { @@ -17,4 +17,12 @@ public interface AreaConvert {
17 17
18 List<AppAreaNodeRespVO> convertList3(List<Area> list); 18 List<AppAreaNodeRespVO> convertList3(List<Area> list);
19 19
  20 + /**
  21 + * 缺少单个转换
  22 + * @param value
  23 + * @return
  24 + */
  25 + AreaNodeRespVO map(Area value);
  26 +
  27 + AppAreaNodeRespVO map3(Area value);
20 } 28 }