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 69 list.forEach(spu -> spu.setSalesCount(spu.getSalesCount() + spu.getVirtualSalesCount()));
70 70 List<AppProductSpuRespVO> voList = BeanUtils.toBean(list, AppProductSpuRespVO.class);
71 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 74 return success(voList);
75 75 }
76 76  
... ... @@ -86,8 +86,8 @@ public class AppProductSpuController {
86 86 pageResult.getList().forEach(spu -> spu.setSalesCount(spu.getSalesCount() + spu.getVirtualSalesCount()));
87 87 PageResult<AppProductSpuRespVO> voPageResult = BeanUtils.toBean(pageResult, AppProductSpuRespVO.class);
88 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 91 return success(voPageResult);
92 92 }
93 93  
... ... @@ -142,7 +142,7 @@ public class AppProductSpuController {
142 142 */
143 143 public Integer calculateVipPrice(Integer price, MemberLevelRespDTO memberLevel) {
144 144 if (memberLevel == null || memberLevel.getDiscountPercent() == null) {
145   - return 0;
  145 + return null;
146 146 }
147 147 Integer newPrice = price * memberLevel.getDiscountPercent() / 100;
148 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 38 @Schema(description = "市场价,单位使用:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
39 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 44 @Schema(description = "库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "666")
45 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 2  
3 3 import lombok.Data;
4 4  
  5 +import java.time.LocalDateTime;
  6 +
5 7 /**
6 8 * 限时折扣活动商品 Response DTO
7 9 *
... ... @@ -44,5 +46,12 @@ public class DiscountProductRespDTO {
44 46 * 活动标题
45 47 */
46 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 3 import cn.iocoder.yudao.module.promotion.api.reward.dto.RewardActivityMatchRespDTO;
4 4  
5 5 import java.time.LocalDateTime;
  6 +import java.util.Collection;
6 7 import java.util.List;
7 8  
8 9 /**
... ... @@ -21,4 +22,13 @@ public interface RewardActivityApi {
21 22 */
22 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 15 ErrorCode DISCOUNT_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_001_002, "限时折扣活动已关闭,不能修改");
16 16 ErrorCode DISCOUNT_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED = new ErrorCode(1_013_001_003, "限时折扣活动未关闭,不能删除");
17 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 20 // ========== Banner 相关 1-013-002-000 ============
20 21 ErrorCode BANNER_NOT_EXISTS = new ErrorCode(1_013_002_000, "Banner 不存在");
... ... @@ -45,6 +46,7 @@ public interface ErrorCodeConstants {
45 46 ErrorCode REWARD_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED = new ErrorCode(1_013_006_003, "满减送活动未关闭,不能删除");
46 47 ErrorCode REWARD_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_006_004, "满减送活动已关闭,不能重复关闭");
47 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 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 24  
25 25 @Override
26 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 2  
3 3 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
4 4 import cn.iocoder.yudao.module.promotion.api.reward.dto.RewardActivityMatchRespDTO;
  5 +import cn.iocoder.yudao.module.promotion.convert.reward.RewardActivityConvert;
5 6 import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;
6 7 import cn.iocoder.yudao.module.promotion.service.reward.RewardActivityService;
7 8 import jakarta.annotation.Resource;
... ... @@ -9,6 +10,7 @@ import org.springframework.stereotype.Service;
9 10 import org.springframework.validation.annotation.Validated;
10 11  
11 12 import java.time.LocalDateTime;
  13 +import java.util.Collection;
12 14 import java.util.List;
13 15  
14 16 /**
... ... @@ -29,4 +31,10 @@ public class RewardActivityApiImpl implements RewardActivityApi {
29 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 2  
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.util.ObjectUtil;
  5 +import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
5 6 import cn.iocoder.yudao.framework.common.validation.InEnum;
6 7 import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;
7 8 import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
  9 +import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
8 10 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTemplateValidityTypeEnum;
9 11 import com.fasterxml.jackson.annotation.JsonFormat;
10 12 import com.fasterxml.jackson.annotation.JsonIgnore;
... ... @@ -12,6 +14,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
12 14 import lombok.Data;
13 15 import org.springframework.format.annotation.DateTimeFormat;
14 16  
  17 +import jakarta.validation.Validator;
15 18 import jakarta.validation.constraints.AssertTrue;
16 19 import jakarta.validation.constraints.Min;
17 20 import jakarta.validation.constraints.NotNull;
... ... @@ -37,11 +40,11 @@ public class CouponTemplateBaseVO {
37 40 private String description;
38 41  
39 42 @Schema(description = "发行总量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") // -1 - 则表示不限制发放数量
40   - @NotNull(message = "发行总量不能为空")
  43 + @NotNull(message = "发行总量不能为空", groups = {User.class})
41 44 private Integer totalCount;
42 45  
43 46 @Schema(description = "每人限领个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "66") // -1 - 则表示不限制
44   - @NotNull(message = "每人限领个数不能为空")
  47 + @NotNull(message = "每人限领个数不能为空", groups = {User.class})
45 48 private Integer takeLimitCount;
46 49  
47 50 @Schema(description = "领取方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
... ... @@ -89,13 +92,16 @@ public class CouponTemplateBaseVO {
89 92 private Integer discountType;
90 93  
91 94 @Schema(description = "折扣百分比", example = "80") // 例如说,80% 为 80
  95 + @NotNull(message = "折扣百分比不能为空", groups = {Percent.class})
92 96 private Integer discountPercent;
93 97  
94 98 @Schema(description = "优惠金额", example = "10")
95 99 @Min(value = 0, message = "优惠金额需要大于等于 0")
  100 + @NotNull(message = "优惠金额不能为空", groups = {Price.class})
96 101 private Integer discountPrice;
97 102  
98 103 @Schema(description = "折扣上限", example = "100") // 单位:分,仅在 discountType 为 PERCENT 使用
  104 + @NotNull(message = "折扣上限不能为空", groups = {Percent.class})
99 105 private Integer discountLimitPrice;
100 106  
101 107 @AssertTrue(message = "商品范围编号的数组不能为空")
... ... @@ -154,4 +160,54 @@ public class CouponTemplateBaseVO {
154 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 33  
34 34 import java.time.LocalDateTime;
35 35 import java.util.*;
  36 +import java.util.stream.Collectors;
36 37  
37 38 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
38 39 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
... ... @@ -150,36 +151,32 @@ public class AppActivityController {
150 151 }
151 152  
152 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 157 if (CollUtil.isEmpty(rewardActivityList)) {
157 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 60 @Operation(summary = "获得匹配指定商品的优惠劵列表", description = "用于下单页,展示优惠劵列表")
61 61 public CommonResult<List<AppCouponMatchRespVO>> getMatchCouponList(AppCouponMatchReqVO matchReqVO) {
62 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 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 4 import io.swagger.v3.oas.annotations.media.Schema;
5 5 import lombok.Data;
6 6  
  7 +import java.time.LocalDateTime;
7 8 import java.util.List;
8 9  
9 10 @Schema(description = "用户 App - 满减送活动 Response VO")
... ... @@ -26,9 +27,15 @@ public class AppRewardActivityRespVO {
26 27 private Integer productScope;
27 28  
28 29 @Schema(description = "商品 SPU 编号的数组", example = "1,2,3")
29   - private List<Long> productSpuIds;
  30 + private List<Long> productScopeValues;
30 31  
31 32 @Schema(description = "优惠规则的数组")
32 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 32  
33 33 CouponRespDTO convert(CouponDO bean);
34 34  
  35 + AppCouponMatchRespVO convert2(CouponDO bean);
  36 +
35 37 default CouponDO convert(CouponTemplateDO template, Long userId) {
36 38 CouponDO couponDO = new CouponDO()
37 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 121 default boolean isEquals(DiscountProductDO productDO, DiscountProductDO productVO) {
122 122 if (ObjectUtil.notEqual(productDO.getSpuId(), productVO.getSpuId())
123 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 128 return false;
126 129 }
127 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 85 }
86 86  
87 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 90 .eq(CouponDO::getUserId, userId)
94 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 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 70 .in(CouponTemplateDO::getTakeType, canTakeTypes) // 2. 领取方式一致
71 71 .and(ww -> ww.gt(CouponTemplateDO::getValidEndTime, LocalDateTime.now()) // 3.1 未过期
72 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 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 1 package cn.iocoder.yudao.module.promotion.dal.mysql.discount;
2 2  
3 3 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  4 +import cn.iocoder.yudao.module.promotion.api.discount.dto.DiscountProductRespDTO;
4 5 import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO;
5 6 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
6 7 import org.apache.ibatis.annotations.Mapper;
... ... @@ -31,7 +32,7 @@ public interface DiscountProductMapper extends BaseMapperX&lt;DiscountProductDO&gt; {
31 32 }
32 33  
33 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 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 1 package cn.iocoder.yudao.module.promotion.dal.mysql.reward;
2 2  
  3 +import cn.hutool.core.util.StrUtil;
3 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 5 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6 7 import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityPageReqVO;
7 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 11 import org.apache.ibatis.annotations.Mapper;
9 12  
10 13 import java.time.LocalDateTime;
  14 +import java.util.Collection;
11 15 import java.util.List;
  16 +import java.util.function.Function;
  17 +import java.util.stream.Collectors;
12 18  
13 19 /**
14 20 * 满减送活动 Mapper
... ... @@ -25,13 +31,48 @@ public interface RewardActivityMapper extends BaseMapperX&lt;RewardActivityDO&gt; {
25 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 51 default List<RewardActivityDO> selectListByStatusAndDateTimeLt(Integer status, LocalDateTime dateTime) {
29 52 return selectList(new LambdaQueryWrapperX<RewardActivityDO>()
30 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 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 51 Assert.isTrue(count > 0);
52 52 return update(null, new LambdaUpdateWrapper<SeckillActivityDO>()
53 53 .eq(SeckillActivityDO::getId, id)
54   - .gt(SeckillActivityDO::getStock, count)
  54 + .ge(SeckillActivityDO::getStock, count)
55 55 .setSql("stock = stock - " + count));
56 56 }
57 57  
... ... @@ -69,9 +69,11 @@ public interface SeckillActivityMapper extends BaseMapperX&lt;SeckillActivityDO&gt; {
69 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 73 return selectPage(pageReqVO, new LambdaQueryWrapperX<SeckillActivityDO>()
74 74 .eqIfPresent(SeckillActivityDO::getStatus, status)
  75 + .lt(SeckillActivityDO::getStartTime, dateTime)
  76 + .gt(SeckillActivityDO::getEndTime, dateTime)// 开始时间 < 指定时间 < 结束时间,也就是说获取指定时间段的活动
75 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 1 package cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillconfig;
2 2  
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
3 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 5 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6 7 import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config.SeckillConfigPageReqVO;
7 8 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillConfigDO;
  9 +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
8 10 import org.apache.ibatis.annotations.Mapper;
9 11  
  12 +import java.time.LocalDateTime;
  13 +import java.time.format.DateTimeFormatter;
10 14 import java.util.List;
11 15  
12 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 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 6 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
7 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 9 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
9 10 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
10 11 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
... ... @@ -178,7 +179,7 @@ public interface CouponService {
178 179 * @param matchReqVO 匹配参数
179 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 13 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
14 14 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
15 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 17 import cn.iocoder.yudao.module.promotion.convert.coupon.CouponConvert;
17 18 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
18 19 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
19 20 import cn.iocoder.yudao.module.promotion.dal.mysql.coupon.CouponMapper;
  21 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
20 22 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;
21 23 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
22 24 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTemplateValidityTypeEnum;
... ... @@ -286,6 +288,7 @@ public class CouponServiceImpl implements CouponService {
286 288 }
287 289 // 校验剩余数量(仅在 CouponTakeTypeEnum.USER 用户领取时)
288 290 if (CouponTakeTypeEnum.isUser(couponTemplate.getTakeCount())
  291 + && couponTemplate.getTotalCount() != null
289 292 && couponTemplate.getTakeCount() + userIds.size() > couponTemplate.getTotalCount()) {
290 293 throw exception(COUPON_TEMPLATE_NOT_ENOUGH);
291 294 }
... ... @@ -308,7 +311,7 @@ public class CouponServiceImpl implements CouponService {
308 311 * @param couponTemplate 优惠劵模版
309 312 */
310 313 private void removeTakeLimitUser(Set<Long> userIds, CouponTemplateDO couponTemplate) {
311   - if (couponTemplate.getTakeLimitCount() <= 0) {
  314 + if (couponTemplate.getTakeLimitCount() == null || couponTemplate.getTakeLimitCount() <= 0) {
312 315 return;
313 316 }
314 317 // 查询已领过券的用户
... ... @@ -356,13 +359,45 @@ public class CouponServiceImpl implements CouponService {
356 359 }
357 360  
358 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 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 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 13 import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
14 14 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
15 15 import org.springframework.stereotype.Service;
  16 +import jakarta.validation.Validator;
16 17 import org.springframework.validation.annotation.Validated;
17 18  
18 19 import jakarta.annotation.Resource;
... ... @@ -40,9 +41,13 @@ public class CouponTemplateServiceImpl implements CouponTemplateService {
40 41 private ProductCategoryApi productCategoryApi;
41 42 @Resource
42 43 private ProductSpuApi productSpuApi;
  44 + @Resource
  45 + private Validator validator;
43 46  
44 47 @Override
45 48 public Long createCouponTemplate(CouponTemplateCreateReqVO createReqVO) {
  49 + // 校验参数
  50 + createReqVO.validate(validator);
46 51 // 校验商品范围
47 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 1 package cn.iocoder.yudao.module.promotion.service.discount;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.promotion.api.discount.dto.DiscountProductRespDTO;
4 5 import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityCreateReqVO;
5 6 import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityPageReqVO;
6 7 import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityUpdateReqVO;
... ... @@ -27,7 +28,7 @@ public interface DiscountActivityService {
27 28 * @param skuIds SKU 编号数组
28 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 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
7 7 import cn.iocoder.yudao.framework.common.pojo.PageResult;
8 8 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  9 +import cn.iocoder.yudao.module.promotion.api.discount.dto.DiscountProductRespDTO;
9 10 import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityBaseVO;
10 11 import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityCreateReqVO;
11 12 import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityPageReqVO;
... ... @@ -49,7 +50,7 @@ public class DiscountActivityServiceImpl implements DiscountActivityService {
49 50 private DiscountProductMapper discountProductMapper;
50 51  
51 52 @Override
52   - public List<DiscountProductDO> getMatchDiscountProductList(Collection<Long> skuIds) {
  53 + public List<DiscountProductRespDTO> getMatchDiscountProductList(Collection<Long> skuIds) {
53 54 return discountProductMapper.getMatchDiscountProductList(skuIds);
54 55 }
55 56  
... ... @@ -130,7 +131,7 @@ public class DiscountActivityServiceImpl implements DiscountActivityService {
130 131 List<DiscountProductDO> list = discountProductMapper.selectListByActivityId(id);
131 132 // TODO @zhangshuai:一般简单的 stream 方法,建议是使用 CollectionUtils,例如说这里是 convertList 对把。
132 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 135 if (id != null) { // 排除自己这个活动
135 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 8 import jakarta.validation.Valid;
9 9  
10 10 import java.time.LocalDateTime;
  11 +import java.util.Collection;
11 12 import java.util.List;
12 13  
13 14 /**
... ... @@ -71,4 +72,14 @@ public interface RewardActivityService {
71 72 */
72 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 1 package cn.iocoder.yudao.module.promotion.service.reward;
2 2  
  3 +import cn.hutool.core.collection.CollUtil;
3 4 import cn.hutool.core.date.LocalDateTimeUtil;
4 5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
5 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 12 import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityCreateReqVO;
12 13 import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityPageReqVO;
13 14 import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityUpdateReqVO;
  15 +import cn.iocoder.yudao.module.promotion.convert.reward.RewardActivityConvert;
14 16 import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;
15 17 import cn.iocoder.yudao.module.promotion.dal.mysql.reward.RewardActivityMapper;
  18 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionActivityStatusEnum;
16 19 import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
17 20 import cn.iocoder.yudao.module.promotion.util.PromotionUtils;
18 21 import jakarta.annotation.Resource;
... ... @@ -20,8 +23,11 @@ import org.springframework.stereotype.Service;
20 23 import org.springframework.validation.annotation.Validated;
21 24  
22 25 import java.time.LocalDateTime;
  26 +import java.util.Collection;
  27 +import java.util.Collections;
23 28 import java.util.List;
24 29 import java.util.Objects;
  30 +import java.util.stream.Collectors;
25 31  
26 32 import static cn.hutool.core.collection.CollUtil.intersectionDistinct;
27 33 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
... ... @@ -52,9 +58,13 @@ public class RewardActivityServiceImpl implements RewardActivityService {
52 58 // 1.2 校验商品是否冲突
53 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 68 rewardActivityMapper.insert(rewardActivity);
59 69 // 返回
60 70 return rewardActivity.getId();
... ... @@ -199,4 +209,19 @@ public class RewardActivityServiceImpl implements RewardActivityService {
199 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 110 List<SeckillActivityDO> getSeckillActivityListByConfigIdAndStatus(Long configId, Integer status);
111 111  
112 112 /**
113   - * 通过活动时段获取秒杀活动
  113 + * 通过活动时段获取开始的秒杀活动
114 114 *
115 115 * @param pageReqVO 请求
116 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 23 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillProductDO;
24 24 import cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity.SeckillActivityMapper;
25 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 27 import org.springframework.stereotype.Service;
27 28 import org.springframework.transaction.annotation.Transactional;
28 29 import org.springframework.validation.annotation.Validated;
... ... @@ -57,6 +58,8 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
57 58 @Resource
58 59 private SeckillProductMapper seckillProductMapper;
59 60 @Resource
  61 + private SeckillConfigMapper seckillConfigMapper;
  62 + @Resource
60 63 private SeckillConfigService seckillConfigService;
61 64 @Resource
62 65 private ProductSpuApi productSpuApi;
... ... @@ -289,7 +292,7 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
289 292  
290 293 @Override
291 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 298 @Override
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/resources/mapper/discount/DiscountProductMapper.xml
... ... @@ -3,8 +3,8 @@
3 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 8 FROM promotion_discount_product pdp
9 9 LEFT JOIN promotion_discount_activity pda
10 10 ON pdp.activity_id = pda.id
... ... @@ -16,9 +16,11 @@
16 16 </foreach>
17 17 </if>
18 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 22 </where>
  23 + ORDER BY pdp.id DESC
22 24 </select>
23 25  
24 26 </mapper>
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java
1 1 package cn.iocoder.yudao.module.trade.controller.app.order;
2 2  
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
3 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 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 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 21 import cn.iocoder.yudao.module.trade.controller.app.order.vo.*;
8 22 import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemCommentCreateReqVO;
9 23 import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemRespVO;
... ... @@ -27,12 +41,14 @@ import lombok.extern.slf4j.Slf4j;
27 41 import org.springframework.validation.annotation.Validated;
28 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 48 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
34 49 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
35 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 53 @Tag(name = "用户 App - 交易订单")
38 54 @RestController
... ... @@ -54,6 +70,17 @@ public class AppTradeOrderController {
54 70 @Resource
55 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 84 @GetMapping("/settlement")
58 85 @Operation(summary = "获得订单结算信息")
59 86 @PreAuthenticated
... ... @@ -61,6 +88,58 @@ public class AppTradeOrderController {
61 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 143 @PostMapping("/create")
65 144 @Operation(summary = "创建订单")
66 145 @PreAuthenticated
... ... @@ -188,4 +267,78 @@ public class AppTradeOrderController {
188 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 1 package cn.iocoder.yudao.module.trade.controller.app.order.vo;
2 2  
3 3 import cn.iocoder.yudao.module.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO;
  4 +import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
4 5 import io.swagger.v3.oas.annotations.media.Schema;
5 6 import jakarta.validation.constraints.NotNull;
6 7 import lombok.AllArgsConstructor;
... ... @@ -31,6 +32,13 @@ public class AppTradeOrderSettlementRespVO {
31 32 @Schema(description = "总积分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
32 33 private Integer totalPoint;
33 34  
  35 + /**
  36 + * 营销活动数组
  37 + *
  38 + * 只对应 {@link TradePriceCalculateRespBO.Price#items} 商品匹配的活动
  39 + */
  40 + private List<TradePriceCalculateRespBO.Promotion> promotions;
  41 +
34 42 @Schema(description = "购物项")
35 43 @Data
36 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 43 @Mapping(source = "afterSale.orderId", target = "merchantOrderId"),
44 44 @Mapping(source = "afterSale.id", target = "merchantRefundId"),
45 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 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 371 @Override
372 372 public void afterCommit() {
373 373 // 创建退款单
374   - PayRefundCreateReqDTO createReqDTO = AfterSaleConvert.INSTANCE.convert(userIp, afterSale, tradeOrderProperties)
  374 + PayRefundCreateReqDTO createReqDTO = AfterSaleConvert.INSTANCE.convert(userIp, afterSale)
375 375 .setReason(StrUtil.format("退款【{}】", afterSale.getSpuName()));
  376 + createReqDTO.setAppKey(tradeOrderProperties.getPayAppKey());
376 377 Long payRefundId = payRefundApi.createRefund(createReqDTO);
377 378 // 更新售后单的退款单号
378 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 68 */
69 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 121 * @return 是否包邮
122 122 */
123 123 private boolean isGlobalExpressFree(TradePriceCalculateRespBO result) {
  124 +
124 125 TradeConfigDO config = tradeConfigService.getTradeConfig();
125   - return config != null
  126 + return result.getFreeDelivery() ||
  127 + (config != null
126 128 && Boolean.TRUE.equals(config.getDeliveryExpressFreeEnabled()) // 开启包邮
127   - && result.getPrice().getPayPrice() >= config.getDeliveryExpressFreePrice(); // 满足包邮的价格
  129 + && result.getPrice().getPayPrice() >= config.getDeliveryExpressFreePrice()
  130 + ); // 满足包邮的价格
128 131 }
129 132  
130 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 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.util.ObjectUtil;
5 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 10 import cn.iocoder.yudao.module.promotion.api.discount.DiscountActivityApi;
7 11 import cn.iocoder.yudao.module.promotion.api.discount.dto.DiscountProductRespDTO;
8 12 import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;
... ... @@ -14,6 +18,8 @@ import org.springframework.core.annotation.Order;
14 18 import org.springframework.stereotype.Component;
15 19  
16 20 import jakarta.annotation.Resource;
  21 +
  22 +import java.math.BigDecimal;
17 23 import java.util.List;
18 24 import java.util.Map;
19 25  
... ... @@ -32,6 +38,10 @@ public class TradeDiscountActivityPriceCalculator implements TradePriceCalculato
32 38  
33 39 @Resource
34 40 private DiscountActivityApi discountActivityApi;
  41 + @Resource
  42 + private MemberLevelApi memberLevelApi;
  43 + @Resource
  44 + private MemberUserApi memberUserApi;
35 45  
36 46 @Override
37 47 public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
... ... @@ -39,35 +49,101 @@ public class TradeDiscountActivityPriceCalculator implements TradePriceCalculato
39 49 if (ObjectUtil.notEqual(result.getType(), TradeOrderTypeEnum.NORMAL.getType())) {
40 50 return;
41 51 }
  52 +
  53 + boolean discount;
  54 + boolean vip;
  55 +
  56 + //----------------------------------限时折扣计算-----------------------------------------
42 57 // 获得 SKU 对应的限时折扣活动
43 58 List<DiscountProductRespDTO> discountProducts = discountActivityApi.getMatchDiscountProductList(
44 59 convertSet(result.getItems(), TradePriceCalculateRespBO.OrderItem::getSkuId));
45 60 if (CollUtil.isEmpty(discountProducts)) {
46   - return;
  61 + discount = false;
  62 + }else {
  63 + discount = true;
47 64 }
48 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 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 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 147 TradePriceCalculatorHelper.recountPayPrice(orderItem);
72 148 });
73 149 TradePriceCalculatorHelper.recountAllPrice(result);
... ... @@ -86,4 +162,20 @@ public class TradeDiscountActivityPriceCalculator implements TradePriceCalculato
86 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 30 @Resource
31 31 private MemberUserApi memberUserApi;
32 32  
  33 + /**
  34 + * 会员计算迁移到限时优惠计算里
  35 + * @param param
  36 + * @param result
  37 + */
33 38 @Override
34 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 23 import java.util.List;
24 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 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 30 import static cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculatorHelper.formatPrice;
28 31  
29 32 // TODO @puhui999:相关的单测,建议改一改
... ... @@ -47,14 +50,15 @@ public class TradeRewardActivityPriceCalculator implements TradePriceCalculator
47 50 return;
48 51 }
49 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 55 if (CollUtil.isEmpty(rewardActivities)) {
53 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 64 private void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result,
... ... @@ -77,6 +81,8 @@ public class TradeRewardActivityPriceCalculator implements TradePriceCalculator
77 81 Integer newDiscountPrice = rule.getDiscountPrice();
78 82 // 2.2 计算分摊的优惠金额
79 83 List<Integer> divideDiscountPrices = TradePriceCalculatorHelper.dividePrice(orderItems, newDiscountPrice);
  84 + //计算是否包邮
  85 + result.setFreeDelivery(rule.getFreeDelivery());
80 86  
81 87 // 3.1 记录使用的优惠劵
82 88 result.setCouponId(param.getCouponId());
... ... @@ -119,27 +125,24 @@ public class TradeRewardActivityPriceCalculator implements TradePriceCalculator
119 125 /**
120 126 * 获得满减送的订单项(商品)列表
121 127 *
122   - * @param result 计算结果
  128 + * @param result 计算结果
123 129 * @param rewardActivity 满减送活动
124 130 * @return 订单项(商品)列表
125 131 */
126 132 private List<TradePriceCalculateRespBO.OrderItem> filterMatchActivityOrderItems(TradePriceCalculateRespBO result,
127 133 RewardActivityMatchRespDTO rewardActivity) {
128   - // 情况一:全部商品都可以参与
129   - if (PromotionProductScopeEnum.isAll(rewardActivity.getProductScope())) {
  134 + Integer productScope = rewardActivity.getProductScope();
  135 + if(PromotionProductScopeEnum.isAll(productScope)){
130 136 return result.getItems();
131   - }
132   - // 情况二:指定商品参与
133   - if (PromotionProductScopeEnum.isSpu(rewardActivity.getProductScope())) {
  137 + }else if (PromotionProductScopeEnum.isSpu(productScope)) {
134 138 return filterList(result.getItems(),
135 139 orderItem -> CollUtil.contains(rewardActivity.getProductScopeValues(), orderItem.getSpuId()));
136   - }
137   - // 情况三:指定商品类型参与
138   - if (PromotionProductScopeEnum.isCategory(rewardActivity.getProductScope())) {
  140 + }else if (PromotionProductScopeEnum.isCategory(productScope)) {
139 141 return filterList(result.getItems(),
140 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 11 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
12 12 import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
13 13 import cn.iocoder.yudao.framework.datapermission.core.util.DataPermissionUtils;
  14 +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
14 15 import cn.iocoder.yudao.module.infra.api.config.ConfigApi;
15 16 import cn.iocoder.yudao.module.infra.api.file.FileApi;
16 17 import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthRegisterReqVO;
... ... @@ -104,6 +105,7 @@ public class AdminUserServiceImpl implements AdminUserService {
104 105 AdminUserDO user = BeanUtils.toBean(createReqVO, AdminUserDO.class);
105 106 user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启
106 107 user.setPassword(encodePassword(createReqVO.getPassword())); // 加密密码
  108 + user.setTenantId(TenantContextHolder.getRequiredTenantId());
107 109 userMapper.insert(user);
108 110 // 2.2 插入关联岗位
109 111 if (CollectionUtil.isNotEmpty(user.getPostIds())) {
... ...
yudao-server/src/main/resources/application-local.yaml
... ... @@ -6,7 +6,7 @@ spring:
6 6 # 数据源配置项
7 7 autoconfigure:
8 8 exclude:
9   - - org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration # 默认 local 环境,不开启 Quartz 的自动配置
  9 + #- org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration # 默认 local 环境,不开启 Quartz 的自动配置
10 10 - de.codecentric.boot.admin.server.config.AdminServerAutoConfiguration # 禁用 Spring Boot Admin 的 Server 的自动配置
11 11 - de.codecentric.boot.admin.server.ui.config.AdminServerUiAutoConfiguration # 禁用 Spring Boot Admin 的 Server UI 的自动配置
12 12 - de.codecentric.boot.admin.client.config.SpringBootAdminClientAutoConfiguration # 禁用 Spring Boot Admin 的 Client 的自动配置
... ... @@ -45,7 +45,7 @@ spring:
45 45 primary: master
46 46 datasource:
47 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 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 50 # url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例
51 51 # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例
... ... @@ -61,19 +61,19 @@ spring:
61 61 # password: SYSDBA001 # DM 连接的示例
62 62 # username: root # OpenGauss 连接的示例
63 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 70 # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
71 71 data:
72 72 redis:
73   - host: 127.0.0.1 # 地址
  73 + host: 192.168.10.207 # 地址
74 74 port: 6379 # 端口
75 75 database: 0 # 数据库索引
76   -# password: dev # 密码,建议生产环境开启
  76 + password: 123456 # 密码,建议生产环境开启
77 77  
78 78 --- #################### 定时任务相关配置 ####################
79 79  
... ... @@ -200,8 +200,8 @@ wx:
200 200 # secret: 6f270509224a7ae1296bbf1c8cb97aed
201 201 # appid: wxc4598c446f8a9cb3 # 测试号(Kongdy 提供的)
202 202 # secret: 4a1a04e07f6a4a0751b39c3064a92c8b
203   - appid: wx66186af0759f47c9 # 测试号(puhui 提供的)
204   - secret: 3218bcbd112cbc614c7264ceb20144ac
  203 + appid: wx9a0a5b259d852380 # 测试号(puhui 提供的)
  204 + secret: 70e65fa9d1a4f2c4e1b2aa8751d3b75e
205 205 config-storage:
206 206 type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取
207 207 key-prefix: wa # Redis Key 的前缀
... ...