diff --git a/sql/tools/convertor.py b/sql/tools/convertor.py index 7ab8ad1ef3..d286d3b238 100644 --- a/sql/tools/convertor.py +++ b/sql/tools/convertor.py @@ -17,6 +17,7 @@ uv run --with simple-ddl-parser convertor.py dm8 > ../dm/ruoyi-vue-pro-dm8.sql import argparse import pathlib import re +import sys import time from abc import ABC, abstractmethod from typing import Dict, Generator, Optional, Tuple, Union @@ -293,8 +294,10 @@ class Convertor(ABC): # 将parse失败的脚本打印出来 if error_scripts: + print("!!! 以下内容无法正常解析", file=sys.stderr) for script in error_scripts: - print(script) + # print to stderr + print(script, file=sys.stderr) class PostgreSQLConvertor(Convertor): diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java index 040760ffef..627a5ea784 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java @@ -5,6 +5,9 @@ import cn.hutool.core.exceptions.ExceptionUtil; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.servlet.JakartaServletUtil; +import cn.iocoder.yudao.framework.common.biz.infra.logger.ApiErrorLogCommonApi; +import cn.iocoder.yudao.framework.common.biz.infra.logger.dto.ApiErrorLogCreateReqDTO; import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; import cn.iocoder.yudao.framework.common.pojo.CommonResult; @@ -13,9 +16,11 @@ import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; -import cn.iocoder.yudao.framework.common.biz.infra.logger.ApiErrorLogCommonApi; -import cn.iocoder.yudao.framework.common.biz.infra.logger.dto.ApiErrorLogCreateReqDTO; import com.fasterxml.jackson.databind.exc.InvalidFormatException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; +import jakarta.validation.ValidationException; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.converter.HttpMessageNotReadableException; @@ -24,6 +29,7 @@ import org.springframework.util.Assert; import org.springframework.validation.BindException; import org.springframework.validation.FieldError; import org.springframework.validation.ObjectError; +import org.springframework.web.HttpMediaTypeNotSupportedException; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.MissingServletRequestParameterException; @@ -31,11 +37,8 @@ import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; import org.springframework.web.servlet.NoHandlerFoundException; +import org.springframework.web.servlet.resource.NoResourceFoundException; -import javax.servlet.http.HttpServletRequest; -import javax.validation.ConstraintViolation; -import javax.validation.ConstraintViolationException; -import javax.validation.ValidationException; import java.time.LocalDateTime; import java.util.List; import java.util.Map; @@ -93,12 +96,15 @@ public class GlobalExceptionHandler { if (ex instanceof NoHandlerFoundException) { return noHandlerFoundExceptionHandler((NoHandlerFoundException) ex); } -// if (ex instanceof NoResourceFoundException) { -// return noResourceFoundExceptionHandler(request, (NoResourceFoundException) ex); -// } + if (ex instanceof NoResourceFoundException) { + return noResourceFoundExceptionHandler(request, (NoResourceFoundException) ex); + } if (ex instanceof HttpRequestMethodNotSupportedException) { return httpRequestMethodNotSupportedExceptionHandler((HttpRequestMethodNotSupportedException) ex); } + if (ex instanceof HttpMediaTypeNotSupportedException) { + return httpMediaTypeNotSupportedExceptionHandler((HttpMediaTypeNotSupportedException) ex); + } if (ex instanceof ServiceException) { return serviceExceptionHandler((ServiceException) ex); } @@ -169,17 +175,19 @@ public class GlobalExceptionHandler { /** * 处理 SpringMVC 请求参数类型错误 * - * 例如说,接口上设置了 @RequestBody实体中 xx 属性类型为 Integer,结果传递 xx 参数类型为 String + * 例如说,接口上设置了 @RequestBody 实体中 xx 属性类型为 Integer,结果传递 xx 参数类型为 String */ @ExceptionHandler(HttpMessageNotReadableException.class) public CommonResult methodArgumentTypeInvalidFormatExceptionHandler(HttpMessageNotReadableException ex) { log.warn("[methodArgumentTypeInvalidFormatExceptionHandler]", ex); - if(ex.getCause() instanceof InvalidFormatException) { + if (ex.getCause() instanceof InvalidFormatException) { InvalidFormatException invalidFormatException = (InvalidFormatException) ex.getCause(); return CommonResult.error(BAD_REQUEST.getCode(), String.format("请求参数类型错误:%s", invalidFormatException.getValue())); - }else { - return defaultExceptionHandler(ServletUtils.getRequest(), ex); } + if (StrUtil.startWith(ex.getMessage(), "Required request body is missing")) { + return CommonResult.error(BAD_REQUEST.getCode(), "请求参数类型错误: request body 缺失"); + } + return defaultExceptionHandler(ServletUtils.getRequest(), ex); } /** @@ -215,14 +223,14 @@ public class GlobalExceptionHandler { return CommonResult.error(NOT_FOUND.getCode(), String.format("请求地址不存在:%s", ex.getRequestURL())); } -// /** -// * 处理 SpringMVC 请求地址不存在 -// */ -// @ExceptionHandler(NoResourceFoundException.class) -// private CommonResult noResourceFoundExceptionHandler(HttpServletRequest req, NoResourceFoundException ex) { -// log.warn("[noResourceFoundExceptionHandler]", ex); -// return CommonResult.error(NOT_FOUND.getCode(), String.format("请求地址不存在:%s", ex.getResourcePath())); -// } + /** + * 处理 SpringMVC 请求地址不存在 + */ + @ExceptionHandler(NoResourceFoundException.class) + private CommonResult noResourceFoundExceptionHandler(HttpServletRequest req, NoResourceFoundException ex) { + log.warn("[noResourceFoundExceptionHandler]", ex); + return CommonResult.error(NOT_FOUND.getCode(), String.format("请求地址不存在:%s", ex.getResourcePath())); + } /** * 处理 SpringMVC 请求方法不正确 @@ -235,6 +243,17 @@ public class GlobalExceptionHandler { return CommonResult.error(METHOD_NOT_ALLOWED.getCode(), String.format("请求方法不正确:%s", ex.getMessage())); } + /** + * 处理 SpringMVC 请求的 Content-Type 不正确 + * + * 例如说,A 接口的 Content-Type 为 application/json,结果请求的 Content-Type 为 application/octet-stream,导致不匹配 + */ + @ExceptionHandler(HttpMediaTypeNotSupportedException.class) + public CommonResult httpMediaTypeNotSupportedExceptionHandler(HttpMediaTypeNotSupportedException ex) { + log.warn("[httpMediaTypeNotSupportedExceptionHandler]", ex); + return CommonResult.error(BAD_REQUEST.getCode(), String.format("请求类型不正确:%s", ex.getMessage())); + } + /** * 处理 Spring Security 权限不足的异常 * @@ -325,12 +344,12 @@ public class GlobalExceptionHandler { errorLog.setApplicationName(applicationName); errorLog.setRequestUrl(request.getRequestURI()); Map requestParams = MapUtil.builder() - .put("query", ServletUtils.getParamMap(request)) - .put("body", ServletUtils.getBody(request)).build(); + .put("query", JakartaServletUtil.getParamMap(request)) + .put("body", JakartaServletUtil.getBody(request)).build(); errorLog.setRequestParams(JsonUtils.toJsonString(requestParams)); errorLog.setRequestMethod(request.getMethod()); errorLog.setUserAgent(ServletUtils.getUserAgent(request)); - errorLog.setUserIp(ServletUtils.getClientIP(request)); + errorLog.setUserIp(JakartaServletUtil.getClientIP(request)); errorLog.setExceptionTime(LocalDateTime.now()); } diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java index 6503a9e3b9..8541e38efe 100644 --- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java +++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java @@ -71,6 +71,9 @@ public class BpmSimpleModelNodeVO { @Schema(description = "是否填写审批意见", example = "false") private Boolean reasonRequire; + @Schema(description = "跳过表达式", example = "{true}") + private String skipExpression; // 用于审批节点 + /** * 审批节点拒绝处理 */ diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java index 4f7d7bf7ce..0e3b1b9208 100644 --- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java +++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java @@ -40,7 +40,7 @@ public interface ErrorCodeConstants { ErrorCode PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS = new ErrorCode(1_009_004_004, "任务({})的候选人({})不存在"); ErrorCode PROCESS_INSTANCE_START_USER_CAN_START = new ErrorCode(1_009_004_005, "发起流程失败,你没有权限发起该流程"); ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_005, "流程取消失败,该流程不允许取消"); - ErrorCode PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR = new ErrorCode(1_009_004_006, "流程 Http 触发器请求调用失败"); + ErrorCode PROCESS_INSTANCE_HTTP_CALL_ERROR = new ErrorCode(1_009_004_006, "流程 Http 请求调用失败"); ErrorCode PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_007, "下一个任务({})的审批人未配置"); ErrorCode PROCESS_INSTANCE_CANCEL_CHILD_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_008, "子流程取消失败,子流程不允许取消"); @@ -58,7 +58,6 @@ public interface ErrorCodeConstants { ErrorCode TASK_SIGN_DELETE_NO_PARENT = new ErrorCode(1_009_005_012, "任务减签失败,被减签的任务必须是通过加签生成的任务"); ErrorCode TASK_TRANSFER_FAIL_USER_REPEAT = new ErrorCode(1_009_005_013, "任务转办失败,转办人和当前审批人为同一人"); ErrorCode TASK_TRANSFER_FAIL_USER_NOT_EXISTS = new ErrorCode(1_009_005_014, "任务转办失败,转办人不存在"); - ErrorCode TASK_CREATE_FAIL_NO_CANDIDATE_USER = new ErrorCode(1_009_006_003, "操作失败,原因:找不到任务的审批人!"); ErrorCode TASK_SIGNATURE_NOT_EXISTS = new ErrorCode(1_009_005_015, "签名不能为空!"); ErrorCode TASK_REASON_REQUIRE = new ErrorCode(1_009_005_016, "审批意见不能为空!"); diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmTaskStatusEnum.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmTaskStatusEnum.java index a19f122bd8..9ba3b5cb3a 100644 --- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmTaskStatusEnum.java +++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmTaskStatusEnum.java @@ -14,6 +14,7 @@ import lombok.Getter; @AllArgsConstructor public enum BpmTaskStatusEnum { + SKIP(-2, "跳过"), NOT_START(-1, "未开始"), RUNNING(1, "审批中"), APPROVE(2, "审批通过"), diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java index bdef35883c..15ef72fb3a 100644 --- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java +++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java @@ -6,15 +6,15 @@ import cn.iocoder.yudao.framework.common.core.KeyValue; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.common.util.spring.SpringUtils; +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; +import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEvent; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; import cn.iocoder.yudao.module.bpm.enums.definition.BpmHttpRequestParamTypeEnum; import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; import com.fasterxml.jackson.core.type.TypeReference; import lombok.extern.slf4j.Slf4j; import org.flowable.engine.runtime.ProcessInstance; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpMethod; -import org.springframework.http.ResponseEntity; +import org.springframework.http.*; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestClientException; @@ -26,7 +26,7 @@ import java.util.Map; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; -import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR; +import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_INSTANCE_HTTP_CALL_ERROR; /** * 工作流发起 HTTP 请求工具类 @@ -42,7 +42,6 @@ public class BpmHttpRequestUtils { List bodyParams, Boolean handleResponse, List> response) { - RestTemplate restTemplate = SpringUtils.getBean(RestTemplate.class); BpmProcessInstanceService processInstanceService = SpringUtils.getBean(BpmProcessInstanceService.class); // 1.1 设置请求头 @@ -51,6 +50,7 @@ public class BpmHttpRequestUtils { MultiValueMap body = buildHttpBody(processInstance, bodyParams); // 2. 发起请求 + RestTemplate restTemplate = SpringUtils.getBean(RestTemplate.class); ResponseEntity responseEntity = sendHttpRequest(url, headers, body, restTemplate); // 3. 处理返回 @@ -78,27 +78,55 @@ public class BpmHttpRequestUtils { } } + public static void executeBpmHttpRequest(BpmProcessInstanceStatusEvent event, + String url) { + // 1.1 设置请求头 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + if (TenantContextHolder.getTenantId() != null) { + headers.add(HEADER_TENANT_ID, String.valueOf(TenantContextHolder.getTenantId())); + } else { + BpmProcessInstanceService processInstanceService = SpringUtils.getBean(BpmProcessInstanceService.class); + ProcessInstance processInstance = processInstanceService.getProcessInstance(event.getId()); + if (processInstance != null) { + headers.add(HEADER_TENANT_ID, String.valueOf(TenantContextHolder.getTenantId())); + } + } + // 1.2 设置请求体 +// MultiValueMap body = new LinkedMultiValueMap<>(); +// body.add("id", event.getId()); +// body.add("processDefinitionKey", event.getProcessDefinitionKey()); +// body.add("status", event.getStatus().toString()); +// if (StrUtil.isNotEmpty(event.getBusinessKey())) { +// body.add("businessKey", event.getBusinessKey()); +// } + + // 2. 发起请求 + RestTemplate restTemplate = SpringUtils.getBean(RestTemplate.class); + sendHttpRequest(url, headers, event, restTemplate); + } + public static ResponseEntity sendHttpRequest(String url, MultiValueMap headers, - MultiValueMap body, + Object body, RestTemplate restTemplate) { - HttpEntity> requestEntity = new HttpEntity<>(body, headers); + HttpEntity requestEntity = new HttpEntity<>(body, headers); ResponseEntity responseEntity; try { responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class); - log.info("[sendHttpRequest][HTTP 触发器,请求头:{},请求体:{},响应结果:{}]", headers, body, responseEntity); + log.info("[sendHttpRequest][HTTP 请求,请求头:{},请求体:{},响应结果:{}]", headers, body, responseEntity); } catch (RestClientException e) { - log.error("[sendHttpRequest][HTTP 触发器,请求头:{},请求体:{},请求出错:{}]", headers, body, e.getMessage()); - throw exception(PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR); + log.error("[sendHttpRequest][HTTP 请求,请求头:{},请求体:{},请求出错:{}]", headers, body, e.getMessage()); + throw exception(PROCESS_INSTANCE_HTTP_CALL_ERROR); } return responseEntity; } public static MultiValueMap buildHttpHeaders(ProcessInstance processInstance, List headerSettings) { - Map processVariables = processInstance.getProcessVariables(); MultiValueMap headers = new LinkedMultiValueMap<>(); headers.add(HEADER_TENANT_ID, processInstance.getTenantId()); + Map processVariables = processInstance.getProcessVariables(); addHttpRequestParam(headers, headerSettings, processVariables); return headers; } diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index 460af124f6..1bafa578db 100644 --- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -800,9 +800,10 @@ public class BpmnModelUtils { || currentElement instanceof EndEvent || currentElement instanceof UserTask || currentElement instanceof ServiceTask) { - // 添加元素 + // 添加节点 FlowNode flowNode = (FlowNode) currentElement; resultElements.add(flowNode); + // 遍历子节点 flowNode.getOutgoingFlows().forEach( nextElement -> simulateNextFlowElements(nextElement.getTargetFlowElement(), variables, resultElements, visitElements)); @@ -835,6 +836,31 @@ public class BpmnModelUtils { } } + /** + * 判断是否跳过此节点 + * + * @param flowNode 节点 + * @param variables 流程变量 + */ + public static boolean isSkipNode(FlowElement flowNode, Map variables) { + // 1. 检查节点是否有跳过表达式(支持多种任务节点类型) + String skipExpression = null; + if (flowNode instanceof UserTask) { + skipExpression = ((UserTask) flowNode).getSkipExpression(); + } else if (flowNode instanceof ServiceTask) { + skipExpression = ((ServiceTask) flowNode).getSkipExpression(); + } else if (flowNode instanceof ScriptTask) { + skipExpression = ((ScriptTask) flowNode).getSkipExpression(); + } + + if (StrUtil.isEmpty(skipExpression)) { + return false; + } + + // 2. 计算跳过表达式的值 + return evalConditionExpress(variables, skipExpression); + } + /** * 根据当前节点,获取下一个节点 * @@ -997,7 +1023,7 @@ public class BpmnModelUtils { * @return 是否满足条件 */ public static boolean evalConditionExpress(Map variables, String expression) { - if (expression == null) { + if (StrUtil.isEmpty(expression)) { return Boolean.FALSE; } // 如果 variables 为空,则创建一个的原因?可能 expression 的计算,不依赖于 variables diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index f3c00258e5..2582399a81 100644 --- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -464,6 +464,10 @@ public class SimpleModelUtils { addReasonRequire(node.getReasonRequire(), userTask); // 节点类型 addNodeType(node.getType(), userTask); + // 添加跳过表达式 + if (StrUtil.isNotEmpty(node.getSkipExpression())) { + userTask.setSkipExpression(node.getSkipExpression()); + } return userTask; } @@ -968,7 +972,7 @@ public class SimpleModelUtils { || nodeType == BpmSimpleModelNodeTypeEnum.COPY_NODE || nodeType == BpmSimpleModelNodeTypeEnum.CHILD_PROCESS || nodeType == BpmSimpleModelNodeTypeEnum.END_NODE) { - // 添加元素 + // 添加此节点 resultNodes.add(currentNode); } @@ -1014,6 +1018,16 @@ public class SimpleModelUtils { simulateNextNode(currentNode.getChildNode(), variables, resultNodes); } + /** + * 根据跳过表达式,判断是否跳过此节点 + */ + public static boolean isSkipNode(BpmSimpleModelNodeVO currentNode, Map variables) { + if (StrUtil.isEmpty(currentNode.getSkipExpression())) { + return false; + } + return BpmnModelUtils.evalConditionExpress(variables, currentNode.getSkipExpression()); + } + public static boolean evalConditionExpress(Map variables, BpmSimpleModelNodeVO.ConditionSetting conditionSetting) { return BpmnModelUtils.evalConditionExpress(variables, buildConditionExpression(conditionSetting)); } diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 6ee23c64a1..2bd43fdc4d 100644 --- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -398,7 +398,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService ? BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType() : ObjUtil.defaultIfNull(parseNodeType(flowNode), // 目的:解决“办理节点”的识别 BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())) - .setStatus(FlowableUtils.getTaskStatus(task)) + .setStatus(getEndActivityNodeStatus(task)) .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode)) .setStartTime(DateUtils.of(task.getCreateTime())).setEndTime(DateUtils.of(task.getEndTime())) .setTasks(singletonList(BpmProcessInstanceConvert.INSTANCE.buildApprovalTaskInfo(task))); @@ -462,6 +462,18 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService return approvalNodes; } + /** + * 获取结束节点的状态 + */ + private Integer getEndActivityNodeStatus(HistoricTaskInstance task) { + Integer status = FlowableUtils.getTaskStatus(task); + if (status != null) { + return status; + } + // 结束节点未获取到状态,为跳过状态。可见 bpmn 或者 simple 的 skipExpression + return BpmTaskStatusEnum.SKIP.getStatus(); + } + /** * 获得【进行中】的活动节点们 */ @@ -565,10 +577,14 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService if (runActivityIds.contains(node.getId())) { return null; } - + Integer status = BpmTaskStatusEnum.NOT_START.getStatus(); + // 如果节点被跳过。设置状态为跳过 + if (SimpleModelUtils.isSkipNode(node, processVariables)) { + status = BpmTaskStatusEnum.SKIP.getStatus(); + } ActivityNode activityNode = new ActivityNode().setId(node.getId()).setName(node.getName()) .setNodeType(node.getType()).setCandidateStrategy(node.getCandidateStrategy()) - .setStatus(BpmTaskStatusEnum.NOT_START.getStatus()); + .setStatus(status); // 1. 开始节点/审批节点 if (ObjectUtils.equalsAny(node.getType(), @@ -608,8 +624,13 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService if (runActivityIds.contains(node.getId())) { return null; } + Integer status = BpmTaskStatusEnum.NOT_START.getStatus(); + // 如果节点被跳过,状态设置为跳过 + if(BpmnModelUtils.isSkipNode(node, processVariables)){ + status = BpmTaskStatusEnum.SKIP.getStatus(); + } ActivityNode activityNode = new ActivityNode().setId(node.getId()) - .setStatus(BpmTaskStatusEnum.NOT_START.getStatus()); + .setStatus(status); // 1. 开始节点 if (node instanceof StartEvent) { diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index 4575f29401..bae0e3f714 100644 --- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -947,7 +947,10 @@ public class BpmTaskServiceImpl implements BpmTaskService { BpmCommentTypeEnum.DELEGATE_START.formatComment(currentUser.getNickname(), delegateUser.getNickname(), reqVO.getReason())); // 3.1 设置任务所有人 (owner) 为原任务的处理人 (assignee) - taskService.setOwner(taskId, task.getAssignee()); + // 特殊:如果已经被委派(owner 非空),则不需要更新 owner:https://gitee.com/zhijiantianya/yudao-cloud/issues/ICJ153 + if (StrUtil.isEmpty(task.getOwner())) { + taskService.setOwner(taskId, task.getAssignee()); + } // 3.2 执行委派,将任务委派给 delegateUser taskService.delegateTask(taskId, reqVO.getDelegateUserId().toString()); // 补充说明:委托不单独设置状态。如果需要,可通过 Task 的 DelegationState 字段,判断是否为 DelegationState.PENDING 委托中 @@ -973,7 +976,10 @@ public class BpmTaskServiceImpl implements BpmTaskService { BpmCommentTypeEnum.TRANSFER.formatComment(currentUser.getNickname(), assigneeUser.getNickname(), reqVO.getReason())); // 3.1 设置任务所有人 (owner) 为原任务的处理人 (assignee) - taskService.setOwner(taskId, task.getAssignee()); + // 特殊:如果已经被转派(owner 非空),则不需要更新 owner:https://gitee.com/zhijiantianya/yudao-cloud/issues/ICJ153 + if (StrUtil.isEmpty(task.getOwner())) { + taskService.setOwner(taskId, task.getAssignee()); + } // 3.2 执行转派(审批人),将任务转派给 assigneeUser // 委托( delegate)和转派(transfer)的差别,就在这块的调用!!!! taskService.setAssignee(taskId, reqVO.getAssigneeUserId().toString()); diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/util/CrmPermissionUtils.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/util/CrmPermissionUtils.java index d2818cd072..f3a0b5792b 100644 --- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/util/CrmPermissionUtils.java +++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/util/CrmPermissionUtils.java @@ -32,7 +32,7 @@ public class CrmPermissionUtils { * @return 是/否 */ public static boolean isCrmAdmin() { - PermissionApi permissionApi = SpringUtil.getBean(PermissionApi.class); + PermissionCommonApi permissionApi = SpringUtil.getBean(PermissionCommonApi.class); return permissionApi.hasAnyRoles(getLoginUserId(), RoleCodeEnum.CRM_ADMIN.getCode()); }