Просмотр исходного кода

工作流 Flowable 发起流程, 用户任务相关实现

jason 3 лет назад
Родитель
Сommit
d6775a5619
12 измененных файлов с 362 добавлено и 8 удалено
  1. 1 1
      yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/framework/activiti/core/behavior/BpmActivityBehaviorFactory.java
  2. 2 2
      yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/framework/activiti/core/behavior/BpmUserTaskActivityBehavior.java
  3. 2 2
      yudao-module-bpm/yudao-module-bpm-impl-activiti/src/test/java/cn/iocoder/yudao/module/bpm/framework/activiti/core/behavior/BpmUserTaskActivityBehaviorTest.java
  4. 14 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java
  5. 3 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java
  6. 39 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java
  7. 28 2
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/config/BpmFlowableConfiguration.java
  8. 51 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmActivityBehaviorFactory.java
  9. 166 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmUserTaskActivityBehavior.java
  10. 1 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmTaskAssignRuleServiceImpl.java
  11. 9 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java
  12. 46 1
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java

+ 1 - 1
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 {
 
     @Override
     public UserTaskActivityBehavior createUserTaskActivityBehavior(UserTask userTask) {
-        BpmUserTaskActivitiBehavior userTaskActivityBehavior = new BpmUserTaskActivitiBehavior(userTask);
+        BpmUserTaskActivityBehavior userTaskActivityBehavior = new BpmUserTaskActivityBehavior(userTask);
         userTaskActivityBehavior.setBpmTaskRuleService(bpmTaskRuleService);
         userTaskActivityBehavior.setPermissionApi(permissionApi);
         userTaskActivityBehavior.setDeptApi(deptApi);

+ 2 - 2
yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/framework/activiti/core/behavior/BpmUserTaskActivitiBehavior.java → 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
  * @author 芋道源码
  */
 @Slf4j
-public class BpmUserTaskActivitiBehavior extends UserTaskActivityBehavior {
+public class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior {
 
     @Setter
     private BpmTaskAssignRuleService bpmTaskRuleService;
@@ -64,7 +64,7 @@ public class BpmUserTaskActivitiBehavior extends UserTaskActivityBehavior {
      */
     private Map<Long, BpmTaskAssignScript> scriptMap = Collections.emptyMap();
 
-    public BpmUserTaskActivitiBehavior(UserTask userTask) {
+    public BpmUserTaskActivityBehavior(UserTask userTask) {
         super(userTask);
     }
 

+ 2 - 2
yudao-module-bpm/yudao-module-bpm-impl-activiti/src/test/java/cn/iocoder/yudao/module/bpm/framework/activiti/core/behavior/BpmUserTaskActivitiBehaviorTest.java → 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;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.when;
 
-public class BpmUserTaskActivitiBehaviorTest extends BaseMockitoUnitTest {
+public class BpmUserTaskActivityBehaviorTest extends BaseMockitoUnitTest {
 
     @InjectMocks
-    private BpmUserTaskActivitiBehavior behavior;
+    private BpmUserTaskActivityBehavior behavior;
     @Mock
     private BpmTaskAssignRuleService bpmTaskRuleService;
     @Mock

+ 14 - 0
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;
 
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskTodoPageItemRespVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskTodoPageReqVO;
 import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;
 import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
 import io.swagger.annotations.ApiOperation;
 import org.flowable.bpmn.model.BpmnModel;
 import org.flowable.engine.TaskService;
@@ -13,11 +15,14 @@ import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
 import javax.annotation.Resource;
 import javax.validation.Valid;
 
+import java.util.List;
+
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
 import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
 
@@ -36,4 +41,13 @@ public class BpmTaskController {
     public CommonResult<PageResult<BpmTaskTodoPageItemRespVO>> getTodoTaskPage(@Valid BpmTaskTodoPageReqVO pageVO) {
         return success(taskService.getTodoTaskPage(getLoginUserId(), pageVO));
     }
+
+    @GetMapping("/list-by-process-instance-id")
+    @ApiOperation(value = "获得指定流程实例的任务列表", notes = "包括完成的、未完成的")
+    @ApiImplicitParam(name = "processInstanceId", value = "流程实例的编号", required = true, dataTypeClass = String.class)
+    @PreAuthorize("@ss.hasPermission('bpm:task:query')")
+    public CommonResult<List<BpmTaskRespVO>> getTaskListByProcessInstanceId(
+            @RequestParam("processInstanceId") String processInstanceId) {
+        return success(taskService.getTaskListByProcessInstanceId(processInstanceId));
+    }
 }

+ 3 - 0
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 {
 
     List<BpmProcessInstancePageItemRespVO> convertList(List<BpmProcessInstanceExtDO> list);
 
+    @Mapping(source = "processInstanceId", target = "id")
+    BpmProcessInstancePageItemRespVO convert(BpmProcessInstanceExtDO bean);
+
     List<BpmProcessInstancePageItemRespVO.Task> convertList2(List<Task> tasks);
 
     default BpmProcessInstanceRespVO convert2(HistoricProcessInstance processInstance, BpmProcessInstanceExtDO processInstanceExt,

+ 39 - 0
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 {
     })
     BpmTaskTodoPageItemRespVO.ProcessInstance convert(ProcessInstance processInstance, AdminUserRespDTO startUser);
 
+    default List<BpmTaskRespVO> convertList3(List<HistoricTaskInstance> tasks, Map<String, BpmTaskExtDO> bpmTaskExtDOMap,
+                                             HistoricProcessInstance processInstance, Map<Long, AdminUserRespDTO> userMap,
+                                             Map<Long, DeptRespDTO> deptMap) {
+        return CollectionUtils.convertList(tasks, task -> {
+            BpmTaskRespVO respVO = convert3(task);
+            BpmTaskExtDO taskExtDO = bpmTaskExtDOMap.get(task.getId());
+            copyTo(taskExtDO, respVO);
+            if (processInstance != null) {
+                AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId()));
+                respVO.setProcessInstance(convert(processInstance, startUser));
+            }
+            AdminUserRespDTO assignUser = userMap.get(NumberUtils.parseLong(task.getAssignee()));
+            if (assignUser != null) {
+                respVO.setAssigneeUser(convert3(assignUser));
+                DeptRespDTO dept = deptMap.get(assignUser.getDeptId());
+                if (dept != null) {
+                    respVO.getAssigneeUser().setDeptName(dept.getName());
+                }
+            }
+            return respVO;
+        });
+    }
+
+    @Mapping(source = "taskDefinitionKey", target = "definitionKey")
+    BpmTaskRespVO convert3(HistoricTaskInstance bean);
+
+    BpmTaskRespVO.User convert3(AdminUserRespDTO bean);
+
+    void copyTo(BpmTaskExtDO from, @MappingTarget BpmTaskDonePageItemRespVO to);
+
+    @Mappings({
+            @Mapping(source = "processInstance.id", target = "id"),
+            @Mapping(source = "processInstance.name", target = "name"),
+            @Mapping(source = "processInstance.startUserId", target = "startUserId"),
+            @Mapping(source = "processInstance.processDefinitionId", target = "processDefinitionId"),
+            @Mapping(source = "startUser.nickname", target = "startUserNickname")
+    })
+    BpmTaskTodoPageItemRespVO.ProcessInstance convert(HistoricProcessInstance processInstance, AdminUserRespDTO startUser);
+
 
 }
 

+ 28 - 2
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/config/BpmFlowableConfiguration.java

@@ -1,6 +1,12 @@
 package cn.iocoder.yudao.module.bpm.framework.flowable.config;
 
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.BpmActivityBehaviorFactory;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmProcessInstanceEventListener;
+import cn.iocoder.yudao.module.bpm.service.definition.BpmTaskAssignRuleService;
+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.permission.PermissionApi;
+import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
 import org.flowable.common.engine.api.delegate.event.FlowableEventListener;
 import org.flowable.engine.runtime.ProcessInstance;
 import org.flowable.spring.SpringProcessEngineConfiguration;
@@ -19,15 +25,35 @@ import java.util.List;
 public class BpmFlowableConfiguration {
 
     /**
-     *将自定义 listener 加入全局listener
+     * 将自定义 listener 加入全局listener
      * @param processInstanceListener 自定义监听 {@link ProcessInstance}
      */
     @Bean
-    public EngineConfigurationConfigurer<SpringProcessEngineConfiguration> addCustomerListenerConfigurer (BpmProcessInstanceEventListener processInstanceListener) {
+    public EngineConfigurationConfigurer<SpringProcessEngineConfiguration> addCustomerListenerConfigurer (BpmProcessInstanceEventListener processInstanceListener,
+                                                                                                          BpmActivityBehaviorFactory bpmActivityBehaviorFactory) {
         return engineConfiguration -> {
             List<FlowableEventListener> eventListeners = new ArrayList<>();
             eventListeners.add(processInstanceListener);
             engineConfiguration.setEventListeners(eventListeners);
+            engineConfiguration.setActivityBehaviorFactory(bpmActivityBehaviorFactory);
         };
     }
+
+
+
+    @Bean
+    public BpmActivityBehaviorFactory bpmActivityBehaviorFactory(BpmTaskAssignRuleService taskRuleService,
+                                                                 BpmUserGroupService userGroupService,
+                                                                 PermissionApi permissionApi,
+                                                                 DeptApi deptApi,
+                                                                 AdminUserApi adminUserApi) {
+        BpmActivityBehaviorFactory bpmActivityBehaviorFactory = new BpmActivityBehaviorFactory();
+        bpmActivityBehaviorFactory.setBpmTaskRuleService(taskRuleService);
+        bpmActivityBehaviorFactory.setUserGroupService(userGroupService);
+        bpmActivityBehaviorFactory.setAdminUserApi(adminUserApi);
+        bpmActivityBehaviorFactory.setPermissionApi(permissionApi);
+        bpmActivityBehaviorFactory.setDeptApi(deptApi);
+//        bpmActivityBehaviorFactory.setScripts(scripts);
+        return bpmActivityBehaviorFactory;
+    }
 }

+ 51 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmActivityBehaviorFactory.java

@@ -0,0 +1,51 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior;
+
+import cn.iocoder.yudao.module.bpm.service.definition.BpmTaskAssignRuleService;
+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.permission.PermissionApi;
+import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.Setter;
+import lombok.ToString;
+import org.flowable.bpmn.model.UserTask;
+import org.flowable.engine.impl.bpmn.behavior.UserTaskActivityBehavior;
+import org.flowable.engine.impl.bpmn.parser.factory.DefaultActivityBehaviorFactory;
+
+/**
+ * 自定义的 ActivityBehaviorFactory 实现类,目的如下:
+ * 1. 自定义 {@link #createUserTaskActivityBehavior(UserTask)}:实现自定义的流程任务的 assignee 负责人的分配
+ *
+ * @author 芋道源码
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class BpmActivityBehaviorFactory extends DefaultActivityBehaviorFactory {
+
+    @Setter
+    private BpmTaskAssignRuleService bpmTaskRuleService;
+    @Setter
+    private BpmUserGroupService userGroupService;
+
+    @Setter
+    private PermissionApi permissionApi;
+    @Setter
+    private DeptApi deptApi;
+    @Setter
+    private AdminUserApi adminUserApi;
+
+    @Override
+    public UserTaskActivityBehavior createUserTaskActivityBehavior(UserTask userTask) {
+        BpmUserTaskActivityBehavior userTaskActivityBehavior = new BpmUserTaskActivityBehavior(userTask);
+        userTaskActivityBehavior.setBpmTaskRuleService(bpmTaskRuleService);
+        userTaskActivityBehavior.setPermissionApi(permissionApi);
+        userTaskActivityBehavior.setDeptApi(deptApi);
+        userTaskActivityBehavior.setUserGroupService(userGroupService);
+        userTaskActivityBehavior.setAdminUserApi(adminUserApi);
+        //TODO
+        //userTaskActivityBehavior.setScripts(scripts);
+        return userTaskActivityBehavior;
+    }
+}

+ 166 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmUserTaskActivityBehavior.java

@@ -0,0 +1,166 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO;
+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.definition.BpmTaskAssignRuleService;
+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.dto.DeptRespDTO;
+import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
+import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
+import com.google.common.annotations.VisibleForTesting;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+import org.flowable.bpmn.model.UserTask;
+import org.flowable.common.engine.api.FlowableException;
+import org.flowable.common.engine.impl.el.ExpressionManager;
+import org.flowable.engine.delegate.DelegateExecution;
+import org.flowable.engine.impl.bpmn.behavior.UserTaskActivityBehavior;
+import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
+import org.flowable.engine.impl.util.TaskHelper;
+import org.flowable.task.service.TaskService;
+import org.flowable.task.service.impl.persistence.entity.TaskEntity;
+
+import java.util.*;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
+import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
+import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.TASK_CREATE_FAIL_NO_CANDIDATE_USER;
+
+/**
+ * 自定义的流程任务的 assignee 负责人的分配
+ * 第一步,获得对应的分配规则;
+ * 第二步,根据分配规则,计算出分配任务的候选人。如果找不到,则直接报业务异常,不继续执行后续的流程;
+ * 第三步,随机选择一个候选人,则选择作为 assignee 负责人。
+ *
+ * @author 芋道源码
+ */
+@Slf4j
+public class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior {
+
+    @Setter
+    private BpmTaskAssignRuleService bpmTaskRuleService;
+    @Setter
+    private BpmUserGroupService userGroupService;
+    @Setter
+    private DeptApi deptApi;
+    @Setter
+    private AdminUserApi adminUserApi;
+    @Setter
+    private PermissionApi permissionApi;
+
+    public BpmUserTaskActivityBehavior(UserTask userTask) {
+        super(userTask);
+    }
+
+    @Override
+    protected void handleAssignments(TaskService taskService, String assignee, String owner, List<String> candidateUsers, List<String> candidateGroups, TaskEntity task, ExpressionManager expressionManager, DelegateExecution execution, ProcessEngineConfigurationImpl processEngineConfiguration) {
+        // 第一步,获得任务的规则
+        BpmTaskAssignRuleDO rule = getTaskRule(task);
+        // 第二步,获得任务的候选用户们
+        Set<Long> candidateUserIds = calculateTaskCandidateUsers(task, rule);
+        // 第三步,设置一个作为负责人
+        Long assigneeUserId = chooseTaskAssignee(candidateUserIds);
+        TaskHelper.changeTaskAssignee(task, String.valueOf(assigneeUserId));
+    }
+
+    private BpmTaskAssignRuleDO getTaskRule(TaskEntity task) {
+        List<BpmTaskAssignRuleDO> taskRules = bpmTaskRuleService.getTaskAssignRuleListByProcessDefinitionId(task.getProcessDefinitionId(),
+                task.getTaskDefinitionKey());
+        if (CollUtil.isEmpty(taskRules)) {
+            throw new FlowableException(StrUtil.format("流程任务({}/{}/{}) 找不到符合的任务规则",
+                    task.getId(), task.getProcessDefinitionId(), task.getTaskDefinitionKey()));
+        }
+        if (taskRules.size() > 1) {
+            throw new FlowableException(StrUtil.format("流程任务({}/{}/{}) 找到过多任务规则({})",
+                    task.getId(), task.getProcessDefinitionId(), task.getTaskDefinitionKey(), taskRules.size()));
+        }
+        return taskRules.get(0);
+    }
+
+    @VisibleForTesting
+    Set<Long> calculateTaskCandidateUsers(TaskEntity task, BpmTaskAssignRuleDO rule) {
+        Set<Long> assigneeUserIds = null;
+        if (Objects.equals(BpmTaskAssignRuleTypeEnum.ROLE.getType(), rule.getType())) {
+            assigneeUserIds = calculateTaskCandidateUsersByRole(task, rule);
+        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(), rule.getType())) {
+            assigneeUserIds = calculateTaskCandidateUsersByDeptMember(task, rule);
+        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType(), rule.getType())) {
+            assigneeUserIds = calculateTaskCandidateUsersByDeptLeader(task, rule);
+        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.POST.getType(), rule.getType())) {
+            assigneeUserIds = calculateTaskCandidateUsersByPost(task, rule);
+        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER.getType(), rule.getType())) {
+            assigneeUserIds = calculateTaskCandidateUsersByUser(task, rule);
+        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER_GROUP.getType(), rule.getType())) {
+            assigneeUserIds = calculateTaskCandidateUsersByUserGroup(task, rule);
+        }
+//        else if (Objects.equals(BpmTaskAssignRuleTypeEnum.SCRIPT.getType(), rule.getType())) {
+//            assigneeUserIds = calculateTaskCandidateUsersByScript(task, rule);
+//        }
+
+        // 移除被禁用的用户
+        removeDisableUsers(assigneeUserIds);
+        // 如果候选人为空,抛出异常 TODO 芋艿:没候选人的策略选择。1 - 挂起;2 - 直接结束;3 - 强制一个兜底人
+        if (CollUtil.isEmpty(assigneeUserIds)) {
+            log.error("[calculateTaskCandidateUsers][流程任务({}/{}/{}) 任务规则({}) 找不到候选人]",
+                    task.getId(), task.getProcessDefinitionId(), task.getTaskDefinitionKey(), toJsonString(rule));
+            throw exception(TASK_CREATE_FAIL_NO_CANDIDATE_USER);
+        }
+        return assigneeUserIds;
+    }
+
+    private Set<Long> calculateTaskCandidateUsersByRole(TaskEntity task, BpmTaskAssignRuleDO rule) {
+        return permissionApi.getUserRoleIdListByRoleIds(rule.getOptions());
+    }
+
+    private Set<Long> calculateTaskCandidateUsersByDeptMember(TaskEntity task, BpmTaskAssignRuleDO rule) {
+        List<AdminUserRespDTO> users = adminUserApi.getUsersByDeptIds(rule.getOptions());
+        return convertSet(users, AdminUserRespDTO::getId);
+    }
+
+    private Set<Long> calculateTaskCandidateUsersByDeptLeader(TaskEntity task, BpmTaskAssignRuleDO rule) {
+        List<DeptRespDTO> depts = deptApi.getDepts(rule.getOptions());
+        return convertSet(depts, DeptRespDTO::getLeaderUserId);
+    }
+
+    private Set<Long> calculateTaskCandidateUsersByPost(TaskEntity task, BpmTaskAssignRuleDO rule) {
+        List<AdminUserRespDTO> users = adminUserApi.getUsersByPostIds(rule.getOptions());
+        return convertSet(users, AdminUserRespDTO::getId);
+    }
+
+    private Set<Long> calculateTaskCandidateUsersByUser(TaskEntity task, BpmTaskAssignRuleDO rule) {
+        return rule.getOptions();
+    }
+
+    private Set<Long> calculateTaskCandidateUsersByUserGroup(TaskEntity task, BpmTaskAssignRuleDO rule) {
+        List<BpmUserGroupDO> userGroups = userGroupService.getUserGroupList(rule.getOptions());
+        Set<Long> userIds = new HashSet<>();
+        userGroups.forEach(group -> userIds.addAll(group.getMemberUserIds()));
+        return userIds;
+    }
+
+    private Long chooseTaskAssignee(Set<Long> candidateUserIds) {
+        // TODO 芋艿:未来可以优化下,改成轮询的策略
+        int index = RandomUtil.randomInt(candidateUserIds.size());
+        return CollUtil.get(candidateUserIds, index);
+    }
+
+    @VisibleForTesting
+    void removeDisableUsers(Set<Long> assigneeUserIds) {
+        if (CollUtil.isEmpty(assigneeUserIds)) {
+            return;
+        }
+        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(assigneeUserIds);
+        assigneeUserIds.removeIf(id -> {
+            AdminUserRespDTO user = userMap.get(id);
+            return user == null || !CommonStatusEnum.ENABLE.getStatus().equals(user.getStatus());
+        });
+    }
+}

+ 1 - 0
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{
     @Lazy // 解决循环依赖
     private BpmModelService modelService;
     @Resource
+    @Lazy // 解决循环依赖
     private BpmProcessDefinitionService processDefinitionService;
     @Resource
     private BpmUserGroupService userGroupService;

+ 9 - 0
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;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
+import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskTodoPageItemRespVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskTodoPageReqVO;
 import org.flowable.task.api.Task;
@@ -44,4 +45,12 @@ public interface BpmTaskService {
      * @return 流程任务列表
      */
     List<Task> getTasksByProcessInstanceIds(List<String> processInstanceIds);
+
+    /**
+     * 获得指令流程实例的流程任务列表,包括所有状态的
+     *
+     * @param processInstanceId 流程实例的编号
+     * @return 流程任务列表
+     */
+    List<BpmTaskRespVO> getTaskListByProcessInstanceId(String processInstanceId);
 }

+ 46 - 1
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;
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
 import cn.iocoder.yudao.framework.common.util.object.PageUtils;
+import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskTodoPageItemRespVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskTodoPageReqVO;
 import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO;
+import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmTaskExtMapper;
+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 lombok.extern.slf4j.Slf4j;
+import org.flowable.engine.HistoryService;
 import org.flowable.engine.TaskService;
+import org.flowable.engine.history.HistoricProcessInstance;
 import org.flowable.engine.runtime.ProcessInstance;
 import org.flowable.task.api.Task;
 import org.flowable.task.api.TaskQuery;
+import org.flowable.task.api.history.HistoricTaskInstance;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
 
 /**
@@ -36,9 +47,16 @@ public class BpmTaskServiceImpl implements BpmTaskService{
     @Resource
     private TaskService taskService;
     @Resource
-    private AdminUserApi adminUserApi;
+    private HistoryService historyService;
+
     @Resource
     private BpmProcessInstanceService processInstanceService;
+    @Resource
+    private AdminUserApi adminUserApi;
+    @Resource
+    private DeptApi deptApi;
+    @Resource
+    private BpmTaskExtMapper taskExtMapper;
 
     @Override
     public PageResult<BpmTaskTodoPageItemRespVO> getTodoTaskPage(Long userId, BpmTaskTodoPageReqVO pageVO) {
@@ -79,4 +97,31 @@ public class BpmTaskServiceImpl implements BpmTaskService{
         }
         return taskService.createTaskQuery().processInstanceIdIn(processInstanceIds).list();
     }
+
+    @Override
+    public List<BpmTaskRespVO> getTaskListByProcessInstanceId(String processInstanceId) {
+        // 获得任务列表
+        List<HistoricTaskInstance> tasks = historyService.createHistoricTaskInstanceQuery()
+                .processInstanceId(processInstanceId)
+                .orderByHistoricTaskInstanceStartTime().desc() // 创建时间倒序
+                .list();
+        if (CollUtil.isEmpty(tasks)) {
+            return Collections.emptyList();
+        }
+
+        // 获得 TaskExtDO Map
+        List<BpmTaskExtDO> bpmTaskExtDOs = taskExtMapper.selectListByTaskIds(convertSet(tasks, HistoricTaskInstance::getId));
+        Map<String, BpmTaskExtDO> bpmTaskExtDOMap = convertMap(bpmTaskExtDOs, BpmTaskExtDO::getTaskId);
+        // 获得 ProcessInstance Map
+        HistoricProcessInstance processInstance = processInstanceService.getHistoricProcessInstance(processInstanceId);
+        // 获得 User Map
+        Set<Long> userIds = convertSet(tasks, task -> NumberUtils.parseLong(task.getAssignee()));
+        userIds.add(NumberUtils.parseLong(processInstance.getStartUserId()));
+        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
+        // 获得 Dept Map
+        Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
+
+        // 拼接数据
+        return BpmTaskConvert.INSTANCE.convertList3(tasks, bpmTaskExtDOMap, processInstance, userMap, deptMap);
+    }
 }