Commit 27e2996c5d855b1bbdf82d29eb215c00e68c7297

Authored by dhb52
2 parents b4a86d14 9a9d95f0

Merge branch 'master-jdk17' into feature/db

# Conflicts:
#	sql/tools/README.md
Showing 18 changed files with 628 additions and 139 deletions
sql/tools/README.md
... ... @@ -67,8 +67,8 @@ exit
67 67  
68 68 ① 下载人大金仓 Docker 镜像:
69 69  
70   -> x86_64版本: https://kingbase.oss-cn-beijing.aliyuncs.com/KESV8R3/V009R001C001B0025-安装包-docker/x86_64/kdb_x86_64_V009R001C001B0025.tar
71   -> aarch64版本:https://kingbase.oss-cn-beijing.aliyuncs.com/KESV8R3/V009R001C001B0025-安装包-docker/aarch64/kdb_aarch64_V009R001C001B0025.tar
  70 +> x86_64 版本: https://kingbase.oss-cn-beijing.aliyuncs.com/KESV8R3/V009R001C001B0025-安装包-docker/x86_64/kdb_x86_64_V009R001C001B0025.tar
  71 +> aarch64 版本:https://kingbase.oss-cn-beijing.aliyuncs.com/KESV8R3/V009R001C001B0025-安装包-docker/aarch64/kdb_aarch64_V009R001C001B0025.tar
72 72  
73 73 ② 加载镜像文件,在镜像 tar 文件所在目录运行:
74 74  
... ... @@ -80,11 +80,11 @@ docker load -i x86_64/kdb_x86_64_V009R001C001B0025.tar
80 80  
81 81 ```Bash
82 82 docker compose up -d kingbase
83   -# 注意:启动完 dm 后,需要手动再执行如下命令,因为 dm 不支持初始化脚本
84   -docker compose exec kingbase bash -c 'ksql -U $DB_USER -d test -f /tmp/schema.sql'
  83 +# 注意:启动完 kingbase 后,需要手动再执行如下命令,因为 dm 不支持初始化脚本
  84 +docker compose exec kingbase bash -c "exec ksql -Uroot -d test -f /tmp/schema.sql"
85 85 ```
86 86  
87   -**注意**: MyBatis, MyBatis Plus 目前不兼容人大金仓,推荐直接使用PostgreSQL JDBC 驱动,已经 url 配置方式连接数据库。
  87 +**注意**: MyBatis、MyBatis Plus 目前不兼容人大金仓,推荐直接使用 PostgreSQL JDBC 驱动,已经 url 配置方式连接数据库。
88 88  
89 89 ### 1.7 华为 OpenGauss
90 90  
... ...
yudao-framework/yudao-spring-boot-starter-biz-ip/src/main/resources/area.csv
... ... @@ -247,15 +247,15 @@ id,name,type,parentId
247 247 246,英属印度洋领地,1,0
248 248 247,东萨摩亚,1,0
249 249 248,诺福克岛,1,0
250   -110000,北京,2,1
251   -120000,天津,2,1
  250 +110000,北京市,2,1
  251 +120000,天津市,2,1
252 252 130000,河北省,2,1
253 253 140000,山西省,2,1
254 254 150000,内蒙古自治区,2,1
255 255 210000,辽宁省,2,1
256 256 220000,吉林省,2,1
257 257 230000,黑龙江省,2,1
258   -310000,上海,2,1
  258 +310000,上海市,2,1
259 259 320000,江苏省,2,1
260 260 330000,浙江省,2,1
261 261 340000,安徽省,2,1
... ... @@ -268,7 +268,7 @@ id,name,type,parentId
268 268 440000,广东省,2,1
269 269 450000,广西壮族自治区,2,1
270 270 460000,海南省,2,1
271   -500000,重庆,2,1
  271 +500000,重庆市,2,1
