diff --git a/yudao-framework/yudao-spring-boot-starter-protection/pom.xml b/yudao-framework/yudao-spring-boot-starter-protection/pom.xml
index da4570b94..86f83d8f1 100644
--- a/yudao-framework/yudao-spring-boot-starter-protection/pom.xml
+++ b/yudao-framework/yudao-spring-boot-starter-protection/pom.xml
@@ -35,6 +35,13 @@
lock4j-redisson-spring-boot-starter
true
+
+
+
+ cn.iocoder.cloud
+ yudao-spring-boot-starter-test
+ test
+
diff --git a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/config/YudaoApiSignatureAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/config/YudaoApiSignatureAutoConfiguration.java
new file mode 100644
index 000000000..7c6842408
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/config/YudaoApiSignatureAutoConfiguration.java
@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.framework.signature.config;
+
+import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration;
+import cn.iocoder.yudao.framework.signature.core.aop.ApiSignatureAspect;
+import cn.iocoder.yudao.framework.signature.core.redis.ApiSignatureRedisDAO;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.data.redis.core.StringRedisTemplate;
+
+/**
+ * HTTP API 签名的自动配置类
+ *
+ * @author Zhougang
+ */
+@AutoConfiguration(after = YudaoRedisAutoConfiguration.class)
+public class YudaoApiSignatureAutoConfiguration {
+
+ @Bean
+ public ApiSignatureAspect signatureAspect(ApiSignatureRedisDAO signatureRedisDAO) {
+ return new ApiSignatureAspect(signatureRedisDAO);
+ }
+
+ @Bean
+ public ApiSignatureRedisDAO signatureRedisDAO(StringRedisTemplate stringRedisTemplate) {
+ return new ApiSignatureRedisDAO(stringRedisTemplate);
+ }
+
+}
diff --git a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/annotation/ApiSignature.java b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/annotation/ApiSignature.java
new file mode 100644
index 000000000..281bcec97
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/annotation/ApiSignature.java
@@ -0,0 +1,59 @@
+package cn.iocoder.yudao.framework.signature.core.annotation;
+
+import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
+
+import java.lang.annotation.*;
+import java.util.concurrent.TimeUnit;
+
+
+/**
+ * HTTP API 签名注解
+ *
+ * @author Zhougang
+ */
+@Inherited
+@Documented
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ApiSignature {
+
+ /**
+ * 同一个请求多长时间内有效 默认 60 秒
+ */
+ int timeout() default 60;
+
+ /**
+ * 时间单位,默认为 SECONDS 秒
+ */
+ TimeUnit timeUnit() default TimeUnit.SECONDS;
+
+ // ========================== 签名参数 ==========================
+
+ /**
+ * 提示信息,签名失败的提示
+ *
+ * @see GlobalErrorCodeConstants#BAD_REQUEST
+ */
+ String message() default "签名不正确"; // 为空时,使用 BAD_REQUEST 错误提示
+
+ /**
+ * 签名字段:appId 应用ID
+ */
+ String appId() default "appId";
+
+ /**
+ * 签名字段:timestamp 时间戳
+ */
+ String timestamp() default "timestamp";
+
+ /**
+ * 签名字段:nonce 随机数,10 位以上
+ */
+ String nonce() default "nonce";
+
+ /**
+ * sign 客户端签名
+ */
+ String sign() default "sign";
+
+}
diff --git a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/aop/ApiSignatureAspect.java b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/aop/ApiSignatureAspect.java
new file mode 100644
index 000000000..3259dac11
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/aop/ApiSignatureAspect.java
@@ -0,0 +1,169 @@
+package cn.iocoder.yudao.framework.signature.core.aop;
+
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.ObjUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.crypto.digest.DigestUtil;
+import cn.iocoder.yudao.framework.common.exception.ServiceException;
+import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
+import cn.iocoder.yudao.framework.signature.core.annotation.ApiSignature;
+import cn.iocoder.yudao.framework.signature.core.redis.ApiSignatureRedisDAO;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;
+
+/**
+ * 拦截声明了 {@link ApiSignature} 注解的方法,实现签名
+ *
+ * @author Zhougang
+ */
+@Aspect
+@Slf4j
+@AllArgsConstructor
+public class ApiSignatureAspect {
+
+ private final ApiSignatureRedisDAO signatureRedisDAO;
+
+ @Before("@annotation(signature)")
+ public void beforePointCut(JoinPoint joinPoint, ApiSignature signature) {
+ // 1. 验证通过,直接结束
+ if (verifySignature(signature, Objects.requireNonNull(ServletUtils.getRequest()))) {
+ return;
+ }
+
+ // 2. 验证不通过,抛出异常
+ log.error("[beforePointCut][方法{} 参数({}) 签名失败]", joinPoint.getSignature().toString(),
+ joinPoint.getArgs());
+ throw new ServiceException(BAD_REQUEST.getCode(),
+ StrUtil.blankToDefault(signature.message(), BAD_REQUEST.getMsg()));
+ }
+
+ public boolean verifySignature(ApiSignature signature, HttpServletRequest request) {
+ // 1.1 校验 Header
+ if (!verifyHeaders(signature, request)) {
+ return false;
+ }
+ // 1.2 校验 appId 是否能获取到对应的 appSecret
+ String appId = request.getHeader(signature.appId());
+ String appSecret = signatureRedisDAO.getAppSecret(appId);
+ Assert.notNull(appSecret, "[appId({})] 找不到对应的 appSecret", appId);
+
+ // 2. 校验签名【重要!】
+ String clientSignature = request.getHeader(signature.sign()); // 客户端签名
+ String serverSignatureString = buildSignatureString(signature, request, appSecret); // 服务端签名字符串
+ String serverSignature = DigestUtil.sha256Hex(serverSignatureString); // 服务端签名
+ if (ObjUtil.notEqual(clientSignature, serverSignature)) {
+ return false;
+ }
+
+ // 3. 将 nonce 记入缓存,防止重复使用(重点二:此处需要将 ttl 设定为允许 timestamp 时间差的值 x 2 )
+ String nonce = request.getHeader(signature.nonce());
+ signatureRedisDAO.setNonce(nonce, signature.timeout() * 2, signature.timeUnit());
+ return true;
+ }
+
+ /**
+ * 校验请求头加签参数
+ *
+ * 1. appId 是否为空
+ * 2. timestamp 是否为空,请求是否已经超时,默认 10 分钟
+ * 3. nonce 是否为空,随机数是否 10 位以上,是否在规定时间内已经访问过了
+ * 4. sign 是否为空
+ *
+ * @param signature signature
+ * @param request request
+ * @return 是否校验 Header 通过
+ */
+ private boolean verifyHeaders(ApiSignature signature, HttpServletRequest request) {
+ // 1. 非空校验
+ String appId = request.getHeader(signature.appId());
+ if (StrUtil.isBlank(appId)) {
+ return false;
+ }
+ String timestamp = request.getHeader(signature.timestamp());
+ if (StrUtil.isBlank(timestamp)) {
+ return false;
+ }
+ String nonce = request.getHeader(signature.nonce());
+ if (StrUtil.length(nonce) < 10) {
+ return false;
+ }
+ String sign = request.getHeader(signature.sign());
+ if (StrUtil.isBlank(sign)) {
+ return false;
+ }
+
+ // 2. 检查 timestamp 是否超出允许的范围 (重点一:此处需要取绝对值)
+ long expireTime = signature.timeUnit().toMillis(signature.timeout());
+ long requestTimestamp = Long.parseLong(timestamp);
+ long timestampDisparity = Math.abs(System.currentTimeMillis() - requestTimestamp);
+ if (timestampDisparity > expireTime) {
+ return false;
+ }
+
+ // 3. 检查 nonce 是否存在,有且仅能使用一次
+ return signatureRedisDAO.getNonce(nonce) == null;
+ }
+
+ /**
+ * 构建签名字符串
+ *
+ * 格式为 = 请求参数 + 请求体 + 请求头 + 密钥
+ *
+ * @param signature signature
+ * @param request request
+ * @param appSecret appSecret
+ * @return 签名字符串
+ */
+ private String buildSignatureString(ApiSignature signature, HttpServletRequest request, String appSecret) {
+ SortedMap parameterMap = getRequestParameterMap(request); // 请求头
+ SortedMap headerMap = getRequestHeaderMap(signature, request); // 请求参数
+ String requestBody = StrUtil.nullToDefault(ServletUtils.getBody(request), ""); // 请求体
+ return MapUtil.join(parameterMap, "&", "=")
+ + requestBody
+ + MapUtil.join(headerMap, "&", "=")
+ + appSecret;
+ }
+
+ /**
+ * 获取请求头加签参数 Map
+ *
+ * @param request 请求
+ * @param signature 签名注解
+ * @return signature params
+ */
+ private static SortedMap getRequestHeaderMap(ApiSignature signature, HttpServletRequest request) {
+ SortedMap sortedMap = new TreeMap<>();
+ sortedMap.put(signature.appId(), request.getHeader(signature.appId()));
+ sortedMap.put(signature.timestamp(), request.getHeader(signature.timestamp()));
+ sortedMap.put(signature.nonce(), request.getHeader(signature.nonce()));
+ return sortedMap;
+ }
+
+ /**
+ * 获取请求参数 Map
+ *
+ * @param request 请求
+ * @return queryParams
+ */
+ private static SortedMap getRequestParameterMap(HttpServletRequest request) {
+ SortedMap sortedMap = new TreeMap<>();
+ for (Map.Entry entry : request.getParameterMap().entrySet()) {
+ sortedMap.put(entry.getKey(), entry.getValue()[0]);
+ }
+ return sortedMap;
+ }
+
+}
+
diff --git a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/redis/ApiSignatureRedisDAO.java b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/redis/ApiSignatureRedisDAO.java
new file mode 100644
index 000000000..f4aa84910
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/redis/ApiSignatureRedisDAO.java
@@ -0,0 +1,57 @@
+package cn.iocoder.yudao.framework.signature.core.redis;
+
+import lombok.AllArgsConstructor;
+import org.springframework.data.redis.core.StringRedisTemplate;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * HTTP API 签名 Redis DAO
+ *
+ * @author Zhougang
+ */
+@AllArgsConstructor
+public class ApiSignatureRedisDAO {
+
+ private final StringRedisTemplate stringRedisTemplate;
+
+ /**
+ * 验签随机数
+ *
+ * KEY 格式:signature_nonce:%s // 参数为 随机数
+ * VALUE 格式:String
+ * 过期时间:不固定
+ */
+ private static final String SIGNATURE_NONCE = "api_signature_nonce:%s";
+
+ /**
+ * 签名密钥
+ *
+ * HASH 结构
+ * KEY 格式:%s // 参数为 appid
+ * VALUE 格式:String
+ * 过期时间:永不过期(预加载到 Redis)
+ */
+ private static final String SIGNATURE_APPID = "api_signature_app";
+
+ // ========== 验签随机数 ==========
+
+ public String getNonce(String nonce) {
+ return stringRedisTemplate.opsForValue().get(formatNonceKey(nonce));
+ }
+
+ public void setNonce(String nonce, int time, TimeUnit timeUnit) {
+ stringRedisTemplate.opsForValue().set(formatNonceKey(nonce), "", time, timeUnit);
+ }
+
+ private static String formatNonceKey(String key) {
+ return String.format(SIGNATURE_NONCE, key);
+ }
+
+ // ========== 签名密钥 ==========
+
+ public String getAppSecret(String appId) {
+ return (String) stringRedisTemplate.opsForHash().get(SIGNATURE_APPID, appId);
+ }
+
+}
diff --git a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/package-info.java b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/package-info.java
new file mode 100644
index 000000000..4ebd87afb
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * HTTP API 签名,校验安全性
+ *
+ * @see builder()
+ .put("v1", new String[]{"k1"}).put("k1", new String[]{"v1"}).build());
+ when(request.getContentType()).thenReturn("application/json");
+ when(request.getReader()).thenReturn(new BufferedReader(new StringReader("test")));
+ // mock 方法
+ when(signatureRedisDAO.getAppSecret(eq(appId))).thenReturn(appSecret);
+
+ // 调用
+ boolean result = apiSignatureAspect.verifySignature(apiSignature, request);
+ // 断言结果
+ assertTrue(result);
+ // 断言调用
+ verify(signatureRedisDAO).setNonce(eq(nonce), eq(120), eq(TimeUnit.SECONDS));
+ }
+
+}
diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java
index ab4c3f9e9..413b8fa0a 100644
--- a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java
+++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java
@@ -28,11 +28,15 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
+import org.springframework.web.util.pattern.PathPattern;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
+
/**
* 自定义的 Spring Security 配置适配器实现
*
@@ -163,13 +167,20 @@ public class YudaoWebSecurityConfigurerAdapter {
if (!handlerMethod.hasMethodAnnotation(PermitAll.class)) {
continue;
}
- if (entry.getKey().getPatternsCondition() == null) {
+ Set urls = new HashSet<>();
+ if (entry.getKey().getPatternsCondition() != null) {
+ urls.addAll(entry.getKey().getPatternsCondition().getPatterns());
+ }
+ if (entry.getKey().getPathPatternsCondition() != null) {
+ urls.addAll(convertList(entry.getKey().getPathPatternsCondition().getPatterns(), PathPattern::getPatternString));
+ }
+ if (urls.isEmpty()) {
continue;
}
- Set urls = entry.getKey().getPatternsCondition().getPatterns();
+
// 特殊:使用 @RequestMapping 注解,并且未写 method 属性,此时认为都需要免登录
Set methods = entry.getKey().getMethodsCondition().getMethods();
- if (CollUtil.isEmpty(methods)) { //
+ if (CollUtil.isEmpty(methods)) {
result.putAll(HttpMethod.GET, urls);
result.putAll(HttpMethod.POST, urls);
result.putAll(HttpMethod.PUT, urls);
diff --git a/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/common/ErpBizTypeEnum.java b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/common/ErpBizTypeEnum.java
index bba2b309b..a2770598e 100644
--- a/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/common/ErpBizTypeEnum.java
+++ b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/common/ErpBizTypeEnum.java
@@ -20,7 +20,7 @@ public enum ErpBizTypeEnum implements IntArrayValuable {
PURCHASE_RETURN(12, "采购退货"),
SALE_ORDER(20, "销售订单"),
- SALE_OUT(21, "销售订单"),
+ SALE_OUT(21, "销售出库"),
SALE_RETURN(22, "销售退货"),
;
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileContentDO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileContentDO.java
index eda8a7264..80e18fc56 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileContentDO.java
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileContentDO.java
@@ -28,8 +28,8 @@ public class FileContentDO extends BaseDO {
/**
* 编号,数据库自增
*/
- @TableId(type = IdType.INPUT)
- private String id;
+ @TableId
+ private Long id;
/**
* 配置编号
*
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/logger/ApiAccessLogDO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/logger/ApiAccessLogDO.java
index d4850fcc8..4f671c70a 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/logger/ApiAccessLogDO.java
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/logger/ApiAccessLogDO.java
@@ -26,6 +26,16 @@ import java.time.LocalDateTime;
@AllArgsConstructor
public class ApiAccessLogDO extends BaseDO {
+ /**
+ * {@link #requestParams} 的最大长度
+ */
+ public static final Integer REQUEST_PARAMS_MAX_LENGTH = 8000;
+
+ /**
+ * {@link #resultMsg} 的最大长度
+ */
+ public static final Integer RESULT_MSG_MAX_LENGTH = 512;
+
/**
* 编号
*/
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/logger/ApiErrorLogDO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/logger/ApiErrorLogDO.java
index 7dc040981..87d6974a9 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/logger/ApiErrorLogDO.java
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/logger/ApiErrorLogDO.java
@@ -25,6 +25,11 @@ import java.time.LocalDateTime;
@KeySequence(value = "infra_api_error_log_seq")
public class ApiErrorLogDO extends BaseDO {
+ /**
+ * {@link #requestParams} 的最大长度
+ */
+ public static final Integer REQUEST_PARAMS_MAX_LENGTH = 8000;
+
/**
* 编号
*/
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiAccessLogServiceImpl.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiAccessLogServiceImpl.java
index aa393b312..f6b496381 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiAccessLogServiceImpl.java
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiAccessLogServiceImpl.java
@@ -1,18 +1,22 @@
package cn.iocoder.yudao.module.infra.service.logger;
+import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.infra.api.logger.dto.ApiAccessLogCreateReqDTO;
import cn.iocoder.yudao.module.infra.controller.admin.logger.vo.apiaccesslog.ApiAccessLogPageReqVO;
import cn.iocoder.yudao.module.infra.dal.dataobject.logger.ApiAccessLogDO;
import cn.iocoder.yudao.module.infra.dal.mysql.logger.ApiAccessLogMapper;
+import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
-import jakarta.annotation.Resource;
import java.time.LocalDateTime;
+import static cn.iocoder.yudao.module.infra.dal.dataobject.logger.ApiAccessLogDO.REQUEST_PARAMS_MAX_LENGTH;
+import static cn.iocoder.yudao.module.infra.dal.dataobject.logger.ApiAccessLogDO.RESULT_MSG_MAX_LENGTH;
+
/**
* API 访问日志 Service 实现类
*
@@ -29,6 +33,8 @@ public class ApiAccessLogServiceImpl implements ApiAccessLogService {
@Override
public void createApiAccessLog(ApiAccessLogCreateReqDTO createDTO) {
ApiAccessLogDO apiAccessLog = BeanUtils.toBean(createDTO, ApiAccessLogDO.class);
+ apiAccessLog.setRequestParams(StrUtil.maxLength(apiAccessLog.getRequestParams(), REQUEST_PARAMS_MAX_LENGTH));
+ apiAccessLog.setResultMsg(StrUtil.maxLength(apiAccessLog.getResultMsg(), RESULT_MSG_MAX_LENGTH));
apiAccessLogMapper.insert(apiAccessLog);
}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiErrorLogServiceImpl.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiErrorLogServiceImpl.java
index b5d265095..5137dc3d3 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiErrorLogServiceImpl.java
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiErrorLogServiceImpl.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.infra.service.logger;
+import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.infra.api.logger.dto.ApiErrorLogCreateReqDTO;
@@ -7,14 +8,15 @@ import cn.iocoder.yudao.module.infra.controller.admin.logger.vo.apierrorlog.ApiE
import cn.iocoder.yudao.module.infra.dal.dataobject.logger.ApiErrorLogDO;
import cn.iocoder.yudao.module.infra.dal.mysql.logger.ApiErrorLogMapper;
import cn.iocoder.yudao.module.infra.enums.logger.ApiErrorLogProcessStatusEnum;
+import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
-import jakarta.annotation.Resource;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.infra.dal.dataobject.logger.ApiErrorLogDO.REQUEST_PARAMS_MAX_LENGTH;
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.API_ERROR_LOG_NOT_FOUND;
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.API_ERROR_LOG_PROCESSED;
@@ -35,6 +37,7 @@ public class ApiErrorLogServiceImpl implements ApiErrorLogService {
public void createApiErrorLog(ApiErrorLogCreateReqDTO createDTO) {
ApiErrorLogDO apiErrorLog = BeanUtils.toBean(createDTO, ApiErrorLogDO.class)
.setProcessStatus(ApiErrorLogProcessStatusEnum.INIT.getStatus());
+ apiErrorLog.setRequestParams(StrUtil.maxLength(apiErrorLog.getRequestParams(), REQUEST_PARAMS_MAX_LENGTH));
apiErrorLogMapper.insert(apiErrorLog);
}
diff --git a/yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/trade/TradeOrderStatisticsMapper.java b/yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/trade/TradeOrderStatisticsMapper.java
index 35e52e031..86b32df2f 100644
--- a/yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/trade/TradeOrderStatisticsMapper.java
+++ b/yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/trade/TradeOrderStatisticsMapper.java
@@ -58,7 +58,7 @@ public interface TradeOrderStatisticsMapper extends BaseMapperX getUser(@RequestParam("id") Long id) {
AdminUserDO user = userService.getUser(id);
+ if (user == null) {
+ return success(null);
+ }
// 拼接数据
DeptDO dept = deptService.getDept(user.getDeptId());
return success(UserConvert.INSTANCE.convert(user, dept));
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java
index af3aa8a57..35db06706 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java
@@ -18,6 +18,7 @@ import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum;
import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum;
import com.google.common.annotations.VisibleForTesting;
import com.mzt.logapi.context.LogRecordContext;
+import com.mzt.logapi.service.impl.DiffParseFunction;
import com.mzt.logapi.starter.annotation.LogRecord;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
@@ -116,6 +117,7 @@ public class RoleServiceImpl implements RoleService {
permissionService.processRoleDeleted(id);
// 3. 记录操作日志上下文
+ LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(role, RoleSaveReqVO.class));
LogRecordContext.putVariable("role", role);
}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsCodeServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsCodeServiceImpl.java
index 5e6637fbd..6b7727705 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsCodeServiceImpl.java
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsCodeServiceImpl.java
@@ -67,7 +67,8 @@ public class SmsCodeServiceImpl implements SmsCodeService {
}
// 创建验证码记录
- String code = String.valueOf(randomInt(smsCodeProperties.getBeginCode(), smsCodeProperties.getEndCode() + 1));
+ String code = String.format("%0" + smsCodeProperties.getEndCode().toString().length() + "d",
+ randomInt(smsCodeProperties.getBeginCode(), smsCodeProperties.getEndCode() + 1));
SmsCodeDO newSmsCode = SmsCodeDO.builder().mobile(mobile).code(code).scene(scene)
.todayIndex(lastSmsCode != null && isToday(lastSmsCode.getCreateTime()) ? lastSmsCode.getTodayIndex() + 1 : 1)
.createIp(ip).used(false).build();