Commit 2876c7ce1626d93ace59aa4c539f5a2154bf7e08
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
pom.xml
| ... | ... | @@ -15,12 +15,12 @@ |
| 15 | 15 | <!-- 各种 module 拓展 --> |
| 16 | 16 | <module>yudao-module-system</module> |
| 17 | 17 | <module>yudao-module-infra</module> |
| 18 | - <module>yudao-module-member</module> | |
| 18 | +<!-- <module>yudao-module-member</module>--> | |
| 19 | 19 | <!-- <module>yudao-module-bpm</module>--> |
| 20 | 20 | <!-- <module>yudao-module-report</module>--> |
| 21 | 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 | 24 | <!-- <module>yudao-module-crm</module>--> |
| 25 | 25 | <!-- <module>yudao-module-erp</module>--> |
| 26 | 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 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.hutool.core.exceptions.ExceptionUtil; |
| 5 | +import cn.hutool.core.util.StrUtil; | |
| 5 | 6 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 6 | 7 | import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService; |
| 7 | 8 | import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; |
| ... | ... | @@ -44,7 +45,8 @@ public class TenantJobAspect { |
| 44 | 45 | // TODO 芋艿:先通过 parallel 实现并行;1)多个租户,是一条执行日志;2)异常的情况 |
| 45 | 46 | TenantUtils.execute(tenantId, () -> { |
| 46 | 47 | try { |
| 47 | - joinPoint.proceed(); | |
| 48 | + Object result = joinPoint.proceed(); | |
| 49 | + results.put(tenantId, StrUtil.toStringOrNull(result)); | |
| 48 | 50 | } catch (Throwable e) { |
| 49 | 51 | log.error("[execute][租户({}) 执行 Job 发生异常", tenantId, e); |
| 50 | 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 | 11 | import jakarta.annotation.Resource; |
| 12 | 12 | import jakarta.servlet.http.HttpServletRequest; |
| 13 | 13 | import lombok.extern.slf4j.Slf4j; |
| 14 | -import org.springframework.scheduling.annotation.Async; | |
| 15 | 14 | |
| 16 | 15 | import java.util.List; |
| 17 | 16 | |
| ... | ... | @@ -29,7 +28,6 @@ public class LogRecordServiceImpl implements ILogRecordService { |
| 29 | 28 | private OperateLogApi operateLogApi; |
| 30 | 29 | |
| 31 | 30 | @Override |
| 32 | - @Async | |
| 33 | 31 | public void record(LogRecord logRecord) { |
| 34 | 32 | OperateLogCreateReqDTO reqDTO = new OperateLogCreateReqDTO(); |
| 35 | 33 | try { |
| ... | ... | @@ -42,7 +40,7 @@ public class LogRecordServiceImpl implements ILogRecordService { |
| 42 | 40 | fillRequestFields(reqDTO); |
| 43 | 41 | |
| 44 | 42 | // 2. 异步记录日志 |
| 45 | - operateLogApi.createOperateLog(reqDTO); | |
| 43 | + operateLogApi.createOperateLogAsync(reqDTO); | |
| 46 | 44 | } catch (Throwable ex) { |
| 47 | 45 | // 由于 @Async 异步调用,这里打印下日志,更容易跟进 |
| 48 | 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 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.apilog.core.filter.ApiAccessLogFilter; |
| 4 | 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 | 5 | import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum; |
| 10 | 6 | import cn.iocoder.yudao.framework.web.config.WebProperties; |
| 11 | 7 | import cn.iocoder.yudao.framework.web.config.YudaoWebAutoConfiguration; |
| 12 | 8 | import cn.iocoder.yudao.module.infra.api.logger.ApiAccessLogApi; |
| 13 | -import cn.iocoder.yudao.module.infra.api.logger.ApiErrorLogApi; | |
| 14 | 9 | import jakarta.servlet.Filter; |
| 15 | 10 | import org.springframework.beans.factory.annotation.Value; |
| 16 | 11 | import org.springframework.boot.autoconfigure.AutoConfiguration; |
| ... | ... | @@ -23,18 +18,6 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; |
| 23 | 18 | @AutoConfiguration(after = YudaoWebAutoConfiguration.class) |
| 24 | 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 | 22 | * 创建 ApiAccessLogFilter Bean,记录 API 请求日志 |
| 40 | 23 | */ |
| ... | ... | @@ -42,8 +25,8 @@ public class YudaoApiLogAutoConfiguration implements WebMvcConfigurer { |
| 42 | 25 | @ConditionalOnProperty(prefix = "yudao.access-log", value = "enable", matchIfMissing = true) // 允许使用 yudao.access-log.enable=false 禁用访问日志 |
| 43 | 26 | public FilterRegistrationBean<ApiAccessLogFilter> apiAccessLogFilter(WebProperties webProperties, |
| 44 | 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 | 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 | 9 | import cn.hutool.core.util.StrUtil; |
| 10 | 10 | import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; |
| 11 | 11 | import cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum; |
| 12 | -import cn.iocoder.yudao.framework.apilog.core.service.ApiAccessLogFrameworkService; | |
| 13 | 12 | import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; |
| 14 | 13 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 15 | 14 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| ... | ... | @@ -18,6 +17,7 @@ import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; |
| 18 | 17 | import cn.iocoder.yudao.framework.web.config.WebProperties; |
| 19 | 18 | import cn.iocoder.yudao.framework.web.core.filter.ApiRequestFilter; |
| 20 | 19 | import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; |
| 20 | +import cn.iocoder.yudao.module.infra.api.logger.ApiAccessLogApi; | |
| 21 | 21 | import cn.iocoder.yudao.module.infra.api.logger.dto.ApiAccessLogCreateReqDTO; |
| 22 | 22 | import com.fasterxml.jackson.databind.JsonNode; |
| 23 | 23 | import io.swagger.v3.oas.annotations.Operation; |
| ... | ... | @@ -36,7 +36,7 @@ import java.time.temporal.ChronoUnit; |
| 36 | 36 | import java.util.Iterator; |
| 37 | 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 | 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 | 53 | |
| 54 | 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 | 59 | super(webProperties); |
| 60 | 60 | this.applicationName = applicationName; |
| 61 | - this.apiAccessLogFrameworkService = apiAccessLogFrameworkService; | |
| 61 | + this.apiAccessLogApi = apiAccessLogApi; | |
| 62 | 62 | } |
| 63 | 63 | |
| 64 | 64 | @Override |
| ... | ... | @@ -91,7 +91,7 @@ public class ApiAccessLogFilter extends ApiRequestFilter { |
| 91 | 91 | if (!enable) { |
| 92 | 92 | return; |
| 93 | 93 | } |
| 94 | - apiAccessLogFrameworkService.createApiAccessLog(accessLog); | |
| 94 | + apiAccessLogApi.createApiAccessLogAsync(accessLog); | |
| 95 | 95 | } catch (Throwable th) { |
| 96 | 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 | 1 | package cn.iocoder.yudao.framework.web.config; |
| 2 | 2 | |
| 3 | -import cn.iocoder.yudao.framework.apilog.core.service.ApiErrorLogFrameworkService; | |
| 4 | 3 | import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum; |
| 5 | 4 | import cn.iocoder.yudao.framework.web.core.filter.CacheRequestBodyFilter; |
| 6 | 5 | import cn.iocoder.yudao.framework.web.core.filter.DemoFilter; |
| 7 | 6 | import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler; |
| 8 | 7 | import cn.iocoder.yudao.framework.web.core.handler.GlobalResponseBodyHandler; |
| 9 | 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 | 12 | import org.springframework.beans.factory.annotation.Value; |
| 11 | 13 | import org.springframework.boot.autoconfigure.AutoConfiguration; |
| 12 | 14 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; |
| ... | ... | @@ -25,9 +27,6 @@ import org.springframework.web.filter.CorsFilter; |
| 25 | 27 | import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; |
| 26 | 28 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; |
| 27 | 29 | |
| 28 | -import jakarta.annotation.Resource; | |
| 29 | -import jakarta.servlet.Filter; | |
| 30 | - | |
| 31 | 30 | @AutoConfiguration |
| 32 | 31 | @EnableConfigurationProperties(WebProperties.class) |
| 33 | 32 | public class YudaoWebAutoConfiguration implements WebMvcConfigurer { |
| ... | ... | @@ -59,8 +58,9 @@ public class YudaoWebAutoConfiguration implements WebMvcConfigurer { |
| 59 | 58 | } |
| 60 | 59 | |
| 61 | 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 | 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 | 5 | import cn.hutool.core.util.ObjUtil; |
| 6 | 6 | import cn.hutool.core.util.StrUtil; |
| 7 | 7 | import cn.hutool.extra.servlet.JakartaServletUtil; |
| 8 | -import cn.iocoder.yudao.framework.apilog.core.service.ApiErrorLogFrameworkService; | |
| 9 | 8 | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| 10 | 9 | import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; |
| 11 | 10 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| ... | ... | @@ -14,6 +13,7 @@ import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 14 | 13 | import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; |
| 15 | 14 | import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; |
| 16 | 15 | import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; |
| 16 | +import cn.iocoder.yudao.module.infra.api.logger.ApiErrorLogApi; | |
| 17 | 17 | import cn.iocoder.yudao.module.infra.api.logger.dto.ApiErrorLogCreateReqDTO; |
| 18 | 18 | import com.fasterxml.jackson.databind.exc.InvalidFormatException; |
| 19 | 19 | import jakarta.servlet.http.HttpServletRequest; |
| ... | ... | @@ -40,12 +40,7 @@ import java.time.LocalDateTime; |
| 40 | 40 | import java.util.Map; |
| 41 | 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 | 46 | * 全局异常处理器,将 Exception 翻译成 CommonResult + 对应的异常编号 |
| ... | ... | @@ -65,7 +60,7 @@ public class GlobalExceptionHandler { |
| 65 | 60 | @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") |
| 66 | 61 | private final String applicationName; |
| 67 | 62 | |
| 68 | - private final ApiErrorLogFrameworkService apiErrorLogFrameworkService; | |
| 63 | + private final ApiErrorLogApi apiErrorLogApi; | |
| 69 | 64 | |
| 70 | 65 | /** |
| 71 | 66 | * 处理所有异常,主要是提供给 Filter 使用 |
| ... | ... | @@ -288,7 +283,7 @@ public class GlobalExceptionHandler { |
| 288 | 283 | // 初始化 errorLog |
| 289 | 284 | buildExceptionLog(errorLog, req, e); |
| 290 | 285 | // 执行插入 errorLog |
| 291 | - apiErrorLogFrameworkService.createApiErrorLog(errorLog); | |
| 286 | + apiErrorLogApi.createApiErrorLogAsync(errorLog); | |
| 292 | 287 | } catch (Throwable th) { |
| 293 | 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 | 5 | import cn.iocoder.yudao.framework.websocket.core.handler.JsonWebSocketMessageHandler; |
| 6 | 6 | import cn.iocoder.yudao.framework.websocket.core.listener.WebSocketMessageListener; |
| 7 | 7 | import cn.iocoder.yudao.framework.websocket.core.security.LoginUserHandshakeInterceptor; |
| 8 | +import cn.iocoder.yudao.framework.websocket.core.security.WebSocketAuthorizeRequestsCustomizer; | |
| 8 | 9 | import cn.iocoder.yudao.framework.websocket.core.sender.kafka.KafkaWebSocketMessageConsumer; |
| 9 | 10 | import cn.iocoder.yudao.framework.websocket.core.sender.kafka.KafkaWebSocketMessageSender; |
| 10 | 11 | import cn.iocoder.yudao.framework.websocket.core.sender.local.LocalWebSocketMessageSender; |
| ... | ... | @@ -76,6 +77,11 @@ public class YudaoWebSocketAutoConfiguration { |
| 76 | 77 | return new WebSocketSessionManagerImpl(); |
| 77 | 78 | } |
| 78 | 79 | |
| 80 | + @Bean | |
| 81 | + public WebSocketAuthorizeRequestsCustomizer webSocketAuthorizeRequestsCustomizer(WebSocketProperties webSocketProperties) { | |
| 82 | + return new WebSocketAuthorizeRequestsCustomizer(webSocketProperties); | |
| 83 | + } | |
| 84 | + | |
| 79 | 85 | // ==================== Sender 相关 ==================== |
| 80 | 86 | |
| 81 | 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 | 13 | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO; |
| 14 | 14 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO; |
| 15 | 15 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; |
| 16 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; | |
| 16 | 17 | import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmModelMetaInfoRespDTO; |
| 17 | 18 | import org.flowable.common.engine.impl.db.SuspensionState; |
| 18 | 19 | import org.flowable.engine.repository.Deployment; |
| ... | ... | @@ -55,7 +56,7 @@ public interface BpmModelConvert { |
| 55 | 56 | BpmModelMetaInfoRespDTO metaInfo = buildMetaInfo(model); |
| 56 | 57 | BpmModelRespVO modelVO = buildModel0(model, metaInfo, null, null, null, null); |
| 57 | 58 | if (ArrayUtil.isNotEmpty(bpmnBytes)) { |
| 58 | - modelVO.setBpmnXml(new String(bpmnBytes)); | |
| 59 | + modelVO.setBpmnXml(BpmnModelUtils.getBpmnXml(bpmnBytes)); | |
| 59 | 60 | } |
| 60 | 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 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.hutool.core.util.ArrayUtil; |
| 5 | +import cn.hutool.core.util.StrUtil; | |
| 5 | 6 | import cn.iocoder.yudao.framework.common.util.number.NumberUtils; |
| 6 | 7 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; |
| 7 | 8 | import org.flowable.bpmn.converter.BpmnXMLConverter; |
| ... | ... | @@ -108,7 +109,14 @@ public class BpmnModelUtils { |
| 108 | 109 | return null; |
| 109 | 110 | } |
| 110 | 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<CrmReceivableDO> { |
| 92 | 92 | List<Map<String, Object>> result = selectMaps(new QueryWrapper<CrmReceivableDO>() |
| 93 | 93 | .select("contract_id, SUM(price) AS total_price") |
| 94 | 94 | .in("audit_status", CrmAuditStatusEnum.DRAFT.getStatus(), // 草稿 + 审批中 + 审批通过 |
| 95 | - CrmAuditStatusEnum.PROCESS, CrmAuditStatusEnum.APPROVE.getStatus()) | |
| 95 | + CrmAuditStatusEnum.PROCESS.getStatus(), CrmAuditStatusEnum.APPROVE.getStatus()) | |
| 96 | 96 | .groupBy("contract_id") |
| 97 | 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 | 3 | import cn.iocoder.yudao.module.infra.api.logger.dto.ApiAccessLogCreateReqDTO; |
| 4 | 4 | |
| 5 | 5 | import jakarta.validation.Valid; |
| 6 | +import org.springframework.scheduling.annotation.Async; | |
| 6 | 7 | |
| 7 | 8 | /** |
| 8 | 9 | * API 访问日志的 API 接口 |
| ... | ... | @@ -18,4 +19,14 @@ public interface ApiAccessLogApi { |
| 18 | 19 | */ |
| 19 | 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 | 3 | import cn.iocoder.yudao.module.infra.api.logger.dto.ApiErrorLogCreateReqDTO; |
| 4 | 4 | |
| 5 | 5 | import jakarta.validation.Valid; |
| 6 | +import org.springframework.scheduling.annotation.Async; | |
| 6 | 7 | |
| 7 | 8 | /** |
| 8 | 9 | * API 错误日志的 API 接口 |
| ... | ... | @@ -18,4 +19,14 @@ public interface ApiErrorLogApi { |
| 18 | 19 | */ |
| 19 | 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 | 22 | ErrorCode JOB_CHANGE_STATUS_EQUALS = new ErrorCode(1_001_001_003, "定时任务已经处于该状态,无需修改"); |
| 23 | 23 | ErrorCode JOB_UPDATE_ONLY_NORMAL_STATUS = new ErrorCode(1_001_001_004, "只有开启状态的任务,才可以修改"); |
| 24 | 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 | 26 | ErrorCode JOB_HANDLER_BEAN_TYPE_ERROR = new ErrorCode(1_001_001_007, "定时任务的处理器 Bean 类型不正确,未实现 JobHandler 接口"); |
| 27 | 27 | |
| 28 | 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
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 | 135 | vue3FilePath("views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}List.vue")) |
| 136 | 136 | .put(CodegenFrontTypeEnum.VUE3.getType(), vue3TemplatePath("api/api.ts"), |
| 137 | 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 | 138 | // Vue3 vben 模版 |
| 148 | 139 | .put(CodegenFrontTypeEnum.VUE3_VBEN.getType(), vue3VbenTemplatePath("views/data.ts"), |
| 149 | 140 | vue3FilePath("views/${table.moduleName}/${table.businessName}/${classNameVar}.data.ts")) |
| ... | ... | @@ -496,10 +487,6 @@ public class CodegenEngine { |
| 496 | 487 | "src/" + path; |
| 497 | 488 | } |
| 498 | 489 | |
| 499 | - private static String vue3SchemaTemplatePath(String path) { | |
| 500 | - return "codegen/vue3_schema/" + path + ".vm"; | |
| 501 | - } | |
| 502 | - | |
| 503 | 490 | private static String vue3VbenTemplatePath(String path) { |
| 504 | 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 | 14 | import jakarta.annotation.Resource; |
| 15 | 15 | import lombok.extern.slf4j.Slf4j; |
| 16 | 16 | import org.quartz.SchedulerException; |
| 17 | +import org.springframework.beans.factory.NoSuchBeanDefinitionException; | |
| 17 | 18 | import org.springframework.stereotype.Service; |
| 18 | 19 | import org.springframework.transaction.annotation.Transactional; |
| 19 | 20 | import org.springframework.validation.annotation.Validated; |
| ... | ... | @@ -91,13 +92,15 @@ public class JobServiceImpl implements JobService { |
| 91 | 92 | } |
| 92 | 93 | |
| 93 | 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 | 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 | 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 | 42 | #foreach($column in $columns) |
| 43 | 43 | #if ($column.listOperation) |
| 44 | 44 | #set ($dictType=$column.dictType) |
| 45 | + #set ($javaType = $column.javaType) | |
| 45 | 46 | #set ($javaField = $column.javaField) |
| 46 | 47 | #set ($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) |
| 47 | 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 | 57 | label: '${comment}', |
| 50 | 58 | field: '${javaField}', |
| ... | ... | @@ -54,16 +62,16 @@ export const searchFormSchema: FormSchema[] = [ |
| 54 | 62 | component: 'Select', |
| 55 | 63 | componentProps: { |
| 56 | 64 | #if ("" != $dictType)## 设置了 dictType 数据字典的情况 |
| 57 | - options: getDictOptions(DICT_TYPE.$dictType.toUpperCase()), | |
| 65 | + options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'), | |
| 58 | 66 | #else## 未设置 dictType 数据字典的情况 |
| 59 | 67 | options: [], |
| 60 | 68 | #end |
| 61 | 69 | }, |
| 62 | 70 | #elseif ($column.htmlType == "radio") |
| 63 | - component: 'Radio', | |
| 71 | + component: 'RadioButtonGroup', | |
| 64 | 72 | componentProps: { |
| 65 | 73 | #if ("" != $dictType)## 设置了 dictType 数据字典的情况 |
| 66 | - options: getDictOptions(DICT_TYPE.$dictType.toUpperCase()), | |
| 74 | + options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'), | |
| 67 | 75 | #else## 未设置 dictType 数据字典的情况 |
| 68 | 76 | options: [], |
| 69 | 77 | #end |
| ... | ... | @@ -87,9 +95,17 @@ export const createFormSchema: FormSchema[] = [ |
| 87 | 95 | #foreach($column in $columns) |
| 88 | 96 | #if ($column.createOperation) |
| 89 | 97 | #set ($dictType = $column.dictType) |
| 98 | + #set ($javaType = $column.javaType) | |
| 90 | 99 | #set ($javaField = $column.javaField) |
| 91 | 100 | #set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) |
| 92 | 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 | 109 | #if (!$column.primaryKey)## 忽略主键,不用在表单里 |
| 94 | 110 | { |
| 95 | 111 | label: '${comment}', |
| ... | ... | @@ -117,7 +133,7 @@ export const createFormSchema: FormSchema[] = [ |
| 117 | 133 | component: 'Select', |
| 118 | 134 | componentProps: { |
| 119 | 135 | #if ("" != $dictType)## 有数据字典 |
| 120 | - options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'number'), | |
| 136 | + options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'), | |
| 121 | 137 | #else##没数据字典 |
| 122 | 138 | options:[], |
| 123 | 139 | #end |
| ... | ... | @@ -126,7 +142,7 @@ export const createFormSchema: FormSchema[] = [ |
| 126 | 142 | component: 'Checkbox', |
| 127 | 143 | componentProps: { |
| 128 | 144 | #if ("" != $dictType)## 有数据字典 |
| 129 | - options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'number'), | |
| 145 | + options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'), | |
| 130 | 146 | #else##没数据字典 |
| 131 | 147 | options:[], |
| 132 | 148 | #end |
| ... | ... | @@ -135,7 +151,7 @@ export const createFormSchema: FormSchema[] = [ |
| 135 | 151 | component: 'RadioButtonGroup', |
| 136 | 152 | componentProps: { |
| 137 | 153 | #if ("" != $dictType)## 有数据字典 |
| 138 | - options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'number'), | |
| 154 | + options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'), | |
| 139 | 155 | #else##没数据字典 |
| 140 | 156 | options:[], |
| 141 | 157 | #end |
| ... | ... | @@ -166,9 +182,17 @@ export const updateFormSchema: FormSchema[] = [ |
| 166 | 182 | #foreach($column in $columns) |
| 167 | 183 | #if ($column.updateOperation) |
| 168 | 184 | #set ($dictType = $column.dictType) |
| 185 | +#set ($javaType = $column.javaType) | |
| 169 | 186 | #set ($javaField = $column.javaField) |
| 170 | 187 | #set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) |
| 171 | 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 | 196 | #if (!$column.primaryKey)## 忽略主键,不用在表单里 |
| 173 | 197 | { |
| 174 | 198 | label: '${comment}', |
| ... | ... | @@ -196,7 +220,7 @@ export const updateFormSchema: FormSchema[] = [ |
| 196 | 220 | component: 'Select', |
| 197 | 221 | componentProps: { |
| 198 | 222 | #if ("" != $dictType)## 有数据字典 |
| 199 | - options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'number'), | |
| 223 | + options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'), | |
| 200 | 224 | #else##没数据字典 |
| 201 | 225 | options:[], |
| 202 | 226 | #end |
| ... | ... | @@ -205,7 +229,7 @@ export const updateFormSchema: FormSchema[] = [ |
| 205 | 229 | component: 'Checkbox', |
| 206 | 230 | componentProps: { |
| 207 | 231 | #if ("" != $dictType)## 有数据字典 |
| 208 | - options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'number'), | |
| 232 | + options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'), | |
| 209 | 233 | #else##没数据字典 |
| 210 | 234 | options:[], |
| 211 | 235 | #end |
| ... | ... | @@ -214,7 +238,7 @@ export const updateFormSchema: FormSchema[] = [ |
| 214 | 238 | component: 'RadioButtonGroup', |
| 215 | 239 | componentProps: { |
| 216 | 240 | #if ("" != $dictType)## 有数据字典 |
| 217 | - options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'number'), | |
| 241 | + options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'), | |
| 218 | 242 | #else##没数据字典 |
| 219 | 243 | options:[], |
| 220 | 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 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO; |
| 4 | 4 | import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateRespDTO; |
| 5 | +import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordRespDTO; | |
| 5 | 6 | import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationValidateJoinRespDTO; |
| 6 | - | |
| 7 | 7 | import jakarta.validation.Valid; |
| 8 | 8 | |
| 9 | 9 | /** |
| ... | ... | @@ -33,13 +33,13 @@ public interface CombinationRecordApi { |
| 33 | 33 | CombinationRecordCreateRespDTO createCombinationRecord(@Valid CombinationRecordCreateReqDTO reqDTO); |
| 34 | 34 | |
| 35 | 35 | /** |
| 36 | - * 查询拼团记录是否成功 | |
| 36 | + * 基于订单编号,查询拼团记录 | |
| 37 | 37 | * |
| 38 | 38 | * @param userId 用户编号 |
| 39 | 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 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponRespDTO; |
| 4 | 4 | import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO; |
| 5 | -import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponValidReqDTO; | |
| 6 | 5 | import jakarta.validation.Valid; |
| 7 | 6 | |
| 8 | 7 | import java.util.List; |
| ... | ... | @@ -16,6 +15,15 @@ import java.util.Map; |
| 16 | 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 | 29 | * @param useReqDTO 使用请求 |
| ... | ... | @@ -30,14 +38,6 @@ public interface CouponApi { |
| 30 | 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 | 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 | 21 | ErrorCode BANNER_NOT_EXISTS = new ErrorCode(1_013_002_000, "Banner 不存在"); |
| 22 | 22 | |
| 23 | 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 | 25 | // ========== 优惠劵模板 1-013-004-000 ========== |
| 28 | 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 | 1 | package cn.iocoder.yudao.module.promotion.api.combination; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | |
| 3 | 4 | import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO; |
| 4 | 5 | import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateRespDTO; |
| 6 | +import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordRespDTO; | |
| 5 | 7 | import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationValidateJoinRespDTO; |
| 6 | 8 | import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert; |
| 7 | 9 | import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO; |
| 8 | -import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum; | |
| 9 | 10 | import cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordService; |
| 10 | 11 | import jakarta.annotation.Resource; |
| 11 | 12 | import org.springframework.stereotype.Service; |
| 12 | 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 | 16 | * 拼团活动 API 实现类 |
| 19 | 17 | * |
| ... | ... | @@ -37,12 +35,9 @@ public class CombinationRecordApiImpl implements CombinationRecordApi { |
| 37 | 35 | } |
| 38 | 36 | |
| 39 | 37 | @Override |
| 40 | - public boolean isCombinationRecordSuccess(Long userId, Long orderId) { | |
| 38 | + public CombinationRecordRespDTO getCombinationRecordByOrderId(Long userId, Long orderId) { | |
| 41 | 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 | 43 | @Override | ... | ... |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/CouponApiImpl.java
| 1 | 1 | package cn.iocoder.yudao.module.promotion.api.coupon; |
| 2 | 2 | |
| 3 | 3 | |
| 4 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | |
| 4 | 5 | import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponRespDTO; |
| 5 | 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 | 7 | import cn.iocoder.yudao.module.promotion.service.coupon.CouponService; |
| 10 | 8 | import jakarta.annotation.Resource; |
| 11 | 9 | import org.springframework.stereotype.Service; |
| ... | ... | @@ -27,6 +25,11 @@ public class CouponApiImpl implements CouponApi { |
| 27 | 25 | private CouponService couponService; |
| 28 | 26 | |
| 29 | 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 | 33 | public void useCoupon(CouponUseReqDTO useReqDTO) { |
| 31 | 34 | couponService.useCoupon(useReqDTO.getId(), useReqDTO.getUserId(), |
| 32 | 35 | useReqDTO.getOrderId()); |
| ... | ... | @@ -38,12 +41,6 @@ public class CouponApiImpl implements CouponApi { |
| 38 | 41 | } |
| 39 | 42 | |
| 40 | 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 | 44 | public List<Long> takeCouponsByAdmin(Map<Long, Integer> giveCoupons, Long userId) { |
| 48 | 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 | 1 | package cn.iocoder.yudao.module.promotion.controller.admin.combination; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | |
| 4 | 5 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 5 | 6 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | 7 | import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; |
| ... | ... | @@ -16,18 +17,20 @@ import cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordSe |
| 16 | 17 | import io.swagger.v3.oas.annotations.Operation; |
| 17 | 18 | import io.swagger.v3.oas.annotations.Parameter; |
| 18 | 19 | import io.swagger.v3.oas.annotations.tags.Tag; |
| 20 | +import jakarta.annotation.Resource; | |
| 21 | +import jakarta.validation.Valid; | |
| 19 | 22 | import org.springframework.security.access.prepost.PreAuthorize; |
| 20 | 23 | import org.springframework.validation.annotation.Validated; |
| 21 | 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 | 27 | import java.util.List; |
| 26 | 28 | import java.util.Map; |
| 27 | 29 | import java.util.Set; |
| 28 | 30 | |
| 29 | 31 | import static cn.hutool.core.collection.CollectionUtil.newArrayList; |
| 30 | 32 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 33 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | |
| 31 | 34 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; |
| 32 | 35 | |
| 33 | 36 | @Tag(name = "管理后台 - 拼团活动") |
| ... | ... | @@ -87,6 +90,23 @@ public class CombinationActivityController { |
| 87 | 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 | 110 | @GetMapping("/page") |
| 91 | 111 | @Operation(summary = "获得拼团活动分页") |
| 92 | 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 | 27 | @Schema(description = "拼团商品", requiredMode = Schema.RequiredMode.REQUIRED) |
| 28 | 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 | 1 | package cn.iocoder.yudao.module.promotion.controller.admin.seckill; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | |
| 4 | 5 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 5 | 6 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | 7 | import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; |
| ... | ... | @@ -13,15 +14,17 @@ import cn.iocoder.yudao.module.promotion.service.seckill.SeckillActivityService; |
| 13 | 14 | import io.swagger.v3.oas.annotations.Operation; |
| 14 | 15 | import io.swagger.v3.oas.annotations.Parameter; |
| 15 | 16 | import io.swagger.v3.oas.annotations.tags.Tag; |
| 17 | +import jakarta.annotation.Resource; | |
| 18 | +import jakarta.validation.Valid; | |
| 16 | 19 | import org.springframework.security.access.prepost.PreAuthorize; |
| 17 | 20 | import org.springframework.validation.annotation.Validated; |
| 18 | 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 | 24 | import java.util.List; |
| 23 | 25 | |
| 24 | 26 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 27 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | |
| 25 | 28 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; |
| 26 | 29 | |
| 27 | 30 | @Tag(name = "管理后台 - 秒杀活动") |
| ... | ... | @@ -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 | 96 | convertSet(pageResult.getList(), SeckillActivityDO::getId)); |
| 94 | 97 | List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList( |
| 95 | 98 | convertSet(pageResult.getList(), SeckillActivityDO::getSpuId)); |
| 96 | 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 | 54 | example = "50") |
| 55 | 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 | 14 | import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO; |
| 15 | 15 | import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO; |
| 16 | 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 | 17 | import io.swagger.v3.oas.annotations.Operation; |
| 20 | 18 | import io.swagger.v3.oas.annotations.Parameter; |
| 21 | 19 | import io.swagger.v3.oas.annotations.tags.Tag; |
| 20 | +import jakarta.annotation.Resource; | |
| 22 | 21 | import org.springframework.validation.annotation.Validated; |
| 23 | 22 | import org.springframework.web.bind.annotation.GetMapping; |
| 24 | 23 | import org.springframework.web.bind.annotation.RequestMapping; |
| 25 | 24 | import org.springframework.web.bind.annotation.RequestParam; |
| 26 | 25 | import org.springframework.web.bind.annotation.RestController; |
| 27 | 26 | |
| 28 | -import jakarta.annotation.Resource; | |
| 29 | -import java.time.Duration; | |
| 30 | 27 | import java.util.Collections; |
| 31 | 28 | import java.util.List; |
| 32 | 29 | |
| 33 | 30 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 34 | -import static cn.iocoder.yudao.framework.common.util.cache.CacheUtils.buildAsyncReloadingCache; | |
| 35 | 31 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; |
| 36 | 32 | |
| 37 | 33 | @Tag(name = "用户 APP - 拼团活动") |
| ... | ... | @@ -40,45 +36,12 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. |
| 40 | 36 | @Validated |
| 41 | 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 | 39 | @Resource |
| 57 | 40 | private CombinationActivityService activityService; |
| 58 | 41 | |
| 59 | 42 | @Resource |
| 60 | 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 | 45 | @GetMapping("/page") |
| 83 | 46 | @Operation(summary = "获得拼团活动分页") |
| 84 | 47 | public CommonResult<PageResult<AppCombinationActivityRespVO>> getCombinationActivityPage(PageParam pageParam) { |
| ... | ... | @@ -93,6 +56,23 @@ public class AppCombinationActivityController { |
| 93 | 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 | 76 | @GetMapping("/get-detail") |
| 97 | 77 | @Operation(summary = "获得拼团活动明细") |
| 98 | 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 | 19 | @Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") |
| 20 | 20 | private Long spuId; |
| 21 | 21 | |
| 22 | + @Schema(description = "商品 SPU 名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "一个白菜") | |
| 23 | + private String spuName; // 从 SPU 的 name 读取 | |
| 22 | 24 | @Schema(description = "商品图片", requiredMode = Schema.RequiredMode.REQUIRED, example = "4096") |
| 23 | - // 从 SPU 的 picUrl 读取 | |
| 24 | - private String picUrl; | |
| 25 | - | |
| 25 | + private String picUrl; // 从 SPU 的 picUrl 读取 | |
| 26 | 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 | 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 | 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | 6 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 7 | 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 | 11 | import cn.iocoder.yudao.module.promotion.convert.coupon.CouponConvert; |
| 10 | 12 | import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO; |
| 11 | 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 | 17 | import io.swagger.v3.oas.annotations.Operation; |
| 16 | 18 | import io.swagger.v3.oas.annotations.Parameter; |
| 17 | 19 | import io.swagger.v3.oas.annotations.tags.Tag; |
| 20 | +import jakarta.annotation.Resource; | |
| 21 | +import jakarta.validation.Valid; | |
| 18 | 22 | import org.springframework.validation.annotation.Validated; |
| 19 | 23 | import org.springframework.web.bind.annotation.*; |
| 20 | 24 | |
| 21 | -import jakarta.annotation.Resource; | |
| 22 | -import jakarta.validation.Valid; | |
| 23 | 25 | import java.util.Collections; |
| 24 | -import java.util.List; | |
| 25 | 26 | |
| 26 | 27 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 27 | 28 | import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; |
| ... | ... | @@ -56,14 +57,6 @@ public class AppCouponController { |
| 56 | 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 | 60 | @GetMapping("/page") |
| 68 | 61 | @Operation(summary = "我的优惠劵列表") |
| 69 | 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 | 3 | import io.swagger.v3.oas.annotations.media.Schema; |
| 4 | 4 | import lombok.Data; |
| 5 | 5 | |
| 6 | -import jakarta.validation.constraints.Min; | |
| 7 | 6 | import java.time.LocalDateTime; |
| 8 | 7 | import java.util.List; |
| 9 | 8 | |
| ... | ... | @@ -42,7 +41,6 @@ public class AppCouponRespVO { |
| 42 | 41 | private Integer discountPercent; |
| 43 | 42 | |
| 44 | 43 | @Schema(description = "优惠金额", example = "10") |
| 45 | - @Min(value = 0, message = "优惠金额需要大于等于 0") | |
| 46 | 44 | private Integer discountPrice; |
| 47 | 45 | |
| 48 | 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 | 23 | import io.swagger.v3.oas.annotations.Operation; |
| 24 | 24 | import io.swagger.v3.oas.annotations.Parameter; |
| 25 | 25 | import io.swagger.v3.oas.annotations.tags.Tag; |
| 26 | +import jakarta.annotation.Resource; | |
| 26 | 27 | import org.springframework.context.annotation.Lazy; |
| 27 | 28 | import org.springframework.validation.annotation.Validated; |
| 28 | 29 | import org.springframework.web.bind.annotation.GetMapping; |
| ... | ... | @@ -30,11 +31,11 @@ import org.springframework.web.bind.annotation.RequestMapping; |
| 30 | 31 | import org.springframework.web.bind.annotation.RequestParam; |
| 31 | 32 | import org.springframework.web.bind.annotation.RestController; |
| 32 | 33 | |
| 33 | -import jakarta.annotation.Resource; | |
| 34 | 34 | import java.time.Duration; |
| 35 | 35 | import java.time.LocalDate; |
| 36 | 36 | import java.time.LocalDateTime; |
| 37 | 37 | import java.time.LocalTime; |
| 38 | +import java.util.Collections; | |
| 38 | 39 | import java.util.List; |
| 39 | 40 | |
| 40 | 41 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| ... | ... | @@ -86,7 +87,7 @@ public class AppSeckillActivityController { |
| 86 | 87 | |
| 87 | 88 | // 2.1 查询满足当前阶段的活动 |
| 88 | 89 | List<SeckillActivityDO> activityList = activityService.getSeckillActivityListByConfigIdAndStatus(config.getId(), CommonStatusEnum.ENABLE.getStatus()); |
| 89 | - List<SeckillProductDO> productList = activityService.getSeckillProductListByActivityId( | |
| 90 | + List<SeckillProductDO> productList = activityService.getSeckillProductListByActivityIds( | |
| 90 | 91 | convertList(activityList, SeckillActivityDO::getId)); |
| 91 | 92 | // 2.2 获取 spu 信息 |
| 92 | 93 | List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(activityList, SeckillActivityDO::getSpuId)); |
| ... | ... | @@ -101,7 +102,7 @@ public class AppSeckillActivityController { |
| 101 | 102 | if (CollUtil.isEmpty(pageResult.getList())) { |
| 102 | 103 | return success(PageResult.empty(pageResult.getTotal())); |
| 103 | 104 | } |
| 104 | - List<SeckillProductDO> productList = activityService.getSeckillProductListByActivityId( | |
| 105 | + List<SeckillProductDO> productList = activityService.getSeckillProductListByActivityIds( | |
| 105 | 106 | convertList(pageResult.getList(), SeckillActivityDO::getId)); |
| 106 | 107 | |
| 107 | 108 | // 2. 拼接数据 |
| ... | ... | @@ -149,4 +150,21 @@ public class AppSeckillActivityController { |
| 149 | 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 | 16 | @Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") |
| 17 | 17 | private Long spuId; |
| 18 | 18 | |
| 19 | + @Schema(description = "商品 SPU 名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "一个白菜") | |
| 20 | + private String spuName; // 从 SPU 的 name 读取 | |
| 21 | + | |
| 19 | 22 | @Schema(description = "商品图片", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 picUrl 读取 |
| 20 | 23 | example = "https://www.iocoder.cn/xx.png") |
| 21 | 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 | 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | 5 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 6 | 6 | import cn.iocoder.yudao.framework.common.util.collection.MapUtils; |
| 7 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | |
| 7 | 8 | import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; |
| 8 | 9 | import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; |
| 9 | 10 | import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; |
| ... | ... | @@ -127,40 +128,42 @@ public interface CombinationActivityConvert { |
| 127 | 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 | 135 | Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId); |
| 137 | 136 | Map<Long, List<CombinationProductDO>> productMap = convertMultiMap(productList, CombinationProductDO::getActivityId); |
| 138 | 137 | return CollectionUtils.convertList(activityList, item -> { |
| 139 | 138 | // 设置 product 信息 |
| 140 | 139 | item.setCombinationPrice(getMinValue(productMap.get(item.getId()), CombinationProductDO::getCombinationPrice)); |
| 141 | 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 | 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 | 151 | Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId); |
| 154 | 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 | 154 | // 设置 product 信息 |
| 157 | 155 | item.setCombinationPrice(getMinValue(productMap.get(item.getId()), CombinationProductDO::getCombinationPrice)); |
| 158 | 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 | 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 | 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 | 4 | import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponRespDTO; |
| 5 | 5 | import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageItemRespVO; |
| 6 | 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 | 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 | 8 | import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO; |
| 11 | 9 | import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO; |
| 12 | 10 | import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum; |
| ... | ... | @@ -16,7 +14,6 @@ import org.mapstruct.factory.Mappers; |
| 16 | 14 | |
| 17 | 15 | import java.time.LocalDateTime; |
| 18 | 16 | import java.util.Collection; |
| 19 | -import java.util.List; | |
| 20 | 17 | |
| 21 | 18 | /** |
| 22 | 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 | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | 4 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 5 | 5 | import cn.iocoder.yudao.framework.common.util.collection.MapUtils; |
| 6 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | |
| 6 | 7 | import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; |
| 7 | 8 | import cn.iocoder.yudao.module.promotion.api.seckill.dto.SeckillValidateJoinRespDTO; |
| 8 | 9 | import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityCreateReqVO; |
| ... | ... | @@ -87,6 +88,38 @@ public interface SeckillActivityConvert { |
| 87 | 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 | 123 | List<SeckillProductRespVO> convertList2(List<SeckillProductDO> list); |
| 91 | 124 | |
| 92 | 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 | 1 | package cn.iocoder.yudao.module.promotion.dal.mysql.coupon; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.map.MapUtil; |
| 4 | -import cn.hutool.core.util.StrUtil; | |
| 5 | 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | 5 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| 7 | 6 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| 8 | 7 | import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO; |
| 9 | 8 | import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO; |
| 10 | -import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum; | |
| 11 | 9 | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| 12 | 10 | import com.github.yulichang.toolkit.MPJWrappers; |
| 13 | 11 | import org.apache.ibatis.annotations.Mapper; |
| ... | ... | @@ -16,8 +14,6 @@ import java.time.LocalDateTime; |
| 16 | 14 | import java.util.Collection; |
| 17 | 15 | import java.util.List; |
| 18 | 16 | import java.util.Map; |
| 19 | -import java.util.function.Function; | |
| 20 | -import java.util.stream.Collectors; | |
| 21 | 17 | |
| 22 | 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 | 103 | /** |
| 104 | 104 | * 获取正在进行的活动分页数据 |
| 105 | 105 | * |
| 106 | - * @param count 需要的数量 | |
| 107 | - * @return 拼团活动分页 | |
| 108 | - */ | |
| 109 | - List<CombinationActivityDO> getCombinationActivityListByCount(Integer count); | |
| 110 | - | |
| 111 | - /** | |
| 112 | - * 获取正在进行的活动分页数据 | |
| 113 | - * | |
| 114 | 106 | * @param pageParam 分页请求 |
| 115 | 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 | 226 | } |
| 227 | 227 | |
| 228 | 228 | @Override |
| 229 | - public List<CombinationActivityDO> getCombinationActivityListByCount(Integer count) { | |
| 230 | - return combinationActivityMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus(), count); | |
| 231 | - } | |
| 232 | - | |
| 233 | - @Override | |
| 234 | 229 | public PageResult<CombinationActivityDO> getCombinationActivityPage(PageParam pageParam) { |
| 235 | 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 | 4 | import cn.hutool.core.map.MapUtil; |
| 5 | 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | 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 | 7 | import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO; |
| 10 | 8 | import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO; |
| 11 | 9 | import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum; |
| ... | ... | @@ -20,26 +18,6 @@ import java.util.*; |
| 20 | 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 | 23 | * @param id 优惠劵编号 |
| ... | ... | @@ -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 | 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 | 12 | import cn.iocoder.yudao.module.member.api.user.MemberUserApi; |
| 13 | 13 | import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; |
| 14 | 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 | 15 | import cn.iocoder.yudao.module.promotion.convert.coupon.CouponConvert; |
| 18 | 16 | import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO; |
| 19 | 17 | import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO; |
| 20 | 18 | import cn.iocoder.yudao.module.promotion.dal.mysql.coupon.CouponMapper; |
| 21 | -import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum; | |
| 22 | 19 | import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum; |
| 23 | 20 | import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum; |
| 24 | 21 | import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTemplateValidityTypeEnum; |
| ... | ... | @@ -58,18 +55,9 @@ public class CouponServiceImpl implements CouponService { |
| 58 | 55 | private MemberUserApi memberUserApi; |
| 59 | 56 | |
| 60 | 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 | 61 | if (ObjectUtil.notEqual(coupon.getStatus(), CouponStatusEnum.UNUSED.getStatus())) { |
| 74 | 62 | throw exception(COUPON_STATUS_NOT_UNUSED); |
| 75 | 63 | } |
| ... | ... | @@ -77,12 +65,6 @@ public class CouponServiceImpl implements CouponService { |
| 77 | 65 | if (!LocalDateTimeUtils.isBetween(coupon.getValidStartTime(), coupon.getValidEndTime())) { |
| 78 | 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 | 70 | int updateCount = couponMapper.updateByIdAndStatus(id, CouponStatusEnum.UNUSED.getStatus(), |
| ... | ... | @@ -286,10 +268,8 @@ public class CouponServiceImpl implements CouponService { |
| 286 | 268 | if (couponTemplate == null) { |
| 287 | 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 | 273 | throw exception(COUPON_TEMPLATE_NOT_ENOUGH); |
| 294 | 274 | } |
| 295 | 275 | // 校验"固定日期"的有效期类型是否过期 |
| ... | ... | @@ -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 | 283 | throw exception(COUPON_TEMPLATE_CANNOT_TAKE); |
| 304 | 284 | } |
| 305 | 285 | } |
| ... | ... | @@ -311,7 +291,7 @@ public class CouponServiceImpl implements CouponService { |
| 311 | 291 | * @param couponTemplate 优惠劵模版 |
| 312 | 292 | */ |
| 313 | 293 | private void removeTakeLimitUser(Set<Long> userIds, CouponTemplateDO couponTemplate) { |
| 314 | - if (couponTemplate.getTakeLimitCount() == null || couponTemplate.getTakeLimitCount() <= 0) { | |
| 294 | + if (couponTemplate.getTakeLimitCount() <= 0) { | |
| 315 | 295 | return; |
| 316 | 296 | } |
| 317 | 297 | // 查询已领过券的用户 |
| ... | ... | @@ -359,48 +339,6 @@ public class CouponServiceImpl implements CouponService { |
| 359 | 339 | } |
| 360 | 340 | |
| 361 | 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 | 342 | public Map<Long, Boolean> getUserCanCanTakeMap(Long userId, List<CouponTemplateDO> templates) { |
| 405 | 343 | // 1. 未登录时,都显示可以领取 |
| 406 | 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 | import cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity.AppSeckillActivityPageReqVO; |
| 9 | 9 | import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillActivityDO; |
| 10 | 10 | import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillProductDO; |
| 11 | - | |
| 12 | 11 | import jakarta.validation.Valid; |
| 12 | + | |
| 13 | 13 | import java.time.LocalDateTime; |
| 14 | 14 | import java.util.Collection; |
| 15 | 15 | import java.util.List; |
| ... | ... | @@ -98,7 +98,7 @@ public interface SeckillActivityService { |
| 98 | 98 | * @param activityIds 活动编号 |
| 99 | 99 | * @return 活动商品列表 |
| 100 | 100 | */ |
| 101 | - List<SeckillProductDO> getSeckillProductListByActivityId(Collection<Long> activityIds); | |
| 101 | + List<SeckillProductDO> getSeckillProductListByActivityIds(Collection<Long> activityIds); | |
| 102 | 102 | |
| 103 | 103 | /** |
| 104 | 104 | * 通过活动时段编号获取指定 status 的秒杀活动 |
| ... | ... | @@ -139,4 +139,12 @@ public interface SeckillActivityService { |
| 139 | 139 | */ |
| 140 | 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 | 51 | ErrorCode AFTER_SALE_REFUND_FAIL_STATUS_NOT_WAIT_REFUND = new ErrorCode(1_011_000_110, "退款失败,售后单状态不是【待退款】"); |
| 52 | 52 | ErrorCode AFTER_SALE_CANCEL_FAIL_STATUS_NOT_APPLY_OR_AGREE_OR_BUYER_DELIVERY = |
| 53 | 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 | 56 | // ========== Cart 模块 1-011-002-000 ========== |
| 56 | 57 | ErrorCode CARD_ITEM_NOT_FOUND = new ErrorCode(1_011_002_000, "购物车项不存在"); |
| ... | ... | @@ -61,7 +62,7 @@ public interface ErrorCodeConstants { |
| 61 | 62 | ErrorCode PRICE_CALCULATE_COUPON_NOT_MATCH_NORMAL_ORDER = new ErrorCode(1_011_003_004, "参与秒杀、拼团、砍价的营销商品,无法使用优惠劵"); |
| 62 | 63 | ErrorCode PRICE_CALCULATE_SECKILL_TOTAL_LIMIT_COUNT = new ErrorCode(1_011_003_005, "参与秒杀的商品,超过了秒杀总限购数量"); |
| 63 | 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 | 67 | // ========== 物流 Express 模块 1-011-004-000 ========== |
| 67 | 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 | 44 | private String name; |
| 45 | 45 | @Schema(description = "提现银行", example = "1") |
| 46 | 46 | @NotNull(message = "提现银行不能为空", groups = {Bank.class}) |
| 47 | - private Integer bankName; | |
| 47 | + private String bankName; | |
| 48 | 48 | @Schema(description = "开户地址", example = "海淀支行") |
| 49 | 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 | 3 | import cn.iocoder.yudao.module.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO; |
| 4 | 4 | import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; |
| 5 | 5 | import io.swagger.v3.oas.annotations.media.Schema; |
| 6 | -import jakarta.validation.constraints.NotNull; | |
| 7 | 6 | import lombok.AllArgsConstructor; |
| 8 | 7 | import lombok.Data; |
| 9 | 8 | import lombok.NoArgsConstructor; |
| 10 | 9 | |
| 10 | +import java.time.LocalDateTime; | |
| 11 | 11 | import java.util.List; |
| 12 | 12 | |
| 13 | 13 | @Schema(description = "用户 App - 交易订单结算信息 Response VO") |
| ... | ... | @@ -20,6 +20,9 @@ public class AppTradeOrderSettlementRespVO { |
| 20 | 20 | @Schema(description = "购物项数组", requiredMode = Schema.RequiredMode.REQUIRED) |
| 21 | 21 | private List<Item> items; |
| 22 | 22 | |
| 23 | + @Schema(description = "优惠劵数组", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 24 | + private List<Coupon> coupons; // 可用 + 不可用 | |
| 25 | + | |
| 23 | 26 | @Schema(description = "费用", requiredMode = Schema.RequiredMode.REQUIRED) |
| 24 | 27 | private Price price; |
| 25 | 28 | |
| ... | ... | @@ -117,7 +120,6 @@ public class AppTradeOrderSettlementRespVO { |
| 117 | 120 | private String mobile; |
| 118 | 121 | |
| 119 | 122 | @Schema(description = "地区编号", requiredMode = Schema.RequiredMode.REQUIRED) |
| 120 | - @NotNull(message = "地区编号不能为空") | |
| 121 | 123 | private Long areaId; |
| 122 | 124 | @Schema(description = "地区名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "上海上海市普陀区") |
| 123 | 125 | private String areaName; |
| ... | ... | @@ -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 | 8 | import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; |
| 9 | 9 | import cn.iocoder.yudao.module.pay.api.refund.PayRefundApi; |
| 10 | 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 | 14 | import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSaleDisagreeReqVO; |
| 12 | 15 | import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSalePageReqVO; |
| 13 | 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 | 29 | import cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleWayEnum; |
| 27 | 30 | import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum; |
| 28 | 31 | import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum; |
| 32 | +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; | |
| 29 | 33 | import cn.iocoder.yudao.module.trade.framework.aftersale.core.annotations.AfterSaleLog; |
| 30 | 34 | import cn.iocoder.yudao.module.trade.framework.aftersale.core.utils.AfterSaleLogUtils; |
| 31 | 35 | import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties; |
| ... | ... | @@ -71,6 +75,8 @@ public class AfterSaleServiceImpl implements AfterSaleService { |
| 71 | 75 | |
| 72 | 76 | @Resource |
| 73 | 77 | private PayRefundApi payRefundApi; |
| 78 | + @Resource | |
| 79 | + private CombinationRecordApi combinationRecordApi; | |
| 74 | 80 | |
| 75 | 81 | @Resource |
| 76 | 82 | private TradeOrderProperties tradeOrderProperties; |
| ... | ... | @@ -148,6 +154,14 @@ public class AfterSaleServiceImpl implements AfterSaleService { |
| 148 | 154 | && !TradeOrderStatusEnum.haveDelivered(order.getStatus())) { |
| 149 | 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 | 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 | 887 | .setAppKey(tradeOrderProperties.getPayAppKey()).setUserIp(getClientIP()) // 支付应用 |
| 888 | 888 | .setMerchantOrderId(String.valueOf(order.getId())) // 支付单号 |
| 889 | 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 | 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 | 3 | import cn.hutool.core.lang.Assert; |
| 4 | 4 | import cn.iocoder.yudao.module.promotion.api.combination.CombinationRecordApi; |
| 5 | 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 | 8 | import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert; |
| 7 | 9 | import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; |
| 8 | 10 | import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; |
| ... | ... | @@ -84,7 +86,9 @@ public class TradeCombinationOrderHandler implements TradeOrderHandler { |
| 84 | 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 | 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 | 5 | import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; |
| 6 | 6 | import lombok.Data; |
| 7 | 7 | |
| 8 | +import java.time.LocalDateTime; | |
| 8 | 9 | import java.util.List; |
| 9 | 10 | import java.util.Map; |
| 10 | 11 | |
| ... | ... | @@ -45,9 +46,13 @@ public class TradePriceCalculateRespBO { |
| 45 | 46 | private List<Promotion> promotions; |
| 46 | 47 | |
| 47 | 48 | /** |
| 48 | - * 优惠劵编号 | |
| 49 | + * 使用的优惠劵编号 | |
| 49 | 50 | */ |
| 50 | 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 | 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 | 1 | package cn.iocoder.yudao.module.trade.service.price.calculator; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | -import cn.hutool.core.lang.Assert; | |
| 5 | 4 | import cn.hutool.core.util.ObjectUtil; |
| 6 | 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 | 8 | import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi; |
| 8 | 9 | import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponRespDTO; |
| 9 | -import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponValidReqDTO; | |
| 10 | 10 | import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum; |
| 11 | 11 | import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum; |
| 12 | 12 | import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum; |
| 13 | +import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum; | |
| 13 | 14 | import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; |
| 14 | 15 | import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; |
| 15 | 16 | import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; |
| 17 | +import jakarta.annotation.Resource; | |
| 16 | 18 | import org.springframework.core.annotation.Order; |
| 17 | 19 | import org.springframework.stereotype.Component; |
| 18 | 20 | |
| 19 | -import jakarta.annotation.Resource; | |
| 20 | 21 | import java.util.List; |
| 21 | 22 | import java.util.function.Predicate; |
| 22 | 23 | |
| 23 | 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 | 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 | 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 | 31 | * 优惠劵的 {@link TradePriceCalculator} 实现类 |
| ... | ... | @@ -41,34 +41,37 @@ public class TradeCouponPriceCalculator implements TradePriceCalculator { |
| 41 | 41 | |
| 42 | 42 | @Override |
| 43 | 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 | 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 | 71 | // 3.1 计算可以优惠的金额 |
| 72 | + List<TradePriceCalculateRespBO.OrderItem> orderItems = filterMatchCouponOrderItems(result, coupon); | |
| 73 | + Integer totalPayPrice = TradePriceCalculatorHelper.calculateTotalPayPrice(orderItems); | |
| 68 | 74 | Integer couponPrice = getCouponPrice(coupon, totalPayPrice); |
| 69 | - if (couponPrice <= totalPayPrice) { | |
| 70 | - throw exception(PRICE_CALCULATE_COUPON_PRICE_TOO_MUCH); | |
| 71 | - } | |
| 72 | 75 | // 3.2 计算分摊的优惠金额 |
| 73 | 76 | List<Integer> divideCouponPrices = TradePriceCalculatorHelper.dividePrice(orderItems, couponPrice); |
| 74 | 77 | |
| ... | ... | @@ -76,7 +79,7 @@ public class TradeCouponPriceCalculator implements TradePriceCalculator { |
| 76 | 79 | result.setCouponId(param.getCouponId()); |
| 77 | 80 | // 4.2 记录优惠明细 |
| 78 | 81 | TradePriceCalculatorHelper.addPromotion(result, orderItems, |
| 79 | - param.getCouponId(), coupon.getName(), PromotionTypeEnum.COUPON.getType(), | |
| 82 | + param.getCouponId(), couponBO.getName(), PromotionTypeEnum.COUPON.getType(), | |
| 80 | 83 | StrUtil.format("优惠劵:省 {} 元", TradePriceCalculatorHelper.formatPrice(couponPrice)), |
| 81 | 84 | divideCouponPrices); |
| 82 | 85 | // 4.3 更新 SKU 优惠金额 |
| ... | ... | @@ -88,6 +91,43 @@ public class TradeCouponPriceCalculator implements TradePriceCalculator { |
| 88 | 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 | 131 | private Integer getCouponPrice(CouponRespDTO coupon, Integer totalPayPrice) { |
| 92 | 132 | if (PromotionDiscountTypeEnum.PRICE.getType().equals(coupon.getDiscountType())) { // 减价 |
| 93 | 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 | 1 | package cn.iocoder.yudao.module.trade.service.price.calculator; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.collection.ListUtil; | |
| 3 | 4 | import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; |
| 4 | 5 | import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi; |
| 5 | 6 | import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponRespDTO; |
| 6 | -import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponValidReqDTO; | |
| 7 | 7 | import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum; |
| 8 | 8 | import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum; |
| 9 | 9 | import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum; |
| 10 | +import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum; | |
| 10 | 11 | import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; |
| 11 | 12 | import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; |
| 12 | 13 | import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; |
| ... | ... | @@ -14,8 +15,10 @@ import org.junit.jupiter.api.Test; |
| 14 | 15 | import org.mockito.InjectMocks; |
| 15 | 16 | import org.mockito.Mock; |
| 16 | 17 | |
| 18 | +import java.time.Duration; | |
| 17 | 19 | import java.util.ArrayList; |
| 18 | 20 | |
| 21 | +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.addTime; | |
| 19 | 22 | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; |
| 20 | 23 | import static java.util.Arrays.asList; |
| 21 | 24 | import static org.junit.jupiter.api.Assertions.assertEquals; |
| ... | ... | @@ -69,8 +72,10 @@ public class TradeCouponPriceCalculatorTest extends BaseMockitoUnitTest { |
| 69 | 72 | CouponRespDTO coupon = randomPojo(CouponRespDTO.class, o -> o.setId(1024L).setName("程序员节") |
| 70 | 73 | .setProductScope(PromotionProductScopeEnum.SPU.getScope()).setProductScopeValues(asList(1L, 2L)) |
| 71 | 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 | 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 | 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 | 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<WxPayClientC |
| 266 | 266 | String errorCode = getErrorCode(e); |
| 267 | 267 | String errorMessage = getErrorMessage(e); |
| 268 | 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 | 5 | import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogPageReqDTO; |
| 6 | 6 | import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogRespDTO; |
| 7 | 7 | import jakarta.validation.Valid; |
| 8 | +import org.springframework.scheduling.annotation.Async; | |
| 8 | 9 | |
| 9 | 10 | /** |
| 10 | 11 | * 操作日志 API 接口 |
| ... | ... | @@ -21,6 +22,16 @@ public interface OperateLogApi { |
| 21 | 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 | 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 | 9 | import cn.iocoder.yudao.module.system.service.logger.OperateLogService; |
| 10 | 10 | import com.fhs.core.trans.anno.TransMethodResult; |
| 11 | 11 | import jakarta.annotation.Resource; |
| 12 | -import org.springframework.scheduling.annotation.Async; | |
| 13 | 12 | import org.springframework.stereotype.Service; |
| 14 | 13 | import org.springframework.validation.annotation.Validated; |
| 15 | 14 | |
| ... | ... | @@ -26,7 +25,6 @@ public class OperateLogApiImpl implements OperateLogApi { |
| 26 | 25 | private OperateLogService operateLogService; |
| 27 | 26 | |
| 28 | 27 | @Override |
| 29 | - @Async | |
| 30 | 28 | public void createOperateLog(OperateLogCreateReqDTO createReqDTO) { |
| 31 | 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 | 144 | // 调用,并断言 |
| 145 | 145 | assertServiceException(() -> oauth2TokenService.refreshAccessToken(refreshToken, clientId), |
| 146 | 146 | new ErrorCode(401, "刷新令牌已过期")); |
| 147 | - assertEquals(0, oauth2RefreshTokenMapper.selectCount()); | |
| 147 | + assertEquals(0, oauth2AccessTokenMapper.selectCount()); | |
| 148 | 148 | } |
| 149 | 149 | |
| 150 | 150 | @Test | ... | ... |
yudao-server/pom.xml
| ... | ... | @@ -33,11 +33,11 @@ |
| 33 | 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 | 43 | <!-- <dependency>--> |
| ... | ... | @@ -52,11 +52,11 @@ |
| 52 | 52 | <!-- <version>${revision}</version>--> |
| 53 | 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 | 62 | <!-- <dependency>--> |
| ... | ... | @@ -66,26 +66,26 @@ |
| 66 | 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 | 90 | <!-- CRM 相关模块。默认注释,保证编译速度 --> |
| 91 | 91 | <!-- <dependency>--> | ... | ... |
yudao-server/src/main/resources/application.yaml
| ... | ... | @@ -154,9 +154,9 @@ spring: |
| 154 | 154 | embedding: |
| 155 | 155 | transformer: |
| 156 | 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 | 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 | 160 | qianfan: # 文心一言 |
| 161 | 161 | api-key: x0cuLZ7XsaTCU08vuJWO87Lg |
| 162 | 162 | secret-key: R9mYF9dl9KASgi5RUq0FQt3wRisSnOcK |
| ... | ... | @@ -245,7 +245,7 @@ yudao: |
| 245 | 245 | codegen: |
| 246 | 246 | base-package: ${yudao.info.base-package} |
| 247 | 247 | db-schemas: ${spring.datasource.dynamic.datasource.master.name} |
| 248 | - front-type: 10 # 前端模版的类型,参见 CodegenFrontTypeEnum 枚举类 | |
| 248 | + front-type: 20 # 前端模版的类型,参见 CodegenFrontTypeEnum 枚举类 | |
| 249 | 249 | tenant: # 多租户相关配置项 |
| 250 | 250 | enable: true |
| 251 | 251 | ignore-urls: | ... | ... |