From 57add7b6e7939642ad3bd4240cbaf0c9af324ec6 Mon Sep 17 00:00:00 2001 From: dhb52 Date: Mon, 21 Jul 2025 16:05:50 +0000 Subject: [PATCH 1/6] =?UTF-8?q?feat:=20=20sql/tools=20=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E8=BE=93=E5=87=BA=E5=88=B0=20stderr?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: dhb52 --- sql/tools/convertor.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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): From 1684706e9aa70df8675b4b6bba212b7f5541d078 Mon Sep 17 00:00:00 2001 From: jason <2667446@qq.com> Date: Tue, 29 Jul 2025 09:36:53 +0800 Subject: [PATCH 2/6] =?UTF-8?q?feat:=20[BPM=20=E5=B7=A5=E4=BD=9C=E6=B5=81]?= =?UTF-8?q?=20=E5=A2=9E=E5=8A=A0=E8=B7=B3=E8=BF=87=E8=A1=A8=E8=BE=BE?= =?UTF-8?q?=E5=BC=8F=E5=88=A4=E6=96=AD=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vo/model/simple/BpmSimpleModelNodeVO.java | 3 ++ .../bpm/enums/task/BpmTaskStatusEnum.java | 2 +- .../flowable/core/util/BpmnModelUtils.java | 30 +++++++++++++++++-- .../flowable/core/util/SimpleModelUtils.java | 18 ++++++++++- .../task/BpmProcessInstanceServiceImpl.java | 26 +++++++++++++--- 5 files changed, 71 insertions(+), 8 deletions(-) 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 1cbebe06e4..4d34df8308 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/task/BpmTaskStatusEnum.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmTaskStatusEnum.java index a19f122bd8..9153b3e780 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 @@ -13,7 +13,7 @@ import lombok.Getter; @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/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 5e9313840b..b016666a8a 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,9 +464,15 @@ public class SimpleModelUtils { addReasonRequire(node.getReasonRequire(), userTask); // 节点类型 addNodeType(node.getType(), userTask); + // 添加跳过表达式 + if (StrUtil.isNotEmpty(node.getSkipExpression())) { + userTask.setSkipExpression(node.getSkipExpression()); + } return userTask; } + + private void addUserTaskListener(BpmSimpleModelNodeVO node, UserTask userTask) { List flowableListeners = new ArrayList<>(3); if (node.getTaskCreateListener() != null @@ -967,7 +973,7 @@ public class SimpleModelUtils { || nodeType == BpmSimpleModelNodeTypeEnum.COPY_NODE || nodeType == BpmSimpleModelNodeTypeEnum.CHILD_PROCESS || nodeType == BpmSimpleModelNodeTypeEnum.END_NODE) { - // 添加元素 + // 添加此节点 resultNodes.add(currentNode); } @@ -1013,6 +1019,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 85d15a6327..524dce20c1 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,15 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService return approvalNodes; } + + /** + * 获取结束节点的状态 + */ + private Integer getEndActivityNodeStatus(HistoricTaskInstance task) { + Integer status = FlowableUtils.getTaskStatus(task); + return status == null ? BpmTaskStatusEnum.SKIP.getStatus() : status; // 结束节点未获取到状态,为跳过状态 + } + /** * 获得【进行中】的活动节点们 */ @@ -565,10 +574,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 +621,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) { From e07fb6c8fe31e9cde0fd8a1cf46a5db3e1b01f55 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 29 Jul 2025 13:13:47 +0800 Subject: [PATCH 3/6] =?UTF-8?q?review=EF=BC=9A=E3=80=90bpm=20=E5=B7=A5?= =?UTF-8?q?=E4=BD=9C=E6=B5=81=E3=80=91usertask=20=E7=9A=84=E8=B7=B3?= =?UTF-8?q?=E8=BF=87=E8=A1=A8=E8=BE=BE=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../framework/web/core/handler/GlobalExceptionHandler.java | 6 +++--- .../yudao/module/bpm/enums/task/BpmTaskStatusEnum.java | 1 + .../bpm/framework/flowable/core/util/SimpleModelUtils.java | 4 +--- .../bpm/service/task/BpmProcessInstanceServiceImpl.java | 7 +++++-- 4 files changed, 10 insertions(+), 8 deletions(-) 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 e27d04ec68..2f103732cc 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 @@ -171,15 +171,15 @@ 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 { + } else { return defaultExceptionHandler(ServletUtils.getRequest(), ex); } } 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 9153b3e780..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 @@ -13,6 +13,7 @@ import lombok.Getter; @Getter @AllArgsConstructor public enum BpmTaskStatusEnum { + SKIP(-2, "跳过"), NOT_START(-1, "未开始"), RUNNING(1, "审批中"), 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 373f56339b..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 @@ -471,8 +471,6 @@ public class SimpleModelUtils { return userTask; } - - private void addUserTaskListener(BpmSimpleModelNodeVO node, UserTask userTask) { List flowableListeners = new ArrayList<>(3); if (node.getTaskCreateListener() != null @@ -1021,7 +1019,7 @@ public class SimpleModelUtils { } /** - * 根据跳过表达式,判断是否跳过此节点。 + * 根据跳过表达式,判断是否跳过此节点 */ public static boolean isSkipNode(BpmSimpleModelNodeVO currentNode, Map variables) { if (StrUtil.isEmpty(currentNode.getSkipExpression())) { 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 91815f8dd4..30adda6341 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 @@ -462,13 +462,16 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService return approvalNodes; } - /** * 获取结束节点的状态 */ private Integer getEndActivityNodeStatus(HistoricTaskInstance task) { Integer status = FlowableUtils.getTaskStatus(task); - return status == null ? BpmTaskStatusEnum.SKIP.getStatus() : status; // 结束节点未获取到状态,为跳过状态 + if (status != null) { + return status; + } + // 结束节点未获取到状态,为跳过状态。可见 bpmn 或者 simple 的 skipExpression + return BpmTaskStatusEnum.SKIP.getStatus(); } /** From ab7d9fdf72f96135efcdc21205d8eee1ba1a9149 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 29 Jul 2025 20:08:27 +0800 Subject: [PATCH 4/6] =?UTF-8?q?fix=EF=BC=9A=E3=80=90bpm=20=E5=B7=A5?= =?UTF-8?q?=E4=BD=9C=E6=B5=81=E3=80=91=E5=A7=94=E6=B4=BE=E3=80=81=E8=BD=AC?= =?UTF-8?q?=E5=8A=9E=E6=97=B6=EF=BC=8Cowner=20=E4=B8=8D=E9=87=8D=E5=A4=8D?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/service/task/BpmTaskServiceImpl.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) 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 1dbd0b17c0..8ea607a773 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()); From 176e87141eec2ff13ff089eb10bec9f15102a817 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 29 Jul 2025 22:24:20 +0800 Subject: [PATCH 5/6] =?UTF-8?q?feat=EF=BC=9AGlobalExceptionHandler=20?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=9B=B4=E5=A4=9A=E8=AF=B7=E6=B1=82=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E4=B8=8D=E5=AF=B9=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/handler/GlobalExceptionHandler.java | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) 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 2f103732cc..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 @@ -6,6 +6,8 @@ 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; @@ -14,8 +16,6 @@ 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; @@ -29,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; @@ -101,6 +102,9 @@ public class GlobalExceptionHandler { if (ex instanceof HttpRequestMethodNotSupportedException) { return httpRequestMethodNotSupportedExceptionHandler((HttpRequestMethodNotSupportedException) ex); } + if (ex instanceof HttpMediaTypeNotSupportedException) { + return httpMediaTypeNotSupportedExceptionHandler((HttpMediaTypeNotSupportedException) ex); + } if (ex instanceof ServiceException) { return serviceExceptionHandler((ServiceException) ex); } @@ -179,9 +183,11 @@ public class GlobalExceptionHandler { 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); } /** @@ -237,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 权限不足的异常 * From f71ec384568d6291fef8216d0a6897c4395a9f70 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Wed, 30 Jul 2025 19:06:44 +0800 Subject: [PATCH 6/6] =?UTF-8?q?=E3=80=90=E5=90=8C=E6=AD=A5=E3=80=91BOOT=20?= =?UTF-8?q?=E5=92=8C=20CLOUD=20=E7=9A=84=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/enums/ErrorCodeConstants.java | 3 +- .../core/util/BpmHttpRequestUtils.java | 50 +++++++++++++++---- .../module/crm/util/CrmPermissionUtils.java | 2 +- 3 files changed, 41 insertions(+), 14 deletions(-) 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/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 014b5e3f0d..358ee9f4dc 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-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()); }