初步实现 BpmUserTaskActivitiBehavior 的规则

This commit is contained in:
YunaiV 2022-01-11 13:24:57 +08:00
parent 8999b7db3b
commit e863b60300
11 changed files with 234 additions and 14 deletions

View File

@ -80,6 +80,7 @@ public interface BpmModelConvert {
default BpmDefinitionCreateReqDTO convert2(Model model) {
BpmDefinitionCreateReqDTO createReqDTO = new BpmDefinitionCreateReqDTO();
createReqDTO.setModelId(model.getId());
createReqDTO.setName(model.getName());
createReqDTO.setKey(model.getKey());
createReqDTO.setCategory(model.getCategory());

View File

@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ProcessDefinition;
/**
@ -33,6 +34,12 @@ public class BpmProcessDefinitionExtDO extends BaseDO {
* 关联 {@link ProcessDefinition#getId()}
*/
private String processDefinitionId;
/**
* 流程模型的编号
*
* 关联 {@link Model#getId()}
*/
private String modelId;
/**
* 描述
*/

View File

@ -3,6 +3,7 @@ package cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.definition;
import cn.iocoder.yudao.adminserver.modules.bpm.enums.definition.BpmTaskRuleScriptEnum;
import cn.iocoder.yudao.adminserver.modules.bpm.enums.definition.BpmTaskRuleTypeEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.framework.mybatis.core.type.JsonLongSetTypeHandler;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@ -13,9 +14,14 @@ import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.task.Task;
import java.util.List;
import java.util.Set;
/**
* Bpm 任务规则表
* Bpm 任务规则表用于自定义配置每个任务的负责人的分配规则
* 也就是说废弃 BPMN 原本的 UserTask 设置的 assigneecandidateUsers 等配置而是通过使用该规则进行计算对应的负责人
*
* 1. 默认情况下{@link #processDefinitionId} {@link #PROCESS_DEFINITION_ID_NULL} 表示贵改则与流程模型关联
* 2. 在流程模型部署后会将他的所有规则记录复制出一份新部署出来的流程定义通过设置 {@link #processDefinitionId} 为新的流程定义的编号进行关联
*
* @author 芋道源码
*/
@ -28,6 +34,11 @@ import java.util.List;
@AllArgsConstructor
public class BpmTaskRuleDO extends BaseDO {
/**
* {@link #processDefinitionId} 空串用于标识属于流程模型而不属于流程定义
*/
private static final String PROCESS_DEFINITION_ID_NULL = "";
/**
* 编号
*/
@ -58,6 +69,7 @@ public class BpmTaskRuleDO extends BaseDO {
*
* 枚举 {@link BpmTaskRuleTypeEnum}
*/
@TableField("`type`")
private Integer type;
/**
* 规则值数组一般关联指定表的编号
@ -70,7 +82,7 @@ public class BpmTaskRuleDO extends BaseDO {
* 5. {@link BpmTaskRuleTypeEnum#USER_GROUP} 用户组编号
* 6. {@link BpmTaskRuleTypeEnum#SCRIPT} 脚本编号目前通过 {@link BpmTaskRuleScriptEnum#getId()} 标识
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private List<Long> values;
@TableField(typeHandler = JsonLongSetTypeHandler.class)
private Set<Long> options;
}

View File

@ -0,0 +1,21 @@
package cn.iocoder.yudao.adminserver.modules.bpm.dal.mysql.definition;
import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.definition.BpmTaskRuleDO;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.lang.Nullable;
import java.util.List;
@Mapper
public interface BpmTaskRuleMapper extends BaseMapperX<BpmTaskRuleDO> {
default List<BpmTaskRuleDO> selectListByProcessDefinitionId(String processDefinitionId,
@Nullable String taskDefinitionKey) {
return selectList(new QueryWrapperX<BpmTaskRuleDO>()
.eq("process_definition_id", processDefinitionId)
.eqIfPresent("task_definition_key", taskDefinitionKey));
}
}

View File

@ -2,6 +2,7 @@ package cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.config;
import cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.behavior.BpmActivityBehaviorFactory;
import cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.listener.BpmTackActivitiEventListener;
import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmTaskRuleService;
import org.activiti.spring.boot.ProcessEngineConfigurationConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -44,8 +45,10 @@ public class BpmActivitiConfiguration {
}
@Bean
public BpmActivityBehaviorFactory bpmActivityBehaviorFactory() {
return new BpmActivityBehaviorFactory();
public BpmActivityBehaviorFactory bpmActivityBehaviorFactory(BpmTaskRuleService taskRuleService) {
BpmActivityBehaviorFactory bpmActivityBehaviorFactory = new BpmActivityBehaviorFactory();
bpmActivityBehaviorFactory.setBpmTaskRuleService(taskRuleService);
return bpmActivityBehaviorFactory;
}
}

View File

@ -1,8 +1,10 @@
package cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.behavior;
import cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.behavior.BpmUserTaskActivitiBehavior;
import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmTaskRuleService;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Setter;
import lombok.ToString;
import org.activiti.bpmn.model.UserTask;
import org.activiti.engine.impl.bpmn.behavior.UserTaskActivityBehavior;
@ -19,9 +21,14 @@ import org.activiti.engine.impl.bpmn.parser.factory.DefaultActivityBehaviorFacto
@ToString(callSuper = true)
public class BpmActivityBehaviorFactory extends DefaultActivityBehaviorFactory {
@Setter
private BpmTaskRuleService bpmTaskRuleService;
@Override
public UserTaskActivityBehavior createUserTaskActivityBehavior(UserTask userTask) {
return new BpmUserTaskActivitiBehavior(userTask);
BpmUserTaskActivitiBehavior userTaskActivityBehavior = new BpmUserTaskActivitiBehavior(userTask);
userTaskActivityBehavior.setBpmTaskRuleService(bpmTaskRuleService);
return userTaskActivityBehavior;
}
// TODO 芋艿并行任务 ParallelMultiInstanceBehavior

View File

@ -1,6 +1,16 @@
package cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.behavior;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.definition.BpmTaskRuleDO;
import cn.iocoder.yudao.adminserver.modules.bpm.enums.definition.BpmTaskRuleTypeEnum;
import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmTaskRuleService;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
import lombok.Setter;
import org.activiti.bpmn.model.UserTask;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.impl.bpmn.behavior.UserTaskActivityBehavior;
import org.activiti.engine.impl.el.ExpressionManager;
@ -8,6 +18,8 @@ import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntityManager;
import java.util.List;
import java.util.Objects;
import java.util.Set;
/**
* 自定义的流程任务的 assignee 负责人的分配
@ -16,6 +28,9 @@ import java.util.List;
*/
public class BpmUserTaskActivitiBehavior extends UserTaskActivityBehavior {
@Setter
private BpmTaskRuleService bpmTaskRuleService;
public BpmUserTaskActivitiBehavior(UserTask userTask) {
super(userTask);
}
@ -24,8 +39,86 @@ public class BpmUserTaskActivitiBehavior extends UserTaskActivityBehavior {
protected void handleAssignments(TaskEntityManager taskEntityManager,
String assignee, String owner, List<String> candidateUsers, List<String> candidateGroups,
TaskEntity task, ExpressionManager expressionManager, DelegateExecution execution) {
System.out.println("");
taskEntityManager.changeTaskAssignee(task, "1");
// 获得任务的规则
BpmTaskRuleDO rule = getTaskRule(task);
// 获得任务的候选用户们
Set<Long> candidateUserIds = calculateTaskCandidateUsers(task, rule);
// 设置负责人
Long assigneeUserId = chooseTaskAssignee(candidateUserIds);
taskEntityManager.changeTaskAssignee(task, String.valueOf(assigneeUserId));
// 设置候选人们
candidateUserIds.remove(assigneeUserId); // 已经成为负责人了就不要在扮演候选人了
if (CollUtil.isNotEmpty(candidateUserIds)) {
task.addCandidateUsers(CollectionUtils.convertSet(candidateUserIds, String::valueOf));
}
}
private BpmTaskRuleDO getTaskRule(TaskEntity task) {
List<BpmTaskRuleDO> taskRules = bpmTaskRuleService.getTaskRulesByProcessDefinitionId(task.getProcessDefinitionId(),
task.getTaskDefinitionKey());
if (CollUtil.isEmpty(taskRules)) {
throw new ActivitiException(StrUtil.format("流程任务({}/{}/{}) 找不到符合的任务规则",
task.getId(), task.getProcessDefinitionId(), task.getTaskDefinitionKey()));
}
if (taskRules.size() > 1) {
throw new ActivitiException(StrUtil.format("流程任务({}/{}/{}) 找到过多任务规则({})",
task.getId(), task.getProcessDefinitionId(), task.getTaskDefinitionKey(), taskRules.size()));
}
return taskRules.get(0);
}
private Long chooseTaskAssignee(Set<Long> candidateUserIds) {
// TODO 芋艿未来可以优化下改成轮询的策略
int index = RandomUtil.randomInt(candidateUserIds.size());
return CollUtil.get(candidateUserIds, index);
}
private Set<Long> calculateTaskCandidateUsers(TaskEntity task, BpmTaskRuleDO rule) {
Set<Long> assigneeUserIds = null;
if (Objects.equals(BpmTaskRuleTypeEnum.ROLE.getType(), rule.getType())) {
assigneeUserIds = calculateTaskCandidateUsersByRole(task, rule);
} else if (Objects.equals(BpmTaskRuleTypeEnum.DEPT.getType(), rule.getType())) {
assigneeUserIds = calculateTaskCandidateUsersByDept(task, rule);
} else if (Objects.equals(BpmTaskRuleTypeEnum.DEPT_LEADER.getType(), rule.getType())) {
assigneeUserIds = calculateTaskCandidateUsersByDept(task, rule);
} else if (Objects.equals(BpmTaskRuleTypeEnum.USER.getType(), rule.getType())) {
assigneeUserIds = calculateTaskCandidateUsersByUser(task, rule);
} else if (Objects.equals(BpmTaskRuleTypeEnum.USER_GROUP.getType(), rule.getType())) {
assigneeUserIds = calculateTaskCandidateUsersByUser(task, rule);
}
// TODO 芋艿统一过滤掉被禁用的用户
// 如果候选人为空抛出异常 TODO 芋艿没候选人的策略选择1 - 挂起2 - 直接结束3 - 强制一个兜底人
if (CollUtil.isEmpty(assigneeUserIds)) {
throw new ActivitiException(StrUtil.format("流程任务({}/{}/{}) 任务规则({}) 找不到候选人",
task.getId(), task.getProcessDefinitionId(), task.getTaskDefinitionKey()));
}
return assigneeUserIds;
}
private Set<Long> calculateTaskCandidateUsersByRole(TaskEntity task, BpmTaskRuleDO rule) {
throw new UnsupportedOperationException("暂不支持该任务规则");
}
private Set<Long> calculateTaskCandidateUsersByDept(TaskEntity task, BpmTaskRuleDO rule) {
throw new UnsupportedOperationException("暂不支持该任务规则");
}
private Set<Long> calculateTaskCandidateUsersByDeptLeader(TaskEntity task, BpmTaskRuleDO rule) {
throw new UnsupportedOperationException("暂不支持该任务规则");
}
private Set<Long> calculateTaskCandidateUsersByUser(TaskEntity task, BpmTaskRuleDO rule) {
return rule.getOptions();
}
private Set<Long> calculateTaskCandidateUsersByUserGroup(TaskEntity task, BpmTaskRuleDO rule) {
throw new UnsupportedOperationException("暂不支持该任务规则");
}
private Set<Long> calculateTaskCandidateUsersByScript(TaskEntity task, BpmTaskRuleDO rule) {
throw new UnsupportedOperationException("暂不支持该任务规则");
}
}

View File

@ -0,0 +1,36 @@
package cn.iocoder.yudao.adminserver.modules.bpm.service.definition;
import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.definition.BpmTaskRuleDO;
import org.springframework.lang.Nullable;
import java.util.List;
/**
* BPM 任务规则 Service 接口
*
* @author 芋道源码
*/
public interface BpmTaskRuleService {
/**
* 获得流程定义的任务规则数组
*
* @param processDefinitionId 流程定义的编号
* @param taskDefinitionKey 流程任务定义的 Key允许空
* @return 任务规则数组
*/
List<BpmTaskRuleDO> getTaskRulesByProcessDefinitionId(String processDefinitionId,
@Nullable String taskDefinitionKey);
/**
* 获得流程模型的任务规则数组
*
* @param modelId 流程模型的编号
* @return 任务规则数组
*/
List<BpmTaskRuleDO> getTaskRulesByModelId(Long modelId);
// TODO 芋艿创建任务规则
// TODO 芋艿复制任务规则
}

View File

@ -10,6 +10,11 @@ import javax.validation.constraints.NotEmpty;
@Data
public class BpmDefinitionCreateReqDTO {
/**
* 流程模型的编号
*/
@NotEmpty(message = "流程模型编号不能为空")
private String modelId;
/**
* 流程标识
*/

View File

@ -214,11 +214,11 @@ public class BpmModelServiceImpl implements BpmModelService {
}
}
public static void main(String[] args) {
// 创建转换对象
BpmnXMLConverter converter = new BpmnXMLConverter();
BpmnModel bpmnModel = converter.convertToBpmnModel(new StringStreamSource(""), true, true);
bpmnModel.getProcesses().get(0).getId()
}
// public static void main(String[] args) {
// // 创建转换对象
// BpmnXMLConverter converter = new BpmnXMLConverter();
// BpmnModel bpmnModel = converter.convertToBpmnModel(new StringStreamSource(""), true, true);
// bpmnModel.getProcesses().get(0).getId()
// }
}

View File

@ -0,0 +1,35 @@
package cn.iocoder.yudao.adminserver.modules.bpm.service.definition.impl;
import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.definition.BpmTaskRuleDO;
import cn.iocoder.yudao.adminserver.modules.bpm.dal.mysql.definition.BpmTaskRuleMapper;
import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmTaskRuleService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.util.List;
/**
* BPM 任务规则 Service 实现类
*/
@Service
@Validated
@Slf4j
public class BpmTaskRuleServiceImpl implements BpmTaskRuleService {
@Resource
private BpmTaskRuleMapper taskRuleMapper;
@Override
public List<BpmTaskRuleDO> getTaskRulesByProcessDefinitionId(String processDefinitionId,
String taskDefinitionKey) {
return taskRuleMapper.selectListByProcessDefinitionId(processDefinitionId, taskDefinitionKey);
}
@Override
public List<BpmTaskRuleDO> getTaskRulesByModelId(Long modelId) {
return null;
}
}