Browse Source

Merge branch 'master' into lc_saas

# Conflicts:
#	yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java
lichen 8 months ago
parent
commit
11b0628293
11 changed files with 545 additions and 141 deletions
  1. 13 65
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskAssignRuleController.java
  2. 34 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/script/BpmTaskAssignScript.java
  3. 70 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderAbstractScript.java
  4. 27 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderX1Script.java
  5. 27 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderX2Script.java
  6. 40 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/script/impl/BpmTaskAssignStartUserScript.java
  7. 40 29
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskAssignRuleService.java
  8. 275 47
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskAssignRuleServiceImpl.java
  9. 9 0
      yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApi.java
  10. 7 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApiImpl.java
  11. 3 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/user/UserConvert.java

+ 13 - 65
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskAssignRuleController.java

@@ -1,7 +1,8 @@
 package cn.iocoder.yudao.module.bpm.controller.admin.task;
 
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleCreateReqVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleRespVO;
-import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO;
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleUpdateReqVO;
 import io.swagger.v3.oas.annotations.Parameters;
 import org.springframework.web.bind.annotation.*;
 import javax.annotation.Resource;
@@ -12,23 +13,9 @@ import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.Operation;
 
 import javax.validation.*;
-import javax.servlet.http.*;
 import java.util.*;
-import java.io.IOException;
-
-import cn.iocoder.yudao.framework.common.pojo.PageParam;
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
-import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
-
-import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
-
-import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
-import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*;
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
-
-import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.*;
 import cn.iocoder.yudao.module.bpm.service.task.BpmTaskAssignRuleService;
 
 @Tag(name = "管理后台 - Bpm 任务规则")
