Merge remote-tracking branch 'origin/feature/bpm' into feature/bpm

This commit is contained in:
jason 2025-02-16 08:47:31 +08:00
commit 1f2222cf83
9 changed files with 75 additions and 14 deletions

View File

@ -25,6 +25,7 @@ public enum BpmSimpleModelNodeTypeEnum implements ArrayValuable<Integer> {
START_USER_NODE(10, "发起人", "userTask"), // 发起人节点前端的开始节点Id 固定 START_USER_NODE(10, "发起人", "userTask"), // 发起人节点前端的开始节点Id 固定
APPROVE_NODE(11, "审批人", "userTask"), APPROVE_NODE(11, "审批人", "userTask"),
COPY_NODE(12, "抄送人", "serviceTask"), COPY_NODE(12, "抄送人", "serviceTask"),
TRANSACTOR_NODE(13, "办理人", "userTask"),
DELAY_TIMER_NODE(14, "延迟器", "receiveTask"), DELAY_TIMER_NODE(14, "延迟器", "receiveTask"),
TRIGGER_NODE(15, "触发器", "serviceTask"), TRIGGER_NODE(15, "触发器", "serviceTask"),

View File

@ -1,6 +1,5 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model; package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model;
import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.framework.common.validation.InEnum; import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmAutoApproveTypeEnum; import cn.iocoder.yudao.module.bpm.enums.definition.BpmAutoApproveTypeEnum;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum; import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum;
@ -27,8 +26,7 @@ import java.util.List;
@Data @Data
public class BpmModelMetaInfoVO { public class BpmModelMetaInfoVO {
@Schema(description = "流程图标", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/yudao.jpg") @Schema(description = "流程图标", example = "https://www.iocoder.cn/yudao.jpg")
@NotEmpty(message = "流程图标不能为空")
@URL(message = "流程图标格式不正确") @URL(message = "流程图标格式不正确")
private String icon; private String icon;

View File

@ -85,6 +85,9 @@ public class BpmTaskRespVO {
@Schema(description = "是否填写审批意见", example = "false") @Schema(description = "是否填写审批意见", example = "false")
private Boolean reasonRequire; private Boolean reasonRequire;
@Schema(description = "节点类型", example = "10")
private Integer nodeType; // 参见 BpmSimpleModelNodeTypeEnum 枚举
@Data @Data
@Schema(description = "流程实例") @Schema(description = "流程实例")
public static class ProcessInstance { public static class ProcessInstance {

View File

@ -1,5 +1,7 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.enums; package cn.iocoder.yudao.module.bpm.framework.flowable.core.enums;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelTypeEnum;
/** /**
* BPMN XML 常量信息 * BPMN XML 常量信息
* *
@ -129,4 +131,11 @@ public interface BpmnModelConstants {
*/ */
String REASON_REQUIRE = "reasonRequire"; String REASON_REQUIRE = "reasonRequire";
/**
* 节点类型
*
* 目前只有 {@link BpmModelTypeEnum#SIMPLE} UserTask 节点会设置该属性用于区分是审批节点还是办理节点
*/
String NODE_TYPE = "nodeType";
} }

View File

@ -410,6 +410,26 @@ public class BpmnModelUtils {
return parseExtensionElement(flowElement, TRIGGER_PARAM); return parseExtensionElement(flowElement, TRIGGER_PARAM);
} }
/**
* 给节点添加节点类型
*
* @param nodeType 节点类型
* @param flowElement 节点
*/
public static void addNodeType(Integer nodeType, FlowElement flowElement) {
addExtensionElement(flowElement, BpmnModelConstants.NODE_TYPE, nodeType);
}
/**
* 解析节点类型
*
* @param flowElement 节点
* @return 节点类型
*/
public static Integer parseNodeType(FlowElement flowElement) {
return NumberUtils.parseInt(parseExtensionElement(flowElement, BpmnModelConstants.NODE_TYPE));
}
// ========== BPM 简单查找相关的方法 ========== // ========== BPM 简单查找相关的方法 ==========
/** /**

View File

@ -40,7 +40,7 @@ public class SimpleModelUtils {
static { static {
List<NodeConvert> converts = asList(new StartNodeConvert(), new EndNodeConvert(), List<NodeConvert> converts = asList(new StartNodeConvert(), new EndNodeConvert(),
new StartUserNodeConvert(), new ApproveNodeConvert(), new CopyNodeConvert(), new StartUserNodeConvert(), new ApproveNodeConvert(), new CopyNodeConvert(), new TransactorNodeConvert(),
new DelayTimerNodeConvert(), new TriggerNodeConvert(), new DelayTimerNodeConvert(), new TriggerNodeConvert(),
new ConditionBranchNodeConvert(), new ParallelBranchNodeConvert(), new InclusiveBranchNodeConvert(), new RouteBranchNodeConvert()); new ConditionBranchNodeConvert(), new ParallelBranchNodeConvert(), new InclusiveBranchNodeConvert(), new RouteBranchNodeConvert());
converts.forEach(convert -> NODE_CONVERTS.put(convert.getType(), convert)); converts.forEach(convert -> NODE_CONVERTS.put(convert.getType(), convert));
@ -445,6 +445,8 @@ public class SimpleModelUtils {
addSignEnable(node.getSignEnable(), userTask); addSignEnable(node.getSignEnable(), userTask);
// 审批意见 // 审批意见
addReasonRequire(node.getReasonRequire(), userTask); addReasonRequire(node.getReasonRequire(), userTask);
// 节点类型
addNodeType(node.getType(), userTask);
return userTask; return userTask;
} }
@ -514,6 +516,15 @@ public class SimpleModelUtils {
} }
private static class TransactorNodeConvert extends ApproveNodeConvert {
@Override
public BpmSimpleModelNodeTypeEnum getType() {
return BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE;
}
}
private static class CopyNodeConvert implements NodeConvert { private static class CopyNodeConvert implements NodeConvert {
@Override @Override
@ -785,10 +796,11 @@ public class SimpleModelUtils {
BpmSimpleModelNodeTypeEnum nodeType = BpmSimpleModelNodeTypeEnum.valueOf(currentNode.getType()); BpmSimpleModelNodeTypeEnum nodeType = BpmSimpleModelNodeTypeEnum.valueOf(currentNode.getType());
Assert.notNull(nodeType, "模型节点类型不支持"); Assert.notNull(nodeType, "模型节点类型不支持");
// 情况START_NODE/START_USER_NODE/APPROVE_NODE/COPY_NODE/END_NODE // 情况START_NODE/START_USER_NODE/APPROVE_NODE/COPY_NODE/END_NODE/TRANSACTOR_NODE
if (nodeType == BpmSimpleModelNodeTypeEnum.START_NODE if (nodeType == BpmSimpleModelNodeTypeEnum.START_NODE
|| nodeType == BpmSimpleModelNodeTypeEnum.START_USER_NODE || nodeType == BpmSimpleModelNodeTypeEnum.START_USER_NODE
|| nodeType == BpmSimpleModelNodeTypeEnum.APPROVE_NODE || nodeType == BpmSimpleModelNodeTypeEnum.APPROVE_NODE
|| nodeType == BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE
|| nodeType == BpmSimpleModelNodeTypeEnum.COPY_NODE || nodeType == BpmSimpleModelNodeTypeEnum.COPY_NODE
|| nodeType == BpmSimpleModelNodeTypeEnum.END_NODE) { || nodeType == BpmSimpleModelNodeTypeEnum.END_NODE) {
// 添加元素 // 添加元素

View File

@ -28,8 +28,7 @@ import java.util.*;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.addIfNotNull; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.addIfNotNull;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_DEFINITION_KEY_NOT_MATCH; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_DEFINITION_NAME_NOT_MATCH;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
/** /**
@ -156,16 +155,25 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
@Override @Override
public void updateProcessDefinitionState(String id, Integer state) { public void updateProcessDefinitionState(String id, Integer state) {
ProcessDefinition processDefinition = repositoryService.getProcessDefinition(id);
if (processDefinition == null) {
throw exception(PROCESS_DEFINITION_NOT_EXISTS);
}
// 激活 // 激活
if (Objects.equals(SuspensionState.ACTIVE.getStateCode(), state)) { if (Objects.equals(SuspensionState.ACTIVE.getStateCode(), state)) {
if (processDefinition.isSuspended()) {
repositoryService.activateProcessDefinitionById(id, false, null); repositoryService.activateProcessDefinitionById(id, false, null);
}
return; return;
} }
// 挂起 // 挂起
if (Objects.equals(SuspensionState.SUSPENDED.getStateCode(), state)) { if (Objects.equals(SuspensionState.SUSPENDED.getStateCode(), state)) {
// suspendProcessInstances = false进行中的任务不进行挂起 // suspendProcessInstances = false进行中的任务不进行挂起
// 原因只要新的流程不允许发起即可老流程继续可以执行 // 原因只要新的流程不允许发起即可老流程继续可以执行
if (!processDefinition.isSuspended()) {
repositoryService.suspendProcessDefinitionById(id, false, null); repositoryService.suspendProcessDefinitionById(id, false, null);
}
return; return;
} }
log.error("[updateProcessDefinitionState][流程定义({}) 修改未知状态({})]", id, state); log.error("[updateProcessDefinitionState][流程定义({}) 修改未知状态({})]", id, state);

View File

@ -5,6 +5,7 @@ import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
@ -67,6 +68,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
import static cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO.ActivityNode; import static cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO.ActivityNode;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.START_USER_NODE_ID; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.START_USER_NODE_ID;
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseNodeType;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static org.flowable.bpmn.constants.BpmnXMLConstants.*; import static org.flowable.bpmn.constants.BpmnXMLConstants.*;
@ -325,7 +327,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
ActivityNode activityNode = new ActivityNode().setId(task.getTaskDefinitionKey()).setName(task.getName()) ActivityNode activityNode = new ActivityNode().setId(task.getTaskDefinitionKey()).setName(task.getName())
.setNodeType(START_USER_NODE_ID.equals(task.getTaskDefinitionKey()) .setNodeType(START_USER_NODE_ID.equals(task.getTaskDefinitionKey())
? BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType() ? BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType()
: BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType()) : ObjUtil.defaultIfNull(parseNodeType(flowNode), // 目的解决办理节点的识别
BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType()))
.setStatus(FlowableUtils.getTaskStatus(task)) .setStatus(FlowableUtils.getTaskStatus(task))
.setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode)) .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode))
.setStartTime(DateUtils.of(task.getCreateTime())).setEndTime(DateUtils.of(task.getEndTime())) .setStartTime(DateUtils.of(task.getCreateTime())).setEndTime(DateUtils.of(task.getEndTime()))
@ -402,7 +405,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
HistoricActivityInstance firstActivity = CollUtil.getFirst(taskActivities); // 取第一个任务会签/或签的任务开始时间相同 HistoricActivityInstance firstActivity = CollUtil.getFirst(taskActivities); // 取第一个任务会签/或签的任务开始时间相同
ActivityNode activityNode = new ActivityNode().setId(firstActivity.getActivityId()) ActivityNode activityNode = new ActivityNode().setId(firstActivity.getActivityId())
.setName(firstActivity.getActivityName()) .setName(firstActivity.getActivityName())
.setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType()) .setNodeType(ObjUtil.defaultIfNull(parseNodeType(flowNode), // 目的解决办理节点的识别
BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType()))
.setStatus(BpmTaskStatusEnum.RUNNING.getStatus()) .setStatus(BpmTaskStatusEnum.RUNNING.getStatus())
.setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode)) .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode))
.setStartTime(DateUtils.of(CollUtil.getFirst(taskActivities).getStartTime())) .setStartTime(DateUtils.of(CollUtil.getFirst(taskActivities).getStartTime()))
@ -479,7 +483,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
// 1. 开始节点/审批节点 // 1. 开始节点/审批节点
if (ObjectUtils.equalsAny(node.getType(), if (ObjectUtils.equalsAny(node.getType(),
BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType(), BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType(),
BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())) { BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType(),
BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE.getType())) {
List<Long> candidateUserIds = getTaskCandidateUserList(bpmnModel, node.getId(), List<Long> candidateUserIds = getTaskCandidateUserList(bpmnModel, node.getId(),
startUserId, processDefinitionInfo.getProcessDefinitionId(), processVariables); startUserId, processDefinitionInfo.getProcessDefinitionId(), processVariables);
activityNode.setCandidateUserIds(candidateUserIds); activityNode.setCandidateUserIds(candidateUserIds);

View File

@ -61,6 +61,7 @@ import java.util.stream.Stream;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.START_USER_NODE_ID;
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG;
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.*; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.*;
@ -162,6 +163,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
bpmnModel, todoTask.getTaskDefinitionKey()); bpmnModel, todoTask.getTaskDefinitionKey());
Boolean signEnable = parseSignEnable(bpmnModel, todoTask.getTaskDefinitionKey()); Boolean signEnable = parseSignEnable(bpmnModel, todoTask.getTaskDefinitionKey());
Boolean reasonRequire = parseReasonRequire(bpmnModel, todoTask.getTaskDefinitionKey()); Boolean reasonRequire = parseReasonRequire(bpmnModel, todoTask.getTaskDefinitionKey());
Integer nodeType = parseNodeType(BpmnModelUtils.getFlowElementById(bpmnModel, todoTask.getTaskDefinitionKey()));
// 4. 任务表单 // 4. 任务表单
BpmFormDO taskForm = null; BpmFormDO taskForm = null;
@ -170,8 +172,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
} }
return BpmTaskConvert.INSTANCE.buildTodoTask(todoTask, childrenTasks, buttonsSetting, taskForm) return BpmTaskConvert.INSTANCE.buildTodoTask(todoTask, childrenTasks, buttonsSetting, taskForm)
.setSignEnable(signEnable) .setNodeType(nodeType).setSignEnable(signEnable).setReasonRequire(reasonRequire);
.setReasonRequire(reasonRequire);
} }
@Override @Override
@ -194,6 +195,10 @@ public class BpmTaskServiceImpl implements BpmTaskService {
return PageResult.empty(); return PageResult.empty();
} }
List<HistoricTaskInstance> tasks = taskQuery.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize()); List<HistoricTaskInstance> tasks = taskQuery.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize());
// 特殊强制移除自动完成的发起人节点
// 补充说明由于 taskQuery 无法方面的过滤所以暂时通过内存过滤
tasks.removeIf(task -> task.getTaskDefinitionKey().equals(START_USER_NODE_ID));
return new PageResult<>(tasks, count); return new PageResult<>(tasks, count);
} }