Commit 241957507c57c6a3300093c1419c9689d458f086

Authored by 芋道源码
Committed by Gitee
2 parents 9805f9f5 6e56ca4b

!1084 批量优化或修改BUG

Merge pull request !1084 from 痴货/develop-mall
Showing 42 changed files with 746 additions and 165 deletions
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.java
@@ -69,8 +69,8 @@ public class AppProductSpuController { @@ -69,8 +69,8 @@ public class AppProductSpuController {
69 list.forEach(spu -> spu.setSalesCount(spu.getSalesCount() + spu.getVirtualSalesCount())); 69 list.forEach(spu -> spu.setSalesCount(spu.getSalesCount() + spu.getVirtualSalesCount()));
70 List<AppProductSpuRespVO> voList = BeanUtils.toBean(list, AppProductSpuRespVO.class); 70 List<AppProductSpuRespVO> voList = BeanUtils.toBean(list, AppProductSpuRespVO.class);
71 // 处理 vip 价格 71 // 处理 vip 价格
72 - MemberLevelRespDTO memberLevel = getMemberLevel();  
73 - voList.forEach(vo -> vo.setVipPrice(calculateVipPrice(vo.getPrice(), memberLevel))); 72 +// MemberLevelRespDTO memberLevel = getMemberLevel();
  73 +// voList.forEach(vo -> vo.setVipPrice(calculateVipPrice(vo.getPrice(), memberLevel)));
74 return success(voList); 74 return success(voList);
75 } 75 }
76 76
@@ -86,8 +86,8 @@ public class AppProductSpuController { @@ -86,8 +86,8 @@ public class AppProductSpuController {
86 pageResult.getList().forEach(spu -> spu.setSalesCount(spu.getSalesCount() + spu.getVirtualSalesCount())); 86 pageResult.getList().forEach(spu -> spu.setSalesCount(spu.getSalesCount() + spu.getVirtualSalesCount()));
87 PageResult<AppProductSpuRespVO> voPageResult = BeanUtils.toBean(pageResult, AppProductSpuRespVO.class); 87 PageResult<AppProductSpuRespVO> voPageResult = BeanUtils.toBean(pageResult, AppProductSpuRespVO.class);
88 // 处理 vip 价格 88 // 处理 vip 价格
89 - MemberLevelRespDTO memberLevel = getMemberLevel();  
90 - voPageResult.getList().forEach(vo -> vo.setVipPrice(calculateVipPrice(vo.getPrice(), memberLevel))); 89 +// MemberLevelRespDTO memberLevel = getMemberLevel();
  90 +// voPageResult.getList().forEach(vo -> vo.setVipPrice(calculateVipPrice(vo.getPrice(), memberLevel)));
91 return success(voPageResult); 91 return success(voPageResult);
92 } 92 }
93 93
@@ -142,7 +142,7 @@ public class AppProductSpuController { @@ -142,7 +142,7 @@ public class AppProductSpuController {
142 */ 142 */
143 public Integer calculateVipPrice(Integer price, MemberLevelRespDTO memberLevel) { 143 public Integer calculateVipPrice(Integer price, MemberLevelRespDTO memberLevel) {
144 if (memberLevel == null || memberLevel.getDiscountPercent() == null) { 144 if (memberLevel == null || memberLevel.getDiscountPercent() == null) {
145 - return 0; 145 + return null;
146 } 146 }
147 Integer newPrice = price * memberLevel.getDiscountPercent() / 100; 147 Integer newPrice = price * memberLevel.getDiscountPercent() / 100;
148 return price - newPrice; 148 return price - newPrice;
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppProductSpuRespVO.java
@@ -38,8 +38,8 @@ public class AppProductSpuRespVO { @@ -38,8 +38,8 @@ public class AppProductSpuRespVO {
38 @Schema(description = "市场价,单位使用:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") 38 @Schema(description = "市场价,单位使用:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
39 private Integer marketPrice; 39 private Integer marketPrice;
40 40
41 - @Schema(description = "VIP 价格,单位使用:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "968") // 通过会员等级,计算出折扣后价格  
42 - private Integer vipPrice; 41 +// @Schema(description = "VIP 价格,单位使用:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "968") // 通过会员等级,计算出折扣后价格
  42 +// private Integer vipPrice;
43 43
44 @Schema(description = "库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "666") 44 @Schema(description = "库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "666")
45 private Integer stock; 45 private Integer stock;
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/discount/dto/DiscountProductRespDTO.java
@@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.promotion.api.discount.dto; @@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.promotion.api.discount.dto;
2 2
3 import lombok.Data; 3 import lombok.Data;
4 4
  5 +import java.time.LocalDateTime;
  6 +
5 /** 7 /**
6 * 限时折扣活动商品 Response DTO 8 * 限时折扣活动商品 Response DTO
7 * 9 *
@@ -44,5 +46,12 @@ public class DiscountProductRespDTO { @@ -44,5 +46,12 @@ public class DiscountProductRespDTO {
44 * 活动标题 46 * 活动标题
45 */ 47 */
46 private String activityName; 48 private String activityName;
  49 + /**
  50 + * 活动结束时间点
  51 + *
  52 + * 冗余 {@link DiscountActivityDO#getEndTime()}
  53 + */
  54 + private LocalDateTime activityEndTime;
  55 +
47 56
48 } 57 }
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/reward/RewardActivityApi.java
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.promotion.api.reward; @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.promotion.api.reward;
3 import cn.iocoder.yudao.module.promotion.api.reward.dto.RewardActivityMatchRespDTO; 3 import cn.iocoder.yudao.module.promotion.api.reward.dto.RewardActivityMatchRespDTO;
4 4
5 import java.time.LocalDateTime; 5 import java.time.LocalDateTime;
  6 +import java.util.Collection;
6 import java.util.List; 7 import java.util.List;
7 8
8 /** 9 /**
@@ -21,4 +22,13 @@ public interface RewardActivityApi { @@ -21,4 +22,13 @@ public interface RewardActivityApi {
21 */ 22 */
22 List<RewardActivityMatchRespDTO> getRewardActivityListByStatusAndNow(Integer status, LocalDateTime dateTime); 23 List<RewardActivityMatchRespDTO> getRewardActivityListByStatusAndNow(Integer status, LocalDateTime dateTime);
23 24
  25 + /**
  26 + * 获取指定 spu 编号最近参加的活动,每个 spuId 只返回一条记录
  27 + *
  28 + * @param spuIds spu 编号
  29 + * @param status 状态
  30 + * @param dateTime 当前日期时间
  31 + * @return 满减送活动列表
  32 + */
  33 + List<RewardActivityMatchRespDTO> getRewardActivityBySpuIdsAndStatusAndDateTimeLt(Collection<Long> spuIds, Integer status, LocalDateTime dateTime);
24 } 34 }
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java
@@ -15,6 +15,7 @@ public interface ErrorCodeConstants { @@ -15,6 +15,7 @@ public interface ErrorCodeConstants {
15 ErrorCode DISCOUNT_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_001_002, "限时折扣活动已关闭,不能修改"); 15 ErrorCode DISCOUNT_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_001_002, "限时折扣活动已关闭,不能修改");
16 ErrorCode DISCOUNT_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED = new ErrorCode(1_013_001_003, "限时折扣活动未关闭,不能删除"); 16 ErrorCode DISCOUNT_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED = new ErrorCode(1_013_001_003, "限时折扣活动未关闭,不能删除");
17 ErrorCode DISCOUNT_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_001_004, "限时折扣活动已关闭,不能重复关闭"); 17 ErrorCode DISCOUNT_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_001_004, "限时折扣活动已关闭,不能重复关闭");
  18 + ErrorCode DISCOUNT_ACTIVITY_TYPE_NOT_EXISTS = new ErrorCode(1_013_001_005, "限时折扣活动类型不存在");
18 19
19 // ========== Banner 相关 1-013-002-000 ============ 20 // ========== Banner 相关 1-013-002-000 ============
20 ErrorCode BANNER_NOT_EXISTS = new ErrorCode(1_013_002_000, "Banner 不存在"); 21 ErrorCode BANNER_NOT_EXISTS = new ErrorCode(1_013_002_000, "Banner 不存在");
@@ -45,6 +46,7 @@ public interface ErrorCodeConstants { @@ -45,6 +46,7 @@ public interface ErrorCodeConstants {
45 ErrorCode REWARD_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED = new ErrorCode(1_013_006_003, "满减送活动未关闭,不能删除"); 46 ErrorCode REWARD_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED = new ErrorCode(1_013_006_003, "满减送活动未关闭,不能删除");
46 ErrorCode REWARD_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_006_004, "满减送活动已关闭,不能重复关闭"); 47 ErrorCode REWARD_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_006_004, "满减送活动已关闭,不能重复关闭");
47 ErrorCode REWARD_ACTIVITY_SCOPE_EXISTS = new ErrorCode(1_013_006_005, "与该时间段已存在的满减送活动商品范围冲突"); 48 ErrorCode REWARD_ACTIVITY_SCOPE_EXISTS = new ErrorCode(1_013_006_005, "与该时间段已存在的满减送活动商品范围冲突");
  49 + ErrorCode REWARD_ACTIVITY_TYPE_NOT_EXISTS = new ErrorCode(1_013_006_006, "满减送活动类型不存在");
48 50
49 // ========== TODO 空着 1-013-007-000 ============ 51 // ========== TODO 空着 1-013-007-000 ============
50 52
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/discount/DiscountActivityApiImpl.java
@@ -24,7 +24,7 @@ public class DiscountActivityApiImpl implements DiscountActivityApi { @@ -24,7 +24,7 @@ public class DiscountActivityApiImpl implements DiscountActivityApi {
24 24
25 @Override 25 @Override
26 public List<DiscountProductRespDTO> getMatchDiscountProductList(Collection<Long> skuIds) { 26 public List<DiscountProductRespDTO> getMatchDiscountProductList(Collection<Long> skuIds) {
27 - return DiscountActivityConvert.INSTANCE.convertList02(discountActivityService.getMatchDiscountProductList(skuIds)); 27 + return discountActivityService.getMatchDiscountProductList(skuIds);
28 } 28 }
29 29
30 } 30 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/reward/RewardActivityApiImpl.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.promotion.api.reward; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.promotion.api.reward;
2 2
3 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 3 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
4 import cn.iocoder.yudao.module.promotion.api.reward.dto.RewardActivityMatchRespDTO; 4 import cn.iocoder.yudao.module.promotion.api.reward.dto.RewardActivityMatchRespDTO;
  5 +import cn.iocoder.yudao.module.promotion.convert.reward.RewardActivityConvert;
5 import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO; 6 import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;
6 import cn.iocoder.yudao.module.promotion.service.reward.RewardActivityService; 7 import cn.iocoder.yudao.module.promotion.service.reward.RewardActivityService;
7 import jakarta.annotation.Resource; 8 import jakarta.annotation.Resource;
@@ -9,6 +10,7 @@ import org.springframework.stereotype.Service; @@ -9,6 +10,7 @@ import org.springframework.stereotype.Service;
9 import org.springframework.validation.annotation.Validated; 10 import org.springframework.validation.annotation.Validated;
10 11
11 import java.time.LocalDateTime; 12 import java.time.LocalDateTime;
  13 +import java.util.Collection;
12 import java.util.List; 14 import java.util.List;
13 15
14 /** 16 /**
@@ -29,4 +31,10 @@ public class RewardActivityApiImpl implements RewardActivityApi { @@ -29,4 +31,10 @@ public class RewardActivityApiImpl implements RewardActivityApi {
29 return BeanUtils.toBean(list, RewardActivityMatchRespDTO.class); 31 return BeanUtils.toBean(list, RewardActivityMatchRespDTO.class);
30 } 32 }
31 33
  34 + @Override
  35 + public List<RewardActivityMatchRespDTO> getRewardActivityBySpuIdsAndStatusAndDateTimeLt(Collection<Long> spuIds, Integer status, LocalDateTime dateTime) {
  36 + List<RewardActivityDO> rewardActivityBySpuIdsAndStatusAndDateTimeLt = rewardActivityService.getRewardActivityBySpuIdsAndStatusAndDateTimeLt(spuIds, status, dateTime);
  37 + return RewardActivityConvert.INSTANCE.convertList(rewardActivityBySpuIdsAndStatusAndDateTimeLt);
  38 + }
  39 +
32 } 40 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplateBaseVO.java
@@ -2,9 +2,11 @@ package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template; @@ -2,9 +2,11 @@ package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.util.ObjectUtil; 4 import cn.hutool.core.util.ObjectUtil;
  5 +import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
5 import cn.iocoder.yudao.framework.common.validation.InEnum; 6 import cn.iocoder.yudao.framework.common.validation.InEnum;
6 import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum; 7 import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;
7 import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum; 8 import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
  9 +import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
8 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTemplateValidityTypeEnum; 10 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTemplateValidityTypeEnum;
9 import com.fasterxml.jackson.annotation.JsonFormat; 11 import com.fasterxml.jackson.annotation.JsonFormat;
10 import com.fasterxml.jackson.annotation.JsonIgnore; 12 import com.fasterxml.jackson.annotation.JsonIgnore;
@@ -12,6 +14,7 @@ import io.swagger.v3.oas.annotations.media.Schema; @@ -12,6 +14,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
12 import lombok.Data; 14 import lombok.Data;
13 import org.springframework.format.annotation.DateTimeFormat; 15 import org.springframework.format.annotation.DateTimeFormat;
14 16
  17 +import jakarta.validation.Validator;
15 import jakarta.validation.constraints.AssertTrue; 18 import jakarta.validation.constraints.AssertTrue;
16 import jakarta.validation.constraints.Min; 19 import jakarta.validation.constraints.Min;
17 import jakarta.validation.constraints.NotNull; 20 import jakarta.validation.constraints.NotNull;
@@ -37,11 +40,11 @@ public class CouponTemplateBaseVO { @@ -37,11 +40,11 @@ public class CouponTemplateBaseVO {
37 private String description; 40 private String description;
38 41
39 @Schema(description = "发行总量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") // -1 - 则表示不限制发放数量 42 @Schema(description = "发行总量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") // -1 - 则表示不限制发放数量
40 - @NotNull(message = "发行总量不能为空") 43 + @NotNull(message = "发行总量不能为空", groups = {User.class})
41 private Integer totalCount; 44 private Integer totalCount;
42 45
43 @Schema(description = "每人限领个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "66") // -1 - 则表示不限制 46 @Schema(description = "每人限领个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "66") // -1 - 则表示不限制
44 - @NotNull(message = "每人限领个数不能为空") 47 + @NotNull(message = "每人限领个数不能为空", groups = {User.class})
45 private Integer takeLimitCount; 48 private Integer takeLimitCount;
46 49
47 @Schema(description = "领取方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") 50 @Schema(description = "领取方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@@ -89,13 +92,16 @@ public class CouponTemplateBaseVO { @@ -89,13 +92,16 @@ public class CouponTemplateBaseVO {
89 private Integer discountType; 92 private Integer discountType;
90 93
91 @Schema(description = "折扣百分比", example = "80") // 例如说,80% 为 80 94 @Schema(description = "折扣百分比", example = "80") // 例如说,80% 为 80
  95 + @NotNull(message = "折扣百分比不能为空", groups = {Percent.class})
92 private Integer discountPercent; 96 private Integer discountPercent;
93 97
94 @Schema(description = "优惠金额", example = "10") 98 @Schema(description = "优惠金额", example = "10")
95 @Min(value = 0, message = "优惠金额需要大于等于 0") 99 @Min(value = 0, message = "优惠金额需要大于等于 0")
  100 + @NotNull(message = "优惠金额不能为空", groups = {Price.class})
96 private Integer discountPrice; 101 private Integer discountPrice;
97 102
98 @Schema(description = "折扣上限", example = "100") // 单位:分,仅在 discountType 为 PERCENT 使用 103 @Schema(description = "折扣上限", example = "100") // 单位:分,仅在 discountType 为 PERCENT 使用
  104 + @NotNull(message = "折扣上限不能为空", groups = {Percent.class})
99 private Integer discountLimitPrice; 105 private Integer discountLimitPrice;
100 106
101 @AssertTrue(message = "商品范围编号的数组不能为空") 107 @AssertTrue(message = "商品范围编号的数组不能为空")
@@ -154,4 +160,54 @@ public class CouponTemplateBaseVO { @@ -154,4 +160,54 @@ public class CouponTemplateBaseVO {
154 || discountLimitPrice != null; 160 || discountLimitPrice != null;
155 } 161 }
156 162
  163 + //-------------------------领取方式校验start----------------------------
  164 +
  165 + /**
  166 + * 直接领取
  167 + */
  168 + public interface User {
  169 + }
  170 +
  171 + /**
  172 + * 指定发放
  173 + */
  174 + public interface Admin {
  175 + }
  176 +
  177 + //-------------------------领取方式校验end------------------------------
  178 +
  179 + //-------------------------优惠类型校验start----------------------------
  180 +
  181 + /**
  182 + * 满减
  183 + */
  184 + public interface Price {
  185 + }
  186 +
  187 + /**
  188 + * 折扣
  189 + */
  190 + public interface Percent {
  191 + }
  192 +
  193 + //-------------------------优惠类型校验end------------------------------
  194 +
  195 + public void validate(Validator validator) {
  196 +
  197 + //领取方式校验
  198 + if (CouponTakeTypeEnum.USER.getType().equals(takeType)) {
  199 + ValidationUtils.validate(validator, this, User.class);
  200 + } else if (CouponTakeTypeEnum.ADMIN.getType().equals(takeType)) {
  201 + ValidationUtils.validate(validator, this, Admin.class);
  202 + }
  203 +
  204 + //优惠类型校验
  205 + if (PromotionDiscountTypeEnum.PRICE.getType().equals(discountType)){
  206 + ValidationUtils.validate(validator, this, Price.class);
  207 + } else if (PromotionDiscountTypeEnum.PERCENT.getType().equals(discountType)) {
  208 + ValidationUtils.validate(validator, this, Percent.class);
  209 + }
  210 +
  211 + }
  212 +
157 } 213 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/activity/AppActivityController.java
@@ -33,6 +33,7 @@ import org.springframework.web.bind.annotation.RestController; @@ -33,6 +33,7 @@ import org.springframework.web.bind.annotation.RestController;
33 33
34 import java.time.LocalDateTime; 34 import java.time.LocalDateTime;
35 import java.util.*; 35 import java.util.*;
  36 +import java.util.stream.Collectors;
36 37
37 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 38 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
38 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; 39 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
@@ -150,36 +151,32 @@ public class AppActivityController { @@ -150,36 +151,32 @@ public class AppActivityController {
150 } 151 }
151 152
152 private void getRewardActivityList(Collection<Long> spuIds, LocalDateTime now, List<AppActivityRespVO> activityList) { 153 private void getRewardActivityList(Collection<Long> spuIds, LocalDateTime now, List<AppActivityRespVO> activityList) {
153 - // 1.1 获得所有的活动  
154 - List<RewardActivityDO> rewardActivityList = rewardActivityService.getRewardActivityListByStatusAndDateTimeLt(  
155 - CommonStatusEnum.ENABLE.getStatus(), now); 154 + // TODO @puhui999:有 3 范围,不只 spuId,还有 categoryId,全部,下次 fix
  155 + List<RewardActivityDO> rewardActivityList = rewardActivityService.getRewardActivityBySpuIdsAndStatusAndDateTimeLt(
  156 + spuIds, CommonStatusEnum.ENABLE.getStatus(), now);
156 if (CollUtil.isEmpty(rewardActivityList)) { 157 if (CollUtil.isEmpty(rewardActivityList)) {
157 return; 158 return;
158 } 159 }
159 - // 1.2 获得所有的商品信息  
160 - List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList(spuIds);  
161 - if (CollUtil.isEmpty(spuList)) {  
162 - return;  
163 - }  
164 160
165 - // 2. 构建活动  
166 - for (RewardActivityDO rewardActivity : rewardActivityList) {  
167 - // 情况一:所有商品都能参加  
168 - if (PromotionProductScopeEnum.isAll(rewardActivity.getProductScope())) {  
169 - buildAppActivityRespVO(rewardActivity, spuIds, activityList);  
170 - }  
171 - // 情况二:指定商品参加  
172 - if (PromotionProductScopeEnum.isSpu(rewardActivity.getProductScope())) {  
173 - List<Long> fSpuIds = spuList.stream().map(ProductSpuRespDTO::getId).filter(id ->  
174 - rewardActivity.getProductScopeValues().contains(id)).toList();  
175 - buildAppActivityRespVO(rewardActivity, fSpuIds, activityList);  
176 - }  
177 - // 情况三:指定商品类型参加  
178 - if (PromotionProductScopeEnum.isCategory(rewardActivity.getProductScope())) {  
179 - List<Long> fSpuIds = spuList.stream().filter(spuItem -> rewardActivity.getProductScopeValues()  
180 - .contains(spuItem.getCategoryId())).map(ProductSpuRespDTO::getId).toList();  
181 - buildAppActivityRespVO(rewardActivity, fSpuIds, activityList); 161 + Map<Long, Optional<RewardActivityDO>> spuIdAndActivityMap = spuIds.stream()
  162 + .collect(Collectors.toMap(
  163 + spuId -> spuId,
  164 + spuId -> rewardActivityList.stream()
  165 + .filter(activity ->
  166 + ( activity.getProductScopeValues()!=null &&
  167 + (activity.getProductScopeValues().contains(spuId) ||
  168 + activity.getProductScopeValues().contains(productSpuApi.getSpu(spuId).getCategoryId()))) ||
  169 + activity.getProductScope()==1
  170 + )
  171 + .max(Comparator.comparing(RewardActivityDO::getCreateTime))));
  172 + for (Long supId : spuIdAndActivityMap.keySet()) {
  173 + if (spuIdAndActivityMap.get(supId).isEmpty()) {
  174 + continue;
182 } 175 }
  176 +
  177 + RewardActivityDO rewardActivityDO = spuIdAndActivityMap.get(supId).get();
  178 + activityList.add(new AppActivityRespVO(rewardActivityDO.getId(), PromotionTypeEnum.REWARD_ACTIVITY.getType(),
  179 + rewardActivityDO.getName(), supId, rewardActivityDO.getStartTime(), rewardActivityDO.getEndTime()));
183 } 180 }
184 } 181 }
185 182
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/coupon/AppCouponController.java
@@ -60,8 +60,8 @@ public class AppCouponController { @@ -60,8 +60,8 @@ public class AppCouponController {
60 @Operation(summary = "获得匹配指定商品的优惠劵列表", description = "用于下单页,展示优惠劵列表") 60 @Operation(summary = "获得匹配指定商品的优惠劵列表", description = "用于下单页,展示优惠劵列表")
61 public CommonResult<List<AppCouponMatchRespVO>> getMatchCouponList(AppCouponMatchReqVO matchReqVO) { 61 public CommonResult<List<AppCouponMatchRespVO>> getMatchCouponList(AppCouponMatchReqVO matchReqVO) {
62 // todo: 优化:优惠金额倒序 62 // todo: 优化:优惠金额倒序
63 - List<CouponDO> list = couponService.getMatchCouponList(getLoginUserId(), matchReqVO);  
64 - return success(BeanUtils.toBean(list, AppCouponMatchRespVO.class)); 63 + List<AppCouponMatchRespVO> list = couponService.getMatchCouponList(getLoginUserId(), matchReqVO);
  64 + return success(list);
65 } 65 }
66 66
67 @GetMapping("/page") 67 @GetMapping("/page")
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/reward/vo/AppRewardActivityRespVO.java
@@ -4,6 +4,7 @@ import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivi @@ -4,6 +4,7 @@ import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivi
4 import io.swagger.v3.oas.annotations.media.Schema; 4 import io.swagger.v3.oas.annotations.media.Schema;
5 import lombok.Data; 5 import lombok.Data;
6 6
  7 +import java.time.LocalDateTime;
7 import java.util.List; 8 import java.util.List;
8 9
9 @Schema(description = "用户 App - 满减送活动 Response VO") 10 @Schema(description = "用户 App - 满减送活动 Response VO")
@@ -26,9 +27,15 @@ public class AppRewardActivityRespVO { @@ -26,9 +27,15 @@ public class AppRewardActivityRespVO {
26 private Integer productScope; 27 private Integer productScope;
27 28
28 @Schema(description = "商品 SPU 编号的数组", example = "1,2,3") 29 @Schema(description = "商品 SPU 编号的数组", example = "1,2,3")
29 - private List<Long> productSpuIds; 30 + private List<Long> productScopeValues;
30 31
31 @Schema(description = "优惠规则的数组") 32 @Schema(description = "优惠规则的数组")
32 private List<RewardActivityBaseVO.Rule> rules; 33 private List<RewardActivityBaseVO.Rule> rules;
33 34
  35 + @Schema(description = "开始时间")
  36 + private LocalDateTime startTime;
  37 +
  38 + @Schema(description = "结束时间")
  39 + private LocalDateTime endTime;
  40 +
34 } 41 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/coupon/CouponConvert.java
@@ -32,6 +32,8 @@ public interface CouponConvert { @@ -32,6 +32,8 @@ public interface CouponConvert {
32 32
33 CouponRespDTO convert(CouponDO bean); 33 CouponRespDTO convert(CouponDO bean);
34 34
  35 + AppCouponMatchRespVO convert2(CouponDO bean);
  36 +
35 default CouponDO convert(CouponTemplateDO template, Long userId) { 37 default CouponDO convert(CouponTemplateDO template, Long userId) {
36 CouponDO couponDO = new CouponDO() 38 CouponDO couponDO = new CouponDO()
37 .setTemplateId(template.getId()) 39 .setTemplateId(template.getId())
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/discount/DiscountActivityConvert.java
@@ -121,7 +121,10 @@ public interface DiscountActivityConvert { @@ -121,7 +121,10 @@ public interface DiscountActivityConvert {
121 default boolean isEquals(DiscountProductDO productDO, DiscountProductDO productVO) { 121 default boolean isEquals(DiscountProductDO productDO, DiscountProductDO productVO) {
122 if (ObjectUtil.notEqual(productDO.getSpuId(), productVO.getSpuId()) 122 if (ObjectUtil.notEqual(productDO.getSpuId(), productVO.getSpuId())
123 || ObjectUtil.notEqual(productDO.getSkuId(), productVO.getSkuId()) 123 || ObjectUtil.notEqual(productDO.getSkuId(), productVO.getSkuId())
124 - || ObjectUtil.notEqual(productDO.getDiscountType(), productVO.getDiscountType())) { 124 + || ObjectUtil.notEqual(productDO.getDiscountType(), productVO.getDiscountType())
  125 + || ObjectUtil.notEqual(productDO.getActivityEndTime(), productVO.getActivityEndTime())
  126 + || ObjectUtil.notEqual(productDO.getActivityStartTime(), productVO.getActivityStartTime())
  127 + || ObjectUtil.notEqual(productDO.getActivityStatus(), productVO.getActivityStatus())) {
125 return false; 128 return false;
126 } 129 }
127 if (productDO.getDiscountType().equals(PromotionDiscountTypeEnum.PRICE.getType())) { 130 if (productDO.getDiscountType().equals(PromotionDiscountTypeEnum.PRICE.getType())) {
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/reward/RewardActivityConvert.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.convert.reward;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.promotion.api.reward.dto.RewardActivityMatchRespDTO;
  5 +import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityCreateReqVO;
  6 +import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityRespVO;
  7 +import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityUpdateReqVO;
  8 +import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;
  9 +import org.mapstruct.Mapper;
  10 +import org.mapstruct.factory.Mappers;
  11 +
  12 +import java.util.List;
  13 +
  14 +/**
  15 + * 满减送活动 Convert
  16 + *
  17 + * @author 芋道源码
  18 + */
  19 +@Mapper
  20 +public interface RewardActivityConvert {
  21 +
  22 + RewardActivityConvert INSTANCE = Mappers.getMapper(RewardActivityConvert.class);
  23 +
  24 + RewardActivityDO convert(RewardActivityCreateReqVO bean);
  25 +
  26 + RewardActivityDO convert(RewardActivityUpdateReqVO bean);
  27 +
  28 + RewardActivityRespVO convert(RewardActivityDO bean);
  29 +
  30 + PageResult<RewardActivityRespVO> convertPage(PageResult<RewardActivityDO> page);
  31 +
  32 + List<RewardActivityMatchRespDTO> convertList(List<RewardActivityDO> rewardActivityBySpuIdsAndStatusAndDateTimeLt);
  33 +}
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponMapper.java
@@ -85,19 +85,12 @@ public interface CouponMapper extends BaseMapperX&lt;CouponDO&gt; { @@ -85,19 +85,12 @@ public interface CouponMapper extends BaseMapperX&lt;CouponDO&gt; {
85 } 85 }
86 86
87 default List<CouponDO> selectListByUserIdAndStatusAndUsePriceLeAndProductScope( 87 default List<CouponDO> selectListByUserIdAndStatusAndUsePriceLeAndProductScope(
88 - Long userId, Integer status, Integer usePrice, List<Long> spuIds, List<Long> categoryIds) {  
89 - Function<List<Long>, String> productScopeValuesFindInSetFunc = ids -> ids.stream()  
90 - .map(id -> StrUtil.format("FIND_IN_SET({}, product_scope_values) ", id))  
91 - .collect(Collectors.joining(" OR "));  
92 - return selectList(new LambdaQueryWrapperX<CouponDO>() 88 + Long userId, Integer status) {
  89 + List<CouponDO> couponDOS = selectList(new LambdaQueryWrapperX<CouponDO>()
93 .eq(CouponDO::getUserId, userId) 90 .eq(CouponDO::getUserId, userId)
94 .eq(CouponDO::getStatus, status) 91 .eq(CouponDO::getStatus, status)
95 - .le(CouponDO::getUsePrice, usePrice) // 价格小于等于,满足价格使用条件  
96 - .and(w -> w.eq(CouponDO::getProductScope, PromotionProductScopeEnum.ALL.getScope()) // 商品范围一:全部  
97 - .or(ww -> ww.eq(CouponDO::getProductScope, PromotionProductScopeEnum.SPU.getScope()) // 商品范围二:满足指定商品  
98 - .apply(productScopeValuesFindInSetFunc.apply(spuIds)))  
99 - .or(ww -> ww.eq(CouponDO::getProductScope, PromotionProductScopeEnum.CATEGORY.getScope()) // 商品范围三:满足指定分类  
100 - .apply(productScopeValuesFindInSetFunc.apply(categoryIds))))); 92 + );
  93 + return couponDOS;
101 } 94 }
102 95
103 default List<CouponDO> selectListByStatusAndValidEndTimeLe(Integer status, LocalDateTime validEndTime) { 96 default List<CouponDO> selectListByStatusAndValidEndTimeLe(Integer status, LocalDateTime validEndTime) {
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponTemplateMapper.java
@@ -70,7 +70,7 @@ public interface CouponTemplateMapper extends BaseMapperX&lt;CouponTemplateDO&gt; { @@ -70,7 +70,7 @@ public interface CouponTemplateMapper extends BaseMapperX&lt;CouponTemplateDO&gt; {
70 .in(CouponTemplateDO::getTakeType, canTakeTypes) // 2. 领取方式一致 70 .in(CouponTemplateDO::getTakeType, canTakeTypes) // 2. 领取方式一致
71 .and(ww -> ww.gt(CouponTemplateDO::getValidEndTime, LocalDateTime.now()) // 3.1 未过期 71 .and(ww -> ww.gt(CouponTemplateDO::getValidEndTime, LocalDateTime.now()) // 3.1 未过期
72 .or().eq(CouponTemplateDO::getValidityType, CouponTemplateValidityTypeEnum.TERM.getType())) // 3.2 领取之后 72 .or().eq(CouponTemplateDO::getValidityType, CouponTemplateValidityTypeEnum.TERM.getType())) // 3.2 领取之后
73 - .apply(" (take_count < total_count OR total_count = -1 )"); // 4. 剩余数量大于 0,或者无限领取 73 + .apply(" (take_count < total_count OR total_count = -1 or total_count is null)"); // 4. 剩余数量大于 0,或者无限领取,或者是指定发放的券
74 } 74 }
75 return canTakeConsumer; 75 return canTakeConsumer;
76 } 76 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/discount/DiscountProductMapper.java
1 package cn.iocoder.yudao.module.promotion.dal.mysql.discount; 1 package cn.iocoder.yudao.module.promotion.dal.mysql.discount;
2 2
3 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 3 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  4 +import cn.iocoder.yudao.module.promotion.api.discount.dto.DiscountProductRespDTO;
4 import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO; 5 import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO;
5 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 6 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
6 import org.apache.ibatis.annotations.Mapper; 7 import org.apache.ibatis.annotations.Mapper;
@@ -31,7 +32,7 @@ public interface DiscountProductMapper extends BaseMapperX&lt;DiscountProductDO&gt; { @@ -31,7 +32,7 @@ public interface DiscountProductMapper extends BaseMapperX&lt;DiscountProductDO&gt; {
31 } 32 }
32 33
33 // TODO @zhangshuai:逻辑里,尽量避免写 join 语句哈,你可以看看这个查询,有什么办法优化?目前的一个思路,是分 2 次查询,性能也是 ok 的 34 // TODO @zhangshuai:逻辑里,尽量避免写 join 语句哈,你可以看看这个查询,有什么办法优化?目前的一个思路,是分 2 次查询,性能也是 ok 的
34 - List<DiscountProductDO> getMatchDiscountProductList(@Param("skuIds") Collection<Long> skuIds); 35 + List<DiscountProductRespDTO> getMatchDiscountProductList(@Param("skuIds") Collection<Long> skuIds);
35 36
36 /** 37 /**
37 * 查询出指定 spuId 的 spu 参加的活动最接近现在的一条记录。多个的话,一个 spuId 对应一个最近的活动编号 38 * 查询出指定 spuId 的 spu 参加的活动最接近现在的一条记录。多个的话,一个 spuId 对应一个最近的活动编号
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/reward/RewardActivityMapper.java
1 package cn.iocoder.yudao.module.promotion.dal.mysql.reward; 1 package cn.iocoder.yudao.module.promotion.dal.mysql.reward;
2 2
  3 +import cn.hutool.core.util.StrUtil;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 5 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6 import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityPageReqVO; 7 import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityPageReqVO;
7 import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO; 8 import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;
  9 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
  10 +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
8 import org.apache.ibatis.annotations.Mapper; 11 import org.apache.ibatis.annotations.Mapper;
9 12
10 import java.time.LocalDateTime; 13 import java.time.LocalDateTime;
  14 +import java.util.Collection;
11 import java.util.List; 15 import java.util.List;
  16 +import java.util.function.Function;
  17 +import java.util.stream.Collectors;
12 18
13 /** 19 /**
14 * 满减送活动 Mapper 20 * 满减送活动 Mapper
@@ -25,13 +31,48 @@ public interface RewardActivityMapper extends BaseMapperX&lt;RewardActivityDO&gt; { @@ -25,13 +31,48 @@ public interface RewardActivityMapper extends BaseMapperX&lt;RewardActivityDO&gt; {
25 .orderByDesc(RewardActivityDO::getId)); 31 .orderByDesc(RewardActivityDO::getId));
26 } 32 }
27 33
  34 + default List<RewardActivityDO> selectListBySpuIdsAndStatus(Collection<Long> spuIds, Integer status) {
  35 + Function<Collection<Long>, String> productScopeValuesFindInSetFunc = ids -> ids.stream()
  36 + .map(id -> StrUtil.format("FIND_IN_SET({}, product_scope_values) ", id))
  37 + .collect(Collectors.joining(" OR "));
  38 + return selectList(new QueryWrapper<RewardActivityDO>()
  39 + .eq("status", status)
  40 + .apply(productScopeValuesFindInSetFunc.apply(spuIds)));
  41 + }
  42 +
  43 + /**
  44 + * 获取指定活动编号的活动列表且
  45 + * 开始时间和结束时间小于给定时间 dateTime 的活动列表
  46 + *
  47 + * @param status 状态
  48 + * @param dateTime 指定日期
  49 + * @return 活动列表
  50 + */
28 default List<RewardActivityDO> selectListByStatusAndDateTimeLt(Integer status, LocalDateTime dateTime) { 51 default List<RewardActivityDO> selectListByStatusAndDateTimeLt(Integer status, LocalDateTime dateTime) {
29 return selectList(new LambdaQueryWrapperX<RewardActivityDO>() 52 return selectList(new LambdaQueryWrapperX<RewardActivityDO>()
30 .eq(RewardActivityDO::getStatus, status) 53 .eq(RewardActivityDO::getStatus, status)
31 - // 开始时间 < 指定时间(dateTime) < 结束时间,也就是说获取指定时间段的活动  
32 - .lt(RewardActivityDO::getStartTime, dateTime).gt(RewardActivityDO::getEndTime, dateTime) 54 + .lt(RewardActivityDO::getStartTime, dateTime)
  55 + .gt(RewardActivityDO::getEndTime, dateTime)// 开始时间 < 指定时间 < 结束时间,也就是说获取指定时间段的活动
33 .orderByAsc(RewardActivityDO::getStartTime) 56 .orderByAsc(RewardActivityDO::getStartTime)
34 ); 57 );
35 } 58 }
36 59
  60 + default List<RewardActivityDO> getRewardActivityByStatusAndDateTimeLt(Collection<Long> spuIds,Collection<Long> categoryIds, Integer status, LocalDateTime dateTime) {
  61 + //拼接通用券查询语句
  62 + Function<Collection<Long>, String> productScopeValuesFindInSetFunc = ids -> ids.stream()
  63 + .map(id -> StrUtil.format("FIND_IN_SET({}, product_scope_values) ", id))
  64 + .collect(Collectors.joining(" OR "));
  65 + return selectList(new LambdaQueryWrapperX<RewardActivityDO>()
  66 + .eq(RewardActivityDO::getStatus,status)
  67 + .lt(RewardActivityDO::getStartTime, dateTime)
  68 + .gt(RewardActivityDO::getEndTime, dateTime)
  69 + .and(i -> i.eq(RewardActivityDO::getProductScope, PromotionProductScopeEnum.SPU.getScope())
  70 + .and(i1 -> i1.apply(productScopeValuesFindInSetFunc.apply(spuIds)))
  71 + .or(i1 -> i1.eq(RewardActivityDO::getProductScope, PromotionProductScopeEnum.ALL.getScope()))
  72 + .or(i1 -> i1.eq(RewardActivityDO::getProductScope, PromotionProductScopeEnum.CATEGORY.getScope())
  73 + .and(i2 -> i2.apply(productScopeValuesFindInSetFunc.apply(categoryIds)))))
  74 + .orderByDesc(RewardActivityDO::getId)
  75 + );
  76 + }
  77 +
37 } 78 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillactivity/SeckillActivityMapper.java
@@ -51,7 +51,7 @@ public interface SeckillActivityMapper extends BaseMapperX&lt;SeckillActivityDO&gt; { @@ -51,7 +51,7 @@ public interface SeckillActivityMapper extends BaseMapperX&lt;SeckillActivityDO&gt; {
51 Assert.isTrue(count > 0); 51 Assert.isTrue(count > 0);
52 return update(null, new LambdaUpdateWrapper<SeckillActivityDO>() 52 return update(null, new LambdaUpdateWrapper<SeckillActivityDO>()
53 .eq(SeckillActivityDO::getId, id) 53 .eq(SeckillActivityDO::getId, id)
54 - .gt(SeckillActivityDO::getStock, count) 54 + .ge(SeckillActivityDO::getStock, count)
55 .setSql("stock = stock - " + count)); 55 .setSql("stock = stock - " + count));
56 } 56 }
57 57
@@ -69,9 +69,11 @@ public interface SeckillActivityMapper extends BaseMapperX&lt;SeckillActivityDO&gt; { @@ -69,9 +69,11 @@ public interface SeckillActivityMapper extends BaseMapperX&lt;SeckillActivityDO&gt; {
69 .setSql("stock = stock + " + count)); 69 .setSql("stock = stock + " + count));
70 } 70 }
71 71
72 - default PageResult<SeckillActivityDO> selectPage(AppSeckillActivityPageReqVO pageReqVO, Integer status) { 72 + default PageResult<SeckillActivityDO> selectPage(AppSeckillActivityPageReqVO pageReqVO, Integer status, LocalDateTime dateTime) {
73 return selectPage(pageReqVO, new LambdaQueryWrapperX<SeckillActivityDO>() 73 return selectPage(pageReqVO, new LambdaQueryWrapperX<SeckillActivityDO>()
74 .eqIfPresent(SeckillActivityDO::getStatus, status) 74 .eqIfPresent(SeckillActivityDO::getStatus, status)
  75 + .lt(SeckillActivityDO::getStartTime, dateTime)
  76 + .gt(SeckillActivityDO::getEndTime, dateTime)// 开始时间 < 指定时间 < 结束时间,也就是说获取指定时间段的活动
75 .apply(ObjectUtil.isNotNull(pageReqVO.getConfigId()), "FIND_IN_SET(" + pageReqVO.getConfigId() + ",config_ids) > 0")); 77 .apply(ObjectUtil.isNotNull(pageReqVO.getConfigId()), "FIND_IN_SET(" + pageReqVO.getConfigId() + ",config_ids) > 0"));
76 } 78 }
77 79
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillconfig/SeckillConfigMapper.java
1 package cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillconfig; 1 package cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillconfig;
2 2
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 5 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6 import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config.SeckillConfigPageReqVO; 7 import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config.SeckillConfigPageReqVO;
7 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillConfigDO; 8 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillConfigDO;
  9 +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
8 import org.apache.ibatis.annotations.Mapper; 10 import org.apache.ibatis.annotations.Mapper;
9 11
  12 +import java.time.LocalDateTime;
  13 +import java.time.format.DateTimeFormatter;
10 import java.util.List; 14 import java.util.List;
11 15
12 @Mapper 16 @Mapper
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponService.java
@@ -5,6 +5,7 @@ import cn.hutool.core.map.MapUtil; @@ -5,6 +5,7 @@ import cn.hutool.core.map.MapUtil;
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO; 6 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
7 import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponMatchReqVO; 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;
8 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO; 9 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
9 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO; 10 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
10 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum; 11 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
@@ -178,7 +179,7 @@ public interface CouponService { @@ -178,7 +179,7 @@ public interface CouponService {
178 * @param matchReqVO 匹配参数 179 * @param matchReqVO 匹配参数
179 * @return 优惠券列表 180 * @return 优惠券列表
180 */ 181 */
181 - List<CouponDO> getMatchCouponList(Long userId, AppCouponMatchReqVO matchReqVO); 182 + List<AppCouponMatchRespVO> getMatchCouponList(Long userId, AppCouponMatchReqVO matchReqVO);
182 183
183 /** 184 /**
184 * 获取用户是否可以领取优惠券 185 * 获取用户是否可以领取优惠券
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java
@@ -13,10 +13,12 @@ import cn.iocoder.yudao.module.member.api.user.MemberUserApi; @@ -13,10 +13,12 @@ import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
13 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; 13 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
14 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO; 14 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
15 import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponMatchReqVO; 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;
16 import cn.iocoder.yudao.module.promotion.convert.coupon.CouponConvert; 17 import cn.iocoder.yudao.module.promotion.convert.coupon.CouponConvert;
17 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO; 18 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
18 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO; 19 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
19 import cn.iocoder.yudao.module.promotion.dal.mysql.coupon.CouponMapper; 20 import cn.iocoder.yudao.module.promotion.dal.mysql.coupon.CouponMapper;
  21 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
20 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum; 22 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;
21 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum; 23 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
22 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTemplateValidityTypeEnum; 24 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTemplateValidityTypeEnum;
@@ -286,6 +288,7 @@ public class CouponServiceImpl implements CouponService { @@ -286,6 +288,7 @@ public class CouponServiceImpl implements CouponService {
286 } 288 }
287 // 校验剩余数量(仅在 CouponTakeTypeEnum.USER 用户领取时) 289 // 校验剩余数量(仅在 CouponTakeTypeEnum.USER 用户领取时)
288 if (CouponTakeTypeEnum.isUser(couponTemplate.getTakeCount()) 290 if (CouponTakeTypeEnum.isUser(couponTemplate.getTakeCount())
  291 + && couponTemplate.getTotalCount() != null
289 && couponTemplate.getTakeCount() + userIds.size() > couponTemplate.getTotalCount()) { 292 && couponTemplate.getTakeCount() + userIds.size() > couponTemplate.getTotalCount()) {
290 throw exception(COUPON_TEMPLATE_NOT_ENOUGH); 293 throw exception(COUPON_TEMPLATE_NOT_ENOUGH);
291 } 294 }
@@ -308,7 +311,7 @@ public class CouponServiceImpl implements CouponService { @@ -308,7 +311,7 @@ public class CouponServiceImpl implements CouponService {
308 * @param couponTemplate 优惠劵模版 311 * @param couponTemplate 优惠劵模版
309 */ 312 */
310 private void removeTakeLimitUser(Set<Long> userIds, CouponTemplateDO couponTemplate) { 313 private void removeTakeLimitUser(Set<Long> userIds, CouponTemplateDO couponTemplate) {
311 - if (couponTemplate.getTakeLimitCount() <= 0) { 314 + if (couponTemplate.getTakeLimitCount() == null || couponTemplate.getTakeLimitCount() <= 0) {
312 return; 315 return;
313 } 316 }
314 // 查询已领过券的用户 317 // 查询已领过券的用户
@@ -356,13 +359,45 @@ public class CouponServiceImpl implements CouponService { @@ -356,13 +359,45 @@ public class CouponServiceImpl implements CouponService {
356 } 359 }
357 360
358 @Override 361 @Override
359 - public List<CouponDO> getMatchCouponList(Long userId, AppCouponMatchReqVO matchReqVO) { 362 + public List<AppCouponMatchRespVO> getMatchCouponList(Long userId, AppCouponMatchReqVO matchReqVO) {
  363 + List<AppCouponMatchRespVO> couponMatchist = new ArrayList<>();
360 List<CouponDO> list = couponMapper.selectListByUserIdAndStatusAndUsePriceLeAndProductScope(userId, 364 List<CouponDO> list = couponMapper.selectListByUserIdAndStatusAndUsePriceLeAndProductScope(userId,
361 - CouponStatusEnum.UNUSED.getStatus(),  
362 - matchReqVO.getPrice(), matchReqVO.getSpuIds(), matchReqVO.getCategoryIds());  
363 - // 兜底逻辑:如果 CouponExpireJob 未执行,status 未变成 EXPIRE ,但是 validEndTime 已经过期了,需要进行过滤  
364 - list.removeIf(coupon -> !LocalDateTimeUtils.isBetween(coupon.getValidStartTime(), coupon.getValidEndTime()));  
365 - return list; 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;
366 } 401 }
367 402
368 @Override 403 @Override
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponTemplateServiceImpl.java
@@ -13,6 +13,7 @@ import cn.iocoder.yudao.module.promotion.dal.mysql.coupon.CouponTemplateMapper; @@ -13,6 +13,7 @@ import cn.iocoder.yudao.module.promotion.dal.mysql.coupon.CouponTemplateMapper;
13 import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum; 13 import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
14 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum; 14 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
15 import org.springframework.stereotype.Service; 15 import org.springframework.stereotype.Service;
  16 +import jakarta.validation.Validator;
16 import org.springframework.validation.annotation.Validated; 17 import org.springframework.validation.annotation.Validated;
17 18
18 import jakarta.annotation.Resource; 19 import jakarta.annotation.Resource;
@@ -40,9 +41,13 @@ public class CouponTemplateServiceImpl implements CouponTemplateService { @@ -40,9 +41,13 @@ public class CouponTemplateServiceImpl implements CouponTemplateService {
40 private ProductCategoryApi productCategoryApi; 41 private ProductCategoryApi productCategoryApi;
41 @Resource 42 @Resource
42 private ProductSpuApi productSpuApi; 43 private ProductSpuApi productSpuApi;
  44 + @Resource
  45 + private Validator validator;
43 46
44 @Override 47 @Override
45 public Long createCouponTemplate(CouponTemplateCreateReqVO createReqVO) { 48 public Long createCouponTemplate(CouponTemplateCreateReqVO createReqVO) {
  49 + // 校验参数
  50 + createReqVO.validate(validator);
46 // 校验商品范围 51 // 校验商品范围
47 validateProductScope(createReqVO.getProductScope(), createReqVO.getProductScopeValues()); 52 validateProductScope(createReqVO.getProductScope(), createReqVO.getProductScopeValues());
48 // 插入 53 // 插入
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountActivityService.java
1 package cn.iocoder.yudao.module.promotion.service.discount; 1 package cn.iocoder.yudao.module.promotion.service.discount;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.promotion.api.discount.dto.DiscountProductRespDTO;
4 import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityCreateReqVO; 5 import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityCreateReqVO;
5 import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityPageReqVO; 6 import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityPageReqVO;
6 import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityUpdateReqVO; 7 import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityUpdateReqVO;
@@ -27,7 +28,7 @@ public interface DiscountActivityService { @@ -27,7 +28,7 @@ public interface DiscountActivityService {
27 * @param skuIds SKU 编号数组 28 * @param skuIds SKU 编号数组
28 * @return 匹配的限时折扣商品 29 * @return 匹配的限时折扣商品
29 */ 30 */
30 - List<DiscountProductDO> getMatchDiscountProductList(Collection<Long> skuIds); 31 + List<DiscountProductRespDTO> getMatchDiscountProductList(Collection<Long> skuIds);
31 32
32 /** 33 /**
33 * 创建限时折扣活动 34 * 创建限时折扣活动
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountActivityServiceImpl.java
@@ -6,6 +6,7 @@ import cn.hutool.core.map.MapUtil; @@ -6,6 +6,7 @@ import cn.hutool.core.map.MapUtil;
6 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 6 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
7 import cn.iocoder.yudao.framework.common.pojo.PageResult; 7 import cn.iocoder.yudao.framework.common.pojo.PageResult;
8 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 8 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  9 +import cn.iocoder.yudao.module.promotion.api.discount.dto.DiscountProductRespDTO;
9 import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityBaseVO; 10 import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityBaseVO;
10 import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityCreateReqVO; 11 import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityCreateReqVO;
11 import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityPageReqVO; 12 import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityPageReqVO;
@@ -49,7 +50,7 @@ public class DiscountActivityServiceImpl implements DiscountActivityService { @@ -49,7 +50,7 @@ public class DiscountActivityServiceImpl implements DiscountActivityService {
49 private DiscountProductMapper discountProductMapper; 50 private DiscountProductMapper discountProductMapper;
50 51
51 @Override 52 @Override
52 - public List<DiscountProductDO> getMatchDiscountProductList(Collection<Long> skuIds) { 53 + public List<DiscountProductRespDTO> getMatchDiscountProductList(Collection<Long> skuIds) {
53 return discountProductMapper.getMatchDiscountProductList(skuIds); 54 return discountProductMapper.getMatchDiscountProductList(skuIds);
54 } 55 }
55 56
@@ -130,7 +131,7 @@ public class DiscountActivityServiceImpl implements DiscountActivityService { @@ -130,7 +131,7 @@ public class DiscountActivityServiceImpl implements DiscountActivityService {
130 List<DiscountProductDO> list = discountProductMapper.selectListByActivityId(id); 131 List<DiscountProductDO> list = discountProductMapper.selectListByActivityId(id);
131 // TODO @zhangshuai:一般简单的 stream 方法,建议是使用 CollectionUtils,例如说这里是 convertList 对把。 132 // TODO @zhangshuai:一般简单的 stream 方法,建议是使用 CollectionUtils,例如说这里是 convertList 对把。
132 List<Long> skuIds = list.stream().map(item -> item.getSkuId()).collect(Collectors.toList()); 133 List<Long> skuIds = list.stream().map(item -> item.getSkuId()).collect(Collectors.toList());
133 - List<DiscountProductDO> matchDiscountProductList = getMatchDiscountProductList(skuIds); 134 + List<DiscountProductRespDTO> matchDiscountProductList = getMatchDiscountProductList(skuIds);
134 if (id != null) { // 排除自己这个活动 135 if (id != null) { // 排除自己这个活动
135 matchDiscountProductList.removeIf(product -> id.equals(product.getActivityId())); 136 matchDiscountProductList.removeIf(product -> id.equals(product.getActivityId()));
136 } 137 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/reward/RewardActivityService.java
@@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO; @@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;
8 import jakarta.validation.Valid; 8 import jakarta.validation.Valid;
9 9
10 import java.time.LocalDateTime; 10 import java.time.LocalDateTime;
  11 +import java.util.Collection;
11 import java.util.List; 12 import java.util.List;
12 13
13 /** 14 /**
@@ -71,4 +72,14 @@ public interface RewardActivityService { @@ -71,4 +72,14 @@ public interface RewardActivityService {
71 */ 72 */
72 List<RewardActivityDO> getRewardActivityListByStatusAndDateTimeLt(Integer status, LocalDateTime dateTime); 73 List<RewardActivityDO> getRewardActivityListByStatusAndDateTimeLt(Integer status, LocalDateTime dateTime);
73 74
  75 + /**
  76 + * 获取指定 spu 编号最近参加的活动,每个 spuId 只返回一条记录
  77 + *
  78 + * @param spuIds SPU 编号数组
  79 + * @param status 状态
  80 + * @param dateTime 当前日期时间
  81 + * @return 满减送活动列表
  82 + */
  83 + List<RewardActivityDO> getRewardActivityBySpuIdsAndStatusAndDateTimeLt(Collection<Long> spuIds, Integer status, LocalDateTime dateTime);
  84 +
74 } 85 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/reward/RewardActivityServiceImpl.java
1 package cn.iocoder.yudao.module.promotion.service.reward; 1 package cn.iocoder.yudao.module.promotion.service.reward;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
3 import cn.hutool.core.date.LocalDateTimeUtil; 4 import cn.hutool.core.date.LocalDateTimeUtil;
4 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
@@ -11,8 +12,10 @@ import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivi @@ -11,8 +12,10 @@ import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivi
11 import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityCreateReqVO; 12 import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityCreateReqVO;
12 import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityPageReqVO; 13 import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityPageReqVO;
13 import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityUpdateReqVO; 14 import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityUpdateReqVO;
  15 +import cn.iocoder.yudao.module.promotion.convert.reward.RewardActivityConvert;
14 import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO; 16 import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;
15 import cn.iocoder.yudao.module.promotion.dal.mysql.reward.RewardActivityMapper; 17 import cn.iocoder.yudao.module.promotion.dal.mysql.reward.RewardActivityMapper;
  18 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionActivityStatusEnum;
16 import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum; 19 import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
17 import cn.iocoder.yudao.module.promotion.util.PromotionUtils; 20 import cn.iocoder.yudao.module.promotion.util.PromotionUtils;
18 import jakarta.annotation.Resource; 21 import jakarta.annotation.Resource;
@@ -20,8 +23,11 @@ import org.springframework.stereotype.Service; @@ -20,8 +23,11 @@ import org.springframework.stereotype.Service;
20 import org.springframework.validation.annotation.Validated; 23 import org.springframework.validation.annotation.Validated;
21 24
22 import java.time.LocalDateTime; 25 import java.time.LocalDateTime;
  26 +import java.util.Collection;
  27 +import java.util.Collections;
23 import java.util.List; 28 import java.util.List;
24 import java.util.Objects; 29 import java.util.Objects;
  30 +import java.util.stream.Collectors;
25 31
26 import static cn.hutool.core.collection.CollUtil.intersectionDistinct; 32 import static cn.hutool.core.collection.CollUtil.intersectionDistinct;
27 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 33 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -52,9 +58,13 @@ public class RewardActivityServiceImpl implements RewardActivityService { @@ -52,9 +58,13 @@ public class RewardActivityServiceImpl implements RewardActivityService {
52 // 1.2 校验商品是否冲突 58 // 1.2 校验商品是否冲突
53 validateRewardActivitySpuConflicts(null, createReqVO); 59 validateRewardActivitySpuConflicts(null, createReqVO);
54 60
55 - // 2. 插入  
56 - RewardActivityDO rewardActivity = BeanUtils.toBean(createReqVO, RewardActivityDO.class)  
57 - .setStatus(PromotionUtils.calculateActivityStatus(createReqVO.getEndTime())); 61 + // 插入
  62 + RewardActivityDO rewardActivity = RewardActivityConvert.INSTANCE.convert(createReqVO)
  63 + .setStatus(
  64 + PromotionUtils.calculateActivityStatus(createReqVO.getEndTime()).equals(CommonStatusEnum.DISABLE.getStatus())?
  65 + PromotionActivityStatusEnum.WAIT.getStatus():
  66 + PromotionActivityStatusEnum.RUN.getStatus()
  67 + );
58 rewardActivityMapper.insert(rewardActivity); 68 rewardActivityMapper.insert(rewardActivity);
59 // 返回 69 // 返回
60 return rewardActivity.getId(); 70 return rewardActivity.getId();
@@ -199,4 +209,19 @@ public class RewardActivityServiceImpl implements RewardActivityService { @@ -199,4 +209,19 @@ public class RewardActivityServiceImpl implements RewardActivityService {
199 return rewardActivityMapper.selectListByStatusAndDateTimeLt(status, dateTime); 209 return rewardActivityMapper.selectListByStatusAndDateTimeLt(status, dateTime);
200 } 210 }
201 211
  212 + @Override
  213 + public List<RewardActivityDO> getRewardActivityBySpuIdsAndStatusAndDateTimeLt(Collection<Long> spuIds, Integer status, LocalDateTime dateTime) {
  214 + List<ProductSpuRespDTO> spuList = productSpuApi.validateSpuList(spuIds);
  215 + //查询出商品的分类ids
  216 + List<Long> categoryIds = spuList.stream().map(ProductSpuRespDTO::getCategoryId).collect(Collectors.toList());
  217 + // 1. 查询出指定 spuId 的 spu 参加的活动
  218 + List<RewardActivityDO> rewardActivityList = rewardActivityMapper.getRewardActivityByStatusAndDateTimeLt(spuIds, categoryIds,status,dateTime);
  219 + if (CollUtil.isEmpty(rewardActivityList)) {
  220 + return Collections.emptyList();
  221 + }
  222 +
  223 + // 2. 查询活动详情
  224 + return rewardActivityList;
  225 + }
  226 +
202 } 227 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityService.java
@@ -110,7 +110,7 @@ public interface SeckillActivityService { @@ -110,7 +110,7 @@ public interface SeckillActivityService {
110 List<SeckillActivityDO> getSeckillActivityListByConfigIdAndStatus(Long configId, Integer status); 110 List<SeckillActivityDO> getSeckillActivityListByConfigIdAndStatus(Long configId, Integer status);
111 111
112 /** 112 /**
113 - * 通过活动时段获取秒杀活动 113 + * 通过活动时段获取开始的秒杀活动
114 * 114 *
115 * @param pageReqVO 请求 115 * @param pageReqVO 请求
116 * @return 秒杀活动列表 116 * @return 秒杀活动列表
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityServiceImpl.java
@@ -23,6 +23,7 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillConfigDO; @@ -23,6 +23,7 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillConfigDO;
23 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillProductDO; 23 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillProductDO;
24 import cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity.SeckillActivityMapper; 24 import cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity.SeckillActivityMapper;
25 import cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity.SeckillProductMapper; 25 import cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity.SeckillProductMapper;
  26 +import cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillconfig.SeckillConfigMapper;
26 import org.springframework.stereotype.Service; 27 import org.springframework.stereotype.Service;
27 import org.springframework.transaction.annotation.Transactional; 28 import org.springframework.transaction.annotation.Transactional;
28 import org.springframework.validation.annotation.Validated; 29 import org.springframework.validation.annotation.Validated;
@@ -57,6 +58,8 @@ public class SeckillActivityServiceImpl implements SeckillActivityService { @@ -57,6 +58,8 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
57 @Resource 58 @Resource
58 private SeckillProductMapper seckillProductMapper; 59 private SeckillProductMapper seckillProductMapper;
59 @Resource 60 @Resource
  61 + private SeckillConfigMapper seckillConfigMapper;
  62 + @Resource
60 private SeckillConfigService seckillConfigService; 63 private SeckillConfigService seckillConfigService;
61 @Resource 64 @Resource
62 private ProductSpuApi productSpuApi; 65 private ProductSpuApi productSpuApi;
@@ -289,7 +292,7 @@ public class SeckillActivityServiceImpl implements SeckillActivityService { @@ -289,7 +292,7 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
289 292
290 @Override 293 @Override
291 public PageResult<SeckillActivityDO> getSeckillActivityAppPageByConfigId(AppSeckillActivityPageReqVO pageReqVO) { 294 public PageResult<SeckillActivityDO> getSeckillActivityAppPageByConfigId(AppSeckillActivityPageReqVO pageReqVO) {
292 - return seckillActivityMapper.selectPage(pageReqVO, CommonStatusEnum.ENABLE.getStatus()); 295 + return seckillActivityMapper.selectPage(pageReqVO, CommonStatusEnum.ENABLE.getStatus(),LocalDateTime.now());
293 } 296 }
294 297
295 @Override 298 @Override
yudao-module-mall/yudao-module-promotion-biz/src/main/resources/mapper/discount/DiscountProductMapper.xml
@@ -3,8 +3,8 @@ @@ -3,8 +3,8 @@
3 <mapper namespace="cn.iocoder.yudao.module.promotion.dal.mysql.discount.DiscountProductMapper"> 3 <mapper namespace="cn.iocoder.yudao.module.promotion.dal.mysql.discount.DiscountProductMapper">
4 4
5 5
6 - <select id="getMatchDiscountProductList" resultType="cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO">  
7 - SELECT pdp.* 6 + <select id="getMatchDiscountProductList" resultType="cn.iocoder.yudao.module.promotion.api.discount.dto.DiscountProductRespDTO">
  7 + SELECT pdp.*,pda.name as activity_name
8 FROM promotion_discount_product pdp 8 FROM promotion_discount_product pdp
9 LEFT JOIN promotion_discount_activity pda 9 LEFT JOIN promotion_discount_activity pda
10 ON pdp.activity_id = pda.id 10 ON pdp.activity_id = pda.id
@@ -16,9 +16,11 @@ @@ -16,9 +16,11 @@
16 </foreach> 16 </foreach>
17 </if> 17 </if>
18 AND pda.start_time &lt;= CURRENT_TIME AND pda.end_time &gt;= CURRENT_TIME 18 AND pda.start_time &lt;= CURRENT_TIME AND pda.end_time &gt;= CURRENT_TIME
19 - AND pda.`status` = 20  
20 - AND pda.deleted != 1 19 + AND pda.`status` = 0
  20 + AND pda.deleted =0
  21 + AND pdp.deleted = 0
21 </where> 22 </where>
  23 + ORDER BY pdp.id DESC
22 </select> 24 </select>
23 25
24 </mapper> 26 </mapper>
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java
1 package cn.iocoder.yudao.module.trade.controller.app.order; 1 package cn.iocoder.yudao.module.trade.controller.app.order;
2 2
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
3 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated; 6 import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
  7 +import cn.iocoder.yudao.module.member.api.level.MemberLevelApi;
  8 +import cn.iocoder.yudao.module.member.api.level.dto.MemberLevelRespDTO;
  9 +import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
  10 +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
6 import cn.iocoder.yudao.module.pay.api.notify.dto.PayOrderNotifyReqDTO; 11 import cn.iocoder.yudao.module.pay.api.notify.dto.PayOrderNotifyReqDTO;
  12 +import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
  13 +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
  14 +import cn.iocoder.yudao.module.promotion.api.discount.DiscountActivityApi;
  15 +import cn.iocoder.yudao.module.promotion.api.discount.dto.DiscountProductRespDTO;
  16 +import cn.iocoder.yudao.module.promotion.api.reward.RewardActivityApi;
  17 +import cn.iocoder.yudao.module.promotion.api.reward.dto.RewardActivityMatchRespDTO;
  18 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionConditionTypeEnum;
  19 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;
  20 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
7 import cn.iocoder.yudao.module.trade.controller.app.order.vo.*; 21 import cn.iocoder.yudao.module.trade.controller.app.order.vo.*;
8 import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemCommentCreateReqVO; 22 import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemCommentCreateReqVO;
9 import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemRespVO; 23 import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemRespVO;
@@ -27,12 +41,14 @@ import lombok.extern.slf4j.Slf4j; @@ -27,12 +41,14 @@ import lombok.extern.slf4j.Slf4j;
27 import org.springframework.validation.annotation.Validated; 41 import org.springframework.validation.annotation.Validated;
28 import org.springframework.web.bind.annotation.*; 42 import org.springframework.web.bind.annotation.*;
29 43
30 -import java.util.List;  
31 -import java.util.Map; 44 +import java.time.LocalDateTime;
  45 +import java.util.*;
32 46
  47 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
33 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 48 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
34 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; 49 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
35 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; 50 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  51 +import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.DISCOUNT_ACTIVITY_TYPE_NOT_EXISTS;
36 52
37 @Tag(name = "用户 App - 交易订单") 53 @Tag(name = "用户 App - 交易订单")
38 @RestController 54 @RestController
@@ -54,6 +70,17 @@ public class AppTradeOrderController { @@ -54,6 +70,17 @@ public class AppTradeOrderController {
54 @Resource 70 @Resource
55 private TradeOrderProperties tradeOrderProperties; 71 private TradeOrderProperties tradeOrderProperties;
56 72
  73 + @Resource
  74 + private MemberLevelApi memberLevelApi;
  75 + @Resource
  76 + private MemberUserApi memberUserApi;
  77 + @Resource
  78 + private DiscountActivityApi discountActivityApi;
  79 + @Resource
  80 + private RewardActivityApi rewardActivityApi;
  81 + @Resource
  82 + private ProductSkuApi productKpuApi;
  83 +
57 @GetMapping("/settlement") 84 @GetMapping("/settlement")
58 @Operation(summary = "获得订单结算信息") 85 @Operation(summary = "获得订单结算信息")
59 @PreAuthenticated 86 @PreAuthenticated
@@ -61,6 +88,58 @@ public class AppTradeOrderController { @@ -61,6 +88,58 @@ public class AppTradeOrderController {
61 return success(tradeOrderUpdateService.settlementOrder(getLoginUserId(), settlementReqVO)); 88 return success(tradeOrderUpdateService.settlementOrder(getLoginUserId(), settlementReqVO));
62 } 89 }
63 90
  91 + @GetMapping("/settlementProduct")
  92 + @Operation(summary = "获得商品结算信息")
  93 + public CommonResult<List<AppTradeProductSettlementRespVO>> settlementProduct(@RequestParam("ids") Set<Long> ids) {
  94 + List<AppTradeProductSettlementRespVO> appTradeProductSettlementRespVOS = new ArrayList<>();
  95 + MemberLevelRespDTO memberLevel = getMemberLevel();
  96 + ids.forEach(spuId -> {
  97 + List<AppTradeProductSettlementRespVO.Sku> skus = new ArrayList<>();
  98 + List<ProductSkuRespDTO> skuList = productKpuApi.getSkuListBySpuId(Collections.singletonList(spuId));
  99 + //查询sku的会员和限时优惠
  100 + skuList.forEach(sku -> {
  101 + //查询限时优惠价格
  102 + AppTradeProductSettlementRespVO.Sku skuDiscount = calculateDiscountPrice(sku.getId(), sku.getPrice());
  103 +
  104 + //查询会员价
  105 + AppTradeProductSettlementRespVO.Sku skuVip = calculateVipPrice(sku.getId(), sku.getPrice(), memberLevel);
  106 +
  107 + if(skuDiscount != null && skuVip != null){
  108 + if(skuDiscount.getPrice() > skuVip.getPrice()){
  109 + skus.add(skuVip);
  110 + }else{
  111 + skus.add(skuDiscount);
  112 + }
  113 + }else if(skuDiscount != null){
  114 + skus.add(skuDiscount);
  115 + }else if(skuVip != null){
  116 + skus.add(skuVip);
  117 + }
  118 +
  119 + });
  120 + AppTradeProductSettlementRespVO.Reward reward = calculateReward(spuId);
  121 + AppTradeProductSettlementRespVO respVO = AppTradeProductSettlementRespVO.builder().id(spuId).skus(skus).build();
  122 + if(reward != null){
  123 + //创建满减活动对象
  124 + respVO.setReward(reward);
  125 + }
  126 + appTradeProductSettlementRespVOS.add(respVO);
  127 + });
  128 + return success(appTradeProductSettlementRespVOS);
  129 + }
  130 +
  131 + private MemberLevelRespDTO getMemberLevel() {
  132 + Long userId = getLoginUserId();
  133 + if (userId == null) {
  134 + return null;
  135 + }
  136 + MemberUserRespDTO user = memberUserApi.getUser(userId);
  137 + if (user.getLevelId() == null || user.getLevelId() <= 0) {
  138 + return null;
  139 + }
  140 + return memberLevelApi.getMemberLevel(user.getLevelId());
  141 + }
  142 +
64 @PostMapping("/create") 143 @PostMapping("/create")
65 @Operation(summary = "创建订单") 144 @Operation(summary = "创建订单")
66 @PreAuthenticated 145 @PreAuthenticated
@@ -188,4 +267,78 @@ public class AppTradeOrderController { @@ -188,4 +267,78 @@ public class AppTradeOrderController {
188 return success(tradeOrderUpdateService.createOrderItemCommentByMember(getLoginUserId(), createReqVO)); 267 return success(tradeOrderUpdateService.createOrderItemCommentByMember(getLoginUserId(), createReqVO));
189 } 268 }
190 269
  270 + /**
  271 + * 计算会员 VIP 优惠价格
  272 + *
  273 + * @param price 原价
  274 + * @param memberLevel 会员等级
  275 + * @return 优惠价格
  276 + */
  277 + public AppTradeProductSettlementRespVO.Sku calculateVipPrice(Long skuId, Integer price, MemberLevelRespDTO memberLevel) {
  278 + if (memberLevel == null || memberLevel.getDiscountPercent() == null) {
  279 + return null;
  280 + }
  281 + Integer newPrice = price * memberLevel.getDiscountPercent() / 100;
  282 + return AppTradeProductSettlementRespVO.Sku.builder().
  283 + skuId(skuId).
  284 + type(PromotionTypeEnum.MEMBER_LEVEL.getType()).
  285 + price(newPrice).build();
  286 + }
  287 +
  288 + /**
  289 + * 计算限时优惠信息
  290 + *
  291 + * @param price 原价
  292 + * @param skuId 商品规格id
  293 + * @return 优惠价格
  294 + */
  295 + private AppTradeProductSettlementRespVO.Sku calculateDiscountPrice(Long skuId, Integer price) {
  296 + if (skuId == null) {
  297 + return null;
  298 + }
  299 +
  300 + //根据商品id查询限时优惠
  301 + List<DiscountProductRespDTO> matchDiscountProductList = discountActivityApi.getMatchDiscountProductList(Collections.singletonList(skuId));
  302 + if (matchDiscountProductList != null && !matchDiscountProductList.isEmpty()) {
  303 + DiscountProductRespDTO discountProductRespDTO = matchDiscountProductList.get(matchDiscountProductList.size() - 1);
  304 + AppTradeProductSettlementRespVO.Sku sku = AppTradeProductSettlementRespVO.Sku.builder().
  305 + skuId(skuId).
  306 + discountId(discountProductRespDTO.getId()).
  307 + type(PromotionTypeEnum.DISCOUNT_ACTIVITY.getType()).
  308 + endTime(discountProductRespDTO.getActivityEndTime()).
  309 + build();
  310 + Integer discountType = discountProductRespDTO.getDiscountType();
  311 + if(Objects.equals(PromotionDiscountTypeEnum.PRICE.getType(), discountType)){
  312 + sku.setPrice(price - discountProductRespDTO.getDiscountPrice() * 100);
  313 + }else if(Objects.equals(PromotionDiscountTypeEnum.PERCENT.getType(), discountType)){
  314 + Integer newPrice = price * discountProductRespDTO.getDiscountPercent() / 100;
  315 + sku.setPrice(price - newPrice);
  316 + }else{
  317 + throw exception(DISCOUNT_ACTIVITY_TYPE_NOT_EXISTS);
  318 + }
  319 + return sku;
  320 + }
  321 + return null;
  322 + }
  323 +
  324 + /**
  325 + * 获取第一层满减活动
  326 + *
  327 + * @param spuId 商品规格id
  328 + * @return 优惠价格
  329 + */
  330 + private AppTradeProductSettlementRespVO.Reward calculateReward(Long spuId) {
  331 + List<RewardActivityMatchRespDTO> matchRewardActivityList = rewardActivityApi.getRewardActivityBySpuIdsAndStatusAndDateTimeLt(Collections.singletonList(spuId), CommonStatusEnum.ENABLE.getStatus(), LocalDateTime.now());
  332 + if(matchRewardActivityList != null && !matchRewardActivityList.isEmpty()){
  333 + RewardActivityMatchRespDTO rewardActivityMatchRespDTO = matchRewardActivityList.get(matchRewardActivityList.size() - 1);
  334 + if(rewardActivityMatchRespDTO != null){
  335 + RewardActivityMatchRespDTO.Rule rule = rewardActivityMatchRespDTO.getRules().get(0);
  336 + return AppTradeProductSettlementRespVO.Reward.builder().
  337 + rewardActivity("满" + rule.getLimit() / 100 + (Objects.equals(rewardActivityMatchRespDTO.getConditionType(), PromotionConditionTypeEnum.PRICE.getType())?"元":"件"+"减") +rule.getDiscountPrice() / 100)
  338 + .id(rewardActivityMatchRespDTO.getId()).build();
  339 + }
  340 + }
  341 + return null;
  342 + }
  343 +
191 } 344 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementRespVO.java
1 package cn.iocoder.yudao.module.trade.controller.app.order.vo; 1 package cn.iocoder.yudao.module.trade.controller.app.order.vo;
2 2
3 import cn.iocoder.yudao.module.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO; 3 import cn.iocoder.yudao.module.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO;
  4 +import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
4 import io.swagger.v3.oas.annotations.media.Schema; 5 import io.swagger.v3.oas.annotations.media.Schema;
5 import jakarta.validation.constraints.NotNull; 6 import jakarta.validation.constraints.NotNull;
6 import lombok.AllArgsConstructor; 7 import lombok.AllArgsConstructor;
@@ -31,6 +32,13 @@ public class AppTradeOrderSettlementRespVO { @@ -31,6 +32,13 @@ public class AppTradeOrderSettlementRespVO {
31 @Schema(description = "总积分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") 32 @Schema(description = "总积分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
32 private Integer totalPoint; 33 private Integer totalPoint;
33 34
  35 + /**
  36 + * 营销活动数组
  37 + *
  38 + * 只对应 {@link TradePriceCalculateRespBO.Price#items} 商品匹配的活动
  39 + */
  40 + private List<TradePriceCalculateRespBO.Promotion> promotions;
  41 +
34 @Schema(description = "购物项") 42 @Schema(description = "购物项")
35 @Data 43 @Data
36 public static class Item { 44 public static class Item {
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeProductSettlementRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.controller.app.order.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Builder;
  5 +import lombok.Data;
  6 +
  7 +import java.io.Serializable;
  8 +import java.time.LocalDateTime;
  9 +import java.util.List;
  10 +
  11 +@Schema(description = "用户 App - 商品结算信息 Response VO")
  12 +@Data
  13 +@Builder
  14 +public class AppTradeProductSettlementRespVO {
  15 +
  16 + @Schema(description = "spu 商品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  17 + private Long id;
  18 +
  19 + @Schema(description = "满减活动对象", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  20 + private Reward reward;
  21 +
  22 + @Schema(description = "sku 活动信息", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  23 + private List<Sku> skus;
  24 +
  25 + /**
  26 + * 满减活动
  27 + */
  28 + @Data
  29 + @Builder
  30 + public static class Reward implements Serializable {
  31 +
  32 + @Schema(description = "满减活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  33 + private Long id;
  34 +
  35 + @Schema(description = "满减活动信息", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  36 + private String rewardActivity;
  37 +
  38 + }
  39 +
  40 + /**
  41 + * SKU 数组
  42 + */
  43 + @Data
  44 + @Builder
  45 + public static class Sku implements Serializable {
  46 +
  47 + @Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  48 + private Long skuId;
  49 +
  50 + @Schema(description = "价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  51 + private Integer price;
  52 +
  53 + @Schema(description = "营销类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  54 + private Integer type; // 对应 PromotionTypeEnum 枚举
  55 +
  56 + @Schema(description = "限时优惠id", requiredMode = Schema.RequiredMode.REQUIRED)
  57 + private Long discountId;
  58 +
  59 + @Schema(description = "活动结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
  60 + private LocalDateTime endTime;
  61 +
  62 + }
  63 +}
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/aftersale/AfterSaleConvert.java
@@ -43,11 +43,9 @@ public interface AfterSaleConvert { @@ -43,11 +43,9 @@ public interface AfterSaleConvert {
43 @Mapping(source = "afterSale.orderId", target = "merchantOrderId"), 43 @Mapping(source = "afterSale.orderId", target = "merchantOrderId"),
44 @Mapping(source = "afterSale.id", target = "merchantRefundId"), 44 @Mapping(source = "afterSale.id", target = "merchantRefundId"),
45 @Mapping(source = "afterSale.applyReason", target = "reason"), 45 @Mapping(source = "afterSale.applyReason", target = "reason"),
46 - @Mapping(source = "afterSale.refundPrice", target = "price"),  
47 - @Mapping(source = "orderProperties.payAppKey", target = "appKey") 46 + @Mapping(source = "afterSale.refundPrice", target = "price")
48 }) 47 })
49 - PayRefundCreateReqDTO convert(String userIp, AfterSaleDO afterSale,  
50 - TradeOrderProperties orderProperties); 48 + PayRefundCreateReqDTO convert(String userIp, AfterSaleDO afterSale);
51 49
52 MemberUserRespVO convert(MemberUserRespDTO bean); 50 MemberUserRespVO convert(MemberUserRespDTO bean);
53 51
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/AfterSaleServiceImpl.java
@@ -371,8 +371,9 @@ public class AfterSaleServiceImpl implements AfterSaleService { @@ -371,8 +371,9 @@ public class AfterSaleServiceImpl implements AfterSaleService {
371 @Override 371 @Override
372 public void afterCommit() { 372 public void afterCommit() {
373 // 创建退款单 373 // 创建退款单
374 - PayRefundCreateReqDTO createReqDTO = AfterSaleConvert.INSTANCE.convert(userIp, afterSale, tradeOrderProperties) 374 + PayRefundCreateReqDTO createReqDTO = AfterSaleConvert.INSTANCE.convert(userIp, afterSale)
375 .setReason(StrUtil.format("退款【{}】", afterSale.getSpuName())); 375 .setReason(StrUtil.format("退款【{}】", afterSale.getSpuName()));
  376 + createReqDTO.setAppKey(tradeOrderProperties.getPayAppKey());
376 Long payRefundId = payRefundApi.createRefund(createReqDTO); 377 Long payRefundId = payRefundApi.createRefund(createReqDTO);
377 // 更新售后单的退款单号 378 // 更新售后单的退款单号
378 tradeAfterSaleMapper.updateById(new AfterSaleDO().setId(afterSale.getId()).setPayRefundId(payRefundId)); 379 tradeAfterSaleMapper.updateById(new AfterSaleDO().setId(afterSale.getId()).setPayRefundId(payRefundId));
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateRespBO.java
@@ -68,10 +68,11 @@ public class TradePriceCalculateRespBO { @@ -68,10 +68,11 @@ public class TradePriceCalculateRespBO {
68 */ 68 */
69 private Long bargainActivityId; 69 private Long bargainActivityId;
70 70
  71 +
71 /** 72 /**
72 * 是否包邮 73 * 是否包邮
73 */ 74 */
74 - private Boolean freeDelivery; 75 + private Boolean freeDelivery = false;
75 76
76 /** 77 /**
77 * 赠送的优惠劵 78 * 赠送的优惠劵
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDeliveryPriceCalculator.java
@@ -121,10 +121,13 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { @@ -121,10 +121,13 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
121 * @return 是否包邮 121 * @return 是否包邮
122 */ 122 */
123 private boolean isGlobalExpressFree(TradePriceCalculateRespBO result) { 123 private boolean isGlobalExpressFree(TradePriceCalculateRespBO result) {
  124 +
124 TradeConfigDO config = tradeConfigService.getTradeConfig(); 125 TradeConfigDO config = tradeConfigService.getTradeConfig();
125 - return config != null 126 + return result.getFreeDelivery() ||
  127 + (config != null
126 && Boolean.TRUE.equals(config.getDeliveryExpressFreeEnabled()) // 开启包邮 128 && Boolean.TRUE.equals(config.getDeliveryExpressFreeEnabled()) // 开启包邮
127 - && result.getPrice().getPayPrice() >= config.getDeliveryExpressFreePrice(); // 满足包邮的价格 129 + && result.getPrice().getPayPrice() >= config.getDeliveryExpressFreePrice()
  130 + ); // 满足包邮的价格
128 } 131 }
129 132
130 private void calculateDeliveryPrice(List<OrderItem> selectedSkus, 133 private void calculateDeliveryPrice(List<OrderItem> selectedSkus,
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDiscountActivityPriceCalculator.java
@@ -3,6 +3,10 @@ package cn.iocoder.yudao.module.trade.service.price.calculator; @@ -3,6 +3,10 @@ package cn.iocoder.yudao.module.trade.service.price.calculator;
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.util.ObjectUtil; 4 import cn.hutool.core.util.ObjectUtil;
5 import cn.hutool.core.util.StrUtil; 5 import cn.hutool.core.util.StrUtil;
  6 +import cn.iocoder.yudao.module.member.api.level.MemberLevelApi;
  7 +import cn.iocoder.yudao.module.member.api.level.dto.MemberLevelRespDTO;
  8 +import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
  9 +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
6 import cn.iocoder.yudao.module.promotion.api.discount.DiscountActivityApi; 10 import cn.iocoder.yudao.module.promotion.api.discount.DiscountActivityApi;
7 import cn.iocoder.yudao.module.promotion.api.discount.dto.DiscountProductRespDTO; 11 import cn.iocoder.yudao.module.promotion.api.discount.dto.DiscountProductRespDTO;
8 import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum; 12 import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;
@@ -14,6 +18,8 @@ import org.springframework.core.annotation.Order; @@ -14,6 +18,8 @@ import org.springframework.core.annotation.Order;
14 import org.springframework.stereotype.Component; 18 import org.springframework.stereotype.Component;
15 19
16 import jakarta.annotation.Resource; 20 import jakarta.annotation.Resource;
  21 +
  22 +import java.math.BigDecimal;
17 import java.util.List; 23 import java.util.List;
18 import java.util.Map; 24 import java.util.Map;
19 25
@@ -32,6 +38,10 @@ public class TradeDiscountActivityPriceCalculator implements TradePriceCalculato @@ -32,6 +38,10 @@ public class TradeDiscountActivityPriceCalculator implements TradePriceCalculato
32 38
33 @Resource 39 @Resource
34 private DiscountActivityApi discountActivityApi; 40 private DiscountActivityApi discountActivityApi;
  41 + @Resource
  42 + private MemberLevelApi memberLevelApi;
  43 + @Resource
  44 + private MemberUserApi memberUserApi;
35 45
36 @Override 46 @Override
37 public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) { 47 public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
@@ -39,35 +49,101 @@ public class TradeDiscountActivityPriceCalculator implements TradePriceCalculato @@ -39,35 +49,101 @@ public class TradeDiscountActivityPriceCalculator implements TradePriceCalculato
39 if (ObjectUtil.notEqual(result.getType(), TradeOrderTypeEnum.NORMAL.getType())) { 49 if (ObjectUtil.notEqual(result.getType(), TradeOrderTypeEnum.NORMAL.getType())) {
40 return; 50 return;
41 } 51 }
  52 +
  53 + boolean discount;
  54 + boolean vip;
  55 +
  56 + //----------------------------------限时折扣计算-----------------------------------------
42 // 获得 SKU 对应的限时折扣活动 57 // 获得 SKU 对应的限时折扣活动
43 List<DiscountProductRespDTO> discountProducts = discountActivityApi.getMatchDiscountProductList( 58 List<DiscountProductRespDTO> discountProducts = discountActivityApi.getMatchDiscountProductList(
44 convertSet(result.getItems(), TradePriceCalculateRespBO.OrderItem::getSkuId)); 59 convertSet(result.getItems(), TradePriceCalculateRespBO.OrderItem::getSkuId));
45 if (CollUtil.isEmpty(discountProducts)) { 60 if (CollUtil.isEmpty(discountProducts)) {
46 - return; 61 + discount = false;
  62 + }else {
  63 + discount = true;
47 } 64 }
48 Map<Long, DiscountProductRespDTO> discountProductMap = convertMap(discountProducts, DiscountProductRespDTO::getSkuId); 65 Map<Long, DiscountProductRespDTO> discountProductMap = convertMap(discountProducts, DiscountProductRespDTO::getSkuId);
49 66
50 - // 处理每个 SKU 的限时折扣 67 +
  68 +
  69 + //----------------------------------会员计算-----------------------------------------
  70 + MemberLevelRespDTO level;
  71 + // 获得用户的会员等级
  72 + MemberUserRespDTO user = memberUserApi.getUser(param.getUserId());
  73 + if (user.getLevelId() != null && user.getLevelId() > 0) {
  74 + level = memberLevelApi.getMemberLevel(user.getLevelId());
  75 + if (level != null && level.getDiscountPercent() != null) {
  76 + vip = true;
  77 + }else {
  78 + vip = false;
  79 + }
  80 + }else {
  81 + level = null;
  82 + vip = false;
  83 + }
  84 +
  85 +
  86 + // 2. 计算每个 SKU 的优惠金额
51 result.getItems().forEach(orderItem -> { 87 result.getItems().forEach(orderItem -> {
52 - // 1. 获取该 SKU 的优惠信息  
53 - DiscountProductRespDTO discountProduct = discountProductMap.get(orderItem.getSkuId());  
54 - if (discountProduct == null) {  
55 - return; 88 +
  89 + //----------------------------------限时折扣计算-----------------------------------------
  90 + DiscountProductRespDTO discountProduct = null;
  91 + Integer newDiscountPrice = 0;
  92 + if (discount){
  93 + // 2.1 计算限时折扣优惠信息
  94 + discountProduct = discountProductMap.get(orderItem.getSkuId());
  95 + if (discountProduct != null) {
  96 + // 2.2 计算优惠金额
  97 + Integer newPayPrice = calculatePayPrice(discountProduct, orderItem);
  98 + newDiscountPrice = orderItem.getPayPrice() - newPayPrice;
  99 + }
  100 + }
  101 +
  102 +
  103 + //----------------------------------会员计算-----------------------------------------
  104 + Integer vipPrice = 0;
  105 + if (vip){
  106 + // 2.3 计算会员优惠金额
  107 + vipPrice = calculateVipPrice(orderItem.getPayPrice(), level.getDiscountPercent());
56 } 108 }
57 - // 2. 计算优惠金额  
58 - Integer newPayPrice = calculatePayPrice(discountProduct, orderItem);  
59 - Integer newDiscountPrice = orderItem.getPayPrice() - newPayPrice;  
60 109
61 - // 3.1 记录优惠明细 110 +
  111 + // 2.4 记录优惠明细
  112 + // 注意,只有在选中的情况下,才会记录到优惠明细。否则仅仅是更新 SKU 优惠金额,用于展示
62 if (orderItem.getSelected()) { 113 if (orderItem.getSelected()) {
63 - // 注意,只有在选中的情况下,才会记录到优惠明细。否则仅仅是更新 SKU 优惠金额,用于展示  
64 - TradePriceCalculatorHelper.addPromotion(result, orderItem,  
65 - discountProduct.getActivityId(), discountProduct.getActivityName(), PromotionTypeEnum.DISCOUNT_ACTIVITY.getType(),  
66 - StrUtil.format("限时折扣:省 {} 元", formatPrice(newDiscountPrice)),  
67 - newDiscountPrice); 114 + if (discount && vip){
  115 + if(newDiscountPrice > vipPrice){
  116 + TradePriceCalculatorHelper.addPromotion(result, orderItem,
  117 + discountProduct.getActivityId(), discountProduct.getActivityName(), PromotionTypeEnum.DISCOUNT_ACTIVITY.getType(),
  118 + StrUtil.format("限时折扣:省 {} 元", formatPrice(newDiscountPrice)),
  119 + newDiscountPrice);
  120 + // 2.5 更新 SKU 优惠金额
  121 + orderItem.setDiscountPrice(orderItem.getDiscountPrice() + newDiscountPrice);
  122 + }else{
  123 + TradePriceCalculatorHelper.addPromotion(result, orderItem,
  124 + level.getId(), level.getName(), PromotionTypeEnum.MEMBER_LEVEL.getType(),
  125 + String.format("会员等级折扣:省 %s 元", formatPrice(vipPrice)),
  126 + vipPrice);
  127 + // 2.5 更新 SKU 的优惠金额
  128 + orderItem.setVipPrice(vipPrice);
  129 + }
  130 + }else if (discount){
  131 + TradePriceCalculatorHelper.addPromotion(result, orderItem,
  132 + discountProduct.getActivityId(), discountProduct.getActivityName(), PromotionTypeEnum.DISCOUNT_ACTIVITY.getType(),
  133 + StrUtil.format("限时折扣:省 {} 元", formatPrice(newDiscountPrice)),
  134 + newDiscountPrice);
  135 + // 2.5 更新 SKU 优惠金额
  136 + orderItem.setDiscountPrice(orderItem.getDiscountPrice() + newDiscountPrice);
  137 + }else if (vip){
  138 + TradePriceCalculatorHelper.addPromotion(result, orderItem,
  139 + level.getId(), level.getName(), PromotionTypeEnum.MEMBER_LEVEL.getType(),
  140 + String.format("会员等级折扣:省 %s 元", formatPrice(vipPrice)),
  141 + vipPrice);
  142 + // 2.5 更新 SKU 的优惠金额
  143 + orderItem.setVipPrice(vipPrice);
  144 + }
68 } 145 }
69 - // 3.2 更新 SKU 优惠金额  
70 - orderItem.setDiscountPrice(orderItem.getDiscountPrice() + newDiscountPrice); 146 +
71 TradePriceCalculatorHelper.recountPayPrice(orderItem); 147 TradePriceCalculatorHelper.recountPayPrice(orderItem);
72 }); 148 });
73 TradePriceCalculatorHelper.recountAllPrice(result); 149 TradePriceCalculatorHelper.recountAllPrice(result);
@@ -86,4 +162,20 @@ public class TradeDiscountActivityPriceCalculator implements TradePriceCalculato @@ -86,4 +162,20 @@ public class TradeDiscountActivityPriceCalculator implements TradePriceCalculato
86 return price; 162 return price;
87 } 163 }
88 164
  165 + /**
  166 + * 计算会员 VIP 优惠价格
  167 + *
  168 + * @param price 原价
  169 + * @param discountPercent 折扣
  170 + * @return 优惠价格
  171 + */
  172 + public Integer calculateVipPrice(Integer price, Integer discountPercent) {
  173 + if (discountPercent == null) {
  174 + return 0;
  175 + }
  176 + BigDecimal divide = new BigDecimal(price).multiply(new BigDecimal(discountPercent)).divide(new BigDecimal(100));
  177 + Integer newPrice = divide.intValue();
  178 + return price - newPrice;
  179 + }
  180 +
89 } 181 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeMemberLevelPriceCalculator.java
@@ -30,44 +30,49 @@ public class TradeMemberLevelPriceCalculator implements TradePriceCalculator { @@ -30,44 +30,49 @@ public class TradeMemberLevelPriceCalculator implements TradePriceCalculator {
30 @Resource 30 @Resource
31 private MemberUserApi memberUserApi; 31 private MemberUserApi memberUserApi;
32 32
  33 + /**
  34 + * 会员计算迁移到限时优惠计算里
  35 + * @param param
  36 + * @param result
  37 + */
33 @Override 38 @Override
34 public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) { 39 public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
35 - // 0. 只有【普通】订单,才计算该优惠  
36 - if (ObjectUtil.notEqual(result.getType(), TradeOrderTypeEnum.NORMAL.getType())) {  
37 - return;  
38 - }  
39 - // 1. 获得用户的会员等级  
40 - MemberUserRespDTO user = memberUserApi.getUser(param.getUserId());  
41 - if (user.getLevelId() == null || user.getLevelId() <= 0) {  
42 - return;  
43 - }  
44 - MemberLevelRespDTO level = memberLevelApi.getMemberLevel(user.getLevelId());  
45 - if (level == null || level.getDiscountPercent() == null) {  
46 - return;  
47 - }  
48 -  
49 - // 2. 计算每个 SKU 的优惠金额  
50 - result.getItems().forEach(orderItem -> {  
51 - // 2.1 计算优惠金额  
52 - Integer vipPrice = calculateVipPrice(orderItem.getPayPrice(), level.getDiscountPercent());  
53 - if (vipPrice <= 0) {  
54 - return;  
55 - }  
56 -  
57 - // 2.2 记录优惠明细  
58 - if (orderItem.getSelected()) {  
59 - // 注意,只有在选中的情况下,才会记录到优惠明细。否则仅仅是更新 SKU 优惠金额,用于展示  
60 - TradePriceCalculatorHelper.addPromotion(result, orderItem,  
61 - level.getId(), level.getName(), PromotionTypeEnum.MEMBER_LEVEL.getType(),  
62 - String.format("会员等级折扣:省 %s 元", formatPrice(vipPrice)),  
63 - vipPrice);  
64 - }  
65 -  
66 - // 2.3 更新 SKU 的优惠金额  
67 - orderItem.setVipPrice(vipPrice);  
68 - TradePriceCalculatorHelper.recountPayPrice(orderItem);  
69 - });  
70 - TradePriceCalculatorHelper.recountAllPrice(result); 40 +// // 0. 只有【普通】订单,才计算该优惠
  41 +// if (ObjectUtil.notEqual(result.getType(), TradeOrderTypeEnum.NORMAL.getType())) {
  42 +// return;
  43 +// }
  44 +// // 1. 获得用户的会员等级
  45 +// MemberUserRespDTO user = memberUserApi.getUser(param.getUserId());
  46 +// if (user.getLevelId() == null || user.getLevelId() <= 0) {
  47 +// return;
  48 +// }
  49 +// MemberLevelRespDTO level = memberLevelApi.getMemberLevel(user.getLevelId());
  50 +// if (level == null || level.getDiscountPercent() == null) {
  51 +// return;
  52 +// }
  53 +//
  54 +// // 2. 计算每个 SKU 的优惠金额
  55 +// result.getItems().forEach(orderItem -> {
  56 +// // 2.1 计算优惠金额
  57 +// Integer vipPrice = calculateVipPrice(orderItem.getPayPrice(), level.getDiscountPercent());
  58 +// if (vipPrice <= 0) {
  59 +// return;
  60 +// }
  61 +//
  62 +// // 2.2 记录优惠明细
  63 +// if (orderItem.getSelected()) {
  64 +// // 注意,只有在选中的情况下,才会记录到优惠明细。否则仅仅是更新 SKU 优惠金额,用于展示
  65 +// TradePriceCalculatorHelper.addPromotion(result, orderItem,
  66 +// level.getId(), level.getName(), PromotionTypeEnum.MEMBER_LEVEL.getType(),
  67 +// String.format("会员等级折扣:省 %s 元", formatPrice(vipPrice)),
  68 +// vipPrice);
  69 +// }
  70 +//
  71 +// // 2.3 更新 SKU 的优惠金额
  72 +// orderItem.setVipPrice(vipPrice);
  73 +// TradePriceCalculatorHelper.recountPayPrice(orderItem);
  74 +// });
  75 +// TradePriceCalculatorHelper.recountAllPrice(result);
71 } 76 }
72 77
73 /** 78 /**
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeRewardActivityPriceCalculator.java
@@ -23,7 +23,10 @@ import java.util.Comparator; @@ -23,7 +23,10 @@ import java.util.Comparator;
23 import java.util.List; 23 import java.util.List;
24 import java.util.Map; 24 import java.util.Map;
25 25
  26 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  27 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
26 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; 28 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;
  29 +import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.REWARD_ACTIVITY_TYPE_NOT_EXISTS;
27 import static cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculatorHelper.formatPrice; 30 import static cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculatorHelper.formatPrice;
28 31
29 // TODO @puhui999:相关的单测,建议改一改 32 // TODO @puhui999:相关的单测,建议改一改
@@ -47,14 +50,15 @@ public class TradeRewardActivityPriceCalculator implements TradePriceCalculator @@ -47,14 +50,15 @@ public class TradeRewardActivityPriceCalculator implements TradePriceCalculator
47 return; 50 return;
48 } 51 }
49 // 获得 SKU 对应的满减送活动 52 // 获得 SKU 对应的满减送活动
50 - List<RewardActivityMatchRespDTO> rewardActivities = rewardActivityApi.getRewardActivityListByStatusAndNow(  
51 - CommonStatusEnum.ENABLE.getStatus(), LocalDateTime.now()); 53 + List<RewardActivityMatchRespDTO> rewardActivities = rewardActivityApi.getRewardActivityBySpuIdsAndStatusAndDateTimeLt(
  54 + convertSet(result.getItems(), TradePriceCalculateRespBO.OrderItem::getSpuId), CommonStatusEnum.ENABLE.getStatus(), LocalDateTime.now());
52 if (CollUtil.isEmpty(rewardActivities)) { 55 if (CollUtil.isEmpty(rewardActivities)) {
53 return; 56 return;
54 } 57 }
55 -  
56 - // 处理每个满减送活动  
57 - rewardActivities.forEach(rewardActivity -> calculate(param, result, rewardActivity)); 58 + // 处理最新的满减送活动
  59 + if(!rewardActivities.isEmpty()){
  60 + calculate(param, result, rewardActivities.get(0));
  61 + }
58 } 62 }
59 63
60 private void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result, 64 private void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result,
@@ -77,6 +81,8 @@ public class TradeRewardActivityPriceCalculator implements TradePriceCalculator @@ -77,6 +81,8 @@ public class TradeRewardActivityPriceCalculator implements TradePriceCalculator
77 Integer newDiscountPrice = rule.getDiscountPrice(); 81 Integer newDiscountPrice = rule.getDiscountPrice();
78 // 2.2 计算分摊的优惠金额 82 // 2.2 计算分摊的优惠金额
79 List<Integer> divideDiscountPrices = TradePriceCalculatorHelper.dividePrice(orderItems, newDiscountPrice); 83 List<Integer> divideDiscountPrices = TradePriceCalculatorHelper.dividePrice(orderItems, newDiscountPrice);
  84 + //计算是否包邮
  85 + result.setFreeDelivery(rule.getFreeDelivery());
80 86
81 // 3.1 记录使用的优惠劵 87 // 3.1 记录使用的优惠劵
82 result.setCouponId(param.getCouponId()); 88 result.setCouponId(param.getCouponId());
@@ -119,27 +125,24 @@ public class TradeRewardActivityPriceCalculator implements TradePriceCalculator @@ -119,27 +125,24 @@ public class TradeRewardActivityPriceCalculator implements TradePriceCalculator
119 /** 125 /**
120 * 获得满减送的订单项(商品)列表 126 * 获得满减送的订单项(商品)列表
121 * 127 *
122 - * @param result 计算结果 128 + * @param result 计算结果
123 * @param rewardActivity 满减送活动 129 * @param rewardActivity 满减送活动
124 * @return 订单项(商品)列表 130 * @return 订单项(商品)列表
125 */ 131 */
126 private List<TradePriceCalculateRespBO.OrderItem> filterMatchActivityOrderItems(TradePriceCalculateRespBO result, 132 private List<TradePriceCalculateRespBO.OrderItem> filterMatchActivityOrderItems(TradePriceCalculateRespBO result,
127 RewardActivityMatchRespDTO rewardActivity) { 133 RewardActivityMatchRespDTO rewardActivity) {
128 - // 情况一:全部商品都可以参与  
129 - if (PromotionProductScopeEnum.isAll(rewardActivity.getProductScope())) { 134 + Integer productScope = rewardActivity.getProductScope();
  135 + if(PromotionProductScopeEnum.isAll(productScope)){
130 return result.getItems(); 136 return result.getItems();
131 - }  
132 - // 情况二:指定商品参与  
133 - if (PromotionProductScopeEnum.isSpu(rewardActivity.getProductScope())) { 137 + }else if (PromotionProductScopeEnum.isSpu(productScope)) {
134 return filterList(result.getItems(), 138 return filterList(result.getItems(),
135 orderItem -> CollUtil.contains(rewardActivity.getProductScopeValues(), orderItem.getSpuId())); 139 orderItem -> CollUtil.contains(rewardActivity.getProductScopeValues(), orderItem.getSpuId()));
136 - }  
137 - // 情况三:指定商品类型参与  
138 - if (PromotionProductScopeEnum.isCategory(rewardActivity.getProductScope())) { 140 + }else if (PromotionProductScopeEnum.isCategory(productScope)) {
139 return filterList(result.getItems(), 141 return filterList(result.getItems(),
140 orderItem -> CollUtil.contains(rewardActivity.getProductScopeValues(), orderItem.getCategoryId())); 142 orderItem -> CollUtil.contains(rewardActivity.getProductScopeValues(), orderItem.getCategoryId()));
  143 + }else{
  144 + throw exception(REWARD_ACTIVITY_TYPE_NOT_EXISTS);
141 } 145 }
142 - return List.of();  
143 } 146 }
144 147
145 /** 148 /**
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java
@@ -11,6 +11,7 @@ import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; @@ -11,6 +11,7 @@ import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
11 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 11 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
12 import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; 12 import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
13 import cn.iocoder.yudao.framework.datapermission.core.util.DataPermissionUtils; 13 import cn.iocoder.yudao.framework.datapermission.core.util.DataPermissionUtils;
  14 +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
14 import cn.iocoder.yudao.module.infra.api.config.ConfigApi; 15 import cn.iocoder.yudao.module.infra.api.config.ConfigApi;
15 import cn.iocoder.yudao.module.infra.api.file.FileApi; 16 import cn.iocoder.yudao.module.infra.api.file.FileApi;
16 import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthRegisterReqVO; 17 import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthRegisterReqVO;
@@ -104,6 +105,7 @@ public class AdminUserServiceImpl implements AdminUserService { @@ -104,6 +105,7 @@ public class AdminUserServiceImpl implements AdminUserService {
104 AdminUserDO user = BeanUtils.toBean(createReqVO, AdminUserDO.class); 105 AdminUserDO user = BeanUtils.toBean(createReqVO, AdminUserDO.class);
105 user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启 106 user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启
106 user.setPassword(encodePassword(createReqVO.getPassword())); // 加密密码 107 user.setPassword(encodePassword(createReqVO.getPassword())); // 加密密码
  108 + user.setTenantId(TenantContextHolder.getRequiredTenantId());
107 userMapper.insert(user); 109 userMapper.insert(user);
108 // 2.2 插入关联岗位 110 // 2.2 插入关联岗位
109 if (CollectionUtil.isNotEmpty(user.getPostIds())) { 111 if (CollectionUtil.isNotEmpty(user.getPostIds())) {
yudao-server/src/main/resources/application-local.yaml
@@ -6,7 +6,7 @@ spring: @@ -6,7 +6,7 @@ spring:
6 # 数据源配置项 6 # 数据源配置项
7 autoconfigure: 7 autoconfigure:
8 exclude: 8 exclude:
9 - - org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration # 默认 local 环境,不开启 Quartz 的自动配置 9 + #- org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration # 默认 local 环境,不开启 Quartz 的自动配置
10 - de.codecentric.boot.admin.server.config.AdminServerAutoConfiguration # 禁用 Spring Boot Admin 的 Server 的自动配置 10 - de.codecentric.boot.admin.server.config.AdminServerAutoConfiguration # 禁用 Spring Boot Admin 的 Server 的自动配置
11 - de.codecentric.boot.admin.server.ui.config.AdminServerUiAutoConfiguration # 禁用 Spring Boot Admin 的 Server UI 的自动配置 11 - de.codecentric.boot.admin.server.ui.config.AdminServerUiAutoConfiguration # 禁用 Spring Boot Admin 的 Server UI 的自动配置
12 - de.codecentric.boot.admin.client.config.SpringBootAdminClientAutoConfiguration # 禁用 Spring Boot Admin 的 Client 的自动配置 12 - de.codecentric.boot.admin.client.config.SpringBootAdminClientAutoConfiguration # 禁用 Spring Boot Admin 的 Client 的自动配置
@@ -45,7 +45,7 @@ spring: @@ -45,7 +45,7 @@ spring:
45 primary: master 45 primary: master
46 datasource: 46 datasource:
47 master: 47 master:
48 - url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 48 + url: jdbc:mysql://192.168.10.207:3306/specialty?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例
49 # url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true # MySQL Connector/J 5.X 连接的示例 49 # url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true # MySQL Connector/J 5.X 连接的示例
50 # url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例 50 # url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例
51 # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例 51 # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例
@@ -61,19 +61,19 @@ spring: @@ -61,19 +61,19 @@ spring:
61 # password: SYSDBA001 # DM 连接的示例 61 # password: SYSDBA001 # DM 连接的示例
62 # username: root # OpenGauss 连接的示例 62 # username: root # OpenGauss 连接的示例
63 # password: Yudao@2024 # OpenGauss 连接的示例 63 # password: Yudao@2024 # OpenGauss 连接的示例
64 - slave: # 模拟从库,可根据自己需要修改  
65 - lazy: true # 开启懒加载,保证启动速度  
66 - url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true&nullCatalogMeansCurrent=true  
67 - username: root  
68 - password: 123456 64 +# slave: # 模拟从库,可根据自己需要修改
  65 +# lazy: true # 开启懒加载,保证启动速度
  66 +# url: jdbc:mysql://192.168.10.207:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true&nullCatalogMeansCurrent=true
  67 +# username: root
  68 +# password: 123456
69 69
70 # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 70 # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
71 data: 71 data:
72 redis: 72 redis:
73 - host: 127.0.0.1 # 地址 73 + host: 192.168.10.207 # 地址
74 port: 6379 # 端口 74 port: 6379 # 端口
75 database: 0 # 数据库索引 75 database: 0 # 数据库索引
76 -# password: dev # 密码,建议生产环境开启 76 + password: 123456 # 密码,建议生产环境开启
77 77
78 --- #################### 定时任务相关配置 #################### 78 --- #################### 定时任务相关配置 ####################
79 79
@@ -200,8 +200,8 @@ wx: @@ -200,8 +200,8 @@ wx:
200 # secret: 6f270509224a7ae1296bbf1c8cb97aed 200 # secret: 6f270509224a7ae1296bbf1c8cb97aed
201 # appid: wxc4598c446f8a9cb3 # 测试号(Kongdy 提供的) 201 # appid: wxc4598c446f8a9cb3 # 测试号(Kongdy 提供的)
202 # secret: 4a1a04e07f6a4a0751b39c3064a92c8b 202 # secret: 4a1a04e07f6a4a0751b39c3064a92c8b
203 - appid: wx66186af0759f47c9 # 测试号(puhui 提供的)  
204 - secret: 3218bcbd112cbc614c7264ceb20144ac 203 + appid: wx9a0a5b259d852380 # 测试号(puhui 提供的)
  204 + secret: 70e65fa9d1a4f2c4e1b2aa8751d3b75e
205 config-storage: 205 config-storage:
206 type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取 206 type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取
207 key-prefix: wa # Redis Key 的前缀 207 key-prefix: wa # Redis Key 的前缀