@@ -40,39 +27,6 @@ public class BpmTaskAssignRuleController {
     @Resource
     private BpmTaskAssignRuleService taskAssignRuleService;
 
-    @PostMapping("/create")
-    @Operation(summary = "创建Bpm 任务规则")
-    @PreAuthorize("@ss.hasPermission('bpm:task-assign-rule:create')")
-    public CommonResult<Long> createTaskAssignRule(@Valid @RequestBody BpmTaskAssignRuleSaveReqVO createReqVO) {
-        return success(taskAssignRuleService.createTaskAssignRule(createReqVO));
-    }
-
-    @PutMapping("/update")
-    @Operation(summary = "更新Bpm 任务规则")
-    @PreAuthorize("@ss.hasPermission('bpm:task-assign-rule:update')")
-    public CommonResult<Boolean> updateTaskAssignRule(@Valid @RequestBody BpmTaskAssignRuleSaveReqVO updateReqVO) {
-        taskAssignRuleService.updateTaskAssignRule(updateReqVO);
-        return success(true);
-    }
-
-    @DeleteMapping("/delete")
-    @Operation(summary = "删除Bpm 任务规则")
-    @Parameter(name = "id", description = "编号", required = true)
-    @PreAuthorize("@ss.hasPermission('bpm:task-assign-rule:delete')")
-    public CommonResult<Boolean> deleteTaskAssignRule(@RequestParam("id") Long id) {
-        taskAssignRuleService.deleteTaskAssignRule(id);
-        return success(true);
-    }
-
-    @GetMapping("/get")
-    @Operation(summary = "获得Bpm 任务规则")
-    @Parameter(name = "id", description = "编号", required = true, example = "1024")
-    @PreAuthorize("@ss.hasPermission('bpm:task-assign-rule:query')")
-    public CommonResult<BpmTaskAssignRuleRespVO> getTaskAssignRule(@RequestParam("id") Long id) {
-        BpmTaskAssignRuleDO taskAssignRule = taskAssignRuleService.getTaskAssignRule(id);
-        return success(BeanUtils.toBean(taskAssignRule, BpmTaskAssignRuleRespVO.class));
-    }
-
     @GetMapping("/list")
     @Operation(summary = "获得任务分配规则列表")
     @Parameters({
@@ -86,25 +40,19 @@ public class BpmTaskAssignRuleController {
         return success(taskAssignRuleService.getTaskAssignRuleList(modelId, processDefinitionId));
     }
 
-    @GetMapping("/page")
-    @Operation(summary = "获得Bpm 任务规则分页")
-    @PreAuthorize("@ss.hasPermission('bpm:task-assign-rule:query')")
-    public CommonResult<PageResult<BpmTaskAssignRuleRespVO>> getTaskAssignRulePage(@Valid BpmTaskAssignRulePageReqVO pageReqVO) {
-        PageResult<BpmTaskAssignRuleDO> pageResult = taskAssignRuleService.getTaskAssignRulePage(pageReqVO);
-        return success(BeanUtils.toBean(pageResult, BpmTaskAssignRuleRespVO.class));
+    @PostMapping("/create")
+    @Operation(summary = "创建任务分配规则")
+    @PreAuthorize("@ss.hasPermission('bpm:task-assign-rule:create')")
+    public CommonResult<Long> createTaskAssignRule(@Valid @RequestBody BpmTaskAssignRuleCreateReqVO reqVO) {
+        return success(taskAssignRuleService.createTaskAssignRule(reqVO));
     }
 
-    @GetMapping("/export-excel")
-    @Operation(summary = "导出Bpm 任务规则 Excel")
-    @PreAuthorize("@ss.hasPermission('bpm:task-assign-rule:export')")
-    @ApiAccessLog(operateType = EXPORT)
-    public void exportTaskAssignRuleExcel(@Valid BpmTaskAssignRulePageReqVO pageReqVO,
-              HttpServletResponse response) throws IOException {
-        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
-        List<BpmTaskAssignRuleDO> list = taskAssignRuleService.getTaskAssignRulePage(pageReqVO).getList();
-        // 导出 Excel
-        ExcelUtils.write(response, "Bpm 任务规则.xls", "数据", BpmTaskAssignRuleRespVO.class,
-                        BeanUtils.toBean(list, BpmTaskAssignRuleRespVO.class));
+    @PutMapping("/update")
+    @Operation(summary = "更新任务分配规则")
+    @PreAuthorize("@ss.hasPermission('bpm:task-assign-rule:update')")
+    public CommonResult<Boolean> updateTaskAssignRule(@Valid @RequestBody BpmTaskAssignRuleUpdateReqVO reqVO) {
+        taskAssignRuleService.updateTaskAssignRule(reqVO);
+        return success(true);
     }
 
 }

+ 34 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/script/BpmTaskAssignScript.java

@@ -0,0 +1,34 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script;
+
+import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskRuleScriptEnum;
+import org.flowable.engine.delegate.DelegateExecution;
+
+import java.util.Set;
+
+/**
+ * Bpm 任务分配的自定义 Script 脚本
+ * 使用场景:
+ * 1. 设置审批人为发起人
+ * 2. 设置审批人为发起人的 Leader
+ * 3. 甚至审批人为发起人的 Leader 的 Leader
+ *
+ * @author 芋道源码
+ */
+public interface BpmTaskAssignScript {
+
+    /**
+     * 基于执行任务,获得任务的候选用户们
+     *
+     * @param execution 执行任务
+     * @return 候选人用户的编号数组
+     */
+    Set<Long> calculateTaskCandidateUsers(DelegateExecution execution);
+
+    /**
+     * 获得枚举值
+     *
+     * @return 枚举值
+     */
+    BpmTaskRuleScriptEnum getEnum();
+}
+

+ 70 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderAbstractScript.java

@@ -0,0 +1,70 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.impl;
+
+import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript;
+import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
+import cn.iocoder.yudao.module.system.api.dept.DeptApi;
+import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
+import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
+import org.flowable.engine.delegate.DelegateExecution;
+import org.flowable.engine.runtime.ProcessInstance;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.util.Assert;
+
+import javax.annotation.Resource;
+import java.util.Set;
+
+import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
+import static java.util.Collections.emptySet;
+
+/**
+ * 分配给发起人的 Leader 审批的 Script 实现类
+ * 目前 Leader 的定义是,
+ *
+ * @author 芋道源码
+ */
+public abstract class BpmTaskAssignLeaderAbstractScript implements BpmTaskAssignScript {
+
+    @Resource
+    private AdminUserApi adminUserApi;
+    @Resource
+    private DeptApi deptApi;
+    @Resource
+    @Lazy // 解决循环依赖
+    private BpmProcessInstanceService bpmProcessInstanceService;
+
+    protected Set<Long> calculateTaskCandidateUsers(DelegateExecution execution, int level) {
+        Assert.isTrue(level > 0, "level 必须大于 0");
+        // 获得发起人
+        ProcessInstance processInstance = bpmProcessInstanceService.getProcessInstance(execution.getProcessInstanceId());
+        Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId());
+        // 获得对应 leve 的部门
+        DeptRespDTO dept = null;
+        for (int i = 0; i < level; i++) {
+            // 获得 level 对应的部门
+            if (dept == null) {
+                dept = getStartUserDept(startUserId);
+                if (dept == null) { // 找不到发起人的部门,所以无法使用该规则
+                    return emptySet();
+                }
+            } else {
+                DeptRespDTO parentDept = deptApi.getDept(dept.getParentId());
+                if (parentDept == null) { // 找不到父级部门,所以只好结束寻找。原因是:例如说,级别比较高的人,所在部门层级比较少
+                    break;
+                }
+                dept = parentDept;
+            }
+        }
+        return dept.getLeaderUserId() != null ? asSet(dept.getLeaderUserId()) : emptySet();
+    }
+
+    private DeptRespDTO getStartUserDept(Long startUserId) {
+        AdminUserRespDTO startUser = adminUserApi.getUser(startUserId);
+        if (startUser.getDeptId() == null) { // 找不到部门,所以无法使用该规则
+            return null;
+        }
+        return deptApi.getDept(startUser.getDeptId());
+    }
+
+}

+ 27 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderX1Script.java

@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.impl;
+
+import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskRuleScriptEnum;
+import org.flowable.engine.delegate.DelegateExecution;
+import org.springframework.stereotype.Component;
+
+import java.util.Set;
+
+/**
+ * 分配给发起人的一级 Leader 审批的 Script 实现类
+ *
+ * @author 芋道源码
+ */
+@Component
+public class BpmTaskAssignLeaderX1Script extends BpmTaskAssignLeaderAbstractScript {
+
+    @Override
+    public Set<Long> calculateTaskCandidateUsers(DelegateExecution execution) {
+        return calculateTaskCandidateUsers(execution, 1);
+    }
+
+    @Override
+    public BpmTaskRuleScriptEnum getEnum() {
+        return BpmTaskRuleScriptEnum.LEADER_X1;
+    }
+
+}

+ 27 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderX2Script.java

@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.impl;
+
+import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskRuleScriptEnum;
+import org.flowable.engine.delegate.DelegateExecution;
+import org.springframework.stereotype.Component;
+
+import java.util.Set;
+
+/**
+ * 分配给发起人的二级 Leader 审批的 Script 实现类
+ *
+ * @author 芋道源码
+ */
+@Component
+public class BpmTaskAssignLeaderX2Script extends BpmTaskAssignLeaderAbstractScript {
+
+    @Override
+    public Set<Long> calculateTaskCandidateUsers(DelegateExecution execution) {
+        return calculateTaskCandidateUsers(execution, 2);
+    }
+
+    @Override
+    public BpmTaskRuleScriptEnum getEnum() {
+        return BpmTaskRuleScriptEnum.LEADER_X2;
+    }
+
+}

+ 40 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/script/impl/BpmTaskAssignStartUserScript.java

@@ -0,0 +1,40 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.impl;
+
+import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
+import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
+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.service.task.BpmProcessInstanceService;
+import org.flowable.engine.delegate.DelegateExecution;
+import org.flowable.engine.runtime.ProcessInstance;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.util.Set;
+
+/**
+ * 分配给发起人审批的 Script 实现类
+ *
+ * @author 芋道源码
+ */
+@Component
+public class BpmTaskAssignStartUserScript implements BpmTaskAssignScript {
+
+    @Resource
+    @Lazy // 解决循环依赖
+    private BpmProcessInstanceService bpmProcessInstanceService;
+
+    @Override
+    public Set<Long> calculateTaskCandidateUsers(DelegateExecution execution) {
+        ProcessInstance processInstance = bpmProcessInstanceService.getProcessInstance(execution.getProcessInstanceId());
+        Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId());
+        return SetUtils.asSet(startUserId);
+    }
+
+    @Override
+    public BpmTaskRuleScriptEnum getEnum() {
+        return BpmTaskRuleScriptEnum.START_USER;
+    }
+
+}

+ 40 - 29
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskAssignRuleService.java

@@ -3,10 +3,13 @@ package cn.iocoder.yudao.module.bpm.service.task;
 import java.util.*;
 import javax.validation.*;
 
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleCreateReqVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleRespVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleUpdateReqVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.*;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO;
+import org.flowable.engine.delegate.DelegateExecution;
 import org.springframework.lang.Nullable;
 
 /**
@@ -26,62 +29,70 @@ public interface BpmTaskAssignRuleService {
     List<BpmTaskAssignRuleDO> getTaskAssignRuleListByProcessDefinitionId(String processDefinitionId,
                                                                          @Nullable String taskDefinitionKey);
 
-
     /**
-     * 创建Bpm 任务规则
+     * 获得流程模型的任务规则数组
      *
-     * @param createReqVO 创建信息
-     * @return 编号
+     * @param modelId 流程模型的编号
+     * @return 任务规则数组
      */
-    Long createTaskAssignRule(@Valid BpmTaskAssignRuleSaveReqVO createReqVO);
+    List<BpmTaskAssignRuleDO> getTaskAssignRuleListByModelId(String modelId);
 
     /**
-     * 更新Bpm 任务规则
+     * 获得流程定义的任务分配规则数组
      *
-     * @param updateReqVO 更新信息
+     * @param modelId 流程模型的编号
+     * @param processDefinitionId 流程定义的编号
+     * @return 任务规则数组
      */
-    void updateTaskAssignRule(@Valid BpmTaskAssignRuleSaveReqVO updateReqVO);
+    List<BpmTaskAssignRuleRespVO> getTaskAssignRuleList(String modelId, String processDefinitionId);
 
     /**
-     * 删除Bpm 任务规则
+     * 创建任务分配规则
      *
-     * @param id 编号
+     * @param reqVO 创建信息
+     * @return 规则编号
      */
-    void deleteTaskAssignRule(Long id);
+    Long createTaskAssignRule(@Valid BpmTaskAssignRuleCreateReqVO reqVO);
 
     /**
-     * 获得Bpm 任务规则
+     * 更新任务分配规则
      *
-     * @param id 编号
-     * @return Bpm 任务规则
+     * @param reqVO 创建信息
      */
-    BpmTaskAssignRuleDO getTaskAssignRule(Long id);
-
+    void updateTaskAssignRule(@Valid BpmTaskAssignRuleUpdateReqVO reqVO);
 
     /**
-     * 获得Bpm 任务规则
+     * 判断指定流程模型和流程定义的分配规则是否相等
      *
-     * @param id 编号
-     * @return Bpm 任务规则
+     * @param modelId 流程模型编号
+     * @param processDefinitionId 流程定义编号
+     * @return 是否相等
      */
-    List<BpmTaskAssignRuleDO> getTaskAssignRuleModelId(String id);
+    boolean isTaskAssignRulesEquals(String modelId, String processDefinitionId);
 
+    /**
+     * 将流程流程模型的任务分配规则,复制一份给流程定义
+     * 目的:每次流程模型部署时,都会生成一个新的流程定义,此时考虑到每次部署的流程不可变性,所以需要复制一份给该流程定义
+     *
+     * @param fromModelId 流程模型编号
+     * @param toProcessDefinitionId 流程定义编号
+     */
+    void copyTaskAssignRules(String fromModelId, String toProcessDefinitionId);
 
     /**
-     * 获得Bpm 任务规则分页
+     * 校验流程模型的任务分配规则全部都配置了
+     * 目的:如果有规则未配置,会导致流程任务找不到负责人,进而流程无法进行下去!
      *
-     * @param pageReqVO 分页查询
-     * @return Bpm 任务规则分页
+     * @param id 流程模型编号
      */
-    PageResult<BpmTaskAssignRuleDO> getTaskAssignRulePage(BpmTaskAssignRulePageReqVO pageReqVO);
+    void checkTaskAssignRuleAllConfig(String id);
 
     /**
-     * 获得流程定义的任务分配规则数组
+     * 计算当前执行任务的处理人
      *
-     * @param modelId             流程模型的编号
-     * @param processDefinitionId 流程定义的编号
-     * @return 任务规则数组
+     * @param execution 执行任务
+     * @return 处理人的编号数组
      */
-    List<BpmTaskAssignRuleRespVO> getTaskAssignRuleList(String modelId, String processDefinitionId);
+    Set<Long> calculateTaskCandidateUsers(DelegateExecution execution);
 
 }

+ 275 - 47
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskAssignRuleServiceImpl.java

@@ -1,28 +1,55 @@
 package cn.iocoder.yudao.module.bpm.service.task;
 
 import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
+import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
+import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleCreateReqVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleRespVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleUpdateReqVO;
 import cn.iocoder.yudao.module.bpm.convert.definition.BpmTaskAssignRuleConvert;
 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.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.definition.BpmModelService;
 import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
+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 com.google.common.annotations.VisibleForTesting;
+import lombok.extern.slf4j.Slf4j;
 import org.flowable.bpmn.model.BpmnModel;
 import org.flowable.bpmn.model.UserTask;
+import org.flowable.common.engine.api.FlowableException;
+import org.flowable.engine.delegate.DelegateExecution;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
 import javax.annotation.Resource;
+import javax.validation.Valid;
+
 import org.springframework.validation.annotation.Validated;
 import cn.iocoder.yudao.framework.flowable.core.util.FlowableUtils;
 
 import java.util.*;
-import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.*;
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 
 import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmTaskAssignRuleMapper;
 
+import static cn.hutool.core.text.CharSequenceUtil.format;
 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.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.*;
 
 /**
@@ -32,62 +59,50 @@ import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
  */
 @Service
 @Validated
+@Slf4j
 public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
 
     @Resource
-    private BpmTaskAssignRuleMapper taskAssignRuleMapper;
+    private BpmTaskAssignRuleMapper taskRuleMapper;
     @Resource
     @Lazy // 解决循环依赖
     private BpmModelService modelService;
     @Resource
     @Lazy // 解决循环依赖
     private BpmProcessDefinitionService processDefinitionService;
+    @Resource
+    private BpmUserGroupService userGroupService;
+    @Resource
+    private RoleApi roleApi;
+    @Resource
+    private DeptApi deptApi;
+    @Resource
+    private PostApi postApi;
+    @Resource
+    private AdminUserApi adminUserApi;
+    @Resource
+    private DictDataApi dictDataApi;
+    @Resource
+    private PermissionApi permissionApi;
+    /**
+     * 任务分配脚本
+     */
+    private Map<Long, BpmTaskAssignScript> scriptMap = Collections.emptyMap();
 
-    @Override
-    public Long createTaskAssignRule(BpmTaskAssignRuleSaveReqVO createReqVO) {
-        // 插入
-        BpmTaskAssignRuleDO taskAssignRule = BeanUtils.toBean(createReqVO, BpmTaskAssignRuleDO.class);
-        taskAssignRuleMapper.insert(taskAssignRule);
-        // 返回
-        return taskAssignRule.getId();
-    }
-
-    @Override
-    public void updateTaskAssignRule(BpmTaskAssignRuleSaveReqVO updateReqVO) {
-        // 校验存在
-        validateTaskAssignRuleExists(updateReqVO.getId());
-        // 更新
-        BpmTaskAssignRuleDO updateObj = BeanUtils.toBean(updateReqVO, BpmTaskAssignRuleDO.class);
-        taskAssignRuleMapper.updateById(updateObj);
-    }
-
-    @Override
-    public void deleteTaskAssignRule(Long id) {
-        // 校验存在
-        validateTaskAssignRuleExists(id);
-        // 删除
-        taskAssignRuleMapper.deleteById(id);
-    }
-
-    private void validateTaskAssignRuleExists(Long id) {
-        if (taskAssignRuleMapper.selectById(id) == null) {
-            throw exception(TASK_ASSIGN_RULE_NOT_EXISTS);
-        }
-    }
-
-    @Override
-    public BpmTaskAssignRuleDO getTaskAssignRule(Long id) {
-        return taskAssignRuleMapper.selectById(id);
+    @Resource
+    public void setScripts(List<BpmTaskAssignScript> scripts) {
+        this.scriptMap = convertMap(scripts, script -> script.getEnum().getId());
     }
 
     @Override
-    public List<BpmTaskAssignRuleDO> getTaskAssignRuleModelId(String modelId) {
-        return taskAssignRuleMapper.selectListByModelId( modelId);
+    public List<BpmTaskAssignRuleDO> getTaskAssignRuleListByProcessDefinitionId(String processDefinitionId,
+                                                                                String taskDefinitionKey) {
+        return taskRuleMapper.selectListByProcessDefinitionId(processDefinitionId, taskDefinitionKey);
     }
 
     @Override
-    public PageResult<BpmTaskAssignRuleDO> getTaskAssignRulePage(BpmTaskAssignRulePageReqVO pageReqVO) {
-        return taskAssignRuleMapper.selectPage(pageReqVO);
+    public List<BpmTaskAssignRuleDO> getTaskAssignRuleListByModelId(String modelId) {
+        return taskRuleMapper.selectListByModelId(modelId);
     }
 
     @Override
@@ -96,7 +111,7 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
         List<BpmTaskAssignRuleDO> rules = Collections.emptyList();
         BpmnModel model = null;
         if (StrUtil.isNotEmpty(modelId)) {
-            rules = getTaskAssignRuleModelId(modelId);
+            rules = getTaskAssignRuleListByModelId(modelId);
             model = modelService.getBpmnModel(modelId);
         } else if (StrUtil.isNotEmpty(processDefinitionId)) {
             rules = getTaskAssignRuleListByProcessDefinitionId(processDefinitionId, null);
@@ -112,13 +127,226 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
         }
         // 转换数据
         return BpmTaskAssignRuleConvert.INSTANCE.convertList(userTasks, rules);
+    }
 
+    @Override
+    public Long createTaskAssignRule(@Valid BpmTaskAssignRuleCreateReqVO reqVO) {
+        // 校验参数
+        validTaskAssignRuleOptions(reqVO.getType(), reqVO.getOptions());
+        // 校验是否已经配置
+        BpmTaskAssignRuleDO existRule =
+                taskRuleMapper.selectListByModelIdAndTaskDefinitionKey(reqVO.getModelId(), reqVO.getTaskDefinitionKey());
+        if (existRule != null) {
+            throw exception(TASK_ASSIGN_RULE_EXISTS, reqVO.getModelId(), reqVO.getTaskDefinitionKey());
+        }
+
+        // 存储
+        BpmTaskAssignRuleDO rule = BpmTaskAssignRuleConvert.INSTANCE.convert(reqVO)
+                .setProcessDefinitionId(BpmTaskAssignRuleDO.PROCESS_DEFINITION_ID_NULL); // 只有流程模型,才允许新建
+        taskRuleMapper.insert(rule);
+        return rule.getId();
     }
 
     @Override
-    public List<BpmTaskAssignRuleDO> getTaskAssignRuleListByProcessDefinitionId(String processDefinitionId,
-                                                                                String taskDefinitionKey) {
-        return taskAssignRuleMapper.selectListByProcessDefinitionId(processDefinitionId, taskDefinitionKey);
+    public void updateTaskAssignRule(@Valid BpmTaskAssignRuleUpdateReqVO reqVO) {
+        // 校验参数
+        validTaskAssignRuleOptions(reqVO.getType(), reqVO.getOptions());
+        // 校验是否存在
+        BpmTaskAssignRuleDO existRule = taskRuleMapper.selectById(reqVO.getId());
+        if (existRule == null) {
+            throw exception(TASK_ASSIGN_RULE_NOT_EXISTS);
+        }
+        // 只允许修改流程模型的规则
+        if (!Objects.equals(BpmTaskAssignRuleDO.PROCESS_DEFINITION_ID_NULL, existRule.getProcessDefinitionId())) {
+            throw exception(TASK_UPDATE_FAIL_NOT_MODEL);
+        }
+
+        // 执行更新
+        taskRuleMapper.updateById(BpmTaskAssignRuleConvert.INSTANCE.convert(reqVO));
+    }
+
+    @Override
+    public boolean isTaskAssignRulesEquals(String modelId, String processDefinitionId) {
+        // 调用 VO 接口的原因是,过滤掉流程模型不需要的规则,保持和 copyTaskAssignRules 方法的一致性
+        List<BpmTaskAssignRuleRespVO> modelRules = getTaskAssignRuleList(modelId, null);
+        List<BpmTaskAssignRuleRespVO> processInstanceRules = getTaskAssignRuleList(null, processDefinitionId);
+        if (modelRules.size() != processInstanceRules.size()) {
+            return false;
+        }
+
+        // 遍历,匹配对应的规则
+        Map<String, BpmTaskAssignRuleRespVO> processInstanceRuleMap =
+                CollectionUtils.convertMap(processInstanceRules, BpmTaskAssignRuleRespVO::getTaskDefinitionKey);
+        for (BpmTaskAssignRuleRespVO modelRule : modelRules) {
+            BpmTaskAssignRuleRespVO processInstanceRule = processInstanceRuleMap.get(modelRule.getTaskDefinitionKey());
+            if (processInstanceRule == null) {
+                return false;
+            }
+            if (!ObjectUtil.equals(modelRule.getType(), processInstanceRule.getType()) || !ObjectUtil.equal(
+                    modelRule.getOptions(), processInstanceRule.getOptions())) {
+                return false;
+            }
+        }
+        return true;
     }
 
+    @Override
+    public void copyTaskAssignRules(String fromModelId, String toProcessDefinitionId) {
+        List<BpmTaskAssignRuleRespVO> rules = getTaskAssignRuleList(fromModelId, null);
+        if (CollUtil.isEmpty(rules)) {
+            return;
+        }
+        // 开始复制
+        List<BpmTaskAssignRuleDO> newRules = BpmTaskAssignRuleConvert.INSTANCE.convertList2(rules);
+        newRules.forEach(rule -> rule.setProcessDefinitionId(toProcessDefinitionId).setId(null).setCreateTime(null)
+                .setUpdateTime(null));
+        taskRuleMapper.insertBatch(newRules);
+    }
+
+    @Override
+    public void checkTaskAssignRuleAllConfig(String id) {
+        // 一个用户任务都没配置,所以无需配置规则
+        List<BpmTaskAssignRuleRespVO> taskAssignRules = getTaskAssignRuleList(id, null);
+        if (CollUtil.isEmpty(taskAssignRules)) {
+            return;
+        }
+        // 校验未配置规则的任务
+        taskAssignRules.forEach(rule -> {
+            if (CollUtil.isEmpty(rule.getOptions())) {
+                throw exception(MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG, rule.getTaskDefinitionName());
+            }
+        });
+    }
+
+    private void validTaskAssignRuleOptions(Integer type, Set<Long> options) {
+        if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.ROLE.getType())) {
+            roleApi.validRoleList(options);
+        } else if (ObjectUtils.equalsAny(type, BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(),
+                BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType())) {
+            deptApi.validateDeptList(options);
+        } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.POST.getType())) {
+            postApi.validPostList(options);
+        } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.USER.getType())) {
+            adminUserApi.validateUserList(options);
+        } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.USER_GROUP.getType())) {
+            userGroupService.validUserGroups(options);
+        } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.SCRIPT.getType())) {
+            dictDataApi.validateDictDataList(DictTypeConstants.TASK_ASSIGN_SCRIPT,
+                    CollectionUtils.convertSet(options, String::valueOf));
+        } else {
+            throw new IllegalArgumentException(format("未知的规则类型({})", type));
+        }
+    }
+
+    @Override
+    @DataPermission(enable = false) // 忽略数据权限,不然分配会存在问题
+    public Set<Long> calculateTaskCandidateUsers(DelegateExecution execution) {
+        BpmTaskAssignRuleDO rule = getTaskRule(execution);
+        return calculateTaskCandidateUsers(execution, rule);
+    }
+
+    @VisibleForTesting
+    BpmTaskAssignRuleDO getTaskRule(DelegateExecution execution) {
+        List<BpmTaskAssignRuleDO> taskRules = getTaskAssignRuleListByProcessDefinitionId(
+                execution.getProcessDefinitionId(), execution.getCurrentActivityId());
+        if (CollUtil.isEmpty(taskRules)) {
+            throw new FlowableException(format("流程任务({}/{}/{}) 找不到符合的任务规则",
+                    execution.getId(), execution.getProcessDefinitionId(), execution.getCurrentActivityId()));
+        }
+        if (taskRules.size() > 1) {
+            throw new FlowableException(format("流程任务({}/{}/{}) 找到过多任务规则({})",
+                    execution.getId(), execution.getProcessDefinitionId(), execution.getCurrentActivityId()));
+        }
+        return taskRules.get(0);
+    }
+
+    @VisibleForTesting
+    Set<Long> calculateTaskCandidateUsers(DelegateExecution execution, BpmTaskAssignRuleDO rule) {
+        Set<Long> assigneeUserIds = null;
+        if (Objects.equals(BpmTaskAssignRuleTypeEnum.ROLE.getType(), rule.getType())) {
+            assigneeUserIds = calculateTaskCandidateUsersByRole(rule);
+        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(), rule.getType())) {
+            assigneeUserIds = calculateTaskCandidateUsersByDeptMember(rule);
+        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType(), rule.getType())) {
+            assigneeUserIds = calculateTaskCandidateUsersByDeptLeader(rule);
+        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.POST.getType(), rule.getType())) {
+            assigneeUserIds = calculateTaskCandidateUsersByPost(rule);
+        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER.getType(), rule.getType())) {
+            assigneeUserIds = calculateTaskCandidateUsersByUser(rule);
+        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER_GROUP.getType(), rule.getType())) {
+            assigneeUserIds = calculateTaskCandidateUsersByUserGroup(rule);
+        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.SCRIPT.getType(), rule.getType())) {
+            assigneeUserIds = calculateTaskCandidateUsersByScript(execution, rule);
+        }
+
+        // 移除被禁用的用户
+        removeDisableUsers(assigneeUserIds);
+        // 如果候选人为空,抛出异常
+        if (CollUtil.isEmpty(assigneeUserIds)) {
+            log.error("[calculateTaskCandidateUsers][流程任务({}/{}/{}) 任务规则({}) 找不到候选人]", execution.getId(),
+                    execution.getProcessDefinitionId(), execution.getCurrentActivityId(), toJsonString(rule));
+            throw exception(TASK_CREATE_FAIL_NO_CANDIDATE_USER);
+        }
+        return assigneeUserIds;
+    }
+
+    private Set<Long> calculateTaskCandidateUsersByRole(BpmTaskAssignRuleDO rule) {
+        return permissionApi.getUserRoleIdListByRoleIds(rule.getOptions());
+    }
+
+    private Set<Long> calculateTaskCandidateUsersByDeptMember(BpmTaskAssignRuleDO rule) {
+        List<AdminUserRespDTO> users = adminUserApi.getUserListByDeptIds(rule.getOptions());
+        return convertSet(users, AdminUserRespDTO::getId);
+    }
+
+    private Set<Long> calculateTaskCandidateUsersByDeptLeader(BpmTaskAssignRuleDO rule) {
+        List<DeptRespDTO> depts = deptApi.getDeptList(rule.getOptions());
+        return convertSet(depts, DeptRespDTO::getLeaderUserId);
+    }
+
+    private Set<Long> calculateTaskCandidateUsersByPost(BpmTaskAssignRuleDO rule) {
+        List<AdminUserRespDTO> users = adminUserApi.getUsersByPostIds(rule.getOptions());
+        return convertSet(users, AdminUserRespDTO::getId);
+    }
+
+    private Set<Long> calculateTaskCandidateUsersByUser(BpmTaskAssignRuleDO rule) {
+        return rule.getOptions();
+    }
+
+    private Set<Long> calculateTaskCandidateUsersByUserGroup(BpmTaskAssignRuleDO rule) {
+        List<BpmUserGroupDO> userGroups = userGroupService.getUserGroupList(rule.getOptions());
+        Set<Long> userIds = new HashSet<>();
+        userGroups.forEach(group -> userIds.addAll(group.getMemberUserIds()));
+        return userIds;
+    }
+
+    private Set<Long> calculateTaskCandidateUsersByScript(DelegateExecution execution, BpmTaskAssignRuleDO rule) {
+        // 获得对应的脚本
+        List<BpmTaskAssignScript> scripts = new ArrayList<>(rule.getOptions().size());
+        rule.getOptions().forEach(id -> {
+            BpmTaskAssignScript script = scriptMap.get(id);
+            if (script == null) {
+                throw exception(TASK_ASSIGN_SCRIPT_NOT_EXISTS, id);
+            }
+            scripts.add(script);
+        });
+        // 逐个计算任务
+        Set<Long> userIds = new HashSet<>();
+        scripts.forEach(script -> CollUtil.addAll(userIds, script.calculateTaskCandidateUsers(execution)));
+        return userIds;
+    }
+
+    @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());
+        });
+    }
+
+
 }

