Merge branch 'develop' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into master-jdk17

This commit is contained in:
YunaiV
2025-07-25 21:18:11 +08:00
19 changed files with 250 additions and 124 deletions

View File

@@ -73,7 +73,7 @@ public class BpmApprovalDetailRespVO {
private List<UserSimpleBaseVO> candidateUsers; // 只包含未生成 ApprovalTaskInfo 的用户列表 private List<UserSimpleBaseVO> candidateUsers; // 只包含未生成 ApprovalTaskInfo 的用户列表
@Schema(description = "流程编号", example = "8761d8e0-0922-11f0-bd37-00ff1db677bf") @Schema(description = "流程编号", example = "8761d8e0-0922-11f0-bd37-00ff1db677bf")
private String processInstanceId; // 当且仅当该节点是子流程节点时才会有值CallActivity 的 processInstanceId 字段) private String processInstanceId; // 当且仅当该节点是子流程节点时才会有值CallActivity 的 calledProcessInstanceId 字段)
} }

View File

@@ -74,6 +74,7 @@ public interface ErrorCodeConstants {
ErrorCode CATEGORY_NOT_EXISTS = new ErrorCode(1_009_012_000, "流程分类不存在"); ErrorCode CATEGORY_NOT_EXISTS = new ErrorCode(1_009_012_000, "流程分类不存在");
ErrorCode CATEGORY_NAME_DUPLICATE = new ErrorCode(1_009_012_001, "流程分类名字【{}】重复"); ErrorCode CATEGORY_NAME_DUPLICATE = new ErrorCode(1_009_012_001, "流程分类名字【{}】重复");
ErrorCode CATEGORY_CODE_DUPLICATE = new ErrorCode(1_009_012_002, "流程分类编码【{}】重复"); ErrorCode CATEGORY_CODE_DUPLICATE = new ErrorCode(1_009_012_002, "流程分类编码【{}】重复");
ErrorCode CATEGORY_DELETE_FAIL_MODEL_USED = new ErrorCode(1_009_012_003, "删除失败,流程分类【{}】已被流程模型使用,请先删除对应的流程模型");
// ========== BPM 流程监听器 1-009-013-000 ========== // ========== BPM 流程监听器 1-009-013-000 ==========
ErrorCode PROCESS_LISTENER_NOT_EXISTS = new ErrorCode(1_009_013_000, "流程监听器不存在"); ErrorCode PROCESS_LISTENER_NOT_EXISTS = new ErrorCode(1_009_013_000, "流程监听器不存在");

View File

@@ -35,7 +35,7 @@ public enum BpmSimpleModelNodeTypeEnum implements ArrayValuable<Integer> {
// 50 ~ 条件分支 // 50 ~ 条件分支
CONDITION_NODE(50, "条件", "sequenceFlow"), // 用于构建流转条件的表达式 CONDITION_NODE(50, "条件", "sequenceFlow"), // 用于构建流转条件的表达式
CONDITION_BRANCH_NODE(51, "条件分支", "exclusiveGateway"), CONDITION_BRANCH_NODE(51, "条件分支", "exclusiveGateway"),
PARALLEL_BRANCH_NODE(52, "并行分支", "parallelGateway"), PARALLEL_BRANCH_NODE(52, "并行分支", "inclusiveGateway"), // 并行分支使用包容网关实现,条件表达式结果设置为 true
INCLUSIVE_BRANCH_NODE(53, "包容分支", "inclusiveGateway"), INCLUSIVE_BRANCH_NODE(53, "包容分支", "inclusiveGateway"),
ROUTER_BRANCH_NODE(54, "路由分支", "exclusiveGateway") ROUTER_BRANCH_NODE(54, "路由分支", "exclusiveGateway")
; ;

View File

@@ -18,10 +18,7 @@ import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.BpmnModel; import org.flowable.bpmn.model.*;
import org.flowable.bpmn.model.CallActivity;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.delegate.DelegateExecution; import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.runtime.ProcessInstance; import org.flowable.engine.runtime.ProcessInstance;
@@ -132,7 +129,7 @@ public class BpmTaskCandidateInvoker {
Long startUserId, String processDefinitionId, Map<String, Object> processVariables) { Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {
// 如果是 CallActivity 子流程,不进行计算候选人 // 如果是 CallActivity 子流程,不进行计算候选人
FlowElement flowElement = BpmnModelUtils.getFlowElementById(bpmnModel, activityId); FlowElement flowElement = BpmnModelUtils.getFlowElementById(bpmnModel, activityId);
if (flowElement instanceof CallActivity) { if (flowElement instanceof CallActivity || flowElement instanceof SubProcess) {
return new HashSet<>(); return new HashSet<>();
} }
// 审批类型非人工审核时,不进行计算候选人。原因是:后续会自动通过、不通过 // 审批类型非人工审核时,不进行计算候选人。原因是:后续会自动通过、不通过

View File

@@ -24,6 +24,7 @@ import static java.util.Collections.emptySet;
* @author 芋道源码 * @author 芋道源码
*/ */
@Component @Component
@Deprecated // 仅仅是表达式的示例,建议使用 BpmTaskCandidateStartUserDeptLeaderStrategy 替代
public class BpmTaskAssignLeaderExpression { public class BpmTaskAssignLeaderExpression {
@Resource @Resource

View File

@@ -16,6 +16,7 @@ import java.util.Set;
* @author 芋道源码 * @author 芋道源码
*/ */
@Component @Component
@Deprecated // 仅仅是表达式的示例,建议使用 BpmTaskCandidateStartUserStrategy 替代
public class BpmTaskAssignStartUserExpression { public class BpmTaskAssignStartUserExpression {
@Resource @Resource

View File

@@ -8,6 +8,7 @@ import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.BpmnModel; import org.flowable.bpmn.model.BpmnModel;
import org.flowable.common.engine.api.FlowableException; import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.impl.javax.el.PropertyNotFoundException;
import org.flowable.engine.delegate.DelegateExecution; import org.flowable.engine.delegate.DelegateExecution;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -48,10 +49,12 @@ public class BpmTaskCandidateExpressionStrategy implements BpmTaskCandidateStrat
Object result = FlowableUtils.getExpressionValue(variables, param); Object result = FlowableUtils.getExpressionValue(variables, param);
return CollectionUtils.toLinkedHashSet(Long.class, result); return CollectionUtils.toLinkedHashSet(Long.class, result);
} catch (FlowableException ex) { } catch (FlowableException ex) {
// 预测未运行的节点时候,表达式如果包含 execution 或者不存在的流程变量会抛异常, // 预测未运行的节点时候,表达式如果包含 execution 或者不存在的流程变量会抛异常,此时忽略该异常!相当于说,不做流程预测!!!
log.warn("[calculateUsersByActivity][表达式({}) 变量({}) 解析报错", param, variables, ex); if (ex.getCause() != null && ex.getCause() instanceof PropertyNotFoundException) {
// 不能预测候选人,返回空列表, 避免流程无法进行 return Sets.newHashSet();
return Sets.newHashSet(); }
log.error("[calculateUsersByActivity][表达式({}) 变量({}) 解析报错", param, variables, ex);
throw ex;
} }
} }

View File

@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener; package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
@@ -37,18 +38,26 @@ public class BpmProcessInstanceEventListener extends AbstractFlowableEngineEvent
@Override @Override
protected void processCreated(FlowableEngineEntityEvent event) { protected void processCreated(FlowableEngineEntityEvent event) {
processInstanceService.processProcessInstanceCreated((ProcessInstance)event.getEntity()); ProcessInstance processInstance = (ProcessInstance) event.getEntity();
FlowableUtils.execute(processInstance.getTenantId(),
() -> processInstanceService.processProcessInstanceCreated(processInstance));
} }
@Override @Override
protected void processCompleted(FlowableEngineEntityEvent event) { protected void processCompleted(FlowableEngineEntityEvent event) {
processInstanceService.processProcessInstanceCompleted((ProcessInstance)event.getEntity()); ProcessInstance processInstance = (ProcessInstance) event.getEntity();
FlowableUtils.execute(processInstance.getTenantId(),
() -> processInstanceService.processProcessInstanceCompleted(processInstance));
} }
@Override // 特殊情况:当跳转到 EndEvent 流程实例未结束, 会执行 deleteProcessInstance 方法 @Override
protected void processCancelled(FlowableCancelledEvent event) { protected void processCancelled(FlowableCancelledEvent event) {
// 特殊情况:当跳转到 EndEvent 流程实例未结束, 会执行 deleteProcessInstance 方法
ProcessInstance processInstance = processInstanceService.getProcessInstance(event.getProcessInstanceId()); ProcessInstance processInstance = processInstanceService.getProcessInstance(event.getProcessInstanceId());
processInstanceService.processProcessInstanceCompleted(processInstance); if (processInstance != null) {
FlowableUtils.execute(processInstance.getTenantId(),
() -> processInstanceService.processProcessInstanceCompleted(processInstance));
}
} }
} }

View File

@@ -3,10 +3,12 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
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.hutool.json.JSONObject;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmBoundaryEventTypeEnum; import cn.iocoder.yudao.module.bpm.enums.definition.BpmBoundaryEventTypeEnum;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService; import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService;
import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService; import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
@@ -58,17 +60,20 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener {
@Override @Override
protected void taskCreated(FlowableEngineEntityEvent event) { protected void taskCreated(FlowableEngineEntityEvent event) {
taskService.processTaskCreated((Task) event.getEntity()); Task entity = (Task) event.getEntity();
FlowableUtils.execute(entity.getTenantId(), () -> taskService.processTaskCreated(entity));
} }
@Override @Override
protected void taskAssigned(FlowableEngineEntityEvent event) { protected void taskAssigned(FlowableEngineEntityEvent event) {
taskService.processTaskAssigned((Task) event.getEntity()); Task entity = (Task) event.getEntity();
FlowableUtils.execute(entity.getTenantId(), () -> taskService.processTaskAssigned(entity));
} }
@Override @Override
protected void taskCompleted(FlowableEngineEntityEvent event) { protected void taskCompleted(FlowableEngineEntityEvent event) {
taskService.processTaskCompleted((Task) event.getEntity()); Task entity = (Task) event.getEntity();
FlowableUtils.execute(entity.getTenantId(), () -> taskService.processTaskCompleted(entity));
} }
@Override @Override
@@ -94,6 +99,23 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener {
String processDefinitionId = event.getProcessDefinitionId(); String processDefinitionId = event.getProcessDefinitionId();
BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processDefinitionId); BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processDefinitionId);
Job entity = (Job) event.getEntity(); Job entity = (Job) event.getEntity();
// 特殊 from https://t.zsxq.com/h6oWr :当 elementId 为空时,尝试从 JobHandlerConfiguration 中解析 JSON 获取
String elementId = entity.getElementId();
if (elementId == null && entity.getJobHandlerConfiguration() != null) {
try {
String handlerConfig = entity.getJobHandlerConfiguration();
if (handlerConfig.startsWith("{") && handlerConfig.contains("activityId")) {
elementId = new JSONObject(handlerConfig).getStr("activityId");
}
} catch (Exception e) {
log.error("[timerFired][解析 entity({}) 失败]", entity, e);
return;
}
}
if (elementId == null) {
log.error("[timerFired][解析 entity({}) elementId 为空,跳过处理]", entity);
return;
}
FlowElement element = BpmnModelUtils.getFlowElementById(bpmnModel, entity.getElementId()); FlowElement element = BpmnModelUtils.getFlowElementById(bpmnModel, entity.getElementId());
if (!(element instanceof BoundaryEvent)) { if (!(element instanceof BoundaryEvent)) {
return; return;

View File

@@ -108,7 +108,9 @@ public class BpmHttpRequestUtils {
Map<String, Object> processVariables = processInstance.getProcessVariables(); Map<String, Object> processVariables = processInstance.getProcessVariables();
MultiValueMap<String, String> body = new LinkedMultiValueMap<>(); MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
addHttpRequestParam(body, bodySettings, processVariables); addHttpRequestParam(body, bodySettings, processVariables);
body.add("processInstanceId", processInstance.getId()); if (!body.containsKey("processInstanceId")) { // 避免重复添加
body.add("processInstanceId", processInstance.getId());
}
return body; return body;
} }

View File

@@ -478,7 +478,11 @@ public class BpmnModelUtils {
*/ */
public static FlowElement getFlowElementById(BpmnModel model, String flowElementId) { public static FlowElement getFlowElementById(BpmnModel model, String flowElementId) {
Process process = model.getMainProcess(); Process process = model.getMainProcess();
return process.getFlowElement(flowElementId); FlowElement flowElement = process.getFlowElement(flowElementId);
if (flowElement != null) {
return flowElement;
}
return model.getFlowElement(flowElementId);
} }
/** /**

View File

@@ -4,7 +4,6 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.*; import cn.hutool.core.util.*;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.ConditionGroups; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.ConditionGroups;
@@ -17,13 +16,14 @@ import cn.iocoder.yudao.module.bpm.service.task.listener.BpmCallActivityListener
import cn.iocoder.yudao.module.bpm.service.task.listener.BpmUserTaskListener; import cn.iocoder.yudao.module.bpm.service.task.listener.BpmUserTaskListener;
import org.flowable.bpmn.BpmnAutoLayout; import org.flowable.bpmn.BpmnAutoLayout;
import org.flowable.bpmn.constants.BpmnXMLConstants; import org.flowable.bpmn.constants.BpmnXMLConstants;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*; import org.flowable.bpmn.model.*;
import org.flowable.bpmn.model.Process;
import org.flowable.engine.delegate.ExecutionListener; import org.flowable.engine.delegate.ExecutionListener;
import org.flowable.engine.delegate.TaskListener; import org.flowable.engine.delegate.TaskListener;
import java.util.*; import java.util.*;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*;
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.*; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.*;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
@@ -239,13 +239,13 @@ public class SimpleModelUtils {
// 3.1 分支有后续节点。即分支 1: A->B->C->D 的情况 // 3.1 分支有后续节点。即分支 1: A->B->C->D 的情况
if (isValidNode(conditionChildNode)) { if (isValidNode(conditionChildNode)) {
// 3.1.1 建立与后续的节点的连线。例如说,建立 A->B 的连线 // 3.1.1 建立与后续的节点的连线。例如说,建立 A->B 的连线
SequenceFlow sequenceFlow = ConditionNodeConvert.buildSequenceFlow(node.getId(), conditionChildNode.getId(), item); SequenceFlow sequenceFlow = ConditionNodeConvert.buildSequenceFlow(node.getId(), conditionChildNode.getId(), nodeType, item);
process.addFlowElement(sequenceFlow); process.addFlowElement(sequenceFlow);
// 3.1.2 递归调用后续节点连线。例如说,建立 B->C->D 的连线 // 3.1.2 递归调用后续节点连线。例如说,建立 B->C->D 的连线
traverseNodeToBuildSequenceFlow(process, conditionChildNode, branchEndNodeId); traverseNodeToBuildSequenceFlow(process, conditionChildNode, branchEndNodeId);
} else { } else {
// 3.2 分支没有后续节点。例如说,建立 A->D 的连线 // 3.2 分支没有后续节点。例如说,建立 A->D 的连线
SequenceFlow sequenceFlow = ConditionNodeConvert.buildSequenceFlow(node.getId(), branchEndNodeId, item); SequenceFlow sequenceFlow = ConditionNodeConvert.buildSequenceFlow(node.getId(), branchEndNodeId, nodeType, item);
process.addFlowElement(sequenceFlow); process.addFlowElement(sequenceFlow);
} }
} }
@@ -591,17 +591,23 @@ public class SimpleModelUtils {
private static class ParallelBranchNodeConvert implements NodeConvert { private static class ParallelBranchNodeConvert implements NodeConvert {
/**
* 并行分支使用包容网关。需要设置所有出口条件表达式的值为 true 。原因是,解决 https://t.zsxq.com/m6GXh 反馈问题
*
* @see {@link ConditionNodeConvert#buildSequenceFlow}
*/
@Override @Override
public List<ParallelGateway> convertList(BpmSimpleModelNodeVO node) { public List<InclusiveGateway> convertList(BpmSimpleModelNodeVO node) {
ParallelGateway parallelGateway = new ParallelGateway();
parallelGateway.setId(node.getId()); InclusiveGateway inclusiveGateway = new InclusiveGateway();
inclusiveGateway.setId(node.getId());
// TODO @jasonsetName // TODO @jasonsetName
// 并行聚合网关由程序创建,前端不需要传入 // 合网关 由程序创建,前端不需要传入
ParallelGateway joinParallelGateway = new ParallelGateway(); InclusiveGateway joinParallelGateway = new InclusiveGateway();
joinParallelGateway.setId(buildGatewayJoinId(node.getId())); joinParallelGateway.setId(buildGatewayJoinId(node.getId()));
// TODO @jasonsetName // TODO @jasonsetName
return CollUtil.newArrayList(parallelGateway, joinParallelGateway); return CollUtil.newArrayList(inclusiveGateway, joinParallelGateway);
} }
@Override @Override
@@ -652,8 +658,14 @@ public class SimpleModelUtils {
} }
public static SequenceFlow buildSequenceFlow(String sourceId, String targetId, public static SequenceFlow buildSequenceFlow(String sourceId, String targetId,
BpmSimpleModelNodeVO node) { BpmSimpleModelNodeTypeEnum nodeType, BpmSimpleModelNodeVO node) {
String conditionExpression = buildConditionExpression(node.getConditionSetting()); String conditionExpression;
// 并行分支,使用包容网关实现,强制设置条件表达式为 true
if (BpmSimpleModelNodeTypeEnum.PARALLEL_BRANCH_NODE == nodeType) {
conditionExpression ="${true}";
} else {
conditionExpression = buildConditionExpression(node.getConditionSetting());
}
return buildBpmnSequenceFlow(sourceId, targetId, node.getId(), node.getName(), conditionExpression); return buildBpmnSequenceFlow(sourceId, targetId, node.getId(), node.getName(), conditionExpression);
} }
} }
@@ -662,7 +674,6 @@ public class SimpleModelUtils {
* 构造条件表达式 * 构造条件表达式
*/ */
public static String buildConditionExpression(BpmSimpleModelNodeVO.ConditionSetting conditionSetting) { public static String buildConditionExpression(BpmSimpleModelNodeVO.ConditionSetting conditionSetting) {
// 并行网关不需要设置条件
if (conditionSetting == null) { if (conditionSetting == null) {
return null; return null;
} }
@@ -684,15 +695,18 @@ public class SimpleModelUtils {
if (conditionGroups == null || CollUtil.isEmpty(conditionGroups.getConditions())) { if (conditionGroups == null || CollUtil.isEmpty(conditionGroups.getConditions())) {
return null; return null;
} }
List<String> strConditionGroups = CollectionUtils.convertList(conditionGroups.getConditions(), item -> { List<String> strConditionGroups = convertList(conditionGroups.getConditions(), item -> {
if (CollUtil.isEmpty(item.getRules())) { if (CollUtil.isEmpty(item.getRules())) {
return ""; return "";
} }
// 构造规则表达式 // 构造规则表达式
List<String> list = CollectionUtils.convertList(item.getRules(), (rule) -> { List<String> list = convertList(item.getRules(), (rule) -> {
String rightSide = NumberUtil.isNumber(rule.getRightSide()) ? rule.getRightSide() String rightSide = NumberUtil.isNumber(rule.getRightSide()) ? rule.getRightSide()
: "\"" + rule.getRightSide() + "\""; // 如果非数值类型加引号 : "\"" + rule.getRightSide() + "\""; // 如果非数值类型加引号
return String.format(" %s %s var:convertByType(%s,%s)", rule.getLeftSide(), rule.getOpCode(), rule.getLeftSide(), rightSide); return String.format(" vars:getOrDefault(%s, null) %s var:convertByType(%s,%s) ",
rule.getLeftSide(), // 左侧:读取变量
rule.getOpCode(), // 中间:操作符,比较
rule.getLeftSide(), rightSide); // 右侧转换变量VariableConvertByTypeExpressionFunction
}); });
// 构造条件组的表达式 // 构造条件组的表达式
Boolean and = item.getAnd(); Boolean and = item.getAnd();

View File

@@ -34,6 +34,9 @@ public class BpmCategoryServiceImpl implements BpmCategoryService {
@Resource @Resource
private BpmCategoryMapper bpmCategoryMapper; private BpmCategoryMapper bpmCategoryMapper;
@Resource
private BpmModelService modelService;
@Override @Override
public Long createCategory(BpmCategorySaveReqVO createReqVO) { public Long createCategory(BpmCategorySaveReqVO createReqVO) {
// 校验唯一 // 校验唯一
@@ -77,15 +80,22 @@ public class BpmCategoryServiceImpl implements BpmCategoryService {
@Override @Override
public void deleteCategory(Long id) { public void deleteCategory(Long id) {
// 校验存在 // 校验存在
validateCategoryExists(id); BpmCategoryDO category = validateCategoryExists(id);
// 校验是否被流程模型使用
Long count = modelService.getModelCountByCategory(category.getCode());
if (count > 0) {
throw exception(CATEGORY_DELETE_FAIL_MODEL_USED, category.getName());
}
// 删除 // 删除
bpmCategoryMapper.deleteById(id); bpmCategoryMapper.deleteById(id);
} }
private void validateCategoryExists(Long id) { private BpmCategoryDO validateCategoryExists(Long id) {
if (bpmCategoryMapper.selectById(id) == null) { BpmCategoryDO category = bpmCategoryMapper.selectById(id);
if (category == null) {
throw exception(CATEGORY_NOT_EXISTS); throw exception(CATEGORY_NOT_EXISTS);
} }
return category;
} }
@Override @Override

View File

@@ -24,6 +24,14 @@ public interface BpmModelService {
*/ */
List<Model> getModelList(String name); List<Model> getModelList(String name);
/**
* 根据分类编码获得流程模型数量
*
* @param category 分类编码
* @return 流程模型数量
*/
Long getModelCountByCategory(String category);
/** /**
* 创建流程模型 * 创建流程模型
* *

View File

@@ -88,6 +88,14 @@ public class BpmModelServiceImpl implements BpmModelService {
return modelQuery.list(); return modelQuery.list();
} }
@Override
public Long getModelCountByCategory(String category) {
return repositoryService.createModelQuery()
.modelCategory(category)
.modelTenantId(FlowableUtils.getTenantId())
.count();
}
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public String createModel(@Valid BpmModelSaveReqVO createReqVO) { public String createModel(@Valid BpmModelSaveReqVO createReqVO) {

View File

@@ -4,11 +4,9 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil; 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.*;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
@@ -61,12 +59,15 @@ import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import java.util.*; 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.*; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
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;
@@ -264,7 +265,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
// 3. 获取下一个将要执行的节点集合 // 3. 获取下一个将要执行的节点集合
FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey()); FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey());
List<FlowNode> nextFlowNodes = BpmnModelUtils.getNextFlowNodes(flowElement, bpmnModel, processVariables); List<FlowNode> nextFlowNodes = BpmnModelUtils.getNextFlowNodes(flowElement, bpmnModel, processVariables);
List<ActivityNode> nextActivityNodes = convertList(nextFlowNodes, node -> new ActivityNode().setId(node.getId()) // 仅仅获取 UserTask 节点 TODO add from jason如果网关节点和网关节点相连获取下个 UserTask. 貌似有点不准。
List<FlowNode> nextUserTaskList = CollectionUtils.filterList(nextFlowNodes, node -> node instanceof UserTask);
List<ActivityNode> nextActivityNodes = convertList(nextUserTaskList, node -> new ActivityNode().setId(node.getId())
.setName(node.getName()).setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType()) .setName(node.getName()).setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())
.setStatus(BpmTaskStatusEnum.RUNNING.getStatus()) .setStatus(BpmTaskStatusEnum.RUNNING.getStatus())
.setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(node)) .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(node))
@@ -449,7 +452,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
.setNodeType(BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getType()).setStatus(processInstanceStatus) .setNodeType(BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getType()).setStatus(processInstanceStatus)
.setStartTime(DateUtils.of(activity.getStartTime())) .setStartTime(DateUtils.of(activity.getStartTime()))
.setEndTime(DateUtils.of(activity.getEndTime())) .setEndTime(DateUtils.of(activity.getEndTime()))
.setProcessInstanceId(activity.getProcessInstanceId()); .setProcessInstanceId(activity.getCalledProcessInstanceId());
approvalNodes.add(callActivity); approvalNodes.add(callActivity);
} }
}); });
@@ -521,7 +524,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
activityNode.setCandidateUserIds(CollUtil.sub(candidateUserIds, index + 1, candidateUserIds.size())); activityNode.setCandidateUserIds(CollUtil.sub(candidateUserIds, index + 1, candidateUserIds.size()));
} }
if (BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getType().equals(activityNode.getNodeType())) { if (BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getType().equals(activityNode.getNodeType())) {
activityNode.setProcessInstanceId(firstActivity.getProcessInstanceId()); activityNode.setProcessInstanceId(firstActivity.getCalledProcessInstanceId());
} }
return activityNode; return activityNode;
}); });
@@ -771,17 +774,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
processInstanceBuilder.predefineProcessInstanceId(processIdRedisDAO.generate(processIdRule)); processInstanceBuilder.predefineProcessInstanceId(processIdRedisDAO.generate(processIdRule));
} }
// 3.2 流程名称 // 3.2 流程名称
BpmModelMetaInfoVO.TitleSetting titleSetting = processDefinitionInfo.getTitleSetting(); processInstanceBuilder.name(generateProcessInstanceName(userId, definition, processDefinitionInfo, variables));
if (titleSetting != null && Boolean.TRUE.equals(titleSetting.getEnable())) {
AdminUserRespDTO user = adminUserApi.getUser(userId);
Map<String, Object> cloneVariables = new HashMap<>(variables);
cloneVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID, user.getNickname());
cloneVariables.put(BpmnVariableConstants.PROCESS_START_TIME, DateUtil.now());
cloneVariables.put(BpmnVariableConstants.PROCESS_DEFINITION_NAME, definition.getName().trim());
processInstanceBuilder.name(StrUtil.format(titleSetting.getTitle(), cloneVariables));
} else {
processInstanceBuilder.name(definition.getName().trim());
}
// 3.3 发起流程实例 // 3.3 发起流程实例
ProcessInstance instance = processInstanceBuilder.start(); ProcessInstance instance = processInstanceBuilder.start();
return instance.getId(); return instance.getId();
@@ -817,6 +810,25 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
}); });
} }
private String generateProcessInstanceName(Long userId,
ProcessDefinition definition,
BpmProcessDefinitionInfoDO definitionInfo,
Map<String, Object> variables) {
if (definition == null || definitionInfo == null) {
return null;
}
BpmModelMetaInfoVO.TitleSetting titleSetting = definitionInfo.getTitleSetting();
if (titleSetting == null || !BooleanUtil.isTrue(titleSetting.getEnable())) {
return definition.getName();
}
AdminUserRespDTO user = adminUserApi.getUser(userId);
Map<String, Object> cloneVariables = new HashMap<>(variables);
cloneVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID, user.getNickname());
cloneVariables.put(BpmnVariableConstants.PROCESS_START_TIME, DateUtil.now());
cloneVariables.put(BpmnVariableConstants.PROCESS_DEFINITION_NAME, definition.getName().trim());
return StrUtil.format(definitionInfo.getTitleSetting().getTitle(), cloneVariables);
}
@Override @Override
public void cancelProcessInstanceByStartUser(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) { public void cancelProcessInstanceByStartUser(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) {
// 1.1 校验流程实例存在 // 1.1 校验流程实例存在
@@ -833,7 +845,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
.getProcessDefinitionInfo(instance.getProcessDefinitionId()); .getProcessDefinitionInfo(instance.getProcessDefinitionId());
Assert.notNull(processDefinitionInfo, "流程定义({})不存在", processDefinitionInfo); Assert.notNull(processDefinitionInfo, "流程定义({})不存在", processDefinitionInfo);
if (processDefinitionInfo.getAllowCancelRunningProcess() != null // 防止未配置 AllowCancelRunningProcess , 默认为可取消 if (processDefinitionInfo.getAllowCancelRunningProcess() != null // 防止未配置 AllowCancelRunningProcess , 默认为可取消
&& Boolean.FALSE.equals(processDefinitionInfo.getAllowCancelRunningProcess())) { && BooleanUtil.isFalse(processDefinitionInfo.getAllowCancelRunningProcess())) {
throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW); throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW);
} }
// 1.4 子流程不允许取消 // 1.4 子流程不允许取消
@@ -900,64 +912,77 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
@Override @Override
public void processProcessInstanceCompleted(ProcessInstance instance) { public void processProcessInstanceCompleted(ProcessInstance instance) {
// 注意:需要基于 instance 设置租户编号,避免 Flowable 内部异步时,丢失租户编号 // 1.1 获取当前状态
FlowableUtils.execute(instance.getTenantId(), () -> { Integer status = (Integer) instance.getProcessVariables()
// 1.1 获取当前状态 .get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS);
Integer status = (Integer) instance.getProcessVariables() String reason = (String) instance.getProcessVariables()
.get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS); .get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON);
String reason = (String) instance.getProcessVariables() // 1.2 当流程状态还是审批状态中,说明审批通过了,则变更下它的状态
.get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON); // 为什么这么处理?因为流程完成,并且完成了,说明审批通过了
// 1.2 当流程状态还是审批状态中,说明审批通过了,则变更下它的状态 if (Objects.equals(status, BpmProcessInstanceStatusEnum.RUNNING.getStatus())) {
// 为什么这么处理?因为流程完成,并且完成了,说明审批通过了 status = BpmProcessInstanceStatusEnum.APPROVE.getStatus();
if (Objects.equals(status, BpmProcessInstanceStatusEnum.RUNNING.getStatus())) { runtimeService.setVariable(instance.getId(), BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS,
status = BpmProcessInstanceStatusEnum.APPROVE.getStatus(); status);
runtimeService.setVariable(instance.getId(), BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, }
status);
// 2. 发送对应的消息通知
if (Objects.equals(status, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) {
messageService.sendMessageWhenProcessInstanceApprove(
BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceApproveMessage(instance));
} else if (Objects.equals(status, BpmProcessInstanceStatusEnum.REJECT.getStatus())) {
messageService.sendMessageWhenProcessInstanceReject(
BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceRejectMessage(instance, reason));
}
// 3. 发送流程实例的状态事件
processInstanceEventPublisher.sendProcessInstanceResultEvent(
BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, instance, status));
// 4. 流程后置通知
if (Objects.equals(status, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) {
BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.
getProcessDefinitionInfo(instance.getProcessDefinitionId());
if (ObjUtil.isNotNull(processDefinitionInfo) &&
ObjUtil.isNotNull(processDefinitionInfo.getProcessAfterTriggerSetting())) {
BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getProcessAfterTriggerSetting();
BpmHttpRequestUtils.executeBpmHttpRequest(instance,
setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse());
} }
}
// 2. 发送对应的消息通知
if (Objects.equals(status, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) {
messageService.sendMessageWhenProcessInstanceApprove(
BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceApproveMessage(instance));
} else if (Objects.equals(status, BpmProcessInstanceStatusEnum.REJECT.getStatus())) {
messageService.sendMessageWhenProcessInstanceReject(
BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceRejectMessage(instance, reason));
}
// 3. 发送流程实例的状态事件
processInstanceEventPublisher.sendProcessInstanceResultEvent(
BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, instance, status));
// 4. 流程后置通知
if (Objects.equals(status, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) {
BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.
getProcessDefinitionInfo(instance.getProcessDefinitionId());
if (ObjUtil.isNotNull(processDefinitionInfo) &&
ObjUtil.isNotNull(processDefinitionInfo.getProcessAfterTriggerSetting())) {
BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getProcessAfterTriggerSetting();
BpmHttpRequestUtils.executeBpmHttpRequest(instance,
setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse());
}
}
});
} }
@Override @Override
public void processProcessInstanceCreated(ProcessInstance instance) { public void processProcessInstanceCreated(ProcessInstance instance) {
// 注意:需要基于 instance 设置租户编号,避免 Flowable 内部异步时,丢失租户编号 BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.
FlowableUtils.execute(instance.getTenantId(), () -> { getProcessDefinitionInfo(instance.getProcessDefinitionId());
// 流程前置通知 ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(instance.getProcessDefinitionId());
BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService. if (processDefinition == null || processDefinitionInfo == null) {
getProcessDefinitionInfo(instance.getProcessDefinitionId()); return;
if (ObjUtil.isNull(processDefinitionInfo) || }
ObjUtil.isNull(processDefinitionInfo.getProcessBeforeTriggerSetting())) {
return; // 自定义标题。目的:主要处理子流程的标题无法处理
// 注意:必须使用 TransactionSynchronizationManager 事务提交后,否则不生效!!!
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCommit() {
String name = generateProcessInstanceName(Long.valueOf(instance.getStartUserId()),
processDefinition, processDefinitionInfo, instance.getProcessVariables());
if (ObjUtil.notEqual(instance.getName(), name)) {
runtimeService.setProcessInstanceName(instance.getProcessInstanceId(), name);
}
} }
BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getProcessBeforeTriggerSetting();
BpmHttpRequestUtils.executeBpmHttpRequest(instance,
setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse());
}); });
// 流程前置通知
if (ObjUtil.isNull(processDefinitionInfo.getProcessBeforeTriggerSetting())) {
return;
}
BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getProcessBeforeTriggerSetting();
BpmHttpRequestUtils.executeBpmHttpRequest(instance,
setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse());
} }
} }

View File

@@ -230,10 +230,10 @@ public class BpmTaskServiceImpl implements BpmTaskService {
if (StrUtil.isNotBlank(pageVO.getName())) { if (StrUtil.isNotBlank(pageVO.getName())) {
taskQuery.taskNameLike("%" + pageVO.getName() + "%"); taskQuery.taskNameLike("%" + pageVO.getName() + "%");
} }
if (ArrayUtil.isNotEmpty(pageVO.getCreateTime())) { // if (ArrayUtil.isNotEmpty(pageVO.getCreateTime())) {
taskQuery.taskCreatedAfter(DateUtils.of(pageVO.getCreateTime()[0])); // taskQuery.taskCreatedAfter(DateUtils.of(pageVO.getCreateTime()[0]));
taskQuery.taskCreatedBefore(DateUtils.of(pageVO.getCreateTime()[1])); // taskQuery.taskCreatedBefore(DateUtils.of(pageVO.getCreateTime()[1]));
} // }
// 执行查询 // 执行查询
long count = taskQuery.count(); long count = taskQuery.count();
if (count == 0) { if (count == 0) {
@@ -244,6 +244,12 @@ public class BpmTaskServiceImpl implements BpmTaskService {
// 特殊:强制移除自动完成的“发起人”节点 // 特殊:强制移除自动完成的“发起人”节点
// 补充说明:由于 taskQuery 无法方面的过滤,所以暂时通过内存过滤 // 补充说明:由于 taskQuery 无法方面的过滤,所以暂时通过内存过滤
tasks.removeIf(task -> task.getTaskDefinitionKey().equals(START_USER_NODE_ID)); tasks.removeIf(task -> task.getTaskDefinitionKey().equals(START_USER_NODE_ID));
// TODO @芋艿https://t.zsxq.com/MNzqp 【flowable bug】taskCreatedAfter、taskCreatedBefore 拼接的是 OR
if (ArrayUtil.isNotEmpty(pageVO.getCreateTime())) {
tasks.removeIf(task -> task.getCreateTime() == null
|| task.getCreateTime().before(DateUtils.of(pageVO.getCreateTime()[0]))
|| task.getCreateTime().after(DateUtils.of(pageVO.getCreateTime()[1])));
}
return new PageResult<>(tasks, count); return new PageResult<>(tasks, count);
} }
@@ -259,16 +265,22 @@ public class BpmTaskServiceImpl implements BpmTaskService {
if (StrUtil.isNotEmpty(pageVO.getCategory())) { if (StrUtil.isNotEmpty(pageVO.getCategory())) {
taskQuery.taskCategory(pageVO.getCategory()); taskQuery.taskCategory(pageVO.getCategory());
} }
if (ArrayUtil.isNotEmpty(pageVO.getCreateTime())) { // if (ArrayUtil.isNotEmpty(pageVO.getCreateTime())) {
taskQuery.taskCreatedAfter(DateUtils.of(pageVO.getCreateTime()[0])); // taskQuery.taskCreatedAfter(DateUtils.of(pageVO.getCreateTime()[0]));
taskQuery.taskCreatedBefore(DateUtils.of(pageVO.getCreateTime()[1])); // taskQuery.taskCreatedBefore(DateUtils.of(pageVO.getCreateTime()[1]));
} // }
// 执行查询 // 执行查询
long count = taskQuery.count(); long count = taskQuery.count();
if (count == 0) { if (count == 0) {
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());
// TODO @芋艿https://t.zsxq.com/MNzqp 【flowable bug】taskCreatedAfter、taskCreatedBefore 拼接的是 OR
if (ArrayUtil.isNotEmpty(pageVO.getCreateTime())) {
tasks.removeIf(task -> task.getCreateTime() == null
|| task.getCreateTime().before(DateUtils.of(pageVO.getCreateTime()[0]))
|| task.getCreateTime().after(DateUtils.of(pageVO.getCreateTime()[1])));
}
return new PageResult<>(tasks, count); return new PageResult<>(tasks, count);
} }
@@ -886,7 +898,9 @@ public class BpmTaskServiceImpl implements BpmTaskService {
if (!returnTaskKeyList.contains(task.getTaskDefinitionKey())) { if (!returnTaskKeyList.contains(task.getTaskDefinitionKey())) {
return; return;
} }
runExecutionIds.add(task.getExecutionId()); if (task.getExecutionId() != null) {
runExecutionIds.add(task.getExecutionId());
}
// 判断是否分配给自己任务,因为会签任务,一个节点会有多个任务 // 判断是否分配给自己任务,因为会签任务,一个节点会有多个任务
if (isAssignUserTask(userId, task)) { // 情况一:自己的任务,进行 RETURN 标记 if (isAssignUserTask(userId, task)) { // 情况一:自己的任务,进行 RETURN 标记
@@ -1367,7 +1381,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE, String.class)); PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE, String.class));
if (userTaskElement.getId().equals(START_USER_NODE_ID) if (userTaskElement.getId().equals(START_USER_NODE_ID)
&& (skipStartUserNodeFlag == null // 目的:一般是“主流程”,发起人节点,自动通过审核 && (skipStartUserNodeFlag == null // 目的:一般是“主流程”,发起人节点,自动通过审核
|| Boolean.TRUE.equals(skipStartUserNodeFlag)) // 目的:一般是“子流程”,发起人节点,按配置自动通过审核 || BooleanUtil.isTrue(skipStartUserNodeFlag)) // 目的:一般是“子流程”,发起人节点,按配置自动通过审核
&& ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) { && ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) {
getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId())
.setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE.getReason())); .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE.getReason()));

View File

@@ -85,10 +85,15 @@ public class BpmCallActivityListener implements ExecutionListener {
// 2.2 使用表单值,并兜底字符串转 Long 失败时使用主流程发起人 // 2.2 使用表单值,并兜底字符串转 Long 失败时使用主流程发起人
try { try {
FlowableUtils.setAuthenticatedUserId(Long.parseLong(formFieldValue)); FlowableUtils.setAuthenticatedUserId(Long.parseLong(formFieldValue));
} catch (Exception e) { } catch (NumberFormatException ex) {
log.error("[notify][监听器:{},子流程监听器设置流程的发起人字符串转 Long 失败,字符串:{}]", try {
DELEGATE_EXPRESSION, formFieldValue); List<Long> formFieldValues = JsonUtils.parseArray(formFieldValue, Long.class);
FlowableUtils.setAuthenticatedUserId(Long.parseLong(processInstance.getStartUserId())); FlowableUtils.setAuthenticatedUserId(formFieldValues.get(0));
} catch (Exception e) {
log.error("[notify][监听器:{},子流程监听器设置流程的发起人字符串转 Long 失败,字符串:{}]",
DELEGATE_EXPRESSION, formFieldValue);
FlowableUtils.setAuthenticatedUserId(Long.parseLong(processInstance.getStartUserId()));
}
} }
} }
} }

View File

@@ -303,7 +303,9 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
throw exception(SECKILL_JOIN_ACTIVITY_TIME_ERROR); throw exception(SECKILL_JOIN_ACTIVITY_TIME_ERROR);
} }
SeckillConfigDO config = seckillConfigService.getCurrentSeckillConfig(); SeckillConfigDO config = seckillConfigService.getCurrentSeckillConfig();
if (config == null || !CollectionUtil.contains(activity.getConfigIds(), config.getId())) { if (config == null
|| !CollectionUtil.contains(activity.getConfigIds(), config.getId())
|| !LocalDateTimeUtils.isBetween(config.getStartTime(), config.getEndTime())) {
throw exception(SECKILL_JOIN_ACTIVITY_TIME_ERROR); throw exception(SECKILL_JOIN_ACTIVITY_TIME_ERROR);
} }
// 1.3 超过单次购买限制 // 1.3 超过单次购买限制