Commit dad0cc4cb1d00840b5df2c80b549e3ac1bafaac2
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 | 71 | <!-- 三方云服务相关 --> |
| 72 | 72 | <commons-io.version>2.17.0</commons-io.version> |
| 73 | 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 | 75 | <justauth.version>2.0.5</justauth.version> |
| 76 | 76 | <jimureport.version>1.8.1</jimureport.version> |
| 77 | 77 | <weixin-java.version>4.7.2.B</weixin-java.version> |
| ... | ... | @@ -553,9 +553,9 @@ |
| 553 | 553 | |
| 554 | 554 | <!-- 三方云服务相关 --> |
| 555 | 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 | 559 | </dependency> |
| 560 | 560 | |
| 561 | 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 | 11 | import java.util.stream.Collectors; |
| 12 | 12 | import java.util.stream.Stream; |
| 13 | 13 | |
| 14 | +import static cn.hutool.core.convert.Convert.toCollection; | |
| 14 | 15 | import static java.util.Arrays.asList; |
| 15 | 16 | |
| 16 | 17 | /** |
| ... | ... | @@ -335,4 +336,17 @@ public class CollectionUtils { |
| 335 | 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 | 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 | 1 | package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.form; |
| 2 | 2 | |
| 3 | -import cn.hutool.core.convert.Convert; | |
| 4 | 3 | import cn.hutool.core.lang.Assert; |
| 4 | +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; | |
| 5 | 5 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; |
| 6 | 6 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user.BpmTaskCandidateUserStrategy; |
| 7 | 7 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; |
| ... | ... | @@ -33,7 +33,7 @@ public class BpmTaskCandidateFormUserStrategy implements BpmTaskCandidateStrateg |
| 33 | 33 | @Override |
| 34 | 34 | public Set<Long> calculateUsersByTask(DelegateExecution execution, String param) { |
| 35 | 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 | 39 | @Override |
| ... | ... | @@ -41,7 +41,7 @@ public class BpmTaskCandidateFormUserStrategy implements BpmTaskCandidateStrateg |
| 41 | 41 | String param, Long startUserId, String processDefinitionId, |
| 42 | 42 | Map<String, Object> processVariables) { |
| 43 | 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 | 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 | 4 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; |
| 5 | 5 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; |
| 6 | 6 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; |
| ... | ... | @@ -37,7 +37,7 @@ public class BpmTaskCandidateExpressionStrategy implements BpmTaskCandidateStrat |
| 37 | 37 | @Override |
| 38 | 38 | public Set<Long> calculateUsersByTask(DelegateExecution execution, String param) { |
| 39 | 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 | 43 | @Override |
| ... | ... | @@ -46,7 +46,7 @@ public class BpmTaskCandidateExpressionStrategy implements BpmTaskCandidateStrat |
| 46 | 46 | Map<String, Object> variables = processVariables == null ? new HashMap<>() : processVariables; |
| 47 | 47 | try { |
| 48 | 48 | Object result = FlowableUtils.getExpressionValue(variables, param); |
| 49 | - return Convert.toSet(Long.class, result); | |
| 49 | + return CollectionUtils.toLinkedHashSet(Long.class, result); | |
| 50 | 50 | } catch (FlowableException ex) { |
| 51 | 51 | // 预测未运行的节点时候,表达式如果包含 execution 或者不存在的流程变量会抛异常, |
| 52 | 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 | 6 | import cn.hutool.core.util.*; |
| 7 | 7 | import cn.hutool.extra.spring.SpringUtil; |
| 8 | 8 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 9 | -import cn.iocoder.yudao.framework.common.util.collection.MapUtils; | |
| 10 | 9 | import cn.iocoder.yudao.framework.common.util.date.DateUtils; |
| 11 | 10 | import cn.iocoder.yudao.framework.common.util.number.NumberUtils; |
| 12 | 11 | import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; |
| ... | ... | @@ -882,6 +881,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 882 | 881 | return; |
| 883 | 882 | } |
| 884 | 883 | runExecutionIds.add(task.getExecutionId()); |
| 884 | + | |
| 885 | 885 | // 判断是否分配给自己任务,因为会签任务,一个节点会有多个任务 |
| 886 | 886 | if (isAssignUserTask(userId, task)) { // 情况一:自己的任务,进行 RETURN 标记 |
| 887 | 887 | // 2.1.1 添加评论 | ... | ... |
yudao-module-infra/yudao-module-infra-biz/pom.xml
| ... | ... | @@ -115,9 +115,10 @@ |
| 115 | 115 | <groupId>com.jcraft</groupId> |
| 116 | 116 | <artifactId>jsch</artifactId> <!-- 文件客户端:解决 sftp 连接 --> |
| 117 | 117 | </dependency> |
| 118 | + <!-- 文件客户端:解决阿里云、腾讯云、minio 等 S3 连接 --> | |
| 118 | 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 | 122 | </dependency> |
| 122 | 123 | |
| 123 | 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 | 13 | public enum CodegenFrontTypeEnum { |
| 14 | 14 | |
| 15 | 15 | VUE2_ELEMENT_UI(10), // Vue2 Element UI 标准模版 |
| 16 | + | |
| 16 | 17 | VUE3_ELEMENT_PLUS(20), // Vue3 Element Plus 标准模版 |
| 18 | + | |
| 17 | 19 | VUE3_VBEN2_ANTD_SCHEMA(30), // Vue3 VBEN2 + ANTD + Schema 模版 |
| 20 | + | |
| 18 | 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 | 4 | import cn.hutool.core.util.StrUtil; |
| 5 | 5 | import cn.hutool.http.HttpUtil; |
| 6 | 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 | 24 | * 基于 S3 协议的文件客户端,实现 MinIO、阿里云、腾讯云、七牛云、华为云等云服务 |
| 22 | - * <p> | |
| 23 | - * S3 协议的客户端,采用亚马逊提供的 software.amazon.awssdk.s3 库 | |
| 24 | 25 | * |
| 25 | 26 | * @author 芋道源码 |
| 26 | 27 | */ |
| 27 | 28 | public class S3FileClient extends AbstractFileClient<S3FileClientConfig> { |
| 28 | 29 | |
| 29 | - private AmazonS3Client client; | |
| 30 | + private S3Client client; | |
| 31 | + private S3Presigner presigner; | |
| 30 | 32 | |
| 31 | 33 | public S3FileClient(Long id, S3FileClientConfig config) { |
| 32 | 34 | super(id, config); |
| ... | ... | @@ -38,31 +40,80 @@ public class S3FileClient extends AbstractFileClient<S3FileClientConfig> { |
| 38 | 40 | if (StrUtil.isEmpty(config.getDomain())) { |
| 39 | 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 | 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<S3FileClientConfig> { |
| 79 | 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 | 67 | @NotNull(message = "accessSecret 不能为空") |
| 68 | 68 | private String accessSecret; |
| 69 | 69 | |
| 70 | + /** | |
| 71 | + * 是否启用 PathStyle 访问 | |
| 72 | + */ | |
| 73 | + @NotNull(message = "enablePathStyleAccess 不能为空") | |
| 74 | + private Boolean enablePathStyleAccess; | |
| 75 | + | |
| 70 | 76 | @SuppressWarnings("RedundantIfStatement") |
| 71 | 77 | @AssertTrue(message = "domain 不能为空") |
| 72 | 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 | 164 | .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/modules/list_sub_erp.vue"), // 特殊:主子表专属逻辑 |
| 165 | 165 | vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-list.vue")) |
| 166 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 184 | vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-list.vue")) |
| 185 | 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 | 5 | |
| 6 | 6 | import java.util.Collection; |
| 7 | 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 | 13 | * 商品 SKU API 接口 |
| ... | ... | @@ -31,6 +34,16 @@ public interface ProductSkuApi { |
| 31 | 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 | 47 | * 批量查询 SKU 数组 |
| 35 | 48 | * |
| 36 | 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 | 30 | * @param ids SPU 编号列表 |
| 31 | 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 | 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 | 124 | List<PointProductDO> products = pointActivityService.getPointProductListByActivityIds( |
| 125 | 125 | convertSet(activityList, PointActivityDO::getId)); |
| 126 | 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 | 128 | convertSet(activityList, PointActivityDO::getSpuId)); |
| 129 | 129 | List<PointActivityRespVO> result = BeanUtils.toBean(activityList, PointActivityRespVO.class); |
| 130 | 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 | 104 | List<PointProductDO> products = pointActivityService.getPointProductListByActivityIds( |
| 105 | 105 | convertSet(activityList, PointActivityDO::getId)); |
| 106 | 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 | 108 | convertSet(activityList, PointActivityDO::getSpuId)); |
| 109 | 109 | List<AppPointActivityRespVO> result = BeanUtils.toBean(activityList, AppPointActivityRespVO.class); |
| 110 | 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 | 22 | import jakarta.annotation.Resource; |
| 23 | 23 | import lombok.extern.slf4j.Slf4j; |
| 24 | 24 | import org.springframework.stereotype.Service; |
| 25 | +import org.springframework.transaction.annotation.Propagation; | |
| 25 | 26 | import org.springframework.transaction.annotation.Transactional; |
| 26 | 27 | import org.springframework.validation.annotation.Validated; |
| 27 | 28 | |
| ... | ... | @@ -180,7 +181,7 @@ public class CouponServiceImpl implements CouponService { |
| 180 | 181 | * @param couponId 模版编号 |
| 181 | 182 | * @param userId 用户编号 |
| 182 | 183 | */ |
| 183 | - @Transactional(rollbackFor = Exception.class) | |
| 184 | + @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW) // 每次调用开启一个新的事务,避免在一个大的事务里面 | |
| 184 | 185 | public void invalidateCoupon(Long couponId, Long userId) { |
| 185 | 186 | if (couponId == null || couponId <= 0) { |
| 186 | 187 | return; |
| ... | ... | @@ -270,13 +271,17 @@ public class CouponServiceImpl implements CouponService { |
| 270 | 271 | if (CollUtil.isEmpty(userIds)) { |
| 271 | 272 | throw exception(COUPON_TEMPLATE_USER_ALREADY_TAKE); |
| 272 | 273 | } |
| 273 | - | |
| 274 | 274 | // 校验模板 |
| 275 | 275 | if (couponTemplate == null) { |
| 276 | 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 | 285 | && couponTemplate.getTakeCount() + userIds.size() > couponTemplate.getTotalCount()) { |
| 281 | 286 | throw exception(COUPON_TEMPLATE_NOT_ENOUGH); |
| 282 | 287 | } |
| ... | ... | @@ -286,10 +291,6 @@ public class CouponServiceImpl implements CouponService { |
| 286 | 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 | 1 | package cn.iocoder.yudao.module.trade.controller.app.aftersale; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 4 | -import cn.iocoder.yudao.framework.common.pojo.PageParam; | |
| 5 | 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | |
| 6 | 6 | import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleCreateReqVO; |
| 7 | 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 | 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 | 11 | import cn.iocoder.yudao.module.trade.service.aftersale.AfterSaleService; |
| 11 | 12 | import io.swagger.v3.oas.annotations.Operation; |
| 12 | 13 | import io.swagger.v3.oas.annotations.Parameter; |
| ... | ... | @@ -31,16 +32,17 @@ public class AppAfterSaleController { |
| 31 | 32 | |
| 32 | 33 | @GetMapping(value = "/page") |
| 33 | 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 | 40 | @GetMapping(value = "/get") |
| 40 | 41 | @Operation(summary = "获得售后订单") |
| 41 | 42 | @Parameter(name = "id", description = "售后编号", required = true, example = "1") |
| 42 | 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 | 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 | 3 | import io.swagger.v3.oas.annotations.media.Schema; |
| 4 | 4 | import lombok.Data; |
| 5 | 5 | |
| 6 | -import java.util.List; | |
| 7 | - | |
| 8 | 6 | /** |
| 9 | 7 | * 商品 SPU 基础 Response VO |
| 10 | 8 | * |
| ... | ... | @@ -25,4 +23,10 @@ public class AppProductSpuBaseRespVO { |
| 25 | 23 | @Schema(description = "商品分类编号", example = "1") |
| 26 | 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 | 1 | package cn.iocoder.yudao.module.trade.controller.app.delivery.vo.pickup; |
| 2 | 2 | |
| 3 | +import com.fasterxml.jackson.annotation.JsonFormat; | |
| 3 | 4 | import io.swagger.v3.oas.annotations.media.Schema; |
| 5 | +import jakarta.validation.constraints.NotNull; | |
| 4 | 6 | import lombok.Data; |
| 5 | 7 | |
| 8 | +import java.time.LocalTime; | |
| 9 | + | |
| 6 | 10 | @Schema(description = "用户 App - 自提门店 Response VO") |
| 7 | 11 | @Data |
| 8 | 12 | public class AppDeliveryPickUpStoreRespVO { |
| ... | ... | @@ -28,6 +32,16 @@ public class AppDeliveryPickUpStoreRespVO { |
| 28 | 32 | @Schema(description = "门店详细地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "复旦大学路 188 号") |
| 29 | 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 | 45 | @Schema(description = "纬度", requiredMode = Schema.RequiredMode.REQUIRED, example = "5.88") |
| 32 | 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 | 11 | import cn.iocoder.yudao.module.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO; |
| 12 | 12 | import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderBaseVO; |
| 13 | 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 | 14 | import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO; |
| 16 | 15 | import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleLogDO; |
| 17 | 16 | import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; |
| ... | ... | @@ -63,10 +62,6 @@ public interface AfterSaleConvert { |
| 63 | 62 | |
| 64 | 63 | ProductPropertyValueDetailRespVO convert(ProductPropertyValueDetailRespDTO bean); |
| 65 | 64 | |
| 66 | - AppAfterSaleRespVO convert(AfterSaleDO bean); | |
| 67 | - | |
| 68 | - PageResult<AppAfterSaleRespVO> convertPage02(PageResult<AfterSaleDO> page); | |
| 69 | - | |
| 70 | 65 | default AfterSaleDetailRespVO convert(AfterSaleDO afterSale, TradeOrderDO order, TradeOrderItemDO orderItem, |
| 71 | 66 | MemberUserRespDTO user, List<AfterSaleLogDO> logs) { |
| 72 | 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 | 1 | package cn.iocoder.yudao.module.trade.convert.cart; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | |
| 3 | 4 | import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; |
| 4 | 5 | import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; |
| 5 | 6 | import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum; |
| ... | ... | @@ -33,21 +34,18 @@ public interface TradeCartConvert { |
| 33 | 34 | cartVO.setId(cart.getId()).setCount(cart.getCount()).setSelected(cart.getSelected()); |
| 34 | 35 | ProductSpuRespDTO spu = spuMap.get(cart.getSpuId()); |
| 35 | 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 | 39 | // 如果 SPU 不存在,或者下架,或者库存不足,说明是无效的 |
| 38 | 40 | if (spu == null |
| 39 | 41 | || !ProductSpuStatusEnum.isEnable(spu.getStatus()) |
| 40 | 42 | || spu.getStock() <= 0) { |
| 41 | - cartVO.setSelected(false); // 强制设置成不可选中 | |
| 42 | 43 | invalidList.add(cartVO); |
| 43 | 44 | } else { |
| 44 | - // 虽然 SKU 可能也会不存在,但是可以通过购物车重新选择 | |
| 45 | 45 | validList.add(cartVO); |
| 46 | 46 | } |
| 47 | 47 | }); |
| 48 | 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 | 1 | package cn.iocoder.yudao.module.trade.dal.mysql.aftersale; |
| 2 | 2 | |
| 3 | -import cn.iocoder.yudao.framework.common.pojo.PageParam; | |
| 4 | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | 4 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| 6 | 5 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| 7 | 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 | 8 | import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO; |
| 9 | 9 | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| 10 | 10 | import org.apache.ibatis.annotations.Mapper; |
| ... | ... | @@ -27,9 +27,10 @@ public interface AfterSaleMapper extends BaseMapperX<AfterSaleDO> { |
| 27 | 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 | 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 | 1 | package cn.iocoder.yudao.module.trade.service.aftersale; |
| 2 | 2 | |
| 3 | -import cn.iocoder.yudao.framework.common.pojo.PageParam; | |
| 4 | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | 4 | import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSaleDisagreeReqVO; |
| 6 | 5 | import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSalePageReqVO; |
| 7 | 6 | import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSaleRefuseReqVO; |
| 8 | 7 | import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleCreateReqVO; |
| 9 | 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 | 10 | import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO; |
| 11 | 11 | |
| 12 | 12 | /** |
| ... | ... | @@ -28,10 +28,10 @@ public interface AfterSaleService { |
| 28 | 28 | * 【会员】获得售后订单分页 |
| 29 | 29 | * |
| 30 | 30 | * @param userId 用户编号 |
| 31 | - * @param pageParam 分页参数 | |
| 31 | + * @param pageReqVO 分页参数 | |
| 32 | 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 | 3 | import cn.hutool.core.map.MapUtil; |
| 4 | 4 | import cn.hutool.core.util.ObjectUtil; |
| 5 | 5 | import cn.hutool.core.util.StrUtil; |
| 6 | -import cn.iocoder.yudao.framework.common.pojo.PageParam; | |
| 7 | 6 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 8 | 7 | import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; |
| 9 | 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 | 15 | import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSaleRefuseReqVO; |
| 17 | 16 | import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleCreateReqVO; |
| 18 | 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 | 19 | import cn.iocoder.yudao.module.trade.convert.aftersale.AfterSaleConvert; |
| 20 | 20 | import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO; |
| 21 | 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 | 36 | import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService; |
| 37 | 37 | import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService; |
| 38 | 38 | import cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService; |
| 39 | +import jakarta.annotation.Resource; | |
| 39 | 40 | import lombok.extern.slf4j.Slf4j; |
| 40 | 41 | import org.springframework.context.annotation.Lazy; |
| 41 | 42 | import org.springframework.stereotype.Service; |
| ... | ... | @@ -44,7 +45,6 @@ import org.springframework.transaction.support.TransactionSynchronization; |
| 44 | 45 | import org.springframework.transaction.support.TransactionSynchronizationManager; |
| 45 | 46 | import org.springframework.validation.annotation.Validated; |
| 46 | 47 | |
| 47 | -import jakarta.annotation.Resource; | |
| 48 | 48 | import java.time.LocalDateTime; |
| 49 | 49 | |
| 50 | 50 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| ... | ... | @@ -87,8 +87,8 @@ public class AfterSaleServiceImpl implements AfterSaleService { |
| 87 | 87 | } |
| 88 | 88 | |
| 89 | 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 | 94 | @Override |
| ... | ... | @@ -386,7 +386,7 @@ public class AfterSaleServiceImpl implements AfterSaleService { |
| 386 | 386 | public void afterCommit() { |
| 387 | 387 | // 创建退款单 |
| 388 | 388 | PayRefundCreateReqDTO createReqDTO = AfterSaleConvert.INSTANCE.convert(userIp, afterSale, tradeOrderProperties) |
| 389 | - .setReason(StrUtil.format("退款【{}】", afterSale.getSpuName()));; | |
| 389 | + .setReason(StrUtil.format("退款【{}】", afterSale.getSpuName())); | |
| 390 | 390 | Long payRefundId = payRefundApi.createRefund(createReqDTO); |
| 391 | 391 | // 更新售后单的退款单号 |
| 392 | 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 | 545 | if (ObjectUtil.notEqual(order.getStatus(), TradeOrderStatusEnum.UNPAID.getStatus())) { |
| 546 | 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 | 557 | // 2. 取消订单 |
| 550 | 558 | cancelOrder0(order, TradeOrderCancelTypeEnum.MEMBER_CANCEL); |
| ... | ... | @@ -581,6 +589,15 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { |
| 581 | 589 | @Transactional(rollbackFor = Exception.class) |
| 582 | 590 | @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.SYSTEM_CANCEL) |
| 583 | 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 | 601 | cancelOrder0(order, TradeOrderCancelTypeEnum.PAY_TIMEOUT); |
| 585 | 602 | } |
| 586 | 603 | |
| ... | ... | @@ -895,12 +912,11 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { |
| 895 | 912 | if (order == null) { |
| 896 | 913 | throw exception(ORDER_NOT_FOUND); |
| 897 | 914 | } |
| 898 | - | |
| 899 | 915 | // 1.3 校验订单是否支付 |
| 900 | 916 | if (!order.getPayStatus()) { |
| 901 | 917 | throw exception(ORDER_CANCEL_PAID_FAIL, "已支付"); |
| 902 | 918 | } |
| 903 | - // 1.3 校验订单是否未退款 | |
| 919 | + // 1.4 校验订单是否未退款 | |
| 904 | 920 | if (ObjUtil.notEqual(TradeOrderRefundStatusEnum.NONE.getStatus(), order.getRefundStatus())) { |
| 905 | 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 | 20 | |
| 21 | 21 | import jakarta.annotation.Resource; |
| 22 | 22 | import java.util.List; |
| 23 | +import java.util.Map; | |
| 23 | 24 | |
| 24 | 25 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; |
| 25 | 26 | |
| ... | ... | @@ -101,13 +102,17 @@ public class TradeBrokerageOrderHandler implements TradeOrderHandler { |
| 101 | 102 | protected void addBrokerage(Long userId, List<TradeOrderItemDO> orderItems) { |
| 102 | 103 | MemberUserRespDTO user = memberUserApi.getUser(userId); |
| 103 | 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 | 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 | 45 | ErrorCode USER_COUNT_MAX = new ErrorCode(1_002_003_008, "创建用户失败,原因:超过租户最大租户配额({})!"); |
| 46 | 46 | ErrorCode USER_IMPORT_INIT_PASSWORD = new ErrorCode(1_002_003_009, "初始密码不能为空"); |
| 47 | 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 | 50 | // ========== 部门模块 1-002-004-000 ========== |
| 50 | 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 | 9 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 10 | 10 | import cn.iocoder.yudao.framework.common.util.date.DateUtils; |
| 11 | 11 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 12 | +import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission; | |
| 12 | 13 | import cn.iocoder.yudao.framework.tenant.config.TenantProperties; |
| 13 | 14 | import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; |
| 14 | 15 | import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; |
| ... | ... | @@ -96,6 +97,7 @@ public class TenantServiceImpl implements TenantService { |
| 96 | 97 | |
| 97 | 98 | @Override |
| 98 | 99 | @DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换 |
| 100 | + @DataPermission(enable = false) // 参见 https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/1154 说明 | |
| 99 | 101 | public Long createTenant(TenantSaveReqVO createReqVO) { |
| 100 | 102 | // 校验租户名称是否重复 |
| 101 | 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 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.hutool.core.collection.CollectionUtil; |
| 5 | 5 | import cn.hutool.core.io.IoUtil; |
| 6 | +import cn.hutool.core.util.ObjUtil; | |
| 6 | 7 | import cn.hutool.core.util.StrUtil; |
| 7 | 8 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 8 | 9 | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| ... | ... | @@ -61,6 +62,8 @@ public class AdminUserServiceImpl implements AdminUserService { |
| 61 | 62 | |
| 62 | 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 | 67 | @Resource |
| 65 | 68 | private AdminUserMapper userMapper; |
| 66 | 69 | |
| ... | ... | @@ -117,14 +120,18 @@ public class AdminUserServiceImpl implements AdminUserService { |
| 117 | 120 | |
| 118 | 121 | @Override |
| 119 | 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 | 128 | tenantService.handleTenantInfo(tenant -> { |
| 122 | 129 | long count = userMapper.selectCount(); |
| 123 | 130 | if (count >= tenant.getAccountCount()) { |
| 124 | 131 | throw exception(USER_COUNT_MAX, tenant.getAccountCount()); |
| 125 | 132 | } |
| 126 | 133 | }); |
| 127 | - // 1.2 校验正确性 | |
| 134 | + // 1.3 校验正确性 | |
| 128 | 135 | validateUserForCreateOrUpdate(null, registerReqVO.getUsername(), null, null, null, null); |
| 129 | 136 | |
| 130 | 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 | 212 | UserProfileUpdateReqVO reqVO = randomPojo(UserProfileUpdateReqVO.class, o -> { |
| 213 | 213 | o.setMobile(randomString()); |
| 214 | 214 | o.setSex(RandomUtil.randomEle(SexEnum.values()).getSex()); |
| 215 | + o.setAvatar(randomURL()); | |
| 215 | 216 | }); |
| 216 | 217 | |
| 217 | 218 | // 调用 | ... | ... |