审批人范围

This commit is contained in:
Pancaihua 2024-11-07 23:24:53 +08:00
parent 10a954f007
commit 827aa93d05
18 changed files with 179 additions and 31 deletions

View File

@ -3,7 +3,10 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.RandomUtil;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.UserTask;
@ -12,9 +15,12 @@ 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.engine.runtime.ProcessInstance;
import org.flowable.task.service.TaskService;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.Resource;
import java.util.List;
import java.util.Set;
@ -35,6 +41,7 @@ public class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior {
super(userTask);
}
@Override
protected void handleAssignments(TaskService taskService, String assignee, String owner,
List<String> candidateUsers, List<String> candidateGroups, TaskEntity task, ExpressionManager expressionManager,
@ -52,7 +59,6 @@ public class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior {
if (super.multiInstanceActivityBehavior != null) {
return execution.getVariable(super.multiInstanceActivityBehavior.getCollectionElementVariable(), Long.class);
}
// 情况二如果非多实例的任务则计算任务处理人
// 第一步先计算可处理该任务的处理人们
Set<Long> candidateUserIds = taskCandidateInvoker.calculateUsers(execution);

View File

@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
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;
@ -14,7 +15,9 @@ import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.delegate.DelegateExecution;
import org.hibernate.validator.internal.util.stereotypes.Lazy;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -36,6 +39,10 @@ public class BpmTaskCandidateInvoker {
private final AdminUserApi adminUserApi;
//private BpmProcessInstanceService processInstanceService;
public BpmTaskCandidateInvoker(List<BpmTaskCandidateStrategy> strategyList,
AdminUserApi adminUserApi) {
strategyList.forEach(strategy -> {
@ -59,14 +66,18 @@ public class BpmTaskCandidateInvoker {
userTaskList.forEach(userTask -> {
// 1. 非空校验
Integer strategy = BpmnModelUtils.parseCandidateStrategy(userTask);
String range = BpmnModelUtils.parseCandidateRange(userTask);
String param = BpmnModelUtils.parseCandidateParam(userTask);
if (strategy == null) {
throw exception(MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG, userTask.getName());
}
BpmTaskCandidateStrategy candidateStrategy = getCandidateStrategy(strategy);
if (candidateStrategy.isParamRequired() && StrUtil.isBlank(param)) {
throw exception(MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG, userTask.getName());
if (!(strategy == 21 && ( range.equals("1") || range.equals("2")) )) { //审批人为部门负责人,选择了本部门或本公司的不检查
if (candidateStrategy.isParamRequired() && StrUtil.isBlank(param)) {
throw exception(MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG, userTask.getName());
}
}
// 2. 具体策略校验
getCandidateStrategy(strategy).validateParam(param);
});
@ -80,10 +91,26 @@ public class BpmTaskCandidateInvoker {
*/
@DataPermission(enable = false) // 忽略数据权限避免因为过滤导致找不到候选人
public Set<Long> calculateUsers(DelegateExecution execution) {
Set<Long> userIds = null;
Integer strategy = BpmnModelUtils.parseCandidateStrategy(execution.getCurrentFlowElement());
String range = BpmnModelUtils.parseCandidateRange(execution.getCurrentFlowElement());
String param = BpmnModelUtils.parseCandidateParam(execution.getCurrentFlowElement());
// 1.1 计算任务的候选人
Set<Long> userIds = getCandidateStrategy(strategy).calculateUsers(execution, param);
// 1.1 计算任务的候选人 审批人范围为本部门或本公司
if ( range.equals("1") || range.equals("2")) {
if (strategy == 21) { //审批人为部门负责人
userIds = getCandidateStrategy(strategy).calculateUsers2(execution, range);
}
if (strategy == 22) { //审批人为岗位
userIds = getCandidateStrategy(strategy).calculateUsers2(execution, range+"#"+param);
}
if (strategy == 10) { //审批人为角色
userIds = getCandidateStrategy(strategy).calculateUsers2(execution, range+"#"+param);
}
//userIds = getCandidateStrategy(strategy).calculateUsers2(execution, param);
}else{ //默认不选择审批人范围
userIds = getCandidateStrategy(strategy).calculateUsers(execution, param);
}
// 1.2 移除被禁用的用户
removeDisableUsers(userIds);
@ -116,4 +143,4 @@ public class BpmTaskCandidateInvoker {
return strategyObj;
}
}
}

View File

@ -35,7 +35,13 @@ public interface BpmTaskCandidateStrategy {
* @return 用户编号集合
*/
Set<Long> calculateUsers(DelegateExecution execution, String param);
/**
* 基于执行任务获得任务的候选用户们
*
* @param execution 执行任务
* @return 用户编号集合
*/
Set<Long> calculateUsers2(DelegateExecution execution, String param);
/**
* 是否一定要输入参数
*

View File

@ -33,7 +33,35 @@ public class BpmTaskAssignLeaderExpression {
@Resource
private BpmProcessInstanceService processInstanceService;
public boolean sameDept(DelegateExecution execution, int level,long userid) {
Assert.isTrue(level > 0, "level 必须大于 0");
// 获得发起人
ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());
Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId());
// 获得发起人对应 leve 的部门
DeptRespDTO startdept = null;
DeptRespDTO userdept = null;
for (int i = 0; i < level; i++) {
// 获得 level 对应的部门
if (startdept == null) {
startdept = getStartUserDept(startUserId);
userdept = getStartUserDept(userid);
if (startdept == null) { // 找不到发起人的部门所以无法使用该规则
return false;
}
} else {
DeptRespDTO startparentDept = deptApi.getDept(startdept.getParentId());
DeptRespDTO userparentDept = deptApi.getDept(userdept.getParentId());
if (startparentDept == null) { // 找不到父级部门所以只好结束寻找原因是例如说级别比较高的人所在部门层级比较少
break;
}
startdept = startparentDept;
userdept = userparentDept;
}
}
return startdept.getId() == userdept.getId() ? true : false;
}
/**
* 计算审批的候选人
*

View File

@ -1,11 +1,18 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.expression.BpmTaskAssignLeaderExpression;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
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 org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@ -13,6 +20,8 @@ import java.util.List;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
import static java.util.Collections.emptySet;
/**
* 部门的负责人 {@link BpmTaskCandidateStrategy} 实现类
@ -24,7 +33,9 @@ public class BpmTaskCandidateDeptLeaderStrategy implements BpmTaskCandidateStrat
@Resource
private DeptApi deptApi;
@Resource
@Lazy // 延迟加载避免循环依赖
private BpmTaskAssignLeaderExpression TaskAssignLeader;
@Override
public BpmTaskCandidateStrategyEnum getStrategy() {
return BpmTaskCandidateStrategyEnum.DEPT_LEADER;
@ -42,5 +53,9 @@ public class BpmTaskCandidateDeptLeaderStrategy implements BpmTaskCandidateStrat
List<DeptRespDTO> depts = deptApi.getDeptList(deptIds);
return convertSet(depts, DeptRespDTO::getLeaderUserId);
}
}
@Override
public Set<Long> calculateUsers2(DelegateExecution execution, String param) {
Set<Long> userid = TaskAssignLeader.calculateUsers(execution,Integer.parseInt(param));
return userid;
}
}

View File

@ -45,5 +45,8 @@ public class BpmTaskCandidateDeptMemberStrategy implements BpmTaskCandidateStrat
List<AdminUserRespDTO> users = adminUserApi.getUserListByDeptIds(deptIds);
return convertSet(users, AdminUserRespDTO::getId);
}
}
@Override
public Set<Long> calculateUsers2(DelegateExecution execution, String param) {
return StrUtils.splitToLongSet(param);
}
}

View File

@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
import cn.hutool.core.convert.Convert;
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
@ -32,5 +33,8 @@ public class BpmTaskCandidateExpressionStrategy implements BpmTaskCandidateStrat
Object result = FlowableUtils.getExpressionValue(execution, param);
return Convert.toSet(Long.class, result);
}
}
@Override
public Set<Long> calculateUsers2(DelegateExecution execution, String param) {
return StrUtils.splitToLongSet(param);
}
}

View File

@ -43,5 +43,8 @@ public class BpmTaskCandidateGroupStrategy implements BpmTaskCandidateStrategy {
List<BpmUserGroupDO> groups = userGroupService.getUserGroupList(groupIds);
return convertSetByFlatMap(groups, BpmUserGroupDO::getUserIds, Collection::stream);
}
}
@Override
public Set<Long> calculateUsers2(DelegateExecution execution, String param) {
return StrUtils.splitToLongSet(param);
}
}

View File

@ -2,11 +2,13 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.expression.BpmTaskAssignLeaderExpression;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
import cn.iocoder.yudao.module.system.api.dept.PostApi;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import org.flowable.engine.delegate.DelegateExecution;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@ -27,7 +29,9 @@ public class BpmTaskCandidatePostStrategy implements BpmTaskCandidateStrategy {
private PostApi postApi;
@Resource
private AdminUserApi adminUserApi;
@Resource
@Lazy // 延迟加载避免循环依赖
private BpmTaskAssignLeaderExpression TaskAssignLeader;
@Override
public BpmTaskCandidateStrategyEnum getStrategy() {
return BpmTaskCandidateStrategyEnum.POST;
@ -45,5 +49,17 @@ public class BpmTaskCandidatePostStrategy implements BpmTaskCandidateStrategy {
List<AdminUserRespDTO> users = adminUserApi.getUserListByPostIds(postIds);
return convertSet(users, AdminUserRespDTO::getId);
}
}
@Override
public Set<Long> calculateUsers2(DelegateExecution execution, String param) {
String[] params = param.split("#");
Set<Long> postIds = StrUtils.splitToLongSet(params[1]);
List<AdminUserRespDTO> users = adminUserApi.getUserListByPostIds(postIds);
for (int i = 0; i < users.size(); i++) {
if (!TaskAssignLeader.sameDept(execution, Integer.parseInt(params[0]),users.get(i).getId())) {
users.remove(i);
i--; // 删除元素后需要调整索引
}
}
return convertSet(users, AdminUserRespDTO::getId);
}
}

View File

@ -2,13 +2,16 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.expression.BpmTaskAssignLeaderExpression;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
import cn.iocoder.yudao.module.system.api.permission.RoleApi;
import org.flowable.engine.delegate.DelegateExecution;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Iterator;
import java.util.Set;
/**
@ -23,7 +26,9 @@ public class BpmTaskCandidateRoleStrategy implements BpmTaskCandidateStrategy {
private RoleApi roleApi;
@Resource
private PermissionApi permissionApi;
@Resource
@Lazy // 延迟加载避免循环依赖
private BpmTaskAssignLeaderExpression TaskAssignLeader;
@Override
public BpmTaskCandidateStrategyEnum getStrategy() {
return BpmTaskCandidateStrategyEnum.ROLE;
@ -40,5 +45,20 @@ public class BpmTaskCandidateRoleStrategy implements BpmTaskCandidateStrategy {
Set<Long> roleIds = StrUtils.splitToLongSet(param);
return permissionApi.getUserRoleIdListByRoleIds(roleIds);
}
@Override
public Set<Long> calculateUsers2(DelegateExecution execution, String param) {
String[] params = param.split("#");
Set<Long> roleIds = StrUtils.splitToLongSet(params[1]);
Set<Long> userids = permissionApi.getUserRoleIdListByRoleIds(roleIds);
Iterator<Long> iterator = userids.iterator();
while (iterator.hasNext()) {
Long userid = iterator.next();
}
if (!TaskAssignLeader.sameDept(execution, Integer.parseInt(params[0]),userid)) {
iterator.remove(); // 删除符合条件的元素
}
}
return userids;
}
}

View File

@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
@ -48,6 +49,9 @@ public class BpmTaskCandidateStartUserSelectStrategy implements BpmTaskCandidate
List<Long> assignees = startUserSelectAssignees.get(execution.getCurrentActivityId());
return new LinkedHashSet<>(assignees);
}
public Set<Long> calculateUsers2(DelegateExecution execution) {
return StrUtils.splitToLongSet("param");
}
@Override
public boolean isParamRequired() {
@ -72,5 +76,8 @@ public class BpmTaskCandidateStartUserSelectStrategy implements BpmTaskCandidate
BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy()));
return userTaskList;
}
@Override
public Set<Long> calculateUsers2(DelegateExecution execution, String param) {
return StrUtils.splitToLongSet(param);
}
}

View File

@ -35,5 +35,8 @@ public class BpmTaskCandidateUserStrategy implements BpmTaskCandidateStrategy {
public Set<Long> calculateUsers(DelegateExecution execution, String param) {
return StrUtils.splitToLongSet(param);
}
}
@Override
public Set<Long> calculateUsers2(DelegateExecution execution, String param) {
return StrUtils.splitToLongSet(param);
}
}

View File

@ -18,6 +18,11 @@ public interface BpmnModelConstants {
* BPMN UserTask 的扩展属性用于标记候选人策略
*/
String USER_TASK_CANDIDATE_STRATEGY = "candidateStrategy";
/**
* BPMN UserTask 的扩展属性用于标审批人范围
*/
String USER_TASK_CANDIDATE_RANGE = "candidateRange";
/**
* BPMN UserTask 的扩展属性用于标记候选人参数
*/

View File

@ -26,6 +26,10 @@ public class BpmnModelUtils {
BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM);
}
public static String parseCandidateRange(FlowElement userTask) {
return userTask.getAttributeValue(
BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_RANGE);
}
/**
* 根据节点获取入口连线
*

View File

@ -44,12 +44,12 @@ public class QjglPageReqVO extends PageParam {
@Schema(description = "作者", example = "赵六")
private String userName;
@Schema(description = "部门名称", example = "**部")
private String deptName;
@Schema(description = "部门id", example = "8402")
private Long deptId;
@Schema(description = "部门名称", example = "IT 部")
private String deptName;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;

View File

@ -25,7 +25,7 @@ public class homeDataPermissionConfiguration {
// rule.addDeptColumn(DeptDO.class, "id");
// user
// AdminUserDO 是想要使用权限的表 id 是对那个字段生效 比如 这个表 user_id 使用户id 那就使用 user_id
// rule.addUserColumn(AdminUserDO.class, "id");
rule.addUserColumn(QjglDO.class, "user_id");
};
}

View File

@ -1,4 +1 @@
/**
* system 模块的数据权限配置
*/
package cn.iocoder.yudao.module.home.framework.datapermission;

View File

@ -0,0 +1,4 @@
/**
* system 模块的数据权限配置
*/
package cn.iocoder.yudao.module.home.framework;