Commit 2876c7ce1626d93ace59aa4c539f5a2154bf7e08

Authored by YunaiV
2 parents 24195750 3ea63b5b

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

# Conflicts:
#	yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/coupon/AppCouponController.java
#	yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponMapper.java
#	yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponService.java
#	yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java
#	yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityServiceImpl.java
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeRewardActivityPriceCalculator.java
Showing 70 changed files with 671 additions and 890 deletions
@@ -15,12 +15,12 @@ @@ -15,12 +15,12 @@
15 <!-- 各种 module 拓展 --> 15 <!-- 各种 module 拓展 -->
16 <module>yudao-module-system</module> 16 <module>yudao-module-system</module>
17 <module>yudao-module-infra</module> 17 <module>yudao-module-infra</module>
18 - <module>yudao-module-member</module> 18 +<!-- <module>yudao-module-member</module>-->
19 <!-- <module>yudao-module-bpm</module>--> 19 <!-- <module>yudao-module-bpm</module>-->
20 <!-- <module>yudao-module-report</module>--> 20 <!-- <module>yudao-module-report</module>-->
21 <!-- <module>yudao-module-mp</module>--> 21 <!-- <module>yudao-module-mp</module>-->
22 - <module>yudao-module-pay</module>  
23 - <module>yudao-module-mall</module> 22 +<!-- <module>yudao-module-pay</module>-->
  23 +<!-- <module>yudao-module-mall</module>-->
24 <!-- <module>yudao-module-crm</module>--> 24 <!-- <module>yudao-module-crm</module>-->
25 <!-- <module>yudao-module-erp</module>--> 25 <!-- <module>yudao-module-erp</module>-->
26 <!-- <module>yudao-module-ai</module>--> 26 <!-- <module>yudao-module-ai</module>-->
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/job/TenantJobAspect.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.tenant.core.job; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.tenant.core.job;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.exceptions.ExceptionUtil; 4 import cn.hutool.core.exceptions.ExceptionUtil;
  5 +import cn.hutool.core.util.StrUtil;
