Commit dad0cc4cb1d00840b5df2c80b549e3ac1bafaac2

Authored by YunaiV
2 parents 5a906a7b cb701d7d

Merge branch 'develop' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into master-jdk17

Showing 30 changed files with 270 additions and 138 deletions
yudao-dependencies/pom.xml
@@ -71,7 +71,7 @@ @@ -71,7 +71,7 @@
71 <!-- 三方云服务相关 --> 71 <!-- 三方云服务相关 -->
72 <commons-io.version>2.17.0</commons-io.version> 72 <commons-io.version>2.17.0</commons-io.version>
73 <commons-compress.version>1.27.1</commons-compress.version> 73 <commons-compress.version>1.27.1</commons-compress.version>
74 - <aws-java-sdk-s3.version>1.12.777</aws-java-sdk-s3.version> 74 + <awssdk.version>2.30.14</awssdk.version>
75 <justauth.version>2.0.5</justauth.version> 75 <justauth.version>2.0.5</justauth.version>
76 <jimureport.version>1.8.1</jimureport.version> 76 <jimureport.version>1.8.1</jimureport.version>
77 <weixin-java.version>4.7.2.B</weixin-java.version> 77 <weixin-java.version>4.7.2.B</weixin-java.version>
@@ -553,9 +553,9 @@ @@ -553,9 +553,9 @@
553 553
554 <!-- 三方云服务相关 --> 554 <!-- 三方云服务相关 -->
555 <dependency> 555 <dependency>
556 - <groupId>com.amazonaws</groupId>  
557 - <artifactId>aws-java-sdk-s3</artifactId>  
558 - <version>${aws-java-sdk-s3.version}</version> 556 + <groupId>software.amazon.awssdk</groupId>
  557 + <artifactId>s3</artifactId>
  558 + <version>${awssdk.version}</version>
559 </dependency> 559 </dependency>
560 560
561 <dependency> 561 <dependency>
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java
@@ -11,6 +11,7 @@ import java.util.function.*; @@ -11,6 +11,7 @@ import java.util.function.*;
11 import java.util.stream.Collectors; 11 import java.util.stream.Collectors;
12 import java.util.stream.Stream; 12 import java.util.stream.Stream;
13 13
  14 +import static cn.hutool.core.convert.Convert.toCollection;
14 import static java.util.Arrays.asList; 15 import static java.util.Arrays.asList;
15 16
16 /** 17 /**
@@ -335,4 +336,17 @@ public class CollectionUtils { @@ -335,4 +336,17 @@ public class CollectionUtils {
335 return list.stream().filter(Objects::nonNull).flatMap(Collection::stream).collect(Collectors.toList()); 336 return list.stream().filter(Objects::nonNull).flatMap(Collection::stream).collect(Collectors.toList());
336 } 337 }
337 338
  339 + /**
  340 + * 转换为 LinkedHashSet
  341 + *
  342 + * @param <T> 元素类型
  343 + * @param elementType 集合中元素类型
  344 + * @param value 被转换的值
  345 + * @return {@link LinkedHashSet}
  346 + */
  347 + @SuppressWarnings("unchecked")
  348 + public static <T> LinkedHashSet<T> toLinkedHashSet(Class<T> elementType, Object value) {
  349 + return (LinkedHashSet<T>) toCollection(LinkedHashSet.class, elementType, value);
  350 + }
  351 +
338 } 352 }
339 \ No newline at end of file 353 \ No newline at end of file
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/form/BpmTaskCandidateFormUserStrategy.java
1 package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.form; 1 package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.form;
2 2
3 -import cn.hutool.core.convert.Convert;  
4 import cn.hutool.core.lang.Assert; 3 import cn.hutool.core.lang.Assert;
  4 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
5 import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; 5 import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
6 import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user.BpmTaskCandidateUserStrategy; 6 import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user.BpmTaskCandidateUserStrategy;
7 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; 7 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
@@ -33,7 +33,7 @@ public class BpmTaskCandidateFormUserStrategy implements BpmTaskCandidateStrateg @@ -33,7 +33,7 @@ public class BpmTaskCandidateFormUserStrategy implements BpmTaskCandidateStrateg
33 @Override 33 @Override
34 public Set<Long> calculateUsersByTask(DelegateExecution execution, String param) { 34 public Set<Long> calculateUsersByTask(DelegateExecution execution, String param) {
35 Object result = execution.getVariable(param); 35 Object result = execution.getVariable(param);
36 - return Convert.toSet(Long.class, result); 36 + return CollectionUtils.toLinkedHashSet(Long.class, result);
37 } 37 }
38 38
39 @Override 39 @Override
@@ -41,7 +41,7 @@ public class BpmTaskCandidateFormUserStrategy implements BpmTaskCandidateStrateg @@ -41,7 +41,7 @@ public class BpmTaskCandidateFormUserStrategy implements BpmTaskCandidateStrateg
41 String param, Long startUserId, String processDefinitionId, 41 String param, Long startUserId, String processDefinitionId,
42 Map<String, Object> processVariables) { 42 Map<String, Object> processVariables) {
43 Object result = processVariables == null ? null : processVariables.get(param); 43 Object result = processVariables == null ? null : processVariables.get(param);
44 - return Convert.toSet(Long.class, result); 44 + return CollectionUtils.toLinkedHashSet(Long.class, result);
45 } 45 }
46 46
47 } 47 }
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategy.java
1 package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.other; 1 package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.other;
2 2
3 -import cn.hutool.core.convert.Convert; 3 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
4 import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; 4 import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
5 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; 5 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
6 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; 6 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
@@ -37,7 +37,7 @@ public class BpmTaskCandidateExpressionStrategy implements BpmTaskCandidateStrat @@ -37,7 +37,7 @@ public class BpmTaskCandidateExpressionStrategy implements BpmTaskCandidateStrat
37 @Override 37 @Override
38 public Set<Long> calculateUsersByTask(DelegateExecution execution, String param) { 38 public Set<Long> calculateUsersByTask(DelegateExecution execution, String param) {
39 Object result = FlowableUtils.getExpressionValue(execution, param); 39 Object result = FlowableUtils.getExpressionValue(execution, param);
40 - return Convert.toSet(Long.class, result); 40 + return CollectionUtils.toLinkedHashSet(Long.class, result);
41 } 41 }
42 42
43 @Override 43 @Override
@@ -46,7 +46,7 @@ public class BpmTaskCandidateExpressionStrategy implements BpmTaskCandidateStrat @@ -46,7 +46,7 @@ public class BpmTaskCandidateExpressionStrategy implements BpmTaskCandidateStrat
46 Map<String, Object> variables = processVariables == null ? new HashMap<>() : processVariables; 46 Map<String, Object> variables = processVariables == null ? new HashMap<>() : processVariables;
47 try { 47 try {
48 Object result = FlowableUtils.getExpressionValue(variables, param); 48 Object result = FlowableUtils.getExpressionValue(variables, param);
49 - return Convert.toSet(Long.class, result); 49 + return CollectionUtils.toLinkedHashSet(Long.class, result);
50 } catch (FlowableException ex) { 50 } catch (FlowableException ex) {
51 // 预测未运行的节点时候,表达式如果包含 execution 或者不存在的流程变量会抛异常, 51 // 预测未运行的节点时候,表达式如果包含 execution 或者不存在的流程变量会抛异常,
52 log.warn("[calculateUsersByActivity][表达式({}) 变量({}) 解析报错", param, variables, ex); 52 log.warn("[calculateUsersByActivity][表达式({}) 变量({}) 解析报错", param, variables, ex);
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
@@ -6,7 +6,6 @@ import cn.hutool.core.lang.Assert; @@ -6,7 +6,6 @@ import cn.hutool.core.lang.Assert;
6 import cn.hutool.core.util.*; 6 import cn.hutool.core.util.*;
7 import cn.hutool.extra.spring.SpringUtil; 7 import cn.hutool.extra.spring.SpringUtil;
8 import cn.iocoder.yudao.framework.common.pojo.PageResult; 8 import cn.iocoder.yudao.framework.common.pojo.PageResult;
9 -import cn.iocoder.yudao.framework.common.util.collection.MapUtils;  
10 import cn.iocoder.yudao.framework.common.util.date.DateUtils; 9 import cn.iocoder.yudao.framework.common.util.date.DateUtils;
11 import cn.iocoder.yudao.framework.common.util.number.NumberUtils; 10 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
12 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; 11 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
@@ -882,6 +881,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -882,6 +881,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
882 return; 881 return;
883 } 882 }
884 runExecutionIds.add(task.getExecutionId()); 883 runExecutionIds.add(task.getExecutionId());
  884 +
885 // 判断是否分配给自己任务,因为会签任务,一个节点会有多个任务 885 // 判断是否分配给自己任务,因为会签任务,一个节点会有多个任务
886 if (isAssignUserTask(userId, task)) { // 情况一:自己的任务,进行 RETURN 标记 886 if (isAssignUserTask(userId, task)) { // 情况一:自己的任务,进行 RETURN 标记
887 // 2.1.1 添加评论 887 // 2.1.1 添加评论
yudao-module-infra/yudao-module-infra-biz/pom.xml
@@ -115,9 +115,10 @@ @@ -115,9 +115,10 @@
115 <groupId>com.jcraft</groupId> 115 <groupId>com.jcraft</groupId>
116 <artifactId>jsch</artifactId> <!-- 文件客户端:解决 sftp 连接 --> 116 <artifactId>jsch</artifactId> <!-- 文件客户端:解决 sftp 连接 -->
117 </dependency> 117 </dependency>
  118 + <!-- 文件客户端:解决阿里云、腾讯云、minio 等 S3 连接 -->
118 <dependency> 119 <dependency>
119 - <groupId>com.amazonaws</groupId>  
120 - <artifactId>aws-java-sdk-s3</artifactId><!-- 文件客户端:解决阿里云、腾讯云、minio 等 S3 连接 --> 120 + <groupId>software.amazon.awssdk</groupId>
  121 + <artifactId>s3</artifactId>
121 </dependency> 122 </dependency>
122 123
123 <dependency> 124 <dependency>
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenFrontTypeEnum.java
@@ -13,10 +13,13 @@ import lombok.Getter; @@ -13,10 +13,13 @@ import lombok.Getter;
13 public enum CodegenFrontTypeEnum { 13 public enum CodegenFrontTypeEnum {
14 14
15 VUE2_ELEMENT_UI(10), // Vue2 Element UI 标准模版 15 VUE2_ELEMENT_UI(10), // Vue2 Element UI 标准模版
  16 +
16 VUE3_ELEMENT_PLUS(20), // Vue3 Element Plus 标准模版 17 VUE3_ELEMENT_PLUS(20), // Vue3 Element Plus 标准模版
  18 +
17 VUE3_VBEN2_ANTD_SCHEMA(30), // Vue3 VBEN2 + ANTD + Schema 模版 19 VUE3_VBEN2_ANTD_SCHEMA(30), // Vue3 VBEN2 + ANTD + Schema 模版
  20 +
18 VUE3_VBEN5_ANTD_SCHEMA(40), // Vue3 VBEN5 + ANTD + schema 模版 21 VUE3_VBEN5_ANTD_SCHEMA(40), // Vue3 VBEN5 + ANTD + schema 模版
19 - VUE3_VBEN5_ANTD(50), // Vue3 VBEN5 + ANTD 模版 22 + VUE3_VBEN5_ANTD_GENERAL(41), // Vue3 VBEN5 + ANTD 标准模版
20 ; 23 ;
21 24
22 /** 25 /**
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClient.java
@@ -4,29 +4,31 @@ import cn.hutool.core.io.IoUtil; @@ -4,29 +4,31 @@ import cn.hutool.core.io.IoUtil;
4 import cn.hutool.core.util.StrUtil; 4 import cn.hutool.core.util.StrUtil;
5 import cn.hutool.http.HttpUtil; 5 import cn.hutool.http.HttpUtil;
6 import cn.iocoder.yudao.module.infra.framework.file.core.client.AbstractFileClient; 6 import cn.iocoder.yudao.module.infra.framework.file.core.client.AbstractFileClient;
7 -import com.amazonaws.HttpMethod;  
8 -import com.amazonaws.auth.AWSStaticCredentialsProvider;  
9 -import com.amazonaws.auth.BasicAWSCredentials;  
10 -import com.amazonaws.client.builder.AwsClientBuilder;  
11 -import com.amazonaws.services.s3.AmazonS3Client;  
12 -import com.amazonaws.services.s3.AmazonS3ClientBuilder;  
13 -import com.amazonaws.services.s3.model.ObjectMetadata;  
14 -import com.amazonaws.services.s3.model.S3Object; 7 +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
  8 +import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
  9 +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
  10 +import software.amazon.awssdk.core.sync.RequestBody;
  11 +import software.amazon.awssdk.regions.Region;
  12 +import software.amazon.awssdk.services.s3.S3Client;
  13 +import software.amazon.awssdk.services.s3.S3Configuration;
  14 +import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
  15 +import software.amazon.awssdk.services.s3.model.GetObjectRequest;
  16 +import software.amazon.awssdk.services.s3.model.PutObjectRequest;
  17 +import software.amazon.awssdk.services.s3.presigner.S3Presigner;
  18 +import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest;
15 19
16 -import java.io.ByteArrayInputStream;  
17 -import java.util.Date;  
18 -import java.util.concurrent.TimeUnit; 20 +import java.net.URI;
  21 +import java.time.Duration;
19 22
20 /** 23 /**
21 * 基于 S3 协议的文件客户端,实现 MinIO、阿里云、腾讯云、七牛云、华为云等云服务 24 * 基于 S3 协议的文件客户端,实现 MinIO、阿里云、腾讯云、七牛云、华为云等云服务
22 - * <p>  
23 - * S3 协议的客户端,采用亚马逊提供的 software.amazon.awssdk.s3 库  
24 * 25 *
25 * @author 芋道源码 26 * @author 芋道源码
26 */ 27 */
27 public class S3FileClient extends AbstractFileClient<S3FileClientConfig> { 28 public class S3FileClient extends AbstractFileClient<S3FileClientConfig> {
28 29
29 - private AmazonS3Client client; 30 + private S3Client client;
  31 + private S3Presigner presigner;
30 32
31 public S3FileClient(Long id, S3FileClientConfig config) { 33 public S3FileClient(Long id, S3FileClientConfig config) {
32 super(id, config); 34 super(id, config);
@@ -38,31 +40,80 @@ public class S3FileClient extends AbstractFileClient&lt;S3FileClientConfig&gt; { @@ -38,31 +40,80 @@ public class S3FileClient extends AbstractFileClient&lt;S3FileClientConfig&gt; {
38 if (StrUtil.isEmpty(config.getDomain())) { 40 if (StrUtil.isEmpty(config.getDomain())) {
39 config.setDomain(buildDomain()); 41 config.setDomain(buildDomain());
40 } 42 }
41 - // 初始化客户端  
42 - client = (AmazonS3Client)AmazonS3ClientBuilder.standard()  
43 - .withCredentials(buildCredentials())  
44 - .withEndpointConfiguration(buildEndpointConfiguration()) 43 + // 初始化 S3 客户端
  44 + Region region = Region.of("us-east-1"); // 必须填,但填什么都行,常见的值有 "us-east-1",不填会报错
  45 + AwsCredentialsProvider credentialsProvider = StaticCredentialsProvider.create(
  46 + AwsBasicCredentials.create(config.getAccessKey(), config.getAccessSecret()));
  47 + URI endpoint = URI.create(buildEndpoint());
  48 + S3Configuration serviceConfiguration = S3Configuration.builder() // Path-style 访问
  49 + .pathStyleAccessEnabled(Boolean.TRUE.equals(config.getEnablePathStyleAccess()))
  50 + .chunkedEncodingEnabled(false) // 禁用分块编码,参见 https://t.zsxq.com/kBy57
  51 + .build();
  52 + client = S3Client.builder()
  53 + .credentialsProvider(credentialsProvider)
  54 + .region(region)
  55 + .endpointOverride(endpoint)
  56 + .serviceConfiguration(serviceConfiguration)
  57 + .build();
  58 + presigner = S3Presigner.builder()
  59 + .credentialsProvider(credentialsProvider)
  60 + .region(region)
  61 + .endpointOverride(endpoint)
  62 + .serviceConfiguration(serviceConfiguration)
45 .build(); 63 .build();
46 } 64 }
47 65
48 - /**  
49 - * 基于 config 秘钥,构建 S3 客户端的认证信息  
50 - *  
51 - * @return S3 客户端的认证信息  
52 - */  
53 - private AWSStaticCredentialsProvider buildCredentials() {  
54 - return new AWSStaticCredentialsProvider(  
55 - new BasicAWSCredentials(config.getAccessKey(), config.getAccessSecret())); 66 + @Override
  67 + public String upload(byte[] content, String path, String type) {
  68 + // 构造 PutObjectRequest
  69 + PutObjectRequest putRequest = PutObjectRequest.builder()
  70 + .bucket(config.getBucket())
  71 + .key(path)
  72 + .contentType(type)
  73 + .contentLength((long) content.length)
  74 + .build();
  75 + // 上传文件
  76 + client.putObject(putRequest, RequestBody.fromBytes(content));
  77 + // 拼接返回路径
  78 + return config.getDomain() + "/" + path;
  79 + }
  80 +
  81 + @Override
  82 + public void delete(String path) {
  83 + DeleteObjectRequest deleteRequest = DeleteObjectRequest.builder()
  84 + .bucket(config.getBucket())
  85 + .key(path)
  86 + .build();
  87 + client.deleteObject(deleteRequest);
  88 + }
  89 +
  90 + @Override
  91 + public byte[] getContent(String path) {
  92 + GetObjectRequest getRequest = GetObjectRequest.builder()
  93 + .bucket(config.getBucket())
  94 + .key(path)
  95 + .build();
  96 + return IoUtil.readBytes(client.getObject(getRequest));
  97 + }
  98 +
  99 + @Override
  100 + public FilePresignedUrlRespDTO getPresignedObjectUrl(String path) {
  101 + Duration expiration = Duration.ofHours(24);
  102 + return new FilePresignedUrlRespDTO(getPresignedUrl(path, expiration), config.getDomain() + "/" + path);
56 } 103 }
57 104
58 /** 105 /**
59 - * 构建 S3 客户端的 Endpoint 配置,包括 region、endpoint 106 + * 生成动态的预签名上传 URL
60 * 107 *
61 - * @return S3 客户端的 EndpointConfiguration 配置 108 + * @param path 相对路径
  109 + * @param expiration 过期时间
  110 + * @return 生成的上传 URL
62 */ 111 */
63 - private AwsClientBuilder.EndpointConfiguration buildEndpointConfiguration() {  
64 - return new AwsClientBuilder.EndpointConfiguration(config.getEndpoint(),  
65 - null); // 无需设置 region 112 + private String getPresignedUrl(String path, Duration expiration) {
  113 + return presigner.presignPutObject(PutObjectPresignRequest.builder()
  114 + .signatureDuration(expiration)
  115 + .putObjectRequest(b -> b.bucket(config.getBucket()).key(path))
  116 + .build()).url().toString();
66 } 117 }
67 118
68 /** 119 /**
@@ -79,40 +130,17 @@ public class S3FileClient extends AbstractFileClient&lt;S3FileClientConfig&gt; { @@ -79,40 +130,17 @@ public class S3FileClient extends AbstractFileClient&lt;S3FileClientConfig&gt; {
79 return StrUtil.format("https://{}.{}", config.getBucket(), config.getEndpoint()); 130 return StrUtil.format("https://{}.{}", config.getBucket(), config.getEndpoint());
80 } 131 }
81 132
82 - @Override  
83 - public String upload(byte[] content, String path, String type) throws Exception {  
84 - // 元数据,主要用于设置文件类型  
85 - ObjectMetadata objectMetadata = new ObjectMetadata();  
86 - objectMetadata.setContentType(type);  
87 - objectMetadata.setContentLength(content.length); // 如果不设置,会有 “ No content length specified for stream data” 警告日志  
88 - // 执行上传  
89 - client.putObject(config.getBucket(),  
90 - path, // 相对路径  
91 - new ByteArrayInputStream(content), // 文件内容  
92 - objectMetadata);  
93 -  
94 - // 拼接返回路径  
95 - return config.getDomain() + "/" + path;  
96 - }  
97 -  
98 - @Override  
99 - public void delete(String path) throws Exception {  
100 - client.deleteObject(config.getBucket(), path);  
101 - }  
102 -  
103 - @Override  
104 - public byte[] getContent(String path) throws Exception {  
105 - S3Object tempS3Object = client.getObject(config.getBucket(), path);  
106 - return IoUtil.readBytes(tempS3Object.getObjectContent());  
107 - }  
108 -  
109 - @Override  
110 - public FilePresignedUrlRespDTO getPresignedObjectUrl(String path) throws Exception {  
111 - // 设定过期时间为 10 分钟。取值范围:1 秒 ~ 7 天  
112 - Date expiration = new Date(System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(10));  
113 - // 生成上传 URL  
114 - String uploadUrl = String.valueOf(client.generatePresignedUrl(config.getBucket(), path, expiration , HttpMethod.PUT));  
115 - return new FilePresignedUrlRespDTO(uploadUrl, config.getDomain() + "/" + path); 133 + /**
  134 + * 节点地址补全协议头
  135 + *
  136 + * @return 节点地址
  137 + */
  138 + private String buildEndpoint() {
  139 + // 如果已经是 http 或者 https,则不进行拼接
  140 + if (HttpUtil.isHttp(config.getEndpoint()) || HttpUtil.isHttps(config.getEndpoint())) {
  141 + return config.getEndpoint();
  142 + }
  143 + return StrUtil.format("https://{}", config.getEndpoint());
116 } 144 }
117 145
118 } 146 }
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClientConfig.java
@@ -67,6 +67,12 @@ public class S3FileClientConfig implements FileClientConfig { @@ -67,6 +67,12 @@ public class S3FileClientConfig implements FileClientConfig {
67 @NotNull(message = "accessSecret 不能为空") 67 @NotNull(message = "accessSecret 不能为空")
68 private String accessSecret; 68 private String accessSecret;
69 69
  70 + /**
  71 + * 是否启用 PathStyle 访问
  72 + */
  73 + @NotNull(message = "enablePathStyleAccess 不能为空")
  74 + private Boolean enablePathStyleAccess;
  75 +
70 @SuppressWarnings("RedundantIfStatement") 76 @SuppressWarnings("RedundantIfStatement")
71 @AssertTrue(message = "domain 不能为空") 77 @AssertTrue(message = "domain 不能为空")
72 @JsonIgnore 78 @JsonIgnore
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java
@@ -164,23 +164,23 @@ public class CodegenEngine { @@ -164,23 +164,23 @@ public class CodegenEngine {
164 .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/modules/list_sub_erp.vue"), // 特殊:主子表专属逻辑 164 .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/modules/list_sub_erp.vue"), // 特殊:主子表专属逻辑
165 vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-list.vue")) 165 vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-list.vue"))
166 // VUE3_VBEN5_ANTD 166 // VUE3_VBEN5_ANTD
167 - .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD.getType(), vue3Vben5AntdGeneralTemplatePath("views/data.ts"), 167 + .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("views/data.ts"),
168 vue3FilePath("views/${table.moduleName}/${table.businessName}/data.ts")) 168 vue3FilePath("views/${table.moduleName}/${table.businessName}/data.ts"))
169 - .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD.getType(), vue3Vben5AntdGeneralTemplatePath("views/index.vue"), 169 + .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("views/index.vue"),
170 vue3FilePath("views/${table.moduleName}/${table.businessName}/index.vue")) 170 vue3FilePath("views/${table.moduleName}/${table.businessName}/index.vue"))
171 - .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD.getType(), vue3Vben5AntdGeneralTemplatePath("views/form.vue"), 171 + .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("views/form.vue"),
172 vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/form.vue")) 172 vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/form.vue"))
173 - .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD.getType(), vue3Vben5AntdGeneralTemplatePath("api/api.ts"), 173 + .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("api/api.ts"),
174 vue3FilePath("api/${table.moduleName}/${table.businessName}/index.ts")) 174 vue3FilePath("api/${table.moduleName}/${table.businessName}/index.ts"))
175 - .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD.getType(), vue3Vben5AntdGeneralTemplatePath("views/modules/form_sub_normal.vue"), // 特殊:主子表专属逻辑 175 + .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("views/modules/form_sub_normal.vue"), // 特殊:主子表专属逻辑
176 vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-form.vue")) 176 vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-form.vue"))
177 - .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD.getType(), vue3Vben5AntdGeneralTemplatePath("views/modules/form_sub_inner.vue"), // 特殊:主子表专属逻辑 177 + .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("views/modules/form_sub_inner.vue"), // 特殊:主子表专属逻辑
178 vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-form.vue")) 178 vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-form.vue"))
179 - .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD.getType(), vue3Vben5AntdGeneralTemplatePath("views/modules/form_sub_erp.vue"), // 特殊:主子表专属逻辑 179 + .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("views/modules/form_sub_erp.vue"), // 特殊:主子表专属逻辑
180 vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-form.vue")) 180 vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-form.vue"))
181 - .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD.getType(), vue3Vben5AntdGeneralTemplatePath("views/modules/list_sub_inner.vue"), // 特殊:主子表专属逻辑 181 + .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("views/modules/list_sub_inner.vue"), // 特殊:主子表专属逻辑
182 vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-list.vue")) 182 vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-list.vue"))
183 - .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD.getType(), vue3Vben5AntdGeneralTemplatePath("views/modules/list_sub_erp.vue"), // 特殊:主子表专属逻辑 183 + .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("views/modules/list_sub_erp.vue"), // 特殊:主子表专属逻辑
184 vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-list.vue")) 184 vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-list.vue"))
185 .build(); 185 .build();
186 186
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApi.java
@@ -5,6 +5,9 @@ import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO; @@ -5,6 +5,9 @@ import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;
5 5
6 import java.util.Collection; 6 import java.util.Collection;
7 import java.util.List; 7 import java.util.List;
  8 +import java.util.Map;
  9 +
  10 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
8 11
9 /** 12 /**
10 * 商品 SKU API 接口 13 * 商品 SKU API 接口
@@ -31,6 +34,16 @@ public interface ProductSkuApi { @@ -31,6 +34,16 @@ public interface ProductSkuApi {
31 List<ProductSkuRespDTO> getSkuList(Collection<Long> ids); 34 List<ProductSkuRespDTO> getSkuList(Collection<Long> ids);
32 35
33 /** 36 /**
  37 + * 批量查询 SKU MAP
  38 + *
  39 + * @param ids SKU 编号列表
  40 + * @return SKU MAP
  41 + */
  42 + default Map<Long, ProductSkuRespDTO> getSkuMap(Collection<Long> ids) {
  43 + return convertMap(getSkuList(ids), ProductSkuRespDTO::getId);
  44 + }
  45 +
  46 + /**
34 * 批量查询 SKU 数组 47 * 批量查询 SKU 数组
35 * 48 *
36 * @param spuIds SPU 编号列表 49 * @param spuIds SPU 编号列表
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/ProductSpuApi.java
@@ -30,7 +30,7 @@ public interface ProductSpuApi { @@ -30,7 +30,7 @@ public interface ProductSpuApi {
30 * @param ids SPU 编号列表 30 * @param ids SPU 编号列表
31 * @return SPU MAP 31 * @return SPU MAP
32 */ 32 */
33 - default Map<Long, ProductSpuRespDTO> getSpusMap(Collection<Long> ids) { 33 + default Map<Long, ProductSpuRespDTO> getSpuMap(Collection<Long> ids) {
34 return convertMap(getSpuList(ids), ProductSpuRespDTO::getId); 34 return convertMap(getSpuList(ids), ProductSpuRespDTO::getId);
35 } 35 }
36 36
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/point/PointActivityController.java
@@ -124,7 +124,7 @@ public class PointActivityController { @@ -124,7 +124,7 @@ public class PointActivityController {
124 List<PointProductDO> products = pointActivityService.getPointProductListByActivityIds( 124 List<PointProductDO> products = pointActivityService.getPointProductListByActivityIds(
125 convertSet(activityList, PointActivityDO::getId)); 125 convertSet(activityList, PointActivityDO::getId));
126 Map<Long, List<PointProductDO>> productsMap = convertMultiMap(products, PointProductDO::getActivityId); 126 Map<Long, List<PointProductDO>> productsMap = convertMultiMap(products, PointProductDO::getActivityId);
127 - Map<Long, ProductSpuRespDTO> spuMap = productSpuApi.getSpusMap( 127 + Map<Long, ProductSpuRespDTO> spuMap = productSpuApi.getSpuMap(
128 convertSet(activityList, PointActivityDO::getSpuId)); 128 convertSet(activityList, PointActivityDO::getSpuId));
129 List<PointActivityRespVO> result = BeanUtils.toBean(activityList, PointActivityRespVO.class); 129 List<PointActivityRespVO> result = BeanUtils.toBean(activityList, PointActivityRespVO.class);
130 result.forEach(activity -> { 130 result.forEach(activity -> {
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/point/AppPointActivityController.java
@@ -104,7 +104,7 @@ public class AppPointActivityController { @@ -104,7 +104,7 @@ public class AppPointActivityController {
104 List<PointProductDO> products = pointActivityService.getPointProductListByActivityIds( 104 List<PointProductDO> products = pointActivityService.getPointProductListByActivityIds(
105 convertSet(activityList, PointActivityDO::getId)); 105 convertSet(activityList, PointActivityDO::getId));
106 Map<Long, List<PointProductDO>> productsMap = convertMultiMap(products, PointProductDO::getActivityId); 106 Map<Long, List<PointProductDO>> productsMap = convertMultiMap(products, PointProductDO::getActivityId);
107 - Map<Long, ProductSpuRespDTO> spuMap = productSpuApi.getSpusMap( 107 + Map<Long, ProductSpuRespDTO> spuMap = productSpuApi.getSpuMap(
108 convertSet(activityList, PointActivityDO::getSpuId)); 108 convertSet(activityList, PointActivityDO::getSpuId));
109 List<AppPointActivityRespVO> result = BeanUtils.toBean(activityList, AppPointActivityRespVO.class); 109 List<AppPointActivityRespVO> result = BeanUtils.toBean(activityList, AppPointActivityRespVO.class);
110 result.forEach(activity -> { 110 result.forEach(activity -> {
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java
@@ -22,6 +22,7 @@ import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTemplateValidityType @@ -22,6 +22,7 @@ import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTemplateValidityType
22 import jakarta.annotation.Resource; 22 import jakarta.annotation.Resource;
23 import lombok.extern.slf4j.Slf4j; 23 import lombok.extern.slf4j.Slf4j;
24 import org.springframework.stereotype.Service; 24 import org.springframework.stereotype.Service;
  25 +import org.springframework.transaction.annotation.Propagation;
25 import org.springframework.transaction.annotation.Transactional; 26 import org.springframework.transaction.annotation.Transactional;
26 import org.springframework.validation.annotation.Validated; 27 import org.springframework.validation.annotation.Validated;
27 28
@@ -180,7 +181,7 @@ public class CouponServiceImpl implements CouponService { @@ -180,7 +181,7 @@ public class CouponServiceImpl implements CouponService {
180 * @param couponId 模版编号 181 * @param couponId 模版编号
181 * @param userId 用户编号 182 * @param userId 用户编号
182 */ 183 */
183 - @Transactional(rollbackFor = Exception.class) 184 + @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW) // 每次调用开启一个新的事务,避免在一个大的事务里面
184 public void invalidateCoupon(Long couponId, Long userId) { 185 public void invalidateCoupon(Long couponId, Long userId) {
185 if (couponId == null || couponId <= 0) { 186 if (couponId == null || couponId <= 0) {
186 return; 187 return;
@@ -270,13 +271,17 @@ public class CouponServiceImpl implements CouponService { @@ -270,13 +271,17 @@ public class CouponServiceImpl implements CouponService {
270 if (CollUtil.isEmpty(userIds)) { 271 if (CollUtil.isEmpty(userIds)) {
271 throw exception(COUPON_TEMPLATE_USER_ALREADY_TAKE); 272 throw exception(COUPON_TEMPLATE_USER_ALREADY_TAKE);
272 } 273 }
273 -  
274 // 校验模板 274 // 校验模板
275 if (couponTemplate == null) { 275 if (couponTemplate == null) {
276 throw exception(COUPON_TEMPLATE_NOT_EXISTS); 276 throw exception(COUPON_TEMPLATE_NOT_EXISTS);
277 } 277 }
278 - // 校验剩余数量  
279 - if (ObjUtil.notEqual(couponTemplate.getTakeLimitCount(), CouponTemplateDO.TIME_LIMIT_COUNT_MAX) // 非不限制 278 + // 校验领取方式
  279 + if (ObjUtil.notEqual(couponTemplate.getTakeType(), takeType.getType())) {
  280 + throw exception(COUPON_TEMPLATE_CANNOT_TAKE);
  281 + }
  282 + // 校验发放数量不能过小(仅在 CouponTakeTypeEnum.USER 用户领取时)
  283 + if (CouponTakeTypeEnum.isUser(couponTemplate.getTakeType())
  284 + && ObjUtil.notEqual(couponTemplate.getTakeLimitCount(), CouponTemplateDO.TIME_LIMIT_COUNT_MAX) // 非不限制
280 && couponTemplate.getTakeCount() + userIds.size() > couponTemplate.getTotalCount()) { 285 && couponTemplate.getTakeCount() + userIds.size() > couponTemplate.getTotalCount()) {
281 throw exception(COUPON_TEMPLATE_NOT_ENOUGH); 286 throw exception(COUPON_TEMPLATE_NOT_ENOUGH);
282 } 287 }
@@ -286,10 +291,6 @@ public class CouponServiceImpl implements CouponService { @@ -286,10 +291,6 @@ public class CouponServiceImpl implements CouponService {
286 throw exception(COUPON_TEMPLATE_EXPIRED); 291 throw exception(COUPON_TEMPLATE_EXPIRED);
287 } 292 }
288 } 293 }
289 - // 校验领取方式  
290 - if (ObjectUtil.notEqual(couponTemplate.getTakeType(), takeType.getType())) {  
291 - throw exception(COUPON_TEMPLATE_CANNOT_TAKE);  
292 - }  
293 } 294 }
294 295
295 /** 296 /**
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/aftersale/AppAfterSaleController.java
1 package cn.iocoder.yudao.module.trade.controller.app.aftersale; 1 package cn.iocoder.yudao.module.trade.controller.app.aftersale;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 -import cn.iocoder.yudao.framework.common.pojo.PageParam;  
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6 import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleCreateReqVO; 6 import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleCreateReqVO;
7 import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleDeliveryReqVO; 7 import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleDeliveryReqVO;
  8 +import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSalePageReqVO;
8 import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleRespVO; 9 import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleRespVO;
9 -import cn.iocoder.yudao.module.trade.convert.aftersale.AfterSaleConvert; 10 +import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO;
10 import cn.iocoder.yudao.module.trade.service.aftersale.AfterSaleService; 11 import cn.iocoder.yudao.module.trade.service.aftersale.AfterSaleService;
11 import io.swagger.v3.oas.annotations.Operation; 12 import io.swagger.v3.oas.annotations.Operation;
12 import io.swagger.v3.oas.annotations.Parameter; 13 import io.swagger.v3.oas.annotations.Parameter;
@@ -31,16 +32,17 @@ public class AppAfterSaleController { @@ -31,16 +32,17 @@ public class AppAfterSaleController {
31 32
32 @GetMapping(value = "/page") 33 @GetMapping(value = "/page")
33 @Operation(summary = "获得售后分页") 34 @Operation(summary = "获得售后分页")
34 - public CommonResult<PageResult<AppAfterSaleRespVO>> getAfterSalePage(PageParam pageParam) {  
35 - return success(AfterSaleConvert.INSTANCE.convertPage02(  
36 - afterSaleService.getAfterSalePage(getLoginUserId(), pageParam))); 35 + public CommonResult<PageResult<AppAfterSaleRespVO>> getAfterSalePage(AppAfterSalePageReqVO pageReqVO) {
  36 + PageResult<AfterSaleDO> pageResult = afterSaleService.getAfterSalePage(getLoginUserId(), pageReqVO);
  37 + return success(BeanUtils.toBean(pageResult, AppAfterSaleRespVO.class));
37 } 38 }
38 39
39 @GetMapping(value = "/get") 40 @GetMapping(value = "/get")
40 @Operation(summary = "获得售后订单") 41 @Operation(summary = "获得售后订单")
41 @Parameter(name = "id", description = "售后编号", required = true, example = "1") 42 @Parameter(name = "id", description = "售后编号", required = true, example = "1")
42 public CommonResult<AppAfterSaleRespVO> getAfterSale(@RequestParam("id") Long id) { 43 public CommonResult<AppAfterSaleRespVO> getAfterSale(@RequestParam("id") Long id) {
43 - return success(AfterSaleConvert.INSTANCE.convert(afterSaleService.getAfterSale(getLoginUserId(), id))); 44 + AfterSaleDO afterSale = afterSaleService.getAfterSale(getLoginUserId(), id);
  45 + return success(BeanUtils.toBean(afterSale, AppAfterSaleRespVO.class));
44 } 46 }
45 47
46 @PostMapping(value = "/create") 48 @PostMapping(value = "/create")
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/aftersale/vo/AppAfterSalePageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.controller.app.aftersale.vo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +
  9 +import java.util.Set;
  10 +
  11 +@Schema(description = "用户 App - 交易售后分页 Request VO")
  12 +@Data
  13 +@EqualsAndHashCode(callSuper = true)
  14 +@ToString(callSuper = true)
  15 +public class AppAfterSalePageReqVO extends PageParam {
  16 +
  17 + @Schema(description = "售后状态", example = "10, 20")
  18 + private Set<Integer> statuses;
  19 +
  20 +}
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/base/spu/AppProductSpuBaseRespVO.java
@@ -3,8 +3,6 @@ package cn.iocoder.yudao.module.trade.controller.app.base.spu; @@ -3,8 +3,6 @@ package cn.iocoder.yudao.module.trade.controller.app.base.spu;
3 import io.swagger.v3.oas.annotations.media.Schema; 3 import io.swagger.v3.oas.annotations.media.Schema;
4 import lombok.Data; 4 import lombok.Data;
5 5
6 -import java.util.List;  
7 -  
8 /** 6 /**
9 * 商品 SPU 基础 Response VO 7 * 商品 SPU 基础 Response VO
10 * 8 *
@@ -25,4 +23,10 @@ public class AppProductSpuBaseRespVO { @@ -25,4 +23,10 @@ public class AppProductSpuBaseRespVO {
25 @Schema(description = "商品分类编号", example = "1") 23 @Schema(description = "商品分类编号", example = "1")
26 private Long categoryId; 24 private Long categoryId;
27 25
  26 + @Schema(description = "商品库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000")
  27 + private Integer stock;
  28 +
  29 + @Schema(description = "商品状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  30 + private Integer status;
  31 +
28 } 32 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/delivery/vo/pickup/AppDeliveryPickUpStoreRespVO.java
1 package cn.iocoder.yudao.module.trade.controller.app.delivery.vo.pickup; 1 package cn.iocoder.yudao.module.trade.controller.app.delivery.vo.pickup;
2 2
  3 +import com.fasterxml.jackson.annotation.JsonFormat;
3 import io.swagger.v3.oas.annotations.media.Schema; 4 import io.swagger.v3.oas.annotations.media.Schema;
  5 +import jakarta.validation.constraints.NotNull;
4 import lombok.Data; 6 import lombok.Data;
5 7
  8 +import java.time.LocalTime;
  9 +
6 @Schema(description = "用户 App - 自提门店 Response VO") 10 @Schema(description = "用户 App - 自提门店 Response VO")
7 @Data 11 @Data
8 public class AppDeliveryPickUpStoreRespVO { 12 public class AppDeliveryPickUpStoreRespVO {
@@ -28,6 +32,16 @@ public class AppDeliveryPickUpStoreRespVO { @@ -28,6 +32,16 @@ public class AppDeliveryPickUpStoreRespVO {
28 @Schema(description = "门店详细地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "复旦大学路 188 号") 32 @Schema(description = "门店详细地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "复旦大学路 188 号")
29 private String detailAddress; 33 private String detailAddress;
30 34
  35 + @Schema(description = "营业开始时间", requiredMode = Schema.RequiredMode.REQUIRED)
  36 + @NotNull(message = "营业开始时间不能为空")
  37 + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "HH:mm")
  38 + private LocalTime openingTime;
  39 +
  40 + @Schema(description = "营业结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
  41 + @NotNull(message = "营业结束时间不能为空")
  42 + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "HH:mm")
  43 + private LocalTime closingTime;
  44 +
31 @Schema(description = "纬度", requiredMode = Schema.RequiredMode.REQUIRED, example = "5.88") 45 @Schema(description = "纬度", requiredMode = Schema.RequiredMode.REQUIRED, example = "5.88")
32 private Double latitude; 46 private Double latitude;
33 47
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/aftersale/AfterSaleConvert.java
@@ -11,7 +11,6 @@ import cn.iocoder.yudao.module.trade.controller.admin.base.member.user.MemberUse @@ -11,7 +11,6 @@ import cn.iocoder.yudao.module.trade.controller.admin.base.member.user.MemberUse
11 import cn.iocoder.yudao.module.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO; 11 import cn.iocoder.yudao.module.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO;
12 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderBaseVO; 12 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderBaseVO;
13 import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleCreateReqVO; 13 import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleCreateReqVO;
14 -import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleRespVO;  
15 import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO; 14 import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO;
16 import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleLogDO; 15 import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleLogDO;
17 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; 16 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
@@ -63,10 +62,6 @@ public interface AfterSaleConvert { @@ -63,10 +62,6 @@ public interface AfterSaleConvert {
63 62
64 ProductPropertyValueDetailRespVO convert(ProductPropertyValueDetailRespDTO bean); 63 ProductPropertyValueDetailRespVO convert(ProductPropertyValueDetailRespDTO bean);
65 64
66 - AppAfterSaleRespVO convert(AfterSaleDO bean);  
67 -  
68 - PageResult<AppAfterSaleRespVO> convertPage02(PageResult<AfterSaleDO> page);  
69 -  
70 default AfterSaleDetailRespVO convert(AfterSaleDO afterSale, TradeOrderDO order, TradeOrderItemDO orderItem, 65 default AfterSaleDetailRespVO convert(AfterSaleDO afterSale, TradeOrderDO order, TradeOrderItemDO orderItem,
71 MemberUserRespDTO user, List<AfterSaleLogDO> logs) { 66 MemberUserRespDTO user, List<AfterSaleLogDO> logs) {
72 AfterSaleDetailRespVO respVO = convert02(afterSale); 67 AfterSaleDetailRespVO respVO = convert02(afterSale);
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/cart/TradeCartConvert.java
1 package cn.iocoder.yudao.module.trade.convert.cart; 1 package cn.iocoder.yudao.module.trade.convert.cart;
2 2
  3 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
3 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; 4 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
4 import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; 5 import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
5 import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum; 6 import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
@@ -33,21 +34,18 @@ public interface TradeCartConvert { @@ -33,21 +34,18 @@ public interface TradeCartConvert {
33 cartVO.setId(cart.getId()).setCount(cart.getCount()).setSelected(cart.getSelected()); 34 cartVO.setId(cart.getId()).setCount(cart.getCount()).setSelected(cart.getSelected());
34 ProductSpuRespDTO spu = spuMap.get(cart.getSpuId()); 35 ProductSpuRespDTO spu = spuMap.get(cart.getSpuId());
35 ProductSkuRespDTO sku = skuMap.get(cart.getSkuId()); 36 ProductSkuRespDTO sku = skuMap.get(cart.getSkuId());
36 - cartVO.setSpu(convert(spu)).setSku(convert(sku)); 37 + cartVO.setSpu(BeanUtils.toBean(spu, AppProductSpuBaseRespVO.class))
  38 + .setSku(BeanUtils.toBean(sku, AppProductSkuBaseRespVO.class));
37 // 如果 SPU 不存在,或者下架,或者库存不足,说明是无效的 39 // 如果 SPU 不存在,或者下架,或者库存不足,说明是无效的
38 if (spu == null 40 if (spu == null
39 || !ProductSpuStatusEnum.isEnable(spu.getStatus()) 41 || !ProductSpuStatusEnum.isEnable(spu.getStatus())
40 || spu.getStock() <= 0) { 42 || spu.getStock() <= 0) {
41 - cartVO.setSelected(false); // 强制设置成不可选中  
42 invalidList.add(cartVO); 43 invalidList.add(cartVO);
43 } else { 44 } else {
44 - // 虽然 SKU 可能也会不存在,但是可以通过购物车重新选择  
45 validList.add(cartVO); 45 validList.add(cartVO);
46 } 46 }
47 }); 47 });
48 return new AppCartListRespVO().setValidList(validList).setInvalidList(invalidList); 48 return new AppCartListRespVO().setValidList(validList).setInvalidList(invalidList);
49 } 49 }
50 - AppProductSpuBaseRespVO convert(ProductSpuRespDTO spu);  
51 - AppProductSkuBaseRespVO convert(ProductSkuRespDTO sku);  
52 50
53 } 51 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/aftersale/AfterSaleMapper.java
1 package cn.iocoder.yudao.module.trade.dal.mysql.aftersale; 1 package cn.iocoder.yudao.module.trade.dal.mysql.aftersale;
2 2
3 -import cn.iocoder.yudao.framework.common.pojo.PageParam;  
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
7 import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSalePageReqVO; 6 import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSalePageReqVO;
  7 +import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSalePageReqVO;
8 import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO; 8 import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO;
9 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 9 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
10 import org.apache.ibatis.annotations.Mapper; 10 import org.apache.ibatis.annotations.Mapper;
@@ -27,9 +27,10 @@ public interface AfterSaleMapper extends BaseMapperX&lt;AfterSaleDO&gt; { @@ -27,9 +27,10 @@ public interface AfterSaleMapper extends BaseMapperX&lt;AfterSaleDO&gt; {
27 .orderByDesc(AfterSaleDO::getId)); 27 .orderByDesc(AfterSaleDO::getId));
28 } 28 }
29 29
30 - default PageResult<AfterSaleDO> selectPage(Long userId, PageParam pageParam) {  
31 - return selectPage(pageParam, new LambdaQueryWrapperX<AfterSaleDO>()  
32 - .eqIfPresent(AfterSaleDO::getUserId, userId) 30 + default PageResult<AfterSaleDO> selectPage(Long userId, AppAfterSalePageReqVO pageReqVO) {
  31 + return selectPage(pageReqVO, new LambdaQueryWrapperX<AfterSaleDO>()
  32 + .eq(AfterSaleDO::getUserId, userId)
  33 + .inIfPresent(AfterSaleDO::getStatus, pageReqVO.getStatuses())
33 .orderByDesc(AfterSaleDO::getId)); 34 .orderByDesc(AfterSaleDO::getId));
34 } 35 }
35 36
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/AfterSaleService.java
1 package cn.iocoder.yudao.module.trade.service.aftersale; 1 package cn.iocoder.yudao.module.trade.service.aftersale;
2 2
3 -import cn.iocoder.yudao.framework.common.pojo.PageParam;  
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSaleDisagreeReqVO; 4 import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSaleDisagreeReqVO;
6 import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSalePageReqVO; 5 import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSalePageReqVO;
7 import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSaleRefuseReqVO; 6 import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSaleRefuseReqVO;
8 import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleCreateReqVO; 7 import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleCreateReqVO;
9 import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleDeliveryReqVO; 8 import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleDeliveryReqVO;
  9 +import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSalePageReqVO;
10 import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO; 10 import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO;
11 11
12 /** 12 /**
@@ -28,10 +28,10 @@ public interface AfterSaleService { @@ -28,10 +28,10 @@ public interface AfterSaleService {
28 * 【会员】获得售后订单分页 28 * 【会员】获得售后订单分页
29 * 29 *
30 * @param userId 用户编号 30 * @param userId 用户编号
31 - * @param pageParam 分页参数 31 + * @param pageReqVO 分页参数
32 * @return 售后订单分页 32 * @return 售后订单分页
33 */ 33 */
34 - PageResult<AfterSaleDO> getAfterSalePage(Long userId, PageParam pageParam); 34 + PageResult<AfterSaleDO> getAfterSalePage(Long userId, AppAfterSalePageReqVO pageReqVO);
35 35
36 /** 36 /**
37 * 【会员】获得售后单 37 * 【会员】获得售后单
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/AfterSaleServiceImpl.java
@@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.trade.service.aftersale; @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.trade.service.aftersale;
3 import cn.hutool.core.map.MapUtil; 3 import cn.hutool.core.map.MapUtil;
4 import cn.hutool.core.util.ObjectUtil; 4 import cn.hutool.core.util.ObjectUtil;
5 import cn.hutool.core.util.StrUtil; 5 import cn.hutool.core.util.StrUtil;
6 -import cn.iocoder.yudao.framework.common.pojo.PageParam;  
7 import cn.iocoder.yudao.framework.common.pojo.PageResult; 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
8 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; 7 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
9 import cn.iocoder.yudao.module.pay.api.refund.PayRefundApi; 8 import cn.iocoder.yudao.module.pay.api.refund.PayRefundApi;
@@ -16,6 +15,7 @@ import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSalePage @@ -16,6 +15,7 @@ import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSalePage
16 import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSaleRefuseReqVO; 15 import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSaleRefuseReqVO;
17 import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleCreateReqVO; 16 import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleCreateReqVO;
18 import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleDeliveryReqVO; 17 import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleDeliveryReqVO;
  18 +import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSalePageReqVO;
19 import cn.iocoder.yudao.module.trade.convert.aftersale.AfterSaleConvert; 19 import cn.iocoder.yudao.module.trade.convert.aftersale.AfterSaleConvert;
20 import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO; 20 import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO;
21 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO; 21 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;
@@ -36,6 +36,7 @@ import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties @@ -36,6 +36,7 @@ import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties
36 import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService; 36 import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService;
37 import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService; 37 import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService;
38 import cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService; 38 import cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService;
  39 +import jakarta.annotation.Resource;
39 import lombok.extern.slf4j.Slf4j; 40 import lombok.extern.slf4j.Slf4j;
40 import org.springframework.context.annotation.Lazy; 41 import org.springframework.context.annotation.Lazy;
41 import org.springframework.stereotype.Service; 42 import org.springframework.stereotype.Service;
@@ -44,7 +45,6 @@ import org.springframework.transaction.support.TransactionSynchronization; @@ -44,7 +45,6 @@ import org.springframework.transaction.support.TransactionSynchronization;
44 import org.springframework.transaction.support.TransactionSynchronizationManager; 45 import org.springframework.transaction.support.TransactionSynchronizationManager;
45 import org.springframework.validation.annotation.Validated; 46 import org.springframework.validation.annotation.Validated;
46 47
47 -import jakarta.annotation.Resource;  
48 import java.time.LocalDateTime; 48 import java.time.LocalDateTime;
49 49
50 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 50 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -87,8 +87,8 @@ public class AfterSaleServiceImpl implements AfterSaleService { @@ -87,8 +87,8 @@ public class AfterSaleServiceImpl implements AfterSaleService {
87 } 87 }
88 88
89 @Override 89 @Override
90 - public PageResult<AfterSaleDO> getAfterSalePage(Long userId, PageParam pageParam) {  
91 - return tradeAfterSaleMapper.selectPage(userId, pageParam); 90 + public PageResult<AfterSaleDO> getAfterSalePage(Long userId, AppAfterSalePageReqVO pageReqVO) {
  91 + return tradeAfterSaleMapper.selectPage(userId, pageReqVO);
92 } 92 }
93 93
94 @Override 94 @Override
@@ -386,7 +386,7 @@ public class AfterSaleServiceImpl implements AfterSaleService { @@ -386,7 +386,7 @@ public class AfterSaleServiceImpl implements AfterSaleService {
386 public void afterCommit() { 386 public void afterCommit() {
387 // 创建退款单 387 // 创建退款单
388 PayRefundCreateReqDTO createReqDTO = AfterSaleConvert.INSTANCE.convert(userIp, afterSale, tradeOrderProperties) 388 PayRefundCreateReqDTO createReqDTO = AfterSaleConvert.INSTANCE.convert(userIp, afterSale, tradeOrderProperties)
389 - .setReason(StrUtil.format("退款【{}】", afterSale.getSpuName()));; 389 + .setReason(StrUtil.format("退款【{}】", afterSale.getSpuName()));
390 Long payRefundId = payRefundApi.createRefund(createReqDTO); 390 Long payRefundId = payRefundApi.createRefund(createReqDTO);
391 // 更新售后单的退款单号 391 // 更新售后单的退款单号
392 tradeAfterSaleMapper.updateById(new AfterSaleDO().setId(afterSale.getId()).setPayRefundId(payRefundId)); 392 tradeAfterSaleMapper.updateById(new AfterSaleDO().setId(afterSale.getId()).setPayRefundId(payRefundId));
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java
@@ -545,6 +545,14 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -545,6 +545,14 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
545 if (ObjectUtil.notEqual(order.getStatus(), TradeOrderStatusEnum.UNPAID.getStatus())) { 545 if (ObjectUtil.notEqual(order.getStatus(), TradeOrderStatusEnum.UNPAID.getStatus())) {
546 throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID); 546 throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID);
547 } 547 }
  548 + // 1.3 校验是否支持延迟(不允许取消)
  549 + if (TradeOrderStatusEnum.isUnpaid(order.getStatus())) {
  550 + PayOrderRespDTO payOrder = payOrderApi.getOrder(order.getPayOrderId());
  551 + if (payOrder != null && PayOrderStatusEnum.isSuccess(payOrder.getStatus())) {
  552 + log.warn("[cancelOrderByMember][order({}) 支付单已支付(支付回调延迟),不支持取消]", order.getId());
  553 + throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID);
  554 + }
  555 + }
548 556
549 // 2. 取消订单 557 // 2. 取消订单
550 cancelOrder0(order, TradeOrderCancelTypeEnum.MEMBER_CANCEL); 558 cancelOrder0(order, TradeOrderCancelTypeEnum.MEMBER_CANCEL);
@@ -581,6 +589,15 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -581,6 +589,15 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
581 @Transactional(rollbackFor = Exception.class) 589 @Transactional(rollbackFor = Exception.class)
582 @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.SYSTEM_CANCEL) 590 @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.SYSTEM_CANCEL)
583 public void cancelOrderBySystem(TradeOrderDO order) { 591 public void cancelOrderBySystem(TradeOrderDO order) {
  592 + // 校验是否支持延迟(不允许取消)
  593 + if (TradeOrderStatusEnum.isUnpaid(order.getStatus())) {
  594 + PayOrderRespDTO payOrder = payOrderApi.getOrder(order.getPayOrderId());
  595 + if (payOrder != null && PayOrderStatusEnum.isSuccess(payOrder.getStatus())) {
  596 + log.warn("[cancelOrderBySystem][order({}) 支付单已支付(支付回调延迟),不支持取消]", order.getId());
  597 + return;
  598 + }
  599 + }
  600 +
584 cancelOrder0(order, TradeOrderCancelTypeEnum.PAY_TIMEOUT); 601 cancelOrder0(order, TradeOrderCancelTypeEnum.PAY_TIMEOUT);
585 } 602 }
586 603
@@ -895,12 +912,11 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -895,12 +912,11 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
895 if (order == null) { 912 if (order == null) {
896 throw exception(ORDER_NOT_FOUND); 913 throw exception(ORDER_NOT_FOUND);
897 } 914 }
898 -  
899 // 1.3 校验订单是否支付 915 // 1.3 校验订单是否支付
900 if (!order.getPayStatus()) { 916 if (!order.getPayStatus()) {
901 throw exception(ORDER_CANCEL_PAID_FAIL, "已支付"); 917 throw exception(ORDER_CANCEL_PAID_FAIL, "已支付");
902 } 918 }
903 - // 1.3 校验订单是否未退款 919 + // 1.4 校验订单是否未退款
904 if (ObjUtil.notEqual(TradeOrderRefundStatusEnum.NONE.getStatus(), order.getRefundStatus())) { 920 if (ObjUtil.notEqual(TradeOrderRefundStatusEnum.NONE.getStatus(), order.getRefundStatus())) {
905 throw exception(ORDER_CANCEL_PAID_FAIL, "未退款"); 921 throw exception(ORDER_CANCEL_PAID_FAIL, "未退款");
906 } 922 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeBrokerageOrderHandler.java
@@ -20,6 +20,7 @@ import org.springframework.stereotype.Component; @@ -20,6 +20,7 @@ import org.springframework.stereotype.Component;
20 20
21 import jakarta.annotation.Resource; 21 import jakarta.annotation.Resource;
22 import java.util.List; 22 import java.util.List;
  23 +import java.util.Map;
23 24
24 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; 25 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
25 26
@@ -101,13 +102,17 @@ public class TradeBrokerageOrderHandler implements TradeOrderHandler { @@ -101,13 +102,17 @@ public class TradeBrokerageOrderHandler implements TradeOrderHandler {
101 protected void addBrokerage(Long userId, List<TradeOrderItemDO> orderItems) { 102 protected void addBrokerage(Long userId, List<TradeOrderItemDO> orderItems) {
102 MemberUserRespDTO user = memberUserApi.getUser(userId); 103 MemberUserRespDTO user = memberUserApi.getUser(userId);
103 Assert.notNull(user); 104 Assert.notNull(user);
104 - ProductSpuRespDTO spu = productSpuApi.getSpu(orderItems.get(0).getSpuId());  
105 - Assert.notNull(spu);  
106 - ProductSkuRespDTO sku = productSkuApi.getSku(orderItems.get(0).getSkuId()); 105 + Map<Long, ProductSpuRespDTO> spusMap = productSpuApi.getSpuMap(convertList(orderItems, TradeOrderItemDO::getSpuId));
  106 + Map<Long, ProductSkuRespDTO> skusMap = productSkuApi.getSkuMap(convertList(orderItems, TradeOrderItemDO::getSkuId));
107 107
108 // 每一个订单项,都会去生成分销记录 108 // 每一个订单项,都会去生成分销记录
109 - List<BrokerageAddReqBO> addList = convertList(orderItems,  
110 - item -> TradeOrderConvert.INSTANCE.convert(user, item, spu, sku)); 109 + List<BrokerageAddReqBO> addList = convertList(orderItems, item -> {
  110 + ProductSpuRespDTO spu = spusMap.get(item.getSpuId());
  111 + Assert.notNull(spu);
  112 + ProductSkuRespDTO sku = skusMap.get(item.getSkuId());
  113 + Assert.notNull(sku);
  114 + return TradeOrderConvert.INSTANCE.convert(user, item, spu, sku);
  115 + });
111 brokerageRecordService.addBrokerage(userId, BrokerageRecordBizTypeEnum.ORDER, addList); 116 brokerageRecordService.addBrokerage(userId, BrokerageRecordBizTypeEnum.ORDER, addList);
112 } 117 }
113 118
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java
@@ -45,6 +45,7 @@ public interface ErrorCodeConstants { @@ -45,6 +45,7 @@ public interface ErrorCodeConstants {
45 ErrorCode USER_COUNT_MAX = new ErrorCode(1_002_003_008, "创建用户失败,原因:超过租户最大租户配额({})!"); 45 ErrorCode USER_COUNT_MAX = new ErrorCode(1_002_003_008, "创建用户失败,原因:超过租户最大租户配额({})!");
46 ErrorCode USER_IMPORT_INIT_PASSWORD = new ErrorCode(1_002_003_009, "初始密码不能为空"); 46 ErrorCode USER_IMPORT_INIT_PASSWORD = new ErrorCode(1_002_003_009, "初始密码不能为空");
47 ErrorCode USER_MOBILE_NOT_EXISTS = new ErrorCode(1_002_003_010, "该手机号尚未注册"); 47 ErrorCode USER_MOBILE_NOT_EXISTS = new ErrorCode(1_002_003_010, "该手机号尚未注册");
  48 + ErrorCode USER_REGISTER_DISABLED = new ErrorCode(1_002_003_011, "注册功能已关闭");
48 49
49 // ========== 部门模块 1-002-004-000 ========== 50 // ========== 部门模块 1-002-004-000 ==========
50 ErrorCode DEPT_NAME_DUPLICATE = new ErrorCode(1_002_004_000, "已经存在该名字的部门"); 51 ErrorCode DEPT_NAME_DUPLICATE = new ErrorCode(1_002_004_000, "已经存在该名字的部门");
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImpl.java
@@ -9,6 +9,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -9,6 +9,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
9 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 9 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
10 import cn.iocoder.yudao.framework.common.util.date.DateUtils; 10 import cn.iocoder.yudao.framework.common.util.date.DateUtils;
11 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 11 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  12 +import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
12 import cn.iocoder.yudao.framework.tenant.config.TenantProperties; 13 import cn.iocoder.yudao.framework.tenant.config.TenantProperties;
13 import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; 14 import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
14 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; 15 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
@@ -96,6 +97,7 @@ public class TenantServiceImpl implements TenantService { @@ -96,6 +97,7 @@ public class TenantServiceImpl implements TenantService {
96 97
97 @Override 98 @Override
98 @DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换 99 @DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换
  100 + @DataPermission(enable = false) // 参见 https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/1154 说明
99 public Long createTenant(TenantSaveReqVO createReqVO) { 101 public Long createTenant(TenantSaveReqVO createReqVO) {
100 // 校验租户名称是否重复 102 // 校验租户名称是否重复
101 validTenantNameDuplicate(createReqVO.getName(), null); 103 validTenantNameDuplicate(createReqVO.getName(), null);
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.system.service.user; @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.system.service.user;
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.collection.CollectionUtil; 4 import cn.hutool.core.collection.CollectionUtil;
5 import cn.hutool.core.io.IoUtil; 5 import cn.hutool.core.io.IoUtil;
  6 +import cn.hutool.core.util.ObjUtil;
6 import cn.hutool.core.util.StrUtil; 7 import cn.hutool.core.util.StrUtil;
7 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 8 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
8 import cn.iocoder.yudao.framework.common.exception.ServiceException; 9 import cn.iocoder.yudao.framework.common.exception.ServiceException;
@@ -61,6 +62,8 @@ public class AdminUserServiceImpl implements AdminUserService { @@ -61,6 +62,8 @@ public class AdminUserServiceImpl implements AdminUserService {
61 62
62 static final String USER_INIT_PASSWORD_KEY = "system.user.init-password"; 63 static final String USER_INIT_PASSWORD_KEY = "system.user.init-password";
63 64
  65 + static final String USER_REGISTER_ENABLED_KEY = "system.user.register-enabled";
  66 +
64 @Resource 67 @Resource
65 private AdminUserMapper userMapper; 68 private AdminUserMapper userMapper;
66 69
@@ -117,14 +120,18 @@ public class AdminUserServiceImpl implements AdminUserService { @@ -117,14 +120,18 @@ public class AdminUserServiceImpl implements AdminUserService {
117 120
118 @Override 121 @Override
119 public Long registerUser(AuthRegisterReqVO registerReqVO) { 122 public Long registerUser(AuthRegisterReqVO registerReqVO) {
120 - // 1.1 校验账户配合 123 + // 1.1 校验是否开启注册
  124 + if (ObjUtil.notEqual(configApi.getConfigValueByKey(USER_REGISTER_ENABLED_KEY), "true")) {
  125 + throw exception(USER_REGISTER_DISABLED);
  126 + }
  127 + // 1.2 校验账户配合
121 tenantService.handleTenantInfo(tenant -> { 128 tenantService.handleTenantInfo(tenant -> {
122 long count = userMapper.selectCount(); 129 long count = userMapper.selectCount();
123 if (count >= tenant.getAccountCount()) { 130 if (count >= tenant.getAccountCount()) {
124 throw exception(USER_COUNT_MAX, tenant.getAccountCount()); 131 throw exception(USER_COUNT_MAX, tenant.getAccountCount());
125 } 132 }
126 }); 133 });
127 - // 1.2 校验正确性 134 + // 1.3 校验正确性
128 validateUserForCreateOrUpdate(null, registerReqVO.getUsername(), null, null, null, null); 135 validateUserForCreateOrUpdate(null, registerReqVO.getUsername(), null, null, null, null);
129 136
130 // 2. 插入用户 137 // 2. 插入用户
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImplTest.java
@@ -212,6 +212,7 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest { @@ -212,6 +212,7 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
212 UserProfileUpdateReqVO reqVO = randomPojo(UserProfileUpdateReqVO.class, o -> { 212 UserProfileUpdateReqVO reqVO = randomPojo(UserProfileUpdateReqVO.class, o -> {
213 o.setMobile(randomString()); 213 o.setMobile(randomString());
214 o.setSex(RandomUtil.randomEle(SexEnum.values()).getSex()); 214 o.setSex(RandomUtil.randomEle(SexEnum.values()).getSex());
  215 + o.setAvatar(randomURL());
215 }); 216 });
216 217
217 // 调用 218 // 调用