diff --git a/sql/mysql/ruoyi-vue-pro.sql b/sql/mysql/ruoyi-vue-pro.sql index 3a0a91228..f96d5e10b 100644 --- a/sql/mysql/ruoyi-vue-pro.sql +++ b/sql/mysql/ruoyi-vue-pro.sql @@ -2479,6 +2479,15 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i 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'); 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'); 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'); +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 (2526, '抄送流程', '', 2, 21, 1200, 'processInstanceCC', 'eye', '/bpm/task/cc/index', + 'BpmCCProcessInstance', 0, true, true, true, 1, '2023-01-13 16:14:00', '1', '2023-01-13 16:14:00', false), + (2527, '抄送流程查询', 'bpm:process-instance-cc:query', 3, 1, 2526, '', '', '', NULL, 0, b'1', b'1', b'1', '1', + '2023-01-13 16:14:00', '1', '2023-01-13 16:14:00', b'0'), + (2528, '抄送流程创建', 'bpm:process-instance-cc:create', 3, 2, 2526, '', '', '', NULL, 0, b'1', b'1', b'1', '1', + '2023-01-13 16:14:00', '1', '2023-01-13 16:14:00', b'0'); COMMIT; -- ---------------------------- diff --git a/yudao-framework/yudao-spring-boot-starter-flowable/src/main/java/cn/iocoder/yudao/framework/flowable/config/YudaoFlowableConfiguration.java b/yudao-framework/yudao-spring-boot-starter-flowable/src/main/java/cn/iocoder/yudao/framework/flowable/config/YudaoFlowableConfiguration.java index 6d657a1b4..859eca510 100644 --- a/yudao-framework/yudao-spring-boot-starter-flowable/src/main/java/cn/iocoder/yudao/framework/flowable/config/YudaoFlowableConfiguration.java +++ b/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 { * * 如果不创建,会导致项目启动时,Flowable 报错的问题 */ - @Bean - @ConditionalOnMissingBean + @Bean(name = "applicationTaskExecutor") + @ConditionalOnMissingBean(name = "applicationTaskExecutor") public AsyncListenableTaskExecutor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(8); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/candidate/vo/BpmTaskCandidateRuleVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/candidate/vo/BpmTaskCandidateRuleVO.java new file mode 100644 index 000000000..25426f604 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/candidate/vo/BpmTaskCandidateRuleVO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo; + +import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleBaseVO; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.Set; + +/** + * 流程任务分配规则 Base VO,提供给添加、修改、详细的子 VO 使用 + * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 + * + * @see BpmTaskAssignRuleBaseVO + */ +@Data +public class BpmTaskCandidateRuleVO { + + @Schema(description = "规则类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "bpm_task_assign_rule_type") + @NotNull(message = "规则类型不能为空") + private Integer type; + + @Schema(description = "规则值数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3") + @NotNull(message = "规则值数组不能为空") + private Set options; + +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java index af8e8edcd..aab10348b 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java +++ b/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; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*; +import cn.iocoder.yudao.module.bpm.service.cc.BpmProcessInstanceCopyService; import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.Parameter; @@ -26,6 +28,9 @@ public class BpmProcessInstanceController { @Resource private BpmProcessInstanceService processInstanceService; + @Resource + private BpmProcessInstanceCopyService processInstanceCopyService; + @GetMapping("/my-page") @Operation(summary = "获得我的实例分页列表", description = "在【我的流程】菜单中,进行调用") @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") @@ -56,4 +61,20 @@ public class BpmProcessInstanceController { processInstanceService.cancelProcessInstance(getLoginUserId(), cancelReqVO); return success(true); } + + + @PostMapping("/cc/create") + @Operation(summary = "抄送流程") + @PreAuthorize("@ss.hasPermission('bpm:process-instance-cc:create')") + public CommonResult createProcessInstanceCC(@Valid @RequestBody BpmProcessInstanceCCReqVO createReqVO) { + return success(processInstanceCopyService.ccProcessInstance(SecurityFrameworkUtils.getLoginUserId(), createReqVO)); + } + + @GetMapping("/cc/my-page") + @Operation(summary = "获得抄送流程分页列表") + @PreAuthorize("@ss.hasPermission('bpm:process-instance-cc:query')") + public CommonResult> getProcessInstanceCCPage(@Valid BpmProcessInstanceCCMyPageReqVO pageReqVO) { + return success(processInstanceCopyService.getMyProcessInstanceCCPage(SecurityFrameworkUtils.getLoginUserId(), pageReqVO)); + } + } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCCMyPageReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCCMyPageReqVO.java new file mode 100644 index 000000000..44de17d38 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCCMyPageReqVO.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 流程实例抄送的分页 Item Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BpmProcessInstanceCCMyPageReqVO extends PageParam { + + @Schema(description = "流程名称", example = "芋道") + private String processInstanceName; + + @Schema(description = "流程编号", example = "123456768") + private String processInstanceId; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCCPageItemRespVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCCPageItemRespVO.java new file mode 100644 index 000000000..2315730ff --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCCPageItemRespVO.java @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 流程实例抄送的分页 Item Response VO") +@Data +public class BpmProcessInstanceCCPageItemRespVO { + + /** + * 编号 + */ + @Schema(description = "抄送主键") + private Long id; + + /** + * 发起人Id + */ + @Schema(description = "发起人Id") + private Long startUserId; + + @Schema(description = "发起人别名") + private String startUserNickname; + + /** + * 流程主键 + */ + @Schema(description = "流程实例的主键") + private String processInstanceId; + + @Schema(description = "流程实例的名称") + private String processInstanceName; + /** + * 任务主键 + */ + @Schema(description = "发起抄送的任务编号") + private String taskId; + + @Schema(description = "发起抄送的任务名称") + private String taskName; + + @Schema(description = "抄送原因") + private String reason; + + @Schema(description = "抄送人") + private String creator; + + @Schema(description = "抄送人别名") + private String creatorNickname; + + @Schema(description = "抄送时间") + private LocalDateTime createTime; +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCCReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCCReqVO.java new file mode 100644 index 000000000..c1a73fb24 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCCReqVO.java @@ -0,0 +1,44 @@ +package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance; + +import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateRuleVO; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.util.Set; + +@Schema(description = "管理后台 - 流程实例的抄送 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BpmProcessInstanceCCReqVO extends BpmTaskCandidateRuleVO { + + @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "任务编号不能为空") + private String taskKey; + + @Schema(description = "任务名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "任务名称不能为空") + private String taskName; + + @Schema(description = "流程实例的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "流程实例的编号不能为空") + private String processInstanceKey; + + @Schema(description = "发起流程的用户的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "发起流程的用户的编号不能为空") + private Long startUserId; + + + @Schema(description = "任务实例名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "任务实例名称不能为空") + private String processInstanceName; + + @Schema(description = "抄送原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "请帮忙审查下!") + @NotBlank(message = "抄送原因不能为空") + private String reason; +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java index 14dca13ea..1a79441ce 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task; +import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateRuleVO; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/cc/BpmProcessInstanceCopyConvert.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/cc/BpmProcessInstanceCopyConvert.java new file mode 100644 index 000000000..49327a134 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/cc/BpmProcessInstanceCopyConvert.java @@ -0,0 +1,51 @@ +package cn.iocoder.yudao.module.bpm.convert.cc; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCCPageItemRespVO; +import cn.iocoder.yudao.module.bpm.dal.dataobject.cc.BpmProcessInstanceCopyDO; +import cn.iocoder.yudao.module.bpm.service.cc.BpmProcessInstanceCopyVO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; +import java.util.Map; + +/** + * 动态表单 Convert + * + * @author 芋艿 + */ +@Mapper +public interface BpmProcessInstanceCopyConvert { + + BpmProcessInstanceCopyConvert INSTANCE = Mappers.getMapper(BpmProcessInstanceCopyConvert.class); + + BpmProcessInstanceCopyDO copy(BpmProcessInstanceCopyDO bean); + + BpmProcessInstanceCopyVO convert(BpmProcessInstanceCopyDO bean); + + List convertList2(List list); + + List convertList(List list); + + default PageResult convertPage(PageResult page + , Map taskMap + , Map processInstaneMap + , Map userMap + ) { + List list = convertList(page.getList()); + for (BpmProcessInstanceCCPageItemRespVO vo : list) { + MapUtils.findAndThen(userMap, Long.valueOf(vo.getCreator()), + vo::setCreatorNickname); + MapUtils.findAndThen(userMap, vo.getStartUserId(), + vo::setStartUserNickname); + MapUtils.findAndThen(taskMap, vo.getTaskId(), + vo::setTaskName); + MapUtils.findAndThen(processInstaneMap, vo.getProcessInstanceId(), + vo::setProcessInstanceName); + } + return new PageResult<>(list, page.getTotal()); + } + +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/cc/BpmProcessInstanceCopyDO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/cc/BpmProcessInstanceCopyDO.java new file mode 100644 index 000000000..1735f021e --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/cc/BpmProcessInstanceCopyDO.java @@ -0,0 +1,67 @@ +package cn.iocoder.yudao.module.bpm.dal.dataobject.cc; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * 流程抄送对象 + * + * @author kyle + * @date 2022-05-19 + */ +@TableName(value = "bpm_process_instance_copy", autoResultMap = true) +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BpmProcessInstanceCopyDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + + /** + * 发起人Id + */ + private Long startUserId; + /** + * 流程名 + */ + private String processInstanceName; + /** + * 流程主键 + */ + private String processInstanceId; + + /** + * 任务主键 + */ + private String taskId; + + /** + * 任务名称 + */ + private String taskName; + + /** + * 用户主键 + */ + private Long userId; + + /** + * 抄送原因 + */ + private String reason; + + /** + * 流程分类 + */ + private String processDefinitionCategory; + +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/cc/BpmProcessInstanceCopyMapper.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/cc/BpmProcessInstanceCopyMapper.java new file mode 100644 index 000000000..af697f4e7 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/cc/BpmProcessInstanceCopyMapper.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.bpm.dal.mysql.cc; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCCMyPageReqVO; +import cn.iocoder.yudao.module.bpm.dal.dataobject.cc.BpmProcessInstanceCopyDO; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface BpmProcessInstanceCopyMapper extends BaseMapperX { + default PageResult selectPage(Long loginUserId, BpmProcessInstanceCCMyPageReqVO reqVO){ + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(BpmProcessInstanceCopyDO::getUserId, loginUserId) + .eqIfPresent(BpmProcessInstanceCopyDO::getProcessInstanceId, reqVO.getProcessInstanceId()) + .likeIfPresent(BpmProcessInstanceCopyDO::getProcessInstanceName, reqVO.getProcessInstanceName()) + .betweenIfPresent(BpmProcessInstanceCopyDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(BpmProcessInstanceCopyDO::getId)); + } +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/bpm/config/BpmCandidateProcessorConfiguration.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/bpm/config/BpmCandidateProcessorConfiguration.java new file mode 100644 index 000000000..293a935d6 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/bpm/config/BpmCandidateProcessorConfiguration.java @@ -0,0 +1,53 @@ +package cn.iocoder.yudao.module.bpm.framework.bpm.config; + +import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript; +import cn.iocoder.yudao.module.bpm.service.candidate.sourceInfoProcessor.*; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.List; + +/** + * BPM 通用的 Configuration 配置类,提供给 Activiti 和 Flowable + * @author kyle + */ +@Configuration(proxyBeanMethods = false) +public class BpmCandidateProcessorConfiguration { + @Bean + public BpmCandidateAdminUserApiSourceInfoProcessor bpmCandidateAdminUserApiSourceInfoProcessor() { + return new BpmCandidateAdminUserApiSourceInfoProcessor(); + } + + @Bean + public BpmCandidateDeptApiSourceInfoProcessor bpmCandidateDeptApiSourceInfoProcessor() { + return new BpmCandidateDeptApiSourceInfoProcessor(); + } + + @Bean + public BpmCandidatePostApiSourceInfoProcessor bpmCandidatePostApiSourceInfoProcessor() { + return new BpmCandidatePostApiSourceInfoProcessor(); + } + + @Bean + public BpmCandidateRoleApiSourceInfoProcessor bpmCandidateRoleApiSourceInfoProcessor() { + return new BpmCandidateRoleApiSourceInfoProcessor(); + } + + @Bean + public BpmCandidateUserGroupApiSourceInfoProcessor bpmCandidateUserGroupApiSourceInfoProcessor() { + return new BpmCandidateUserGroupApiSourceInfoProcessor(); + } + + /** + * 可以自己定制脚本,然后通过这里设置到处理器里面去 + * @param scriptsOp 脚本包装对象 + * @return + */ + @Bean + public BpmCandidateScriptApiSourceInfoProcessor bpmCandidateScriptApiSourceInfoProcessor(ObjectProvider scriptsOp) { + BpmCandidateScriptApiSourceInfoProcessor bpmCandidateScriptApiSourceInfoProcessor = new BpmCandidateScriptApiSourceInfoProcessor(); + bpmCandidateScriptApiSourceInfoProcessor.setScripts(scriptsOp); + return bpmCandidateScriptApiSourceInfoProcessor; + } +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/BpmCandidateSourceInfo.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/BpmCandidateSourceInfo.java new file mode 100644 index 000000000..8582ad9a0 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/BpmCandidateSourceInfo.java @@ -0,0 +1,50 @@ +package cn.iocoder.yudao.module.bpm.service.candidate; + +import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateRuleVO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import java.util.HashSet; +import java.util.Set; + +/** + * 获取候选人信息 + */ +@AllArgsConstructor +@NoArgsConstructor +@Data +public class BpmCandidateSourceInfo { + + @Schema(description = "流程id") + @NotNull + private String processInstanceId; + + @Schema(description = "当前任务ID") + @NotNull + private String taskId; + /** + * 通过这些规则,生成最终需要生成的用户 + */ + @Schema(description = "当前任务预选规则") + @NotEmpty(message = "不允许空规则") + private Set rules; + + @Schema(description = "发起抄送的用户") + private String creator; + + @Schema(description = "抄送原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "请帮忙审查下!") + @NotEmpty(message = "抄送原因不能为空") + private String reason; + + public void addRule(BpmTaskCandidateRuleVO vo) { + assert vo != null; + if (rules == null) { + rules = new HashSet<>(); + } + rules.add(vo); + } +} \ No newline at end of file diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/BpmCandidateSourceInfoProcessor.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/BpmCandidateSourceInfoProcessor.java new file mode 100644 index 000000000..0fe741c20 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/BpmCandidateSourceInfoProcessor.java @@ -0,0 +1,53 @@ +package cn.iocoder.yudao.module.bpm.service.candidate; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateRuleVO; +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum; +import org.flowable.engine.delegate.DelegateExecution; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +public interface BpmCandidateSourceInfoProcessor { + /** + * 获取该处理器支持的类型 + * 来自 {@link BpmTaskAssignRuleTypeEnum} + * + * @return + */ + Set getSupportedTypes(); + + /** + * 对规则和人员做校验 + * + * @param type 规则 + * @param options 人员id + */ + void validRuleOptions(Integer type, Set options); + + /** + * 默认的处理 + * 如果想去操作所有的规则,则可以覆盖此方法 + * + * @param request 原始请求 + * @param delegateExecution 审批过程中的对象 + * @return 必须包含的是用户ID,而不是其他的ID + * @throws Exception + */ + default Set process(BpmCandidateSourceInfo request, DelegateExecution delegateExecution) throws Exception { + Set rules = request.getRules(); + Set results = new HashSet<>(); + for (BpmTaskCandidateRuleVO rule : rules) { + // 每个处理器都有机会处理自己支持的事件 + if (CollUtil.contains(getSupportedTypes(), rule.getType())) { + results.addAll(doProcess(request, rule, delegateExecution)); + } + } + return results; + } + + default Set doProcess(BpmCandidateSourceInfo request, BpmTaskCandidateRuleVO rule, DelegateExecution delegateExecution) { + return Collections.emptySet(); + } +} \ No newline at end of file diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/BpmCandidateSourceInfoProcessorChain.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/BpmCandidateSourceInfoProcessorChain.java new file mode 100644 index 000000000..dafc0835a --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/BpmCandidateSourceInfoProcessorChain.java @@ -0,0 +1,107 @@ +package cn.iocoder.yudao.module.bpm.service.candidate; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.ListUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateRuleVO; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import org.flowable.engine.delegate.DelegateExecution; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; +import java.util.*; + +@Service +public class BpmCandidateSourceInfoProcessorChain { + + // 保存处理节点 + + private List processorList; + @Resource + private AdminUserApi adminUserApi; + + /** + * 可添加其他处理器 + * + * @param processorOp + * @return + */ + @Resource + // 动态扩展处理节点 + public BpmCandidateSourceInfoProcessorChain addProcessor(ObjectProvider processorOp) { + List processor = ListUtil.toList(processorOp.iterator()); + if (null == processorList) { + processorList = new ArrayList<>(processor.size()); + } + processorList.addAll(processor); + return this; + } + + // 获取处理器处理 + public Set process(BpmCandidateSourceInfo sourceInfo, DelegateExecution execution) throws Exception { + // Verify our parameters + if (sourceInfo == null) { + throw new IllegalArgumentException(); + } + for (BpmCandidateSourceInfoProcessor processor : processorList) { + try { + for (BpmTaskCandidateRuleVO vo : sourceInfo.getRules()) { + if (CollUtil.contains(processor.getSupportedTypes(), vo.getType())) { + processor.validRuleOptions(vo.getType(), vo.getOptions()); + } + } + } catch (Exception e) { + e.printStackTrace(); + throw e; + } + } + Set saveResult = Collections.emptySet(); + Exception saveException = null; + for (BpmCandidateSourceInfoProcessor processor : processorList) { + try { + saveResult = processor.process(sourceInfo, execution); + if (CollUtil.isNotEmpty(saveResult)) { + removeDisableUsers(saveResult); + break; + } + } catch (Exception e) { + saveException = e; + break; + } + } + // Return the exception or result state from the last execute() + if ((saveException != null)) { + throw saveException; + } else { + return (saveResult); + } + } + + public Set calculateTaskCandidateUsers(DelegateExecution execution, BpmCandidateSourceInfo sourceInfo) { + Set results = Collections.emptySet(); + try { + results = process(sourceInfo, execution); + } catch (Exception e) { + e.printStackTrace(); + } + return results; + } + + /** + * 移除禁用用户 + * + * @param assigneeUserIds + */ + public void removeDisableUsers(Set assigneeUserIds) { + if (CollUtil.isEmpty(assigneeUserIds)) { + return; + } + Map userMap = adminUserApi.getUserMap(assigneeUserIds); + assigneeUserIds.removeIf(id -> { + AdminUserRespDTO user = userMap.get(id); + return user == null || !CommonStatusEnum.ENABLE.getStatus().equals(user.getStatus()); + }); + } +} \ No newline at end of file diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidateAdminUserApiSourceInfoProcessor.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidateAdminUserApiSourceInfoProcessor.java new file mode 100644 index 000000000..536b3eec2 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidateAdminUserApiSourceInfoProcessor.java @@ -0,0 +1,32 @@ +package cn.iocoder.yudao.module.bpm.service.candidate.sourceInfoProcessor; + +import cn.iocoder.yudao.framework.common.util.collection.SetUtils; +import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateRuleVO; +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum; +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo; +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfoProcessor; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import org.flowable.engine.delegate.DelegateExecution; + +import jakarta.annotation.Resource; +import java.util.Set; + +public class BpmCandidateAdminUserApiSourceInfoProcessor implements BpmCandidateSourceInfoProcessor { + @Resource + private AdminUserApi api; + + @Override + public Set getSupportedTypes() { + return SetUtils.asSet(BpmTaskAssignRuleTypeEnum.USER.getType()); + } + + @Override + public void validRuleOptions(Integer type, Set options) { + api.validateUserList(options); + } + + @Override + public Set doProcess(BpmCandidateSourceInfo request, BpmTaskCandidateRuleVO rule, DelegateExecution delegateExecution) { + return rule.getOptions(); + } +} \ No newline at end of file diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidateDeptApiSourceInfoProcessor.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidateDeptApiSourceInfoProcessor.java new file mode 100644 index 000000000..6fcfa23fd --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidateDeptApiSourceInfoProcessor.java @@ -0,0 +1,50 @@ +package cn.iocoder.yudao.module.bpm.service.candidate.sourceInfoProcessor; + +import cn.iocoder.yudao.framework.common.util.collection.SetUtils; +import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateRuleVO; +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum; +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo; +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfoProcessor; +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.flowable.engine.delegate.DelegateExecution; + +import jakarta.annotation.Resource; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; + +public class BpmCandidateDeptApiSourceInfoProcessor implements BpmCandidateSourceInfoProcessor { + @Resource + private DeptApi api; + @Resource + private AdminUserApi adminUserApi; + + @Override + public Set getSupportedTypes() { + return SetUtils.asSet(BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(), + BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType()); + } + + @Override + public void validRuleOptions(Integer type, Set options) { + api.validateDeptList(options); + } + + @Override + public Set doProcess(BpmCandidateSourceInfo request, BpmTaskCandidateRuleVO rule, DelegateExecution delegateExecution) { + if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(), rule.getType())) { + List users = adminUserApi.getUserListByDeptIds(rule.getOptions()); + return convertSet(users, AdminUserRespDTO::getId); + } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType(), rule.getType())) { + List depts = api.getDeptList(rule.getOptions()); + return convertSet(depts, DeptRespDTO::getLeaderUserId); + } + return Collections.emptySet(); + } +} \ No newline at end of file diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidatePostApiSourceInfoProcessor.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidatePostApiSourceInfoProcessor.java new file mode 100644 index 000000000..f924d87ef --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidatePostApiSourceInfoProcessor.java @@ -0,0 +1,40 @@ +package cn.iocoder.yudao.module.bpm.service.candidate.sourceInfoProcessor; + +import cn.iocoder.yudao.framework.common.util.collection.SetUtils; +import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateRuleVO; +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum; +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo; +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfoProcessor; +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 org.flowable.engine.delegate.DelegateExecution; + +import jakarta.annotation.Resource; +import java.util.List; +import java.util.Set; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; + +public class BpmCandidatePostApiSourceInfoProcessor implements BpmCandidateSourceInfoProcessor { + @Resource + private PostApi api; + @Resource + private AdminUserApi adminUserApi; + + @Override + public Set getSupportedTypes() { + return SetUtils.asSet(BpmTaskAssignRuleTypeEnum.POST.getType()); + } + + @Override + public void validRuleOptions(Integer type, Set options) { + api.validPostList(options); + } + + @Override + public Set doProcess(BpmCandidateSourceInfo request, BpmTaskCandidateRuleVO rule, DelegateExecution delegateExecution) { + List users = adminUserApi.getUserListByPostIds(rule.getOptions()); + return convertSet(users, AdminUserRespDTO::getId); + } +} \ No newline at end of file diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidateRoleApiSourceInfoProcessor.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidateRoleApiSourceInfoProcessor.java new file mode 100644 index 000000000..f3ba5b92b --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidateRoleApiSourceInfoProcessor.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.bpm.service.candidate.sourceInfoProcessor; + +import cn.iocoder.yudao.framework.common.util.collection.SetUtils; +import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateRuleVO; +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum; +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo; +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfoProcessor; +import cn.iocoder.yudao.module.system.api.permission.PermissionApi; +import cn.iocoder.yudao.module.system.api.permission.RoleApi; +import org.flowable.engine.delegate.DelegateExecution; + +import jakarta.annotation.Resource; +import java.util.Set; + +public class BpmCandidateRoleApiSourceInfoProcessor implements BpmCandidateSourceInfoProcessor { + @Resource + private RoleApi api; + + @Resource + private PermissionApi permissionApi; + + @Override + public Set getSupportedTypes() { + return SetUtils.asSet(BpmTaskAssignRuleTypeEnum.ROLE.getType()); + } + + @Override + public void validRuleOptions(Integer type, Set options) { + api.validRoleList(options); + } + + @Override + public Set doProcess(BpmCandidateSourceInfo request, BpmTaskCandidateRuleVO rule, DelegateExecution delegateExecution) { + return permissionApi.getUserRoleIdListByRoleIds(rule.getOptions()); + } + +} \ No newline at end of file diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidateScriptApiSourceInfoProcessor.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidateScriptApiSourceInfoProcessor.java new file mode 100644 index 000000000..4bf25694d --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidateScriptApiSourceInfoProcessor.java @@ -0,0 +1,73 @@ +package cn.iocoder.yudao.module.bpm.service.candidate.sourceInfoProcessor; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.collection.SetUtils; +import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateRuleVO; +import cn.iocoder.yudao.module.bpm.enums.DictTypeConstants; +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript; +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo; +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfoProcessor; +import cn.iocoder.yudao.module.system.api.dict.DictDataApi; +import org.flowable.engine.delegate.DelegateExecution; +import org.springframework.beans.factory.ObjectProvider; + +import jakarta.annotation.Resource; +import java.util.*; +import java.util.stream.Collectors; + +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.TASK_ASSIGN_SCRIPT_NOT_EXISTS; + +public class BpmCandidateScriptApiSourceInfoProcessor implements BpmCandidateSourceInfoProcessor { + @Resource + private DictDataApi dictDataApi; + + /** + * 任务分配脚本 + */ + private Map scriptMap = Collections.emptyMap(); + + public void setScripts(ObjectProvider scriptsOp) { + List scripts = scriptsOp.orderedStream().collect(Collectors.toList()); + setScripts(scripts); + } + + public void setScripts(List scripts) { + this.scriptMap = convertMap(scripts, script -> script.getEnum().getId()); + } + + @Override + public Set getSupportedTypes() { + return SetUtils.asSet(BpmTaskAssignRuleTypeEnum.SCRIPT.getType()); + } + + @Override + public void validRuleOptions(Integer type, Set options) { + dictDataApi.validateDictDataList(DictTypeConstants.TASK_ASSIGN_SCRIPT, + CollectionUtils.convertSet(options, String::valueOf)); + } + + @Override + public Set doProcess(BpmCandidateSourceInfo request, BpmTaskCandidateRuleVO rule, DelegateExecution delegateExecution) { + return calculateTaskCandidateUsersByScript(delegateExecution, rule.getOptions()); + } + + private Set calculateTaskCandidateUsersByScript(DelegateExecution execution, Set options) { + // 获得对应的脚本 + List scripts = new ArrayList<>(options.size()); + options.forEach(id -> { + BpmTaskAssignScript script = scriptMap.get(id); + if (script == null) { + throw exception(TASK_ASSIGN_SCRIPT_NOT_EXISTS, id); + } + scripts.add(script); + }); + // 逐个计算任务 + Set userIds = new HashSet<>(); + scripts.forEach(script -> CollUtil.addAll(userIds, script.calculateTaskCandidateUsers(execution))); + return userIds; + } +} \ No newline at end of file diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidateUserGroupApiSourceInfoProcessor.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidateUserGroupApiSourceInfoProcessor.java new file mode 100644 index 000000000..b0720b0d5 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidateUserGroupApiSourceInfoProcessor.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.module.bpm.service.candidate.sourceInfoProcessor; + +import cn.iocoder.yudao.framework.common.util.collection.SetUtils; +import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateRuleVO; +import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO; +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum; +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo; +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfoProcessor; +import cn.iocoder.yudao.module.bpm.service.definition.BpmUserGroupService; +import org.flowable.engine.delegate.DelegateExecution; + +import jakarta.annotation.Resource; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class BpmCandidateUserGroupApiSourceInfoProcessor implements BpmCandidateSourceInfoProcessor { + @Resource + private BpmUserGroupService api; + @Resource + private BpmUserGroupService userGroupService; + + @Override + public Set getSupportedTypes() { + return SetUtils.asSet(BpmTaskAssignRuleTypeEnum.USER_GROUP.getType()); + } + + @Override + public void validRuleOptions(Integer type, Set options) { + api.validUserGroups(options); + } + + @Override + public Set doProcess(BpmCandidateSourceInfo request, BpmTaskCandidateRuleVO rule, DelegateExecution delegateExecution) { + List userGroups = userGroupService.getUserGroupList(rule.getOptions()); + Set userIds = new HashSet<>(); + userGroups.forEach(group -> userIds.addAll(group.getMemberUserIds())); + return userIds; + } + +} \ No newline at end of file diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/cc/BpmProcessInstanceCopyService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/cc/BpmProcessInstanceCopyService.java new file mode 100644 index 000000000..20285639d --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/cc/BpmProcessInstanceCopyService.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.module.bpm.service.cc; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCCMyPageReqVO; +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCCPageItemRespVO; +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCCReqVO; +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo; + +/** + * 流程抄送Service接口 + * + * 现在是在审批的时候进行流程抄送 + */ +public interface BpmProcessInstanceCopyService { + + /** + * 查询流程抄送 + * + * @param copyId 流程抄送主键 + * @return 流程抄送 + */ + BpmProcessInstanceCopyVO queryById(Long copyId); + + /** + * 抄送 + * @param sourceInfo 抄送源信息,方便抄送处理 + * @return + */ + boolean makeCopy(BpmCandidateSourceInfo sourceInfo); + + /** + * 流程实例的抄送 + * @param loginUserId 当前登录用户 + * @param createReqVO 创建的抄送请求 + * @return 是否抄送成功,抄送成功则返回true + */ + boolean ccProcessInstance(Long loginUserId, BpmProcessInstanceCCReqVO createReqVO); + + /** + * 抄送的流程 + * @param loginUserId 登录用户id + * @param pageReqVO 分页请求 + * @return 抄送的分页结果 + */ + PageResult getMyProcessInstanceCCPage(Long loginUserId, BpmProcessInstanceCCMyPageReqVO pageReqVO); +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/cc/BpmProcessInstanceCopyServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/cc/BpmProcessInstanceCopyServiceImpl.java new file mode 100644 index 000000000..865a6f699 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/cc/BpmProcessInstanceCopyServiceImpl.java @@ -0,0 +1,150 @@ +package cn.iocoder.yudao.module.bpm.service.cc; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCCMyPageReqVO; +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCCPageItemRespVO; +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCCReqVO; +import cn.iocoder.yudao.module.bpm.convert.cc.BpmProcessInstanceCopyConvert; +import cn.iocoder.yudao.module.bpm.dal.dataobject.cc.BpmProcessInstanceCopyDO; +import cn.iocoder.yudao.module.bpm.dal.mysql.cc.BpmProcessInstanceCopyMapper; +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo; +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfoProcessorChain; +import cn.iocoder.yudao.module.bpm.service.cc.dto.BpmDelegateExecutionDTO; +import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService; +import cn.iocoder.yudao.module.bpm.util.FlowableUtils; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.RuntimeService; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import jakarta.annotation.Resource; +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; + +@Slf4j +@Service +@Validated +public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopyService { + @Resource + private BpmProcessInstanceCopyMapper processInstanceCopyMapper; + + /** + * 和flowable有关的,查询流程名用的 + */ + @Resource + private RuntimeService runtimeService; + + /** + * 找抄送人用的 + */ + @Resource + private BpmCandidateSourceInfoProcessorChain processorChain; + + @Resource + @Lazy // 解决循环依赖 + private BpmTaskService taskService; + @Resource + private AdminUserApi adminUserApi; + + @Override + public BpmProcessInstanceCopyVO queryById(Long copyId) { + BpmProcessInstanceCopyDO bpmProcessInstanceCopyDO = processInstanceCopyMapper.selectById(copyId); + return BpmProcessInstanceCopyConvert.INSTANCE.convert(bpmProcessInstanceCopyDO); + } + + @Override + public boolean makeCopy(BpmCandidateSourceInfo sourceInfo) { + if (null == sourceInfo) { + return false; + } + + DelegateExecution executionEntity = new BpmDelegateExecutionDTO(sourceInfo.getProcessInstanceId()); + Set ccCandidates = processorChain.calculateTaskCandidateUsers(executionEntity, sourceInfo); + if (CollUtil.isEmpty(ccCandidates)) { + log.warn("相关抄送人不存在 {}", sourceInfo.getTaskId()); + return false; + } else { + BpmProcessInstanceCopyDO copyDO = new BpmProcessInstanceCopyDO(); + // 调用 + // 设置任务id + copyDO.setTaskId(sourceInfo.getTaskId()); + copyDO.setTaskName(FlowableUtils.getTaskNameByTaskId(sourceInfo.getTaskId())); + copyDO.setProcessInstanceId(sourceInfo.getProcessInstanceId()); + ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() + .processInstanceId(sourceInfo.getProcessInstanceId()) + .singleResult(); + if (null == processInstance) { + log.warn("相关流程实例不存在 {}", sourceInfo.getTaskId()); + return false; + } + copyDO.setStartUserId(FlowableUtils.getStartUserIdFromProcessInstance(processInstance)); + copyDO.setProcessInstanceName(processInstance.getName()); + ProcessDefinition processDefinition = FlowableUtils.getProcessDefinition(processInstance.getProcessDefinitionId()); + copyDO.setProcessDefinitionCategory(processDefinition.getCategory()); + copyDO.setReason(sourceInfo.getReason()); + copyDO.setCreator(sourceInfo.getCreator()); + copyDO.setCreateTime(LocalDateTime.now()); + List copyList = new ArrayList<>(ccCandidates.size()); + for (Long userId : ccCandidates) { + BpmProcessInstanceCopyDO copy = BpmProcessInstanceCopyConvert.INSTANCE.copy(copyDO); + copy.setUserId(userId); + copyList.add(copy); + } + return processInstanceCopyMapper.insertBatch(copyList); + } + } + + @Override + public boolean ccProcessInstance(Long loginUserId, BpmProcessInstanceCCReqVO reqVO) { + // 在能正常审批的情况下抄送流程 + BpmCandidateSourceInfo sourceInfo = new BpmCandidateSourceInfo(); + sourceInfo.setTaskId(reqVO.getTaskKey()); + sourceInfo.setProcessInstanceId(reqVO.getProcessInstanceKey()); + sourceInfo.addRule(reqVO); + sourceInfo.setCreator(String.valueOf(loginUserId)); + sourceInfo.setReason(reqVO.getReason()); + if (!makeCopy(sourceInfo)) { + throw new RuntimeException("抄送任务失败"); + } + return false; + } + + //获取流程抄送分页 + @Override + public PageResult getMyProcessInstanceCCPage(Long loginUserId, BpmProcessInstanceCCMyPageReqVO pageReqVO) { + // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页 + PageResult pageResult = processInstanceCopyMapper.selectPage(loginUserId, pageReqVO); + if (CollUtil.isEmpty(pageResult.getList())) { + return new PageResult<>(pageResult.getTotal()); + } + + Set taskIds = new HashSet<>(); + Set processInstaneIds = new HashSet<>(); + Set userIds = new HashSet<>(); + for (BpmProcessInstanceCopyDO doItem : pageResult.getList()) { + taskIds.add(doItem.getTaskId()); + processInstaneIds.add(doItem.getProcessInstanceId()); + userIds.add(doItem.getStartUserId()); + Long userId = Long.valueOf(doItem.getCreator()); + userIds.add(userId); + } + + Map taskNameByTaskIds = FlowableUtils.getTaskNameByTaskIds(taskIds); + Map processInstanceNameByTaskIds = FlowableUtils.getProcessInstanceNameByTaskIds(processInstaneIds); + + Map userMap = adminUserApi.getUserList(userIds).stream().collect(Collectors.toMap( + AdminUserRespDTO::getId, AdminUserRespDTO::getNickname)); + + // 转换返回 + return BpmProcessInstanceCopyConvert.INSTANCE.convertPage(pageResult, taskNameByTaskIds, processInstanceNameByTaskIds, userMap); + } + +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/cc/BpmProcessInstanceCopyVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/cc/BpmProcessInstanceCopyVO.java new file mode 100644 index 000000000..773335837 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/cc/BpmProcessInstanceCopyVO.java @@ -0,0 +1,69 @@ +package cn.iocoder.yudao.module.bpm.service.cc; + +import com.baomidou.mybatisplus.annotation.TableId; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.Date; + + +/** + * 流程抄送视图对象 wf_copy + * + * @author ruoyi + * @date 2022-05-19 + */ +@Data +public class BpmProcessInstanceCopyVO { + + /** + * 编号 + */ + @Schema(description = "抄送主键") + private Long id; + + /** + * 发起人Id + */ + @Schema(description = "发起人Id") + private Long startUserId; + + @Schema(description = "发起人别名") + private String startUserNickname; + + /** + * 流程主键 + */ + @Schema(description = "流程实例的主键") + private String processInstanceId; + + @Schema(description = "流程实例的名字") + private String processInstanceName; + + /** + * 任务主键 + */ + @Schema(description = "发起抄送的任务编号") + private String taskId; + + @Schema(description = "发起抄送的任务名称") + private String taskName; + /** + * 用户主键 + */ + @Schema(description = "用户编号") + private Long userId; + + @Schema(description = "用户别名") + private Long userNickname; + + @Schema(description = "抄送原因") + private String reason; + + @Schema(description = "抄送人") + private String creator; + + @Schema(description = "抄送时间") + private LocalDateTime createTime; +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/cc/dto/BpmDelegateExecutionDTO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/cc/dto/BpmDelegateExecutionDTO.java new file mode 100644 index 000000000..47213ae52 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/cc/dto/BpmDelegateExecutionDTO.java @@ -0,0 +1,439 @@ +package cn.iocoder.yudao.module.bpm.service.cc.dto; + +import org.flowable.bpmn.model.FlowElement; +import org.flowable.bpmn.model.FlowableListener; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.delegate.ReadOnlyDelegateExecution; +import org.flowable.variable.api.persistence.entity.VariableInstance; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * 仅为了传输processInstanceId + */ +public class BpmDelegateExecutionDTO implements DelegateExecution { + + public BpmDelegateExecutionDTO(String getProcessInstanceId) { + this.getProcessInstanceId = getProcessInstanceId; + } + + private final String getProcessInstanceId; + + @Override + public String getId() { + return null; + } + + @Override + public String getProcessInstanceId() { + return null; + } + + @Override + public String getRootProcessInstanceId() { + return null; + } + + @Override + public String getEventName() { + return null; + } + + @Override + public void setEventName(String eventName) { + + } + + @Override + public String getProcessInstanceBusinessKey() { + return null; + } + + @Override + public String getProcessInstanceBusinessStatus() { + return null; + } + + @Override + public String getProcessDefinitionId() { + return null; + } + + @Override + public String getPropagatedStageInstanceId() { + return null; + } + + @Override + public String getParentId() { + return null; + } + + @Override + public String getSuperExecutionId() { + return null; + } + + @Override + public String getCurrentActivityId() { + return null; + } + + @Override + public String getTenantId() { + return null; + } + + @Override + public FlowElement getCurrentFlowElement() { + return null; + } + + @Override + public void setCurrentFlowElement(FlowElement flowElement) { + + } + + @Override + public FlowableListener getCurrentFlowableListener() { + return null; + } + + @Override + public void setCurrentFlowableListener(FlowableListener currentListener) { + + } + + @Override + public ReadOnlyDelegateExecution snapshotReadOnly() { + return null; + } + + @Override + public DelegateExecution getParent() { + return null; + } + + @Override + public List getExecutions() { + return null; + } + + @Override + public void setActive(boolean isActive) { + + } + + @Override + public boolean isActive() { + return false; + } + + @Override + public boolean isEnded() { + return false; + } + + @Override + public void setConcurrent(boolean isConcurrent) { + + } + + @Override + public boolean isConcurrent() { + return false; + } + + @Override + public boolean isProcessInstanceType() { + return false; + } + + @Override + public void inactivate() { + + } + + @Override + public boolean isScope() { + return false; + } + + @Override + public void setScope(boolean isScope) { + + } + + @Override + public boolean isMultiInstanceRoot() { + return false; + } + + @Override + public void setMultiInstanceRoot(boolean isMultiInstanceRoot) { + + } + + @Override + public Map getVariables() { + return null; + } + + @Override + public Map getVariableInstances() { + return null; + } + + @Override + public Map getVariables(Collection collection) { + return null; + } + + @Override + public Map getVariableInstances(Collection collection) { + return null; + } + + @Override + public Map getVariables(Collection collection, boolean b) { + return null; + } + + @Override + public Map getVariableInstances(Collection collection, boolean b) { + return null; + } + + @Override + public Map getVariablesLocal() { + return null; + } + + @Override + public Map getVariableInstancesLocal() { + return null; + } + + @Override + public Map getVariablesLocal(Collection collection) { + return null; + } + + @Override + public Map getVariableInstancesLocal(Collection collection) { + return null; + } + + @Override + public Map getVariablesLocal(Collection collection, boolean b) { + return null; + } + + @Override + public Map getVariableInstancesLocal(Collection collection, boolean b) { + return null; + } + + @Override + public Object getVariable(String s) { + return null; + } + + @Override + public VariableInstance getVariableInstance(String s) { + return null; + } + + @Override + public Object getVariable(String s, boolean b) { + return null; + } + + @Override + public VariableInstance getVariableInstance(String s, boolean b) { + return null; + } + + @Override + public Object getVariableLocal(String s) { + return null; + } + + @Override + public VariableInstance getVariableInstanceLocal(String s) { + return null; + } + + @Override + public Object getVariableLocal(String s, boolean b) { + return null; + } + + @Override + public VariableInstance getVariableInstanceLocal(String s, boolean b) { + return null; + } + + @Override + public T getVariable(String s, Class aClass) { + return null; + } + + @Override + public T getVariableLocal(String s, Class aClass) { + return null; + } + + @Override + public Set getVariableNames() { + return null; + } + + @Override + public Set getVariableNamesLocal() { + return null; + } + + @Override + public void setVariable(String s, Object o) { + + } + + @Override + public void setVariable(String s, Object o, boolean b) { + + } + + @Override + public Object setVariableLocal(String s, Object o) { + return null; + } + + @Override + public Object setVariableLocal(String s, Object o, boolean b) { + return null; + } + + @Override + public void setVariables(Map map) { + + } + + @Override + public void setVariablesLocal(Map map) { + + } + + @Override + public boolean hasVariables() { + return false; + } + + @Override + public boolean hasVariablesLocal() { + return false; + } + + @Override + public boolean hasVariable(String s) { + return false; + } + + @Override + public boolean hasVariableLocal(String s) { + return false; + } + + @Override + public void removeVariable(String s) { + + } + + @Override + public void removeVariableLocal(String s) { + + } + + @Override + public void removeVariables(Collection collection) { + + } + + @Override + public void removeVariablesLocal(Collection collection) { + + } + + @Override + public void removeVariables() { + + } + + @Override + public void removeVariablesLocal() { + + } + + @Override + public void setTransientVariable(String s, Object o) { + + } + + @Override + public void setTransientVariableLocal(String s, Object o) { + + } + + @Override + public void setTransientVariables(Map map) { + + } + + @Override + public Object getTransientVariable(String s) { + return null; + } + + @Override + public Map getTransientVariables() { + return null; + } + + @Override + public void setTransientVariablesLocal(Map map) { + + } + + @Override + public Object getTransientVariableLocal(String s) { + return null; + } + + @Override + public Map getTransientVariablesLocal() { + return null; + } + + @Override + public void removeTransientVariableLocal(String s) { + + } + + @Override + public void removeTransientVariable(String s) { + + } + + @Override + public void removeTransientVariables() { + + } + + @Override + public void removeTransientVariablesLocal() { + + } +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index a0f938e67..27a4c7763 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/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; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskAddSignTypeEnum; +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo; +import cn.iocoder.yudao.module.bpm.service.cc.BpmProcessInstanceCopyService; import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService; import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; import cn.iocoder.yudao.module.system.api.dept.DeptApi; @@ -94,6 +96,9 @@ public class BpmTaskServiceImpl implements BpmTaskService { @Resource private ManagementService managementService; + @Resource + private BpmProcessInstanceCopyService processInstanceCopyService; + @Override public PageResult getTodoTaskPage(Long userId, BpmTaskTodoPageReqVO pageVO) { // 查询待办任务 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/util/FlowableUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/util/FlowableUtils.java new file mode 100644 index 000000000..94dabcd8e --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/util/FlowableUtils.java @@ -0,0 +1,125 @@ +package cn.iocoder.yudao.module.bpm.util; + + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.extra.spring.SpringUtil; +import cn.iocoder.yudao.framework.common.util.number.NumberUtils; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.bpmn.model.ExtensionElement; +import org.flowable.bpmn.model.FlowElement; +import org.flowable.bpmn.model.FlowNode; +import org.flowable.engine.RepositoryService; +import org.flowable.engine.RuntimeService; +import org.flowable.engine.TaskService; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.task.api.Task; + +import java.util.*; + +/** + * 流程引擎工具类封装 + * + * @author: linjinp + * @create: 2019-12-24 13:51 + **/ +public class FlowableUtils { + + /** + * 获取流程名称 + * + * @param processDefinitionId + * @return + */ + public static String getProcessDefinitionName(String processDefinitionId) { + RepositoryService repositoryService = SpringUtil.getBean(RepositoryService.class); + ProcessDefinition processDefinition = repositoryService.getProcessDefinition(processDefinitionId); + return processDefinition.getName(); + } + + public static ProcessDefinition getProcessDefinition(String processDefinitionId) { + RepositoryService repositoryService = SpringUtil.getBean(RepositoryService.class); + return repositoryService.getProcessDefinition(processDefinitionId); + } + + /** + * 获取节点数据 + * + * @param processInstanceId + * @param nodeId + * @return + */ + public static FlowNode getFlowNode(String processInstanceId, String nodeId) { + + RuntimeService runtimeService = SpringUtil.getBean(RuntimeService.class); + RepositoryService repositoryService = SpringUtil.getBean(RepositoryService.class); + + String definitionld = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult().getProcessDefinitionId(); // 获取bpm(模型)对象 + BpmnModel bpmnModel = repositoryService.getBpmnModel(definitionld); + // 传节点定义key获取当前节点 + FlowNode flowNode = (FlowNode) bpmnModel.getFlowElement(nodeId); + return flowNode; + } + + public static ExtensionElement generateFlowNodeIdExtension(String nodeId) { + ExtensionElement extensionElement = new ExtensionElement(); + extensionElement.setElementText(nodeId); + extensionElement.setName("nodeId"); + extensionElement.setNamespacePrefix("flowable"); + extensionElement.setNamespace("nodeId"); + return extensionElement; + } + + public static String getNodeIdFromExtension(FlowElement flowElement) { + Map> extensionElements = flowElement.getExtensionElements(); + return extensionElements.get("nodeId").get(0).getElementText(); + } + + public static Long getStartUserIdFromProcessInstance(ProcessInstance instance) { + if (null == instance) { + return null; + } + return NumberUtils.parseLong(instance.getStartUserId()); + } + + public static String getTaskNameByTaskId(String taskId) { + TaskService taskService = SpringUtil.getBean(TaskService.class); + Task task = taskService.createTaskQuery().taskId(taskId).singleResult(); + return task.getName(); + } + + public static Map getTaskNameByTaskIds(Collection taskIds) { + TaskService taskService = SpringUtil.getBean(TaskService.class); + List tasks = taskService.createTaskQuery().taskIds(taskIds).list(); + if (CollUtil.isNotEmpty(tasks)) { + Map taskMap = new HashMap<>(tasks.size()); + for (Task task : tasks) { + taskMap.putIfAbsent(task.getId(), task.getName()); + } + return taskMap; + } + return Collections.emptyMap(); + } + + public static String getProcessInstanceNameByTaskId(String processInstanceId) { + RuntimeService runtimeService = SpringUtil.getBean(RuntimeService.class); + ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() + .processInstanceId(processInstanceId) + .singleResult(); + return processInstance.getName(); + } + + public static Map getProcessInstanceNameByTaskIds(Set taskIds) { + RuntimeService runtimeService = SpringUtil.getBean(RuntimeService.class); + List processInstances = runtimeService.createProcessInstanceQuery().processInstanceIds(taskIds).list(); + if (CollUtil.isNotEmpty(processInstances)) { + Map processInstaneMap = new HashMap<>(processInstances.size()); + for (ProcessInstance processInstance : processInstances) { + processInstaneMap.putIfAbsent(processInstance.getId(), processInstance.getName()); + } + return processInstaneMap; + } + return Collections.emptyMap(); + } + +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/candidate/BpmCandidateSourceInfoProcessorChainTest.java b/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/candidate/BpmCandidateSourceInfoProcessorChainTest.java new file mode 100644 index 000000000..38eb7cbea --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/candidate/BpmCandidateSourceInfoProcessorChainTest.java @@ -0,0 +1,242 @@ +package cn.iocoder.yudao.module.bpm.service.candidate; + +import cn.hutool.core.map.MapUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateRuleVO; +import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO; +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum; +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskRuleScriptEnum; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.impl.BpmTaskAssignLeaderX1Script; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.impl.BpmTaskAssignLeaderX2Script; +import cn.iocoder.yudao.module.bpm.service.candidate.sourceInfoProcessor.BpmCandidateScriptApiSourceInfoProcessor; +import cn.iocoder.yudao.module.bpm.service.definition.BpmUserGroupService; +import cn.iocoder.yudao.module.system.api.dept.DeptApi; +import cn.iocoder.yudao.module.system.api.dept.PostApi; +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; +import cn.iocoder.yudao.module.system.api.dict.DictDataApi; +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 cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import jakarta.annotation.Resource; +import org.flowable.engine.delegate.DelegateExecution; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static java.util.Collections.singleton; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +@Import({BpmCandidateSourceInfoProcessorChain.class, + BpmCandidateScriptApiSourceInfoProcessor.class, BpmTaskAssignLeaderX1Script.class, + BpmTaskAssignLeaderX2Script.class}) +public class BpmCandidateSourceInfoProcessorChainTest extends BaseDbUnitTest { + @Resource + private BpmCandidateSourceInfoProcessorChain processorChain; + + @MockBean + private BpmUserGroupService userGroupService; + @MockBean + private DeptApi deptApi; + @MockBean + private AdminUserApi adminUserApi; + @MockBean + private PermissionApi permissionApi; + @MockBean + private RoleApi roleApi; + @MockBean + private PostApi postApi; + @MockBean + private DictDataApi dictDataApi; + @Resource + private BpmCandidateScriptApiSourceInfoProcessor bpmCandidateScriptApiSourceInfoProcessor; + + @Test + public void testCalculateTaskCandidateUsers_Role() { + // 准备参数 + BpmTaskCandidateRuleVO rule = new BpmTaskCandidateRuleVO().setOptions(asSet(1L, 2L)) + .setType(BpmTaskAssignRuleTypeEnum.ROLE.getType()); + // mock 方法 + when(permissionApi.getUserRoleIdListByRoleIds(eq(rule.getOptions()))) + .thenReturn(asSet(11L, 22L)); + mockGetUserMap(asSet(11L, 22L)); + + // 调用 + BpmCandidateSourceInfo sourceInfo = new BpmCandidateSourceInfo(); + sourceInfo.addRule(rule); + Set results = processorChain.calculateTaskCandidateUsers(null, sourceInfo); + // 断言 + assertEquals(asSet(11L, 22L), results); + } + + @Test + public void testCalculateTaskCandidateUsers_DeptMember() { + // 准备参数 + BpmTaskCandidateRuleVO rule = new BpmTaskCandidateRuleVO().setOptions(asSet(1L, 2L)) + .setType(BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType()); + // mock 方法 + List users = CollectionUtils.convertList(asSet(11L, 22L), + id -> new AdminUserRespDTO().setId(id)); + when(adminUserApi.getUserListByDeptIds(eq(rule.getOptions()))).thenReturn(users); + mockGetUserMap(asSet(11L, 22L)); + + // 调用 + BpmCandidateSourceInfo sourceInfo = new BpmCandidateSourceInfo(); + sourceInfo.addRule(rule); + Set results = processorChain.calculateTaskCandidateUsers(null, sourceInfo); + // 断言 + assertEquals(asSet(11L, 22L), results); + } + + @Test + public void testCalculateTaskCandidateUsers_DeptLeader() { + // 准备参数 + BpmTaskCandidateRuleVO rule = new BpmTaskCandidateRuleVO().setOptions(asSet(1L, 2L)) + .setType(BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType()); + // mock 方法 + DeptRespDTO dept1 = randomPojo(DeptRespDTO.class, o -> o.setLeaderUserId(11L)); + DeptRespDTO dept2 = randomPojo(DeptRespDTO.class, o -> o.setLeaderUserId(22L)); + when(deptApi.getDeptList(eq(rule.getOptions()))).thenReturn(Arrays.asList(dept1, dept2)); + mockGetUserMap(asSet(11L, 22L)); + + // 调用 + BpmCandidateSourceInfo sourceInfo = new BpmCandidateSourceInfo(); + sourceInfo.addRule(rule); + Set results = processorChain.calculateTaskCandidateUsers(null, sourceInfo); + // 断言 + assertEquals(asSet(11L, 22L), results); + } + + @Test + public void testCalculateTaskCandidateUsers_Post() { + // 准备参数 + BpmTaskCandidateRuleVO rule = new BpmTaskCandidateRuleVO().setOptions(asSet(1L, 2L)) + .setType(BpmTaskAssignRuleTypeEnum.POST.getType()); + // mock 方法 + List users = CollectionUtils.convertList(asSet(11L, 22L), + id -> new AdminUserRespDTO().setId(id)); + when(adminUserApi.getUserListByPostIds(eq(rule.getOptions()))).thenReturn(users); + mockGetUserMap(asSet(11L, 22L)); + + // 调用 + BpmCandidateSourceInfo sourceInfo = new BpmCandidateSourceInfo(); + sourceInfo.addRule(rule); + Set results = processorChain.calculateTaskCandidateUsers(null, sourceInfo); + // 断言 + assertEquals(asSet(11L, 22L), results); + } + + @Test + public void testCalculateTaskCandidateUsers_User() { + // 准备参数 + BpmTaskCandidateRuleVO rule = new BpmTaskCandidateRuleVO().setOptions(asSet(1L, 2L)) + .setType(BpmTaskAssignRuleTypeEnum.USER.getType()); + // mock 方法 + mockGetUserMap(asSet(1L, 2L)); + + // 调用 + BpmCandidateSourceInfo sourceInfo = new BpmCandidateSourceInfo(); + sourceInfo.addRule(rule); + Set results = processorChain.calculateTaskCandidateUsers(null, sourceInfo); + // 断言 + assertEquals(asSet(1L, 2L), results); + } + + @Test + public void testCalculateTaskCandidateUsers_UserGroup() { + // 准备参数 + BpmTaskCandidateRuleVO rule = new BpmTaskCandidateRuleVO().setOptions(asSet(1L, 2L)) + .setType(BpmTaskAssignRuleTypeEnum.USER_GROUP.getType()); + // mock 方法 + BpmUserGroupDO userGroup1 = randomPojo(BpmUserGroupDO.class, o -> o.setMemberUserIds(asSet(11L, 12L))); + BpmUserGroupDO userGroup2 = randomPojo(BpmUserGroupDO.class, o -> o.setMemberUserIds(asSet(21L, 22L))); + when(userGroupService.getUserGroupList(eq(rule.getOptions()))).thenReturn(Arrays.asList(userGroup1, userGroup2)); + mockGetUserMap(asSet(11L, 12L, 21L, 22L)); + + // 调用 + BpmCandidateSourceInfo sourceInfo = new BpmCandidateSourceInfo(); + sourceInfo.addRule(rule); + Set results = processorChain.calculateTaskCandidateUsers(null, sourceInfo); + // 断言 + assertEquals(asSet(11L, 12L, 21L, 22L), results); + } + + private void mockGetUserMap(Set assigneeUserIds) { + Map userMap = CollectionUtils.convertMap(assigneeUserIds, id -> id, + id -> new AdminUserRespDTO().setId(id).setStatus(CommonStatusEnum.ENABLE.getStatus())); + when(adminUserApi.getUserMap(eq(assigneeUserIds))).thenReturn(userMap); + } + + @Test + public void testCalculateTaskCandidateUsers_Script() { + // 准备参数 + BpmTaskCandidateRuleVO rule = new BpmTaskCandidateRuleVO().setOptions(asSet(20L, 21L)) + .setType(BpmTaskAssignRuleTypeEnum.SCRIPT.getType()); + // mock 方法 + BpmTaskAssignScript script1 = new BpmTaskAssignScript() { + + @Override + public Set calculateTaskCandidateUsers(DelegateExecution task) { + return singleton(11L); + } + + @Override + public BpmTaskRuleScriptEnum getEnum() { + return BpmTaskRuleScriptEnum.LEADER_X1; + } + }; + BpmTaskAssignScript script2 = new BpmTaskAssignScript() { + + @Override + public Set calculateTaskCandidateUsers(DelegateExecution task) { + return singleton(22L); + } + + @Override + public BpmTaskRuleScriptEnum getEnum() { + return BpmTaskRuleScriptEnum.LEADER_X2; + } + }; + bpmCandidateScriptApiSourceInfoProcessor.setScripts(Arrays.asList(script1, script2)); + mockGetUserMap(asSet(11L, 22L)); + + // 调用 + BpmCandidateSourceInfo sourceInfo = new BpmCandidateSourceInfo(); + sourceInfo.addRule(rule); + Set results = processorChain.calculateTaskCandidateUsers(null, sourceInfo); + // 断言 + assertEquals(asSet(11L, 22L), results); + } + + @Test + public void testRemoveDisableUsers() { + // 准备参数. 1L 可以找到;2L 是禁用的;3L 找不到 + Set assigneeUserIds = asSet(1L, 2L, 3L); + // mock 方法 + AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L) + .setStatus(CommonStatusEnum.DISABLE.getStatus())); + Map userMap = MapUtil.builder(user1.getId(), user1) + .put(user2.getId(), user2).build(); + when(adminUserApi.getUserMap(eq(assigneeUserIds))).thenReturn(userMap); + + // 调用 + processorChain.removeDisableUsers(assigneeUserIds); + // 断言 + assertEquals(asSet(1L), assigneeUserIds); + } + +} \ No newline at end of file diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/cc/BpmProcessInstanceCopyServiceTest.java b/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/cc/BpmProcessInstanceCopyServiceTest.java new file mode 100644 index 000000000..1f0ced103 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/cc/BpmProcessInstanceCopyServiceTest.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.bpm.service.cc; + +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +@Import({BpmProcessInstanceCopyServiceImpl.class}) +class BpmProcessInstanceCopyServiceTest extends BaseDbUnitTest { + @Resource + private BpmProcessInstanceCopyServiceImpl service; + + @Test + void queryById() { + } +} \ No newline at end of file diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserBindReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserBindReqVO.java index 5ed0042a8..815f367fa 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserBindReqVO.java +++ b/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 { @NotEmpty(message = "state 不能为空") private String state; + public Integer getType() { + return type; + } } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/ip/AreaConvert.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/ip/AreaConvert.java index ae98bd25d..6ba76ef92 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/ip/AreaConvert.java +++ b/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 { List convertList3(List list); + /** + * 缺少单个转换 + * @param value + * @return + */ + AreaNodeRespVO map(Area value); + + AppAreaNodeRespVO map3(Area value); }