Bläddra i källkod

!1084 批量优化或修改BUG
Merge pull request !1084 from 痴货/develop-mall

芋道源码 7 månader sedan
förälder
incheckning
241957507c
42 ändrade filer med 746 tillägg och 165 borttagningar
  1. 5 5
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.java
  2. 2 2
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppProductSpuRespVO.java
  3. 9 0
      yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/discount/dto/DiscountProductRespDTO.java
  4. 10 0
      yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/reward/RewardActivityApi.java
  5. 2 0
      yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java
  6. 1 1
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/discount/DiscountActivityApiImpl.java
  7. 8 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/reward/RewardActivityApiImpl.java
  8. 58 2
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplateBaseVO.java
  9. 22 25
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/activity/AppActivityController.java
  10. 2 2
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/coupon/AppCouponController.java
  11. 8 1
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/reward/vo/AppRewardActivityRespVO.java
  12. 2 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/coupon/CouponConvert.java
  13. 4 1
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/discount/DiscountActivityConvert.java
  14. 33 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/reward/RewardActivityConvert.java
  15. 4 11
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponMapper.java
  16. 1 1
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponTemplateMapper.java
  17. 2 1
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/discount/DiscountProductMapper.java
  18. 43 2
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/reward/RewardActivityMapper.java
  19. 4 2
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillactivity/SeckillActivityMapper.java
  20. 4 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillconfig/SeckillConfigMapper.java
  21. 2 1
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponService.java
  22. 42 7
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java
  23. 5 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponTemplateServiceImpl.java
  24. 2 1
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountActivityService.java
  25. 3 2
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountActivityServiceImpl.java
  26. 11 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/reward/RewardActivityService.java
  27. 28 3
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/reward/RewardActivityServiceImpl.java
  28. 1 1
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityService.java
  29. 4 1
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityServiceImpl.java
  30. 6 4
      yudao-module-mall/yudao-module-promotion-biz/src/main/resources/mapper/discount/DiscountProductMapper.xml
  31. 155 2
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java
  32. 8 0
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementRespVO.java
  33. 63 0
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeProductSettlementRespVO.java
  34. 2 4
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/aftersale/AfterSaleConvert.java
  35. 2 1
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/AfterSaleServiceImpl.java
  36. 2 1
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateRespBO.java
  37. 5 2
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDeliveryPriceCalculator.java
  38. 109 17
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDiscountActivityPriceCalculator.java
  39. 41 36
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeMemberLevelPriceCalculator.java
  40. 18 15
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeRewardActivityPriceCalculator.java
  41. 2 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java
  42. 11 11
      yudao-server/src/main/resources/application-local.yaml

+ 5 - 5
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 {
         list.forEach(spu -> spu.setSalesCount(spu.getSalesCount() + spu.getVirtualSalesCount()));
         List<AppProductSpuRespVO> voList = BeanUtils.toBean(list, AppProductSpuRespVO.class);
         // 处理 vip 价格
-        MemberLevelRespDTO memberLevel = getMemberLevel();
-        voList.forEach(vo -> vo.setVipPrice(calculateVipPrice(vo.getPrice(), memberLevel)));
+//        MemberLevelRespDTO memberLevel = getMemberLevel();
+//        voList.forEach(vo -> vo.setVipPrice(calculateVipPrice(vo.getPrice(), memberLevel)));
         return success(voList);
     }
 
@@ -86,8 +86,8 @@ public class AppProductSpuController {
         pageResult.getList().forEach(spu -> spu.setSalesCount(spu.getSalesCount() + spu.getVirtualSalesCount()));
         PageResult<AppProductSpuRespVO> voPageResult = BeanUtils.toBean(pageResult, AppProductSpuRespVO.class);
         // 处理 vip 价格
-        MemberLevelRespDTO memberLevel = getMemberLevel();
-        voPageResult.getList().forEach(vo -> vo.setVipPrice(calculateVipPrice(vo.getPrice(), memberLevel)));
+//        MemberLevelRespDTO memberLevel = getMemberLevel();
+//        voPageResult.getList().forEach(vo -> vo.setVipPrice(calculateVipPrice(vo.getPrice(), memberLevel)));
         return success(voPageResult);
     }
 
