mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-10-31 02:08:43 +08:00 
			
		
		
		
	增加会签或签
This commit is contained in:
		
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -4,6 +4,7 @@ import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO; | |||||||
| import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | ||||||
| import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | ||||||
| import org.apache.ibatis.annotations.Mapper; | import org.apache.ibatis.annotations.Mapper; | ||||||
|  | import org.apache.ibatis.annotations.Param; | ||||||
|  |  | ||||||
| import java.util.Collection; | import java.util.Collection; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| @@ -22,4 +23,11 @@ public interface BpmTaskExtMapper extends BaseMapperX<BpmTaskExtDO> { | |||||||
|     default List<BpmTaskExtDO> selectListByProcessInstanceId(String processInstanceId) { |     default List<BpmTaskExtDO> selectListByProcessInstanceId(String processInstanceId) { | ||||||
|         return selectList("process_instance_id", processInstanceId); |         return selectList("process_instance_id", processInstanceId); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 修改或签任务信息 | ||||||
|  |      * | ||||||
|  |      * @param entity 任务信息 | ||||||
|  |      */ | ||||||
|  |     void updateUserOrSignTask(@Param("entity") BpmTaskExtDO entity); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -13,15 +13,13 @@ import lombok.Getter; | |||||||
| public enum BpmTaskAssignRuleTypeEnum { | public enum BpmTaskAssignRuleTypeEnum { | ||||||
|  |  | ||||||
|     ROLE(10, "角色"), |     ROLE(10, "角色"), | ||||||
|  |  | ||||||
|     DEPT_MEMBER(20, "部门的成员"), // 包括负责人 |     DEPT_MEMBER(20, "部门的成员"), // 包括负责人 | ||||||
|     DEPT_LEADER(21, "部门的负责人"), |     DEPT_LEADER(21, "部门的负责人"), | ||||||
|     POST(22, "岗位"), |     POST(22, "岗位"), | ||||||
|  |  | ||||||
|     USER(30, "用户"), |     USER(30, "用户"), | ||||||
|  |     USER_SIGN(31, "用户---会签"), | ||||||
|  |     USER_OR_SIGN(32, "用户---或签"), | ||||||
|     USER_GROUP(40, "用户组"), |     USER_GROUP(40, "用户组"), | ||||||
|  |  | ||||||
|     SCRIPT(50, "自定义脚本"), // 例如说,发起人所在部门的领导、发起人所在部门的领导的领导 |     SCRIPT(50, "自定义脚本"), // 例如说,发起人所在部门的领导、发起人所在部门的领导的领导 | ||||||
|     ; |     ; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -10,7 +10,10 @@ import lombok.Data; | |||||||
| import lombok.EqualsAndHashCode; | import lombok.EqualsAndHashCode; | ||||||
| import lombok.Setter; | import lombok.Setter; | ||||||
| import lombok.ToString; | import lombok.ToString; | ||||||
|  | import org.flowable.bpmn.model.Activity; | ||||||
| import org.flowable.bpmn.model.UserTask; | import org.flowable.bpmn.model.UserTask; | ||||||
|  | import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior; | ||||||
|  | import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior; | ||||||
| import org.flowable.engine.impl.bpmn.behavior.UserTaskActivityBehavior; | import org.flowable.engine.impl.bpmn.behavior.UserTaskActivityBehavior; | ||||||
| import org.flowable.engine.impl.bpmn.parser.factory.DefaultActivityBehaviorFactory; | import org.flowable.engine.impl.bpmn.parser.factory.DefaultActivityBehaviorFactory; | ||||||
|  |  | ||||||
| @@ -52,4 +55,18 @@ public class BpmActivityBehaviorFactory extends DefaultActivityBehaviorFactory { | |||||||
|         userTaskActivityBehavior.setScripts(scripts); |         userTaskActivityBehavior.setScripts(scripts); | ||||||
|         return userTaskActivityBehavior; |         return userTaskActivityBehavior; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public ParallelMultiInstanceBehavior createParallelMultiInstanceBehavior(Activity activity, | ||||||
|  |         AbstractBpmnActivityBehavior innerActivityBehavior) { | ||||||
|  |         BpmParallelMultiInstanceActivityBehavior bpmParallelMultiInstanceActivityBehavior = | ||||||
|  |             new BpmParallelMultiInstanceActivityBehavior(activity, innerActivityBehavior); | ||||||
|  |         bpmParallelMultiInstanceActivityBehavior.setBpmTaskRuleService(bpmTaskRuleService); | ||||||
|  |         bpmParallelMultiInstanceActivityBehavior.setPermissionApi(permissionApi); | ||||||
|  |         bpmParallelMultiInstanceActivityBehavior.setDeptApi(deptApi); | ||||||
|  |         bpmParallelMultiInstanceActivityBehavior.setUserGroupService(userGroupService); | ||||||
|  |         bpmParallelMultiInstanceActivityBehavior.setAdminUserApi(adminUserApi); | ||||||
|  |         bpmParallelMultiInstanceActivityBehavior.setScripts(scripts); | ||||||
|  |         return bpmParallelMultiInstanceActivityBehavior; | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,194 @@ | |||||||
|  | package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior; | ||||||
|  |  | ||||||
|  | import cn.hutool.core.collection.CollUtil; | ||||||
|  | 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.enums.definition.BpmTaskAssignRuleTypeEnum; | ||||||
|  | import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript; | ||||||
|  | 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 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.Activity; | ||||||
|  | import org.flowable.common.engine.api.FlowableException; | ||||||
|  | import org.flowable.engine.delegate.DelegateExecution; | ||||||
|  | import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior; | ||||||
|  | import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior; | ||||||
|  | import org.flowable.engine.impl.util.CommandContextUtil; | ||||||
|  |  | ||||||
|  | import java.util.*; | ||||||
|  |  | ||||||
|  | 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.json.JsonUtils.toJsonString; | ||||||
|  | import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.TASK_CREATE_FAIL_NO_CANDIDATE_USER; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @author kemengkai | ||||||
|  |  * @create 2022-04-21 16:57 | ||||||
|  |  */ | ||||||
|  | @Slf4j | ||||||
|  | public class BpmParallelMultiInstanceActivityBehavior extends ParallelMultiInstanceBehavior { | ||||||
|  |  | ||||||
|  |     @Setter | ||||||
|  |     private BpmTaskAssignRuleService bpmTaskRuleService; | ||||||
|  |     @Setter | ||||||
|  |     private BpmUserGroupService userGroupService; | ||||||
|  |     @Setter | ||||||
|  |     private DeptApi deptApi; | ||||||
|  |     @Setter | ||||||
|  |     private AdminUserApi adminUserApi; | ||||||
|  |     @Setter | ||||||
|  |     private PermissionApi permissionApi; | ||||||
|  |     /** | ||||||
|  |      * EL表达式集合模板 | ||||||
|  |      */ | ||||||
|  |     private final static String EXPRESSION_TEXT_TEMPLATE = "${coll_userList}"; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 任务分配脚本 | ||||||
|  |      */ | ||||||
|  |     private Map<Long, BpmTaskAssignScript> scriptMap = Collections.emptyMap(); | ||||||
|  |  | ||||||
|  |     public BpmParallelMultiInstanceActivityBehavior(Activity activity, | ||||||
|  |         AbstractBpmnActivityBehavior innerActivityBehavior) { | ||||||
|  |         super(activity, innerActivityBehavior); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void setScripts(List<BpmTaskAssignScript> scripts) { | ||||||
|  |         this.scriptMap = convertMap(scripts, script -> script.getEnum().getId()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 创建并行任务 | ||||||
|  |      * | ||||||
|  |      * @param multiInstanceRootExecution 并行任务入参 | ||||||
|  |      * | ||||||
|  |      * @return 返回结果 | ||||||
|  |      */ | ||||||
|  |     @Override | ||||||
|  |     protected int createInstances(DelegateExecution multiInstanceRootExecution) { | ||||||
|  |         // 查找任务信息 | ||||||
|  |         BpmTaskAssignRuleDO taskRule = getTaskRule(multiInstanceRootExecution); | ||||||
|  |         // 获取任务用户 | ||||||
|  |         Set<Long> assigneeUserIds = calculateTaskCandidateUsers(multiInstanceRootExecution, taskRule); | ||||||
|  |         // 设置任务集合变量 | ||||||
|  |         String expressionText = String.format("%s_userList", taskRule.getTaskDefinitionKey()); | ||||||
|  |         // 设置任务集合变量与任务关系 | ||||||
|  |         multiInstanceRootExecution.setVariable(expressionText, assigneeUserIds); | ||||||
|  |         // 设置任务集合EL表达式 | ||||||
|  |         this.collectionExpression = CommandContextUtil.getProcessEngineConfiguration().getExpressionManager() | ||||||
|  |             .createExpression(String.format("${%s}", expressionText)); | ||||||
|  |         // 根据会签,或签类型,设置会签,或签条件 | ||||||
|  |         if (BpmTaskAssignRuleTypeEnum.USER_SIGN.getType().equals(taskRule.getType())) { | ||||||
|  |             // 会签 | ||||||
|  |             this.completionCondition = "${ nrOfInstances == nrOfCompletedInstances }"; | ||||||
|  |         } else { | ||||||
|  |             // 或签 | ||||||
|  |             this.completionCondition = "${ nrOfCompletedInstances == 1 }"; | ||||||
|  |         } | ||||||
|  |         // 设置取出集合变量 | ||||||
|  |         this.collectionElementVariable = "user"; | ||||||
|  |         return super.createInstances(multiInstanceRootExecution); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected Object resolveCollection(DelegateExecution execution) { | ||||||
|  |         Object collection = null; | ||||||
|  |         if (EXPRESSION_TEXT_TEMPLATE.equals(this.collectionExpression.getExpressionText())) { | ||||||
|  |             // 查找任务信息 | ||||||
|  |             BpmTaskAssignRuleDO taskRule = getTaskRule(execution); | ||||||
|  |             // 设置任务集合变量 | ||||||
|  |             String expressionText = String.format("%s_userList", execution.getCurrentActivityId()); | ||||||
|  |             // 获取任务用户 | ||||||
|  |             Set<Long> assigneeUserIds = calculateTaskCandidateUsers(execution, taskRule); | ||||||
|  |             // 设置任务集合变量与任务关系 | ||||||
|  |             execution.setVariable(expressionText, assigneeUserIds); | ||||||
|  |             // 设置任务集合EL表达式 | ||||||
|  |             this.collectionExpression = CommandContextUtil.getProcessEngineConfiguration().getExpressionManager() | ||||||
|  |                 .createExpression(String.format("${%s}", expressionText)); | ||||||
|  |         } | ||||||
|  |         if (this.collectionExpression != null) { | ||||||
|  |             collection = this.collectionExpression.getValue(execution); | ||||||
|  |         } else if (this.collectionVariable != null) { | ||||||
|  |             collection = execution.getVariable(this.collectionVariable); | ||||||
|  |         } else if (this.collectionString != null) { | ||||||
|  |             collection = this.collectionString; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return collection; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private BpmTaskAssignRuleDO getTaskRule(DelegateExecution task) { | ||||||
|  |         List<BpmTaskAssignRuleDO> taskRules = | ||||||
|  |             bpmTaskRuleService.getTaskAssignRuleListByProcessDefinitionId(task.getProcessDefinitionId(), | ||||||
|  |                 task.getCurrentActivityId()); | ||||||
|  |         if (CollUtil.isEmpty(taskRules)) { | ||||||
|  |             throw new FlowableException( | ||||||
|  |                 StrUtil.format("流程任务({}/{}/{}) 找不到符合的任务规则", task.getId(), task.getProcessDefinitionId(), | ||||||
|  |                     task.getCurrentActivityId())); | ||||||
|  |         } | ||||||
|  |         if (taskRules.size() > 1) { | ||||||
|  |             throw new FlowableException( | ||||||
|  |                 StrUtil.format("流程任务({}/{}/{}) 找到过多任务规则({})", task.getId(), task.getProcessDefinitionId(), | ||||||
|  |                     task.getCurrentActivityId(), taskRules.size())); | ||||||
|  |         } | ||||||
|  |         return taskRules.get(0); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     Set<Long> calculateTaskCandidateUsers(DelegateExecution 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); | ||||||
|  |         if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER_SIGN.getType(), rule.getType())) { | ||||||
|  |             assigneeUserIds = calculateTaskCandidateUsersSignByUser(task, rule); | ||||||
|  |         } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER_OR_SIGN.getType(), rule.getType())) { | ||||||
|  |             assigneeUserIds = calculateTaskCandidateUsersSignByUser(task, rule); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // 移除被禁用的用户 | ||||||
|  |         removeDisableUsers(assigneeUserIds); | ||||||
|  |         // 如果候选人为空,抛出异常 TODO 芋艿:没候选人的策略选择。1 - 挂起;2 - 直接结束;3 - 强制一个兜底人 | ||||||
|  |         if (CollUtil.isEmpty(assigneeUserIds)) { | ||||||
|  |             log.error("[calculateTaskCandidateUsers][流程任务({}/{}/{}) 任务规则({}) 找不到候选人]", task.getId(), | ||||||
|  |                 task.getProcessDefinitionId(), task.getCurrentActivityId(), toJsonString(rule)); | ||||||
|  |             throw exception(TASK_CREATE_FAIL_NO_CANDIDATE_USER); | ||||||
|  |         } | ||||||
|  |         return assigneeUserIds; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private Set<Long> calculateTaskCandidateUsersSignByUser(DelegateExecution task, BpmTaskAssignRuleDO rule) { | ||||||
|  |         return rule.getOptions(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @VisibleForTesting | ||||||
|  |     void removeDisableUsers(Set<Long> assigneeUserIds) { | ||||||
|  |         if (CollUtil.isEmpty(assigneeUserIds)) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         //TODO 芋艿 这里有数据权限的问题。默认会加上数据权限 dept_id IN (deptId). 导致查询不到数据 | ||||||
|  |         Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(assigneeUserIds); | ||||||
|  |         assigneeUserIds.removeIf(id -> { | ||||||
|  |             AdminUserRespDTO user = userMap.get(id); | ||||||
|  |             return user == null || !CommonStatusEnum.ENABLE.getStatus().equals(user.getStatus()); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -75,8 +75,10 @@ public class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior { | |||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     @DataPermission(enable = false) // 不需要处理数据权限, 不然会有问题,查询不到数据 |     @DataPermission(enable = false) // 不需要处理数据权限, 不然会有问题,查询不到数据 | ||||||
|     protected void handleAssignments(TaskService taskService, String assignee, String owner, List<String> candidateUsers, List<String> candidateGroups, TaskEntity task, ExpressionManager expressionManager, DelegateExecution execution, ProcessEngineConfigurationImpl processEngineConfiguration) { |     protected void handleAssignments(TaskService taskService, String assignee, String owner, | ||||||
|         boolean isMultiInstance = hasMultiInstanceCharacteristics(); |         List<String> candidateUsers, List<String> candidateGroups, TaskEntity task, ExpressionManager expressionManager, | ||||||
|  |         DelegateExecution execution, ProcessEngineConfigurationImpl processEngineConfiguration) { | ||||||
|  |         /*boolean isMultiInstance = hasMultiInstanceCharacteristics(); | ||||||
|         if (isMultiInstance) { |         if (isMultiInstance) { | ||||||
|             //多实例 会签/或签,执行多次每个人 待办人都在execution里面获取 |             //多实例 会签/或签,执行多次每个人 待办人都在execution里面获取 | ||||||
|             Integer assigneeUserId = execution.getVariableLocal("user", Integer.class); |             Integer assigneeUserId = execution.getVariableLocal("user", Integer.class); | ||||||
| @@ -87,22 +89,32 @@ public class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior { | |||||||
|             // 第二步,获得任务的候选用户们 |             // 第二步,获得任务的候选用户们 | ||||||
|             Set<Long> candidateUserIds = calculateTaskCandidateUsers(task, rule); |             Set<Long> candidateUserIds = calculateTaskCandidateUsers(task, rule); | ||||||
|             // 第三步,设置一个作为负责人 |             // 第三步,设置一个作为负责人 | ||||||
|             Long assigneeUserId = chooseTaskAssignee(candidateUserIds); |             Long assigneeUserId = chooseTaskAssignee(execution, candidateUserIds); | ||||||
|  |             TaskHelper.changeTaskAssignee(task, String.valueOf(assigneeUserId)); | ||||||
|  |         } | ||||||
|  |         */ | ||||||
|  |         // 第一步,获得任务的规则 | ||||||
|  |         BpmTaskAssignRuleDO rule = getTaskRule(task); | ||||||
|  |         // 第二步,获得任务的候选用户们 | ||||||
|  |         Set<Long> candidateUserIds = calculateTaskCandidateUsers(task, rule); | ||||||
|  |         // 第三步,设置一个作为负责人 | ||||||
|  |         Long assigneeUserId = chooseTaskAssignee(execution, candidateUserIds); | ||||||
|         TaskHelper.changeTaskAssignee(task, String.valueOf(assigneeUserId)); |         TaskHelper.changeTaskAssignee(task, String.valueOf(assigneeUserId)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private BpmTaskAssignRuleDO getTaskRule(TaskEntity task) { |     private BpmTaskAssignRuleDO getTaskRule(TaskEntity task) { | ||||||
|         List<BpmTaskAssignRuleDO> taskRules = bpmTaskRuleService.getTaskAssignRuleListByProcessDefinitionId(task.getProcessDefinitionId(), |         List<BpmTaskAssignRuleDO> taskRules = | ||||||
|  |             bpmTaskRuleService.getTaskAssignRuleListByProcessDefinitionId(task.getProcessDefinitionId(), | ||||||
|                 task.getTaskDefinitionKey()); |                 task.getTaskDefinitionKey()); | ||||||
|         if (CollUtil.isEmpty(taskRules)) { |         if (CollUtil.isEmpty(taskRules)) { | ||||||
|             throw new FlowableException(StrUtil.format("流程任务({}/{}/{}) 找不到符合的任务规则", |             throw new FlowableException( | ||||||
|                     task.getId(), task.getProcessDefinitionId(), task.getTaskDefinitionKey())); |                 StrUtil.format("流程任务({}/{}/{}) 找不到符合的任务规则", task.getId(), task.getProcessDefinitionId(), | ||||||
|  |                     task.getTaskDefinitionKey())); | ||||||
|         } |         } | ||||||
|         if (taskRules.size() > 1) { |         if (taskRules.size() > 1) { | ||||||
|             throw new FlowableException(StrUtil.format("流程任务({}/{}/{}) 找到过多任务规则({})", |             throw new FlowableException( | ||||||
|                     task.getId(), task.getProcessDefinitionId(), task.getTaskDefinitionKey(), taskRules.size())); |                 StrUtil.format("流程任务({}/{}/{}) 找到过多任务规则({})", task.getId(), task.getProcessDefinitionId(), | ||||||
|  |                     task.getTaskDefinitionKey(), taskRules.size())); | ||||||
|         } |         } | ||||||
|         return taskRules.get(0); |         return taskRules.get(0); | ||||||
|     } |     } | ||||||
| @@ -123,14 +135,18 @@ public class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior { | |||||||
|             assigneeUserIds = calculateTaskCandidateUsersByUserGroup(task, rule); |             assigneeUserIds = calculateTaskCandidateUsersByUserGroup(task, rule); | ||||||
|         } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.SCRIPT.getType(), rule.getType())) { |         } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.SCRIPT.getType(), rule.getType())) { | ||||||
|             assigneeUserIds = calculateTaskCandidateUsersByScript(task, rule); |             assigneeUserIds = calculateTaskCandidateUsersByScript(task, rule); | ||||||
|  |         } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER_SIGN.getType(), rule.getType())) { | ||||||
|  |             assigneeUserIds = calculateTaskCandidateUsersByUser(task, rule); | ||||||
|  |         } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER_OR_SIGN.getType(), rule.getType())) { | ||||||
|  |             assigneeUserIds = calculateTaskCandidateUsersByUser(task, rule); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // 移除被禁用的用户 |         // 移除被禁用的用户 | ||||||
|         removeDisableUsers(assigneeUserIds); |         removeDisableUsers(assigneeUserIds); | ||||||
|         // 如果候选人为空,抛出异常 TODO 芋艿:没候选人的策略选择。1 - 挂起;2 - 直接结束;3 - 强制一个兜底人 |         // 如果候选人为空,抛出异常 TODO 芋艿:没候选人的策略选择。1 - 挂起;2 - 直接结束;3 - 强制一个兜底人 | ||||||
|         if (CollUtil.isEmpty(assigneeUserIds)) { |         if (CollUtil.isEmpty(assigneeUserIds)) { | ||||||
|             log.error("[calculateTaskCandidateUsers][流程任务({}/{}/{}) 任务规则({}) 找不到候选人]", |             log.error("[calculateTaskCandidateUsers][流程任务({}/{}/{}) 任务规则({}) 找不到候选人]", task.getId(), | ||||||
|                     task.getId(), task.getProcessDefinitionId(), task.getTaskDefinitionKey(), toJsonString(rule)); |                 task.getProcessDefinitionId(), task.getTaskDefinitionKey(), toJsonString(rule)); | ||||||
|             throw exception(TASK_CREATE_FAIL_NO_CANDIDATE_USER); |             throw exception(TASK_CREATE_FAIL_NO_CANDIDATE_USER); | ||||||
|         } |         } | ||||||
|         return assigneeUserIds; |         return assigneeUserIds; | ||||||
| @@ -182,7 +198,17 @@ public class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior { | |||||||
|         return userIds; |         return userIds; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private Long chooseTaskAssignee(Set<Long> candidateUserIds) { |     private Long chooseTaskAssignee(DelegateExecution execution, Set<Long> candidateUserIds) { | ||||||
|  |         // 获取任务变量 | ||||||
|  |         Map<String, Object> variables = execution.getVariables(); | ||||||
|  |         // 设置任务集合变量key | ||||||
|  |         String expressionText = String.format("%s_userList", execution.getCurrentActivityId()); | ||||||
|  |         // 判断当前任务是否为并行任务, 是的话获取任务变量 | ||||||
|  |         if (variables.containsKey(expressionText)) { | ||||||
|  |             String user = variables.get("user").toString(); | ||||||
|  |             return Long.valueOf(user); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         // TODO 芋艿:未来可以优化下,改成轮询的策略 |         // TODO 芋艿:未来可以优化下,改成轮询的策略 | ||||||
|         int index = RandomUtil.randomInt(candidateUserIds.size()); |         int index = RandomUtil.randomInt(candidateUserIds.size()); | ||||||
|         return CollUtil.get(candidateUserIds, index); |         return CollUtil.get(candidateUserIds, index); | ||||||
|   | |||||||
| @@ -63,7 +63,8 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService{ | |||||||
|     private DictDataApi dictDataApi; |     private DictDataApi dictDataApi; | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public List<BpmTaskAssignRuleDO> getTaskAssignRuleListByProcessDefinitionId(String processDefinitionId, String taskDefinitionKey) { |     public List<BpmTaskAssignRuleDO> getTaskAssignRuleListByProcessDefinitionId(String processDefinitionId, | ||||||
|  |         String taskDefinitionKey) { | ||||||
|         return taskRuleMapper.selectListByProcessDefinitionId(processDefinitionId, taskDefinitionKey); |         return taskRuleMapper.selectListByProcessDefinitionId(processDefinitionId, taskDefinitionKey); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -101,8 +102,8 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService{ | |||||||
|         // 校验参数 |         // 校验参数 | ||||||
|         validTaskAssignRuleOptions(reqVO.getType(), reqVO.getOptions()); |         validTaskAssignRuleOptions(reqVO.getType(), reqVO.getOptions()); | ||||||
|         // 校验是否已经配置 |         // 校验是否已经配置 | ||||||
|         BpmTaskAssignRuleDO existRule = taskRuleMapper.selectListByModelIdAndTaskDefinitionKey( |         BpmTaskAssignRuleDO existRule = | ||||||
|                 reqVO.getModelId(), reqVO.getTaskDefinitionKey()); |             taskRuleMapper.selectListByModelIdAndTaskDefinitionKey(reqVO.getModelId(), reqVO.getTaskDefinitionKey()); | ||||||
|         if (existRule != null) { |         if (existRule != null) { | ||||||
|             throw exception(TASK_ASSIGN_RULE_EXISTS, reqVO.getModelId(), reqVO.getTaskDefinitionKey()); |             throw exception(TASK_ASSIGN_RULE_EXISTS, reqVO.getModelId(), reqVO.getTaskDefinitionKey()); | ||||||
|         } |         } | ||||||
| @@ -142,15 +143,15 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService{ | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         // 遍历,匹配对应的规则 |         // 遍历,匹配对应的规则 | ||||||
|         Map<String, BpmTaskAssignRuleRespVO> processInstanceRuleMap = CollectionUtils.convertMap(processInstanceRules, |         Map<String, BpmTaskAssignRuleRespVO> processInstanceRuleMap = | ||||||
|                 BpmTaskAssignRuleRespVO::getTaskDefinitionKey); |             CollectionUtils.convertMap(processInstanceRules, BpmTaskAssignRuleRespVO::getTaskDefinitionKey); | ||||||
|         for (BpmTaskAssignRuleRespVO modelRule : modelRules) { |         for (BpmTaskAssignRuleRespVO modelRule : modelRules) { | ||||||
|             BpmTaskAssignRuleRespVO processInstanceRule = processInstanceRuleMap.get(modelRule.getTaskDefinitionKey()); |             BpmTaskAssignRuleRespVO processInstanceRule = processInstanceRuleMap.get(modelRule.getTaskDefinitionKey()); | ||||||
|             if (processInstanceRule == null) { |             if (processInstanceRule == null) { | ||||||
|                 return false; |                 return false; | ||||||
|             } |             } | ||||||
|             if (!ObjectUtil.equals(modelRule.getType(), processInstanceRule.getType()) |             if (!ObjectUtil.equals(modelRule.getType(), processInstanceRule.getType()) || !ObjectUtil.equal( | ||||||
|                     || !ObjectUtil.equal(modelRule.getOptions(), processInstanceRule.getOptions())) { |                 modelRule.getOptions(), processInstanceRule.getOptions())) { | ||||||
|                 return false; |                 return false; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -165,8 +166,8 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService{ | |||||||
|         } |         } | ||||||
|         // 开始复制 |         // 开始复制 | ||||||
|         List<BpmTaskAssignRuleDO> newRules = BpmTaskAssignRuleConvert.INSTANCE.convertList2(rules); |         List<BpmTaskAssignRuleDO> newRules = BpmTaskAssignRuleConvert.INSTANCE.convertList2(rules); | ||||||
|         newRules.forEach(rule -> rule.setProcessDefinitionId(toProcessDefinitionId).setId(null) |         newRules.forEach(rule -> rule.setProcessDefinitionId(toProcessDefinitionId).setId(null).setCreateTime(null) | ||||||
|                 .setCreateTime(null).setUpdateTime(null)); |             .setUpdateTime(null)); | ||||||
|         taskRuleMapper.insertBatch(newRules); |         taskRuleMapper.insertBatch(newRules); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -197,6 +198,10 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService{ | |||||||
|             adminUserApi.validUsers(options); |             adminUserApi.validUsers(options); | ||||||
|         } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.USER_GROUP.getType())) { |         } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.USER_GROUP.getType())) { | ||||||
|             userGroupService.validUserGroups(options); |             userGroupService.validUserGroups(options); | ||||||
|  |         } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.USER_SIGN.getType())) { | ||||||
|  |             adminUserApi.validUsers(options); | ||||||
|  |         } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.USER_OR_SIGN.getType())) { | ||||||
|  |             adminUserApi.validUsers(options); | ||||||
|         } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.SCRIPT.getType())) { |         } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.SCRIPT.getType())) { | ||||||
|             dictDataApi.validDictDatas(DictTypeConstants.TASK_ASSIGN_SCRIPT, |             dictDataApi.validDictDatas(DictTypeConstants.TASK_ASSIGN_SCRIPT, | ||||||
|                 CollectionUtils.convertSet(options, String::valueOf)); |                 CollectionUtils.convertSet(options, String::valueOf)); | ||||||
|   | |||||||
| @@ -7,8 +7,11 @@ import cn.iocoder.yudao.framework.common.util.number.NumberUtils; | |||||||
| import cn.iocoder.yudao.framework.common.util.object.PageUtils; | import cn.iocoder.yudao.framework.common.util.object.PageUtils; | ||||||
| import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*; | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*; | ||||||
| import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert; | import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert; | ||||||
|  | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO; | ||||||
| import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO; | import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO; | ||||||
|  | import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmTaskAssignRuleMapper; | ||||||
| import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmTaskExtMapper; | import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmTaskExtMapper; | ||||||
|  | import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum; | ||||||
| import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum; | import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum; | ||||||
| import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; | import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; | ||||||
| import cn.iocoder.yudao.module.system.api.dept.DeptApi; | import cn.iocoder.yudao.module.system.api.dept.DeptApi; | ||||||
| @@ -66,6 +69,8 @@ public class BpmTaskServiceImpl implements BpmTaskService{ | |||||||
|     private BpmTaskExtMapper taskExtMapper; |     private BpmTaskExtMapper taskExtMapper; | ||||||
|     @Resource |     @Resource | ||||||
|     private BpmMessageService messageService; |     private BpmMessageService messageService; | ||||||
|  |     @Resource | ||||||
|  |     private BpmTaskAssignRuleMapper taskAssignRuleMapper; | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public PageResult<BpmTaskTodoPageItemRespVO> getTodoTaskPage(Long userId, BpmTaskTodoPageReqVO pageVO) { |     public PageResult<BpmTaskTodoPageItemRespVO> getTodoTaskPage(Long userId, BpmTaskTodoPageReqVO pageVO) { | ||||||
| @@ -183,9 +188,22 @@ public class BpmTaskServiceImpl implements BpmTaskService{ | |||||||
|  |  | ||||||
|         // 完成任务,审批通过 |         // 完成任务,审批通过 | ||||||
|         taskService.complete(task.getId(), instance.getProcessVariables()); |         taskService.complete(task.getId(), instance.getProcessVariables()); | ||||||
|  |  | ||||||
|         // 更新任务拓展表为通过 |         // 更新任务拓展表为通过 | ||||||
|         taskExtMapper.updateByTaskId(new BpmTaskExtDO().setTaskId(task.getId()) |         taskExtMapper.updateByTaskId( | ||||||
|                 .setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()).setComment(reqVO.getComment())); |             new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()) | ||||||
|  |                 .setComment(reqVO.getComment())); | ||||||
|  |         // 判断任务是否为或签,或签时删除其余不用审批的任务 | ||||||
|  |         List<BpmTaskAssignRuleDO> bpmTaskAssignRuleList = | ||||||
|  |             taskAssignRuleMapper.selectListByProcessDefinitionId(task.getProcessDefinitionId(), | ||||||
|  |                 task.getTaskDefinitionKey()); | ||||||
|  |         if (CollUtil.isNotEmpty(bpmTaskAssignRuleList) && bpmTaskAssignRuleList.size() > 0) { | ||||||
|  |             if (BpmTaskAssignRuleTypeEnum.USER_OR_SIGN.getType().equals(bpmTaskAssignRuleList.get(0).getType())) { | ||||||
|  |                 taskExtMapper.updateUserOrSignTask( | ||||||
|  |                     (BpmTaskExtDO)new BpmTaskExtDO().setTaskId(task.getId()).setName(task.getName()) | ||||||
|  |                         .setProcessInstanceId(task.getProcessInstanceId()).setDeleted(true)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|   | |||||||
| @@ -0,0 +1,9 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> | ||||||
|  | <mapper namespace="cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmTaskExtMapper"> | ||||||
|  |  | ||||||
|  |      | ||||||
|  |     <update id="updateUserOrSignTask"> | ||||||
|  |         UPDATE bpm_task_ext SET deleted=1 WHERE process_instance_id = #{entity.processInstanceId} AND name = #{entity.name} AND task_id != #{entity.taskId} | ||||||
|  |     </update> | ||||||
|  | </mapper> | ||||||
| @@ -15,7 +15,7 @@ | |||||||
|         <el-form-item label="循环基数" key="loopCardinality"> |         <el-form-item label="循环基数" key="loopCardinality"> | ||||||
|           <el-input v-model="loopInstanceForm.loopCardinality" clearable @change="updateLoopCardinality" /> |           <el-input v-model="loopInstanceForm.loopCardinality" clearable @change="updateLoopCardinality" /> | ||||||
|         </el-form-item> |         </el-form-item> | ||||||
|         <el-form-item label="集合" key="collection"> |         <el-form-item label="集合" key="collection" v-show="false"> | ||||||
|           <el-input v-model="loopInstanceForm.collection" clearable @change="updateLoopBase" /> |           <el-input v-model="loopInstanceForm.collection" clearable @change="updateLoopBase" /> | ||||||
|         </el-form-item> |         </el-form-item> | ||||||
|         <el-form-item label="元素变量" key="elementVariable"> |         <el-form-item label="元素变量" key="elementVariable"> | ||||||
| @@ -131,7 +131,7 @@ export default { | |||||||
|       if (type === "SequentialMultiInstance") { |       if (type === "SequentialMultiInstance") { | ||||||
|         this.multiLoopInstance = window.bpmnInstances.moddle.create("bpmn:MultiInstanceLoopCharacteristics", { isSequential: true }); |         this.multiLoopInstance = window.bpmnInstances.moddle.create("bpmn:MultiInstanceLoopCharacteristics", { isSequential: true }); | ||||||
|       } else { |       } else { | ||||||
|         this.multiLoopInstance = window.bpmnInstances.moddle.create("bpmn:MultiInstanceLoopCharacteristics"); |         this.multiLoopInstance = window.bpmnInstances.moddle.create("bpmn:MultiInstanceLoopCharacteristics", { collection: "${coll_userList}" }); | ||||||
|       } |       } | ||||||
|       window.bpmnInstances.modeling.updateProperties(this.bpmnElement, { |       window.bpmnInstances.modeling.updateProperties(this.bpmnElement, { | ||||||
|         loopCharacteristics: this.multiLoopInstance |         loopCharacteristics: this.multiLoopInstance | ||||||
|   | |||||||
| @@ -53,7 +53,7 @@ | |||||||
|             <el-option v-for="item in postOptions" :key="parseInt(item.id)" :label="item.name" :value="parseInt(item.id)" /> |             <el-option v-for="item in postOptions" :key="parseInt(item.id)" :label="item.name" :value="parseInt(item.id)" /> | ||||||
|           </el-select> |           </el-select> | ||||||
|         </el-form-item> |         </el-form-item> | ||||||
|         <el-form-item v-if="form.type === 30" label="指定用户" prop="userIds"> |         <el-form-item v-if="form.type === 30 || form.type === 31 || form.type === 32" label="指定用户" prop="userIds"> | ||||||
|           <el-select v-model="form.userIds" multiple clearable style="width: 100%"> |           <el-select v-model="form.userIds" multiple clearable style="width: 100%"> | ||||||
|             <el-option v-for="item in userOptions" :key="parseInt(item.id)" :label="item.nickname" :value="parseInt(item.id)" /> |             <el-option v-for="item in userOptions" :key="parseInt(item.id)" :label="item.nickname" :value="parseInt(item.id)" /> | ||||||
|           </el-select> |           </el-select> | ||||||
| @@ -215,7 +215,7 @@ export default { | |||||||
|         this.form.deptIds.push(...row.options); |         this.form.deptIds.push(...row.options); | ||||||
|       } else if (row.type === 22) { |       } else if (row.type === 22) { | ||||||
|         this.form.postIds.push(...row.options); |         this.form.postIds.push(...row.options); | ||||||
|       } else if (row.type === 30) { |       } else if (row.type === 30 || row.type === 31 || row.type === 32) { | ||||||
|         this.form.userIds.push(...row.options); |         this.form.userIds.push(...row.options); | ||||||
|       } else if (row.type === 40) { |       } else if (row.type === 40) { | ||||||
|         this.form.userGroupIds.push(...row.options); |         this.form.userGroupIds.push(...row.options); | ||||||
| @@ -240,7 +240,7 @@ export default { | |||||||
|             form.options = form.deptIds; |             form.options = form.deptIds; | ||||||
|           } else if (form.type === 22) { |           } else if (form.type === 22) { | ||||||
|             form.options = form.postIds; |             form.options = form.postIds; | ||||||
|           } else if (form.type === 30) { |           } else if (form.type === 30 || form.type === 31 || form.type === 32) { | ||||||
|             form.options = form.userIds; |             form.options = form.userIds; | ||||||
|           } else if (form.type === 40) { |           } else if (form.type === 40) { | ||||||
|             form.options = form.userGroupIds; |             form.options = form.userGroupIds; | ||||||
| @@ -302,7 +302,7 @@ export default { | |||||||
|             return postOption.name; |             return postOption.name; | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|       } else if (type === 30) { |       } else if (type === 30 || type === 31 || type === 32) { | ||||||
|         for (const userOption of this.userOptions) { |         for (const userOption of this.userOptions) { | ||||||
|           if (userOption.id === option) { |           if (userOption.id === option) { | ||||||
|             return userOption.nickname; |             return userOption.nickname; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 kemengkai
					kemengkai