Commit 27e2996c5d855b1bbdf82d29eb215c00e68c7297
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,8 +67,8 @@ exit | ||
| 67 | 67 | ||
| 68 | ① 下载人大金仓 Docker 镜像: | 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 | ② 加载镜像文件,在镜像 tar 文件所在目录运行: | 73 | ② 加载镜像文件,在镜像 tar 文件所在目录运行: |
| 74 | 74 | ||
| @@ -80,11 +80,11 @@ docker load -i x86_64/kdb_x86_64_V009R001C001B0025.tar | @@ -80,11 +80,11 @@ docker load -i x86_64/kdb_x86_64_V009R001C001B0025.tar | ||
| 80 | 80 | ||
| 81 | ```Bash | 81 | ```Bash |
| 82 | docker compose up -d kingbase | 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 | ### 1.7 华为 OpenGauss | 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,15 +247,15 @@ id,name,type,parentId | ||
| 247 | 246,英属印度洋领地,1,0 | 247 | 246,英属印度洋领地,1,0 |
| 248 | 247,东萨摩亚,1,0 | 248 | 247,东萨摩亚,1,0 |
| 249 | 248,诺福克岛,1,0 | 249 | 248,诺福克岛,1,0 |
| 250 | -110000,北京,2,1 | ||
| 251 | -120000,天津,2,1 | 250 | +110000,北京市,2,1 |
| 251 | +120000,天津市,2,1 | ||
| 252 | 130000,河北省,2,1 | 252 | 130000,河北省,2,1 |
| 253 | 140000,山西省,2,1 | 253 | 140000,山西省,2,1 |
| 254 | 150000,内蒙古自治区,2,1 | 254 | 150000,内蒙古自治区,2,1 |
| 255 | 210000,辽宁省,2,1 | 255 | 210000,辽宁省,2,1 |
| 256 | 220000,吉林省,2,1 | 256 | 220000,吉林省,2,1 |
| 257 | 230000,黑龙江省,2,1 | 257 | 230000,黑龙江省,2,1 |
| 258 | -310000,上海,2,1 | 258 | +310000,上海市,2,1 |
| 259 | 320000,江苏省,2,1 | 259 | 320000,江苏省,2,1 |
| 260 | 330000,浙江省,2,1 | 260 | 330000,浙江省,2,1 |
| 261 | 340000,安徽省,2,1 | 261 | 340000,安徽省,2,1 |
| @@ -268,7 +268,7 @@ id,name,type,parentId | @@ -268,7 +268,7 @@ id,name,type,parentId | ||
| 268 | 440000,广东省,2,1 | 268 | 440000,广东省,2,1 |
| 269 | 450000,广西壮族自治区,2,1 | 269 | 450000,广西壮族自治区,2,1 |
| 270 | 460000,海南省,2,1 | 270 | 460000,海南省,2,1 |
| 271 | -500000,重庆,2,1 | 271 | +500000,重庆市,2,1 |
| 272 | 510000,四川省,2,1 | 272 | 510000,四川省,2,1 |
| 273 | 520000,贵州省,2,1 | 273 | 520000,贵州省,2,1 |
| 274 | 530000,云南省,2,1 | 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,20 +34,24 @@ public class CronUtils { | ||
| 34 | * @return 满足条件的执行时间 | 34 | * @return 满足条件的执行时间 |
| 35 | */ | 35 | */ |
| 36 | public static List<LocalDateTime> getNextTimes(String cronExpression, int n) { | 36 | public static List<LocalDateTime> getNextTimes(String cronExpression, int n) { |
| 37 | - // 获得 CronExpression 对象 | 37 | + // 1. 获得 CronExpression 对象 |
| 38 | CronExpression cron; | 38 | CronExpression cron; |
| 39 | try { | 39 | try { |
| 40 | cron = new CronExpression(cronExpression); | 40 | cron = new CronExpression(cronExpression); |
| 41 | } catch (ParseException e) { | 41 | } catch (ParseException e) { |
| 42 | throw new IllegalArgumentException(e.getMessage()); | 42 | throw new IllegalArgumentException(e.getMessage()); |
| 43 | } | 43 | } |
| 44 | - // 从当前开始计算,n 个满足条件的 | 44 | + // 2. 从当前开始计算,n 个满足条件的 |
| 45 | Date now = new Date(); | 45 | Date now = new Date(); |
| 46 | List<LocalDateTime> nextTimes = new ArrayList<>(n); | 46 | List<LocalDateTime> nextTimes = new ArrayList<>(n); |
| 47 | for (int i = 0; i < n; i++) { | 47 | for (int i = 0; i < n; i++) { |
| 48 | Date nextTime = cron.getNextValidTimeAfter(now); | 48 | Date nextTime = cron.getNextValidTimeAfter(now); |
| 49 | + // 2.1 如果 nextTime 为 null,说明没有更多的有效时间,退出循环 | ||
| 50 | + if (nextTime == null) { | ||
| 51 | + break; | ||
| 52 | + } | ||
| 49 | nextTimes.add(LocalDateTimeUtil.of(nextTime)); | 53 | nextTimes.add(LocalDateTimeUtil.of(nextTime)); |
| 50 | - // 切换现在,为下一个触发时间; | 54 | + // 2.2 切换现在,为下一个触发时间; |
| 51 | now = nextTime; | 55 | now = nextTime; |
| 52 | } | 56 | } |
| 53 | return nextTimes; | 57 | return nextTimes; |
yudao-framework/yudao-spring-boot-starter-protection/pom.xml
| @@ -12,7 +12,7 @@ | @@ -12,7 +12,7 @@ | ||
| 12 | <packaging>jar</packaging> | 12 | <packaging>jar</packaging> |
| 13 | 13 | ||
| 14 | <name>${project.artifactId}</name> | 14 | <name>${project.artifactId}</name> |
| 15 | - <description>服务保证,提供分布式锁、幂等、限流、熔断等等功能</description> | 15 | + <description>服务保证,提供分布式锁、幂等、限流、熔断、API 签名等等功能</description> |
| 16 | <url>https://github.com/YunaiV/ruoyi-vue-pro</url> | 16 | <url>https://github.com/YunaiV/ruoyi-vue-pro</url> |
| 17 | 17 | ||
| 18 | <dependencies> | 18 | <dependencies> |
| @@ -35,6 +35,13 @@ | @@ -35,6 +35,13 @@ | ||
| 35 | <artifactId>lock4j-redisson-spring-boot-starter</artifactId> | 35 | <artifactId>lock4j-redisson-spring-boot-starter</artifactId> |
| 36 | <optional>true</optional> | 36 | <optional>true</optional> |
| 37 | </dependency> | 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 | </dependencies> | 45 | </dependencies> |
| 39 | 46 | ||
| 40 | </project> | 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
yudao-framework/yudao-spring-boot-starter-protection/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
| 1 | cn.iocoder.yudao.framework.idempotent.config.YudaoIdempotentConfiguration | 1 | cn.iocoder.yudao.framework.idempotent.config.YudaoIdempotentConfiguration |
| 2 | cn.iocoder.yudao.framework.lock4j.config.YudaoLock4jConfiguration | 2 | cn.iocoder.yudao.framework.lock4j.config.YudaoLock4jConfiguration |
| 3 | -cn.iocoder.yudao.framework.ratelimiter.config.YudaoRateLimiterConfiguration | ||
| 4 | \ No newline at end of file | 3 | \ No newline at end of file |
| 4 | +cn.iocoder.yudao.framework.ratelimiter.config.YudaoRateLimiterConfiguration | ||
| 5 | +cn.iocoder.yudao.framework.signature.config.YudaoApiSignatureAutoConfiguration | ||
| 5 | \ No newline at end of file | 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 + "×tamp=" + 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 | package cn.iocoder.yudao.module.crm.dal.dataobject.business; | 1 | package cn.iocoder.yudao.module.crm.dal.dataobject.business; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; | ||
| 3 | import com.baomidou.mybatisplus.annotation.KeySequence; | 4 | import com.baomidou.mybatisplus.annotation.KeySequence; |
| 4 | import com.baomidou.mybatisplus.annotation.TableId; | 5 | import com.baomidou.mybatisplus.annotation.TableId; |
| 5 | import com.baomidou.mybatisplus.annotation.TableName; | 6 | import com.baomidou.mybatisplus.annotation.TableName; |
| @@ -19,7 +20,7 @@ import lombok.*; | @@ -19,7 +20,7 @@ import lombok.*; | ||
| 19 | @Builder | 20 | @Builder |
| 20 | @NoArgsConstructor | 21 | @NoArgsConstructor |
| 21 | @AllArgsConstructor | 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,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,7 +25,6 @@ import java.time.LocalDateTime; | ||
| 25 | @KeySequence(value = "infra_api_error_log_seq") | 25 | @KeySequence(value = "infra_api_error_log_seq") |
| 26 | public class ApiErrorLogDO extends BaseDO { | 26 | public class ApiErrorLogDO extends BaseDO { |
| 27 | 27 | ||
| 28 | - | ||
| 29 | /** | 28 | /** |
| 30 | * {@link #requestParams} 的最大长度 | 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,7 +342,8 @@ public class CodegenEngine { | ||
| 342 | 342 | ||
| 343 | // className 相关 | 343 | // className 相关 |
| 344 | // 去掉指定前缀,将 TestDictType 转换成 DictType. 因为在 create 等方法后,不需要带上 Test 前缀 | 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 | bindingMap.put("simpleClassName", simpleClassName); | 347 | bindingMap.put("simpleClassName", simpleClassName); |
| 347 | bindingMap.put("simpleClassName_underlineCase", toUnderlineCase(simpleClassName)); // 将 DictType 转换成 dict_type | 348 | bindingMap.put("simpleClassName_underlineCase", toUnderlineCase(simpleClassName)); // 将 DictType 转换成 dict_type |
| 348 | bindingMap.put("classNameVar", lowerFirst(simpleClassName)); // 将 DictType 转换成 dictType,用于变量 | 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 | package cn.iocoder.yudao.module.trade.service.order; | 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 | import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | 5 | import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; |
| 4 | import cn.iocoder.yudao.module.member.api.address.MemberAddressApi; | 6 | import cn.iocoder.yudao.module.member.api.address.MemberAddressApi; |
| 5 | import cn.iocoder.yudao.module.member.api.user.MemberUserApi; | 7 | import cn.iocoder.yudao.module.member.api.user.MemberUserApi; |
| 6 | import cn.iocoder.yudao.module.pay.api.order.PayOrderApi; | 8 | import cn.iocoder.yudao.module.pay.api.order.PayOrderApi; |
| 7 | import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO; | 9 | import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO; |
| 8 | import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; | 10 | import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; |
| 11 | +import cn.iocoder.yudao.module.product.api.comment.ProductCommentApi; | ||
| 9 | import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; | 12 | import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; |
| 10 | import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; | 13 | import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; |
| 11 | import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi; | 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 | import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO; | 17 | import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO; |
| 13 | import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; | 18 | import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; |
| 14 | import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper; | 19 | import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper; |
| 15 | import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper; | 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 | import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum; | 24 | import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum; |
| 17 | import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderConfig; | 25 | import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderConfig; |
| 18 | import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties; | 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 | import org.junit.jupiter.api.BeforeEach; | 35 | import org.junit.jupiter.api.BeforeEach; |
| 20 | import org.junit.jupiter.api.Disabled; | 36 | import org.junit.jupiter.api.Disabled; |
| 21 | import org.junit.jupiter.api.Test; | 37 | import org.junit.jupiter.api.Test; |
| 22 | import org.springframework.boot.test.mock.mockito.MockBean; | 38 | import org.springframework.boot.test.mock.mockito.MockBean; |
| 23 | import org.springframework.context.annotation.Import; | 39 | import org.springframework.context.annotation.Import; |
| 24 | 40 | ||
| 25 | -import jakarta.annotation.Resource; | ||
| 26 | import java.time.Duration; | 41 | import java.time.Duration; |
| 27 | 42 | ||
| 28 | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | 43 | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; |
| 29 | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; | 44 | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; |
| 30 | import static org.junit.jupiter.api.Assertions.*; | 45 | import static org.junit.jupiter.api.Assertions.*; |
| 46 | +import static org.mockito.ArgumentMatchers.anyString; | ||
| 31 | import static org.mockito.ArgumentMatchers.eq; | 47 | import static org.mockito.ArgumentMatchers.eq; |
| 32 | import static org.mockito.Mockito.when; | 48 | import static org.mockito.Mockito.when; |
| 33 | 49 | ||
| @@ -38,7 +54,9 @@ import static org.mockito.Mockito.when; | @@ -38,7 +54,9 @@ import static org.mockito.Mockito.when; | ||
| 38 | * @since 2022-09-07 | 54 | * @since 2022-09-07 |
| 39 | */ | 55 | */ |
| 40 | @Disabled // TODO 芋艿:后续 fix 补充的单测 | 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 | public class TradeOrderUpdateServiceTest extends BaseDbUnitTest { | 60 | public class TradeOrderUpdateServiceTest extends BaseDbUnitTest { |
| 43 | 61 | ||
| 44 | @Resource | 62 | @Resource |
| @@ -55,7 +73,9 @@ public class TradeOrderUpdateServiceTest extends BaseDbUnitTest { | @@ -55,7 +73,9 @@ public class TradeOrderUpdateServiceTest extends BaseDbUnitTest { | ||
| 55 | private ProductSpuApi productSpuApi; | 73 | private ProductSpuApi productSpuApi; |
| 56 | @MockBean | 74 | @MockBean |
| 57 | private ProductSkuApi productSkuApi; | 75 | private ProductSkuApi productSkuApi; |
| 58 | -// @MockBean | 76 | + @MockBean |
| 77 | + private ProductCommentApi productCommentApi; | ||
| 78 | + // @MockBean | ||
| 59 | // private PriceApi priceApi; | 79 | // private PriceApi priceApi; |
| 60 | @MockBean | 80 | @MockBean |
| 61 | private PayOrderApi payOrderApi; | 81 | private PayOrderApi payOrderApi; |
| @@ -66,11 +86,22 @@ public class TradeOrderUpdateServiceTest extends BaseDbUnitTest { | @@ -66,11 +86,22 @@ public class TradeOrderUpdateServiceTest extends BaseDbUnitTest { | ||
| 66 | 86 | ||
| 67 | @MockBean | 87 | @MockBean |
| 68 | private TradeOrderProperties tradeOrderProperties; | 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 | @BeforeEach | 100 | @BeforeEach |
| 71 | public void setUp() { | 101 | public void setUp() { |
| 72 | when(tradeOrderProperties.getAppId()).thenReturn(888L); | 102 | when(tradeOrderProperties.getAppId()).thenReturn(888L); |
| 73 | when(tradeOrderProperties.getPayExpireTime()).thenReturn(Duration.ofDays(1)); | 103 | when(tradeOrderProperties.getPayExpireTime()).thenReturn(Duration.ofDays(1)); |
| 104 | + when(tradeNoRedisDAO.generate(anyString())).thenReturn(IdUtil.randomUUID()); | ||
| 74 | } | 105 | } |
| 75 | 106 | ||
| 76 | // @Test | 107 | // @Test |
| @@ -259,11 +290,18 @@ public class TradeOrderUpdateServiceTest extends BaseDbUnitTest { | @@ -259,11 +290,18 @@ public class TradeOrderUpdateServiceTest extends BaseDbUnitTest { | ||
| 259 | TradeOrderDO order = randomPojo(TradeOrderDO.class, o -> { | 290 | TradeOrderDO order = randomPojo(TradeOrderDO.class, o -> { |
| 260 | o.setId(1L).setStatus(TradeOrderStatusEnum.UNDELIVERED.getStatus()); | 291 | o.setId(1L).setStatus(TradeOrderStatusEnum.UNDELIVERED.getStatus()); |
| 261 | o.setLogisticsId(null).setLogisticsNo(null).setDeliveryTime(null); | 292 | o.setLogisticsId(null).setLogisticsNo(null).setDeliveryTime(null); |
| 293 | + o.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus()); | ||
| 294 | + o.setDeliveryType(DeliveryTypeEnum.EXPRESS.getType()); | ||
| 262 | }); | 295 | }); |
| 263 | tradeOrderMapper.insert(order); | 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 | TradeOrderDeliveryReqVO deliveryReqVO = new TradeOrderDeliveryReqVO().setId(1L) | 302 | TradeOrderDeliveryReqVO deliveryReqVO = new TradeOrderDeliveryReqVO().setId(1L) |
| 266 | - .setLogisticsId(10L).setLogisticsNo("100"); | 303 | + .setLogisticsId(deliveryExpressId).setLogisticsNo("100"); |
| 304 | + | ||
| 267 | // mock 方法(支付单) | 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 | ) COMMENT '交易订单表'; | 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 | ) COMMENT '交易订单明细表'; | 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 | "apply_description" varchar, | 107 | "apply_description" varchar, |
| 82 | "apply_pic_urls" varchar, | 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 | "receive_reason" varchar, | 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 | PRIMARY KEY ("id") | 134 | PRIMARY KEY ("id") |
| 109 | ) COMMENT '交易售后表'; | 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 | "before_status" int, | 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 | PRIMARY KEY ("id") | 153 | PRIMARY KEY ("id") |
| 127 | ) COMMENT '交易售后日志'; | 154 | ) COMMENT '交易售后日志'; |
| 128 | 155 | ||
| @@ -161,7 +188,7 @@ CREATE TABLE IF NOT EXISTS "trade_brokerage_record" | @@ -161,7 +188,7 @@ CREATE TABLE IF NOT EXISTS "trade_brokerage_record" | ||
| 161 | "updater" varchar DEFAULT '', | 188 | "updater" varchar DEFAULT '', |
| 162 | "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, | 189 | "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, |
| 163 | "deleted" bit NOT NULL DEFAULT FALSE, | 190 | "deleted" bit NOT NULL DEFAULT FALSE, |
| 164 | - "tenant_id" bigint not null default '0', | 191 | + "tenant_id" bigint not null default '0', |
| 165 | PRIMARY KEY ("id") | 192 | PRIMARY KEY ("id") |
| 166 | ) COMMENT '佣金记录'; | 193 | ) COMMENT '佣金记录'; |
| 167 | CREATE TABLE IF NOT EXISTS "trade_brokerage_withdraw" | 194 | CREATE TABLE IF NOT EXISTS "trade_brokerage_withdraw" |
| @@ -186,6 +213,22 @@ CREATE TABLE IF NOT EXISTS "trade_brokerage_withdraw" | @@ -186,6 +213,22 @@ CREATE TABLE IF NOT EXISTS "trade_brokerage_withdraw" | ||
| 186 | "updater" varchar DEFAULT '', | 213 | "updater" varchar DEFAULT '', |
| 187 | "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, | 214 | "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, |
| 188 | "deleted" bit NOT NULL DEFAULT FALSE, | 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 | PRIMARY KEY ("id") | 233 | PRIMARY KEY ("id") |
| 191 | ) COMMENT '佣金提现'; | 234 | ) COMMENT '佣金提现'; |
| 192 | \ No newline at end of file | 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