@@ -142,7 +142,7 @@ public class AppProductSpuController {
      */
     public Integer calculateVipPrice(Integer price, MemberLevelRespDTO memberLevel) {
         if (memberLevel == null || memberLevel.getDiscountPercent() == null) {
-            return 0;
+            return null;
         }
         Integer newPrice = price * memberLevel.getDiscountPercent() / 100;
         return price - newPrice;

+ 2 - 2
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 {
     @Schema(description = "市场价,单位使用:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
     private Integer marketPrice;
 
-    @Schema(description = "VIP 价格,单位使用:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "968") // 通过会员等级,计算出折扣后价格
-    private Integer vipPrice;
+//    @Schema(description = "VIP 价格,单位使用:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "968") // 通过会员等级,计算出折扣后价格
+//    private Integer vipPrice;
 
     @Schema(description = "库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "666")
     private Integer stock;

+ 9 - 0
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;
 
 import lombok.Data;
 
+import java.time.LocalDateTime;
+
 /**
  * 限时折扣活动商品 Response DTO
  *
@@ -44,5 +46,12 @@ public class DiscountProductRespDTO {
      * 活动标题
      */
     private String activityName;
+    /**
+     * 活动结束时间点
+     *
+     * 冗余 {@link DiscountActivityDO#getEndTime()}
+     */
+    private LocalDateTime activityEndTime;
+
 
 }

+ 10 - 0
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;
 import cn.iocoder.yudao.module.promotion.api.reward.dto.RewardActivityMatchRespDTO;
 
 import java.time.LocalDateTime;
+import java.util.Collection;
 import java.util.List;
 
 /**
@@ -21,4 +22,13 @@ public interface RewardActivityApi {
      */
     List<RewardActivityMatchRespDTO> getRewardActivityListByStatusAndNow(Integer status, LocalDateTime dateTime);
 
+    /**
+     * 获取指定 spu 编号最近参加的活动,每个 spuId 只返回一条记录
+     *
+     * @param spuIds   spu 编号
+     * @param status   状态
+     * @param dateTime 当前日期时间
+     * @return 满减送活动列表
+     */
+    List<RewardActivityMatchRespDTO> getRewardActivityBySpuIdsAndStatusAndDateTimeLt(Collection<Long> spuIds, Integer status, LocalDateTime dateTime);
 }

+ 2 - 0
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 {
     ErrorCode DISCOUNT_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_001_002, "限时折扣活动已关闭,不能修改");
     ErrorCode DISCOUNT_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED = new ErrorCode(1_013_001_003, "限时折扣活动未关闭,不能删除");
     ErrorCode DISCOUNT_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_001_004, "限时折扣活动已关闭,不能重复关闭");
+    ErrorCode DISCOUNT_ACTIVITY_TYPE_NOT_EXISTS = new ErrorCode(1_013_001_005, "限时折扣活动类型不存在");
 
     // ========== Banner 相关 1-013-002-000 ============
     ErrorCode BANNER_NOT_EXISTS = new ErrorCode(1_013_002_000, "Banner 不存在");
@@ -45,6 +46,7 @@ public interface ErrorCodeConstants {
     ErrorCode REWARD_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED = new ErrorCode(1_013_006_003, "满减送活动未关闭,不能删除");
     ErrorCode REWARD_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_006_004, "满减送活动已关闭,不能重复关闭");
     ErrorCode REWARD_ACTIVITY_SCOPE_EXISTS = new ErrorCode(1_013_006_005, "与该时间段已存在的满减送活动商品范围冲突");
+    ErrorCode REWARD_ACTIVITY_TYPE_NOT_EXISTS = new ErrorCode(1_013_006_006, "满减送活动类型不存在");
 
     // ========== TODO 空着 1-013-007-000 ============
 

+ 1 - 1
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 {
 
     @Override
     public List<DiscountProductRespDTO> getMatchDiscountProductList(Collection<Long> skuIds) {
-        return DiscountActivityConvert.INSTANCE.convertList02(discountActivityService.getMatchDiscountProductList(skuIds));
+        return discountActivityService.getMatchDiscountProductList(skuIds);
     }
 
 }

+ 8 - 0
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;
 
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.module.promotion.api.reward.dto.RewardActivityMatchRespDTO;
+import cn.iocoder.yudao.module.promotion.convert.reward.RewardActivityConvert;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;
 import cn.iocoder.yudao.module.promotion.service.reward.RewardActivityService;
 import jakarta.annotation.Resource;
@@ -9,6 +10,7 @@ import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 
 import java.time.LocalDateTime;
+import java.util.Collection;
 import java.util.List;
 
 /**
@@ -29,4 +31,10 @@ public class RewardActivityApiImpl implements RewardActivityApi {
         return BeanUtils.toBean(list, RewardActivityMatchRespDTO.class);
     }
 
+    @Override
+    public List<RewardActivityMatchRespDTO> getRewardActivityBySpuIdsAndStatusAndDateTimeLt(Collection<Long> spuIds, Integer status, LocalDateTime dateTime) {
+        List<RewardActivityDO> rewardActivityBySpuIdsAndStatusAndDateTimeLt = rewardActivityService.getRewardActivityBySpuIdsAndStatusAndDateTimeLt(spuIds, status, dateTime);
+        return RewardActivityConvert.INSTANCE.convertList(rewardActivityBySpuIdsAndStatusAndDateTimeLt);
+    }
+
 }

+ 58 - 2
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;
 
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.ObjectUtil;
+import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
 import cn.iocoder.yudao.framework.common.validation.InEnum;
 import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;
 import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
+import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTemplateValidityTypeEnum;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.annotation.JsonIgnore;
@@ -12,6 +14,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 import org.springframework.format.annotation.DateTimeFormat;
 
+import jakarta.validation.Validator;
 import jakarta.validation.constraints.AssertTrue;
 import jakarta.validation.constraints.Min;
 import jakarta.validation.constraints.NotNull;
@@ -37,11 +40,11 @@ public class CouponTemplateBaseVO {
     private String description;
 
     @Schema(description = "发行总量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") // -1 - 则表示不限制发放数量
-    @NotNull(message = "发行总量不能为空")
+    @NotNull(message = "发行总量不能为空", groups = {User.class})
     private Integer totalCount;
 
     @Schema(description = "每人限领个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "66") // -1 - 则表示不限制
-    @NotNull(message = "每人限领个数不能为空")
+    @NotNull(message = "每人限领个数不能为空", groups = {User.class})
     private Integer takeLimitCount;
 
     @Schema(description = "领取方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@@ -89,13 +92,16 @@ public class CouponTemplateBaseVO {
     private Integer discountType;
 
     @Schema(description = "折扣百分比", example = "80") //  例如说,80% 为 80
+    @NotNull(message = "折扣百分比不能为空", groups = {Percent.class})
     private Integer discountPercent;
 
     @Schema(description = "优惠金额", example = "10")
     @Min(value = 0, message = "优惠金额需要大于等于 0")
+    @NotNull(message = "优惠金额不能为空", groups = {Price.class})
     private Integer discountPrice;
 
     @Schema(description = "折扣上限", example = "100") // 单位:分,仅在 discountType 为 PERCENT 使用
+    @NotNull(message = "折扣上限不能为空", groups = {Percent.class})
     private Integer discountLimitPrice;
 
     @AssertTrue(message = "商品范围编号的数组不能为空")
@@ -154,4 +160,54 @@ public class CouponTemplateBaseVO {
                 || discountLimitPrice != null;
     }
 
+    //-------------------------领取方式校验start----------------------------
+
+    /**
+     * 直接领取
+     */
+    public interface User {
+    }
+
+    /**
+     * 指定发放
+     */
+    public interface Admin {
+    }
+
+    //-------------------------领取方式校验end------------------------------
+
+    //-------------------------优惠类型校验start----------------------------
+
+    /**
+     * 满减
+     */
+    public interface Price {
+    }
+
+    /**
+     * 折扣
+     */
+    public interface Percent {
+    }
+
+    //-------------------------优惠类型校验end------------------------------
+
+    public void validate(Validator validator) {
+
+        //领取方式校验
+        if (CouponTakeTypeEnum.USER.getType().equals(takeType)) {
+            ValidationUtils.validate(validator, this, User.class);
+        } else if (CouponTakeTypeEnum.ADMIN.getType().equals(takeType)) {
+            ValidationUtils.validate(validator, this, Admin.class);
+        }
+
+        //优惠类型校验
+        if (PromotionDiscountTypeEnum.PRICE.getType().equals(discountType)){
+            ValidationUtils.validate(validator, this, Price.class);
+        } else if (PromotionDiscountTypeEnum.PERCENT.getType().equals(discountType)) {
+            ValidationUtils.validate(validator, this, Percent.class);
+        }
+
+    }
+
 }

+ 22 - 25
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;
 
 import java.time.LocalDateTime;
 import java.util.*;
+import java.util.stream.Collectors;
 
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
@@ -150,36 +151,32 @@ public class AppActivityController {
     }
 
     private void getRewardActivityList(Collection<Long> spuIds, LocalDateTime now, List<AppActivityRespVO> activityList) {
-        // 1.1 获得所有的活动
-        List<RewardActivityDO> rewardActivityList = rewardActivityService.getRewardActivityListByStatusAndDateTimeLt(
-                CommonStatusEnum.ENABLE.getStatus(), now);
+        // TODO @puhui999:有 3 范围,不只 spuId,还有 categoryId,全部,下次 fix
+        List<RewardActivityDO> rewardActivityList = rewardActivityService.getRewardActivityBySpuIdsAndStatusAndDateTimeLt(
+                spuIds, CommonStatusEnum.ENABLE.getStatus(), now);
         if (CollUtil.isEmpty(rewardActivityList)) {
             return;
         }
-        // 1.2 获得所有的商品信息
-        List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList(spuIds);
-        if (CollUtil.isEmpty(spuList)) {
-            return;
-        }
 
-        // 2. 构建活动
-        for (RewardActivityDO rewardActivity : rewardActivityList) {
-            // 情况一:所有商品都能参加
-            if (PromotionProductScopeEnum.isAll(rewardActivity.getProductScope())) {
-                buildAppActivityRespVO(rewardActivity, spuIds, activityList);
-            }
-            // 情况二:指定商品参加
-            if (PromotionProductScopeEnum.isSpu(rewardActivity.getProductScope())) {
-                List<Long> fSpuIds = spuList.stream().map(ProductSpuRespDTO::getId).filter(id ->
-                        rewardActivity.getProductScopeValues().contains(id)).toList();
-                buildAppActivityRespVO(rewardActivity, fSpuIds, activityList);
-            }
-            // 情况三:指定商品类型参加
-            if (PromotionProductScopeEnum.isCategory(rewardActivity.getProductScope())) {
-                List<Long> fSpuIds = spuList.stream().filter(spuItem -> rewardActivity.getProductScopeValues()
-                        .contains(spuItem.getCategoryId())).map(ProductSpuRespDTO::getId).toList();
-                buildAppActivityRespVO(rewardActivity, fSpuIds, activityList);
+        Map<Long, Optional<RewardActivityDO>> spuIdAndActivityMap = spuIds.stream()
+                .collect(Collectors.toMap(
+                        spuId -> spuId,
+                        spuId -> rewardActivityList.stream()
+                                .filter(activity ->
+                                        ( activity.getProductScopeValues()!=null &&
+                                                (activity.getProductScopeValues().contains(spuId) ||
+                                                        activity.getProductScopeValues().contains(productSpuApi.getSpu(spuId).getCategoryId()))) ||
+                                                activity.getProductScope()==1
+                                )
+                                .max(Comparator.comparing(RewardActivityDO::getCreateTime))));
+        for (Long supId : spuIdAndActivityMap.keySet()) {
+            if (spuIdAndActivityMap.get(supId).isEmpty()) {
+                continue;
             }
+
+            RewardActivityDO rewardActivityDO = spuIdAndActivityMap.get(supId).get();
+            activityList.add(new AppActivityRespVO(rewardActivityDO.getId(), PromotionTypeEnum.REWARD_ACTIVITY.getType(),
+                    rewardActivityDO.getName(), supId, rewardActivityDO.getStartTime(), rewardActivityDO.getEndTime()));
         }
     }
 

+ 2 - 2
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 {
     @Operation(summary = "获得匹配指定商品的优惠劵列表", description = "用于下单页,展示优惠劵列表")
     public CommonResult<List<AppCouponMatchRespVO>> getMatchCouponList(AppCouponMatchReqVO matchReqVO) {
         // todo: 优化:优惠金额倒序
-        List<CouponDO> list = couponService.getMatchCouponList(getLoginUserId(), matchReqVO);
-        return success(BeanUtils.toBean(list, AppCouponMatchRespVO.class));
+        List<AppCouponMatchRespVO> list = couponService.getMatchCouponList(getLoginUserId(), matchReqVO);
+        return success(list);
     }
 
     @GetMapping("/page")

+ 8 - 1
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
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 
+import java.time.LocalDateTime;
 import java.util.List;
 
 @Schema(description = "用户 App - 满减送活动 Response VO")
@@ -26,9 +27,15 @@ public class AppRewardActivityRespVO {
     private Integer productScope;
 
     @Schema(description = "商品 SPU 编号的数组", example = "1,2,3")
-    private List<Long> productSpuIds;
+    private List<Long> productScopeValues;
 
     @Schema(description = "优惠规则的数组")
     private List<RewardActivityBaseVO.Rule> rules;
 
+    @Schema(description = "开始时间")
+    private LocalDateTime startTime;
+
+    @Schema(description = "结束时间")
+    private LocalDateTime endTime;
+
 }

+ 2 - 0
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 {
 
     CouponRespDTO convert(CouponDO bean);
 
+    AppCouponMatchRespVO convert2(CouponDO bean);
+
     default CouponDO convert(CouponTemplateDO template, Long userId) {
         CouponDO couponDO = new CouponDO()
                 .setTemplateId(template.getId())

+ 4 - 1
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 {
     default boolean isEquals(DiscountProductDO productDO, DiscountProductDO productVO) {
         if (ObjectUtil.notEqual(productDO.getSpuId(), productVO.getSpuId())
                 || ObjectUtil.notEqual(productDO.getSkuId(), productVO.getSkuId())
-                || ObjectUtil.notEqual(productDO.getDiscountType(), productVO.getDiscountType())) {
+                || ObjectUtil.notEqual(productDO.getDiscountType(), productVO.getDiscountType())
+                || ObjectUtil.notEqual(productDO.getActivityEndTime(), productVO.getActivityEndTime())
+                || ObjectUtil.notEqual(productDO.getActivityStartTime(), productVO.getActivityStartTime())
+                || ObjectUtil.notEqual(productDO.getActivityStatus(), productVO.getActivityStatus())) {
             return false;
         }
         if (productDO.getDiscountType().equals(PromotionDiscountTypeEnum.PRICE.getType())) {

+ 33 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/reward/RewardActivityConvert.java

@@ -0,0 +1,33 @@
+package cn.iocoder.yudao.module.promotion.convert.reward;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.promotion.api.reward.dto.RewardActivityMatchRespDTO;
+import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityCreateReqVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityRespVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityUpdateReqVO;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+
+/**
+ * 满减送活动 Convert
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface RewardActivityConvert {
+
+    RewardActivityConvert INSTANCE = Mappers.getMapper(RewardActivityConvert.class);
+
+    RewardActivityDO convert(RewardActivityCreateReqVO bean);
+
+    RewardActivityDO convert(RewardActivityUpdateReqVO bean);
+
+    RewardActivityRespVO convert(RewardActivityDO bean);
+
+    PageResult<RewardActivityRespVO> convertPage(PageResult<RewardActivityDO> page);
+
+    List<RewardActivityMatchRespDTO> convertList(List<RewardActivityDO> rewardActivityBySpuIdsAndStatusAndDateTimeLt);
+}

+ 4 - 11
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<CouponDO> {
     }
 
     default List<CouponDO> selectListByUserIdAndStatusAndUsePriceLeAndProductScope(
-            Long userId, Integer status, Integer usePrice, List<Long> spuIds, List<Long> categoryIds) {
-        Function<List<Long>, String> productScopeValuesFindInSetFunc = ids -> ids.stream()
-                .map(id -> StrUtil.format("FIND_IN_SET({}, product_scope_values) ", id))
-                .collect(Collectors.joining(" OR "));
-        return selectList(new LambdaQueryWrapperX<CouponDO>()
+            Long userId, Integer status) {
+        List<CouponDO> couponDOS = selectList(new LambdaQueryWrapperX<CouponDO>()
                 .eq(CouponDO::getUserId, userId)
                 .eq(CouponDO::getStatus, status)
-                .le(CouponDO::getUsePrice, usePrice) // 价格小于等于,满足价格使用条件
-                .and(w -> w.eq(CouponDO::getProductScope, PromotionProductScopeEnum.ALL.getScope()) // 商品范围一:全部
-                        .or(ww -> ww.eq(CouponDO::getProductScope, PromotionProductScopeEnum.SPU.getScope()) // 商品范围二:满足指定商品
-                                .apply(productScopeValuesFindInSetFunc.apply(spuIds)))
-                        .or(ww -> ww.eq(CouponDO::getProductScope, PromotionProductScopeEnum.CATEGORY.getScope()) // 商品范围三:满足指定分类
-                                .apply(productScopeValuesFindInSetFunc.apply(categoryIds)))));
+        );
+        return couponDOS;
     }
 
     default List<CouponDO> selectListByStatusAndValidEndTimeLe(Integer status, LocalDateTime validEndTime) {

+ 1 - 1
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<CouponTemplateDO> {
                             .in(CouponTemplateDO::getTakeType, canTakeTypes) // 2. 领取方式一致
                             .and(ww -> ww.gt(CouponTemplateDO::getValidEndTime, LocalDateTime.now())  // 3.1 未过期
                                     .or().eq(CouponTemplateDO::getValidityType, CouponTemplateValidityTypeEnum.TERM.getType())) // 3.2 领取之后
-                            .apply(" (take_count < total_count OR total_count = -1 )"); // 4. 剩余数量大于 0,或者无限领取
+                            .apply(" (take_count < total_count OR total_count = -1 or total_count is null)"); // 4. 剩余数量大于 0,或者无限领取,或者是指定发放的券
         }
         return canTakeConsumer;
     }

+ 2 - 1
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/discount/DiscountProductMapper.java

@@ -1,6 +1,7 @@
 package cn.iocoder.yudao.module.promotion.dal.mysql.discount;
 
 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.module.promotion.api.discount.dto.DiscountProductRespDTO;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import org.apache.ibatis.annotations.Mapper;
@@ -31,7 +32,7 @@ public interface DiscountProductMapper extends BaseMapperX<DiscountProductDO> {
     }
 
     // TODO @zhangshuai:逻辑里,尽量避免写 join 语句哈,你可以看看这个查询,有什么办法优化?目前的一个思路,是分 2 次查询,性能也是 ok 的
-    List<DiscountProductDO> getMatchDiscountProductList(@Param("skuIds") Collection<Long> skuIds);
+    List<DiscountProductRespDTO> getMatchDiscountProductList(@Param("skuIds") Collection<Long> skuIds);
 
     /**
      * 查询出指定 spuId 的 spu 参加的活动最接近现在的一条记录。多个的话,一个 spuId 对应一个最近的活动编号

+ 43 - 2
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/reward/RewardActivityMapper.java

@@ -1,14 +1,20 @@
 package cn.iocoder.yudao.module.promotion.dal.mysql.reward;
 
+import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
 import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityPageReqVO;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;
+import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import org.apache.ibatis.annotations.Mapper;
 
 import java.time.LocalDateTime;
+import java.util.Collection;
 import java.util.List;
+import java.util.function.Function;
+import java.util.stream.Collectors;
 
 /**
  * 满减送活动 Mapper
@@ -25,13 +31,48 @@ public interface RewardActivityMapper extends BaseMapperX<RewardActivityDO> {
                 .orderByDesc(RewardActivityDO::getId));
     }
 
+    default List<RewardActivityDO> selectListBySpuIdsAndStatus(Collection<Long> spuIds, Integer status) {
+        Function<Collection<Long>, String> productScopeValuesFindInSetFunc = ids -> ids.stream()
+                .map(id -> StrUtil.format("FIND_IN_SET({}, product_scope_values) ", id))
+                .collect(Collectors.joining(" OR "));
+        return selectList(new QueryWrapper<RewardActivityDO>()
+                .eq("status", status)
+                .apply(productScopeValuesFindInSetFunc.apply(spuIds)));
+    }
+
+    /**
+     * 获取指定活动编号的活动列表且
+     * 开始时间和结束时间小于给定时间 dateTime 的活动列表
+     *
+     * @param status   状态
+     * @param dateTime 指定日期
+     * @return 活动列表
+     */
     default List<RewardActivityDO> selectListByStatusAndDateTimeLt(Integer status, LocalDateTime dateTime) {
         return selectList(new LambdaQueryWrapperX<RewardActivityDO>()
                 .eq(RewardActivityDO::getStatus, status)
-                // 开始时间 < 指定时间(dateTime) < 结束时间,也就是说获取指定时间段的活动
-                .lt(RewardActivityDO::getStartTime, dateTime).gt(RewardActivityDO::getEndTime, dateTime)
+                .lt(RewardActivityDO::getStartTime, dateTime)
+                .gt(RewardActivityDO::getEndTime, dateTime)// 开始时间 < 指定时间 < 结束时间,也就是说获取指定时间段的活动
                 .orderByAsc(RewardActivityDO::getStartTime)
         );
     }
 
+    default List<RewardActivityDO> getRewardActivityByStatusAndDateTimeLt(Collection<Long> spuIds,Collection<Long> categoryIds, Integer status, LocalDateTime dateTime) {
+        //拼接通用券查询语句
+        Function<Collection<Long>, String> productScopeValuesFindInSetFunc = ids -> ids.stream()
+                .map(id -> StrUtil.format("FIND_IN_SET({}, product_scope_values) ", id))
+                .collect(Collectors.joining(" OR "));
+        return selectList(new LambdaQueryWrapperX<RewardActivityDO>()
+                .eq(RewardActivityDO::getStatus,status)
+                .lt(RewardActivityDO::getStartTime, dateTime)
+                .gt(RewardActivityDO::getEndTime, dateTime)
+                .and(i -> i.eq(RewardActivityDO::getProductScope, PromotionProductScopeEnum.SPU.getScope())
+                        .and(i1 -> i1.apply(productScopeValuesFindInSetFunc.apply(spuIds)))
+                        .or(i1 -> i1.eq(RewardActivityDO::getProductScope, PromotionProductScopeEnum.ALL.getScope()))
+                        .or(i1 -> i1.eq(RewardActivityDO::getProductScope, PromotionProductScopeEnum.CATEGORY.getScope())
+                                .and(i2 -> i2.apply(productScopeValuesFindInSetFunc.apply(categoryIds)))))
+                .orderByDesc(RewardActivityDO::getId)
+        );
+    }
+
 }

+ 4 - 2
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<SeckillActivityDO> {
         Assert.isTrue(count > 0);
         return update(null, new LambdaUpdateWrapper<SeckillActivityDO>()
                 .eq(SeckillActivityDO::getId, id)
-                .gt(SeckillActivityDO::getStock, count)
+                .ge(SeckillActivityDO::getStock, count)
                 .setSql("stock = stock - " + count));
     }
 
@@ -69,9 +69,11 @@ public interface SeckillActivityMapper extends BaseMapperX<SeckillActivityDO> {
                 .setSql("stock = stock + " + count));
     }
 
-    default PageResult<SeckillActivityDO> selectPage(AppSeckillActivityPageReqVO pageReqVO, Integer status) {
+    default PageResult<SeckillActivityDO> selectPage(AppSeckillActivityPageReqVO pageReqVO, Integer status, LocalDateTime dateTime) {
         return selectPage(pageReqVO, new LambdaQueryWrapperX<SeckillActivityDO>()
                 .eqIfPresent(SeckillActivityDO::getStatus, status)
+                .lt(SeckillActivityDO::getStartTime, dateTime)
+                .gt(SeckillActivityDO::getEndTime, dateTime)// 开始时间 < 指定时间 < 结束时间,也就是说获取指定时间段的活动
                 .apply(ObjectUtil.isNotNull(pageReqVO.getConfigId()), "FIND_IN_SET(" + pageReqVO.getConfigId() + ",config_ids) > 0"));
     }
 

+ 4 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillconfig/SeckillConfigMapper.java

@@ -1,12 +1,16 @@
 package cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillconfig;
 
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
 import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config.SeckillConfigPageReqVO;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillConfigDO;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import org.apache.ibatis.annotations.Mapper;
 
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
 import java.util.List;
 
 @Mapper

+ 2 - 1
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;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
 import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponMatchReqVO;
+import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponMatchRespVO;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
@@ -178,7 +179,7 @@ public interface CouponService {
      * @param matchReqVO 匹配参数
      * @return 优惠券列表
      */
-    List<CouponDO> getMatchCouponList(Long userId, AppCouponMatchReqVO matchReqVO);
+    List<AppCouponMatchRespVO> getMatchCouponList(Long userId, AppCouponMatchReqVO matchReqVO);
 
     /**
      * 获取用户是否可以领取优惠券

+ 42 - 7
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;
 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
 import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponMatchReqVO;
+import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponMatchRespVO;
 import cn.iocoder.yudao.module.promotion.convert.coupon.CouponConvert;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
 import cn.iocoder.yudao.module.promotion.dal.mysql.coupon.CouponMapper;
+import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;
 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTemplateValidityTypeEnum;
@@ -286,6 +288,7 @@ public class CouponServiceImpl implements CouponService {
         }
         // 校验剩余数量(仅在 CouponTakeTypeEnum.USER 用户领取时)
         if (CouponTakeTypeEnum.isUser(couponTemplate.getTakeCount())
+                && couponTemplate.getTotalCount() != null
                 && couponTemplate.getTakeCount() + userIds.size() > couponTemplate.getTotalCount()) {
             throw exception(COUPON_TEMPLATE_NOT_ENOUGH);
         }
@@ -308,7 +311,7 @@ public class CouponServiceImpl implements CouponService {
      * @param couponTemplate 优惠劵模版
      */
     private void removeTakeLimitUser(Set<Long> userIds, CouponTemplateDO couponTemplate) {
-        if (couponTemplate.getTakeLimitCount() <= 0) {
+        if (couponTemplate.getTakeLimitCount() == null || couponTemplate.getTakeLimitCount() <= 0) {
             return;
         }
         // 查询已领过券的用户
@@ -356,13 +359,45 @@ public class CouponServiceImpl implements CouponService {
     }
 
     @Override
-    public List<CouponDO> getMatchCouponList(Long userId, AppCouponMatchReqVO matchReqVO) {
+    public List<AppCouponMatchRespVO> getMatchCouponList(Long userId, AppCouponMatchReqVO matchReqVO) {
+        List<AppCouponMatchRespVO> couponMatchist = new ArrayList<>();
         List<CouponDO> list = couponMapper.selectListByUserIdAndStatusAndUsePriceLeAndProductScope(userId,
-                CouponStatusEnum.UNUSED.getStatus(),
-                matchReqVO.getPrice(), matchReqVO.getSpuIds(), matchReqVO.getCategoryIds());
-        // 兜底逻辑:如果 CouponExpireJob 未执行,status 未变成 EXPIRE ,但是 validEndTime 已经过期了,需要进行过滤
-        list.removeIf(coupon -> !LocalDateTimeUtils.isBetween(coupon.getValidStartTime(), coupon.getValidEndTime()));
-        return list;
+                CouponStatusEnum.UNUSED.getStatus());
+        for (CouponDO couponDO : list) {
+            AppCouponMatchRespVO appCouponMatchRespVO = CouponConvert.INSTANCE.convert2(couponDO);
+            Integer productScope = appCouponMatchRespVO.getProductScope();
+            List<Long> productScopeValues = appCouponMatchRespVO.getProductScopeValues();
+            Integer usePrice = appCouponMatchRespVO.getUsePrice();
+            if(matchReqVO.getPrice() < usePrice){
+                // 价格小于等于,满足价格使用条件
+                appCouponMatchRespVO.setMatch(false);
+                appCouponMatchRespVO.setDescription("未达到使用门槛");
+            }else if(!LocalDateTimeUtils.isBetween(appCouponMatchRespVO.getValidStartTime(), appCouponMatchRespVO.getValidEndTime())) {
+                //判断时间
+                appCouponMatchRespVO.setMatch(false);
+                appCouponMatchRespVO.setDescription("使用时间未到");
+            }else if (PromotionProductScopeEnum.ALL.getScope().equals(productScope)){
+                appCouponMatchRespVO.setMatch(true);
+            }else if (PromotionProductScopeEnum.SPU.getScope().equals(productScope)){
+                boolean spu = new HashSet<>(productScopeValues).containsAll(matchReqVO.getSpuIds());
+                if(spu){
+                    appCouponMatchRespVO.setMatch(true);
+                }else {
+                    appCouponMatchRespVO.setMatch(false);
+                    appCouponMatchRespVO.setDescription("与商品不匹配");
+                }
+            }else if (PromotionProductScopeEnum.CATEGORY.getScope().equals(productScope)){
+                boolean category = new HashSet<>(productScopeValues).containsAll(matchReqVO.getCategoryIds());
+                if(category){
+                    appCouponMatchRespVO.setMatch(true);
+                }else {
+                    appCouponMatchRespVO.setMatch(false);
+                    appCouponMatchRespVO.setDescription("与商品类型不匹配");
+                }
+            }
+            couponMatchist.add(appCouponMatchRespVO);
+        }
+        return couponMatchist;
     }
 
     @Override

+ 5 - 0
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;
 import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
 import org.springframework.stereotype.Service;
+import jakarta.validation.Validator;
 import org.springframework.validation.annotation.Validated;
 
 import jakarta.annotation.Resource;
@@ -40,9 +41,13 @@ public class CouponTemplateServiceImpl implements CouponTemplateService {
     private ProductCategoryApi productCategoryApi;
     @Resource
     private ProductSpuApi productSpuApi;
+    @Resource
+    private Validator validator;
 
     @Override
     public Long createCouponTemplate(CouponTemplateCreateReqVO createReqVO) {
+        // 校验参数
+        createReqVO.validate(validator);
         // 校验商品范围
         validateProductScope(createReqVO.getProductScope(), createReqVO.getProductScopeValues());
         // 插入

+ 2 - 1
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountActivityService.java

@@ -1,6 +1,7 @@
 package cn.iocoder.yudao.module.promotion.service.discount;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.promotion.api.discount.dto.DiscountProductRespDTO;
 import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityCreateReqVO;
 import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityPageReqVO;
 import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityUpdateReqVO;
@@ -27,7 +28,7 @@ public interface DiscountActivityService {
      * @param skuIds SKU 编号数组
      * @return 匹配的限时折扣商品
      */
-    List<DiscountProductDO> getMatchDiscountProductList(Collection<Long> skuIds);
+    List<DiscountProductRespDTO> getMatchDiscountProductList(Collection<Long> skuIds);
 
     /**
      * 创建限时折扣活动

+ 3 - 2
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;
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.promotion.api.discount.dto.DiscountProductRespDTO;
 import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityBaseVO;
 import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityCreateReqVO;
 import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityPageReqVO;
@@ -49,7 +50,7 @@ public class DiscountActivityServiceImpl implements DiscountActivityService {
     private DiscountProductMapper discountProductMapper;
 
     @Override
-    public List<DiscountProductDO> getMatchDiscountProductList(Collection<Long> skuIds) {
+    public List<DiscountProductRespDTO> getMatchDiscountProductList(Collection<Long> skuIds) {
         return discountProductMapper.getMatchDiscountProductList(skuIds);
     }
 
@@ -130,7 +131,7 @@ public class DiscountActivityServiceImpl implements DiscountActivityService {
         List<DiscountProductDO> list = discountProductMapper.selectListByActivityId(id);
         // TODO @zhangshuai:一般简单的 stream 方法,建议是使用 CollectionUtils,例如说这里是 convertList 对把。
         List<Long> skuIds = list.stream().map(item -> item.getSkuId()).collect(Collectors.toList());
-        List<DiscountProductDO> matchDiscountProductList = getMatchDiscountProductList(skuIds);
+        List<DiscountProductRespDTO> matchDiscountProductList = getMatchDiscountProductList(skuIds);
         if (id != null) { // 排除自己这个活动
             matchDiscountProductList.removeIf(product -> id.equals(product.getActivityId()));
         }

+ 11 - 0
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;
 import jakarta.validation.Valid;
 
 import java.time.LocalDateTime;
+import java.util.Collection;
 import java.util.List;
 
 /**
@@ -71,4 +72,14 @@ public interface RewardActivityService {
      */
     List<RewardActivityDO> getRewardActivityListByStatusAndDateTimeLt(Integer status, LocalDateTime dateTime);
 
+    /**
+     * 获取指定 spu 编号最近参加的活动,每个 spuId 只返回一条记录
+     *
+     * @param spuIds   SPU 编号数组
+     * @param status   状态
+     * @param dateTime 当前日期时间
+     * @return 满减送活动列表
+     */
+    List<RewardActivityDO> getRewardActivityBySpuIdsAndStatusAndDateTimeLt(Collection<Long> spuIds, Integer status, LocalDateTime dateTime);
+
 }

+ 28 - 3
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/reward/RewardActivityServiceImpl.java

@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.module.promotion.service.reward;
 
+import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.date.LocalDateTimeUtil;
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
@@ -11,8 +12,10 @@ import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivi
 import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityCreateReqVO;
 import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityPageReqVO;
 import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityUpdateReqVO;
+import cn.iocoder.yudao.module.promotion.convert.reward.RewardActivityConvert;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;
 import cn.iocoder.yudao.module.promotion.dal.mysql.reward.RewardActivityMapper;
+import cn.iocoder.yudao.module.promotion.enums.common.PromotionActivityStatusEnum;
 import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
 import cn.iocoder.yudao.module.promotion.util.PromotionUtils;
 import jakarta.annotation.Resource;
@@ -20,8 +23,11 @@ import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 
 import java.time.LocalDateTime;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
+import java.util.stream.Collectors;
 
 import static cn.hutool.core.collection.CollUtil.intersectionDistinct;
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -52,9 +58,13 @@ public class RewardActivityServiceImpl implements RewardActivityService {
         // 1.2 校验商品是否冲突
         validateRewardActivitySpuConflicts(null, createReqVO);
 
-        // 2. 插入
-        RewardActivityDO rewardActivity = BeanUtils.toBean(createReqVO, RewardActivityDO.class)
-                .setStatus(PromotionUtils.calculateActivityStatus(createReqVO.getEndTime()));
+        // 插入
+        RewardActivityDO rewardActivity = RewardActivityConvert.INSTANCE.convert(createReqVO)
+                .setStatus(
+                        PromotionUtils.calculateActivityStatus(createReqVO.getEndTime()).equals(CommonStatusEnum.DISABLE.getStatus())?
+                                PromotionActivityStatusEnum.WAIT.getStatus():
+                                PromotionActivityStatusEnum.RUN.getStatus()
+                );
         rewardActivityMapper.insert(rewardActivity);
         // 返回
         return rewardActivity.getId();
@@ -199,4 +209,19 @@ public class RewardActivityServiceImpl implements RewardActivityService {
         return rewardActivityMapper.selectListByStatusAndDateTimeLt(status, dateTime);
     }
 
+    @Override
+    public List<RewardActivityDO> getRewardActivityBySpuIdsAndStatusAndDateTimeLt(Collection<Long> spuIds, Integer status, LocalDateTime dateTime) {
+        List<ProductSpuRespDTO> spuList = productSpuApi.validateSpuList(spuIds);
+        //查询出商品的分类ids
+        List<Long> categoryIds = spuList.stream().map(ProductSpuRespDTO::getCategoryId).collect(Collectors.toList());
+        // 1. 查询出指定 spuId 的 spu 参加的活动
+        List<RewardActivityDO> rewardActivityList = rewardActivityMapper.getRewardActivityByStatusAndDateTimeLt(spuIds, categoryIds,status,dateTime);
+        if (CollUtil.isEmpty(rewardActivityList)) {
+            return Collections.emptyList();
+        }
+
+        // 2. 查询活动详情
+        return rewardActivityList;
+    }
+
 }

+ 1 - 1
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 {
     List<SeckillActivityDO> getSeckillActivityListByConfigIdAndStatus(Long configId, Integer status);
 
     /**
-     * 通过活动时段获取秒杀活动
+     * 通过活动时段获取开始的秒杀活动
      *
      * @param pageReqVO 请求
      * @return 秒杀活动列表

+ 4 - 1
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;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillProductDO;
 import cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity.SeckillActivityMapper;
 import cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity.SeckillProductMapper;
+import cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillconfig.SeckillConfigMapper;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.validation.annotation.Validated;
@@ -57,6 +58,8 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
     @Resource
     private SeckillProductMapper seckillProductMapper;
     @Resource
+    private SeckillConfigMapper seckillConfigMapper;
+    @Resource
     private SeckillConfigService seckillConfigService;
     @Resource
     private ProductSpuApi productSpuApi;
@@ -289,7 +292,7 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
 
     @Override
     public PageResult<SeckillActivityDO> getSeckillActivityAppPageByConfigId(AppSeckillActivityPageReqVO pageReqVO) {
-        return seckillActivityMapper.selectPage(pageReqVO, CommonStatusEnum.ENABLE.getStatus());
+        return seckillActivityMapper.selectPage(pageReqVO, CommonStatusEnum.ENABLE.getStatus(),LocalDateTime.now());
     }
 
     @Override

+ 6 - 4
yudao-module-mall/yudao-module-promotion-biz/src/main/resources/mapper/discount/DiscountProductMapper.xml

@@ -3,8 +3,8 @@
 <mapper namespace="cn.iocoder.yudao.module.promotion.dal.mysql.discount.DiscountProductMapper">
 
 
-    <select id="getMatchDiscountProductList"   resultType="cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO">
-        SELECT pdp.*
+    <select id="getMatchDiscountProductList"   resultType="cn.iocoder.yudao.module.promotion.api.discount.dto.DiscountProductRespDTO">
+        SELECT pdp.*,pda.name as activity_name
         FROM promotion_discount_product pdp
         LEFT JOIN promotion_discount_activity pda
         ON pdp.activity_id = pda.id
@@ -16,9 +16,11 @@
                 </foreach>
             </if>
             AND pda.start_time &lt;= CURRENT_TIME AND pda.end_time &gt;= CURRENT_TIME
-            AND pda.`status` = 20
-            AND pda.deleted != 1
+            AND pda.`status` = 0
+            AND pda.deleted =0
+            AND pdp.deleted = 0
         </where>
+        ORDER BY pdp.id DESC
     </select>
 
 </mapper>

+ 155 - 2
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java

@@ -1,9 +1,23 @@
 package cn.iocoder.yudao.module.trade.controller.app.order;
 
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
+import cn.iocoder.yudao.module.member.api.level.MemberLevelApi;
+import cn.iocoder.yudao.module.member.api.level.dto.MemberLevelRespDTO;
+import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
+import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
 import cn.iocoder.yudao.module.pay.api.notify.dto.PayOrderNotifyReqDTO;
+import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
+import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
+import cn.iocoder.yudao.module.promotion.api.discount.DiscountActivityApi;
+import cn.iocoder.yudao.module.promotion.api.discount.dto.DiscountProductRespDTO;
+import cn.iocoder.yudao.module.promotion.api.reward.RewardActivityApi;
+import cn.iocoder.yudao.module.promotion.api.reward.dto.RewardActivityMatchRespDTO;
+import cn.iocoder.yudao.module.promotion.enums.common.PromotionConditionTypeEnum;
+import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;
+import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
 import cn.iocoder.yudao.module.trade.controller.app.order.vo.*;
 import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemCommentCreateReqVO;
 import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemRespVO;
@@ -27,12 +41,14 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
-import java.util.List;
-import java.util.Map;
+import java.time.LocalDateTime;
+import java.util.*;
 
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
+import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.DISCOUNT_ACTIVITY_TYPE_NOT_EXISTS;
 
 @Tag(name = "用户 App - 交易订单")
 @RestController
@@ -54,6 +70,17 @@ public class AppTradeOrderController {
     @Resource
     private TradeOrderProperties tradeOrderProperties;
 
+    @Resource
+    private MemberLevelApi memberLevelApi;
+    @Resource
+    private MemberUserApi memberUserApi;
+    @Resource
+    private DiscountActivityApi discountActivityApi;
+    @Resource
+    private RewardActivityApi rewardActivityApi;
+    @Resource
+    private ProductSkuApi productKpuApi;
+
     @GetMapping("/settlement")
     @Operation(summary = "获得订单结算信息")
     @PreAuthenticated
@@ -61,6 +88,58 @@ public class AppTradeOrderController {
         return success(tradeOrderUpdateService.settlementOrder(getLoginUserId(), settlementReqVO));
     }
 
+    @GetMapping("/settlementProduct")
+    @Operation(summary = "获得商品结算信息")
+    public CommonResult<List<AppTradeProductSettlementRespVO>> settlementProduct(@RequestParam("ids") Set<Long> ids) {
+        List<AppTradeProductSettlementRespVO> appTradeProductSettlementRespVOS = new ArrayList<>();
+        MemberLevelRespDTO memberLevel = getMemberLevel();
+        ids.forEach(spuId -> {
+            List<AppTradeProductSettlementRespVO.Sku> skus = new ArrayList<>();
+            List<ProductSkuRespDTO> skuList = productKpuApi.getSkuListBySpuId(Collections.singletonList(spuId));
+            //查询sku的会员和限时优惠
+            skuList.forEach(sku -> {
+                //查询限时优惠价格
+                AppTradeProductSettlementRespVO.Sku skuDiscount = calculateDiscountPrice(sku.getId(), sku.getPrice());
+
+                //查询会员价
+                AppTradeProductSettlementRespVO.Sku skuVip = calculateVipPrice(sku.getId(), sku.getPrice(), memberLevel);
+
+                if(skuDiscount != null && skuVip != null){
+                    if(skuDiscount.getPrice() > skuVip.getPrice()){
+                        skus.add(skuVip);
+                    }else{
+                        skus.add(skuDiscount);
+                    }
+                }else if(skuDiscount != null){
+                    skus.add(skuDiscount);
+                }else if(skuVip != null){
+                    skus.add(skuVip);
+                }
+
+            });
+            AppTradeProductSettlementRespVO.Reward reward = calculateReward(spuId);
+            AppTradeProductSettlementRespVO respVO = AppTradeProductSettlementRespVO.builder().id(spuId).skus(skus).build();
+            if(reward != null){
+                //创建满减活动对象
+                respVO.setReward(reward);
+            }
+            appTradeProductSettlementRespVOS.add(respVO);
+        });
+        return success(appTradeProductSettlementRespVOS);
+    }
+
+    private MemberLevelRespDTO getMemberLevel() {
+        Long userId = getLoginUserId();
+        if (userId == null) {
+            return null;
+        }
+        MemberUserRespDTO user = memberUserApi.getUser(userId);
+        if (user.getLevelId() == null || user.getLevelId() <= 0) {
+            return null;
+        }
+        return memberLevelApi.getMemberLevel(user.getLevelId());
+    }
+
     @PostMapping("/create")
     @Operation(summary = "创建订单")
     @PreAuthenticated
@@ -188,4 +267,78 @@ public class AppTradeOrderController {
         return success(tradeOrderUpdateService.createOrderItemCommentByMember(getLoginUserId(), createReqVO));
     }
 
+    /**
+     * 计算会员 VIP 优惠价格
+     *
+     * @param price 原价
+     * @param memberLevel 会员等级
+     * @return 优惠价格
+     */
+    public AppTradeProductSettlementRespVO.Sku calculateVipPrice(Long skuId, Integer price, MemberLevelRespDTO memberLevel) {
+        if (memberLevel == null || memberLevel.getDiscountPercent() == null) {
+            return null;
+        }
+        Integer newPrice = price * memberLevel.getDiscountPercent() / 100;
+        return AppTradeProductSettlementRespVO.Sku.builder().
+                skuId(skuId).
+                type(PromotionTypeEnum.MEMBER_LEVEL.getType()).
+                price(newPrice).build();
+    }
+
+    /**
+     * 计算限时优惠信息
+     *
+     * @param price 原价
+     * @param skuId 商品规格id
+     * @return 优惠价格
+     */
+    private AppTradeProductSettlementRespVO.Sku calculateDiscountPrice(Long skuId, Integer price) {
+        if (skuId == null) {
+            return null;
+        }
+
+        //根据商品id查询限时优惠
+        List<DiscountProductRespDTO> matchDiscountProductList = discountActivityApi.getMatchDiscountProductList(Collections.singletonList(skuId));
+        if (matchDiscountProductList != null && !matchDiscountProductList.isEmpty()) {
+            DiscountProductRespDTO discountProductRespDTO = matchDiscountProductList.get(matchDiscountProductList.size() - 1);
+            AppTradeProductSettlementRespVO.Sku sku = AppTradeProductSettlementRespVO.Sku.builder().
+                    skuId(skuId).
+                    discountId(discountProductRespDTO.getId()).
+                    type(PromotionTypeEnum.DISCOUNT_ACTIVITY.getType()).
+                    endTime(discountProductRespDTO.getActivityEndTime()).
+                    build();
+            Integer discountType = discountProductRespDTO.getDiscountType();
+            if(Objects.equals(PromotionDiscountTypeEnum.PRICE.getType(), discountType)){
+                sku.setPrice(price - discountProductRespDTO.getDiscountPrice() * 100);
+            }else if(Objects.equals(PromotionDiscountTypeEnum.PERCENT.getType(), discountType)){
+                Integer newPrice = price * discountProductRespDTO.getDiscountPercent() / 100;
+                sku.setPrice(price - newPrice);
+            }else{
+                throw exception(DISCOUNT_ACTIVITY_TYPE_NOT_EXISTS);
+            }
+            return sku;
+        }
+        return null;
+    }
+
+    /**
+     * 获取第一层满减活动
+     *
+     * @param spuId 商品规格id
+     * @return 优惠价格
+     */
+    private AppTradeProductSettlementRespVO.Reward calculateReward(Long spuId) {
+        List<RewardActivityMatchRespDTO> matchRewardActivityList = rewardActivityApi.getRewardActivityBySpuIdsAndStatusAndDateTimeLt(Collections.singletonList(spuId), CommonStatusEnum.ENABLE.getStatus(), LocalDateTime.now());
+        if(matchRewardActivityList != null && !matchRewardActivityList.isEmpty()){
+            RewardActivityMatchRespDTO rewardActivityMatchRespDTO = matchRewardActivityList.get(matchRewardActivityList.size() - 1);
+            if(rewardActivityMatchRespDTO != null){
+                RewardActivityMatchRespDTO.Rule rule = rewardActivityMatchRespDTO.getRules().get(0);
+                return AppTradeProductSettlementRespVO.Reward.builder().
+                        rewardActivity("满" + rule.getLimit() / 100 + (Objects.equals(rewardActivityMatchRespDTO.getConditionType(), PromotionConditionTypeEnum.PRICE.getType())?"元":"件"+"减") +rule.getDiscountPrice() / 100)
+                        .id(rewardActivityMatchRespDTO.getId()).build();
+            }
+        }
+        return null;
+    }
+
 }

+ 8 - 0
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementRespVO.java

@@ -1,6 +1,7 @@
 package cn.iocoder.yudao.module.trade.controller.app.order.vo;
 
 import cn.iocoder.yudao.module.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO;
+import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.constraints.NotNull;
 import lombok.AllArgsConstructor;
@@ -31,6 +32,13 @@ public class AppTradeOrderSettlementRespVO {
     @Schema(description = "总积分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
     private Integer totalPoint;
 
+    /**
+     * 营销活动数组
+     *
+     * 只对应 {@link TradePriceCalculateRespBO.Price#items} 商品匹配的活动
+     */
+    private List<TradePriceCalculateRespBO.Promotion> promotions;
+
     @Schema(description = "购物项")
     @Data
     public static class Item {

+ 63 - 0
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeProductSettlementRespVO.java

@@ -0,0 +1,63 @@
+package cn.iocoder.yudao.module.trade.controller.app.order.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Builder;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Schema(description = "用户 App - 商品结算信息 Response VO")
+@Data
+@Builder
+public class AppTradeProductSettlementRespVO {
+
+    @Schema(description = "spu 商品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    private Long id;
+
+    @Schema(description = "满减活动对象", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    private Reward reward;
+
+    @Schema(description = "sku 活动信息", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    private List<Sku> skus;
+
+    /**
+     * 满减活动
+     */
+    @Data
+    @Builder
+    public static class Reward implements Serializable {
+
+        @Schema(description = "满减活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+        private Long id;
+
+        @Schema(description = "满减活动信息", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+        private String rewardActivity;
+
+    }
+
+    /**
+     * SKU 数组
+     */
+    @Data
+    @Builder
+    public static class Sku implements Serializable {
+
+        @Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+        private Long skuId;
+
+        @Schema(description = "价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+        private Integer price;
+
+        @Schema(description = "营销类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+        private Integer type; // 对应 PromotionTypeEnum 枚举
+
+        @Schema(description = "限时优惠id", requiredMode = Schema.RequiredMode.REQUIRED)
+        private Long discountId;
+
+        @Schema(description = "活动结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
+        private LocalDateTime endTime;
+
+    }
+}

+ 2 - 4
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 {
             @Mapping(source = "afterSale.orderId", target = "merchantOrderId"),
             @Mapping(source = "afterSale.id", target = "merchantRefundId"),
             @Mapping(source = "afterSale.applyReason", target = "reason"),
-            @Mapping(source = "afterSale.refundPrice", target = "price"),
-            @Mapping(source = "orderProperties.payAppKey", target = "appKey")
+            @Mapping(source = "afterSale.refundPrice", target = "price")
     })
-    PayRefundCreateReqDTO convert(String userIp, AfterSaleDO afterSale,
-                                  TradeOrderProperties orderProperties);
+    PayRefundCreateReqDTO convert(String userIp, AfterSaleDO afterSale);
 
     MemberUserRespVO convert(MemberUserRespDTO bean);
 

+ 2 - 1
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 {
             @Override
             public void afterCommit() {
                 // 创建退款单
-                PayRefundCreateReqDTO createReqDTO = AfterSaleConvert.INSTANCE.convert(userIp, afterSale, tradeOrderProperties)
+                PayRefundCreateReqDTO createReqDTO = AfterSaleConvert.INSTANCE.convert(userIp, afterSale)
                         .setReason(StrUtil.format("退款【{}】", afterSale.getSpuName()));
+                createReqDTO.setAppKey(tradeOrderProperties.getPayAppKey());
                 Long payRefundId = payRefundApi.createRefund(createReqDTO);
                 // 更新售后单的退款单号
                 tradeAfterSaleMapper.updateById(new AfterSaleDO().setId(afterSale.getId()).setPayRefundId(payRefundId));

+ 2 - 1
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 {
      */
     private Long bargainActivityId;
 
+
     /**
      * 是否包邮
      */
-    private Boolean freeDelivery;
+    private Boolean freeDelivery = false;
 
     /**
      * 赠送的优惠劵

+ 5 - 2
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 {
      * @return 是否包邮
      */
     private boolean isGlobalExpressFree(TradePriceCalculateRespBO result) {
+
         TradeConfigDO config = tradeConfigService.getTradeConfig();
-        return config != null
+        return  result.getFreeDelivery() ||
+                (config != null
                 && Boolean.TRUE.equals(config.getDeliveryExpressFreeEnabled()) // 开启包邮
-                && result.getPrice().getPayPrice() >= config.getDeliveryExpressFreePrice(); // 满足包邮的价格
+                && result.getPrice().getPayPrice() >= config.getDeliveryExpressFreePrice()
+                ); // 满足包邮的价格
     }
 
     private void calculateDeliveryPrice(List<OrderItem> selectedSkus,

+ 109 - 17
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;
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.module.member.api.level.MemberLevelApi;
+import cn.iocoder.yudao.module.member.api.level.dto.MemberLevelRespDTO;
+import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
+import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
 import cn.iocoder.yudao.module.promotion.api.discount.DiscountActivityApi;
 import cn.iocoder.yudao.module.promotion.api.discount.dto.DiscountProductRespDTO;
 import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;
@@ -14,6 +18,8 @@ import org.springframework.core.annotation.Order;
 import org.springframework.stereotype.Component;
 
 import jakarta.annotation.Resource;
+
+import java.math.BigDecimal;
 import java.util.List;
 import java.util.Map;
 
@@ -32,6 +38,10 @@ public class TradeDiscountActivityPriceCalculator implements TradePriceCalculato
 
     @Resource
     private DiscountActivityApi discountActivityApi;
+    @Resource
+    private MemberLevelApi memberLevelApi;
+    @Resource
+    private MemberUserApi memberUserApi;
 
     @Override
     public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
@@ -39,35 +49,101 @@ public class TradeDiscountActivityPriceCalculator implements TradePriceCalculato
         if (ObjectUtil.notEqual(result.getType(), TradeOrderTypeEnum.NORMAL.getType())) {
             return;
         }
+
+        boolean discount;
+        boolean vip;
+
+        //----------------------------------限时折扣计算-----------------------------------------
         // 获得 SKU 对应的限时折扣活动
         List<DiscountProductRespDTO> discountProducts = discountActivityApi.getMatchDiscountProductList(
                 convertSet(result.getItems(), TradePriceCalculateRespBO.OrderItem::getSkuId));
         if (CollUtil.isEmpty(discountProducts)) {
-            return;
+            discount = false;
+        }else {
+            discount = true;
         }
         Map<Long, DiscountProductRespDTO> discountProductMap = convertMap(discountProducts, DiscountProductRespDTO::getSkuId);
 
-        // 处理每个 SKU 的限时折扣
+
+
+        //----------------------------------会员计算-----------------------------------------
+        MemberLevelRespDTO level;
+        // 获得用户的会员等级
+        MemberUserRespDTO user = memberUserApi.getUser(param.getUserId());
+        if (user.getLevelId() != null && user.getLevelId() > 0) {
+            level = memberLevelApi.getMemberLevel(user.getLevelId());
+            if (level != null && level.getDiscountPercent() != null) {
+                vip = true;
+            }else {
+                vip = false;
+            }
+        }else {
+            level = null;
+            vip = false;
+        }
+
+
+        // 2. 计算每个 SKU 的优惠金额
         result.getItems().forEach(orderItem -> {
-            // 1. 获取该 SKU 的优惠信息
-            DiscountProductRespDTO discountProduct = discountProductMap.get(orderItem.getSkuId());
-            if (discountProduct == null) {
-                return;
+
+            //----------------------------------限时折扣计算-----------------------------------------
+            DiscountProductRespDTO discountProduct = null;
+            Integer newDiscountPrice = 0;
+            if (discount){
+                // 2.1  计算限时折扣优惠信息
+                discountProduct = discountProductMap.get(orderItem.getSkuId());
+                if (discountProduct != null) {
+                    // 2.2 计算优惠金额
+                    Integer newPayPrice = calculatePayPrice(discountProduct, orderItem);
+                    newDiscountPrice = orderItem.getPayPrice() - newPayPrice;
+                }
+            }
+
+
+            //----------------------------------会员计算-----------------------------------------
+            Integer vipPrice = 0;
+            if (vip){
+                // 2.3 计算会员优惠金额
+                vipPrice = calculateVipPrice(orderItem.getPayPrice(), level.getDiscountPercent());
             }
-            // 2. 计算优惠金额
-            Integer newPayPrice = calculatePayPrice(discountProduct, orderItem);
-            Integer newDiscountPrice = orderItem.getPayPrice() - newPayPrice;
 
-            // 3.1 记录优惠明细
+
+            // 2.4 记录优惠明细
+            // 注意,只有在选中的情况下,才会记录到优惠明细。否则仅仅是更新 SKU 优惠金额,用于展示
             if (orderItem.getSelected()) {
-                // 注意,只有在选中的情况下,才会记录到优惠明细。否则仅仅是更新 SKU 优惠金额,用于展示
-                TradePriceCalculatorHelper.addPromotion(result, orderItem,
-                        discountProduct.getActivityId(), discountProduct.getActivityName(), PromotionTypeEnum.DISCOUNT_ACTIVITY.getType(),
-                        StrUtil.format("限时折扣:省 {} 元", formatPrice(newDiscountPrice)),
-                        newDiscountPrice);
+                if (discount && vip){
+                    if(newDiscountPrice > vipPrice){
+                        TradePriceCalculatorHelper.addPromotion(result, orderItem,
+                                discountProduct.getActivityId(), discountProduct.getActivityName(), PromotionTypeEnum.DISCOUNT_ACTIVITY.getType(),
+                                StrUtil.format("限时折扣:省 {} 元", formatPrice(newDiscountPrice)),
+                                newDiscountPrice);
+                        // 2.5 更新 SKU 优惠金额
+                        orderItem.setDiscountPrice(orderItem.getDiscountPrice() + newDiscountPrice);
+                    }else{
+                        TradePriceCalculatorHelper.addPromotion(result, orderItem,
+                                level.getId(), level.getName(), PromotionTypeEnum.MEMBER_LEVEL.getType(),
+                                String.format("会员等级折扣:省 %s 元", formatPrice(vipPrice)),
+                                vipPrice);
+                        // 2.5 更新 SKU 的优惠金额
+                        orderItem.setVipPrice(vipPrice);
+                    }
+                }else if (discount){
+                    TradePriceCalculatorHelper.addPromotion(result, orderItem,
+                            discountProduct.getActivityId(), discountProduct.getActivityName(), PromotionTypeEnum.DISCOUNT_ACTIVITY.getType(),
+                            StrUtil.format("限时折扣:省 {} 元", formatPrice(newDiscountPrice)),
+                            newDiscountPrice);
+                    // 2.5 更新 SKU 优惠金额
+                    orderItem.setDiscountPrice(orderItem.getDiscountPrice() + newDiscountPrice);
+                }else if (vip){
+                    TradePriceCalculatorHelper.addPromotion(result, orderItem,
+                            level.getId(), level.getName(), PromotionTypeEnum.MEMBER_LEVEL.getType(),
+                            String.format("会员等级折扣:省 %s 元", formatPrice(vipPrice)),
+                            vipPrice);
+                    // 2.5 更新 SKU 的优惠金额
+                    orderItem.setVipPrice(vipPrice);
+                }
             }
-            // 3.2 更新 SKU 优惠金额
-            orderItem.setDiscountPrice(orderItem.getDiscountPrice() + newDiscountPrice);
+
             TradePriceCalculatorHelper.recountPayPrice(orderItem);
         });
         TradePriceCalculatorHelper.recountAllPrice(result);
@@ -86,4 +162,20 @@ public class TradeDiscountActivityPriceCalculator implements TradePriceCalculato
         return price;
     }
 
+    /**
+     * 计算会员 VIP 优惠价格
+     *
+     * @param price 原价
+     * @param discountPercent 折扣
+     * @return 优惠价格
+     */
+    public Integer calculateVipPrice(Integer price, Integer discountPercent) {
+        if (discountPercent == null) {
+            return 0;
+        }
+        BigDecimal divide = new BigDecimal(price).multiply(new BigDecimal(discountPercent)).divide(new BigDecimal(100));
+        Integer newPrice =  divide.intValue();
+        return price - newPrice;
+    }
+
 }

+ 41 - 36
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 {
     @Resource
     private MemberUserApi memberUserApi;
 
+    /**
+     * 会员计算迁移到限时优惠计算里
+     * @param param
+     * @param result
+     */
     @Override
     public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
-        // 0. 只有【普通】订单,才计算该优惠
-        if (ObjectUtil.notEqual(result.getType(), TradeOrderTypeEnum.NORMAL.getType())) {
-            return;
-        }
-        // 1. 获得用户的会员等级
-        MemberUserRespDTO user = memberUserApi.getUser(param.getUserId());
-        if (user.getLevelId() == null || user.getLevelId() <= 0) {
-            return;
-        }
-        MemberLevelRespDTO level = memberLevelApi.getMemberLevel(user.getLevelId());
-        if (level == null || level.getDiscountPercent() == null) {
-            return;
-        }
-
-        // 2. 计算每个 SKU 的优惠金额
-        result.getItems().forEach(orderItem -> {
-            // 2.1 计算优惠金额
-            Integer vipPrice = calculateVipPrice(orderItem.getPayPrice(), level.getDiscountPercent());
-            if (vipPrice <= 0) {
-                return;
-            }
-
-            // 2.2 记录优惠明细
-            if (orderItem.getSelected()) {
-                // 注意,只有在选中的情况下,才会记录到优惠明细。否则仅仅是更新 SKU 优惠金额,用于展示
-                TradePriceCalculatorHelper.addPromotion(result, orderItem,
-                        level.getId(), level.getName(), PromotionTypeEnum.MEMBER_LEVEL.getType(),
-                        String.format("会员等级折扣:省 %s 元", formatPrice(vipPrice)),
-                        vipPrice);
-            }
-
-            // 2.3 更新 SKU 的优惠金额
-            orderItem.setVipPrice(vipPrice);
-            TradePriceCalculatorHelper.recountPayPrice(orderItem);
-        });
-        TradePriceCalculatorHelper.recountAllPrice(result);
+//        // 0. 只有【普通】订单,才计算该优惠
+//        if (ObjectUtil.notEqual(result.getType(), TradeOrderTypeEnum.NORMAL.getType())) {
+//            return;
+//        }
+//        // 1. 获得用户的会员等级
+//        MemberUserRespDTO user = memberUserApi.getUser(param.getUserId());
+//        if (user.getLevelId() == null || user.getLevelId() <= 0) {
+//            return;
+//        }
+//        MemberLevelRespDTO level = memberLevelApi.getMemberLevel(user.getLevelId());
+//        if (level == null || level.getDiscountPercent() == null) {
+//            return;
+//        }
+//
+//        // 2. 计算每个 SKU 的优惠金额
+//        result.getItems().forEach(orderItem -> {
+//            // 2.1 计算优惠金额
+//            Integer vipPrice = calculateVipPrice(orderItem.getPayPrice(), level.getDiscountPercent());
+//            if (vipPrice <= 0) {
+//                return;
+//            }
+//
+//            // 2.2 记录优惠明细
+//            if (orderItem.getSelected()) {
+//                // 注意,只有在选中的情况下,才会记录到优惠明细。否则仅仅是更新 SKU 优惠金额,用于展示
+//                TradePriceCalculatorHelper.addPromotion(result, orderItem,
+//                        level.getId(), level.getName(), PromotionTypeEnum.MEMBER_LEVEL.getType(),
+//                        String.format("会员等级折扣:省 %s 元", formatPrice(vipPrice)),
+//                        vipPrice);
+//            }
+//
+//            // 2.3 更新 SKU 的优惠金额
+//            orderItem.setVipPrice(vipPrice);
+//            TradePriceCalculatorHelper.recountPayPrice(orderItem);
+//        });
+//        TradePriceCalculatorHelper.recountAllPrice(result);
     }
 
     /**

+ 18 - 15
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;
 import java.util.List;
 import java.util.Map;
 
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;
+import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.REWARD_ACTIVITY_TYPE_NOT_EXISTS;
 import static cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculatorHelper.formatPrice;
 
 // TODO @puhui999:相关的单测,建议改一改
@@ -47,14 +50,15 @@ public class TradeRewardActivityPriceCalculator implements TradePriceCalculator
             return;
         }
         // 获得 SKU 对应的满减送活动
-        List<RewardActivityMatchRespDTO> rewardActivities = rewardActivityApi.getRewardActivityListByStatusAndNow(
-                CommonStatusEnum.ENABLE.getStatus(), LocalDateTime.now());
+        List<RewardActivityMatchRespDTO> rewardActivities = rewardActivityApi.getRewardActivityBySpuIdsAndStatusAndDateTimeLt(
+                convertSet(result.getItems(), TradePriceCalculateRespBO.OrderItem::getSpuId), CommonStatusEnum.ENABLE.getStatus(), LocalDateTime.now());
         if (CollUtil.isEmpty(rewardActivities)) {
             return;
         }
-
-        // 处理每个满减送活动
-        rewardActivities.forEach(rewardActivity -> calculate(param, result, rewardActivity));
+        // 处理最新的满减送活动
+        if(!rewardActivities.isEmpty()){
+            calculate(param, result, rewardActivities.get(0));
+        }
     }
 
     private void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result,
@@ -77,6 +81,8 @@ public class TradeRewardActivityPriceCalculator implements TradePriceCalculator
         Integer newDiscountPrice = rule.getDiscountPrice();
         // 2.2 计算分摊的优惠金额
         List<Integer> divideDiscountPrices = TradePriceCalculatorHelper.dividePrice(orderItems, newDiscountPrice);
+        //计算是否包邮
+        result.setFreeDelivery(rule.getFreeDelivery());
 
         // 3.1 记录使用的优惠劵
         result.setCouponId(param.getCouponId());
@@ -119,27 +125,24 @@ public class TradeRewardActivityPriceCalculator implements TradePriceCalculator
     /**
      * 获得满减送的订单项(商品)列表
      *
-     * @param result         计算结果
+     * @param result 计算结果
      * @param rewardActivity 满减送活动
      * @return 订单项(商品)列表
      */
     private List<TradePriceCalculateRespBO.OrderItem> filterMatchActivityOrderItems(TradePriceCalculateRespBO result,
                                                                                     RewardActivityMatchRespDTO rewardActivity) {
-        // 情况一:全部商品都可以参与
-        if (PromotionProductScopeEnum.isAll(rewardActivity.getProductScope())) {
+        Integer productScope = rewardActivity.getProductScope();
+        if(PromotionProductScopeEnum.isAll(productScope)){
             return result.getItems();
-        }
-        // 情况二:指定商品参与
-        if (PromotionProductScopeEnum.isSpu(rewardActivity.getProductScope())) {
+        }else if (PromotionProductScopeEnum.isSpu(productScope)) {
             return filterList(result.getItems(),
                     orderItem -> CollUtil.contains(rewardActivity.getProductScopeValues(), orderItem.getSpuId()));
-        }
-        // 情况三:指定商品类型参与
-        if (PromotionProductScopeEnum.isCategory(rewardActivity.getProductScope())) {
+        }else if (PromotionProductScopeEnum.isCategory(productScope)) {
             return filterList(result.getItems(),
                     orderItem -> CollUtil.contains(rewardActivity.getProductScopeValues(), orderItem.getCategoryId()));
+        }else{
+            throw exception(REWARD_ACTIVITY_TYPE_NOT_EXISTS);
         }
-        return List.of();
     }
 
     /**

+ 2 - 0
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;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
 import cn.iocoder.yudao.framework.datapermission.core.util.DataPermissionUtils;
+import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
 import cn.iocoder.yudao.module.infra.api.config.ConfigApi;
 import cn.iocoder.yudao.module.infra.api.file.FileApi;
 import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthRegisterReqVO;
@@ -104,6 +105,7 @@ public class AdminUserServiceImpl implements AdminUserService {
         AdminUserDO user = BeanUtils.toBean(createReqVO, AdminUserDO.class);
         user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启
         user.setPassword(encodePassword(createReqVO.getPassword())); // 加密密码
+        user.setTenantId(TenantContextHolder.getRequiredTenantId());
         userMapper.insert(user);
         // 2.2 插入关联岗位
         if (CollectionUtil.isNotEmpty(user.getPostIds())) {

+ 11 - 11
yudao-server/src/main/resources/application-local.yaml

@@ -6,7 +6,7 @@ spring:
   # 数据源配置项
   autoconfigure:
     exclude:
-      - org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration # 默认 local 环境,不开启 Quartz 的自动配置
+      #- org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration # 默认 local 环境,不开启 Quartz 的自动配置
       - de.codecentric.boot.admin.server.config.AdminServerAutoConfiguration # 禁用 Spring Boot Admin 的 Server 的自动配置
       - de.codecentric.boot.admin.server.ui.config.AdminServerUiAutoConfiguration # 禁用 Spring Boot Admin 的 Server UI 的自动配置
       - de.codecentric.boot.admin.client.config.SpringBootAdminClientAutoConfiguration # 禁用 Spring Boot Admin 的 Client 的自动配置
@@ -45,7 +45,7 @@ spring:
       primary: master
       datasource:
         master:
-          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 连接的示例
+          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 连接的示例
           #          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 连接的示例
           #          url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例
           #          url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例
@@ -61,19 +61,19 @@ spring:
           #          password: SYSDBA001 # DM 连接的示例
           #          username: root # OpenGauss 连接的示例
           #          password: Yudao@2024 # OpenGauss 连接的示例
-        slave: # 模拟从库,可根据自己需要修改
-          lazy: true # 开启懒加载,保证启动速度
-          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true&nullCatalogMeansCurrent=true
-          username: root
-          password: 123456
+#        slave: # 模拟从库,可根据自己需要修改
+#          lazy: true # 开启懒加载,保证启动速度
+#          url: jdbc:mysql://192.168.10.207:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true&nullCatalogMeansCurrent=true
+#          username: root
+#          password: 123456
 
   # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
   data:
     redis:
-      host: 127.0.0.1 # 地址
+      host: 192.168.10.207 # 地址
       port: 6379 # 端口
       database: 0 # 数据库索引
-#    password: dev # 密码,建议生产环境开启
+      password: 123456 # 密码,建议生产环境开启
 
 --- #################### 定时任务相关配置 ####################
 
@@ -200,8 +200,8 @@ wx:
 #    secret: 6f270509224a7ae1296bbf1c8cb97aed
 #    appid: wxc4598c446f8a9cb3 # 测试号(Kongdy 提供的)
 #    secret: 4a1a04e07f6a4a0751b39c3064a92c8b
-    appid: wx66186af0759f47c9 # 测试号(puhui 提供的)
-    secret: 3218bcbd112cbc614c7264ceb20144ac
+    appid: wx9a0a5b259d852380 # 测试号(puhui 提供的)
+    secret: 70e65fa9d1a4f2c4e1b2aa8751d3b75e
     config-storage:
       type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取
       key-prefix: wa # Redis Key 的前缀