272 272 510000,四川省,2,1
273 273 520000,贵州省,2,1
274 274 530000,云南省,2,1
... ...
yudao-framework/yudao-spring-boot-starter-job/src/main/java/cn/iocoder/yudao/framework/quartz/core/util/CronUtils.java
... ... @@ -34,20 +34,24 @@ public class CronUtils {
34 34 * @return 满足条件的执行时间
35 35 */
36 36 public static List<LocalDateTime> getNextTimes(String cronExpression, int n) {
37   - // 获得 CronExpression 对象
  37 + // 1. 获得 CronExpression 对象
38 38 CronExpression cron;
39 39 try {
40 40 cron = new CronExpression(cronExpression);
41 41 } catch (ParseException e) {
42 42 throw new IllegalArgumentException(e.getMessage());
43 43 }
44   - // 从当前开始计算,n 个满足条件的
  44 + // 2. 从当前开始计算,n 个满足条件的
45 45 Date now = new Date();
46 46 List<LocalDateTime> nextTimes = new ArrayList<>(n);
47 47 for (int i = 0; i < n; i++) {
48 48 Date nextTime = cron.getNextValidTimeAfter(now);
  49 + // 2.1 如果 nextTime 为 null,说明没有更多的有效时间,退出循环
  50 + if (nextTime == null) {
  51 + break;
  52 + }
49 53 nextTimes.add(LocalDateTimeUtil.of(nextTime));
50   - // 切换现在,为下一个触发时间;
  54 + // 2.2 切换现在,为下一个触发时间;
51 55 now = nextTime;
52 56 }
53 57 return nextTimes;
... ...
yudao-framework/yudao-spring-boot-starter-protection/pom.xml
... ... @@ -12,7 +12,7 @@
12 12 <packaging>jar</packaging>
13 13  
14 14 <name>${project.artifactId}</name>
15   - <description>服务保证,提供分布式锁、幂等、限流、熔断等等功能</description>
  15 + <description>服务保证,提供分布式锁、幂等、限流、熔断、API 签名等等功能</description>
16 16 <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
17 17  
18 18 <dependencies>
... ... @@ -35,6 +35,13 @@
35 35 <artifactId>lock4j-redisson-spring-boot-starter</artifactId>
36 36 <optional>true</optional>
37 37 </dependency>
  38 +
  39 + <!-- Test 测试相关 -->
  40 + <dependency>
  41 + <groupId>cn.iocoder.boot</groupId>
  42 + <artifactId>yudao-spring-boot-starter-test</artifactId>
  43 + <scope>test</scope>
  44 + </dependency>
38 45 </dependencies>
39 46  
40 47 </project>
... ...
yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/config/YudaoApiSignatureAutoConfiguration.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.signature.config;
  2 +
  3 +import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration;
  4 +import cn.iocoder.yudao.framework.signature.core.aop.ApiSignatureAspect;
  5 +import cn.iocoder.yudao.framework.signature.core.redis.ApiSignatureRedisDAO;
  6 +import org.springframework.boot.autoconfigure.AutoConfiguration;
  7 +import org.springframework.context.annotation.Bean;
  8 +import org.springframework.data.redis.core.StringRedisTemplate;
  9 +
  10 +/**
  11 + * HTTP API 签名的自动配置类
  12 + *
  13 + * @author Zhougang
  14 + */
  15 +@AutoConfiguration(after = YudaoRedisAutoConfiguration.class)
  16 +public class YudaoApiSignatureAutoConfiguration {
  17 +
  18 + @Bean
  19 + public ApiSignatureAspect signatureAspect(ApiSignatureRedisDAO signatureRedisDAO) {
  20 + return new ApiSignatureAspect(signatureRedisDAO);
  21 + }
  22 +
  23 + @Bean
  24 + public ApiSignatureRedisDAO signatureRedisDAO(StringRedisTemplate stringRedisTemplate) {
  25 + return new ApiSignatureRedisDAO(stringRedisTemplate);
  26 + }
  27 +
  28 +}
... ...
yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/annotation/ApiSignature.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.signature.core.annotation;
  2 +
  3 +import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
  4 +
  5 +import java.lang.annotation.*;
  6 +import java.util.concurrent.TimeUnit;
  7 +
  8 +
  9 +/**
  10 + * HTTP API 签名注解
  11 + *
  12 + * @author Zhougang
  13 + */
  14 +@Inherited
  15 +@Documented
  16 +@Target({ElementType.METHOD, ElementType.TYPE})
  17 +@Retention(RetentionPolicy.RUNTIME)
  18 +public @interface ApiSignature {
  19 +
  20 + /**
  21 + * 同一个请求多长时间内有效 默认 60 秒
  22 + */
  23 + int timeout() default 60;
  24 +
  25 + /**
  26 + * 时间单位,默认为 SECONDS 秒
  27 + */
  28 + TimeUnit timeUnit() default TimeUnit.SECONDS;
  29 +
  30 + // ========================== 签名参数 ==========================
  31 +
  32 + /**
  33 + * 提示信息,签名失败的提示
  34 + *
  35 + * @see GlobalErrorCodeConstants#BAD_REQUEST
  36 + */
  37 + String message() default "签名不正确"; // 为空时,使用 BAD_REQUEST 错误提示
  38 +
  39 + /**
  40 + * 签名字段:appId 应用ID
  41 + */
  42 + String appId() default "appId";
  43 +
  44 + /**
  45 + * 签名字段:timestamp 时间戳
  46 + */
  47 + String timestamp() default "timestamp";
  48 +
  49 + /**
  50 + * 签名字段:nonce 随机数,10 位以上
  51 + */
  52 + String nonce() default "nonce";
  53 +
  54 + /**
  55 + * sign 客户端签名
  56 + */
  57 + String sign() default "sign";
  58 +
  59 +}
... ...
yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/aop/ApiSignatureAspect.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.signature.core.aop;
  2 +
  3 +import cn.hutool.core.lang.Assert;
  4 +import cn.hutool.core.map.MapUtil;
  5 +import cn.hutool.core.util.ObjUtil;
  6 +import cn.hutool.core.util.StrUtil;
  7 +import cn.hutool.crypto.digest.DigestUtil;
  8 +import cn.iocoder.yudao.framework.common.exception.ServiceException;
  9 +import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
  10 +import cn.iocoder.yudao.framework.signature.core.annotation.ApiSignature;
  11 +import cn.iocoder.yudao.framework.signature.core.redis.ApiSignatureRedisDAO;
  12 +import jakarta.servlet.http.HttpServletRequest;
  13 +import lombok.AllArgsConstructor;
  14 +import lombok.extern.slf4j.Slf4j;
  15 +import org.aspectj.lang.JoinPoint;
  16 +import org.aspectj.lang.annotation.Aspect;
  17 +import org.aspectj.lang.annotation.Before;
  18 +
  19 +import java.util.Map;
  20 +import java.util.Objects;
  21 +import java.util.SortedMap;
  22 +import java.util.TreeMap;
  23 +
  24 +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;
  25 +
  26 +/**
  27 + * 拦截声明了 {@link ApiSignature} 注解的方法,实现签名
  28 + *
  29 + * @author Zhougang
  30 + */
  31 +@Aspect
  32 +@Slf4j
  33 +@AllArgsConstructor
  34 +public class ApiSignatureAspect {
  35 +
  36 + private final ApiSignatureRedisDAO signatureRedisDAO;
  37 +
  38 + @Before("@annotation(signature)")
  39 + public void beforePointCut(JoinPoint joinPoint, ApiSignature signature) {
  40 + // 1. 验证通过,直接结束
  41 + if (verifySignature(signature, Objects.requireNonNull(ServletUtils.getRequest()))) {
  42 + return;
  43 + }
  44 +
  45 + // 2. 验证不通过,抛出异常
  46 + log.error("[beforePointCut][方法{} 参数({}) 签名失败]", joinPoint.getSignature().toString(),
  47 + joinPoint.getArgs());
  48 + throw new ServiceException(BAD_REQUEST.getCode(),
  49 + StrUtil.blankToDefault(signature.message(), BAD_REQUEST.getMsg()));
  50 + }
  51 +
  52 + public boolean verifySignature(ApiSignature signature, HttpServletRequest request) {
  53 + // 1.1 校验 Header
  54 + if (!verifyHeaders(signature, request)) {
  55 + return false;
  56 + }
  57 + // 1.2 校验 appId 是否能获取到对应的 appSecret
  58 + String appId = request.getHeader(signature.appId());
  59 + String appSecret = signatureRedisDAO.getAppSecret(appId);
  60 + Assert.notNull(appSecret, "[appId({})] 找不到对应的 appSecret", appId);
  61 +
  62 + // 2. 校验签名【重要!】
  63 + String clientSignature = request.getHeader(signature.sign()); // 客户端签名
  64 + String serverSignatureString = buildSignatureString(signature, request, appSecret); // 服务端签名字符串
  65 + String serverSignature = DigestUtil.sha256Hex(serverSignatureString); // 服务端签名
  66 + if (ObjUtil.notEqual(clientSignature, serverSignature)) {
  67 + return false;
  68 + }
  69 +
  70 + // 3. 将 nonce 记入缓存,防止重复使用(重点二:此处需要将 ttl 设定为允许 timestamp 时间差的值 x 2 )
  71 + String nonce = request.getHeader(signature.nonce());
  72 + signatureRedisDAO.setNonce(nonce, signature.timeout() * 2, signature.timeUnit());
  73 + return true;
  74 + }
  75 +
  76 + /**
  77 + * 校验请求头加签参数
  78 + *
  79 + * 1. appId 是否为空
  80 + * 2. timestamp 是否为空,请求是否已经超时,默认 10 分钟
  81 + * 3. nonce 是否为空,随机数是否 10 位以上,是否在规定时间内已经访问过了
  82 + * 4. sign 是否为空
  83 + *
  84 + * @param signature signature
  85 + * @param request request
  86 + * @return 是否校验 Header 通过
  87 + */
  88 + private boolean verifyHeaders(ApiSignature signature, HttpServletRequest request) {
  89 + // 1. 非空校验
  90 + String appId = request.getHeader(signature.appId());
  91 + if (StrUtil.isBlank(appId)) {
  92 + return false;
  93 + }
  94 + String timestamp = request.getHeader(signature.timestamp());
  95 + if (StrUtil.isBlank(timestamp)) {
  96 + return false;
  97 + }
  98 + String nonce = request.getHeader(signature.nonce());
  99 + if (StrUtil.length(nonce) < 10) {
  100 + return false;
  101 + }
  102 + String sign = request.getHeader(signature.sign());
  103 + if (StrUtil.isBlank(sign)) {
  104 + return false;
  105 + }
  106 +
  107 + // 2. 检查 timestamp 是否超出允许的范围 (重点一:此处需要取绝对值)
  108 + long expireTime = signature.timeUnit().toMillis(signature.timeout());
  109 + long requestTimestamp = Long.parseLong(timestamp);
  110 + long timestampDisparity = Math.abs(System.currentTimeMillis() - requestTimestamp);
  111 + if (timestampDisparity > expireTime) {
  112 + return false;
  113 + }
  114 +
  115 + // 3. 检查 nonce 是否存在,有且仅能使用一次
  116 + return signatureRedisDAO.getNonce(nonce) == null;
  117 + }
  118 +
  119 + /**
  120 + * 构建签名字符串
  121 + *
  122 + * 格式为 = 请求参数 + 请求体 + 请求头 + 密钥
  123 + *
  124 + * @param signature signature
  125 + * @param request request
  126 + * @param appSecret appSecret
  127 + * @return 签名字符串
  128 + */
  129 + private String buildSignatureString(ApiSignature signature, HttpServletRequest request, String appSecret) {
  130 + SortedMap<String, String> parameterMap = getRequestParameterMap(request); // 请求头
  131 + SortedMap<String, String> headerMap = getRequestHeaderMap(signature, request); // 请求参数
  132 + String requestBody = StrUtil.nullToDefault(ServletUtils.getBody(request), ""); // 请求体
  133 + return MapUtil.join(parameterMap, "&", "=")
  134 + + requestBody
  135 + + MapUtil.join(headerMap, "&", "=")
  136 + + appSecret;
  137 + }
  138 +
  139 + /**
  140 + * 获取请求头加签参数 Map
  141 + *
  142 + * @param request 请求
  143 + * @param signature 签名注解
  144 + * @return signature params
  145 + */
  146 + private static SortedMap<String, String> getRequestHeaderMap(ApiSignature signature, HttpServletRequest request) {
  147 + SortedMap<String, String> sortedMap = new TreeMap<>();
  148 + sortedMap.put(signature.appId(), request.getHeader(signature.appId()));
  149 + sortedMap.put(signature.timestamp(), request.getHeader(signature.timestamp()));
  150 + sortedMap.put(signature.nonce(), request.getHeader(signature.nonce()));
  151 + return sortedMap;
  152 + }
  153 +
  154 + /**
  155 + * 获取请求参数 Map
  156 + *
  157 + * @param request 请求
  158 + * @return queryParams
  159 + */
  160 + private static SortedMap<String, String> getRequestParameterMap(HttpServletRequest request) {
  161 + SortedMap<String, String> sortedMap = new TreeMap<>();
  162 + for (Map.Entry<String, String[]> entry : request.getParameterMap().entrySet()) {
  163 + sortedMap.put(entry.getKey(), entry.getValue()[0]);
  164 + }
  165 + return sortedMap;
  166 + }
  167 +
  168 +}
  169 +
... ...
yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/redis/ApiSignatureRedisDAO.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.signature.core.redis;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import org.springframework.data.redis.core.StringRedisTemplate;
  5 +
  6 +import java.util.concurrent.TimeUnit;
  7 +
  8 +/**
  9 + * HTTP API 签名 Redis DAO
  10 + *
  11 + * @author Zhougang
  12 + */
  13 +@AllArgsConstructor
  14 +public class ApiSignatureRedisDAO {
  15 +
  16 + private final StringRedisTemplate stringRedisTemplate;
  17 +
  18 + /**
  19 + * 验签随机数
  20 + *
  21 + * KEY 格式:signature_nonce:%s // 参数为 随机数
  22 + * VALUE 格式:String
  23 + * 过期时间:不固定
  24 + */
  25 + private static final String SIGNATURE_NONCE = "api_signature_nonce:%s";
  26 +
  27 + /**
  28 + * 签名密钥
  29 + *
  30 + * HASH 结构
  31 + * KEY 格式:%s // 参数为 appid
  32 + * VALUE 格式:String
  33 + * 过期时间:永不过期(预加载到 Redis)
  34 + */
  35 + private static final String SIGNATURE_APPID = "api_signature_app";
  36 +
  37 + // ========== 验签随机数 ==========
  38 +
  39 + public String getNonce(String nonce) {
  40 + return stringRedisTemplate.opsForValue().get(formatNonceKey(nonce));
  41 + }
  42 +
  43 + public void setNonce(String nonce, int time, TimeUnit timeUnit) {
  44 + stringRedisTemplate.opsForValue().set(formatNonceKey(nonce), "", time, timeUnit);
  45 + }
  46 +
  47 + private static String formatNonceKey(String key) {
  48 + return String.format(SIGNATURE_NONCE, key);
  49 + }
  50 +
  51 + // ========== 签名密钥 ==========
  52 +
  53 + public String getAppSecret(String appId) {
  54 + return (String) stringRedisTemplate.opsForHash().get(SIGNATURE_APPID, appId);
  55 + }
  56 +
  57 +}
... ...
yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/package-info.java 0 → 100644
  1 +/**
  2 + * HTTP API 签名,校验安全性
  3 + *
  4 + * @see <a href="https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3>微信支付 —— 安全规范</a>
  5 + */
  6 +package cn.iocoder.yudao.framework.signature;
0 7 \ No newline at end of file
... ...
yudao-framework/yudao-spring-boot-starter-protection/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
1 1 cn.iocoder.yudao.framework.idempotent.config.YudaoIdempotentConfiguration
2 2 cn.iocoder.yudao.framework.lock4j.config.YudaoLock4jConfiguration
3   -cn.iocoder.yudao.framework.ratelimiter.config.YudaoRateLimiterConfiguration
4 3 \ No newline at end of file
  4 +cn.iocoder.yudao.framework.ratelimiter.config.YudaoRateLimiterConfiguration
  5 +cn.iocoder.yudao.framework.signature.config.YudaoApiSignatureAutoConfiguration
5 6 \ No newline at end of file
... ...
yudao-framework/yudao-spring-boot-starter-protection/src/test/java/cn/iocoder/yudao/framework/signature/core/ApiSignatureTest.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.signature.core;
  2 +
  3 +import cn.hutool.core.map.MapUtil;
  4 +import cn.hutool.core.util.IdUtil;
  5 +import cn.hutool.crypto.digest.DigestUtil;
  6 +import cn.iocoder.yudao.framework.signature.core.annotation.ApiSignature;
  7 +import cn.iocoder.yudao.framework.signature.core.aop.ApiSignatureAspect;
  8 +import cn.iocoder.yudao.framework.signature.core.redis.ApiSignatureRedisDAO;
  9 +import jakarta.servlet.http.HttpServletRequest;
  10 +import org.junit.jupiter.api.Test;
  11 +import org.junit.jupiter.api.extension.ExtendWith;
  12 +import org.mockito.InjectMocks;
  13 +import org.mockito.Mock;
  14 +import org.mockito.junit.jupiter.MockitoExtension;
  15 +
  16 +import java.io.BufferedReader;
  17 +import java.io.IOException;
  18 +import java.io.StringReader;
  19 +import java.util.concurrent.TimeUnit;
  20 +
  21 +import static org.junit.jupiter.api.Assertions.assertTrue;
  22 +import static org.mockito.ArgumentMatchers.eq;
  23 +import static org.mockito.Mockito.*;
  24 +
  25 +/**
  26 + * {@link ApiSignatureTest} 的单元测试
  27 + */
  28 +@ExtendWith(MockitoExtension.class)
  29 +public class ApiSignatureTest {
  30 +
  31 + @InjectMocks
  32 + private ApiSignatureAspect apiSignatureAspect;
  33 +
  34 + @Mock
  35 + private ApiSignatureRedisDAO signatureRedisDAO;
  36 +
  37 + @Test
  38 + public void testSignatureGet() throws IOException {
  39 + // 搞一个签名
  40 + Long timestamp = System.currentTimeMillis();
  41 + String nonce = IdUtil.randomUUID();
  42 + String appId = "xxxxxx";
  43 + String appSecret = "yyyyyy";
  44 + String signString = "k1=v1&v1=k1testappId=xxxxxx&nonce=" + nonce + "&timestamp=" + timestamp + "yyyyyy";
  45 + String sign = DigestUtil.sha256Hex(signString);
  46 +
  47 + // 准备参数
  48 + ApiSignature apiSignature = mock(ApiSignature.class);
  49 + when(apiSignature.appId()).thenReturn("appId");
  50 + when(apiSignature.timestamp()).thenReturn("timestamp");
  51 + when(apiSignature.nonce()).thenReturn("nonce");
  52 + when(apiSignature.sign()).thenReturn("sign");
  53 + when(apiSignature.timeout()).thenReturn(60);
  54 + when(apiSignature.timeUnit()).thenReturn(TimeUnit.SECONDS);
  55 + HttpServletRequest request = mock(HttpServletRequest.class);
  56 + when(request.getHeader(eq("appId"))).thenReturn(appId);
  57 + when(request.getHeader(eq("timestamp"))).thenReturn(String.valueOf(timestamp));
  58 + when(request.getHeader(eq("nonce"))).thenReturn(nonce);
  59 + when(request.getHeader(eq("sign"))).thenReturn(sign);
  60 + when(request.getParameterMap()).thenReturn(MapUtil.<String, String[]>builder()
  61 + .put("v1", new String[]{"k1"}).put("k1", new String[]{"v1"}).build());
  62 + when(request.getContentType()).thenReturn("application/json");
  63 + when(request.getReader()).thenReturn(new BufferedReader(new StringReader("test")));
  64 + // mock 方法
  65 + when(signatureRedisDAO.getAppSecret(eq(appId))).thenReturn(appSecret);
  66 +
  67 + // 调用
  68 + boolean result = apiSignatureAspect.verifySignature(apiSignature, request);
  69 + // 断言结果
  70 + assertTrue(result);
  71 + // 断言调用
  72 + verify(signatureRedisDAO).setNonce(eq(nonce), eq(120), eq(TimeUnit.SECONDS));
  73 + }
  74 +
  75 +}
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessStatusDO.java
1 1 package cn.iocoder.yudao.module.crm.dal.dataobject.business;
2 2  
  3 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
3 4 import com.baomidou.mybatisplus.annotation.KeySequence;
4 5 import com.baomidou.mybatisplus.annotation.TableId;
5 6 import com.baomidou.mybatisplus.annotation.TableName;
... ... @@ -19,7 +20,7 @@ import lombok.*;
19 20 @Builder
20 21 @NoArgsConstructor
21 22 @AllArgsConstructor
22   -public class CrmBusinessStatusDO {
  23 +public class CrmBusinessStatusDO extends BaseDO {
23 24  
24 25 /**
25 26 * 主键
... ...
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 {
28 28 /**
29 29 * 编号,数据库自增
30 30 */
31   - @TableId(type = IdType.INPUT)
32   - private String id;
  31 + @TableId
  32 + private Long id;
33 33 /**
34 34 * 配置编号
35 35 *
... ...
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/logger/ApiErrorLogDO.java
... ... @@ -25,7 +25,6 @@ import java.time.LocalDateTime;
25 25 @KeySequence(value = "infra_api_error_log_seq")
26 26 public class ApiErrorLogDO extends BaseDO {
27 27  
28   -
29 28 /**
30 29 * {@link #requestParams} 的最大长度
31 30 */
... ...
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java
... ... @@ -342,7 +342,8 @@ public class CodegenEngine {
342 342  
343 343 // className 相关
344 344 // 去掉指定前缀,将 TestDictType 转换成 DictType. 因为在 create 等方法后,不需要带上 Test 前缀
345   - String simpleClassName = removePrefix(table.getClassName(), upperFirst(table.getModuleName()));
  345 + String simpleClassName = equalsAnyIgnoreCase(table.getClassName(), table.getModuleName()) ? table.getClassName()
  346 + : removePrefix(table.getClassName(), upperFirst(table.getModuleName()));
346 347 bindingMap.put("simpleClassName", simpleClassName);
347 348 bindingMap.put("simpleClassName_underlineCase", toUnderlineCase(simpleClassName)); // 将 DictType 转换成 dict_type
348 349 bindingMap.put("classNameVar", lowerFirst(simpleClassName)); // 将 DictType 转换成 dictType,用于变量
... ...
yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceTest.java
1 1 package cn.iocoder.yudao.module.trade.service.order;
2 2  
  3 +import cn.hutool.core.util.IdUtil;
  4 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
3 5 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
4 6 import cn.iocoder.yudao.module.member.api.address.MemberAddressApi;
5 7 import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
6 8 import cn.iocoder.yudao.module.pay.api.order.PayOrderApi;
7 9 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO;
8 10 import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
  11 +import cn.iocoder.yudao.module.product.api.comment.ProductCommentApi;
9 12 import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
10 13 import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
11 14 import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi;
  15 +import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi;
  16 +import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.express.DeliveryExpressCreateReqVO;
12 17 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO;
13 18 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
14 19 import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper;
15 20 import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper;
  21 +import cn.iocoder.yudao.module.trade.dal.redis.no.TradeNoRedisDAO;
  22 +import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
  23 +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderRefundStatusEnum;
16 24 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
17 25 import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderConfig;
18 26 import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
  27 +import cn.iocoder.yudao.module.trade.service.cart.CartServiceImpl;
  28 +import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService;
  29 +import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressServiceImpl;
  30 +import cn.iocoder.yudao.module.trade.service.message.TradeMessageServiceImpl;
  31 +import cn.iocoder.yudao.module.trade.service.order.handler.TradeOrderHandler;
  32 +import cn.iocoder.yudao.module.trade.service.price.TradePriceServiceImpl;
  33 +import cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculator;
  34 +import jakarta.annotation.Resource;
19 35 import org.junit.jupiter.api.BeforeEach;
20 36 import org.junit.jupiter.api.Disabled;
21 37 import org.junit.jupiter.api.Test;
22 38 import org.springframework.boot.test.mock.mockito.MockBean;
23 39 import org.springframework.context.annotation.Import;
24 40  
25   -import jakarta.annotation.Resource;
26 41 import java.time.Duration;
27 42  
28 43 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
29 44 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
30 45 import static org.junit.jupiter.api.Assertions.*;
  46 +import static org.mockito.ArgumentMatchers.anyString;
31 47 import static org.mockito.ArgumentMatchers.eq;
32 48 import static org.mockito.Mockito.when;
33 49  
... ... @@ -38,7 +54,9 @@ import static org.mockito.Mockito.when;
38 54 * @since 2022-09-07
39 55 */
40 56 @Disabled // TODO 芋艿:后续 fix 补充的单测
41   -@Import({TradeOrderUpdateServiceImpl.class, TradeOrderConfig.class})
  57 +@Import({TradeOrderUpdateServiceImpl.class, TradeOrderConfig.class, CartServiceImpl.class, TradePriceServiceImpl.class,
  58 + DeliveryExpressServiceImpl.class, TradeMessageServiceImpl.class
  59 +})
42 60 public class TradeOrderUpdateServiceTest extends BaseDbUnitTest {
43 61  
44 62 @Resource
... ... @@ -55,7 +73,9 @@ public class TradeOrderUpdateServiceTest extends BaseDbUnitTest {
55 73 private ProductSpuApi productSpuApi;
56 74 @MockBean
57 75 private ProductSkuApi productSkuApi;
58   -// @MockBean
  76 + @MockBean
  77 + private ProductCommentApi productCommentApi;
  78 + // @MockBean
59 79 // private PriceApi priceApi;
60 80 @MockBean
61 81 private PayOrderApi payOrderApi;
... ... @@ -66,11 +86,22 @@ public class TradeOrderUpdateServiceTest extends BaseDbUnitTest {
66 86  
67 87 @MockBean
68 88 private TradeOrderProperties tradeOrderProperties;
  89 + @MockBean
  90 + private TradeNoRedisDAO tradeNoRedisDAO;
  91 + @MockBean
  92 + private TradeOrderHandler tradeOrderHandler;
  93 + @MockBean
  94 + private TradePriceCalculator tradePriceCalculator;
  95 + @MockBean
  96 + private NotifyMessageSendApi notifyMessageSendApi;
  97 + @MockBean
  98 + private DeliveryExpressService deliveryExpressService;
69 99  
70 100 @BeforeEach
71 101 public void setUp() {
72 102 when(tradeOrderProperties.getAppId()).thenReturn(888L);
73 103 when(tradeOrderProperties.getPayExpireTime()).thenReturn(Duration.ofDays(1));
  104 + when(tradeNoRedisDAO.generate(anyString())).thenReturn(IdUtil.randomUUID());
74 105 }
75 106  
76 107 // @Test
... ... @@ -259,11 +290,18 @@ public class TradeOrderUpdateServiceTest extends BaseDbUnitTest {
259 290 TradeOrderDO order = randomPojo(TradeOrderDO.class, o -> {
260 291 o.setId(1L).setStatus(TradeOrderStatusEnum.UNDELIVERED.getStatus());
261 292 o.setLogisticsId(null).setLogisticsNo(null).setDeliveryTime(null);
  293 + o.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus());
  294 + o.setDeliveryType(DeliveryTypeEnum.EXPRESS.getType());
262 295 });
263 296 tradeOrderMapper.insert(order);
  297 +
  298 + DeliveryExpressCreateReqVO expressCreateReqVO = new DeliveryExpressCreateReqVO();
  299 + expressCreateReqVO.setCode("code").setName("Name").setLogo("logo").setSort(0).setStatus(CommonStatusEnum.ENABLE.getStatus());
  300 + Long deliveryExpressId = deliveryExpressService.createDeliveryExpress(expressCreateReqVO);
264 301 // 准备参数
265 302 TradeOrderDeliveryReqVO deliveryReqVO = new TradeOrderDeliveryReqVO().setId(1L)
266   - .setLogisticsId(10L).setLogisticsNo("100");
  303 + .setLogisticsId(deliveryExpressId).setLogisticsNo("100");
  304 +
267 305 // mock 方法(支付单)
268 306  
269 307 // 调用
... ...
yudao-module-mall/yudao-module-trade-biz/src/test/resources/sql/create_tables.sql
1   -CREATE TABLE IF NOT EXISTS "trade_order" (
2   - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
3   - "no" varchar NOT NULL,
4   - "type" int NOT NULL,
5   - "terminal" int NOT NULL,
6   - "user_id" bigint NOT NULL,
7   - "user_ip" varchar NOT NULL,
8   - "user_remark" varchar,
9   - "status" int NOT NULL,
10   - "product_count" int NOT NULL,
11   - "cancel_type" int,
12   - "remark" varchar,
13   - "pay_status" bit NOT NULL,
14   - "pay_time" datetime,
15   - "finish_time" datetime,
16   - "cancel_time" datetime,
17   - "original_price" int NOT NULL,
18   - "order_price" int NOT NULL,
19   - "discount_price" int NOT NULL,
20   - "delivery_price" int NOT NULL,
21   - "adjust_price" int NOT NULL,
22   - "pay_price" int NOT NULL,
23   - "pay_order_id" bigint,
24   - "pay_channel_code" varchar,
25   - "delivery_template_id" bigint,
26   - "logistics_id" bigint,
27   - "logistics_no" varchar,
28   - "delivery_time" datetime,
29   - "receive_time" datetime,
30   - "receiver_name" varchar NOT NULL,
31   - "receiver_mobile" varchar NOT NULL,
32   - "receiver_area_id" int NOT NULL,
33   - "receiver_post_code" int,
34   - "receiver_detail_address" varchar NOT NULL,
35   - "after_sale_status" int NOT NULL,
36   - "refund_price" int NOT NULL,
37   - "coupon_id" bigint NOT NULL,
38   - "coupon_price" int NOT NULL,
39   - "point_price" int NOT NULL,
40   - "creator" varchar DEFAULT '',
41   - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
42   - "updater" varchar DEFAULT '',
43   - "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
44   - "deleted" bit NOT NULL DEFAULT FALSE,
45   - PRIMARY KEY ("id")
  1 +CREATE TABLE IF NOT EXISTS "trade_order"
  2 +(
  3 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  4 + "no" varchar NOT NULL,
  5 + "type" int NOT NULL,
  6 + "terminal" int NOT NULL,
  7 + "user_id" bigint NOT NULL,
  8 + "user_ip" varchar NOT NULL,
  9 + "user_remark" varchar,
  10 + "status" int NOT NULL,
  11 + "product_count" int NOT NULL,
  12 + "cancel_type" int,
  13 + "remark" varchar,
  14 + "comment_status" boolean,
  15 + "brokerage_user_id" bigint,
  16 + "pay_status" bit NOT NULL,
  17 + "pay_time" datetime,
  18 + "finish_time" datetime,
  19 + "cancel_time" datetime,
  20 + "total_price" int NULL,
  21 + "order_price" int NULL,
  22 + "discount_price" int NOT NULL,
  23 + "delivery_price" int NOT NULL,
  24 + "adjust_price" int NOT NULL,
  25 + "pay_price" int NOT NULL,
  26 + "delivery_type" int NOT NULL,
  27 + "pay_order_id" bigint,
  28 + "pay_channel_code" varchar,
  29 + "delivery_template_id" bigint,
  30 + "logistics_id" bigint,
  31 + "logistics_no" varchar,
  32 + "delivery_time" datetime,
  33 + "receive_time" datetime,
  34 + "receiver_name" varchar NOT NULL,
  35 + "receiver_mobile" varchar NOT NULL,
  36 + "receiver_area_id" int NOT NULL,
  37 + "receiver_post_code" int,
  38 + "receiver_detail_address" varchar NOT NULL,
  39 + "pick_up_store_id" long NULL,
  40 + "pick_up_verify_code" varchar NULL,
  41 + "refund_status" int NULL,
  42 + "refund_price" int NULL,
  43 + "after_sale_status" int NULL,
  44 + "coupon_id" bigint NOT NULL,
  45 + "coupon_price" int NOT NULL,
  46 + "use_point" int NULL,
  47 + "point_price" int NOT NULL,
  48 + "give_point" int NULL,
  49 + "refund_point" int NULL,
  50 + "vip_price" int NULL,
  51 + "seckill_activity_id" long NULL,
  52 + "bargain_activity_id" long NULL,
  53 + "bargain_record_id" long NULL,
  54 + "combination_activity_id" long NULL,
  55 + "combination_head_id" long NULL,
  56 + "combination_record_id" long NULL,
  57 + "creator" varchar DEFAULT '',
  58 + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  59 + "updater" varchar DEFAULT '',
  60 + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  61 + "deleted" bit NOT NULL DEFAULT FALSE,
  62 + PRIMARY KEY ("id")
46 63 ) COMMENT '交易订单表';
47 64  
48   -CREATE TABLE IF NOT EXISTS "trade_order_item" (
49   - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
50   - "user_id" bigint NOT NULL,
51   - "order_id" bigint NOT NULL,
52   - "spu_id" bigint NOT NULL,
53   - "spu_name" varchar NOT NULL,
54   - "sku_id" bigint NOT NULL,
55   - "properties" varchar,
56   - "pic_url" varchar,
57   - "count" int NOT NULL,
58   - "original_price" int NOT NULL,
59   - "original_unit_price" int NOT NULL,
60   - "discount_price" int NOT NULL,
61   - "pay_price" int NOT NULL,
62   - "order_part_price" int NOT NULL,
63   - "order_divide_price" int NOT NULL,
64   - "after_sale_status" int NOT NULL,
65   - "creator" varchar DEFAULT '',
66   - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
67   - "updater" varchar DEFAULT '',
68   - "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
69   - "deleted" bit NOT NULL DEFAULT FALSE,
70   - PRIMARY KEY ("id")
  65 +CREATE TABLE IF NOT EXISTS "trade_order_item"
  66 +(
  67 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  68 + "user_id" bigint NOT NULL,
  69 + "order_id" bigint NOT NULL,
  70 + "cart_id" int NULL,
  71 + "spu_id" bigint NOT NULL,
  72 + "spu_name" varchar NOT NULL,
  73 + "sku_id" bigint NOT NULL,
  74 + "properties" varchar,
  75 + "pic_url" varchar,
  76 + "count" int NOT NULL,
  77 + "comment_status" boolean NULL,
  78 + "price" int NOT NULL,
  79 + "discount_price" int NOT NULL,
  80 + "delivery_price" int NULL,
  81 + "adjust_price" int NULL,
  82 + "pay_price" int NOT NULL,
  83 + "coupon_price" int NULL,
  84 + "point_price" int NULL,
  85 + "use_point" int NULL,
  86 + "give_point" int NULL,
  87 + "vip_price" int NULL,
  88 + "after_sale_id" long NULL,
  89 + "after_sale_status" int NOT NULL,
  90 + "creator" varchar DEFAULT '',
  91 + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  92 + "updater" varchar DEFAULT '',
  93 + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  94 + "deleted" bit NOT NULL DEFAULT FALSE,
  95 + PRIMARY KEY ("id")
71 96 ) COMMENT '交易订单明细表';
72 97  
73   -CREATE TABLE IF NOT EXISTS "trade_after_sale" (
74   - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
75   - "no" varchar NOT NULL,
76   - "status" int NOT NULL,
77   - "type" int NOT NULL,
78   - "way" int NOT NULL,
79   - "user_id" bigint NOT NULL,
80   - "apply_reason" varchar NOT NULL,
  98 +CREATE TABLE IF NOT EXISTS "trade_after_sale"
  99 +(
  100 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  101 + "no" varchar NOT NULL,
  102 + "status" int NOT NULL,
  103 + "type" int NOT NULL,
  104 + "way" int NOT NULL,
  105 + "user_id" bigint NOT NULL,
  106 + "apply_reason" varchar NOT NULL,
81 107 "apply_description" varchar,
82 108 "apply_pic_urls" varchar,
83   - "order_id" bigint NOT NULL,
84   - "order_no" varchar NOT NULL,
85   - "order_item_id" bigint NOT NULL,
86   - "spu_id" bigint NOT NULL,
87   - "spu_name" varchar NOT NULL,
88   - "sku_id" bigint NOT NULL,
89   - "properties" varchar,
90   - "pic_url" varchar,
91   - "count" int NOT NULL,
92   - "audit_time" varchar,
93   - "audit_user_id" bigint,
94   - "audit_reason" varchar,
95   - "refund_price" int NOT NULL,
96   - "pay_refund_id" bigint,
97   - "refund_time" varchar,
98   - "logistics_id" bigint,
99   - "logistics_no" varchar,
100   - "delivery_time" varchar,
101   - "receive_time" varchar,
  109 + "order_id" bigint NOT NULL,
  110 + "order_no" varchar NOT NULL,
  111 + "order_item_id" bigint NOT NULL,
  112 + "spu_id" bigint NOT NULL,
  113 + "spu_name" varchar NOT NULL,
  114 + "sku_id" bigint NOT NULL,
  115 + "properties" varchar,
  116 + "pic_url" varchar,
  117 + "count" int NOT NULL,
  118 + "audit_time" varchar,
  119 + "audit_user_id" bigint,
  120 + "audit_reason" varchar,
  121 + "refund_price" int NOT NULL,
  122 + "pay_refund_id" bigint,
  123 + "refund_time" varchar,
  124 + "logistics_id" bigint,
  125 + "logistics_no" varchar,
  126 + "delivery_time" varchar,
  127 + "receive_time" varchar,
102 128 "receive_reason" varchar,
103   - "creator" varchar DEFAULT '',
104   - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
105   - "updater" varchar DEFAULT '',
106   - "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
107   - "deleted" bit NOT NULL DEFAULT FALSE,
  129 + "creator" varchar DEFAULT '',
  130 + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  131 + "updater" varchar DEFAULT '',
  132 + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  133 + "deleted" bit NOT NULL DEFAULT FALSE,
108 134 PRIMARY KEY ("id")
109 135 ) COMMENT '交易售后表';
110 136  
111   -CREATE TABLE IF NOT EXISTS "trade_after_sale_log" (
112   - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
113   - "user_id" bigint NOT NULL,
114   - "user_type" int NOT NULL,
115   - "after_sale_id" bigint NOT NULL,
116   - "order_id" bigint NOT NULL,
117   - "order_item_id" bigint NOT NULL,
  137 +CREATE TABLE IF NOT EXISTS "trade_after_sale_log"
  138 +(
  139 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  140 + "user_id" bigint NOT NULL,
  141 + "user_type" int NOT NULL,
  142 + "after_sale_id" bigint NOT NULL,
  143 + "order_id" bigint NOT NULL,
  144 + "order_item_id" bigint NOT NULL,
118 145 "before_status" int,
119   - "after_status" int NOT NULL,
120   - "content" varchar NOT NULL,
121   - "creator" varchar DEFAULT '',
122   - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
123   - "updater" varchar DEFAULT '',
124   - "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
125   - "deleted" bit NOT NULL DEFAULT FALSE,
  146 + "after_status" int NOT NULL,
  147 + "content" varchar NOT NULL,
  148 + "creator" varchar DEFAULT '',
  149 + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  150 + "updater" varchar DEFAULT '',
  151 + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  152 + "deleted" bit NOT NULL DEFAULT FALSE,
126 153 PRIMARY KEY ("id")
127 154 ) COMMENT '交易售后日志';
128 155  
... ... @@ -161,7 +188,7 @@ CREATE TABLE IF NOT EXISTS &quot;trade_brokerage_record&quot;
161 188 "updater" varchar DEFAULT '',
162 189 "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
163 190 "deleted" bit NOT NULL DEFAULT FALSE,
164   - "tenant_id" bigint not null default '0',
  191 + "tenant_id" bigint not null default '0',
165 192 PRIMARY KEY ("id")
166 193 ) COMMENT '佣金记录';
167 194 CREATE TABLE IF NOT EXISTS "trade_brokerage_withdraw"
... ... @@ -186,6 +213,22 @@ CREATE TABLE IF NOT EXISTS &quot;trade_brokerage_withdraw&quot;
186 213 "updater" varchar DEFAULT '',
187 214 "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
188 215 "deleted" bit NOT NULL DEFAULT FALSE,
189   - "tenant_id" bigint not null default '0',
  216 + "tenant_id" bigint not null default '0',
  217 + PRIMARY KEY ("id")
  218 +) COMMENT '佣金提现';
  219 +
  220 +CREATE TABLE IF NOT EXISTS "trade_delivery_express"
  221 +(
  222 + "id" int NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  223 + "code" varchar NULL,
  224 + "name" varchar,
  225 + "logo" varchar NULL,
  226 + "sort" int NOT NULL,
  227 + "status" int NOT NULL,
  228 + "creator" varchar DEFAULT '',
  229 + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  230 + "updater" varchar DEFAULT '',
  231 + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  232 + "deleted" bit NOT NULL DEFAULT FALSE,
190 233 PRIMARY KEY ("id")
191 234 ) COMMENT '佣金提现';
192 235 \ No newline at end of file
... ...
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/common/SexEnum.java
... ... @@ -11,6 +11,7 @@ import lombok.Getter;
11 11 @Getter
12 12 @AllArgsConstructor
13 13 public enum SexEnum {
  14 +
14 15 /** 男 */
15 16 MALE(1),
16 17 /** 女 */
... ...