diff --git a/pom.xml b/pom.xml
index e6ed08f211..bcb1982731 100644
--- a/pom.xml
+++ b/pom.xml
@@ -32,7 +32,7 @@
https://github.com/YunaiV/ruoyi-vue-pro
- 2.6.1-SNAPSHOT
+ 2025.08-SNAPSHOT
17
${java.version}
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-dependencies/pom.xml b/yudao-dependencies/pom.xml
index b6ca6f79d6..3ea8b7bb56 100644
--- a/yudao-dependencies/pom.xml
+++ b/yudao-dependencies/pom.xml
@@ -14,7 +14,7 @@
https://github.com/YunaiV/ruoyi-vue-pro
- 2.6.1-SNAPSHOT
+ 2025.08-SNAPSHOT
1.7.0
3.5.3
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..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);
}
@@ -171,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);
}
/**
@@ -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 权限不足的异常
*
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/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 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