diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenFrontTypeEnum.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenFrontTypeEnum.java
index edcb85e474..741d7d9664 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenFrontTypeEnum.java
+++ b/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;
public enum CodegenFrontTypeEnum {
VUE2_ELEMENT_UI(10), // Vue2 Element UI 标准模版
+
VUE3_ELEMENT_PLUS(20), // Vue3 Element Plus 标准模版
+
VUE3_VBEN2_ANTD_SCHEMA(30), // Vue3 VBEN2 + ANTD + Schema 模版
+
VUE3_VBEN5_ANTD_SCHEMA(40), // Vue3 VBEN5 + ANTD + schema 模版
- VUE3_VBEN5_ANTD(50), // Vue3 VBEN5 + ANTD 模版
+ VUE3_VBEN5_ANTD_GENERAL(41), // Vue3 VBEN5 + ANTD 标准模版
;
/**
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClient.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClient.java
index 5c76e1a7c8..a33f0d738c 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClient.java
+++ b/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;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.iocoder.yudao.module.infra.framework.file.core.client.AbstractFileClient;
-import com.amazonaws.HttpMethod;
-import com.amazonaws.auth.AWSStaticCredentialsProvider;
-import com.amazonaws.auth.BasicAWSCredentials;
-import com.amazonaws.client.builder.AwsClientBuilder;
-import com.amazonaws.services.s3.AmazonS3Client;
-import com.amazonaws.services.s3.AmazonS3ClientBuilder;
-import com.amazonaws.services.s3.model.ObjectMetadata;
-import com.amazonaws.services.s3.model.S3Object;
+import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
+import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
+import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
+import software.amazon.awssdk.core.sync.RequestBody;
+import software.amazon.awssdk.regions.Region;
+import software.amazon.awssdk.services.s3.S3Client;
+import software.amazon.awssdk.services.s3.S3Configuration;
+import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
+import software.amazon.awssdk.services.s3.model.GetObjectRequest;
+import software.amazon.awssdk.services.s3.model.PutObjectRequest;
+import software.amazon.awssdk.services.s3.presigner.S3Presigner;
+import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest;
-import java.io.ByteArrayInputStream;
-import java.util.Date;
-import java.util.concurrent.TimeUnit;
+import java.net.URI;
+import java.time.Duration;
/**
* 基于 S3 协议的文件客户端,实现 MinIO、阿里云、腾讯云、七牛云、华为云等云服务
- *
- * S3 协议的客户端,采用亚马逊提供的 software.amazon.awssdk.s3 库
*
* @author 芋道源码
*/
public class S3FileClient extends AbstractFileClient {
- private AmazonS3Client client;
+ private S3Client client;
+ private S3Presigner presigner;
public S3FileClient(Long id, S3FileClientConfig config) {
super(id, config);
@@ -38,31 +40,80 @@ public class S3FileClient extends AbstractFileClient {
if (StrUtil.isEmpty(config.getDomain())) {
config.setDomain(buildDomain());
}
- // 初始化客户端
- client = (AmazonS3Client)AmazonS3ClientBuilder.standard()
- .withCredentials(buildCredentials())
- .withEndpointConfiguration(buildEndpointConfiguration())
+ // 初始化 S3 客户端
+ Region region = Region.of("us-east-1"); // 必须填,但填什么都行,常见的值有 "us-east-1",不填会报错
+ AwsCredentialsProvider credentialsProvider = StaticCredentialsProvider.create(
+ AwsBasicCredentials.create(config.getAccessKey(), config.getAccessSecret()));
+ URI endpoint = URI.create(buildEndpoint());
+ S3Configuration serviceConfiguration = S3Configuration.builder() // Path-style 访问
+ .pathStyleAccessEnabled(Boolean.TRUE.equals(config.getEnablePathStyleAccess()))
+ .chunkedEncodingEnabled(false) // 禁用分块编码,参见 https://t.zsxq.com/kBy57
+ .build();
+ client = S3Client.builder()
+ .credentialsProvider(credentialsProvider)
+ .region(region)
+ .endpointOverride(endpoint)
+ .serviceConfiguration(serviceConfiguration)
+ .build();
+ presigner = S3Presigner.builder()
+ .credentialsProvider(credentialsProvider)
+ .region(region)
+ .endpointOverride(endpoint)
+ .serviceConfiguration(serviceConfiguration)
.build();
}
- /**
- * 基于 config 秘钥,构建 S3 客户端的认证信息
- *
- * @return S3 客户端的认证信息
- */
- private AWSStaticCredentialsProvider buildCredentials() {
- return new AWSStaticCredentialsProvider(
- new BasicAWSCredentials(config.getAccessKey(), config.getAccessSecret()));
+ @Override
+ public String upload(byte[] content, String path, String type) {
+ // 构造 PutObjectRequest
+ PutObjectRequest putRequest = PutObjectRequest.builder()
+ .bucket(config.getBucket())
+ .key(path)
+ .contentType(type)
+ .contentLength((long) content.length)
+ .build();
+ // 上传文件
+ client.putObject(putRequest, RequestBody.fromBytes(content));
+ // 拼接返回路径
+ return config.getDomain() + "/" + path;
+ }
+
+ @Override
+ public void delete(String path) {
+ DeleteObjectRequest deleteRequest = DeleteObjectRequest.builder()
+ .bucket(config.getBucket())
+ .key(path)
+ .build();
+ client.deleteObject(deleteRequest);
+ }
+
+ @Override
+ public byte[] getContent(String path) {
+ GetObjectRequest getRequest = GetObjectRequest.builder()
+ .bucket(config.getBucket())
+ .key(path)
+ .build();
+ return IoUtil.readBytes(client.getObject(getRequest));
+ }
+
+ @Override
+ public FilePresignedUrlRespDTO getPresignedObjectUrl(String path) {
+ Duration expiration = Duration.ofHours(24);
+ return new FilePresignedUrlRespDTO(getPresignedUrl(path, expiration), config.getDomain() + "/" + path);
}
/**
- * 构建 S3 客户端的 Endpoint 配置,包括 region、endpoint
+ * 生成动态的预签名上传 URL
*
- * @return S3 客户端的 EndpointConfiguration 配置
+ * @param path 相对路径
+ * @param expiration 过期时间
+ * @return 生成的上传 URL
*/
- private AwsClientBuilder.EndpointConfiguration buildEndpointConfiguration() {
- return new AwsClientBuilder.EndpointConfiguration(config.getEndpoint(),
- null); // 无需设置 region
+ private String getPresignedUrl(String path, Duration expiration) {
+ return presigner.presignPutObject(PutObjectPresignRequest.builder()
+ .signatureDuration(expiration)
+ .putObjectRequest(b -> b.bucket(config.getBucket()).key(path))
+ .build()).url().toString();
}
/**
@@ -79,40 +130,17 @@ public class S3FileClient extends AbstractFileClient {
return StrUtil.format("https://{}.{}", config.getBucket(), config.getEndpoint());
}
- @Override
- public String upload(byte[] content, String path, String type) throws Exception {
- // 元数据,主要用于设置文件类型
- ObjectMetadata objectMetadata = new ObjectMetadata();
- objectMetadata.setContentType(type);
- objectMetadata.setContentLength(content.length); // 如果不设置,会有 “ No content length specified for stream data” 警告日志
- // 执行上传
- client.putObject(config.getBucket(),
- path, // 相对路径
- new ByteArrayInputStream(content), // 文件内容
- objectMetadata);
-
- // 拼接返回路径
- return config.getDomain() + "/" + path;
- }
-
- @Override
- public void delete(String path) throws Exception {
- client.deleteObject(config.getBucket(), path);
- }
-
- @Override
- public byte[] getContent(String path) throws Exception {
- S3Object tempS3Object = client.getObject(config.getBucket(), path);
- return IoUtil.readBytes(tempS3Object.getObjectContent());
- }
-
- @Override
- public FilePresignedUrlRespDTO getPresignedObjectUrl(String path) throws Exception {
- // 设定过期时间为 10 分钟。取值范围:1 秒 ~ 7 天
- Date expiration = new Date(System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(10));
- // 生成上传 URL
- String uploadUrl = String.valueOf(client.generatePresignedUrl(config.getBucket(), path, expiration , HttpMethod.PUT));
- return new FilePresignedUrlRespDTO(uploadUrl, config.getDomain() + "/" + path);
+ /**
+ * 节点地址补全协议头
+ *
+ * @return 节点地址
+ */
+ private String buildEndpoint() {
+ // 如果已经是 http 或者 https,则不进行拼接
+ if (HttpUtil.isHttp(config.getEndpoint()) || HttpUtil.isHttps(config.getEndpoint())) {
+ return config.getEndpoint();
+ }
+ return StrUtil.format("https://{}", config.getEndpoint());
}
}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClientConfig.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClientConfig.java
index e59a7594d2..fb19317e02 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClientConfig.java
+++ b/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 {
@NotNull(message = "accessSecret 不能为空")
private String accessSecret;
+ /**
+ * 是否启用 PathStyle 访问
+ */
+ @NotNull(message = "enablePathStyleAccess 不能为空")
+ private Boolean enablePathStyleAccess;
+
@SuppressWarnings("RedundantIfStatement")
@AssertTrue(message = "domain 不能为空")
@JsonIgnore
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java
index 89737fe937..0b5b870eb9 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java
+++ b/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 {
.put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/modules/list_sub_erp.vue"), // 特殊:主子表专属逻辑
vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-list.vue"))
// VUE3_VBEN5_ANTD
- .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD.getType(), vue3Vben5AntdGeneralTemplatePath("views/data.ts"),
+ .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("views/data.ts"),
vue3FilePath("views/${table.moduleName}/${table.businessName}/data.ts"))
- .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD.getType(), vue3Vben5AntdGeneralTemplatePath("views/index.vue"),
+ .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("views/index.vue"),
vue3FilePath("views/${table.moduleName}/${table.businessName}/index.vue"))
- .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD.getType(), vue3Vben5AntdGeneralTemplatePath("views/form.vue"),
+ .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("views/form.vue"),
vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/form.vue"))
- .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD.getType(), vue3Vben5AntdGeneralTemplatePath("api/api.ts"),
+ .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("api/api.ts"),
vue3FilePath("api/${table.moduleName}/${table.businessName}/index.ts"))
- .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD.getType(), vue3Vben5AntdGeneralTemplatePath("views/modules/form_sub_normal.vue"), // 特殊:主子表专属逻辑
+ .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("views/modules/form_sub_normal.vue"), // 特殊:主子表专属逻辑
vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-form.vue"))
- .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD.getType(), vue3Vben5AntdGeneralTemplatePath("views/modules/form_sub_inner.vue"), // 特殊:主子表专属逻辑
+ .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("views/modules/form_sub_inner.vue"), // 特殊:主子表专属逻辑
vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-form.vue"))
- .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD.getType(), vue3Vben5AntdGeneralTemplatePath("views/modules/form_sub_erp.vue"), // 特殊:主子表专属逻辑
+ .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("views/modules/form_sub_erp.vue"), // 特殊:主子表专属逻辑
vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-form.vue"))
- .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD.getType(), vue3Vben5AntdGeneralTemplatePath("views/modules/list_sub_inner.vue"), // 特殊:主子表专属逻辑
+ .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("views/modules/list_sub_inner.vue"), // 特殊:主子表专属逻辑
vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-list.vue"))
- .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD.getType(), vue3Vben5AntdGeneralTemplatePath("views/modules/list_sub_erp.vue"), // 特殊:主子表专属逻辑
+ .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("views/modules/list_sub_erp.vue"), // 特殊:主子表专属逻辑
vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-list.vue"))
.build();
diff --git a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApi.java b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApi.java
index 3581fdb913..f6e6034875 100644
--- a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApi.java
+++ b/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;
import java.util.Collection;
import java.util.List;
+import java.util.Map;
+
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
/**
* 商品 SKU API 接口
@@ -30,6 +33,16 @@ public interface ProductSkuApi {
*/
List getSkuList(Collection ids);
+ /**
+ * 批量查询 SKU MAP
+ *
+ * @param ids SKU 编号列表
+ * @return SKU MAP
+ */
+ default Map getSkuMap(Collection ids) {
+ return convertMap(getSkuList(ids), ProductSkuRespDTO::getId);
+ }
+
/**
* 批量查询 SKU 数组
*
diff --git a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/ProductSpuApi.java b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/ProductSpuApi.java
index 64d24c399a..499a0ca896 100644
--- a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/ProductSpuApi.java
+++ b/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 {
* @param ids SPU 编号列表
* @return SPU MAP
*/
- default Map getSpusMap(Collection ids) {
+ default Map getSpuMap(Collection ids) {
return convertMap(getSpuList(ids), ProductSpuRespDTO::getId);
}
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/point/PointActivityController.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/point/PointActivityController.java
index d8fb54b081..f545db6f50 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/point/PointActivityController.java
+++ b/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 {
List products = pointActivityService.getPointProductListByActivityIds(
convertSet(activityList, PointActivityDO::getId));
Map> productsMap = convertMultiMap(products, PointProductDO::getActivityId);
- Map spuMap = productSpuApi.getSpusMap(
+ Map spuMap = productSpuApi.getSpuMap(
convertSet(activityList, PointActivityDO::getSpuId));
List result = BeanUtils.toBean(activityList, PointActivityRespVO.class);
result.forEach(activity -> {
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/point/AppPointActivityController.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/point/AppPointActivityController.java
index c364b64f2a..9730056870 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/point/AppPointActivityController.java
+++ b/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 {
List products = pointActivityService.getPointProductListByActivityIds(
convertSet(activityList, PointActivityDO::getId));
Map> productsMap = convertMultiMap(products, PointProductDO::getActivityId);
- Map spuMap = productSpuApi.getSpusMap(
+ Map spuMap = productSpuApi.getSpuMap(
convertSet(activityList, PointActivityDO::getSpuId));
List result = BeanUtils.toBean(activityList, AppPointActivityRespVO.class);
result.forEach(activity -> {
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java
index 1c5db82889..e6f82a69fc 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java
+++ b/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
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
@@ -180,7 +181,7 @@ public class CouponServiceImpl implements CouponService {
* @param couponId 模版编号
* @param userId 用户编号
*/
- @Transactional(rollbackFor = Exception.class)
+ @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW) // 每次调用开启一个新的事务,避免在一个大的事务里面
public void invalidateCoupon(Long couponId, Long userId) {
if (couponId == null || couponId <= 0) {
return;
@@ -270,13 +271,17 @@ public class CouponServiceImpl implements CouponService {
if (CollUtil.isEmpty(userIds)) {
throw exception(COUPON_TEMPLATE_USER_ALREADY_TAKE);
}
-
// 校验模板
if (couponTemplate == null) {
throw exception(COUPON_TEMPLATE_NOT_EXISTS);
}
- // 校验剩余数量
- if (ObjUtil.notEqual(couponTemplate.getTakeLimitCount(), CouponTemplateDO.TIME_LIMIT_COUNT_MAX) // 非不限制
+ // 校验领取方式
+ if (ObjUtil.notEqual(couponTemplate.getTakeType(), takeType.getType())) {
+ throw exception(COUPON_TEMPLATE_CANNOT_TAKE);
+ }
+ // 校验发放数量不能过小(仅在 CouponTakeTypeEnum.USER 用户领取时)
+ if (CouponTakeTypeEnum.isUser(couponTemplate.getTakeType())
+ && ObjUtil.notEqual(couponTemplate.getTakeLimitCount(), CouponTemplateDO.TIME_LIMIT_COUNT_MAX) // 非不限制
&& couponTemplate.getTakeCount() + userIds.size() > couponTemplate.getTotalCount()) {
throw exception(COUPON_TEMPLATE_NOT_ENOUGH);
}
@@ -286,10 +291,6 @@ public class CouponServiceImpl implements CouponService {
throw exception(COUPON_TEMPLATE_EXPIRED);
}
}
- // 校验领取方式
- if (ObjectUtil.notEqual(couponTemplate.getTakeType(), takeType.getType())) {
- throw exception(COUPON_TEMPLATE_CANNOT_TAKE);
- }
}
/**
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/aftersale/AppAfterSaleController.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/aftersale/AppAfterSaleController.java
index 2328119d06..46495c003e 100644
--- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/aftersale/AppAfterSaleController.java
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/aftersale/AppAfterSaleController.java
@@ -1,12 +1,13 @@
package cn.iocoder.yudao.module.trade.controller.app.aftersale;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
-import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleCreateReqVO;
import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleDeliveryReqVO;
+import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSalePageReqVO;
import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleRespVO;
-import cn.iocoder.yudao.module.trade.convert.aftersale.AfterSaleConvert;
+import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO;
import cn.iocoder.yudao.module.trade.service.aftersale.AfterSaleService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@@ -31,16 +32,17 @@ public class AppAfterSaleController {
@GetMapping(value = "/page")
@Operation(summary = "获得售后分页")
- public CommonResult> getAfterSalePage(PageParam pageParam) {
- return success(AfterSaleConvert.INSTANCE.convertPage02(
- afterSaleService.getAfterSalePage(getLoginUserId(), pageParam)));
+ public CommonResult> getAfterSalePage(AppAfterSalePageReqVO pageReqVO) {
+ PageResult pageResult = afterSaleService.getAfterSalePage(getLoginUserId(), pageReqVO);
+ return success(BeanUtils.toBean(pageResult, AppAfterSaleRespVO.class));
}
@GetMapping(value = "/get")
@Operation(summary = "获得售后订单")
@Parameter(name = "id", description = "售后编号", required = true, example = "1")
public CommonResult getAfterSale(@RequestParam("id") Long id) {
- return success(AfterSaleConvert.INSTANCE.convert(afterSaleService.getAfterSale(getLoginUserId(), id)));
+ AfterSaleDO afterSale = afterSaleService.getAfterSale(getLoginUserId(), id);
+ return success(BeanUtils.toBean(afterSale, AppAfterSaleRespVO.class));
}
@PostMapping(value = "/create")
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/aftersale/vo/AppAfterSalePageReqVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/aftersale/vo/AppAfterSalePageReqVO.java
new file mode 100644
index 0000000000..35e2429b39
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/aftersale/vo/AppAfterSalePageReqVO.java
@@ -0,0 +1,20 @@
+package cn.iocoder.yudao.module.trade.controller.app.aftersale.vo;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import java.util.Set;
+
+@Schema(description = "用户 App - 交易售后分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class AppAfterSalePageReqVO extends PageParam {
+
+ @Schema(description = "售后状态", example = "10, 20")
+ private Set statuses;
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/base/spu/AppProductSpuBaseRespVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/base/spu/AppProductSpuBaseRespVO.java
index d30417818f..c7a3c1d9e7 100644
--- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/base/spu/AppProductSpuBaseRespVO.java
+++ b/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;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
-import java.util.List;
-
/**
* 商品 SPU 基础 Response VO
*
@@ -25,4 +23,10 @@ public class AppProductSpuBaseRespVO {
@Schema(description = "商品分类编号", example = "1")
private Long categoryId;
+ @Schema(description = "商品库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000")
+ private Integer stock;
+
+ @Schema(description = "商品状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ private Integer status;
+
}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/delivery/vo/pickup/AppDeliveryPickUpStoreRespVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/delivery/vo/pickup/AppDeliveryPickUpStoreRespVO.java
index 1ca25ade5f..685a8f23fc 100644
--- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/delivery/vo/pickup/AppDeliveryPickUpStoreRespVO.java
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/delivery/vo/pickup/AppDeliveryPickUpStoreRespVO.java
@@ -1,8 +1,12 @@
package cn.iocoder.yudao.module.trade.controller.app.delivery.vo.pickup;
+import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
import lombok.Data;
+import java.time.LocalTime;
+
@Schema(description = "用户 App - 自提门店 Response VO")
@Data
public class AppDeliveryPickUpStoreRespVO {
@@ -28,6 +32,16 @@ public class AppDeliveryPickUpStoreRespVO {
@Schema(description = "门店详细地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "复旦大学路 188 号")
private String detailAddress;
+ @Schema(description = "营业开始时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotNull(message = "营业开始时间不能为空")
+ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "HH:mm")
+ private LocalTime openingTime;
+
+ @Schema(description = "营业结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotNull(message = "营业结束时间不能为空")
+ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "HH:mm")
+ private LocalTime closingTime;
+
@Schema(description = "纬度", requiredMode = Schema.RequiredMode.REQUIRED, example = "5.88")
private Double latitude;
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/aftersale/AfterSaleConvert.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/aftersale/AfterSaleConvert.java
index 45f6e31891..54480f696b 100644
--- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/aftersale/AfterSaleConvert.java
+++ b/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
import cn.iocoder.yudao.module.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO;
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderBaseVO;
import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleCreateReqVO;
-import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleRespVO;
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleLogDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
@@ -63,10 +62,6 @@ public interface AfterSaleConvert {
ProductPropertyValueDetailRespVO convert(ProductPropertyValueDetailRespDTO bean);
- AppAfterSaleRespVO convert(AfterSaleDO bean);
-
- PageResult convertPage02(PageResult page);
-
default AfterSaleDetailRespVO convert(AfterSaleDO afterSale, TradeOrderDO order, TradeOrderItemDO orderItem,
MemberUserRespDTO user, List logs) {
AfterSaleDetailRespVO respVO = convert02(afterSale);
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/cart/TradeCartConvert.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/cart/TradeCartConvert.java
index 83cd459542..fa5fccd54e 100644
--- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/cart/TradeCartConvert.java
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/cart/TradeCartConvert.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.trade.convert.cart;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
@@ -33,21 +34,18 @@ public interface TradeCartConvert {
cartVO.setId(cart.getId()).setCount(cart.getCount()).setSelected(cart.getSelected());
ProductSpuRespDTO spu = spuMap.get(cart.getSpuId());
ProductSkuRespDTO sku = skuMap.get(cart.getSkuId());
- cartVO.setSpu(convert(spu)).setSku(convert(sku));
+ cartVO.setSpu(BeanUtils.toBean(spu, AppProductSpuBaseRespVO.class))
+ .setSku(BeanUtils.toBean(sku, AppProductSkuBaseRespVO.class));
// 如果 SPU 不存在,或者下架,或者库存不足,说明是无效的
if (spu == null
|| !ProductSpuStatusEnum.isEnable(spu.getStatus())
|| spu.getStock() <= 0) {
- cartVO.setSelected(false); // 强制设置成不可选中
invalidList.add(cartVO);
} else {
- // 虽然 SKU 可能也会不存在,但是可以通过购物车重新选择
validList.add(cartVO);
}
});
return new AppCartListRespVO().setValidList(validList).setInvalidList(invalidList);
}
- AppProductSpuBaseRespVO convert(ProductSpuRespDTO spu);
- AppProductSkuBaseRespVO convert(ProductSkuRespDTO sku);
}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/aftersale/AfterSaleMapper.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/aftersale/AfterSaleMapper.java
index 341dabc45e..9901792e37 100644
--- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/aftersale/AfterSaleMapper.java
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/aftersale/AfterSaleMapper.java
@@ -1,10 +1,10 @@
package cn.iocoder.yudao.module.trade.dal.mysql.aftersale;
-import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSalePageReqVO;
+import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSalePageReqVO;
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.ibatis.annotations.Mapper;
@@ -27,9 +27,10 @@ public interface AfterSaleMapper extends BaseMapperX {
.orderByDesc(AfterSaleDO::getId));
}
- default PageResult selectPage(Long userId, PageParam pageParam) {
- return selectPage(pageParam, new LambdaQueryWrapperX()
- .eqIfPresent(AfterSaleDO::getUserId, userId)
+ default PageResult selectPage(Long userId, AppAfterSalePageReqVO pageReqVO) {
+ return selectPage(pageReqVO, new LambdaQueryWrapperX()
+ .eq(AfterSaleDO::getUserId, userId)
+ .inIfPresent(AfterSaleDO::getStatus, pageReqVO.getStatuses())
.orderByDesc(AfterSaleDO::getId));
}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/AfterSaleService.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/AfterSaleService.java
index 1a0c1e95d9..486a68b7c0 100644
--- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/AfterSaleService.java
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/AfterSaleService.java
@@ -1,12 +1,12 @@
package cn.iocoder.yudao.module.trade.service.aftersale;
-import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSaleDisagreeReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSalePageReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSaleRefuseReqVO;
import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleCreateReqVO;
import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleDeliveryReqVO;
+import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSalePageReqVO;
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO;
/**
@@ -28,10 +28,10 @@ public interface AfterSaleService {
* 【会员】获得售后订单分页
*
* @param userId 用户编号
- * @param pageParam 分页参数
+ * @param pageReqVO 分页参数
* @return 售后订单分页
*/
- PageResult getAfterSalePage(Long userId, PageParam pageParam);
+ PageResult getAfterSalePage(Long userId, AppAfterSalePageReqVO pageReqVO);
/**
* 【会员】获得售后单
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/AfterSaleServiceImpl.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/AfterSaleServiceImpl.java
index bcac6b5b6a..915f253f0f 100644
--- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/AfterSaleServiceImpl.java
+++ b/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;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
-import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.module.pay.api.refund.PayRefundApi;
@@ -16,6 +15,7 @@ import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSalePage
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSaleRefuseReqVO;
import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleCreateReqVO;
import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleDeliveryReqVO;
+import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSalePageReqVO;
import cn.iocoder.yudao.module.trade.convert.aftersale.AfterSaleConvert;
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;
@@ -36,6 +36,7 @@ import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties
import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService;
import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService;
import cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService;
+import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
@@ -44,7 +45,6 @@ import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.validation.annotation.Validated;
-import jakarta.annotation.Resource;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -87,8 +87,8 @@ public class AfterSaleServiceImpl implements AfterSaleService {
}
@Override
- public PageResult getAfterSalePage(Long userId, PageParam pageParam) {
- return tradeAfterSaleMapper.selectPage(userId, pageParam);
+ public PageResult getAfterSalePage(Long userId, AppAfterSalePageReqVO pageReqVO) {
+ return tradeAfterSaleMapper.selectPage(userId, pageReqVO);
}
@Override
@@ -386,7 +386,7 @@ public class AfterSaleServiceImpl implements AfterSaleService {
public void afterCommit() {
// 创建退款单
PayRefundCreateReqDTO createReqDTO = AfterSaleConvert.INSTANCE.convert(userIp, afterSale, tradeOrderProperties)
- .setReason(StrUtil.format("退款【{}】", afterSale.getSpuName()));;
+ .setReason(StrUtil.format("退款【{}】", afterSale.getSpuName()));
Long payRefundId = payRefundApi.createRefund(createReqDTO);
// 更新售后单的退款单号
tradeAfterSaleMapper.updateById(new AfterSaleDO().setId(afterSale.getId()).setPayRefundId(payRefundId));
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java
index 92036fa7e7..d47da484e2 100644
--- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java
+++ b/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 {
if (ObjectUtil.notEqual(order.getStatus(), TradeOrderStatusEnum.UNPAID.getStatus())) {
throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID);
}
+ // 1.3 校验是否支持延迟(不允许取消)
+ if (TradeOrderStatusEnum.isUnpaid(order.getStatus())) {
+ PayOrderRespDTO payOrder = payOrderApi.getOrder(order.getPayOrderId());
+ if (payOrder != null && PayOrderStatusEnum.isSuccess(payOrder.getStatus())) {
+ log.warn("[cancelOrderByMember][order({}) 支付单已支付(支付回调延迟),不支持取消]", order.getId());
+ throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID);
+ }
+ }
// 2. 取消订单
cancelOrder0(order, TradeOrderCancelTypeEnum.MEMBER_CANCEL);
@@ -581,6 +589,15 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
@Transactional(rollbackFor = Exception.class)
@TradeOrderLog(operateType = TradeOrderOperateTypeEnum.SYSTEM_CANCEL)
public void cancelOrderBySystem(TradeOrderDO order) {
+ // 校验是否支持延迟(不允许取消)
+ if (TradeOrderStatusEnum.isUnpaid(order.getStatus())) {
+ PayOrderRespDTO payOrder = payOrderApi.getOrder(order.getPayOrderId());
+ if (payOrder != null && PayOrderStatusEnum.isSuccess(payOrder.getStatus())) {
+ log.warn("[cancelOrderBySystem][order({}) 支付单已支付(支付回调延迟),不支持取消]", order.getId());
+ return;
+ }
+ }
+
cancelOrder0(order, TradeOrderCancelTypeEnum.PAY_TIMEOUT);
}
@@ -895,12 +912,11 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
if (order == null) {
throw exception(ORDER_NOT_FOUND);
}
-
// 1.3 校验订单是否支付
if (!order.getPayStatus()) {
throw exception(ORDER_CANCEL_PAID_FAIL, "已支付");
}
- // 1.3 校验订单是否未退款
+ // 1.4 校验订单是否未退款
if (ObjUtil.notEqual(TradeOrderRefundStatusEnum.NONE.getStatus(), order.getRefundStatus())) {
throw exception(ORDER_CANCEL_PAID_FAIL, "未退款");
}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeBrokerageOrderHandler.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeBrokerageOrderHandler.java
index 026d98c238..36d31402eb 100644
--- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeBrokerageOrderHandler.java
+++ b/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;
import jakarta.annotation.Resource;
import java.util.List;
+import java.util.Map;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
@@ -101,13 +102,17 @@ public class TradeBrokerageOrderHandler implements TradeOrderHandler {
protected void addBrokerage(Long userId, List orderItems) {
MemberUserRespDTO user = memberUserApi.getUser(userId);
Assert.notNull(user);
- ProductSpuRespDTO spu = productSpuApi.getSpu(orderItems.get(0).getSpuId());
- Assert.notNull(spu);
- ProductSkuRespDTO sku = productSkuApi.getSku(orderItems.get(0).getSkuId());
+ Map spusMap = productSpuApi.getSpuMap(convertList(orderItems, TradeOrderItemDO::getSpuId));
+ Map skusMap = productSkuApi.getSkuMap(convertList(orderItems, TradeOrderItemDO::getSkuId));
// 每一个订单项,都会去生成分销记录
- List addList = convertList(orderItems,
- item -> TradeOrderConvert.INSTANCE.convert(user, item, spu, sku));
+ List addList = convertList(orderItems, item -> {
+ ProductSpuRespDTO spu = spusMap.get(item.getSpuId());
+ Assert.notNull(spu);
+ ProductSkuRespDTO sku = skusMap.get(item.getSkuId());
+ Assert.notNull(sku);
+ return TradeOrderConvert.INSTANCE.convert(user, item, spu, sku);
+ });
brokerageRecordService.addBrokerage(userId, BrokerageRecordBizTypeEnum.ORDER, addList);
}
diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java
index 6db0e567c4..6ed77aeef7 100644
--- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java
+++ b/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 {
ErrorCode USER_COUNT_MAX = new ErrorCode(1_002_003_008, "创建用户失败,原因:超过租户最大租户配额({})!");
ErrorCode USER_IMPORT_INIT_PASSWORD = new ErrorCode(1_002_003_009, "初始密码不能为空");
ErrorCode USER_MOBILE_NOT_EXISTS = new ErrorCode(1_002_003_010, "该手机号尚未注册");
+ ErrorCode USER_REGISTER_DISABLED = new ErrorCode(1_002_003_011, "注册功能已关闭");
// ========== 部门模块 1-002-004-000 ==========
ErrorCode DEPT_NAME_DUPLICATE = new ErrorCode(1_002_004_000, "已经存在该名字的部门");
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImpl.java
index c3b09ec5a9..5060b16e05 100755
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImpl.java
+++ b/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;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
import cn.iocoder.yudao.framework.tenant.config.TenantProperties;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
@@ -96,6 +97,7 @@ public class TenantServiceImpl implements TenantService {
@Override
@DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换
+ @DataPermission(enable = false) // 参见 https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/1154 说明
public Long createTenant(TenantSaveReqVO createReqVO) {
// 校验租户名称是否重复
validTenantNameDuplicate(createReqVO.getName(), null);
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java
index 09e7b61e1f..eaa5710074 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java
+++ b/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;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.IoUtil;
+import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
@@ -61,6 +62,8 @@ public class AdminUserServiceImpl implements AdminUserService {
static final String USER_INIT_PASSWORD_KEY = "system.user.init-password";
+ static final String USER_REGISTER_ENABLED_KEY = "system.user.register-enabled";
+
@Resource
private AdminUserMapper userMapper;
@@ -117,14 +120,18 @@ public class AdminUserServiceImpl implements AdminUserService {
@Override
public Long registerUser(AuthRegisterReqVO registerReqVO) {
- // 1.1 校验账户配合
+ // 1.1 校验是否开启注册
+ if (ObjUtil.notEqual(configApi.getConfigValueByKey(USER_REGISTER_ENABLED_KEY), "true")) {
+ throw exception(USER_REGISTER_DISABLED);
+ }
+ // 1.2 校验账户配合
tenantService.handleTenantInfo(tenant -> {
long count = userMapper.selectCount();
if (count >= tenant.getAccountCount()) {
throw exception(USER_COUNT_MAX, tenant.getAccountCount());
}
});
- // 1.2 校验正确性
+ // 1.3 校验正确性
validateUserForCreateOrUpdate(null, registerReqVO.getUsername(), null, null, null, null);
// 2. 插入用户
diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImplTest.java
index c811c3c741..5286f7a0f1 100644
--- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImplTest.java
+++ b/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 {
UserProfileUpdateReqVO reqVO = randomPojo(UserProfileUpdateReqVO.class, o -> {
o.setMobile(randomString());
o.setSex(RandomUtil.randomEle(SexEnum.values()).getSex());
+ o.setAvatar(randomURL());
});
// 调用