Commit d6775a5619226611da610eb9785007c90250e5ea
1 parent
073d860a
工作流 Flowable 发起流程, 用户任务相关实现
Showing
12 changed files
with
362 additions
and
8 deletions
yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/framework/activiti/core/behavior/BpmActivityBehaviorFactory.java
| ... | ... | @@ -44,7 +44,7 @@ public class BpmActivityBehaviorFactory extends DefaultActivityBehaviorFactory { |
| 44 | 44 | |
| 45 | 45 | @Override |
| 46 | 46 | public UserTaskActivityBehavior createUserTaskActivityBehavior(UserTask userTask) { |
| 47 | - BpmUserTaskActivitiBehavior userTaskActivityBehavior = new BpmUserTaskActivitiBehavior(userTask); | |
| 47 | + BpmUserTaskActivityBehavior userTaskActivityBehavior = new BpmUserTaskActivityBehavior(userTask); | |
| 48 | 48 | userTaskActivityBehavior.setBpmTaskRuleService(bpmTaskRuleService); |
| 49 | 49 | userTaskActivityBehavior.setPermissionApi(permissionApi); |
| 50 | 50 | userTaskActivityBehavior.setDeptApi(deptApi); | ... | ... |
yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/framework/activiti/core/behavior/BpmUserTaskActivitiBehavior.java renamed to yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/framework/activiti/core/behavior/BpmUserTaskActivityBehavior.java
| ... | ... | @@ -44,7 +44,7 @@ import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.TASK_CREATE_F |
| 44 | 44 | * @author 芋道源码 |
| 45 | 45 | */ |
| 46 | 46 | @Slf4j |
| 47 | -public class BpmUserTaskActivitiBehavior extends UserTaskActivityBehavior { | |
| 47 | +public class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior { | |
| 48 | 48 | |
| 49 | 49 | @Setter |
| 50 | 50 | private BpmTaskAssignRuleService bpmTaskRuleService; |
| ... | ... | @@ -64,7 +64,7 @@ public class BpmUserTaskActivitiBehavior extends UserTaskActivityBehavior { |
| 64 | 64 | */ |
| 65 | 65 | private Map<Long, BpmTaskAssignScript> scriptMap = Collections.emptyMap(); |
| 66 | 66 | |
| 67 | - public BpmUserTaskActivitiBehavior(UserTask userTask) { | |
| 67 | + public BpmUserTaskActivityBehavior(UserTask userTask) { | |
| 68 | 68 | super(userTask); |
| 69 | 69 | } |
| 70 | 70 | ... | ... |
yudao-module-bpm/yudao-module-bpm-impl-activiti/src/test/java/cn/iocoder/yudao/module/bpm/framework/activiti/core/behavior/BpmUserTaskActivitiBehaviorTest.java renamed to yudao-module-bpm/yudao-module-bpm-impl-activiti/src/test/java/cn/iocoder/yudao/module/bpm/framework/activiti/core/behavior/BpmUserTaskActivityBehaviorTest.java
| ... | ... | @@ -33,10 +33,10 @@ import static org.junit.jupiter.api.Assertions.assertEquals; |
| 33 | 33 | import static org.mockito.ArgumentMatchers.eq; |
| 34 | 34 | import static org.mockito.Mockito.when; |
| 35 | 35 | |
| 36 | -public class BpmUserTaskActivitiBehaviorTest extends BaseMockitoUnitTest { | |
| 36 | +public class BpmUserTaskActivityBehaviorTest extends BaseMockitoUnitTest { | |
| 37 | 37 | |
| 38 | 38 | @InjectMocks |
| 39 | - private BpmUserTaskActivitiBehavior behavior; | |
| 39 | + private BpmUserTaskActivityBehavior behavior; | |
| 40 | 40 | @Mock |
| 41 | 41 | private BpmTaskAssignRuleService bpmTaskRuleService; |
| 42 | 42 | @Mock | ... | ... |
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java
| ... | ... | @@ -2,10 +2,12 @@ package cn.iocoder.yudao.module.bpm.controller.admin.task; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 4 | 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO; | |
| 5 | 6 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskTodoPageItemRespVO; |
| 6 | 7 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskTodoPageReqVO; |
| 7 | 8 | import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService; |
| 8 | 9 | import io.swagger.annotations.Api; |
| 10 | +import io.swagger.annotations.ApiImplicitParam; | |
| 9 | 11 | import io.swagger.annotations.ApiOperation; |
| 10 | 12 | import org.flowable.bpmn.model.BpmnModel; |
| 11 | 13 | import org.flowable.engine.TaskService; |
| ... | ... | @@ -13,11 +15,14 @@ import org.springframework.security.access.prepost.PreAuthorize; |
| 13 | 15 | import org.springframework.validation.annotation.Validated; |
| 14 | 16 | import org.springframework.web.bind.annotation.GetMapping; |
| 15 | 17 | import org.springframework.web.bind.annotation.RequestMapping; |
| 18 | +import org.springframework.web.bind.annotation.RequestParam; | |
| 16 | 19 | import org.springframework.web.bind.annotation.RestController; |
| 17 | 20 | |
| 18 | 21 | import javax.annotation.Resource; |
| 19 | 22 | import javax.validation.Valid; |
| 20 | 23 | |
| 24 | +import java.util.List; | |
| 25 | + | |
| 21 | 26 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 22 | 27 | import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId; |
| 23 | 28 | |
| ... | ... | @@ -36,4 +41,13 @@ public class BpmTaskController { |
| 36 | 41 | public CommonResult<PageResult<BpmTaskTodoPageItemRespVO>> getTodoTaskPage(@Valid BpmTaskTodoPageReqVO pageVO) { |
| 37 | 42 | return success(taskService.getTodoTaskPage(getLoginUserId(), pageVO)); |
| 38 | 43 | } |
| 44 | + | |
| 45 | + @GetMapping("/list-by-process-instance-id") | |
| 46 | + @ApiOperation(value = "获得指定流程实例的任务列表", notes = "包括完成的、未完成的") | |
| 47 | + @ApiImplicitParam(name = "processInstanceId", value = "流程实例的编号", required = true, dataTypeClass = String.class) | |
| 48 | + @PreAuthorize("@ss.hasPermission('bpm:task:query')") | |
| 49 | + public CommonResult<List<BpmTaskRespVO>> getTaskListByProcessInstanceId( | |
| 50 | + @RequestParam("processInstanceId") String processInstanceId) { | |
| 51 | + return success(taskService.getTaskListByProcessInstanceId(processInstanceId)); | |
| 52 | + } | |
| 39 | 53 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java
| ... | ... | @@ -37,6 +37,9 @@ public interface BpmProcessInstanceConvert { |
| 37 | 37 | |
| 38 | 38 | List<BpmProcessInstancePageItemRespVO> convertList(List<BpmProcessInstanceExtDO> list); |
| 39 | 39 | |
| 40 | + @Mapping(source = "processInstanceId", target = "id") | |
| 41 | + BpmProcessInstancePageItemRespVO convert(BpmProcessInstanceExtDO bean); | |
| 42 | + | |
| 40 | 43 | List<BpmProcessInstancePageItemRespVO.Task> convertList2(List<Task> tasks); |
| 41 | 44 | |
| 42 | 45 | default BpmProcessInstanceRespVO convert2(HistoricProcessInstance processInstance, BpmProcessInstanceExtDO processInstanceExt, | ... | ... |
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java
| ... | ... | @@ -62,6 +62,45 @@ public interface BpmTaskConvert { |
| 62 | 62 | }) |
| 63 | 63 | BpmTaskTodoPageItemRespVO.ProcessInstance convert(ProcessInstance processInstance, AdminUserRespDTO startUser); |
| 64 | 64 | |
| 65 | + default List<BpmTaskRespVO> convertList3(List<HistoricTaskInstance> tasks, Map<String, BpmTaskExtDO> bpmTaskExtDOMap, | |
| 66 | + HistoricProcessInstance processInstance, Map<Long, AdminUserRespDTO> userMap, | |
| 67 | + Map<Long, DeptRespDTO> deptMap) { | |
| 68 | + return CollectionUtils.convertList(tasks, task -> { | |
| 69 | + BpmTaskRespVO respVO = convert3(task); | |
| 70 | + BpmTaskExtDO taskExtDO = bpmTaskExtDOMap.get(task.getId()); | |
| 71 | + copyTo(taskExtDO, respVO); | |
| 72 | + if (processInstance != null) { | |
| 73 | + AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId())); | |
| 74 | + respVO.setProcessInstance(convert(processInstance, startUser)); | |
| 75 | + } | |
| 76 | + AdminUserRespDTO assignUser = userMap.get(NumberUtils.parseLong(task.getAssignee())); | |
| 77 | + if (assignUser != null) { | |
| 78 | + respVO.setAssigneeUser(convert3(assignUser)); | |
| 79 | + DeptRespDTO dept = deptMap.get(assignUser.getDeptId()); | |
| 80 | + if (dept != null) { | |
| 81 | + respVO.getAssigneeUser().setDeptName(dept.getName()); | |
| 82 | + } | |
| 83 | + } | |
| 84 | + return respVO; | |
| 85 | + }); | |
| 86 | + } | |
| 87 | + | |
| 88 | + @Mapping(source = "taskDefinitionKey", target = "definitionKey") | |
| 89 | + BpmTaskRespVO convert3(HistoricTaskInstance bean); | |
| 90 | + | |
| 91 | + BpmTaskRespVO.User convert3(AdminUserRespDTO bean); | |
| 92 | + | |
| 93 | + void copyTo(BpmTaskExtDO from, @MappingTarget BpmTaskDonePageItemRespVO to); | |
| 94 | + | |
| 95 | + @Mappings({ | |
| 96 | + @Mapping(source = "processInstance.id", target = "id"), | |
| 97 | + @Mapping(source = "processInstance.name", target = "name"), | |
| 98 | + @Mapping(source = "processInstance.startUserId", target = "startUserId"), | |
| 99 | + @Mapping(source = "processInstance.processDefinitionId", target = "processDefinitionId"), | |
| 100 | + @Mapping(source = "startUser.nickname", target = "startUserNickname") | |
| 101 | + }) | |
| 102 | + BpmTaskTodoPageItemRespVO.ProcessInstance convert(HistoricProcessInstance processInstance, AdminUserRespDTO startUser); | |
| 103 | + | |
| 65 | 104 | |
| 66 | 105 | } |
| 67 | 106 | ... | ... |
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/config/BpmFlowableConfiguration.java
| 1 | 1 | package cn.iocoder.yudao.module.bpm.framework.flowable.config; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.BpmActivityBehaviorFactory; | |
| 3 | 4 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmProcessInstanceEventListener; |
| 5 | +import cn.iocoder.yudao.module.bpm.service.definition.BpmTaskAssignRuleService; | |
| 6 | +import cn.iocoder.yudao.module.bpm.service.definition.BpmUserGroupService; | |
| 7 | +import cn.iocoder.yudao.module.system.api.dept.DeptApi; | |
| 8 | +import cn.iocoder.yudao.module.system.api.permission.PermissionApi; | |
| 9 | +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; | |
| 4 | 10 | import org.flowable.common.engine.api.delegate.event.FlowableEventListener; |
| 5 | 11 | import org.flowable.engine.runtime.ProcessInstance; |
| 6 | 12 | import org.flowable.spring.SpringProcessEngineConfiguration; |
| ... | ... | @@ -19,15 +25,35 @@ import java.util.List; |
| 19 | 25 | public class BpmFlowableConfiguration { |
| 20 | 26 | |
| 21 | 27 | /** |
| 22 | - *将自定义 listener 加入全局listener | |
| 28 | + * 将自定义 listener 加入全局listener | |
| 23 | 29 | * @param processInstanceListener 自定义监听 {@link ProcessInstance} |
| 24 | 30 | */ |
| 25 | 31 | @Bean |
| 26 | - public EngineConfigurationConfigurer<SpringProcessEngineConfiguration> addCustomerListenerConfigurer (BpmProcessInstanceEventListener processInstanceListener) { | |
| 32 | + public EngineConfigurationConfigurer<SpringProcessEngineConfiguration> addCustomerListenerConfigurer (BpmProcessInstanceEventListener processInstanceListener, | |
| 33 | + BpmActivityBehaviorFactory bpmActivityBehaviorFactory) { | |
| 27 | 34 | return engineConfiguration -> { |
| 28 | 35 | List<FlowableEventListener> eventListeners = new ArrayList<>(); |
| 29 | 36 | eventListeners.add(processInstanceListener); |
| 30 | 37 | engineConfiguration.setEventListeners(eventListeners); |
| 38 | + engineConfiguration.setActivityBehaviorFactory(bpmActivityBehaviorFactory); | |
| 31 | 39 | }; |
| 32 | 40 | } |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + @Bean | |
| 45 | + public BpmActivityBehaviorFactory bpmActivityBehaviorFactory(BpmTaskAssignRuleService taskRuleService, | |
| 46 | + BpmUserGroupService userGroupService, | |
| 47 | + PermissionApi permissionApi, | |
| 48 | + DeptApi deptApi, | |
| 49 | + AdminUserApi adminUserApi) { | |
| 50 | + BpmActivityBehaviorFactory bpmActivityBehaviorFactory = new BpmActivityBehaviorFactory(); | |
| 51 | + bpmActivityBehaviorFactory.setBpmTaskRuleService(taskRuleService); | |
| 52 | + bpmActivityBehaviorFactory.setUserGroupService(userGroupService); | |
| 53 | + bpmActivityBehaviorFactory.setAdminUserApi(adminUserApi); | |
| 54 | + bpmActivityBehaviorFactory.setPermissionApi(permissionApi); | |
| 55 | + bpmActivityBehaviorFactory.setDeptApi(deptApi); | |
| 56 | +// bpmActivityBehaviorFactory.setScripts(scripts); | |
| 57 | + return bpmActivityBehaviorFactory; | |
| 58 | + } | |
| 33 | 59 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmActivityBehaviorFactory.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.module.bpm.service.definition.BpmTaskAssignRuleService; | |
| 4 | +import cn.iocoder.yudao.module.bpm.service.definition.BpmUserGroupService; | |
| 5 | +import cn.iocoder.yudao.module.system.api.dept.DeptApi; | |
| 6 | +import cn.iocoder.yudao.module.system.api.permission.PermissionApi; | |
| 7 | +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; | |
| 8 | +import lombok.Data; | |
| 9 | +import lombok.EqualsAndHashCode; | |
| 10 | +import lombok.Setter; | |
| 11 | +import lombok.ToString; | |
| 12 | +import org.flowable.bpmn.model.UserTask; | |
| 13 | +import org.flowable.engine.impl.bpmn.behavior.UserTaskActivityBehavior; | |
| 14 | +import org.flowable.engine.impl.bpmn.parser.factory.DefaultActivityBehaviorFactory; | |
| 15 | + | |
| 16 | +/** | |
| 17 | + * 自定义的 ActivityBehaviorFactory 实现类,目的如下: | |
| 18 | + * 1. 自定义 {@link #createUserTaskActivityBehavior(UserTask)}:实现自定义的流程任务的 assignee 负责人的分配 | |
| 19 | + * | |
| 20 | + * @author 芋道源码 | |
| 21 | + */ | |
| 22 | +@Data | |
| 23 | +@EqualsAndHashCode(callSuper = true) | |
| 24 | +@ToString(callSuper = true) | |
| 25 | +public class BpmActivityBehaviorFactory extends DefaultActivityBehaviorFactory { | |
| 26 | + | |
| 27 | + @Setter | |
| 28 | + private BpmTaskAssignRuleService bpmTaskRuleService; | |
| 29 | + @Setter | |
| 30 | + private BpmUserGroupService userGroupService; | |
| 31 | + | |
| 32 | + @Setter | |
| 33 | + private PermissionApi permissionApi; | |
| 34 | + @Setter | |
| 35 | + private DeptApi deptApi; | |
| 36 | + @Setter | |
| 37 | + private AdminUserApi adminUserApi; | |
| 38 | + | |
| 39 | + @Override | |
| 40 | + public UserTaskActivityBehavior createUserTaskActivityBehavior(UserTask userTask) { | |
| 41 | + BpmUserTaskActivityBehavior userTaskActivityBehavior = new BpmUserTaskActivityBehavior(userTask); | |
| 42 | + userTaskActivityBehavior.setBpmTaskRuleService(bpmTaskRuleService); | |
| 43 | + userTaskActivityBehavior.setPermissionApi(permissionApi); | |
| 44 | + userTaskActivityBehavior.setDeptApi(deptApi); | |
| 45 | + userTaskActivityBehavior.setUserGroupService(userGroupService); | |
| 46 | + userTaskActivityBehavior.setAdminUserApi(adminUserApi); | |
| 47 | + //TODO | |
| 48 | + //userTaskActivityBehavior.setScripts(scripts); | |
| 49 | + return userTaskActivityBehavior; | |
| 50 | + } | |
| 51 | +} | ... | ... |
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmUserTaskActivityBehavior.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior; | |
| 2 | + | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import cn.hutool.core.util.RandomUtil; | |
| 5 | +import cn.hutool.core.util.StrUtil; | |
| 6 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | |
| 7 | +import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO; | |
| 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.service.definition.BpmTaskAssignRuleService; | |
| 11 | +import cn.iocoder.yudao.module.bpm.service.definition.BpmUserGroupService; | |
| 12 | +import cn.iocoder.yudao.module.system.api.dept.DeptApi; | |
| 13 | +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; | |
| 14 | +import cn.iocoder.yudao.module.system.api.permission.PermissionApi; | |
| 15 | +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; | |
| 16 | +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; | |
| 17 | +import com.google.common.annotations.VisibleForTesting; | |
| 18 | +import lombok.Setter; | |
| 19 | +import lombok.extern.slf4j.Slf4j; | |
| 20 | +import org.flowable.bpmn.model.UserTask; | |
| 21 | +import org.flowable.common.engine.api.FlowableException; | |
| 22 | +import org.flowable.common.engine.impl.el.ExpressionManager; | |
| 23 | +import org.flowable.engine.delegate.DelegateExecution; | |
| 24 | +import org.flowable.engine.impl.bpmn.behavior.UserTaskActivityBehavior; | |
| 25 | +import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; | |
| 26 | +import org.flowable.engine.impl.util.TaskHelper; | |
| 27 | +import org.flowable.task.service.TaskService; | |
| 28 | +import org.flowable.task.service.impl.persistence.entity.TaskEntity; | |
| 29 | + | |
| 30 | +import java.util.*; | |
| 31 | + | |
| 32 | +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | |
| 33 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | |
| 34 | +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; | |
| 35 | +import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.TASK_CREATE_FAIL_NO_CANDIDATE_USER; | |
| 36 | + | |
| 37 | +/** | |
| 38 | + * 自定义的流程任务的 assignee 负责人的分配 | |
| 39 | + * 第一步,获得对应的分配规则; | |
| 40 | + * 第二步,根据分配规则,计算出分配任务的候选人。如果找不到,则直接报业务异常,不继续执行后续的流程; | |
| 41 | + * 第三步,随机选择一个候选人,则选择作为 assignee 负责人。 | |
| 42 | + * | |
| 43 | + * @author 芋道源码 | |
| 44 | + */ | |
| 45 | +@Slf4j | |
| 46 | +public class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior { | |
| 47 | + | |
| 48 | + @Setter | |
| 49 | + private BpmTaskAssignRuleService bpmTaskRuleService; | |
| 50 | + @Setter | |
| 51 | + private BpmUserGroupService userGroupService; | |
| 52 | + @Setter | |
| 53 | + private DeptApi deptApi; | |
| 54 | + @Setter | |
| 55 | + private AdminUserApi adminUserApi; | |
| 56 | + @Setter | |
| 57 | + private PermissionApi permissionApi; | |
| 58 | + | |
| 59 | + public BpmUserTaskActivityBehavior(UserTask userTask) { | |
| 60 | + super(userTask); | |
| 61 | + } | |
| 62 | + | |
| 63 | + @Override | |
| 64 | + protected void handleAssignments(TaskService taskService, String assignee, String owner, List<String> candidateUsers, List<String> candidateGroups, TaskEntity task, ExpressionManager expressionManager, DelegateExecution execution, ProcessEngineConfigurationImpl processEngineConfiguration) { | |
| 65 | + // 第一步,获得任务的规则 | |
| 66 | + BpmTaskAssignRuleDO rule = getTaskRule(task); | |
| 67 | + // 第二步,获得任务的候选用户们 | |
| 68 | + Set<Long> candidateUserIds = calculateTaskCandidateUsers(task, rule); | |
| 69 | + // 第三步,设置一个作为负责人 | |
| 70 | + Long assigneeUserId = chooseTaskAssignee(candidateUserIds); | |
| 71 | + TaskHelper.changeTaskAssignee(task, String.valueOf(assigneeUserId)); | |
| 72 | + } | |
| 73 | + | |
| 74 | + private BpmTaskAssignRuleDO getTaskRule(TaskEntity task) { | |
| 75 | + List<BpmTaskAssignRuleDO> taskRules = bpmTaskRuleService.getTaskAssignRuleListByProcessDefinitionId(task.getProcessDefinitionId(), | |
| 76 | + task.getTaskDefinitionKey()); | |
| 77 | + if (CollUtil.isEmpty(taskRules)) { | |
| 78 | + throw new FlowableException(StrUtil.format("流程任务({}/{}/{}) 找不到符合的任务规则", | |
| 79 | + task.getId(), task.getProcessDefinitionId(), task.getTaskDefinitionKey())); | |
| 80 | + } | |
| 81 | + if (taskRules.size() > 1) { | |
| 82 | + throw new FlowableException(StrUtil.format("流程任务({}/{}/{}) 找到过多任务规则({})", | |
| 83 | + task.getId(), task.getProcessDefinitionId(), task.getTaskDefinitionKey(), taskRules.size())); | |
| 84 | + } | |
| 85 | + return taskRules.get(0); | |
| 86 | + } | |
| 87 | + | |
| 88 | + @VisibleForTesting | |
| 89 | + Set<Long> calculateTaskCandidateUsers(TaskEntity task, BpmTaskAssignRuleDO rule) { | |
| 90 | + Set<Long> assigneeUserIds = null; | |
| 91 | + if (Objects.equals(BpmTaskAssignRuleTypeEnum.ROLE.getType(), rule.getType())) { | |
| 92 | + assigneeUserIds = calculateTaskCandidateUsersByRole(task, rule); | |
| 93 | + } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(), rule.getType())) { | |
| 94 | + assigneeUserIds = calculateTaskCandidateUsersByDeptMember(task, rule); | |
| 95 | + } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType(), rule.getType())) { | |
| 96 | + assigneeUserIds = calculateTaskCandidateUsersByDeptLeader(task, rule); | |
| 97 | + } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.POST.getType(), rule.getType())) { | |
| 98 | + assigneeUserIds = calculateTaskCandidateUsersByPost(task, rule); | |
| 99 | + } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER.getType(), rule.getType())) { | |
| 100 | + assigneeUserIds = calculateTaskCandidateUsersByUser(task, rule); | |
| 101 | + } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER_GROUP.getType(), rule.getType())) { | |
| 102 | + assigneeUserIds = calculateTaskCandidateUsersByUserGroup(task, rule); | |
| 103 | + } | |
| 104 | +// else if (Objects.equals(BpmTaskAssignRuleTypeEnum.SCRIPT.getType(), rule.getType())) { | |
| 105 | +// assigneeUserIds = calculateTaskCandidateUsersByScript(task, rule); | |
| 106 | +// } | |
| 107 | + | |
| 108 | + // 移除被禁用的用户 | |
| 109 | + removeDisableUsers(assigneeUserIds); | |
| 110 | + // 如果候选人为空,抛出异常 TODO 芋艿:没候选人的策略选择。1 - 挂起;2 - 直接结束;3 - 强制一个兜底人 | |
| 111 | + if (CollUtil.isEmpty(assigneeUserIds)) { | |
| 112 | + log.error("[calculateTaskCandidateUsers][流程任务({}/{}/{}) 任务规则({}) 找不到候选人]", | |
| 113 | + task.getId(), task.getProcessDefinitionId(), task.getTaskDefinitionKey(), toJsonString(rule)); | |
| 114 | + throw exception(TASK_CREATE_FAIL_NO_CANDIDATE_USER); | |
| 115 | + } | |
| 116 | + return assigneeUserIds; | |
| 117 | + } | |
| 118 | + | |
| 119 | + private Set<Long> calculateTaskCandidateUsersByRole(TaskEntity task, BpmTaskAssignRuleDO rule) { | |
| 120 | + return permissionApi.getUserRoleIdListByRoleIds(rule.getOptions()); | |
| 121 | + } | |
| 122 | + | |
| 123 | + private Set<Long> calculateTaskCandidateUsersByDeptMember(TaskEntity task, BpmTaskAssignRuleDO rule) { | |
| 124 | + List<AdminUserRespDTO> users = adminUserApi.getUsersByDeptIds(rule.getOptions()); | |
| 125 | + return convertSet(users, AdminUserRespDTO::getId); | |
| 126 | + } | |
| 127 | + | |
| 128 | + private Set<Long> calculateTaskCandidateUsersByDeptLeader(TaskEntity task, BpmTaskAssignRuleDO rule) { | |
| 129 | + List<DeptRespDTO> depts = deptApi.getDepts(rule.getOptions()); | |
| 130 | + return convertSet(depts, DeptRespDTO::getLeaderUserId); | |
| 131 | + } | |
| 132 | + | |
| 133 | + private Set<Long> calculateTaskCandidateUsersByPost(TaskEntity task, BpmTaskAssignRuleDO rule) { | |
| 134 | + List<AdminUserRespDTO> users = adminUserApi.getUsersByPostIds(rule.getOptions()); | |
| 135 | + return convertSet(users, AdminUserRespDTO::getId); | |
| 136 | + } | |
| 137 | + | |
| 138 | + private Set<Long> calculateTaskCandidateUsersByUser(TaskEntity task, BpmTaskAssignRuleDO rule) { | |
| 139 | + return rule.getOptions(); | |
| 140 | + } | |
| 141 | + | |
| 142 | + private Set<Long> calculateTaskCandidateUsersByUserGroup(TaskEntity task, BpmTaskAssignRuleDO rule) { | |
| 143 | + List<BpmUserGroupDO> userGroups = userGroupService.getUserGroupList(rule.getOptions()); | |
| 144 | + Set<Long> userIds = new HashSet<>(); | |
| 145 | + userGroups.forEach(group -> userIds.addAll(group.getMemberUserIds())); | |
| 146 | + return userIds; | |
| 147 | + } | |
| 148 | + | |
| 149 | + private Long chooseTaskAssignee(Set<Long> candidateUserIds) { | |
| 150 | + // TODO 芋艿:未来可以优化下,改成轮询的策略 | |
| 151 | + int index = RandomUtil.randomInt(candidateUserIds.size()); | |
| 152 | + return CollUtil.get(candidateUserIds, index); | |
| 153 | + } | |
| 154 | + | |
| 155 | + @VisibleForTesting | |
| 156 | + void removeDisableUsers(Set<Long> assigneeUserIds) { | |
| 157 | + if (CollUtil.isEmpty(assigneeUserIds)) { | |
| 158 | + return; | |
| 159 | + } | |
| 160 | + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(assigneeUserIds); | |
| 161 | + assigneeUserIds.removeIf(id -> { | |
| 162 | + AdminUserRespDTO user = userMap.get(id); | |
| 163 | + return user == null || !CommonStatusEnum.ENABLE.getStatus().equals(user.getStatus()); | |
| 164 | + }); | |
| 165 | + } | |
| 166 | +} | ... | ... |
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmTaskAssignRuleServiceImpl.java
| ... | ... | @@ -47,6 +47,7 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService{ |
| 47 | 47 | @Lazy // 解决循环依赖 |
| 48 | 48 | private BpmModelService modelService; |
| 49 | 49 | @Resource |
| 50 | + @Lazy // 解决循环依赖 | |
| 50 | 51 | private BpmProcessDefinitionService processDefinitionService; |
| 51 | 52 | @Resource |
| 52 | 53 | private BpmUserGroupService userGroupService; | ... | ... |
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java
| ... | ... | @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.bpm.service.task; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | 4 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 5 | +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO; | |
| 5 | 6 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskTodoPageItemRespVO; |
| 6 | 7 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskTodoPageReqVO; |
| 7 | 8 | import org.flowable.task.api.Task; |
| ... | ... | @@ -44,4 +45,12 @@ public interface BpmTaskService { |
| 44 | 45 | * @return 流程任务列表 |
| 45 | 46 | */ |
| 46 | 47 | List<Task> getTasksByProcessInstanceIds(List<String> processInstanceIds); |
| 48 | + | |
| 49 | + /** | |
| 50 | + * 获得指令流程实例的流程任务列表,包括所有状态的 | |
| 51 | + * | |
| 52 | + * @param processInstanceId 流程实例的编号 | |
| 53 | + * @return 流程任务列表 | |
| 54 | + */ | |
| 55 | + List<BpmTaskRespVO> getTaskListByProcessInstanceId(String processInstanceId); | |
| 47 | 56 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
| ... | ... | @@ -3,24 +3,35 @@ package cn.iocoder.yudao.module.bpm.service.task; |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.hutool.core.util.StrUtil; |
| 5 | 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | +import cn.iocoder.yudao.framework.common.util.number.NumberUtils; | |
| 6 | 7 | import cn.iocoder.yudao.framework.common.util.object.PageUtils; |
| 8 | +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO; | |
| 7 | 9 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskTodoPageItemRespVO; |
| 8 | 10 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskTodoPageReqVO; |
| 9 | 11 | import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert; |
| 12 | +import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO; | |
| 13 | +import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmTaskExtMapper; | |
| 14 | +import cn.iocoder.yudao.module.system.api.dept.DeptApi; | |
| 15 | +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; | |
| 10 | 16 | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; |
| 11 | 17 | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; |
| 12 | 18 | import lombok.extern.slf4j.Slf4j; |
| 19 | +import org.flowable.engine.HistoryService; | |
| 13 | 20 | import org.flowable.engine.TaskService; |
| 21 | +import org.flowable.engine.history.HistoricProcessInstance; | |
| 14 | 22 | import org.flowable.engine.runtime.ProcessInstance; |
| 15 | 23 | import org.flowable.task.api.Task; |
| 16 | 24 | import org.flowable.task.api.TaskQuery; |
| 25 | +import org.flowable.task.api.history.HistoricTaskInstance; | |
| 17 | 26 | import org.springframework.stereotype.Service; |
| 18 | 27 | |
| 19 | 28 | import javax.annotation.Resource; |
| 20 | 29 | import java.util.Collections; |
| 21 | 30 | import java.util.List; |
| 22 | 31 | import java.util.Map; |
| 32 | +import java.util.Set; | |
| 23 | 33 | |
| 34 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; | |
| 24 | 35 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; |
| 25 | 36 | |
| 26 | 37 | /** |
| ... | ... | @@ -36,9 +47,16 @@ public class BpmTaskServiceImpl implements BpmTaskService{ |
| 36 | 47 | @Resource |
| 37 | 48 | private TaskService taskService; |
| 38 | 49 | @Resource |
| 39 | - private AdminUserApi adminUserApi; | |
| 50 | + private HistoryService historyService; | |
| 51 | + | |
| 40 | 52 | @Resource |
| 41 | 53 | private BpmProcessInstanceService processInstanceService; |
| 54 | + @Resource | |
| 55 | + private AdminUserApi adminUserApi; | |
| 56 | + @Resource | |
| 57 | + private DeptApi deptApi; | |
| 58 | + @Resource | |
| 59 | + private BpmTaskExtMapper taskExtMapper; | |
| 42 | 60 | |
| 43 | 61 | @Override |
| 44 | 62 | public PageResult<BpmTaskTodoPageItemRespVO> getTodoTaskPage(Long userId, BpmTaskTodoPageReqVO pageVO) { |
| ... | ... | @@ -79,4 +97,31 @@ public class BpmTaskServiceImpl implements BpmTaskService{ |
| 79 | 97 | } |
| 80 | 98 | return taskService.createTaskQuery().processInstanceIdIn(processInstanceIds).list(); |
| 81 | 99 | } |
| 100 | + | |
| 101 | + @Override | |
| 102 | + public List<BpmTaskRespVO> getTaskListByProcessInstanceId(String processInstanceId) { | |
| 103 | + // 获得任务列表 | |
| 104 | + List<HistoricTaskInstance> tasks = historyService.createHistoricTaskInstanceQuery() | |
| 105 | + .processInstanceId(processInstanceId) | |
| 106 | + .orderByHistoricTaskInstanceStartTime().desc() // 创建时间倒序 | |
| 107 | + .list(); | |
| 108 | + if (CollUtil.isEmpty(tasks)) { | |
| 109 | + return Collections.emptyList(); | |
| 110 | + } | |
| 111 | + | |
| 112 | + // 获得 TaskExtDO Map | |
| 113 | + List<BpmTaskExtDO> bpmTaskExtDOs = taskExtMapper.selectListByTaskIds(convertSet(tasks, HistoricTaskInstance::getId)); | |
| 114 | + Map<String, BpmTaskExtDO> bpmTaskExtDOMap = convertMap(bpmTaskExtDOs, BpmTaskExtDO::getTaskId); | |
| 115 | + // 获得 ProcessInstance Map | |
| 116 | + HistoricProcessInstance processInstance = processInstanceService.getHistoricProcessInstance(processInstanceId); | |
| 117 | + // 获得 User Map | |
| 118 | + Set<Long> userIds = convertSet(tasks, task -> NumberUtils.parseLong(task.getAssignee())); | |
| 119 | + userIds.add(NumberUtils.parseLong(processInstance.getStartUserId())); | |
| 120 | + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds); | |
| 121 | + // 获得 Dept Map | |
| 122 | + Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); | |
| 123 | + | |
| 124 | + // 拼接数据 | |
| 125 | + return BpmTaskConvert.INSTANCE.convertList3(tasks, bpmTaskExtDOMap, processInstance, userMap, deptMap); | |
| 126 | + } | |
| 82 | 127 | } | ... | ... |