+ 9 - 0
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApi.java

@@ -86,4 +86,13 @@ public interface AdminUserApi {
      */
     void validateUserList(Collection<Long> ids);
 
+    /**
+     * 获得指定岗位的用户数组
+     *
+     * @param postIds 岗位数组
+     * @return 用户数组
+     */
+    List<AdminUserRespDTO> getUsersByPostIds(Collection<Long> postIds);
+
+
 }

+ 7 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApiImpl.java

@@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.ObjUtil;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
+import cn.iocoder.yudao.module.system.convert.user.UserConvert;
 import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
 import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
 import cn.iocoder.yudao.module.system.service.dept.DeptService;
@@ -88,4 +89,10 @@ public class AdminUserApiImpl implements AdminUserApi {
         userService.validateUserList(ids);
     }
 
+    @Override
+    public List<AdminUserRespDTO> getUsersByPostIds(Collection<Long> postIds) {
+        List<AdminUserDO> users = userService.getUserListByPostIds(postIds);
+        return UserConvert.INSTANCE.convertList4(users);
+    }
+
 }

+ 3 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/user/UserConvert.java

@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.system.convert.user;
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
 import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptSimpleRespVO;
 import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostSimpleRespVO;
 import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleSimpleRespVO;
@@ -55,4 +56,6 @@ public interface UserConvert {
         return userVO;
     }
 
+    List<AdminUserRespDTO> convertList4(List<AdminUserDO> users);
+
 }