5 import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 6 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
6 import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService; 7 import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService;
7 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; 8 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
@@ -44,7 +45,8 @@ public class TenantJobAspect { @@ -44,7 +45,8 @@ public class TenantJobAspect {
44 // TODO 芋艿:先通过 parallel 实现并行;1)多个租户,是一条执行日志;2)异常的情况 45 // TODO 芋艿:先通过 parallel 实现并行;1)多个租户,是一条执行日志;2)异常的情况
45 TenantUtils.execute(tenantId, () -> { 46 TenantUtils.execute(tenantId, () -> {
46 try { 47 try {
47 - joinPoint.proceed(); 48 + Object result = joinPoint.proceed();
  49 + results.put(tenantId, StrUtil.toStringOrNull(result));
48 } catch (Throwable e) { 50 } catch (Throwable e) {
49 log.error("[execute][租户({}) 执行 Job 发生异常", tenantId, e); 51 log.error("[execute][租户({}) 执行 Job 发生异常", tenantId, e);
50 results.put(tenantId, ExceptionUtil.getRootCauseMessage(e)); 52 results.put(tenantId, ExceptionUtil.getRootCauseMessage(e));
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/operatelog/core/service/LogRecordServiceImpl.java
@@ -11,7 +11,6 @@ import com.mzt.logapi.service.ILogRecordService; @@ -11,7 +11,6 @@ import com.mzt.logapi.service.ILogRecordService;
11 import jakarta.annotation.Resource; 11 import jakarta.annotation.Resource;
12 import jakarta.servlet.http.HttpServletRequest; 12 import jakarta.servlet.http.HttpServletRequest;
13 import lombok.extern.slf4j.Slf4j; 13 import lombok.extern.slf4j.Slf4j;
14 -import org.springframework.scheduling.annotation.Async;  
15 14
16 import java.util.List; 15 import java.util.List;
17 16
@@ -29,7 +28,6 @@ public class LogRecordServiceImpl implements ILogRecordService { @@ -29,7 +28,6 @@ public class LogRecordServiceImpl implements ILogRecordService {
29 private OperateLogApi operateLogApi; 28 private OperateLogApi operateLogApi;
30 29
31 @Override 30 @Override
32 - @Async  
33 public void record(LogRecord logRecord) { 31 public void record(LogRecord logRecord) {
34 OperateLogCreateReqDTO reqDTO = new OperateLogCreateReqDTO(); 32 OperateLogCreateReqDTO reqDTO = new OperateLogCreateReqDTO();
35 try { 33 try {
@@ -42,7 +40,7 @@ public class LogRecordServiceImpl implements ILogRecordService { @@ -42,7 +40,7 @@ public class LogRecordServiceImpl implements ILogRecordService {
42 fillRequestFields(reqDTO); 40 fillRequestFields(reqDTO);
43 41
44 // 2. 异步记录日志 42 // 2. 异步记录日志
45 - operateLogApi.createOperateLog(reqDTO); 43 + operateLogApi.createOperateLogAsync(reqDTO);
46 } catch (Throwable ex) { 44 } catch (Throwable ex) {
47 // 由于 @Async 异步调用,这里打印下日志,更容易跟进 45 // 由于 @Async 异步调用,这里打印下日志,更容易跟进
48 log.error("[record][url({}) log({}) 发生异常]", reqDTO.getRequestUrl(), reqDTO, ex); 46 log.error("[record][url({}) log({}) 发生异常]", reqDTO.getRequestUrl(), reqDTO, ex);
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/config/YudaoApiLogAutoConfiguration.java
@@ -2,15 +2,10 @@ package cn.iocoder.yudao.framework.apilog.config; @@ -2,15 +2,10 @@ package cn.iocoder.yudao.framework.apilog.config;
2 2
3 import cn.iocoder.yudao.framework.apilog.core.filter.ApiAccessLogFilter; 3 import cn.iocoder.yudao.framework.apilog.core.filter.ApiAccessLogFilter;
4 import cn.iocoder.yudao.framework.apilog.core.interceptor.ApiAccessLogInterceptor; 4 import cn.iocoder.yudao.framework.apilog.core.interceptor.ApiAccessLogInterceptor;
5 -import cn.iocoder.yudao.framework.apilog.core.service.ApiAccessLogFrameworkService;  
6 -import cn.iocoder.yudao.framework.apilog.core.service.ApiAccessLogFrameworkServiceImpl;  
7 -import cn.iocoder.yudao.framework.apilog.core.service.ApiErrorLogFrameworkService;  
8 -import cn.iocoder.yudao.framework.apilog.core.service.ApiErrorLogFrameworkServiceImpl;  
9 import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum; 5 import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;
10 import cn.iocoder.yudao.framework.web.config.WebProperties; 6 import cn.iocoder.yudao.framework.web.config.WebProperties;
11 import cn.iocoder.yudao.framework.web.config.YudaoWebAutoConfiguration; 7 import cn.iocoder.yudao.framework.web.config.YudaoWebAutoConfiguration;
12 import cn.iocoder.yudao.module.infra.api.logger.ApiAccessLogApi; 8 import cn.iocoder.yudao.module.infra.api.logger.ApiAccessLogApi;
13 -import cn.iocoder.yudao.module.infra.api.logger.ApiErrorLogApi;  
14 import jakarta.servlet.Filter; 9 import jakarta.servlet.Filter;
15 import org.springframework.beans.factory.annotation.Value; 10 import org.springframework.beans.factory.annotation.Value;
16 import org.springframework.boot.autoconfigure.AutoConfiguration; 11 import org.springframework.boot.autoconfigure.AutoConfiguration;
@@ -23,18 +18,6 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @@ -23,18 +18,6 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
23 @AutoConfiguration(after = YudaoWebAutoConfiguration.class) 18 @AutoConfiguration(after = YudaoWebAutoConfiguration.class)
24 public class YudaoApiLogAutoConfiguration implements WebMvcConfigurer { 19 public class YudaoApiLogAutoConfiguration implements WebMvcConfigurer {
25 20
26 - @Bean  
27 - @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")  
28 - public ApiAccessLogFrameworkService apiAccessLogFrameworkService(ApiAccessLogApi apiAccessLogApi) {  
29 - return new ApiAccessLogFrameworkServiceImpl(apiAccessLogApi);  
30 - }  
31 -  
32 - @Bean  
33 - @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")  
34 - public ApiErrorLogFrameworkService apiErrorLogFrameworkService(ApiErrorLogApi apiErrorLogApi) {  
35 - return new ApiErrorLogFrameworkServiceImpl(apiErrorLogApi);  
36 - }  
37 -  
38 /** 21 /**
39 * 创建 ApiAccessLogFilter Bean,记录 API 请求日志 22 * 创建 ApiAccessLogFilter Bean,记录 API 请求日志
40 */ 23 */
@@ -42,8 +25,8 @@ public class YudaoApiLogAutoConfiguration implements WebMvcConfigurer { @@ -42,8 +25,8 @@ public class YudaoApiLogAutoConfiguration implements WebMvcConfigurer {
42 @ConditionalOnProperty(prefix = "yudao.access-log", value = "enable", matchIfMissing = true) // 允许使用 yudao.access-log.enable=false 禁用访问日志 25 @ConditionalOnProperty(prefix = "yudao.access-log", value = "enable", matchIfMissing = true) // 允许使用 yudao.access-log.enable=false 禁用访问日志
43 public FilterRegistrationBean<ApiAccessLogFilter> apiAccessLogFilter(WebProperties webProperties, 26 public FilterRegistrationBean<ApiAccessLogFilter> apiAccessLogFilter(WebProperties webProperties,
44 @Value("${spring.application.name}") String applicationName, 27 @Value("${spring.application.name}") String applicationName,
45 - ApiAccessLogFrameworkService apiAccessLogFrameworkService) {  
46 - ApiAccessLogFilter filter = new ApiAccessLogFilter(webProperties, applicationName, apiAccessLogFrameworkService); 28 + ApiAccessLogApi apiAccessLogApi) {
  29 + ApiAccessLogFilter filter = new ApiAccessLogFilter(webProperties, applicationName, apiAccessLogApi);
47 return createFilterBean(filter, WebFilterOrderEnum.API_ACCESS_LOG_FILTER); 30 return createFilterBean(filter, WebFilterOrderEnum.API_ACCESS_LOG_FILTER);
48 } 31 }
49 32
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/filter/ApiAccessLogFilter.java
@@ -9,7 +9,6 @@ import cn.hutool.core.util.BooleanUtil; @@ -9,7 +9,6 @@ import cn.hutool.core.util.BooleanUtil;
9 import cn.hutool.core.util.StrUtil; 9 import cn.hutool.core.util.StrUtil;
10 import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; 10 import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
11 import cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum; 11 import cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum;
12 -import cn.iocoder.yudao.framework.apilog.core.service.ApiAccessLogFrameworkService;  
13 import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; 12 import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
14 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 13 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
15 import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 14 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
@@ -18,6 +17,7 @@ import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; @@ -18,6 +17,7 @@ import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
18 import cn.iocoder.yudao.framework.web.config.WebProperties; 17 import cn.iocoder.yudao.framework.web.config.WebProperties;
19 import cn.iocoder.yudao.framework.web.core.filter.ApiRequestFilter; 18 import cn.iocoder.yudao.framework.web.core.filter.ApiRequestFilter;
20 import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; 19 import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
  20 +import cn.iocoder.yudao.module.infra.api.logger.ApiAccessLogApi;
21 import cn.iocoder.yudao.module.infra.api.logger.dto.ApiAccessLogCreateReqDTO; 21 import cn.iocoder.yudao.module.infra.api.logger.dto.ApiAccessLogCreateReqDTO;
22 import com.fasterxml.jackson.databind.JsonNode; 22 import com.fasterxml.jackson.databind.JsonNode;
23 import io.swagger.v3.oas.annotations.Operation; 23 import io.swagger.v3.oas.annotations.Operation;
@@ -36,7 +36,7 @@ import java.time.temporal.ChronoUnit; @@ -36,7 +36,7 @@ import java.time.temporal.ChronoUnit;
36 import java.util.Iterator; 36 import java.util.Iterator;
37 import java.util.Map; 37 import java.util.Map;
38 38
39 -import static cn.iocoder.yudao.framework.apilog.core.interceptor.ApiAccessLogInterceptor.*; 39 +import static cn.iocoder.yudao.framework.apilog.core.interceptor.ApiAccessLogInterceptor.ATTRIBUTE_HANDLER_METHOD;
40 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; 40 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
41 41
42 /** 42 /**
@@ -53,12 +53,12 @@ public class ApiAccessLogFilter extends ApiRequestFilter { @@ -53,12 +53,12 @@ public class ApiAccessLogFilter extends ApiRequestFilter {
53 53
54 private final String applicationName; 54 private final String applicationName;
55 55
56 - private final ApiAccessLogFrameworkService apiAccessLogFrameworkService; 56 + private final ApiAccessLogApi apiAccessLogApi;
57 57
58 - public ApiAccessLogFilter(WebProperties webProperties, String applicationName, ApiAccessLogFrameworkService apiAccessLogFrameworkService) { 58 + public ApiAccessLogFilter(WebProperties webProperties, String applicationName, ApiAccessLogApi apiAccessLogApi) {
59 super(webProperties); 59 super(webProperties);
60 this.applicationName = applicationName; 60 this.applicationName = applicationName;
61 - this.apiAccessLogFrameworkService = apiAccessLogFrameworkService; 61 + this.apiAccessLogApi = apiAccessLogApi;
62 } 62 }
63 63
64 @Override 64 @Override
@@ -91,7 +91,7 @@ public class ApiAccessLogFilter extends ApiRequestFilter { @@ -91,7 +91,7 @@ public class ApiAccessLogFilter extends ApiRequestFilter {
91 if (!enable) { 91 if (!enable) {
92 return; 92 return;
93 } 93 }
94 - apiAccessLogFrameworkService.createApiAccessLog(accessLog); 94 + apiAccessLogApi.createApiAccessLogAsync(accessLog);
95 } catch (Throwable th) { 95 } catch (Throwable th) {
96 log.error("[createApiAccessLog][url({}) log({}) 发生异常]", request.getRequestURI(), toJsonString(accessLog), th); 96 log.error("[createApiAccessLog][url({}) log({}) 发生异常]", request.getRequestURI(), toJsonString(accessLog), th);
97 } 97 }
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiAccessLogFrameworkService.java deleted
1 -package cn.iocoder.yudao.framework.apilog.core.service;  
2 -  
3 -import cn.iocoder.yudao.module.infra.api.logger.dto.ApiAccessLogCreateReqDTO;  
4 -  
5 -/**  
6 - * API 访问日志 Framework Service 接口  
7 - *  
8 - * @author 芋道源码  
9 - */  
10 -public interface ApiAccessLogFrameworkService {  
11 -  
12 - /**  
13 - * 创建 API 访问日志  
14 - *  
15 - * @param reqDTO API 访问日志  
16 - */  
17 - void createApiAccessLog(ApiAccessLogCreateReqDTO reqDTO);  
18 -  
19 -}  
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiAccessLogFrameworkServiceImpl.java deleted
1 -package cn.iocoder.yudao.framework.apilog.core.service;  
2 -  
3 -import cn.iocoder.yudao.module.infra.api.logger.ApiAccessLogApi;  
4 -import cn.iocoder.yudao.module.infra.api.logger.dto.ApiAccessLogCreateReqDTO;  
5 -import lombok.RequiredArgsConstructor;  
6 -import lombok.extern.slf4j.Slf4j;  
7 -import org.springframework.scheduling.annotation.Async;  
8 -  
9 -/**  
10 - * API 访问日志 Framework Service 实现类  
11 - *  
12 - * 基于 {@link ApiAccessLogApi} 服务,记录访问日志  
13 - *  
14 - * @author 芋道源码  
15 - */  
16 -@RequiredArgsConstructor  
17 -@Slf4j  
18 -public class ApiAccessLogFrameworkServiceImpl implements ApiAccessLogFrameworkService {  
19 -  
20 - private final ApiAccessLogApi apiAccessLogApi;  
21 -  
22 - @Override  
23 - @Async  
24 - public void createApiAccessLog(ApiAccessLogCreateReqDTO reqDTO) {  
25 - try {  
26 - apiAccessLogApi.createApiAccessLog(reqDTO);  
27 - } catch (Throwable ex) {  
28 - // 由于 @Async 异步调用,这里打印下日志,更容易跟进  
29 - log.error("[createApiAccessLog][url({}) log({}) 发生异常]", reqDTO.getRequestUrl(), reqDTO, ex);  
30 - }  
31 - }  
32 -  
33 -}  
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLogFrameworkService.java deleted
1 -package cn.iocoder.yudao.framework.apilog.core.service;  
2 -  
3 -import cn.iocoder.yudao.module.infra.api.logger.dto.ApiErrorLogCreateReqDTO;  
4 -  
5 -/**  
6 - * API 错误日志 Framework Service 接口  
7 - *  
8 - * @author 芋道源码  
9 - */  
10 -public interface ApiErrorLogFrameworkService {  
11 -  
12 - /**  
13 - * 创建 API 错误日志  
14 - *  
15 - * @param reqDTO API 错误日志  
16 - */  
17 - void createApiErrorLog(ApiErrorLogCreateReqDTO reqDTO);  
18 -  
19 -}  
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLogFrameworkServiceImpl.java deleted
1 -package cn.iocoder.yudao.framework.apilog.core.service;  
2 -  
3 -import cn.iocoder.yudao.module.infra.api.logger.ApiErrorLogApi;  
4 -import cn.iocoder.yudao.module.infra.api.logger.dto.ApiErrorLogCreateReqDTO;  
5 -import lombok.RequiredArgsConstructor;  
6 -import lombok.extern.slf4j.Slf4j;  
7 -import org.springframework.scheduling.annotation.Async;  
8 -  
9 -/**  
10 - * API 错误日志 Framework Service 实现类  
11 - *  
12 - * 基于 {@link ApiErrorLogApi} 服务,记录错误日志  
13 - *  
14 - * @author 芋道源码  
15 - */  
16 -@RequiredArgsConstructor  
17 -@Slf4j  
18 -public class ApiErrorLogFrameworkServiceImpl implements ApiErrorLogFrameworkService {  
19 -  
20 - private final ApiErrorLogApi apiErrorLogApi;  
21 -  
22 - @Override  
23 - @Async  
24 - public void createApiErrorLog(ApiErrorLogCreateReqDTO reqDTO) {  
25 - try {  
26 - apiErrorLogApi.createApiErrorLog(reqDTO);  
27 - } catch (Throwable ex) {  
28 - // 由于 @Async 异步调用,这里打印下日志,更容易跟进  
29 - log.error("[createApiErrorLog][url({}) log({}) 发生异常]", reqDTO.getRequestUrl(), reqDTO, ex);  
30 - }  
31 - }  
32 -  
33 -}  
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/YudaoWebAutoConfiguration.java
1 package cn.iocoder.yudao.framework.web.config; 1 package cn.iocoder.yudao.framework.web.config;
2 2
3 -import cn.iocoder.yudao.framework.apilog.core.service.ApiErrorLogFrameworkService;  
4 import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum; 3 import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;
5 import cn.iocoder.yudao.framework.web.core.filter.CacheRequestBodyFilter; 4 import cn.iocoder.yudao.framework.web.core.filter.CacheRequestBodyFilter;
6 import cn.iocoder.yudao.framework.web.core.filter.DemoFilter; 5 import cn.iocoder.yudao.framework.web.core.filter.DemoFilter;
7 import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler; 6 import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
8 import cn.iocoder.yudao.framework.web.core.handler.GlobalResponseBodyHandler; 7 import cn.iocoder.yudao.framework.web.core.handler.GlobalResponseBodyHandler;
9 import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; 8 import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
  9 +import cn.iocoder.yudao.module.infra.api.logger.ApiErrorLogApi;
  10 +import jakarta.annotation.Resource;
  11 +import jakarta.servlet.Filter;
10 import org.springframework.beans.factory.annotation.Value; 12 import org.springframework.beans.factory.annotation.Value;
11 import org.springframework.boot.autoconfigure.AutoConfiguration; 13 import org.springframework.boot.autoconfigure.AutoConfiguration;
12 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 14 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -25,9 +27,6 @@ import org.springframework.web.filter.CorsFilter; @@ -25,9 +27,6 @@ import org.springframework.web.filter.CorsFilter;
25 import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; 27 import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
26 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 28 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
27 29
28 -import jakarta.annotation.Resource;  
29 -import jakarta.servlet.Filter;  
30 -  
31 @AutoConfiguration 30 @AutoConfiguration
32 @EnableConfigurationProperties(WebProperties.class) 31 @EnableConfigurationProperties(WebProperties.class)
33 public class YudaoWebAutoConfiguration implements WebMvcConfigurer { 32 public class YudaoWebAutoConfiguration implements WebMvcConfigurer {
@@ -59,8 +58,9 @@ public class YudaoWebAutoConfiguration implements WebMvcConfigurer { @@ -59,8 +58,9 @@ public class YudaoWebAutoConfiguration implements WebMvcConfigurer {
59 } 58 }
60 59
61 @Bean 60 @Bean
62 - public GlobalExceptionHandler globalExceptionHandler(ApiErrorLogFrameworkService apiErrorLogFrameworkService) {  
63 - return new GlobalExceptionHandler(applicationName, apiErrorLogFrameworkService); 61 + @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
  62 + public GlobalExceptionHandler globalExceptionHandler(ApiErrorLogApi apiErrorLogApi) {
  63 + return new GlobalExceptionHandler(applicationName, apiErrorLogApi);
64 } 64 }
65 65
66 @Bean 66 @Bean
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java
@@ -5,7 +5,6 @@ import cn.hutool.core.map.MapUtil; @@ -5,7 +5,6 @@ import cn.hutool.core.map.MapUtil;
5 import cn.hutool.core.util.ObjUtil; 5 import cn.hutool.core.util.ObjUtil;
6 import cn.hutool.core.util.StrUtil; 6 import cn.hutool.core.util.StrUtil;
7 import cn.hutool.extra.servlet.JakartaServletUtil; 7 import cn.hutool.extra.servlet.JakartaServletUtil;
8 -import cn.iocoder.yudao.framework.apilog.core.service.ApiErrorLogFrameworkService;  
9 import cn.iocoder.yudao.framework.common.exception.ServiceException; 8 import cn.iocoder.yudao.framework.common.exception.ServiceException;
10 import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; 9 import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
11 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 10 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
@@ -14,6 +13,7 @@ import cn.iocoder.yudao.framework.common.util.json.JsonUtils; @@ -14,6 +13,7 @@ import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
14 import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; 13 import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
15 import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; 14 import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
16 import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; 15 import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
  16 +import cn.iocoder.yudao.module.infra.api.logger.ApiErrorLogApi;
17 import cn.iocoder.yudao.module.infra.api.logger.dto.ApiErrorLogCreateReqDTO; 17 import cn.iocoder.yudao.module.infra.api.logger.dto.ApiErrorLogCreateReqDTO;
18 import com.fasterxml.jackson.databind.exc.InvalidFormatException; 18 import com.fasterxml.jackson.databind.exc.InvalidFormatException;
19 import jakarta.servlet.http.HttpServletRequest; 19 import jakarta.servlet.http.HttpServletRequest;
@@ -40,12 +40,7 @@ import java.time.LocalDateTime; @@ -40,12 +40,7 @@ import java.time.LocalDateTime;
40 import java.util.Map; 40 import java.util.Map;
41 import java.util.Set; 41 import java.util.Set;
42 42
43 -import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;  
44 -import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.FORBIDDEN;  
45 -import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR;  
46 -import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.METHOD_NOT_ALLOWED;  
47 -import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.NOT_FOUND;  
48 -import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.NOT_IMPLEMENTED; 43 +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.*;
49 44
50 /** 45 /**
51 * 全局异常处理器,将 Exception 翻译成 CommonResult + 对应的异常编号 46 * 全局异常处理器,将 Exception 翻译成 CommonResult + 对应的异常编号
@@ -65,7 +60,7 @@ public class GlobalExceptionHandler { @@ -65,7 +60,7 @@ public class GlobalExceptionHandler {
65 @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") 60 @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
66 private final String applicationName; 61 private final String applicationName;
67 62
68 - private final ApiErrorLogFrameworkService apiErrorLogFrameworkService; 63 + private final ApiErrorLogApi apiErrorLogApi;
69 64
70 /** 65 /**
71 * 处理所有异常,主要是提供给 Filter 使用 66 * 处理所有异常,主要是提供给 Filter 使用
@@ -288,7 +283,7 @@ public class GlobalExceptionHandler { @@ -288,7 +283,7 @@ public class GlobalExceptionHandler {
288 // 初始化 errorLog 283 // 初始化 errorLog
289 buildExceptionLog(errorLog, req, e); 284 buildExceptionLog(errorLog, req, e);
290 // 执行插入 errorLog 285 // 执行插入 errorLog
291 - apiErrorLogFrameworkService.createApiErrorLog(errorLog); 286 + apiErrorLogApi.createApiErrorLogAsync(errorLog);
292 } catch (Throwable th) { 287 } catch (Throwable th) {
293 log.error("[createExceptionLog][url({}) log({}) 发生异常]", req.getRequestURI(), JsonUtils.toJsonString(errorLog), th); 288 log.error("[createExceptionLog][url({}) log({}) 发生异常]", req.getRequestURI(), JsonUtils.toJsonString(errorLog), th);
294 } 289 }
yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/config/YudaoWebSocketAutoConfiguration.java
@@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.mq.redis.core.RedisMQTemplate; @@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.mq.redis.core.RedisMQTemplate;
5 import cn.iocoder.yudao.framework.websocket.core.handler.JsonWebSocketMessageHandler; 5 import cn.iocoder.yudao.framework.websocket.core.handler.JsonWebSocketMessageHandler;
6 import cn.iocoder.yudao.framework.websocket.core.listener.WebSocketMessageListener; 6 import cn.iocoder.yudao.framework.websocket.core.listener.WebSocketMessageListener;
7 import cn.iocoder.yudao.framework.websocket.core.security.LoginUserHandshakeInterceptor; 7 import cn.iocoder.yudao.framework.websocket.core.security.LoginUserHandshakeInterceptor;
  8 +import cn.iocoder.yudao.framework.websocket.core.security.WebSocketAuthorizeRequestsCustomizer;
8 import cn.iocoder.yudao.framework.websocket.core.sender.kafka.KafkaWebSocketMessageConsumer; 9 import cn.iocoder.yudao.framework.websocket.core.sender.kafka.KafkaWebSocketMessageConsumer;
9 import cn.iocoder.yudao.framework.websocket.core.sender.kafka.KafkaWebSocketMessageSender; 10 import cn.iocoder.yudao.framework.websocket.core.sender.kafka.KafkaWebSocketMessageSender;
10 import cn.iocoder.yudao.framework.websocket.core.sender.local.LocalWebSocketMessageSender; 11 import cn.iocoder.yudao.framework.websocket.core.sender.local.LocalWebSocketMessageSender;
@@ -76,6 +77,11 @@ public class YudaoWebSocketAutoConfiguration { @@ -76,6 +77,11 @@ public class YudaoWebSocketAutoConfiguration {
76 return new WebSocketSessionManagerImpl(); 77 return new WebSocketSessionManagerImpl();
77 } 78 }
78 79
  80 + @Bean
  81 + public WebSocketAuthorizeRequestsCustomizer webSocketAuthorizeRequestsCustomizer(WebSocketProperties webSocketProperties) {
  82 + return new WebSocketAuthorizeRequestsCustomizer(webSocketProperties);
  83 + }
  84 +
79 // ==================== Sender 相关 ==================== 85 // ==================== Sender 相关 ====================
80 86
81 @Configuration 87 @Configuration
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmModelConvert.java
@@ -13,6 +13,7 @@ import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModel @@ -13,6 +13,7 @@ import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModel
13 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO; 13 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO;
14 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO; 14 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
15 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; 15 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
  16 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
16 import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmModelMetaInfoRespDTO; 17 import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmModelMetaInfoRespDTO;
17 import org.flowable.common.engine.impl.db.SuspensionState; 18 import org.flowable.common.engine.impl.db.SuspensionState;
18 import org.flowable.engine.repository.Deployment; 19 import org.flowable.engine.repository.Deployment;
@@ -55,7 +56,7 @@ public interface BpmModelConvert { @@ -55,7 +56,7 @@ public interface BpmModelConvert {
55 BpmModelMetaInfoRespDTO metaInfo = buildMetaInfo(model); 56 BpmModelMetaInfoRespDTO metaInfo = buildMetaInfo(model);
56 BpmModelRespVO modelVO = buildModel0(model, metaInfo, null, null, null, null); 57 BpmModelRespVO modelVO = buildModel0(model, metaInfo, null, null, null, null);
57 if (ArrayUtil.isNotEmpty(bpmnBytes)) { 58 if (ArrayUtil.isNotEmpty(bpmnBytes)) {
58 - modelVO.setBpmnXml(new String(bpmnBytes)); 59 + modelVO.setBpmnXml(BpmnModelUtils.getBpmnXml(bpmnBytes));
59 } 60 }
60 return modelVO; 61 return modelVO;
61 } 62 }
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.util; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.util;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.util.ArrayUtil; 4 import cn.hutool.core.util.ArrayUtil;
  5 +import cn.hutool.core.util.StrUtil;
5 import cn.iocoder.yudao.framework.common.util.number.NumberUtils; 6 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
6 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; 7 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
7 import org.flowable.bpmn.converter.BpmnXMLConverter; 8 import org.flowable.bpmn.converter.BpmnXMLConverter;
@@ -108,7 +109,14 @@ public class BpmnModelUtils { @@ -108,7 +109,14 @@ public class BpmnModelUtils {
108 return null; 109 return null;
109 } 110 }
110 BpmnXMLConverter converter = new BpmnXMLConverter(); 111 BpmnXMLConverter converter = new BpmnXMLConverter();
111 - return new String(converter.convertToXML(model)); 112 + return StrUtil.utf8Str(converter.convertToXML(model));
  113 + }
  114 +
  115 + public static String getBpmnXml(byte[] bpmnBytes) {
  116 + if (ArrayUtil.isEmpty(bpmnBytes)) {
  117 + return null;
  118 + }
  119 + return StrUtil.utf8Str(bpmnBytes);
112 } 120 }
113 121
114 // ========== 遍历相关的方法 ========== 122 // ========== 遍历相关的方法 ==========
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java
@@ -92,7 +92,7 @@ public interface CrmReceivableMapper extends BaseMapperX&lt;CrmReceivableDO&gt; { @@ -92,7 +92,7 @@ public interface CrmReceivableMapper extends BaseMapperX&lt;CrmReceivableDO&gt; {
92 List<Map<String, Object>> result = selectMaps(new QueryWrapper<CrmReceivableDO>() 92 List<Map<String, Object>> result = selectMaps(new QueryWrapper<CrmReceivableDO>()
93 .select("contract_id, SUM(price) AS total_price") 93 .select("contract_id, SUM(price) AS total_price")
94 .in("audit_status", CrmAuditStatusEnum.DRAFT.getStatus(), // 草稿 + 审批中 + 审批通过 94 .in("audit_status", CrmAuditStatusEnum.DRAFT.getStatus(), // 草稿 + 审批中 + 审批通过
95 - CrmAuditStatusEnum.PROCESS, CrmAuditStatusEnum.APPROVE.getStatus()) 95 + CrmAuditStatusEnum.PROCESS.getStatus(), CrmAuditStatusEnum.APPROVE.getStatus())
96 .groupBy("contract_id") 96 .groupBy("contract_id")
97 .in("contract_id", contractIds)); 97 .in("contract_id", contractIds));
98 // 获得金额 98 // 获得金额
yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/api/logger/ApiAccessLogApi.java
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.infra.api.logger; @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.infra.api.logger;
3 import cn.iocoder.yudao.module.infra.api.logger.dto.ApiAccessLogCreateReqDTO; 3 import cn.iocoder.yudao.module.infra.api.logger.dto.ApiAccessLogCreateReqDTO;
4 4
5 import jakarta.validation.Valid; 5 import jakarta.validation.Valid;
  6 +import org.springframework.scheduling.annotation.Async;
6 7
7 /** 8 /**
8 * API 访问日志的 API 接口 9 * API 访问日志的 API 接口
@@ -18,4 +19,14 @@ public interface ApiAccessLogApi { @@ -18,4 +19,14 @@ public interface ApiAccessLogApi {
18 */ 19 */
19 void createApiAccessLog(@Valid ApiAccessLogCreateReqDTO createDTO); 20 void createApiAccessLog(@Valid ApiAccessLogCreateReqDTO createDTO);
20 21
  22 + /**
  23 + * 【异步】创建 API 访问日志
  24 + *
  25 + * @param createDTO 访问日志 DTO
  26 + */
  27 + @Async
  28 + default void createApiAccessLogAsync(ApiAccessLogCreateReqDTO createDTO) {
  29 + createApiAccessLog(createDTO);
  30 + }
  31 +
21 } 32 }
yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/api/logger/ApiErrorLogApi.java
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.infra.api.logger; @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.infra.api.logger;
3 import cn.iocoder.yudao.module.infra.api.logger.dto.ApiErrorLogCreateReqDTO; 3 import cn.iocoder.yudao.module.infra.api.logger.dto.ApiErrorLogCreateReqDTO;
4 4
5 import jakarta.validation.Valid; 5 import jakarta.validation.Valid;
  6 +import org.springframework.scheduling.annotation.Async;
6 7
7 /** 8 /**
8 * API 错误日志的 API 接口 9 * API 错误日志的 API 接口
@@ -18,4 +19,14 @@ public interface ApiErrorLogApi { @@ -18,4 +19,14 @@ public interface ApiErrorLogApi {
18 */ 19 */
19 void createApiErrorLog(@Valid ApiErrorLogCreateReqDTO createDTO); 20 void createApiErrorLog(@Valid ApiErrorLogCreateReqDTO createDTO);
20 21
  22 + /**
  23 + * 【异步】创建 API 异常日志
  24 + *
  25 + * @param createDTO 异常日志 DTO
  26 + */
  27 + @Async
  28 + default void createApiErrorLogAsync(ApiErrorLogCreateReqDTO createDTO) {
  29 + createApiErrorLog(createDTO);
  30 + }
  31 +
21 } 32 }
yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants.java
@@ -22,7 +22,7 @@ public interface ErrorCodeConstants { @@ -22,7 +22,7 @@ public interface ErrorCodeConstants {
22 ErrorCode JOB_CHANGE_STATUS_EQUALS = new ErrorCode(1_001_001_003, "定时任务已经处于该状态,无需修改"); 22 ErrorCode JOB_CHANGE_STATUS_EQUALS = new ErrorCode(1_001_001_003, "定时任务已经处于该状态,无需修改");
23 ErrorCode JOB_UPDATE_ONLY_NORMAL_STATUS = new ErrorCode(1_001_001_004, "只有开启状态的任务,才可以修改"); 23 ErrorCode JOB_UPDATE_ONLY_NORMAL_STATUS = new ErrorCode(1_001_001_004, "只有开启状态的任务,才可以修改");
24 ErrorCode JOB_CRON_EXPRESSION_VALID = new ErrorCode(1_001_001_005, "CRON 表达式不正确"); 24 ErrorCode JOB_CRON_EXPRESSION_VALID = new ErrorCode(1_001_001_005, "CRON 表达式不正确");
25 - ErrorCode JOB_HANDLER_BEAN_NOT_EXISTS = new ErrorCode(1_001_001_006, "定时任务的处理器 Bean 不存在"); 25 + ErrorCode JOB_HANDLER_BEAN_NOT_EXISTS = new ErrorCode(1_001_001_006, "定时任务的处理器 Bean 不存在,注意 Bean 默认首字母小写");
26 ErrorCode JOB_HANDLER_BEAN_TYPE_ERROR = new ErrorCode(1_001_001_007, "定时任务的处理器 Bean 类型不正确,未实现 JobHandler 接口"); 26 ErrorCode JOB_HANDLER_BEAN_TYPE_ERROR = new ErrorCode(1_001_001_007, "定时任务的处理器 Bean 类型不正确,未实现 JobHandler 接口");
27 27
28 // ========== API 错误日志 1-001-002-000 ========== 28 // ========== API 错误日志 1-001-002-000 ==========
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenFrontTypeEnum.java
@@ -14,7 +14,6 @@ public enum CodegenFrontTypeEnum { @@ -14,7 +14,6 @@ public enum CodegenFrontTypeEnum {
14 14
15 VUE2(10), // Vue2 Element UI 标准模版 15 VUE2(10), // Vue2 Element UI 标准模版
16 VUE3(20), // Vue3 Element Plus 标准模版 16 VUE3(20), // Vue3 Element Plus 标准模版
17 - VUE3_SCHEMA(21), // Vue3 Element Plus Schema 模版  
18 VUE3_VBEN(30), // Vue3 VBEN 模版 17 VUE3_VBEN(30), // Vue3 VBEN 模版
19 ; 18 ;
20 19
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java
@@ -135,15 +135,6 @@ public class CodegenEngine { @@ -135,15 +135,6 @@ public class CodegenEngine {
135 vue3FilePath("views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}List.vue")) 135 vue3FilePath("views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}List.vue"))
136 .put(CodegenFrontTypeEnum.VUE3.getType(), vue3TemplatePath("api/api.ts"), 136 .put(CodegenFrontTypeEnum.VUE3.getType(), vue3TemplatePath("api/api.ts"),
137 vue3FilePath("api/${table.moduleName}/${table.businessName}/index.ts")) 137 vue3FilePath("api/${table.moduleName}/${table.businessName}/index.ts"))
138 - // Vue3 Schema 模版  
139 - .put(CodegenFrontTypeEnum.VUE3_SCHEMA.getType(), vue3SchemaTemplatePath("views/data.ts"),  
140 - vue3FilePath("views/${table.moduleName}/${table.businessName}/${classNameVar}.data.ts"))  
141 - .put(CodegenFrontTypeEnum.VUE3_SCHEMA.getType(), vue3SchemaTemplatePath("views/index.vue"),  
142 - vue3FilePath("views/${table.moduleName}/${table.businessName}/index.vue"))  
143 - .put(CodegenFrontTypeEnum.VUE3_SCHEMA.getType(), vue3SchemaTemplatePath("views/form.vue"),  
144 - vue3FilePath("views/${table.moduleName}/${table.businessName}/${simpleClassName}Form.vue"))  
145 - .put(CodegenFrontTypeEnum.VUE3_SCHEMA.getType(), vue3SchemaTemplatePath("api/api.ts"),  
146 - vue3FilePath("api/${table.moduleName}/${table.businessName}/index.ts"))  
147 // Vue3 vben 模版 138 // Vue3 vben 模版
148 .put(CodegenFrontTypeEnum.VUE3_VBEN.getType(), vue3VbenTemplatePath("views/data.ts"), 139 .put(CodegenFrontTypeEnum.VUE3_VBEN.getType(), vue3VbenTemplatePath("views/data.ts"),
149 vue3FilePath("views/${table.moduleName}/${table.businessName}/${classNameVar}.data.ts")) 140 vue3FilePath("views/${table.moduleName}/${table.businessName}/${classNameVar}.data.ts"))
@@ -496,10 +487,6 @@ public class CodegenEngine { @@ -496,10 +487,6 @@ public class CodegenEngine {
496 "src/" + path; 487 "src/" + path;
497 } 488 }
498 489
499 - private static String vue3SchemaTemplatePath(String path) {  
500 - return "codegen/vue3_schema/" + path + ".vm";  
501 - }  
502 -  
503 private static String vue3VbenTemplatePath(String path) { 490 private static String vue3VbenTemplatePath(String path) {
504 return "codegen/vue3_vben/" + path + ".vm"; 491 return "codegen/vue3_vben/" + path + ".vm";
505 } 492 }
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/job/JobServiceImpl.java
@@ -14,6 +14,7 @@ import cn.iocoder.yudao.module.infra.enums.job.JobStatusEnum; @@ -14,6 +14,7 @@ import cn.iocoder.yudao.module.infra.enums.job.JobStatusEnum;
14 import jakarta.annotation.Resource; 14 import jakarta.annotation.Resource;
15 import lombok.extern.slf4j.Slf4j; 15 import lombok.extern.slf4j.Slf4j;
16 import org.quartz.SchedulerException; 16 import org.quartz.SchedulerException;
  17 +import org.springframework.beans.factory.NoSuchBeanDefinitionException;
17 import org.springframework.stereotype.Service; 18 import org.springframework.stereotype.Service;
18 import org.springframework.transaction.annotation.Transactional; 19 import org.springframework.transaction.annotation.Transactional;
19 import org.springframework.validation.annotation.Validated; 20 import org.springframework.validation.annotation.Validated;
@@ -91,13 +92,15 @@ public class JobServiceImpl implements JobService { @@ -91,13 +92,15 @@ public class JobServiceImpl implements JobService {
91 } 92 }
92 93
93 private void validateJobHandlerExists(String handlerName) { 94 private void validateJobHandlerExists(String handlerName) {
94 - Object handler = SpringUtil.getBean(handlerName);  
95 - if (handler == null) { 95 + try {
  96 + Object handler = SpringUtil.getBean(handlerName);
  97 + assert handler != null;
  98 + if (!(handler instanceof JobHandler)) {
  99 + throw exception(JOB_HANDLER_BEAN_TYPE_ERROR);
  100 + }
  101 + } catch (NoSuchBeanDefinitionException e) {
96 throw exception(JOB_HANDLER_BEAN_NOT_EXISTS); 102 throw exception(JOB_HANDLER_BEAN_NOT_EXISTS);
97 } 103 }
98 - if (!(handler instanceof JobHandler)) {  
99 - throw exception(JOB_HANDLER_BEAN_TYPE_ERROR);  
100 - }  
101 } 104 }
102 105
103 @Override 106 @Override
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_schema/api/api.ts.vm deleted
1 -import request from '@/config/axios'  
2 -#set ($baseURL = "/${table.moduleName}/${simpleClassName_strikeCase}")  
3 -  
4 -export interface ${simpleClassName}VO {  
5 - #foreach ($column in $columns)  
6 - #if ($column.createOperation || $column.updateOperation)  
7 - #if(${column.javaType.toLowerCase()} == "long" || ${column.javaType.toLowerCase()} == "integer" || ${column.javaType.toLowerCase()} == "double" || ${column.javaType.toLowerCase()} == "bigdecimal")  
8 - ${column.javaField}: number  
9 - #elseif(${column.javaType.toLowerCase()} == "date" || ${column.javaType.toLowerCase()} == "localdatetime")  
10 - ${column.javaField}: Date  
11 - #else  
12 - ${column.javaField}: ${column.javaType.toLowerCase()}  
13 - #end  
14 - #end  
15 - #end  
16 -}  
17 -  
18 -// 查询${table.classComment}列表  
19 -export const get${simpleClassName}Page = async (params) => {  
20 - return await request.get({ url: '${baseURL}/page', params })  
21 -}  
22 -  
23 -// 查询${table.classComment}详情  
24 -export const get${simpleClassName} = async (id: number) => {  
25 - return await request.get({ url: '${baseURL}/get?id=' + id })  
26 -}  
27 -  
28 -// 新增${table.classComment}  
29 -export const create${simpleClassName} = async (data: ${simpleClassName}VO) => {  
30 - return await request.post({ url: '${baseURL}/create', data })  
31 -}  
32 -  
33 -// 修改${table.classComment}  
34 -export const update${simpleClassName} = async (data: ${simpleClassName}VO) => {  
35 - return await request.put({ url: '${baseURL}/update', data })  
36 -}  
37 -  
38 -// 删除${table.classComment}  
39 -export const delete${simpleClassName} = async (id: number) => {  
40 - return await request.delete({ url: '${baseURL}/delete?id=' + id })  
41 -}  
42 -  
43 -// 导出${table.classComment} Excel  
44 -export const export${simpleClassName}Api = async (params) => {  
45 - return await request.download({ url: '${baseURL}/export-excel', params })  
46 -}  
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_schema/views/data.ts.vm deleted
1 -import type { CrudSchema } from '@/hooks/web/useCrudSchemas'  
2 -import { dateFormatter } from '@/utils/formatTime'  
3 -  
4 -// 表单校验  
5 -export const rules = reactive({  
6 -#foreach ($column in $columns)  
7 -#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键  
8 -#set($comment=$column.columnComment)  
9 - $column.javaField: [required],  
10 -#end  
11 -#end  
12 -})  
13 -  
14 -// CrudSchema https://doc.iocoder.cn/vue3/crud-schema/  
15 -const crudSchemas = reactive<CrudSchema[]>([  
16 -#foreach($column in $columns)  
17 -#if ($column.listOperation || $column.listOperationResult || $column.createOperation || $column.updateOperation)  
18 -#set ($dictType = $column.dictType)  
19 -#set ($javaField = $column.javaField)  
20 -#set ($javaType = $column.javaType)  
21 - {  
22 - label: '${column.columnComment}',  
23 - field: '${column.javaField}',  
24 -## ========= 字典部分 =========  
25 - #if ("" != $dictType)## 有数据字典  
26 - dictType: DICT_TYPE.$dictType.toUpperCase(),  
27 - #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")  
28 - dictClass: 'number',  
29 - #elseif ($javaType == "String")  
30 - dictClass: 'string',  
31 - #elseif ($javaType == "Boolean")  
32 - dictClass: 'boolean',  
33 - #end  
34 - #end  
35 -## ========= Table 表格部分 =========  
36 - #if (!$column.listOperationResult)  
37 - isTable: false,  
38 - #else  
39 - #if ($column.htmlType == "datetime")  
40 - formatter: dateFormatter,  
41 - #end  
42 - #end  
43 -## ========= Search 表格部分 =========  
44 - #if ($column.listOperation)  
45 - isSearch: true,  
46 - #if ($column.htmlType == "datetime")  
47 - search: {  
48 - component: 'DatePicker',  
49 - componentProps: {  
50 - valueFormat: 'YYYY-MM-DD HH:mm:ss',  
51 - type: 'daterange',  
52 - defaultTime: [new Date('1 00:00:00'), new Date('1 23:59:59')]  
53 - }  
54 - },  
55 - #end  
56 - #end  
57 -## ========= Form 表单部分 =========  
58 - #if ((!$column.createOperation && !$column.updateOperation) || $column.primaryKey)  
59 - isForm: false,  
60 - #else  
61 - #if($column.htmlType == "imageUpload")## 图片上传  
62 - form: {  
63 - component: 'UploadImg'  
64 - },  
65 - #elseif($column.htmlType == "fileUpload")## 文件上传  
66 - form: {  
67 - component: 'UploadFile'  
68 - },  
69 - #elseif($column.htmlType == "editor")## 文本编辑器  
70 - form: {  
71 - component: 'Editor',  
72 - componentProps: {  
73 - valueHtml: '',  
74 - height: 200  
75 - }  
76 - },  
77 - #elseif($column.htmlType == "select")## 下拉框  
78 - form: {  
79 - component: 'SelectV2'  
80 - },  
81 - #elseif($column.htmlType == "checkbox")## 多选框  
82 - form: {  
83 - component: 'Checkbox'  
84 - },  
85 - #elseif($column.htmlType == "radio")## 单选框  
86 - form: {  
87 - component: 'Radio'  
88 - },  
89 - #elseif($column.htmlType == "datetime")## 时间框  
90 - form: {  
91 - component: 'DatePicker',  
92 - componentProps: {  
93 - type: 'datetime',  
94 - valueFormat: 'x'  
95 - }  
96 - },  
97 - #elseif($column.htmlType == "textarea")## 文本框  
98 - form: {  
99 - component: 'Input',  
100 - componentProps: {  
101 - type: 'textarea',  
102 - rows: 4  
103 - },  
104 - colProps: {  
105 - span: 24  
106 - }  
107 - },  
108 - #elseif(${javaType.toLowerCase()} == "long" || ${javaType.toLowerCase()} == "integer")## 文本框  
109 - form: {  
110 - component: 'InputNumber',  
111 - value: 0  
112 - },  
113 - #end  
114 - #end  
115 - },  
116 -#end  
117 -#end  
118 - {  
119 - label: '操作',  
120 - field: 'action',  
121 - isForm: false  
122 - }  
123 -])  
124 -export const { allSchemas } = useCrudSchemas(crudSchemas)  
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_schema/views/form.vue.vm deleted
1 -<template>  
2 - <Dialog :title="dialogTitle" v-model="dialogVisible">  
3 - <Form ref="formRef" :schema="allSchemas.formSchema" :rules="rules" v-loading="formLoading" />  
4 - <template #footer>  
5 - <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>  
6 - <el-button @click="dialogVisible = false">取 消</el-button>  
7 - </template>  
8 - </Dialog>  
9 -</template>  
10 -<script setup lang="ts">  
11 -import * as ${simpleClassName}Api from '@/api/${table.moduleName}/${table.businessName}'  
12 -import { rules, allSchemas } from './${classNameVar}.data'  
13 -const { t } = useI18n() // 国际化  
14 -const message = useMessage() // 消息弹窗  
15 -  
16 -const dialogVisible = ref(false) // 弹窗的是否展示  
17 -const dialogTitle = ref('') // 弹窗的标题  
18 -const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用  
19 -const formType = ref('') // 表单的类型:create - 新增;update - 修改  
20 -const formRef = ref() // 表单 Ref  
21 -  
22 -/** 打开弹窗 */  
23 -const open = async (type: string, id?: number) => {  
24 - dialogVisible.value = true  
25 - dialogTitle.value = t('action.' + type)  
26 - formType.value = type  
27 - // 修改时,设置数据  
28 - if (id) {  
29 - formLoading.value = true  
30 - try {  
31 - const data = await ${simpleClassName}Api.get${simpleClassName}(id)  
32 - formRef.value.setValues(data)  
33 - } finally {  
34 - formLoading.value = false  
35 - }  
36 - }  
37 -}  
38 -defineExpose({ open }) // 提供 open 方法,用于打开弹窗  
39 -  
40 -/** 提交表单 */  
41 -const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调  
42 -const submitForm = async () => {  
43 - // 校验表单  
44 - if (!formRef) return  
45 - const valid = await formRef.value.getElFormRef().validate()  
46 - if (!valid) return  
47 - // 提交请求  
48 - formLoading.value = true  
49 - try {  
50 - const data = formRef.value.formModel as ${simpleClassName}Api.${simpleClassName}VO  
51 - if (formType.value === 'create') {  
52 - await ${simpleClassName}Api.create${simpleClassName}(data)  
53 - message.success(t('common.createSuccess'))  
54 - } else {  
55 - await ${simpleClassName}Api.update${simpleClassName}(data)  
56 - message.success(t('common.updateSuccess'))  
57 - }  
58 - dialogVisible.value = false  
59 - // 发送操作成功的事件  
60 - emit('success')  
61 - } finally {  
62 - formLoading.value = false  
63 - }  
64 -}  
65 -</script>  
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_schema/views/index.vue.vm deleted
1 -<template>  
2 - <!-- 搜索工作栏 -->  
3 - <ContentWrap>  
4 - <Search :schema="allSchemas.searchSchema" @search="setSearchParams" @reset="setSearchParams">  
5 - <!-- 新增等操作按钮 -->  
6 - <template #actionMore>  
7 - <el-button  
8 - type="primary"  
9 - plain  
10 - @click="openForm('create')"  
11 - v-hasPermi="['${permissionPrefix}:create']"  
12 - >  
13 - <Icon icon="ep:plus" class="mr-5px" /> 新增  
14 - </el-button>  
15 - </template>  
16 - </Search>  
17 - </ContentWrap>  
18 -  
19 - <!-- 列表 -->  
20 - <ContentWrap>  
21 - <Table  
22 - :columns="allSchemas.tableColumns"  
23 - :data="tableObject.tableList"  
24 - :loading="tableObject.loading"  
25 - :pagination="{  
26 - total: tableObject.total  
27 - }"  
28 - v-model:pageSize="tableObject.pageSize"  
29 - v-model:currentPage="tableObject.currentPage"  
30 - >  
31 - <template #action="{ row }">  
32 - <el-button  
33 - link  
34 - type="primary"  
35 - @click="openForm('update', row.id)"  
36 - v-hasPermi="['${permissionPrefix}:update']"  
37 - >  
38 - 编辑  
39 - </el-button>  
40 - <el-button  
41 - link  
42 - type="danger"  
43 - v-hasPermi="['${permissionPrefix}:delete']"  
44 - @click="handleDelete(row.id)"  
45 - >  
46 - 删除  
47 - </el-button>  
48 - </template>  
49 - </Table>  
50 - </ContentWrap>  
51 -  
52 - <!-- 表单弹窗:添加/修改 -->  
53 - <${simpleClassName}Form ref="formRef" @success="getList" />  
54 -</template>  
55 -<script setup lang="ts" name="${table.className}">  
56 -import { allSchemas } from './${classNameVar}.data'  
57 -import * as ${simpleClassName}Api from '@/api/${table.moduleName}/${table.businessName}'  
58 -import ${simpleClassName}Form from './${simpleClassName}Form.vue'  
59 -  
60 -// tableObject:表格的属性对象,可获得分页大小、条数等属性  
61 -// tableMethods:表格的操作对象,可进行获得分页、删除记录等操作  
62 -// 详细可见:https://doc.iocoder.cn/vue3/crud-schema/  
63 -const { tableObject, tableMethods } = useTable({  
64 - getListApi: ${simpleClassName}Api.get${simpleClassName}Page, // 分页接口  
65 - delListApi: ${simpleClassName}Api.delete${simpleClassName} // 删除接口  
66 -})  
67 -// 获得表格的各种操作  
68 -const { getList, setSearchParams } = tableMethods  
69 -  
70 -/** 添加/修改操作 */  
71 -const formRef = ref()  
72 -const openForm = (type: string, id?: number) => {  
73 - formRef.value.open(type, id)  
74 -}  
75 -  
76 -/** 删除按钮操作 */  
77 -const handleDelete = (id: number) => {  
78 - tableMethods.delList(id, false)  
79 -}  
80 -  
81 -/** 初始化 **/  
82 -onMounted(() => {  
83 - getList()  
84 -})  
85 -</script>  
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/views/data.ts.vm
@@ -42,9 +42,17 @@ export const searchFormSchema: FormSchema[] = [ @@ -42,9 +42,17 @@ export const searchFormSchema: FormSchema[] = [
42 #foreach($column in $columns) 42 #foreach($column in $columns)
43 #if ($column.listOperation) 43 #if ($column.listOperation)
44 #set ($dictType=$column.dictType) 44 #set ($dictType=$column.dictType)
  45 + #set ($javaType = $column.javaType)
45 #set ($javaField = $column.javaField) 46 #set ($javaField = $column.javaField)
46 #set ($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) 47 #set ($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
47 #set ($comment=$column.columnComment) 48 #set ($comment=$column.columnComment)
  49 + #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
  50 + #set ($dictMethod = "number")
  51 + #elseif ($javaType == "String")
  52 + #set ($dictMethod = "string")
  53 + #elseif ($javaType == "Boolean")
  54 + #set ($dictMethod = "boolean")
  55 + #end
48 { 56 {
49 label: '${comment}', 57 label: '${comment}',
50 field: '${javaField}', 58 field: '${javaField}',
@@ -54,16 +62,16 @@ export const searchFormSchema: FormSchema[] = [ @@ -54,16 +62,16 @@ export const searchFormSchema: FormSchema[] = [
54 component: 'Select', 62 component: 'Select',
55 componentProps: { 63 componentProps: {
56 #if ("" != $dictType)## 设置了 dictType 数据字典的情况 64 #if ("" != $dictType)## 设置了 dictType 数据字典的情况
57 - options: getDictOptions(DICT_TYPE.$dictType.toUpperCase()), 65 + options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
58 #else## 未设置 dictType 数据字典的情况 66 #else## 未设置 dictType 数据字典的情况
59 options: [], 67 options: [],
60 #end 68 #end
61 }, 69 },
62 #elseif ($column.htmlType == "radio") 70 #elseif ($column.htmlType == "radio")
63 - component: 'Radio', 71 + component: 'RadioButtonGroup',
64 componentProps: { 72 componentProps: {
65 #if ("" != $dictType)## 设置了 dictType 数据字典的情况 73 #if ("" != $dictType)## 设置了 dictType 数据字典的情况
66 - options: getDictOptions(DICT_TYPE.$dictType.toUpperCase()), 74 + options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
67 #else## 未设置 dictType 数据字典的情况 75 #else## 未设置 dictType 数据字典的情况
68 options: [], 76 options: [],
69 #end 77 #end
@@ -87,9 +95,17 @@ export const createFormSchema: FormSchema[] = [ @@ -87,9 +95,17 @@ export const createFormSchema: FormSchema[] = [
87 #foreach($column in $columns) 95 #foreach($column in $columns)
88 #if ($column.createOperation) 96 #if ($column.createOperation)
89 #set ($dictType = $column.dictType) 97 #set ($dictType = $column.dictType)
  98 + #set ($javaType = $column.javaType)
90 #set ($javaField = $column.javaField) 99 #set ($javaField = $column.javaField)
91 #set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) 100 #set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
92 #set ($comment = $column.columnComment) 101 #set ($comment = $column.columnComment)
  102 + #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
  103 + #set ($dictMethod = "number")
  104 + #elseif ($javaType == "String")
  105 + #set ($dictMethod = "string")
  106 + #elseif ($javaType == "Boolean")
  107 + #set ($dictMethod = "boolean")
  108 + #end
93 #if (!$column.primaryKey)## 忽略主键,不用在表单里 109 #if (!$column.primaryKey)## 忽略主键,不用在表单里
94 { 110 {
95 label: '${comment}', 111 label: '${comment}',
@@ -117,7 +133,7 @@ export const createFormSchema: FormSchema[] = [ @@ -117,7 +133,7 @@ export const createFormSchema: FormSchema[] = [
117 component: 'Select', 133 component: 'Select',
118 componentProps: { 134 componentProps: {
119 #if ("" != $dictType)## 有数据字典 135 #if ("" != $dictType)## 有数据字典
120 - options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'number'), 136 + options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
121 #else##没数据字典 137 #else##没数据字典
122 options:[], 138 options:[],
123 #end 139 #end
@@ -126,7 +142,7 @@ export const createFormSchema: FormSchema[] = [ @@ -126,7 +142,7 @@ export const createFormSchema: FormSchema[] = [
126 component: 'Checkbox', 142 component: 'Checkbox',
127 componentProps: { 143 componentProps: {
128 #if ("" != $dictType)## 有数据字典 144 #if ("" != $dictType)## 有数据字典
129 - options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'number'), 145 + options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
130 #else##没数据字典 146 #else##没数据字典
131 options:[], 147 options:[],
132 #end 148 #end
@@ -135,7 +151,7 @@ export const createFormSchema: FormSchema[] = [ @@ -135,7 +151,7 @@ export const createFormSchema: FormSchema[] = [
135 component: 'RadioButtonGroup', 151 component: 'RadioButtonGroup',
136 componentProps: { 152 componentProps: {
137 #if ("" != $dictType)## 有数据字典 153 #if ("" != $dictType)## 有数据字典
138 - options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'number'), 154 + options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
139 #else##没数据字典 155 #else##没数据字典
140 options:[], 156 options:[],
141 #end 157 #end
@@ -166,9 +182,17 @@ export const updateFormSchema: FormSchema[] = [ @@ -166,9 +182,17 @@ export const updateFormSchema: FormSchema[] = [
166 #foreach($column in $columns) 182 #foreach($column in $columns)
167 #if ($column.updateOperation) 183 #if ($column.updateOperation)
168 #set ($dictType = $column.dictType) 184 #set ($dictType = $column.dictType)
  185 +#set ($javaType = $column.javaType)
169 #set ($javaField = $column.javaField) 186 #set ($javaField = $column.javaField)
170 #set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) 187 #set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
171 #set ($comment = $column.columnComment) 188 #set ($comment = $column.columnComment)
  189 +#if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
  190 + #set ($dictMethod = "number")
  191 +#elseif ($javaType == "String")
  192 + #set ($dictMethod = "string")
  193 +#elseif ($javaType == "Boolean")
  194 + #set ($dictMethod = "boolean")
  195 +#end
172 #if (!$column.primaryKey)## 忽略主键,不用在表单里 196 #if (!$column.primaryKey)## 忽略主键,不用在表单里
173 { 197 {
174 label: '${comment}', 198 label: '${comment}',
@@ -196,7 +220,7 @@ export const updateFormSchema: FormSchema[] = [ @@ -196,7 +220,7 @@ export const updateFormSchema: FormSchema[] = [
196 component: 'Select', 220 component: 'Select',
197 componentProps: { 221 componentProps: {
198 #if ("" != $dictType)## 有数据字典 222 #if ("" != $dictType)## 有数据字典
199 - options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'number'), 223 + options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
200 #else##没数据字典 224 #else##没数据字典
201 options:[], 225 options:[],
202 #end 226 #end
@@ -205,7 +229,7 @@ export const updateFormSchema: FormSchema[] = [ @@ -205,7 +229,7 @@ export const updateFormSchema: FormSchema[] = [
205 component: 'Checkbox', 229 component: 'Checkbox',
206 componentProps: { 230 componentProps: {
207 #if ("" != $dictType)## 有数据字典 231 #if ("" != $dictType)## 有数据字典
208 - options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'number'), 232 + options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
209 #else##没数据字典 233 #else##没数据字典
210 options:[], 234 options:[],
211 #end 235 #end
@@ -214,7 +238,7 @@ export const updateFormSchema: FormSchema[] = [ @@ -214,7 +238,7 @@ export const updateFormSchema: FormSchema[] = [
214 component: 'RadioButtonGroup', 238 component: 'RadioButtonGroup',
215 componentProps: { 239 componentProps: {
216 #if ("" != $dictType)## 有数据字典 240 #if ("" != $dictType)## 有数据字典
217 - options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'number'), 241 + options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
218 #else##没数据字典 242 #else##没数据字典
219 options:[], 243 options:[],
220 #end 244 #end
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationRecordApi.java
@@ -2,8 +2,8 @@ package cn.iocoder.yudao.module.promotion.api.combination; @@ -2,8 +2,8 @@ package cn.iocoder.yudao.module.promotion.api.combination;
2 2
3 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO; 3 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;
4 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateRespDTO; 4 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateRespDTO;
  5 +import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordRespDTO;
5 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationValidateJoinRespDTO; 6 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationValidateJoinRespDTO;
6 -  
7 import jakarta.validation.Valid; 7 import jakarta.validation.Valid;
8 8
9 /** 9 /**
@@ -33,13 +33,13 @@ public interface CombinationRecordApi { @@ -33,13 +33,13 @@ public interface CombinationRecordApi {
33 CombinationRecordCreateRespDTO createCombinationRecord(@Valid CombinationRecordCreateReqDTO reqDTO); 33 CombinationRecordCreateRespDTO createCombinationRecord(@Valid CombinationRecordCreateReqDTO reqDTO);
34 34
35 /** 35 /**
36 - * 查询拼团记录是否成功 36 + * 基于订单编号,查询拼团记录
37 * 37 *
38 * @param userId 用户编号 38 * @param userId 用户编号
39 * @param orderId 订单编号 39 * @param orderId 订单编号
40 - * @return 拼团是否成功 40 + * @return 拼团记录
41 */ 41 */
42 - boolean isCombinationRecordSuccess(Long userId, Long orderId); 42 + CombinationRecordRespDTO getCombinationRecordByOrderId(Long userId, Long orderId);
43 43
44 /** 44 /**
45 * 【下单前】校验是否满足拼团活动条件 45 * 【下单前】校验是否满足拼团活动条件
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/dto/CombinationRecordRespDTO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.api.combination.dto;
  2 +
  3 +import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +
  8 +/**
  9 + * 拼团记录 Response DTO
  10 + *
  11 + * @author 芋道源码
  12 + */
  13 +@Data
  14 +public class CombinationRecordRespDTO {
  15 +
  16 + /**
  17 + * 编号,主键自增
  18 + */
  19 + private Long id;
  20 +
  21 + /**
  22 + * 拼团活动编号
  23 + *
  24 + * 关联 CombinationActivityDO 的 id 字段
  25 + */
  26 + private Long activityId;
  27 + /**
  28 + * 拼团商品单价
  29 + *
  30 + * 冗余 CombinationProductDO 的 combinationPrice 字段
  31 + */
  32 + private Integer combinationPrice;
  33 + /**
  34 + * SPU 编号
  35 + */
  36 + private Long spuId;
  37 + /**
  38 + * 商品名字
  39 + */
  40 + private String spuName;
  41 + /**
  42 + * 商品图片
  43 + */
  44 + private String picUrl;
  45 + /**
  46 + * SKU 编号
  47 + */
  48 + private Long skuId;
  49 + /**
  50 + * 购买的商品数量
  51 + */
  52 + private Integer count;
  53 +
  54 + /**
  55 + * 用户编号
  56 + */
  57 + private Long userId;
  58 +
  59 + /**
  60 + * 用户昵称
  61 + */
  62 + private String nickname;
  63 + /**
  64 + * 用户头像
  65 + */
  66 + private String avatar;
  67 +
  68 + /**
  69 + * 团长编号
  70 + */
  71 + private Long headId;
  72 + /**
  73 + * 开团状态
  74 + *
  75 + * 关联 {@link CombinationRecordStatusEnum}
  76 + */
  77 + private Integer status;
  78 + /**
  79 + * 订单编号
  80 + */
  81 + private Long orderId;
  82 + /**
  83 + * 开团需要人数
  84 + *
  85 + * 关联 CombinationActivityDO 的 userSize 字段
  86 + */
  87 + private Integer userSize;
  88 + /**
  89 + * 已加入拼团人数
  90 + */
  91 + private Integer userCount;
  92 + /**
  93 + * 是否虚拟成团
  94 + */
  95 + private Boolean virtualGroup;
  96 +
  97 + /**
  98 + * 过期时间
  99 + */
  100 + private LocalDateTime expireTime;
  101 + /**
  102 + * 开始时间 (订单付款后开始的时间)
  103 + */
  104 + private LocalDateTime startTime;
  105 + /**
  106 + * 结束时间(成团时间/失败时间)
  107 + */
  108 + private LocalDateTime endTime;
  109 +
  110 +}
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/CouponApi.java
@@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.promotion.api.coupon; @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.promotion.api.coupon;
2 2
3 import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponRespDTO; 3 import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponRespDTO;
4 import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO; 4 import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO;
5 -import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponValidReqDTO;  
6 import jakarta.validation.Valid; 5 import jakarta.validation.Valid;
7 6
8 import java.util.List; 7 import java.util.List;
@@ -16,6 +15,15 @@ import java.util.Map; @@ -16,6 +15,15 @@ import java.util.Map;
16 public interface CouponApi { 15 public interface CouponApi {
17 16
18 /** 17 /**
  18 + * 获得用户的优惠劵列表
  19 + *
  20 + * @param userId 用户编号
  21 + * @param status 优惠劵状态
  22 + * @return 优惠劵列表
  23 + */
  24 + List<CouponRespDTO> getCouponListByUserId(Long userId, Integer status);
  25 +
  26 + /**
19 * 使用优惠劵 27 * 使用优惠劵
20 * 28 *
21 * @param useReqDTO 使用请求 29 * @param useReqDTO 使用请求
@@ -30,14 +38,6 @@ public interface CouponApi { @@ -30,14 +38,6 @@ public interface CouponApi {
30 void returnUsedCoupon(Long id); 38 void returnUsedCoupon(Long id);
31 39
32 /** 40 /**
33 - * 校验优惠劵  
34 - *  
35 - * @param validReqDTO 校验请求  
36 - * @return 优惠劵  
37 - */  
38 - CouponRespDTO validateCoupon(@Valid CouponValidReqDTO validReqDTO);  
39 -  
40 - /**  
41 * 【管理员】给指定用户批量发送优惠券 41 * 【管理员】给指定用户批量发送优惠券
42 * 42 *
43 * @param giveCoupons key: 优惠劵模版编号,value:对应的数量 43 * @param giveCoupons key: 优惠劵模版编号,value:对应的数量
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/dto/CouponValidReqDTO.java deleted
1 -package cn.iocoder.yudao.module.promotion.api.coupon.dto;  
2 -  
3 -import lombok.Data;  
4 -  
5 -import jakarta.validation.constraints.NotNull;  
6 -  
7 -/**  
8 - * 优惠劵使用 Request DTO  
9 - *  
10 - * @author 芋道源码  
11 - */  
12 -@Data  
13 -public class CouponValidReqDTO {  
14 -  
15 - /**  
16 - * 优惠劵编号  
17 - */  
18 - @NotNull(message = "优惠劵编号不能为空")  
19 - private Long id;  
20 -  
21 - /**  
22 - * 用户编号  
23 - */  
24 - @NotNull(message = "用户编号不能为空")  
25 - private Long userId;  
26 -  
27 -}  
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java
@@ -21,8 +21,6 @@ public interface ErrorCodeConstants { @@ -21,8 +21,6 @@ public interface ErrorCodeConstants {
21 ErrorCode BANNER_NOT_EXISTS = new ErrorCode(1_013_002_000, "Banner 不存在"); 21 ErrorCode BANNER_NOT_EXISTS = new ErrorCode(1_013_002_000, "Banner 不存在");
22 22
23 // ========== Coupon 相关 1-013-003-000 ============ 23 // ========== Coupon 相关 1-013-003-000 ============
24 - ErrorCode COUPON_NO_MATCH_SPU = new ErrorCode(1_013_003_000, "优惠劵没有可使用的商品!");  
25 - ErrorCode COUPON_NO_MATCH_MIN_PRICE = new ErrorCode(1_013_003_001, "所结算的商品中未满足使用的金额");  
26 24
27 // ========== 优惠劵模板 1-013-004-000 ========== 25 // ========== 优惠劵模板 1-013-004-000 ==========
28 ErrorCode COUPON_TEMPLATE_NOT_EXISTS = new ErrorCode(1_013_004_000, "优惠劵模板不存在"); 26 ErrorCode COUPON_TEMPLATE_NOT_EXISTS = new ErrorCode(1_013_004_000, "优惠劵模板不存在");
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationRecordApiImpl.java
1 package cn.iocoder.yudao.module.promotion.api.combination; 1 package cn.iocoder.yudao.module.promotion.api.combination;
2 2
  3 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
3 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO; 4 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;
4 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateRespDTO; 5 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateRespDTO;
  6 +import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordRespDTO;
5 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationValidateJoinRespDTO; 7 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationValidateJoinRespDTO;
6 import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert; 8 import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert;
7 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO; 9 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
8 -import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;  
9 import cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordService; 10 import cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordService;
10 import jakarta.annotation.Resource; 11 import jakarta.annotation.Resource;
11 import org.springframework.stereotype.Service; 12 import org.springframework.stereotype.Service;
12 import org.springframework.validation.annotation.Validated; 13 import org.springframework.validation.annotation.Validated;
13 14
14 -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;  
15 -import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COMBINATION_RECORD_NOT_EXISTS;  
16 -  
17 /** 15 /**
18 * 拼团活动 API 实现类 16 * 拼团活动 API 实现类
19 * 17 *
@@ -37,12 +35,9 @@ public class CombinationRecordApiImpl implements CombinationRecordApi { @@ -37,12 +35,9 @@ public class CombinationRecordApiImpl implements CombinationRecordApi {
37 } 35 }
38 36
39 @Override 37 @Override
40 - public boolean isCombinationRecordSuccess(Long userId, Long orderId) { 38 + public CombinationRecordRespDTO getCombinationRecordByOrderId(Long userId, Long orderId) {
41 CombinationRecordDO record = combinationRecordService.getCombinationRecord(userId, orderId); 39 CombinationRecordDO record = combinationRecordService.getCombinationRecord(userId, orderId);
42 - if (record == null) {  
43 - throw exception(COMBINATION_RECORD_NOT_EXISTS);  
44 - }  
45 - return CombinationRecordStatusEnum.isSuccess(record.getStatus()); 40 + return BeanUtils.toBean(record, CombinationRecordRespDTO.class);
46 } 41 }
47 42
48 @Override 43 @Override
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/CouponApiImpl.java
1 package cn.iocoder.yudao.module.promotion.api.coupon; 1 package cn.iocoder.yudao.module.promotion.api.coupon;
2 2
3 3
  4 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
4 import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponRespDTO; 5 import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponRespDTO;
5 import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO; 6 import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO;
6 -import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponValidReqDTO;  
7 -import cn.iocoder.yudao.module.promotion.convert.coupon.CouponConvert;  
8 -import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;  
9 import cn.iocoder.yudao.module.promotion.service.coupon.CouponService; 7 import cn.iocoder.yudao.module.promotion.service.coupon.CouponService;
10 import jakarta.annotation.Resource; 8 import jakarta.annotation.Resource;
11 import org.springframework.stereotype.Service; 9 import org.springframework.stereotype.Service;
@@ -27,6 +25,11 @@ public class CouponApiImpl implements CouponApi { @@ -27,6 +25,11 @@ public class CouponApiImpl implements CouponApi {
27 private CouponService couponService; 25 private CouponService couponService;
28 26
29 @Override 27 @Override
  28 + public List<CouponRespDTO> getCouponListByUserId(Long userId, Integer status) {
  29 + return BeanUtils.toBean(couponService.getCouponList(userId, status), CouponRespDTO.class);
  30 + }
  31 +
  32 + @Override
30 public void useCoupon(CouponUseReqDTO useReqDTO) { 33 public void useCoupon(CouponUseReqDTO useReqDTO) {
31 couponService.useCoupon(useReqDTO.getId(), useReqDTO.getUserId(), 34 couponService.useCoupon(useReqDTO.getId(), useReqDTO.getUserId(),
32 useReqDTO.getOrderId()); 35 useReqDTO.getOrderId());
@@ -38,12 +41,6 @@ public class CouponApiImpl implements CouponApi { @@ -38,12 +41,6 @@ public class CouponApiImpl implements CouponApi {
38 } 41 }
39 42
40 @Override 43 @Override
41 - public CouponRespDTO validateCoupon(CouponValidReqDTO validReqDTO) {  
42 - CouponDO coupon = couponService.validCoupon(validReqDTO.getId(), validReqDTO.getUserId());  
43 - return CouponConvert.INSTANCE.convert(coupon);  
44 - }  
45 -  
46 - @Override  
47 public List<Long> takeCouponsByAdmin(Map<Long, Integer> giveCoupons, Long userId) { 44 public List<Long> takeCouponsByAdmin(Map<Long, Integer> giveCoupons, Long userId) {
48 return couponService.takeCouponsByAdmin(giveCoupons, userId); 45 return couponService.takeCouponsByAdmin(giveCoupons, userId);
49 } 46 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/CombinationActivityController.java
1 package cn.iocoder.yudao.module.promotion.controller.admin.combination; 1 package cn.iocoder.yudao.module.promotion.controller.admin.combination;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 5 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; 7 import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
@@ -16,18 +17,20 @@ import cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordSe @@ -16,18 +17,20 @@ import cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordSe
16 import io.swagger.v3.oas.annotations.Operation; 17 import io.swagger.v3.oas.annotations.Operation;
17 import io.swagger.v3.oas.annotations.Parameter; 18 import io.swagger.v3.oas.annotations.Parameter;
18 import io.swagger.v3.oas.annotations.tags.Tag; 19 import io.swagger.v3.oas.annotations.tags.Tag;
  20 +import jakarta.annotation.Resource;
  21 +import jakarta.validation.Valid;
19 import org.springframework.security.access.prepost.PreAuthorize; 22 import org.springframework.security.access.prepost.PreAuthorize;
20 import org.springframework.validation.annotation.Validated; 23 import org.springframework.validation.annotation.Validated;
21 import org.springframework.web.bind.annotation.*; 24 import org.springframework.web.bind.annotation.*;
22 25
23 -import jakarta.annotation.Resource;  
24 -import jakarta.validation.Valid; 26 +import java.util.Collections;
25 import java.util.List; 27 import java.util.List;
26 import java.util.Map; 28 import java.util.Map;
27 import java.util.Set; 29 import java.util.Set;
28 30
29 import static cn.hutool.core.collection.CollectionUtil.newArrayList; 31 import static cn.hutool.core.collection.CollectionUtil.newArrayList;
30 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 32 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  33 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
31 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; 34 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
32 35
33 @Tag(name = "管理后台 - 拼团活动") 36 @Tag(name = "管理后台 - 拼团活动")
@@ -87,6 +90,23 @@ public class CombinationActivityController { @@ -87,6 +90,23 @@ public class CombinationActivityController {
87 return success(CombinationActivityConvert.INSTANCE.convert(activity, products)); 90 return success(CombinationActivityConvert.INSTANCE.convert(activity, products));
88 } 91 }
89 92
  93 + @GetMapping("/list-by-ids")
  94 + @Operation(summary = "获得拼团活动列表,基于活动编号数组")
  95 + @Parameter(name = "ids", description = "活动编号数组", required = true, example = "[1024, 1025]")
  96 + public CommonResult<List<CombinationActivityRespVO>> getCombinationActivityListByIds(@RequestParam("ids") List<Long> ids) {
  97 + // 1. 获得开启的活动列表
  98 + List<CombinationActivityDO> activityList = combinationActivityService.getCombinationActivityListByIds(ids);
  99 + activityList.removeIf(activity -> CommonStatusEnum.isDisable(activity.getStatus()));
  100 + if (CollUtil.isEmpty(activityList)) {
  101 + return success(Collections.emptyList());
  102 + }
  103 + // 2. 拼接返回
  104 + List<CombinationProductDO> productList = combinationActivityService.getCombinationProductListByActivityIds(
  105 + convertList(activityList, CombinationActivityDO::getId));
  106 + List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList(convertList(activityList, CombinationActivityDO::getSpuId));
  107 + return success(CombinationActivityConvert.INSTANCE.convertList(activityList, productList, spuList));
  108 + }
  109 +
90 @GetMapping("/page") 110 @GetMapping("/page")
91 @Operation(summary = "获得拼团活动分页") 111 @Operation(summary = "获得拼团活动分页")
92 @PreAuthorize("@ss.hasPermission('promotion:combination-activity:query')") 112 @PreAuthorize("@ss.hasPermission('promotion:combination-activity:query')")
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityRespVO.java
@@ -27,4 +27,14 @@ public class CombinationActivityRespVO extends CombinationActivityBaseVO { @@ -27,4 +27,14 @@ public class CombinationActivityRespVO extends CombinationActivityBaseVO {
27 @Schema(description = "拼团商品", requiredMode = Schema.RequiredMode.REQUIRED) 27 @Schema(description = "拼团商品", requiredMode = Schema.RequiredMode.REQUIRED)
28 private List<CombinationProductRespVO> products; 28 private List<CombinationProductRespVO> products;
29 29
  30 + @Schema(description = "商品 SPU 名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "一个白菜")
  31 + private String spuName; // 从 SPU 的 name 读取
  32 + @Schema(description = "商品图片", requiredMode = Schema.RequiredMode.REQUIRED, example = "4096")
  33 + private String picUrl; // 从 SPU 的 picUrl 读取
  34 + @Schema(description = "商品市场价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "50")
  35 + private Integer marketPrice; // 从 SPU 的 marketPrice 读取
  36 +
  37 + @Schema(description = "拼团金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
  38 + private Integer combinationPrice; // 从 products 获取最小 price 读取
  39 +
30 } 40 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/SeckillActivityController.java
1 package cn.iocoder.yudao.module.promotion.controller.admin.seckill; 1 package cn.iocoder.yudao.module.promotion.controller.admin.seckill;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 5 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; 7 import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
@@ -13,15 +14,17 @@ import cn.iocoder.yudao.module.promotion.service.seckill.SeckillActivityService; @@ -13,15 +14,17 @@ import cn.iocoder.yudao.module.promotion.service.seckill.SeckillActivityService;
13 import io.swagger.v3.oas.annotations.Operation; 14 import io.swagger.v3.oas.annotations.Operation;
14 import io.swagger.v3.oas.annotations.Parameter; 15 import io.swagger.v3.oas.annotations.Parameter;
15 import io.swagger.v3.oas.annotations.tags.Tag; 16 import io.swagger.v3.oas.annotations.tags.Tag;
  17 +import jakarta.annotation.Resource;
  18 +import jakarta.validation.Valid;
16 import org.springframework.security.access.prepost.PreAuthorize; 19 import org.springframework.security.access.prepost.PreAuthorize;
17 import org.springframework.validation.annotation.Validated; 20 import org.springframework.validation.annotation.Validated;
18 import org.springframework.web.bind.annotation.*; 21 import org.springframework.web.bind.annotation.*;
19 22
20 -import jakarta.annotation.Resource;  
21 -import jakarta.validation.Valid; 23 +import java.util.Collections;
22 import java.util.List; 24 import java.util.List;
23 25
24 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 26 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  27 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
25 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; 28 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
26 29
27 @Tag(name = "管理后台 - 秒杀活动") 30 @Tag(name = "管理后台 - 秒杀活动")
@@ -89,11 +92,28 @@ public class SeckillActivityController { @@ -89,11 +92,28 @@ public class SeckillActivityController {
89 } 92 }
90 93
91 // 拼接数据 94 // 拼接数据
92 - List<SeckillProductDO> products = seckillActivityService.getSeckillProductListByActivityId( 95 + List<SeckillProductDO> products = seckillActivityService.getSeckillProductListByActivityIds(
93 convertSet(pageResult.getList(), SeckillActivityDO::getId)); 96 convertSet(pageResult.getList(), SeckillActivityDO::getId));
94 List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList( 97 List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList(
95 convertSet(pageResult.getList(), SeckillActivityDO::getSpuId)); 98 convertSet(pageResult.getList(), SeckillActivityDO::getSpuId));
96 return success(SeckillActivityConvert.INSTANCE.convertPage(pageResult, products, spuList)); 99 return success(SeckillActivityConvert.INSTANCE.convertPage(pageResult, products, spuList));
97 } 100 }
98 101
  102 + @GetMapping("/list-by-ids")
  103 + @Operation(summary = "获得秒杀活动列表,基于活动编号数组")
  104 + @Parameter(name = "ids", description = "活动编号数组", required = true, example = "[1024, 1025]")
  105 + public CommonResult<List<SeckillActivityRespVO>> getCombinationActivityListByIds(@RequestParam("ids") List<Long> ids) {
  106 + // 1. 获得开启的活动列表
  107 + List<SeckillActivityDO> activityList = seckillActivityService.getSeckillActivityListByIds(ids);
  108 + activityList.removeIf(activity -> CommonStatusEnum.isDisable(activity.getStatus()));
  109 + if (CollUtil.isEmpty(activityList)) {
  110 + return success(Collections.emptyList());
  111 + }
  112 + // 2. 拼接返回
  113 + List<SeckillProductDO> productList = seckillActivityService.getSeckillProductListByActivityIds(
  114 + convertList(activityList, SeckillActivityDO::getId));
  115 + List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList(convertList(activityList, SeckillActivityDO::getSpuId));
  116 + return success(SeckillActivityConvert.INSTANCE.convertList(activityList, productList, spuList));
  117 + }
  118 +
99 } 119 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/activity/SeckillActivityRespVO.java
@@ -54,4 +54,7 @@ public class SeckillActivityRespVO extends SeckillActivityBaseVO { @@ -54,4 +54,7 @@ public class SeckillActivityRespVO extends SeckillActivityBaseVO {
54 example = "50") 54 example = "50")
55 private Integer marketPrice; 55 private Integer marketPrice;
56 56
  57 + @Schema(description = "拼团金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
  58 + private Integer seckillPrice; // 从 products 获取最小 price 读取
  59 +
57 } 60 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/AppCombinationActivityController.java
@@ -14,24 +14,20 @@ import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivity @@ -14,24 +14,20 @@ import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivity
14 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO; 14 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
15 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO; 15 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO;
16 import cn.iocoder.yudao.module.promotion.service.combination.CombinationActivityService; 16 import cn.iocoder.yudao.module.promotion.service.combination.CombinationActivityService;
17 -import com.google.common.cache.CacheLoader;  
18 -import com.google.common.cache.LoadingCache;  
19 import io.swagger.v3.oas.annotations.Operation; 17 import io.swagger.v3.oas.annotations.Operation;
20 import io.swagger.v3.oas.annotations.Parameter; 18 import io.swagger.v3.oas.annotations.Parameter;
21 import io.swagger.v3.oas.annotations.tags.Tag; 19 import io.swagger.v3.oas.annotations.tags.Tag;
  20 +import jakarta.annotation.Resource;
22 import org.springframework.validation.annotation.Validated; 21 import org.springframework.validation.annotation.Validated;
23 import org.springframework.web.bind.annotation.GetMapping; 22 import org.springframework.web.bind.annotation.GetMapping;
24 import org.springframework.web.bind.annotation.RequestMapping; 23 import org.springframework.web.bind.annotation.RequestMapping;
25 import org.springframework.web.bind.annotation.RequestParam; 24 import org.springframework.web.bind.annotation.RequestParam;
26 import org.springframework.web.bind.annotation.RestController; 25 import org.springframework.web.bind.annotation.RestController;
27 26
28 -import jakarta.annotation.Resource;  
29 -import java.time.Duration;  
30 import java.util.Collections; 27 import java.util.Collections;
31 import java.util.List; 28 import java.util.List;
32 29
33 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 30 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
34 -import static cn.iocoder.yudao.framework.common.util.cache.CacheUtils.buildAsyncReloadingCache;  
35 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; 31 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
36 32
37 @Tag(name = "用户 APP - 拼团活动") 33 @Tag(name = "用户 APP - 拼团活动")
@@ -40,45 +36,12 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. @@ -40,45 +36,12 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
40 @Validated 36 @Validated
41 public class AppCombinationActivityController { 37 public class AppCombinationActivityController {
42 38
43 - /**  
44 - * {@link AppCombinationActivityRespVO} 缓存,通过它异步刷新 {@link #getCombinationActivityList0(Integer)} 所要的首页数据  
45 - */  
46 - private final LoadingCache<Integer, List<AppCombinationActivityRespVO>> combinationActivityListCache = buildAsyncReloadingCache(Duration.ofSeconds(10L),  
47 - new CacheLoader<Integer, List<AppCombinationActivityRespVO>>() {  
48 -  
49 - @Override  
50 - public List<AppCombinationActivityRespVO> load(Integer count) {  
51 - return getCombinationActivityList0(count);  
52 - }  
53 -  
54 - });  
55 -  
56 @Resource 39 @Resource
57 private CombinationActivityService activityService; 40 private CombinationActivityService activityService;
58 41
59 @Resource 42 @Resource
60 private ProductSpuApi spuApi; 43 private ProductSpuApi spuApi;
61 44
62 - @GetMapping("/list")  
63 - @Operation(summary = "获得拼团活动列表", description = "用于小程序首页")  
64 - @Parameter(name = "count", description = "需要展示的数量", example = "6")  
65 - public CommonResult<List<AppCombinationActivityRespVO>> getCombinationActivityList(  
66 - @RequestParam(name = "count", defaultValue = "6") Integer count) {  
67 - return success(combinationActivityListCache.getUnchecked(count));  
68 - }  
69 -  
70 - private List<AppCombinationActivityRespVO> getCombinationActivityList0(Integer count) {  
71 - List<CombinationActivityDO> activityList = activityService.getCombinationActivityListByCount(count);  
72 - if (CollUtil.isEmpty(activityList)) {  
73 - return Collections.emptyList();  
74 - }  
75 - // 拼接返回  
76 - List<CombinationProductDO> productList = activityService.getCombinationProductListByActivityIds(  
77 - convertList(activityList, CombinationActivityDO::getId));  
78 - List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(activityList, CombinationActivityDO::getSpuId));  
79 - return CombinationActivityConvert.INSTANCE.convertAppList(activityList, productList, spuList);  
80 - }  
81 -  
82 @GetMapping("/page") 45 @GetMapping("/page")
83 @Operation(summary = "获得拼团活动分页") 46 @Operation(summary = "获得拼团活动分页")
84 public CommonResult<PageResult<AppCombinationActivityRespVO>> getCombinationActivityPage(PageParam pageParam) { 47 public CommonResult<PageResult<AppCombinationActivityRespVO>> getCombinationActivityPage(PageParam pageParam) {
@@ -93,6 +56,23 @@ public class AppCombinationActivityController { @@ -93,6 +56,23 @@ public class AppCombinationActivityController {
93 return success(CombinationActivityConvert.INSTANCE.convertAppPage(pageResult, productList, spuList)); 56 return success(CombinationActivityConvert.INSTANCE.convertAppPage(pageResult, productList, spuList));
94 } 57 }
95 58
  59 + @GetMapping("/list-by-ids")
  60 + @Operation(summary = "获得拼团活动列表,基于活动编号数组")
  61 + @Parameter(name = "ids", description = "活动编号数组", required = true, example = "[1024, 1025]")
  62 + public CommonResult<List<AppCombinationActivityRespVO>> getCombinationActivityListByIds(@RequestParam("ids") List<Long> ids) {
  63 + // 1. 获得开启的活动列表
  64 + List<CombinationActivityDO> activityList = activityService.getCombinationActivityListByIds(ids);
  65 + activityList.removeIf(activity -> CommonStatusEnum.isDisable(activity.getStatus()));
  66 + if (CollUtil.isEmpty(activityList)) {
  67 + return success(Collections.emptyList());
  68 + }
  69 + // 2. 拼接返回
  70 + List<CombinationProductDO> productList = activityService.getCombinationProductListByActivityIds(
  71 + convertList(activityList, CombinationActivityDO::getId));
  72 + List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(activityList, CombinationActivityDO::getSpuId));
  73 + return success(CombinationActivityConvert.INSTANCE.convertAppList(activityList, productList, spuList));
  74 + }
  75 +
96 @GetMapping("/get-detail") 76 @GetMapping("/get-detail")
97 @Operation(summary = "获得拼团活动明细") 77 @Operation(summary = "获得拼团活动明细")
98 @Parameter(name = "id", description = "活动编号", required = true, example = "1024") 78 @Parameter(name = "id", description = "活动编号", required = true, example = "1024")
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/vo/activity/AppCombinationActivityRespVO.java
@@ -19,15 +19,14 @@ public class AppCombinationActivityRespVO { @@ -19,15 +19,14 @@ public class AppCombinationActivityRespVO {
19 @Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") 19 @Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
20 private Long spuId; 20 private Long spuId;
21 21
  22 + @Schema(description = "商品 SPU 名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "一个白菜")
  23 + private String spuName; // 从 SPU 的 name 读取
22 @Schema(description = "商品图片", requiredMode = Schema.RequiredMode.REQUIRED, example = "4096") 24 @Schema(description = "商品图片", requiredMode = Schema.RequiredMode.REQUIRED, example = "4096")
23 - // 从 SPU 的 picUrl 读取  
24 - private String picUrl;  
25 - 25 + private String picUrl; // 从 SPU 的 picUrl 读取
26 @Schema(description = "商品市场价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "50") 26 @Schema(description = "商品市场价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "50")
27 - // 从 SPU 的 marketPrice 读取  
28 - private Integer marketPrice; 27 + private Integer marketPrice; // 从 SPU 的 marketPrice 读取
29 28
30 @Schema(description = "拼团金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") 29 @Schema(description = "拼团金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
31 - private Integer combinationPrice; 30 + private Integer combinationPrice; // 从 products 获取最小 price 读取
32 31
33 } 32 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/coupon/AppCouponController.java
@@ -5,7 +5,9 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; @@ -5,7 +5,9 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
7 import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated; 7 import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
8 -import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.*; 8 +import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponPageReqVO;
  9 +import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponRespVO;
  10 +import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponTakeReqVO;
9 import cn.iocoder.yudao.module.promotion.convert.coupon.CouponConvert; 11 import cn.iocoder.yudao.module.promotion.convert.coupon.CouponConvert;
10 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO; 12 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
11 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO; 13 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
@@ -15,13 +17,12 @@ import cn.iocoder.yudao.module.promotion.service.coupon.CouponTemplateService; @@ -15,13 +17,12 @@ import cn.iocoder.yudao.module.promotion.service.coupon.CouponTemplateService;
15 import io.swagger.v3.oas.annotations.Operation; 17 import io.swagger.v3.oas.annotations.Operation;
16 import io.swagger.v3.oas.annotations.Parameter; 18 import io.swagger.v3.oas.annotations.Parameter;
17 import io.swagger.v3.oas.annotations.tags.Tag; 19 import io.swagger.v3.oas.annotations.tags.Tag;
  20 +import jakarta.annotation.Resource;
  21 +import jakarta.validation.Valid;
18 import org.springframework.validation.annotation.Validated; 22 import org.springframework.validation.annotation.Validated;
19 import org.springframework.web.bind.annotation.*; 23 import org.springframework.web.bind.annotation.*;
20 24
21 -import jakarta.annotation.Resource;  
22 -import jakarta.validation.Valid;  
23 import java.util.Collections; 25 import java.util.Collections;
24 -import java.util.List;  
25 26
26 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 27 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
27 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; 28 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@@ -56,14 +57,6 @@ public class AppCouponController { @@ -56,14 +57,6 @@ public class AppCouponController {
56 return success(canTakeAgain); 57 return success(canTakeAgain);
57 } 58 }
58 59
59 - @GetMapping("/match-list")  
60 - @Operation(summary = "获得匹配指定商品的优惠劵列表", description = "用于下单页,展示优惠劵列表")  
61 - public CommonResult<List<AppCouponMatchRespVO>> getMatchCouponList(AppCouponMatchReqVO matchReqVO) {  
62 - // todo: 优化:优惠金额倒序  
63 - List<AppCouponMatchRespVO> list = couponService.getMatchCouponList(getLoginUserId(), matchReqVO);  
64 - return success(list);  
65 - }  
66 -  
67 @GetMapping("/page") 60 @GetMapping("/page")
68 @Operation(summary = "我的优惠劵列表") 61 @Operation(summary = "我的优惠劵列表")
69 @PreAuthenticated 62 @PreAuthenticated
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/coupon/vo/coupon/AppCouponMatchReqVO.java deleted
1 -package cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon;  
2 -  
3 -import io.swagger.v3.oas.annotations.media.Schema;  
4 -import lombok.Data;  
5 -  
6 -import jakarta.validation.constraints.NotEmpty;  
7 -import jakarta.validation.constraints.NotNull;  
8 -import java.util.List;  
9 -  
10 -@Schema(description = "用户 App - 优惠劵的匹配 Request VO")  
11 -@Data  
12 -public class AppCouponMatchReqVO {  
13 -  
14 - @Schema(description = "商品金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")  
15 - @NotNull(message = "商品金额不能为空")  
16 - private Integer price;  
17 -  
18 - @Schema(description = "商品 SPU 编号的数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1, 2]")  
19 - @NotEmpty(message = "商品 SPU 编号不能为空")  
20 - private List<Long> spuIds;  
21 -  
22 - @Schema(description = "商品 SKU 编号的数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1, 2]")  
23 - @NotEmpty(message = "商品 SKU 编号不能为空")  
24 - private List<Long> skuIds;  
25 -  
26 - @Schema(description = "分类编号的数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[10, 20]")  
27 - @NotEmpty(message = "分类编号不能为空")  
28 - private List<Long> categoryIds;  
29 -  
30 -}  
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/coupon/vo/coupon/AppCouponMatchRespVO.java deleted
1 -package cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon;  
2 -  
3 -import io.swagger.v3.oas.annotations.media.Schema;  
4 -import lombok.Data;  
5 -  
6 -@Schema(description = "用户 App - 优惠劵 Response VO")  
7 -@Data  
8 -public class AppCouponMatchRespVO extends AppCouponRespVO {  
9 -  
10 - @Schema(description = "是否匹配", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")  
11 - private Boolean match;  
12 -  
13 - @Schema(description = "匹配条件的提示", example = "所结算商品没有符合条件的商品")  
14 - private String description;  
15 -  
16 -}  
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/coupon/vo/coupon/AppCouponRespVO.java
@@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon; @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon;
3 import io.swagger.v3.oas.annotations.media.Schema; 3 import io.swagger.v3.oas.annotations.media.Schema;
4 import lombok.Data; 4 import lombok.Data;
5 5
6 -import jakarta.validation.constraints.Min;  
7 import java.time.LocalDateTime; 6 import java.time.LocalDateTime;
8 import java.util.List; 7 import java.util.List;
9 8
@@ -42,7 +41,6 @@ public class AppCouponRespVO { @@ -42,7 +41,6 @@ public class AppCouponRespVO {
42 private Integer discountPercent; 41 private Integer discountPercent;
43 42
44 @Schema(description = "优惠金额", example = "10") 43 @Schema(description = "优惠金额", example = "10")
45 - @Min(value = 0, message = "优惠金额需要大于等于 0")  
46 private Integer discountPrice; 44 private Integer discountPrice;
47 45
48 @Schema(description = "折扣上限", example = "100") // 单位:分,仅在 discountType 为 PERCENT 使用 46 @Schema(description = "折扣上限", example = "100") // 单位:分,仅在 discountType 为 PERCENT 使用
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/seckill/AppSeckillActivityController.java
@@ -23,6 +23,7 @@ import com.google.common.cache.LoadingCache; @@ -23,6 +23,7 @@ import com.google.common.cache.LoadingCache;
23 import io.swagger.v3.oas.annotations.Operation; 23 import io.swagger.v3.oas.annotations.Operation;
24 import io.swagger.v3.oas.annotations.Parameter; 24 import io.swagger.v3.oas.annotations.Parameter;
25 import io.swagger.v3.oas.annotations.tags.Tag; 25 import io.swagger.v3.oas.annotations.tags.Tag;
  26 +import jakarta.annotation.Resource;
26 import org.springframework.context.annotation.Lazy; 27 import org.springframework.context.annotation.Lazy;
27 import org.springframework.validation.annotation.Validated; 28 import org.springframework.validation.annotation.Validated;
28 import org.springframework.web.bind.annotation.GetMapping; 29 import org.springframework.web.bind.annotation.GetMapping;
@@ -30,11 +31,11 @@ import org.springframework.web.bind.annotation.RequestMapping; @@ -30,11 +31,11 @@ import org.springframework.web.bind.annotation.RequestMapping;
30 import org.springframework.web.bind.annotation.RequestParam; 31 import org.springframework.web.bind.annotation.RequestParam;
31 import org.springframework.web.bind.annotation.RestController; 32 import org.springframework.web.bind.annotation.RestController;
32 33
33 -import jakarta.annotation.Resource;  
34 import java.time.Duration; 34 import java.time.Duration;
35 import java.time.LocalDate; 35 import java.time.LocalDate;
36 import java.time.LocalDateTime; 36 import java.time.LocalDateTime;
37 import java.time.LocalTime; 37 import java.time.LocalTime;
  38 +import java.util.Collections;
38 import java.util.List; 39 import java.util.List;
39 40
40 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 41 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@@ -86,7 +87,7 @@ public class AppSeckillActivityController { @@ -86,7 +87,7 @@ public class AppSeckillActivityController {
86 87
87 // 2.1 查询满足当前阶段的活动 88 // 2.1 查询满足当前阶段的活动
88 List<SeckillActivityDO> activityList = activityService.getSeckillActivityListByConfigIdAndStatus(config.getId(), CommonStatusEnum.ENABLE.getStatus()); 89 List<SeckillActivityDO> activityList = activityService.getSeckillActivityListByConfigIdAndStatus(config.getId(), CommonStatusEnum.ENABLE.getStatus());
89 - List<SeckillProductDO> productList = activityService.getSeckillProductListByActivityId( 90 + List<SeckillProductDO> productList = activityService.getSeckillProductListByActivityIds(
90 convertList(activityList, SeckillActivityDO::getId)); 91 convertList(activityList, SeckillActivityDO::getId));
91 // 2.2 获取 spu 信息 92 // 2.2 获取 spu 信息
92 List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(activityList, SeckillActivityDO::getSpuId)); 93 List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(activityList, SeckillActivityDO::getSpuId));
@@ -101,7 +102,7 @@ public class AppSeckillActivityController { @@ -101,7 +102,7 @@ public class AppSeckillActivityController {
101 if (CollUtil.isEmpty(pageResult.getList())) { 102 if (CollUtil.isEmpty(pageResult.getList())) {
102 return success(PageResult.empty(pageResult.getTotal())); 103 return success(PageResult.empty(pageResult.getTotal()));
103 } 104 }
104 - List<SeckillProductDO> productList = activityService.getSeckillProductListByActivityId( 105 + List<SeckillProductDO> productList = activityService.getSeckillProductListByActivityIds(
105 convertList(pageResult.getList(), SeckillActivityDO::getId)); 106 convertList(pageResult.getList(), SeckillActivityDO::getId));
106 107
107 // 2. 拼接数据 108 // 2. 拼接数据
@@ -149,4 +150,21 @@ public class AppSeckillActivityController { @@ -149,4 +150,21 @@ public class AppSeckillActivityController {
149 return success(SeckillActivityConvert.INSTANCE.convert3(activity, productList, startTime, endTime)); 150 return success(SeckillActivityConvert.INSTANCE.convert3(activity, productList, startTime, endTime));
150 } 151 }
151 152
  153 + @GetMapping("/list-by-ids")
  154 + @Operation(summary = "获得拼团活动列表,基于活动编号数组")
  155 + @Parameter(name = "ids", description = "活动编号数组", required = true, example = "[1024, 1025]")
  156 + public CommonResult<List<AppSeckillActivityRespVO>> getCombinationActivityListByIds(@RequestParam("ids") List<Long> ids) {
  157 + // 1. 获得开启的活动列表
  158 + List<SeckillActivityDO> activityList = activityService.getSeckillActivityListByIds(ids);
  159 + activityList.removeIf(activity -> CommonStatusEnum.isDisable(activity.getStatus()));
  160 + if (CollUtil.isEmpty(activityList)) {
  161 + return success(Collections.emptyList());
  162 + }
  163 + // 2. 拼接返回
  164 + List<SeckillProductDO> productList = activityService.getSeckillProductListByActivityIds(
  165 + convertList(activityList, SeckillActivityDO::getId));
  166 + List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(activityList, SeckillActivityDO::getSpuId));
  167 + return success(SeckillActivityConvert.INSTANCE.convertAppList(activityList, productList, spuList));
  168 + }
  169 +
152 } 170 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/seckill/vo/activity/AppSeckillActivityRespVO.java
@@ -16,6 +16,9 @@ public class AppSeckillActivityRespVO { @@ -16,6 +16,9 @@ public class AppSeckillActivityRespVO {
16 @Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") 16 @Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
17 private Long spuId; 17 private Long spuId;
18 18
  19 + @Schema(description = "商品 SPU 名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "一个白菜")
  20 + private String spuName; // 从 SPU 的 name 读取
  21 +
19 @Schema(description = "商品图片", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 picUrl 读取 22 @Schema(description = "商品图片", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 picUrl 读取
20 example = "https://www.iocoder.cn/xx.png") 23 example = "https://www.iocoder.cn/xx.png")
21 private String picUrl; 24 private String picUrl;
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/combination/CombinationActivityConvert.java
@@ -4,6 +4,7 @@ import cn.hutool.core.util.ObjectUtil; @@ -4,6 +4,7 @@ import cn.hutool.core.util.ObjectUtil;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 5 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
6 import cn.iocoder.yudao.framework.common.util.collection.MapUtils; 6 import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
  7 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
7 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; 8 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
8 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; 9 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
9 import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; 10 import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
@@ -127,40 +128,42 @@ public interface CombinationActivityConvert { @@ -127,40 +128,42 @@ public interface CombinationActivityConvert {
127 .setSpuName(spu.getName()).setPicUrl(sku.getPicUrl()); 128 .setSpuName(spu.getName()).setPicUrl(sku.getPicUrl());
128 } 129 }
129 130
130 - List<AppCombinationActivityRespVO> convertAppList(List<CombinationActivityDO> list);  
131 -  
132 - default List<AppCombinationActivityRespVO> convertAppList(List<CombinationActivityDO> list,  
133 - List<CombinationProductDO> productList,  
134 - List<ProductSpuRespDTO> spuList) {  
135 - List<AppCombinationActivityRespVO> activityList = convertAppList(list); 131 + default List<CombinationActivityRespVO> convertList(List<CombinationActivityDO> list,
  132 + List<CombinationProductDO> productList,
  133 + List<ProductSpuRespDTO> spuList) {
  134 + List<CombinationActivityRespVO> activityList = BeanUtils.toBean(list, CombinationActivityRespVO.class);
136 Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId); 135 Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);
137 Map<Long, List<CombinationProductDO>> productMap = convertMultiMap(productList, CombinationProductDO::getActivityId); 136 Map<Long, List<CombinationProductDO>> productMap = convertMultiMap(productList, CombinationProductDO::getActivityId);
138 return CollectionUtils.convertList(activityList, item -> { 137 return CollectionUtils.convertList(activityList, item -> {
139 // 设置 product 信息 138 // 设置 product 信息
140 item.setCombinationPrice(getMinValue(productMap.get(item.getId()), CombinationProductDO::getCombinationPrice)); 139 item.setCombinationPrice(getMinValue(productMap.get(item.getId()), CombinationProductDO::getCombinationPrice));
141 // 设置 SPU 信息 140 // 设置 SPU 信息
142 - findAndThen(spuMap, item.getSpuId(), spu -> item.setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice())); 141 + findAndThen(spuMap, item.getSpuId(), spu -> item.setSpuName(spu.getName())
  142 + .setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice()));
143 return item; 143 return item;
144 }); 144 });
145 } 145 }
146 146
147 - PageResult<AppCombinationActivityRespVO> convertAppPage(PageResult<CombinationActivityDO> result);  
148 -  
149 - default PageResult<AppCombinationActivityRespVO> convertAppPage(PageResult<CombinationActivityDO> result,  
150 - List<CombinationProductDO> productList,  
151 - List<ProductSpuRespDTO> spuList) {  
152 - PageResult<AppCombinationActivityRespVO> appPage = convertAppPage(result); 147 + default List<AppCombinationActivityRespVO> convertAppList(List<CombinationActivityDO> list,
  148 + List<CombinationProductDO> productList,
  149 + List<ProductSpuRespDTO> spuList) {
  150 + List<AppCombinationActivityRespVO> activityList = BeanUtils.toBean(list, AppCombinationActivityRespVO.class);
153 Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId); 151 Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);
154 Map<Long, List<CombinationProductDO>> productMap = convertMultiMap(productList, CombinationProductDO::getActivityId); 152 Map<Long, List<CombinationProductDO>> productMap = convertMultiMap(productList, CombinationProductDO::getActivityId);
155 - List<AppCombinationActivityRespVO> list = CollectionUtils.convertList(appPage.getList(), item -> { 153 + return CollectionUtils.convertList(activityList, item -> {
156 // 设置 product 信息 154 // 设置 product 信息
157 item.setCombinationPrice(getMinValue(productMap.get(item.getId()), CombinationProductDO::getCombinationPrice)); 155 item.setCombinationPrice(getMinValue(productMap.get(item.getId()), CombinationProductDO::getCombinationPrice));
158 // 设置 SPU 信息 156 // 设置 SPU 信息
159 - findAndThen(spuMap, item.getSpuId(), spu -> item.setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice())); 157 + findAndThen(spuMap, item.getSpuId(), spu -> item.setSpuName(spu.getName())
  158 + .setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice()));
160 return item; 159 return item;
161 }); 160 });
162 - appPage.setList(list);  
163 - return appPage; 161 + }
  162 +
  163 + default PageResult<AppCombinationActivityRespVO> convertAppPage(PageResult<CombinationActivityDO> result,
  164 + List<CombinationProductDO> productList,
  165 + List<ProductSpuRespDTO> spuList) {
  166 + return new PageResult<>(convertAppList(result.getList(), productList, spuList), result.getTotal());
164 } 167 }
165 168
166 AppCombinationActivityDetailRespVO convert2(CombinationActivityDO combinationActivity); 169 AppCombinationActivityDetailRespVO convert2(CombinationActivityDO combinationActivity);
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/coupon/CouponConvert.java
@@ -4,9 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -4,9 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponRespDTO; 4 import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponRespDTO;
5 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageItemRespVO; 5 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageItemRespVO;
6 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO; 6 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
7 -import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponMatchRespVO;  
8 import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponPageReqVO; 7 import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponPageReqVO;
9 -import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponRespVO;  
10 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO; 8 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
11 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO; 9 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
12 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum; 10 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;
@@ -16,7 +14,6 @@ import org.mapstruct.factory.Mappers; @@ -16,7 +14,6 @@ import org.mapstruct.factory.Mappers;
16 14
17 import java.time.LocalDateTime; 15 import java.time.LocalDateTime;
18 import java.util.Collection; 16 import java.util.Collection;
19 -import java.util.List;  
20 17
21 /** 18 /**
22 * 优惠劵 Convert 19 * 优惠劵 Convert
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/seckill/SeckillActivityConvert.java
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.promotion.convert.seckill; @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.promotion.convert.seckill;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 4 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
5 import cn.iocoder.yudao.framework.common.util.collection.MapUtils; 5 import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6 import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; 7 import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
7 import cn.iocoder.yudao.module.promotion.api.seckill.dto.SeckillValidateJoinRespDTO; 8 import cn.iocoder.yudao.module.promotion.api.seckill.dto.SeckillValidateJoinRespDTO;
8 import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityCreateReqVO; 9 import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityCreateReqVO;
@@ -87,6 +88,38 @@ public interface SeckillActivityConvert { @@ -87,6 +88,38 @@ public interface SeckillActivityConvert {
87 return CollectionUtils.convertList(products, item -> convert(activity, item).setActivityStatus(activity.getStatus())); 88 return CollectionUtils.convertList(products, item -> convert(activity, item).setActivityStatus(activity.getStatus()));
88 } 89 }
89 90
  91 + default List<SeckillActivityRespVO> convertList(List<SeckillActivityDO> list,
  92 + List<SeckillProductDO> productList,
  93 + List<ProductSpuRespDTO> spuList) {
  94 + List<SeckillActivityRespVO> activityList = BeanUtils.toBean(list, SeckillActivityRespVO.class);
  95 + Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);
  96 + Map<Long, List<SeckillProductDO>> productMap = convertMultiMap(productList, SeckillProductDO::getActivityId);
  97 + return CollectionUtils.convertList(activityList, item -> {
  98 + // 设置 product 信息
  99 + item.setSeckillPrice(getMinValue(productMap.get(item.getId()), SeckillProductDO::getSeckillPrice));
  100 + // 设置 SPU 信息
  101 + findAndThen(spuMap, item.getSpuId(), spu -> item.setSpuName(spu.getName())
  102 + .setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice()));
  103 + return item;
  104 + });
  105 + }
  106 +
  107 + default List<AppSeckillActivityRespVO> convertAppList(List<SeckillActivityDO> list,
  108 + List<SeckillProductDO> productList,
  109 + List<ProductSpuRespDTO> spuList) {
  110 + List<AppSeckillActivityRespVO> activityList = BeanUtils.toBean(list, AppSeckillActivityRespVO.class);
  111 + Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);
  112 + Map<Long, List<SeckillProductDO>> productMap = convertMultiMap(productList, SeckillProductDO::getActivityId);
  113 + return CollectionUtils.convertList(activityList, item -> {
  114 + // 设置 product 信息
  115 + item.setSeckillPrice(getMinValue(productMap.get(item.getId()), SeckillProductDO::getSeckillPrice));
  116 + // 设置 SPU 信息
  117 + findAndThen(spuMap, item.getSpuId(), spu -> item.setSpuName(spu.getName())
  118 + .setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice()));
  119 + return item;
  120 + });
  121 + }
  122 +
90 List<SeckillProductRespVO> convertList2(List<SeckillProductDO> list); 123 List<SeckillProductRespVO> convertList2(List<SeckillProductDO> list);
91 124
92 List<AppSeckillActivityRespVO> convertList3(List<SeckillActivityDO> activityList); 125 List<AppSeckillActivityRespVO> convertList3(List<SeckillActivityDO> activityList);
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponMapper.java
1 package cn.iocoder.yudao.module.promotion.dal.mysql.coupon; 1 package cn.iocoder.yudao.module.promotion.dal.mysql.coupon;
2 2
3 import cn.hutool.core.map.MapUtil; 3 import cn.hutool.core.map.MapUtil;
4 -import cn.hutool.core.util.StrUtil;  
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 5 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
7 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
8 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO; 7 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
9 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO; 8 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
10 -import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;  
11 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 9 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
12 import com.github.yulichang.toolkit.MPJWrappers; 10 import com.github.yulichang.toolkit.MPJWrappers;
13 import org.apache.ibatis.annotations.Mapper; 11 import org.apache.ibatis.annotations.Mapper;
@@ -16,8 +14,6 @@ import java.time.LocalDateTime; @@ -16,8 +14,6 @@ import java.time.LocalDateTime;
16 import java.util.Collection; 14 import java.util.Collection;
17 import java.util.List; 15 import java.util.List;
18 import java.util.Map; 16 import java.util.Map;
19 -import java.util.function.Function;  
20 -import java.util.stream.Collectors;  
21 17
22 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; 18 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
23 19
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityService.java
@@ -103,14 +103,6 @@ public interface CombinationActivityService { @@ -103,14 +103,6 @@ public interface CombinationActivityService {
103 /** 103 /**
104 * 获取正在进行的活动分页数据 104 * 获取正在进行的活动分页数据
105 * 105 *
106 - * @param count 需要的数量  
107 - * @return 拼团活动分页  
108 - */  
109 - List<CombinationActivityDO> getCombinationActivityListByCount(Integer count);  
110 -  
111 - /**  
112 - * 获取正在进行的活动分页数据  
113 - *  
114 * @param pageParam 分页请求 106 * @param pageParam 分页请求
115 * @return 拼团活动分页 107 * @return 拼团活动分页
116 */ 108 */
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityServiceImpl.java
@@ -226,11 +226,6 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic @@ -226,11 +226,6 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
226 } 226 }
227 227
228 @Override 228 @Override
229 - public List<CombinationActivityDO> getCombinationActivityListByCount(Integer count) {  
230 - return combinationActivityMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus(), count);  
231 - }  
232 -  
233 - @Override  
234 public PageResult<CombinationActivityDO> getCombinationActivityPage(PageParam pageParam) { 229 public PageResult<CombinationActivityDO> getCombinationActivityPage(PageParam pageParam) {
235 return combinationActivityMapper.selectPage(pageParam, CommonStatusEnum.ENABLE.getStatus()); 230 return combinationActivityMapper.selectPage(pageParam, CommonStatusEnum.ENABLE.getStatus());
236 } 231 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponService.java
@@ -4,8 +4,6 @@ import cn.hutool.core.collection.CollUtil; @@ -4,8 +4,6 @@ import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.map.MapUtil; 4 import cn.hutool.core.map.MapUtil;
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO; 6 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
7 -import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponMatchReqVO;  
8 -import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponMatchRespVO;  
9 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO; 7 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
10 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO; 8 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
11 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum; 9 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
@@ -20,26 +18,6 @@ import java.util.*; @@ -20,26 +18,6 @@ import java.util.*;
20 public interface CouponService { 18 public interface CouponService {
21 19
22 /** 20 /**
23 - * 校验优惠劵,包括状态、有限期  
24 - * <p>  
25 - * 1. 如果校验通过,则返回优惠劵信息  
26 - * 2. 如果校验不通过,则直接抛出业务异常  
27 - *  
28 - * @param id 优惠劵编号  
29 - * @param userId 用户编号  
30 - * @return 优惠劵信息  
31 - */  
32 - CouponDO validCoupon(Long id, Long userId);  
33 -  
34 - /**  
35 - * 校验优惠劵,包括状态、有限期  
36 - *  
37 - * @param coupon 优惠劵  
38 - * @see #validCoupon(Long, Long) 逻辑相同,只是入参不同  
39 - */  
40 - void validCoupon(CouponDO coupon);  
41 -  
42 - /**  
43 * 使用优惠劵 21 * 使用优惠劵
44 * 22 *
45 * @param id 优惠劵编号 23 * @param id 优惠劵编号
@@ -173,15 +151,6 @@ public interface CouponService { @@ -173,15 +151,6 @@ public interface CouponService {
173 } 151 }
174 152
175 /** 153 /**
176 - * 获取用户匹配的优惠券列表  
177 - *  
178 - * @param userId 用户编号  
179 - * @param matchReqVO 匹配参数  
180 - * @return 优惠券列表  
181 - */  
182 - List<AppCouponMatchRespVO> getMatchCouponList(Long userId, AppCouponMatchReqVO matchReqVO);  
183 -  
184 - /**  
185 * 获取用户是否可以领取优惠券 154 * 获取用户是否可以领取优惠券
186 * 155 *
187 * @param userId 用户编号 156 * @param userId 用户编号
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java
@@ -12,13 +12,10 @@ import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; @@ -12,13 +12,10 @@ import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
12 import cn.iocoder.yudao.module.member.api.user.MemberUserApi; 12 import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
13 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; 13 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
14 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO; 14 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
15 -import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponMatchReqVO;  
16 -import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponMatchRespVO;  
17 import cn.iocoder.yudao.module.promotion.convert.coupon.CouponConvert; 15 import cn.iocoder.yudao.module.promotion.convert.coupon.CouponConvert;
18 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO; 16 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
19 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO; 17 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
20 import cn.iocoder.yudao.module.promotion.dal.mysql.coupon.CouponMapper; 18 import cn.iocoder.yudao.module.promotion.dal.mysql.coupon.CouponMapper;
21 -import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;  
22 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum; 19 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;
23 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum; 20 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
24 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTemplateValidityTypeEnum; 21 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTemplateValidityTypeEnum;
@@ -58,18 +55,9 @@ public class CouponServiceImpl implements CouponService { @@ -58,18 +55,9 @@ public class CouponServiceImpl implements CouponService {
58 private MemberUserApi memberUserApi; 55 private MemberUserApi memberUserApi;
59 56
60 @Override 57 @Override
61 - public CouponDO validCoupon(Long id, Long userId) {  
62 - CouponDO coupon = couponMapper.selectByIdAndUserId(id, userId);  
63 - if (coupon == null) {  
64 - throw exception(COUPON_NOT_EXISTS);  
65 - }  
66 - validCoupon(coupon);  
67 - return coupon;  
68 - }  
69 -  
70 - @Override  
71 - public void validCoupon(CouponDO coupon) { 58 + public void useCoupon(Long id, Long userId, Long orderId) {
72 // 校验状态 59 // 校验状态
  60 + CouponDO coupon = couponMapper.selectByIdAndUserId(id, userId);
73 if (ObjectUtil.notEqual(coupon.getStatus(), CouponStatusEnum.UNUSED.getStatus())) { 61 if (ObjectUtil.notEqual(coupon.getStatus(), CouponStatusEnum.UNUSED.getStatus())) {
74 throw exception(COUPON_STATUS_NOT_UNUSED); 62 throw exception(COUPON_STATUS_NOT_UNUSED);
75 } 63 }
@@ -77,12 +65,6 @@ public class CouponServiceImpl implements CouponService { @@ -77,12 +65,6 @@ public class CouponServiceImpl implements CouponService {
77 if (!LocalDateTimeUtils.isBetween(coupon.getValidStartTime(), coupon.getValidEndTime())) { 65 if (!LocalDateTimeUtils.isBetween(coupon.getValidStartTime(), coupon.getValidEndTime())) {
78 throw exception(COUPON_VALID_TIME_NOT_NOW); 66 throw exception(COUPON_VALID_TIME_NOT_NOW);
79 } 67 }
80 - }  
81 -  
82 - @Override  
83 - public void useCoupon(Long id, Long userId, Long orderId) {  
84 - // 校验优惠劵  
85 - validCoupon(id, userId);  
86 68
87 // 更新状态 69 // 更新状态
88 int updateCount = couponMapper.updateByIdAndStatus(id, CouponStatusEnum.UNUSED.getStatus(), 70 int updateCount = couponMapper.updateByIdAndStatus(id, CouponStatusEnum.UNUSED.getStatus(),
@@ -286,10 +268,8 @@ public class CouponServiceImpl implements CouponService { @@ -286,10 +268,8 @@ public class CouponServiceImpl implements CouponService {
286 if (couponTemplate == null) { 268 if (couponTemplate == null) {
287 throw exception(COUPON_TEMPLATE_NOT_EXISTS); 269 throw exception(COUPON_TEMPLATE_NOT_EXISTS);
288 } 270 }
289 - // 校验剩余数量(仅在 CouponTakeTypeEnum.USER 用户领取时)  
290 - if (CouponTakeTypeEnum.isUser(couponTemplate.getTakeCount())  
291 - && couponTemplate.getTotalCount() != null  
292 - && couponTemplate.getTakeCount() + userIds.size() > couponTemplate.getTotalCount()) { 271 + // 校验剩余数量
  272 + if (couponTemplate.getTakeCount() + userIds.size() > couponTemplate.getTotalCount()) {
293 throw exception(COUPON_TEMPLATE_NOT_ENOUGH); 273 throw exception(COUPON_TEMPLATE_NOT_ENOUGH);
294 } 274 }
295 // 校验"固定日期"的有效期类型是否过期 275 // 校验"固定日期"的有效期类型是否过期
@@ -299,7 +279,7 @@ public class CouponServiceImpl implements CouponService { @@ -299,7 +279,7 @@ public class CouponServiceImpl implements CouponService {
299 } 279 }
300 } 280 }
301 // 校验领取方式 281 // 校验领取方式
302 - if (ObjectUtil.notEqual(couponTemplate.getTakeType(), takeType.getType())) { 282 + if (ObjectUtil.notEqual(couponTemplate.getTakeType(), takeType.getValue())) {
303 throw exception(COUPON_TEMPLATE_CANNOT_TAKE); 283 throw exception(COUPON_TEMPLATE_CANNOT_TAKE);
304 } 284 }
305 } 285 }
@@ -311,7 +291,7 @@ public class CouponServiceImpl implements CouponService { @@ -311,7 +291,7 @@ public class CouponServiceImpl implements CouponService {
311 * @param couponTemplate 优惠劵模版 291 * @param couponTemplate 优惠劵模版
312 */ 292 */
313 private void removeTakeLimitUser(Set<Long> userIds, CouponTemplateDO couponTemplate) { 293 private void removeTakeLimitUser(Set<Long> userIds, CouponTemplateDO couponTemplate) {
314 - if (couponTemplate.getTakeLimitCount() == null || couponTemplate.getTakeLimitCount() <= 0) { 294 + if (couponTemplate.getTakeLimitCount() <= 0) {
315 return; 295 return;
316 } 296 }
317 // 查询已领过券的用户 297 // 查询已领过券的用户
@@ -359,48 +339,6 @@ public class CouponServiceImpl implements CouponService { @@ -359,48 +339,6 @@ public class CouponServiceImpl implements CouponService {
359 } 339 }
360 340
361 @Override 341 @Override
362 - public List<AppCouponMatchRespVO> getMatchCouponList(Long userId, AppCouponMatchReqVO matchReqVO) {  
363 - List<AppCouponMatchRespVO> couponMatchist = new ArrayList<>();  
364 - List<CouponDO> list = couponMapper.selectListByUserIdAndStatusAndUsePriceLeAndProductScope(userId,  
365 - CouponStatusEnum.UNUSED.getStatus());  
366 - for (CouponDO couponDO : list) {  
367 - AppCouponMatchRespVO appCouponMatchRespVO = CouponConvert.INSTANCE.convert2(couponDO);  
368 - Integer productScope = appCouponMatchRespVO.getProductScope();  
369 - List<Long> productScopeValues = appCouponMatchRespVO.getProductScopeValues();  
370 - Integer usePrice = appCouponMatchRespVO.getUsePrice();  
371 - if(matchReqVO.getPrice() < usePrice){  
372 - // 价格小于等于,满足价格使用条件  
373 - appCouponMatchRespVO.setMatch(false);  
374 - appCouponMatchRespVO.setDescription("未达到使用门槛");  
375 - }else if(!LocalDateTimeUtils.isBetween(appCouponMatchRespVO.getValidStartTime(), appCouponMatchRespVO.getValidEndTime())) {  
376 - //判断时间  
377 - appCouponMatchRespVO.setMatch(false);  
378 - appCouponMatchRespVO.setDescription("使用时间未到");  
379 - }else if (PromotionProductScopeEnum.ALL.getScope().equals(productScope)){  
380 - appCouponMatchRespVO.setMatch(true);  
381 - }else if (PromotionProductScopeEnum.SPU.getScope().equals(productScope)){  
382 - boolean spu = new HashSet<>(productScopeValues).containsAll(matchReqVO.getSpuIds());  
383 - if(spu){  
384 - appCouponMatchRespVO.setMatch(true);  
385 - }else {  
386 - appCouponMatchRespVO.setMatch(false);  
387 - appCouponMatchRespVO.setDescription("与商品不匹配");  
388 - }  
389 - }else if (PromotionProductScopeEnum.CATEGORY.getScope().equals(productScope)){  
390 - boolean category = new HashSet<>(productScopeValues).containsAll(matchReqVO.getCategoryIds());  
391 - if(category){  
392 - appCouponMatchRespVO.setMatch(true);  
393 - }else {  
394 - appCouponMatchRespVO.setMatch(false);  
395 - appCouponMatchRespVO.setDescription("与商品类型不匹配");  
396 - }  
397 - }  
398 - couponMatchist.add(appCouponMatchRespVO);  
399 - }  
400 - return couponMatchist;  
401 - }  
402 -  
403 - @Override  
404 public Map<Long, Boolean> getUserCanCanTakeMap(Long userId, List<CouponTemplateDO> templates) { 342 public Map<Long, Boolean> getUserCanCanTakeMap(Long userId, List<CouponTemplateDO> templates) {
405 // 1. 未登录时,都显示可以领取 343 // 1. 未登录时,都显示可以领取
406 Map<Long, Boolean> userCanTakeMap = convertMap(templates, CouponTemplateDO::getId, templateId -> true); 344 Map<Long, Boolean> userCanTakeMap = convertMap(templates, CouponTemplateDO::getId, templateId -> true);
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityService.java
@@ -8,8 +8,8 @@ import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.Se @@ -8,8 +8,8 @@ import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.Se
8 import cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity.AppSeckillActivityPageReqVO; 8 import cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity.AppSeckillActivityPageReqVO;
9 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillActivityDO; 9 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillActivityDO;
10 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillProductDO; 10 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillProductDO;
11 -  
12 import jakarta.validation.Valid; 11 import jakarta.validation.Valid;
  12 +
13 import java.time.LocalDateTime; 13 import java.time.LocalDateTime;
14 import java.util.Collection; 14 import java.util.Collection;
15 import java.util.List; 15 import java.util.List;
@@ -98,7 +98,7 @@ public interface SeckillActivityService { @@ -98,7 +98,7 @@ public interface SeckillActivityService {
98 * @param activityIds 活动编号 98 * @param activityIds 活动编号
99 * @return 活动商品列表 99 * @return 活动商品列表
100 */ 100 */
101 - List<SeckillProductDO> getSeckillProductListByActivityId(Collection<Long> activityIds); 101 + List<SeckillProductDO> getSeckillProductListByActivityIds(Collection<Long> activityIds);
102 102
103 /** 103 /**
104 * 通过活动时段编号获取指定 status 的秒杀活动 104 * 通过活动时段编号获取指定 status 的秒杀活动
@@ -139,4 +139,12 @@ public interface SeckillActivityService { @@ -139,4 +139,12 @@ public interface SeckillActivityService {
139 */ 139 */
140 List<SeckillActivityDO> getSeckillActivityBySpuIdsAndStatusAndDateTimeLt(Collection<Long> spuIds, Integer status, LocalDateTime dateTime); 140 List<SeckillActivityDO> getSeckillActivityBySpuIdsAndStatusAndDateTimeLt(Collection<Long> spuIds, Integer status, LocalDateTime dateTime);
141 141
  142 + /**
  143 + * 获得拼团活动列表
  144 + *
  145 + * @param ids 拼团活动编号数组
  146 + * @return 拼团活动的列表
  147 + */
  148 + List<SeckillActivityDO> getSeckillActivityListByIds(Collection<Long> ids);
  149 +
142 } 150 }
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java
@@ -51,6 +51,7 @@ public interface ErrorCodeConstants { @@ -51,6 +51,7 @@ public interface ErrorCodeConstants {
51 ErrorCode AFTER_SALE_REFUND_FAIL_STATUS_NOT_WAIT_REFUND = new ErrorCode(1_011_000_110, "退款失败,售后单状态不是【待退款】"); 51 ErrorCode AFTER_SALE_REFUND_FAIL_STATUS_NOT_WAIT_REFUND = new ErrorCode(1_011_000_110, "退款失败,售后单状态不是【待退款】");
52 ErrorCode AFTER_SALE_CANCEL_FAIL_STATUS_NOT_APPLY_OR_AGREE_OR_BUYER_DELIVERY = 52 ErrorCode AFTER_SALE_CANCEL_FAIL_STATUS_NOT_APPLY_OR_AGREE_OR_BUYER_DELIVERY =
53 new ErrorCode(1_011_000_111, "取消售后单失败,售后单状态不是【待审核】或【卖家同意】或【商家待收货】"); 53 new ErrorCode(1_011_000_111, "取消售后单失败,售后单状态不是【待审核】或【卖家同意】或【商家待收货】");
  54 + ErrorCode AFTER_SALE_CREATE_FAIL_ORDER_STATUS_COMBINATION_IN_PROGRESS = new ErrorCode(1_011_000_112, "订单拼团中,无法申请售后");
54 55
55 // ========== Cart 模块 1-011-002-000 ========== 56 // ========== Cart 模块 1-011-002-000 ==========
56 ErrorCode CARD_ITEM_NOT_FOUND = new ErrorCode(1_011_002_000, "购物车项不存在"); 57 ErrorCode CARD_ITEM_NOT_FOUND = new ErrorCode(1_011_002_000, "购物车项不存在");
@@ -61,7 +62,7 @@ public interface ErrorCodeConstants { @@ -61,7 +62,7 @@ public interface ErrorCodeConstants {
61 ErrorCode PRICE_CALCULATE_COUPON_NOT_MATCH_NORMAL_ORDER = new ErrorCode(1_011_003_004, "参与秒杀、拼团、砍价的营销商品,无法使用优惠劵"); 62 ErrorCode PRICE_CALCULATE_COUPON_NOT_MATCH_NORMAL_ORDER = new ErrorCode(1_011_003_004, "参与秒杀、拼团、砍价的营销商品,无法使用优惠劵");
62 ErrorCode PRICE_CALCULATE_SECKILL_TOTAL_LIMIT_COUNT = new ErrorCode(1_011_003_005, "参与秒杀的商品,超过了秒杀总限购数量"); 63 ErrorCode PRICE_CALCULATE_SECKILL_TOTAL_LIMIT_COUNT = new ErrorCode(1_011_003_005, "参与秒杀的商品,超过了秒杀总限购数量");
63 ErrorCode PRICE_CALCULATE_DELIVERY_PRICE_TYPE_ILLEGAL = new ErrorCode(1_011_003_006, "计算快递运费异常,配送方式不匹配"); 64 ErrorCode PRICE_CALCULATE_DELIVERY_PRICE_TYPE_ILLEGAL = new ErrorCode(1_011_003_006, "计算快递运费异常,配送方式不匹配");
64 - ErrorCode PRICE_CALCULATE_COUPON_PRICE_TOO_MUCH = new ErrorCode(1_011_003_007, "该优惠劵无法使用,原因:优惠金额超过订单金额"); 65 + ErrorCode PRICE_CALCULATE_COUPON_CAN_NOT_USE = new ErrorCode(1_011_003_007, "该优惠劵无法使用,原因:{}」");
65 66
66 // ========== 物流 Express 模块 1-011-004-000 ========== 67 // ========== 物流 Express 模块 1-011-004-000 ==========
67 ErrorCode EXPRESS_NOT_EXISTS = new ErrorCode(1_011_004_000, "快递公司不存在"); 68 ErrorCode EXPRESS_NOT_EXISTS = new ErrorCode(1_011_004_000, "快递公司不存在");
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/withdraw/AppBrokerageWithdrawCreateReqVO.java
@@ -44,7 +44,7 @@ public class AppBrokerageWithdrawCreateReqVO { @@ -44,7 +44,7 @@ public class AppBrokerageWithdrawCreateReqVO {
44 private String name; 44 private String name;
45 @Schema(description = "提现银行", example = "1") 45 @Schema(description = "提现银行", example = "1")
46 @NotNull(message = "提现银行不能为空", groups = {Bank.class}) 46 @NotNull(message = "提现银行不能为空", groups = {Bank.class})
47 - private Integer bankName; 47 + private String bankName;
48 @Schema(description = "开户地址", example = "海淀支行") 48 @Schema(description = "开户地址", example = "海淀支行")
49 private String bankAddress; 49 private String bankAddress;
50 50
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementRespVO.java
@@ -3,11 +3,11 @@ package cn.iocoder.yudao.module.trade.controller.app.order.vo; @@ -3,11 +3,11 @@ package cn.iocoder.yudao.module.trade.controller.app.order.vo;
3 import cn.iocoder.yudao.module.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO; 3 import cn.iocoder.yudao.module.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO;
4 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; 4 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
5 import io.swagger.v3.oas.annotations.media.Schema; 5 import io.swagger.v3.oas.annotations.media.Schema;
6 -import jakarta.validation.constraints.NotNull;  
7 import lombok.AllArgsConstructor; 6 import lombok.AllArgsConstructor;
8 import lombok.Data; 7 import lombok.Data;
9 import lombok.NoArgsConstructor; 8 import lombok.NoArgsConstructor;
10 9
  10 +import java.time.LocalDateTime;
11 import java.util.List; 11 import java.util.List;
12 12
13 @Schema(description = "用户 App - 交易订单结算信息 Response VO") 13 @Schema(description = "用户 App - 交易订单结算信息 Response VO")
@@ -20,6 +20,9 @@ public class AppTradeOrderSettlementRespVO { @@ -20,6 +20,9 @@ public class AppTradeOrderSettlementRespVO {
20 @Schema(description = "购物项数组", requiredMode = Schema.RequiredMode.REQUIRED) 20 @Schema(description = "购物项数组", requiredMode = Schema.RequiredMode.REQUIRED)
21 private List<Item> items; 21 private List<Item> items;
22 22
  23 + @Schema(description = "优惠劵数组", requiredMode = Schema.RequiredMode.REQUIRED)
  24 + private List<Coupon> coupons; // 可用 + 不可用
  25 +
23 @Schema(description = "费用", requiredMode = Schema.RequiredMode.REQUIRED) 26 @Schema(description = "费用", requiredMode = Schema.RequiredMode.REQUIRED)
24 private Price price; 27 private Price price;
25 28
@@ -117,7 +120,6 @@ public class AppTradeOrderSettlementRespVO { @@ -117,7 +120,6 @@ public class AppTradeOrderSettlementRespVO {
117 private String mobile; 120 private String mobile;
118 121
119 @Schema(description = "地区编号", requiredMode = Schema.RequiredMode.REQUIRED) 122 @Schema(description = "地区编号", requiredMode = Schema.RequiredMode.REQUIRED)
120 - @NotNull(message = "地区编号不能为空")  
121 private Long areaId; 123 private Long areaId;
122 @Schema(description = "地区名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "上海上海市普陀区") 124 @Schema(description = "地区名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "上海上海市普陀区")
123 private String areaName; 125 private String areaName;
@@ -130,4 +132,43 @@ public class AppTradeOrderSettlementRespVO { @@ -130,4 +132,43 @@ public class AppTradeOrderSettlementRespVO {
130 132
131 } 133 }
132 134
  135 + @Schema(description = "优惠劵信息")
  136 + @Data
  137 + public static class Coupon {
  138 +
  139 + @Schema(description = "优惠劵编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  140 + private Long id;
  141 +
  142 + @Schema(description = "优惠劵名", requiredMode = Schema.RequiredMode.REQUIRED, example = "春节送送送")
  143 + private String name;
  144 +
  145 + @Schema(description = "是否设置满多少金额可用", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") // 单位:分;0 - 不限制
  146 + private Integer usePrice;
  147 +
  148 + @Schema(description = "固定日期 - 生效开始时间")
  149 + private LocalDateTime validStartTime;
  150 +
  151 + @Schema(description = "固定日期 - 生效结束时间")
  152 + private LocalDateTime validEndTime;
  153 +
  154 + @Schema(description = "优惠类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  155 + private Integer discountType;
  156 +
  157 + @Schema(description = "折扣百分比", example = "80") // 例如说,80% 为 80
  158 + private Integer discountPercent;
  159 +
  160 + @Schema(description = "优惠金额", example = "10")
  161 + private Integer discountPrice;
  162 +
  163 + @Schema(description = "折扣上限", example = "100") // 单位:分,仅在 discountType 为 PERCENT 使用
  164 + private Integer discountLimitPrice;
  165 +
  166 + @Schema(description = "是否可用", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
  167 + private Boolean match;
  168 +
  169 + @Schema(description = "不可用原因", example = "优惠劵已过期")
  170 + private String mismatchReason;
  171 +
  172 + }
  173 +
133 } 174 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/AfterSaleServiceImpl.java
@@ -8,6 +8,9 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -8,6 +8,9 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
8 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; 8 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
9 import cn.iocoder.yudao.module.pay.api.refund.PayRefundApi; 9 import cn.iocoder.yudao.module.pay.api.refund.PayRefundApi;
10 import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; 10 import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO;
  11 +import cn.iocoder.yudao.module.promotion.api.combination.CombinationRecordApi;
  12 +import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordRespDTO;
  13 +import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
11 import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSaleDisagreeReqVO; 14 import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSaleDisagreeReqVO;
12 import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSalePageReqVO; 15 import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSalePageReqVO;
13 import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSaleRefuseReqVO; 16 import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSaleRefuseReqVO;
@@ -26,6 +29,7 @@ import cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleTypeEnum; @@ -26,6 +29,7 @@ import cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleTypeEnum;
26 import cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleWayEnum; 29 import cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleWayEnum;
27 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum; 30 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum;
28 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum; 31 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
  32 +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
29 import cn.iocoder.yudao.module.trade.framework.aftersale.core.annotations.AfterSaleLog; 33 import cn.iocoder.yudao.module.trade.framework.aftersale.core.annotations.AfterSaleLog;
30 import cn.iocoder.yudao.module.trade.framework.aftersale.core.utils.AfterSaleLogUtils; 34 import cn.iocoder.yudao.module.trade.framework.aftersale.core.utils.AfterSaleLogUtils;
31 import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties; 35 import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
@@ -71,6 +75,8 @@ public class AfterSaleServiceImpl implements AfterSaleService { @@ -71,6 +75,8 @@ public class AfterSaleServiceImpl implements AfterSaleService {
71 75
72 @Resource 76 @Resource
73 private PayRefundApi payRefundApi; 77 private PayRefundApi payRefundApi;
  78 + @Resource
  79 + private CombinationRecordApi combinationRecordApi;
74 80
75 @Resource 81 @Resource
76 private TradeOrderProperties tradeOrderProperties; 82 private TradeOrderProperties tradeOrderProperties;
@@ -148,6 +154,14 @@ public class AfterSaleServiceImpl implements AfterSaleService { @@ -148,6 +154,14 @@ public class AfterSaleServiceImpl implements AfterSaleService {
148 && !TradeOrderStatusEnum.haveDelivered(order.getStatus())) { 154 && !TradeOrderStatusEnum.haveDelivered(order.getStatus())) {
149 throw exception(AFTER_SALE_CREATE_FAIL_ORDER_STATUS_NO_DELIVERED); 155 throw exception(AFTER_SALE_CREATE_FAIL_ORDER_STATUS_NO_DELIVERED);
150 } 156 }
  157 + // 如果是拼团订单,则进行中不允许售后
  158 + if (TradeOrderTypeEnum.isCombination(order.getType())) {
  159 + CombinationRecordRespDTO combinationRecord = combinationRecordApi.getCombinationRecordByOrderId(
  160 + order.getUserId(), order.getId());
  161 + if (combinationRecord != null && CombinationRecordStatusEnum.isInProgress(combinationRecord.getStatus())) {
  162 + throw exception(AFTER_SALE_CREATE_FAIL_ORDER_STATUS_COMBINATION_IN_PROGRESS);
  163 + }
  164 + }
151 return orderItem; 165 return orderItem;
152 } 166 }
153 167
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java
@@ -887,7 +887,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -887,7 +887,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
887 .setAppKey(tradeOrderProperties.getPayAppKey()).setUserIp(getClientIP()) // 支付应用 887 .setAppKey(tradeOrderProperties.getPayAppKey()).setUserIp(getClientIP()) // 支付应用
888 .setMerchantOrderId(String.valueOf(order.getId())) // 支付单号 888 .setMerchantOrderId(String.valueOf(order.getId())) // 支付单号
889 .setMerchantRefundId(String.valueOf(order.getId())) 889 .setMerchantRefundId(String.valueOf(order.getId()))
890 - .setReason(TradeOrderCancelTypeEnum.COMBINATION_CLOSE.getName()).setPrice(order.getPayPrice()));// 价格信息 890 + .setReason(TradeOrderCancelTypeEnum.COMBINATION_CLOSE.getName()).setPrice(order.getPayPrice())); // 价格信息
891 } 891 }
892 892
893 @Override 893 @Override
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeCombinationOrderHandler.java
@@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.trade.service.order.handler; @@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.trade.service.order.handler;
3 import cn.hutool.core.lang.Assert; 3 import cn.hutool.core.lang.Assert;
4 import cn.iocoder.yudao.module.promotion.api.combination.CombinationRecordApi; 4 import cn.iocoder.yudao.module.promotion.api.combination.CombinationRecordApi;
5 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateRespDTO; 5 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateRespDTO;
  6 +import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordRespDTO;
  7 +import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
6 import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert; 8 import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
7 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; 9 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
8 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; 10 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
@@ -84,7 +86,9 @@ public class TradeCombinationOrderHandler implements TradeOrderHandler { @@ -84,7 +86,9 @@ public class TradeCombinationOrderHandler implements TradeOrderHandler {
84 return; 86 return;
85 } 87 }
86 // 校验订单拼团是否成功 88 // 校验订单拼团是否成功
87 - if (!combinationRecordApi.isCombinationRecordSuccess(order.getUserId(), order.getId())) { 89 + CombinationRecordRespDTO combinationRecord = combinationRecordApi.getCombinationRecordByOrderId(order.getUserId(), order.getId());
  90 + Assert.notNull(combinationRecord, "订单({})对应的拼团记录不存在", order.getId());
  91 + if (!CombinationRecordStatusEnum.isSuccess(combinationRecord.getStatus())) {
88 throw exception(ORDER_DELIVERY_FAIL_COMBINATION_RECORD_STATUS_NOT_SUCCESS); 92 throw exception(ORDER_DELIVERY_FAIL_COMBINATION_RECORD_STATUS_NOT_SUCCESS);
89 } 93 }
90 } 94 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateRespBO.java
@@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum; @@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
5 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; 5 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
6 import lombok.Data; 6 import lombok.Data;
7 7
  8 +import java.time.LocalDateTime;
8 import java.util.List; 9 import java.util.List;
9 import java.util.Map; 10 import java.util.Map;
10 11
@@ -45,9 +46,13 @@ public class TradePriceCalculateRespBO { @@ -45,9 +46,13 @@ public class TradePriceCalculateRespBO {
45 private List<Promotion> promotions; 46 private List<Promotion> promotions;
46 47
47 /** 48 /**
48 - * 优惠劵编号 49 + * 使用的优惠劵编号
49 */ 50 */
50 private Long couponId; 51 private Long couponId;
  52 + /**
  53 + * 用户的优惠劵列表(可用 + 不可用)
  54 + */
  55 + private List<Coupon> coupons;
51 56
52 /** 57 /**
53 * 会员剩余积分 58 * 会员剩余积分
@@ -340,4 +345,62 @@ public class TradePriceCalculateRespBO { @@ -340,4 +345,62 @@ public class TradePriceCalculateRespBO {
340 345
341 } 346 }
342 347
  348 + /**
  349 + * 优惠劵信息
  350 + */
  351 + @Data
  352 + public static class Coupon {
  353 +
  354 + /**
  355 + * 优惠劵编号
  356 + */
  357 + private Long id;
  358 + /**
  359 + * 优惠劵名
  360 + */
  361 + private String name;
  362 +
  363 + /**
  364 + * 是否设置满多少金额可用,单位:分
  365 + */
  366 + private Integer usePrice;
  367 +
  368 + /**
  369 + * 生效开始时间
  370 + */
  371 + private LocalDateTime validStartTime;
  372 + /**
  373 + * 生效结束时间
  374 + */
  375 + private LocalDateTime validEndTime;
  376 +
  377 + /**
  378 + * 优惠类型
  379 + */
  380 + private Integer discountType;
  381 + /**
  382 + * 折扣百分比
  383 + */
  384 + private Integer discountPercent;
  385 + /**
  386 + * 优惠金额,单位:分
  387 + */
  388 + private Integer discountPrice;
  389 + /**
  390 + * 折扣上限,单位:分
  391 + */
  392 + private Integer discountLimitPrice;
  393 +
  394 + /**
  395 + * 是否匹配
  396 + */
  397 + private Boolean match;
  398 + /**
  399 + * 不匹配的原因
  400 + */
  401 + private String mismatchReason;
  402 +
  403 + }
  404 +
  405 +
343 } 406 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeCouponPriceCalculator.java
1 package cn.iocoder.yudao.module.trade.service.price.calculator; 1 package cn.iocoder.yudao.module.trade.service.price.calculator;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 -import cn.hutool.core.lang.Assert;  
5 import cn.hutool.core.util.ObjectUtil; 4 import cn.hutool.core.util.ObjectUtil;
6 import cn.hutool.core.util.StrUtil; 5 import cn.hutool.core.util.StrUtil;
  6 +import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
  7 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
7 import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi; 8 import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi;
8 import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponRespDTO; 9 import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponRespDTO;
9 -import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponValidReqDTO;  
10 import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum; 10 import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;
11 import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum; 11 import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
12 import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum; 12 import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
  13 +import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;
13 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; 14 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
14 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; 15 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
15 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; 16 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
  17 +import jakarta.annotation.Resource;
16 import org.springframework.core.annotation.Order; 18 import org.springframework.core.annotation.Order;
17 import org.springframework.stereotype.Component; 19 import org.springframework.stereotype.Component;
18 20
19 -import jakarta.annotation.Resource;  
20 import java.util.List; 21 import java.util.List;
21 import java.util.function.Predicate; 22 import java.util.function.Predicate;
22 23
23 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 24 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  25 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
24 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; 26 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;
25 -import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COUPON_NO_MATCH_MIN_PRICE;  
26 -import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COUPON_NO_MATCH_SPU; 27 +import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_COUPON_CAN_NOT_USE;
27 import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_COUPON_NOT_MATCH_NORMAL_ORDER; 28 import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_COUPON_NOT_MATCH_NORMAL_ORDER;
28 -import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_COUPON_PRICE_TOO_MUCH;  
29 29
30 /** 30 /**
31 * 优惠劵的 {@link TradePriceCalculator} 实现类 31 * 优惠劵的 {@link TradePriceCalculator} 实现类
@@ -41,34 +41,37 @@ public class TradeCouponPriceCalculator implements TradePriceCalculator { @@ -41,34 +41,37 @@ public class TradeCouponPriceCalculator implements TradePriceCalculator {
41 41
42 @Override 42 @Override
43 public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) { 43 public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
44 - // 1.1 校验优惠劵  
45 - if (param.getCouponId() == null) {  
46 - return;  
47 - }  
48 - CouponRespDTO coupon = couponApi.validateCoupon(new CouponValidReqDTO()  
49 - .setId(param.getCouponId()).setUserId(param.getUserId()));  
50 - Assert.notNull(coupon, "校验通过的优惠劵({}),不能为空", param.getCouponId());  
51 - // 1.2 只有【普通】订单,才允许使用优惠劵 44 + // 只有【普通】订单,才允许使用优惠劵
52 if (ObjectUtil.notEqual(result.getType(), TradeOrderTypeEnum.NORMAL.getType())) { 45 if (ObjectUtil.notEqual(result.getType(), TradeOrderTypeEnum.NORMAL.getType())) {
53 - throw exception(PRICE_CALCULATE_COUPON_NOT_MATCH_NORMAL_ORDER); 46 + if (param.getCouponId() != null) {
  47 + throw exception(PRICE_CALCULATE_COUPON_NOT_MATCH_NORMAL_ORDER);
  48 + }
  49 + return;
54 } 50 }
55 51
56 - // 2.1 获得匹配的商品 SKU 数组  
57 - List<TradePriceCalculateRespBO.OrderItem> orderItems = filterMatchCouponOrderItems(result, coupon);  
58 - if (CollUtil.isEmpty(orderItems)) {  
59 - throw exception(COUPON_NO_MATCH_SPU); 52 + // 1.1 加载用户的优惠劵列表
  53 + List<CouponRespDTO> coupons = couponApi.getCouponListByUserId(param.getUserId(), CouponStatusEnum.UNUSED.getStatus());
  54 + coupons.removeIf(coupon -> LocalDateTimeUtils.beforeNow(coupon.getValidEndTime()));
  55 + // 1.2 计算优惠劵的使用条件
  56 + result.setCoupons(calculateCoupons(coupons, result));
  57 +
  58 + // 2. 校验优惠劵是否可用
  59 + if (param.getCouponId() == null) {
  60 + return;
60 } 61 }
61 - // 2.2 计算是否满足优惠劵的使用金额  
62 - Integer totalPayPrice = TradePriceCalculatorHelper.calculateTotalPayPrice(orderItems);  
63 - if (totalPayPrice < coupon.getUsePrice()) {  
64 - throw exception(COUPON_NO_MATCH_MIN_PRICE); 62 + TradePriceCalculateRespBO.Coupon couponBO = CollUtil.findOne(result.getCoupons(), item -> item.getId().equals(param.getCouponId()));
  63 + CouponRespDTO coupon = CollUtil.findOne(coupons, item -> item.getId().equals(param.getCouponId()));
  64 + if (couponBO == null || coupon == null) {
  65 + throw exception(PRICE_CALCULATE_COUPON_CAN_NOT_USE, "优惠劵不存在");
  66 + }
  67 + if (Boolean.FALSE.equals(couponBO.getMatch())) {
  68 + throw exception(PRICE_CALCULATE_COUPON_CAN_NOT_USE, couponBO.getMismatchReason());
65 } 69 }
66 70
67 // 3.1 计算可以优惠的金额 71 // 3.1 计算可以优惠的金额
  72 + List<TradePriceCalculateRespBO.OrderItem> orderItems = filterMatchCouponOrderItems(result, coupon);
  73 + Integer totalPayPrice = TradePriceCalculatorHelper.calculateTotalPayPrice(orderItems);
68 Integer couponPrice = getCouponPrice(coupon, totalPayPrice); 74 Integer couponPrice = getCouponPrice(coupon, totalPayPrice);
69 - if (couponPrice <= totalPayPrice) {  
70 - throw exception(PRICE_CALCULATE_COUPON_PRICE_TOO_MUCH);  
71 - }  
72 // 3.2 计算分摊的优惠金额 75 // 3.2 计算分摊的优惠金额
73 List<Integer> divideCouponPrices = TradePriceCalculatorHelper.dividePrice(orderItems, couponPrice); 76 List<Integer> divideCouponPrices = TradePriceCalculatorHelper.dividePrice(orderItems, couponPrice);
74 77
@@ -76,7 +79,7 @@ public class TradeCouponPriceCalculator implements TradePriceCalculator { @@ -76,7 +79,7 @@ public class TradeCouponPriceCalculator implements TradePriceCalculator {
76 result.setCouponId(param.getCouponId()); 79 result.setCouponId(param.getCouponId());
77 // 4.2 记录优惠明细 80 // 4.2 记录优惠明细
78 TradePriceCalculatorHelper.addPromotion(result, orderItems, 81 TradePriceCalculatorHelper.addPromotion(result, orderItems,
79 - param.getCouponId(), coupon.getName(), PromotionTypeEnum.COUPON.getType(), 82 + param.getCouponId(), couponBO.getName(), PromotionTypeEnum.COUPON.getType(),
80 StrUtil.format("优惠劵:省 {} 元", TradePriceCalculatorHelper.formatPrice(couponPrice)), 83 StrUtil.format("优惠劵:省 {} 元", TradePriceCalculatorHelper.formatPrice(couponPrice)),
81 divideCouponPrices); 84 divideCouponPrices);
82 // 4.3 更新 SKU 优惠金额 85 // 4.3 更新 SKU 优惠金额
@@ -88,6 +91,43 @@ public class TradeCouponPriceCalculator implements TradePriceCalculator { @@ -88,6 +91,43 @@ public class TradeCouponPriceCalculator implements TradePriceCalculator {
88 TradePriceCalculatorHelper.recountAllPrice(result); 91 TradePriceCalculatorHelper.recountAllPrice(result);
89 } 92 }
90 93
  94 + /**
  95 + * 计算用户的优惠劵列表(可用 + 不可用)
  96 + *
  97 + * @param coupons 优惠劵
  98 + * @param result 计算结果
  99 + * @return 优惠劵列表
  100 + */
  101 + private List<TradePriceCalculateRespBO.Coupon> calculateCoupons(List<CouponRespDTO> coupons,
  102 + TradePriceCalculateRespBO result) {
  103 + return convertList(coupons, coupon -> {
  104 + TradePriceCalculateRespBO.Coupon matchCoupon = BeanUtils.toBean(coupon, TradePriceCalculateRespBO.Coupon.class);
  105 + // 1.1 优惠劵未到使用时间
  106 + if (LocalDateTimeUtils.afterNow(coupon.getValidStartTime())) {
  107 + return matchCoupon.setMatch(false).setMismatchReason("优惠劵未到使用时间");
  108 + }
  109 + // 1.2 优惠劵没有匹配的商品
  110 + List<TradePriceCalculateRespBO.OrderItem> orderItems = filterMatchCouponOrderItems(result, coupon);
  111 + if (CollUtil.isEmpty(orderItems)) {
  112 + return matchCoupon.setMatch(false).setMismatchReason("优惠劵没有匹配的商品");
  113 + }
  114 + // 1.3 差 %1$,.2f 元可用优惠劵
  115 + Integer totalPayPrice = TradePriceCalculatorHelper.calculateTotalPayPrice(orderItems);
  116 + if (totalPayPrice < coupon.getUsePrice()) {
  117 + return matchCoupon.setMatch(false)
  118 + .setMismatchReason(String.format("差 %1$,.2f 元可用优惠劵", (coupon.getUsePrice() - totalPayPrice) / 100D));
  119 + }
  120 + // 1.4 优惠金额超过订单金额
  121 + Integer couponPrice = getCouponPrice(coupon, totalPayPrice);
  122 + if (couponPrice >= totalPayPrice) {
  123 + return matchCoupon.setMatch(false).setMismatchReason("优惠金额超过订单金额");
  124 + }
  125 +
  126 + // 2. 满足条件
  127 + return matchCoupon.setMatch(true);
  128 + });
  129 + }
  130 +
91 private Integer getCouponPrice(CouponRespDTO coupon, Integer totalPayPrice) { 131 private Integer getCouponPrice(CouponRespDTO coupon, Integer totalPayPrice) {
92 if (PromotionDiscountTypeEnum.PRICE.getType().equals(coupon.getDiscountType())) { // 减价 132 if (PromotionDiscountTypeEnum.PRICE.getType().equals(coupon.getDiscountType())) { // 减价
93 return coupon.getDiscountPrice(); 133 return coupon.getDiscountPrice();
yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeCouponPriceCalculatorTest.java
1 package cn.iocoder.yudao.module.trade.service.price.calculator; 1 package cn.iocoder.yudao.module.trade.service.price.calculator;
2 2
  3 +import cn.hutool.core.collection.ListUtil;
3 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; 4 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
4 import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi; 5 import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi;
5 import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponRespDTO; 6 import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponRespDTO;
6 -import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponValidReqDTO;  
7 import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum; 7 import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;
8 import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum; 8 import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
9 import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum; 9 import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
  10 +import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;
10 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; 11 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
11 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; 12 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
12 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; 13 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
@@ -14,8 +15,10 @@ import org.junit.jupiter.api.Test; @@ -14,8 +15,10 @@ import org.junit.jupiter.api.Test;
14 import org.mockito.InjectMocks; 15 import org.mockito.InjectMocks;
15 import org.mockito.Mock; 16 import org.mockito.Mock;
16 17
  18 +import java.time.Duration;
17 import java.util.ArrayList; 19 import java.util.ArrayList;
18 20
  21 +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.addTime;
19 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; 22 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
20 import static java.util.Arrays.asList; 23 import static java.util.Arrays.asList;
21 import static org.junit.jupiter.api.Assertions.assertEquals; 24 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -69,8 +72,10 @@ public class TradeCouponPriceCalculatorTest extends BaseMockitoUnitTest { @@ -69,8 +72,10 @@ public class TradeCouponPriceCalculatorTest extends BaseMockitoUnitTest {
69 CouponRespDTO coupon = randomPojo(CouponRespDTO.class, o -> o.setId(1024L).setName("程序员节") 72 CouponRespDTO coupon = randomPojo(CouponRespDTO.class, o -> o.setId(1024L).setName("程序员节")
70 .setProductScope(PromotionProductScopeEnum.SPU.getScope()).setProductScopeValues(asList(1L, 2L)) 73 .setProductScope(PromotionProductScopeEnum.SPU.getScope()).setProductScopeValues(asList(1L, 2L))
71 .setUsePrice(350).setDiscountType(PromotionDiscountTypeEnum.PERCENT.getType()) 74 .setUsePrice(350).setDiscountType(PromotionDiscountTypeEnum.PERCENT.getType())
72 - .setDiscountPercent(50).setDiscountLimitPrice(70));  
73 - when(couponApi.validateCoupon(eq(new CouponValidReqDTO().setId(1024L).setUserId(233L)))).thenReturn(coupon); 75 + .setDiscountPercent(50).setDiscountLimitPrice(70))
  76 + .setValidStartTime(addTime(Duration.ofDays(1))).setValidEndTime(addTime(Duration.ofDays(2)));
  77 + when(couponApi.getCouponListByUserId(eq(233L), eq(CouponStatusEnum.UNUSED.getStatus())))
  78 + .thenReturn(ListUtil.toList(coupon));
74 79
75 // 调用 80 // 调用
76 tradeCouponPriceCalculator.calculate(param, result); 81 tradeCouponPriceCalculator.calculate(param, result);
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java
@@ -431,9 +431,7 @@ public class PayOrderServiceImpl implements PayOrderService { @@ -431,9 +431,7 @@ public class PayOrderServiceImpl implements PayOrderService {
431 return; 431 return;
432 } 432 }
433 433
434 - // TODO 芋艿:应该 new 出来更新  
435 - order.setPrice(payPrice);  
436 - orderMapper.updateById(order); 434 + orderMapper.updateById(new PayOrderDO().setId(order.getId()).setPrice(payPrice));
437 } 435 }
438 436
439 @Override 437 @Override
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java
@@ -266,7 +266,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC @@ -266,7 +266,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC
266 String errorCode = getErrorCode(e); 266 String errorCode = getErrorCode(e);
267 String errorMessage = getErrorMessage(e); 267 String errorMessage = getErrorMessage(e);
268 return PayRefundRespDTO.failureOf(errorCode, errorMessage, 268 return PayRefundRespDTO.failureOf(errorCode, errorMessage,
269 - reqDTO.getOutTradeNo(), e.getXmlString()); 269 + reqDTO.getOutRefundNo(), e.getXmlString());
270 } 270 }
271 } 271 }
272 272
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/OperateLogApi.java
@@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogCreateReqDTO; @@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogCreateReqDTO;
5 import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogPageReqDTO; 5 import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogPageReqDTO;
6 import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogRespDTO; 6 import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogRespDTO;
7 import jakarta.validation.Valid; 7 import jakarta.validation.Valid;
  8 +import org.springframework.scheduling.annotation.Async;
8 9
9 /** 10 /**
10 * 操作日志 API 接口 11 * 操作日志 API 接口
@@ -21,6 +22,16 @@ public interface OperateLogApi { @@ -21,6 +22,16 @@ public interface OperateLogApi {
21 void createOperateLog(@Valid OperateLogCreateReqDTO createReqDTO); 22 void createOperateLog(@Valid OperateLogCreateReqDTO createReqDTO);
22 23
23 /** 24 /**
  25 + * 【异步】创建操作日志
  26 + *
  27 + * @param createReqDTO 请求
  28 + */
  29 + @Async
  30 + default void createOperateLogAsync(OperateLogCreateReqDTO createReqDTO) {
  31 + createOperateLog(createReqDTO);
  32 + }
  33 +
  34 + /**
24 * 获取指定模块的指定数据的操作日志分页 35 * 获取指定模块的指定数据的操作日志分页
25 * 36 *
26 * @param pageReqDTO 请求 37 * @param pageReqDTO 请求
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/logger/OperateLogApiImpl.java
@@ -9,7 +9,6 @@ import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO; @@ -9,7 +9,6 @@ import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO;
9 import cn.iocoder.yudao.module.system.service.logger.OperateLogService; 9 import cn.iocoder.yudao.module.system.service.logger.OperateLogService;
10 import com.fhs.core.trans.anno.TransMethodResult; 10 import com.fhs.core.trans.anno.TransMethodResult;
11 import jakarta.annotation.Resource; 11 import jakarta.annotation.Resource;
12 -import org.springframework.scheduling.annotation.Async;  
13 import org.springframework.stereotype.Service; 12 import org.springframework.stereotype.Service;
14 import org.springframework.validation.annotation.Validated; 13 import org.springframework.validation.annotation.Validated;
15 14
@@ -26,7 +25,6 @@ public class OperateLogApiImpl implements OperateLogApi { @@ -26,7 +25,6 @@ public class OperateLogApiImpl implements OperateLogApi {
26 private OperateLogService operateLogService; 25 private OperateLogService operateLogService;
27 26
28 @Override 27 @Override
29 - @Async  
30 public void createOperateLog(OperateLogCreateReqDTO createReqDTO) { 28 public void createOperateLog(OperateLogCreateReqDTO createReqDTO) {
31 operateLogService.createOperateLog(createReqDTO); 29 operateLogService.createOperateLog(createReqDTO);
32 } 30 }
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImplTest.java
@@ -144,7 +144,7 @@ public class OAuth2TokenServiceImplTest extends BaseDbAndRedisUnitTest { @@ -144,7 +144,7 @@ public class OAuth2TokenServiceImplTest extends BaseDbAndRedisUnitTest {
144 // 调用,并断言 144 // 调用,并断言
145 assertServiceException(() -> oauth2TokenService.refreshAccessToken(refreshToken, clientId), 145 assertServiceException(() -> oauth2TokenService.refreshAccessToken(refreshToken, clientId),
146 new ErrorCode(401, "刷新令牌已过期")); 146 new ErrorCode(401, "刷新令牌已过期"));
147 - assertEquals(0, oauth2RefreshTokenMapper.selectCount()); 147 + assertEquals(0, oauth2AccessTokenMapper.selectCount());
148 } 148 }
149 149
150 @Test 150 @Test
yudao-server/pom.xml
@@ -33,11 +33,11 @@ @@ -33,11 +33,11 @@
33 </dependency> 33 </dependency>
34 34
35 <!-- 会员中心。默认注释,保证编译速度 --> 35 <!-- 会员中心。默认注释,保证编译速度 -->
36 - <dependency>  
37 - <groupId>cn.iocoder.boot</groupId>  
38 - <artifactId>yudao-module-member-biz</artifactId>  
39 - <version>${revision}</version>  
40 - </dependency> 36 +<!-- <dependency>-->
  37 +<!-- <groupId>cn.iocoder.boot</groupId>-->
  38 +<!-- <artifactId>yudao-module-member-biz</artifactId>-->
  39 +<!-- <version>${revision}</version>-->
  40 +<!-- </dependency>-->
41 41
42 <!-- 数据报表。默认注释,保证编译速度 --> 42 <!-- 数据报表。默认注释,保证编译速度 -->
43 <!-- <dependency>--> 43 <!-- <dependency>-->
@@ -52,11 +52,11 @@ @@ -52,11 +52,11 @@
52 <!-- <version>${revision}</version>--> 52 <!-- <version>${revision}</version>-->
53 <!-- </dependency>--> 53 <!-- </dependency>-->
54 <!-- 支付服务。默认注释,保证编译速度 --> 54 <!-- 支付服务。默认注释,保证编译速度 -->
55 - <dependency>  
56 - <groupId>cn.iocoder.boot</groupId>  
57 - <artifactId>yudao-module-pay-biz</artifactId>  
58 - <version>${revision}</version>  
59 - </dependency> 55 +<!-- <dependency>-->
  56 +<!-- <groupId>cn.iocoder.boot</groupId>-->
  57 +<!-- <artifactId>yudao-module-pay-biz</artifactId>-->
  58 +<!-- <version>${revision}</version>-->
  59 +<!-- </dependency>-->
60 60
61 <!-- 微信公众号模块。默认注释,保证编译速度 --> 61 <!-- 微信公众号模块。默认注释,保证编译速度 -->
62 <!-- <dependency>--> 62 <!-- <dependency>-->
@@ -66,26 +66,26 @@ @@ -66,26 +66,26 @@
66 <!-- </dependency>--> 66 <!-- </dependency>-->
67 67
68 <!-- 商城相关模块。默认注释,保证编译速度--> 68 <!-- 商城相关模块。默认注释,保证编译速度-->
69 - <dependency>  
70 - <groupId>cn.iocoder.boot</groupId>  
71 - <artifactId>yudao-module-promotion-biz</artifactId>  
72 - <version>${revision}</version>  
73 - </dependency>  
74 - <dependency>  
75 - <groupId>cn.iocoder.boot</groupId>  
76 - <artifactId>yudao-module-product-biz</artifactId>  
77 - <version>${revision}</version>  
78 - </dependency>  
79 - <dependency>  
80 - <groupId>cn.iocoder.boot</groupId>  
81 - <artifactId>yudao-module-trade-biz</artifactId>  
82 - <version>${revision}</version>  
83 - </dependency>  
84 - <dependency>  
85 - <groupId>cn.iocoder.boot</groupId>  
86 - <artifactId>yudao-module-statistics-biz</artifactId>  
87 - <version>${revision}</version>  
88 - </dependency> 69 +<!-- <dependency>-->
  70 +<!-- <groupId>cn.iocoder.boot</groupId>-->
  71 +<!-- <artifactId>yudao-module-promotion-biz</artifactId>-->
  72 +<!-- <version>${revision}</version>-->
  73 +<!-- </dependency>-->
  74 +<!-- <dependency>-->
  75 +<!-- <groupId>cn.iocoder.boot</groupId>-->
  76 +<!-- <artifactId>yudao-module-product-biz</artifactId>-->
  77 +<!-- <version>${revision}</version>-->
  78 +<!-- </dependency>-->
  79 +<!-- <dependency>-->
  80 +<!-- <groupId>cn.iocoder.boot</groupId>-->
  81 +<!-- <artifactId>yudao-module-trade-biz</artifactId>-->
  82 +<!-- <version>${revision}</version>-->
  83 +<!-- </dependency>-->
  84 +<!-- <dependency>-->
  85 +<!-- <groupId>cn.iocoder.boot</groupId>-->
  86 +<!-- <artifactId>yudao-module-statistics-biz</artifactId>-->
  87 +<!-- <version>${revision}</version>-->
  88 +<!-- </dependency>-->
89 89
90 <!-- CRM 相关模块。默认注释,保证编译速度 --> 90 <!-- CRM 相关模块。默认注释,保证编译速度 -->
91 <!-- <dependency>--> 91 <!-- <dependency>-->
yudao-server/src/main/resources/application.yaml
@@ -154,9 +154,9 @@ spring: @@ -154,9 +154,9 @@ spring:
154 embedding: 154 embedding:
155 transformer: 155 transformer:
156 onnx: 156 onnx:
157 - model-uri: http://test.yudao.iocoder.cn/model.onnx 157 + model-uri: https://raw.gitcode.com/yudaocode/yudao-demo/raw/master/yudao-static/ai/model.onnx
158 tokenizer: 158 tokenizer:
159 - uri: http://test.yudao.iocoder.cn/tokenizer.json 159 + uri: https://raw.gitcode.com/yudaocode/yudao-demo/raw/master/yudao-static/ai/tokenizer.json
160 qianfan: # 文心一言 160 qianfan: # 文心一言
161 api-key: x0cuLZ7XsaTCU08vuJWO87Lg 161 api-key: x0cuLZ7XsaTCU08vuJWO87Lg
162 secret-key: R9mYF9dl9KASgi5RUq0FQt3wRisSnOcK 162 secret-key: R9mYF9dl9KASgi5RUq0FQt3wRisSnOcK
@@ -245,7 +245,7 @@ yudao: @@ -245,7 +245,7 @@ yudao:
245 codegen: 245 codegen:
246 base-package: ${yudao.info.base-package} 246 base-package: ${yudao.info.base-package}
247 db-schemas: ${spring.datasource.dynamic.datasource.master.name} 247 db-schemas: ${spring.datasource.dynamic.datasource.master.name}
248 - front-type: 10 # 前端模版的类型,参见 CodegenFrontTypeEnum 枚举类 248 + front-type: 20 # 前端模版的类型,参见 CodegenFrontTypeEnum 枚举类
249 tenant: # 多租户相关配置项 249 tenant: # 多租户相关配置项
250 enable: true 250 enable: true
251 ignore-urls: 251 ignore-urls: