diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.java index e4e497dba..61808fc66 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.java +++ b/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 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 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; diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppProductSpuRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppProductSpuRespVO.java index df61090bb..b08d4125a 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppProductSpuRespVO.java +++ b/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; diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/discount/dto/DiscountProductRespDTO.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/discount/dto/DiscountProductRespDTO.java index 52dfdbe27..7f143ec83 100644 --- a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/discount/dto/DiscountProductRespDTO.java +++ b/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; + } diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/reward/RewardActivityApi.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/reward/RewardActivityApi.java index 68f76a1fa..c703cdca0 100644 --- a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/reward/RewardActivityApi.java +++ b/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 getRewardActivityListByStatusAndNow(Integer status, LocalDateTime dateTime); + /** + * 获取指定 spu 编号最近参加的活动,每个 spuId 只返回一条记录 + * + * @param spuIds spu 编号 + * @param status 状态 + * @param dateTime 当前日期时间 + * @return 满减送活动列表 + */ + List getRewardActivityBySpuIdsAndStatusAndDateTimeLt(Collection spuIds, Integer status, LocalDateTime dateTime); } diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java index 319625387..5f785dccf 100644 --- a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java +++ b/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 ============ diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/discount/DiscountActivityApiImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/discount/DiscountActivityApiImpl.java index 82b8516f9..9c9d760d0 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/discount/DiscountActivityApiImpl.java +++ b/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 getMatchDiscountProductList(Collection skuIds) { - return DiscountActivityConvert.INSTANCE.convertList02(discountActivityService.getMatchDiscountProductList(skuIds)); + return discountActivityService.getMatchDiscountProductList(skuIds); } } diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/reward/RewardActivityApiImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/reward/RewardActivityApiImpl.java index ce3d1c802..6b33fdb7c 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/reward/RewardActivityApiImpl.java +++ b/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 getRewardActivityBySpuIdsAndStatusAndDateTimeLt(Collection spuIds, Integer status, LocalDateTime dateTime) { + List rewardActivityBySpuIdsAndStatusAndDateTimeLt = rewardActivityService.getRewardActivityBySpuIdsAndStatusAndDateTimeLt(spuIds, status, dateTime); + return RewardActivityConvert.INSTANCE.convertList(rewardActivityBySpuIdsAndStatusAndDateTimeLt); + } + } diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplateBaseVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplateBaseVO.java index 6885246b4..5b8a68f38 100755 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplateBaseVO.java +++ b/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); + } + + } + } diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/activity/AppActivityController.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/activity/AppActivityController.java index fae7fa54d..c2c028267 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/activity/AppActivityController.java +++ b/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 spuIds, LocalDateTime now, List activityList) { - // 1.1 获得所有的活动 - List rewardActivityList = rewardActivityService.getRewardActivityListByStatusAndDateTimeLt( - CommonStatusEnum.ENABLE.getStatus(), now); + // TODO @puhui999:有 3 范围,不只 spuId,还有 categoryId,全部,下次 fix + List rewardActivityList = rewardActivityService.getRewardActivityBySpuIdsAndStatusAndDateTimeLt( + spuIds, CommonStatusEnum.ENABLE.getStatus(), now); if (CollUtil.isEmpty(rewardActivityList)) { return; } - // 1.2 获得所有的商品信息 - List 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 fSpuIds = spuList.stream().map(ProductSpuRespDTO::getId).filter(id -> - rewardActivity.getProductScopeValues().contains(id)).toList(); - buildAppActivityRespVO(rewardActivity, fSpuIds, activityList); - } - // 情况三:指定商品类型参加 - if (PromotionProductScopeEnum.isCategory(rewardActivity.getProductScope())) { - List fSpuIds = spuList.stream().filter(spuItem -> rewardActivity.getProductScopeValues() - .contains(spuItem.getCategoryId())).map(ProductSpuRespDTO::getId).toList(); - buildAppActivityRespVO(rewardActivity, fSpuIds, activityList); + Map> 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())); } } diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/coupon/AppCouponController.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/coupon/AppCouponController.java index ed19d9141..f4235d9d6 100755 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/coupon/AppCouponController.java +++ b/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> getMatchCouponList(AppCouponMatchReqVO matchReqVO) { // todo: 优化:优惠金额倒序 - List list = couponService.getMatchCouponList(getLoginUserId(), matchReqVO); - return success(BeanUtils.toBean(list, AppCouponMatchRespVO.class)); + List list = couponService.getMatchCouponList(getLoginUserId(), matchReqVO); + return success(list); } @GetMapping("/page") diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/reward/vo/AppRewardActivityRespVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/reward/vo/AppRewardActivityRespVO.java index acaa5225d..f07bb9f64 100755 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/reward/vo/AppRewardActivityRespVO.java +++ b/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 productSpuIds; + private List productScopeValues; @Schema(description = "优惠规则的数组") private List rules; + @Schema(description = "开始时间") + private LocalDateTime startTime; + + @Schema(description = "结束时间") + private LocalDateTime endTime; + } diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/coupon/CouponConvert.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/coupon/CouponConvert.java index 542a77e84..eb76395f6 100755 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/coupon/CouponConvert.java +++ b/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()) diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/discount/DiscountActivityConvert.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/discount/DiscountActivityConvert.java index 0ecbd92ef..05063b918 100755 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/discount/DiscountActivityConvert.java +++ b/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())) { diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/reward/RewardActivityConvert.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/reward/RewardActivityConvert.java new file mode 100644 index 000000000..2f03f2aae --- /dev/null +++ b/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 convertPage(PageResult page); + + List convertList(List rewardActivityBySpuIdsAndStatusAndDateTimeLt); +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponMapper.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponMapper.java index e5f1daf6c..47341c2fa 100755 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponMapper.java +++ b/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 { } default List selectListByUserIdAndStatusAndUsePriceLeAndProductScope( - Long userId, Integer status, Integer usePrice, List spuIds, List categoryIds) { - Function, String> productScopeValuesFindInSetFunc = ids -> ids.stream() - .map(id -> StrUtil.format("FIND_IN_SET({}, product_scope_values) ", id)) - .collect(Collectors.joining(" OR ")); - return selectList(new LambdaQueryWrapperX() + Long userId, Integer status) { + List couponDOS = selectList(new LambdaQueryWrapperX() .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 selectListByStatusAndValidEndTimeLe(Integer status, LocalDateTime validEndTime) { diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponTemplateMapper.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponTemplateMapper.java index 50e3c0315..dfd8c5b3b 100755 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponTemplateMapper.java +++ b/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 { .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; } diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/discount/DiscountProductMapper.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/discount/DiscountProductMapper.java index 5257b836d..263afe5e4 100755 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/discount/DiscountProductMapper.java +++ b/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 { } // TODO @zhangshuai:逻辑里,尽量避免写 join 语句哈,你可以看看这个查询,有什么办法优化?目前的一个思路,是分 2 次查询,性能也是 ok 的 - List getMatchDiscountProductList(@Param("skuIds") Collection skuIds); + List getMatchDiscountProductList(@Param("skuIds") Collection skuIds); /** * 查询出指定 spuId 的 spu 参加的活动最接近现在的一条记录。多个的话,一个 spuId 对应一个最近的活动编号 diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/reward/RewardActivityMapper.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/reward/RewardActivityMapper.java index 6f377fb60..5abbc9265 100755 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/reward/RewardActivityMapper.java +++ b/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 { .orderByDesc(RewardActivityDO::getId)); } + default List selectListBySpuIdsAndStatus(Collection spuIds, Integer status) { + Function, String> productScopeValuesFindInSetFunc = ids -> ids.stream() + .map(id -> StrUtil.format("FIND_IN_SET({}, product_scope_values) ", id)) + .collect(Collectors.joining(" OR ")); + return selectList(new QueryWrapper() + .eq("status", status) + .apply(productScopeValuesFindInSetFunc.apply(spuIds))); + } + + /** + * 获取指定活动编号的活动列表且 + * 开始时间和结束时间小于给定时间 dateTime 的活动列表 + * + * @param status 状态 + * @param dateTime 指定日期 + * @return 活动列表 + */ default List selectListByStatusAndDateTimeLt(Integer status, LocalDateTime dateTime) { return selectList(new LambdaQueryWrapperX() .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 getRewardActivityByStatusAndDateTimeLt(Collection spuIds,Collection categoryIds, Integer status, LocalDateTime dateTime) { + //拼接通用券查询语句 + Function, String> productScopeValuesFindInSetFunc = ids -> ids.stream() + .map(id -> StrUtil.format("FIND_IN_SET({}, product_scope_values) ", id)) + .collect(Collectors.joining(" OR ")); + return selectList(new LambdaQueryWrapperX() + .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) + ); + } + } diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillactivity/SeckillActivityMapper.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillactivity/SeckillActivityMapper.java index 0b68609c9..3d30111fd 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillactivity/SeckillActivityMapper.java +++ b/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 { Assert.isTrue(count > 0); return update(null, new LambdaUpdateWrapper() .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 { .setSql("stock = stock + " + count)); } - default PageResult selectPage(AppSeckillActivityPageReqVO pageReqVO, Integer status) { + default PageResult selectPage(AppSeckillActivityPageReqVO pageReqVO, Integer status, LocalDateTime dateTime) { return selectPage(pageReqVO, new LambdaQueryWrapperX() .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")); } diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillconfig/SeckillConfigMapper.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillconfig/SeckillConfigMapper.java index f1dcaca32..1d205189f 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillconfig/SeckillConfigMapper.java +++ b/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 diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponService.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponService.java index 5fdcd0669..27c392ba4 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponService.java +++ b/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 getMatchCouponList(Long userId, AppCouponMatchReqVO matchReqVO); + List getMatchCouponList(Long userId, AppCouponMatchReqVO matchReqVO); /** * 获取用户是否可以领取优惠券 diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java index 3945aa06b..c01d28849 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java +++ b/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 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 getMatchCouponList(Long userId, AppCouponMatchReqVO matchReqVO) { + public List getMatchCouponList(Long userId, AppCouponMatchReqVO matchReqVO) { + List couponMatchist = new ArrayList<>(); List 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 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 diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponTemplateServiceImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponTemplateServiceImpl.java index 019c45dae..360787978 100755 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponTemplateServiceImpl.java +++ b/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()); // 插入 diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountActivityService.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountActivityService.java index e08c7e2b5..42f27f6de 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountActivityService.java +++ b/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 getMatchDiscountProductList(Collection skuIds); + List getMatchDiscountProductList(Collection skuIds); /** * 创建限时折扣活动 diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountActivityServiceImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountActivityServiceImpl.java index 0c995267b..95694d52f 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountActivityServiceImpl.java +++ b/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 getMatchDiscountProductList(Collection skuIds) { + public List getMatchDiscountProductList(Collection skuIds) { return discountProductMapper.getMatchDiscountProductList(skuIds); } @@ -130,7 +131,7 @@ public class DiscountActivityServiceImpl implements DiscountActivityService { List list = discountProductMapper.selectListByActivityId(id); // TODO @zhangshuai:一般简单的 stream 方法,建议是使用 CollectionUtils,例如说这里是 convertList 对把。 List skuIds = list.stream().map(item -> item.getSkuId()).collect(Collectors.toList()); - List matchDiscountProductList = getMatchDiscountProductList(skuIds); + List matchDiscountProductList = getMatchDiscountProductList(skuIds); if (id != null) { // 排除自己这个活动 matchDiscountProductList.removeIf(product -> id.equals(product.getActivityId())); } diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/reward/RewardActivityService.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/reward/RewardActivityService.java index d35e0874a..ebfce39c3 100755 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/reward/RewardActivityService.java +++ b/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 getRewardActivityListByStatusAndDateTimeLt(Integer status, LocalDateTime dateTime); + /** + * 获取指定 spu 编号最近参加的活动,每个 spuId 只返回一条记录 + * + * @param spuIds SPU 编号数组 + * @param status 状态 + * @param dateTime 当前日期时间 + * @return 满减送活动列表 + */ + List getRewardActivityBySpuIdsAndStatusAndDateTimeLt(Collection spuIds, Integer status, LocalDateTime dateTime); + } diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/reward/RewardActivityServiceImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/reward/RewardActivityServiceImpl.java index b475abce8..96ad87b2f 100755 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/reward/RewardActivityServiceImpl.java +++ b/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 getRewardActivityBySpuIdsAndStatusAndDateTimeLt(Collection spuIds, Integer status, LocalDateTime dateTime) { + List spuList = productSpuApi.validateSpuList(spuIds); + //查询出商品的分类ids + List categoryIds = spuList.stream().map(ProductSpuRespDTO::getCategoryId).collect(Collectors.toList()); + // 1. 查询出指定 spuId 的 spu 参加的活动 + List rewardActivityList = rewardActivityMapper.getRewardActivityByStatusAndDateTimeLt(spuIds, categoryIds,status,dateTime); + if (CollUtil.isEmpty(rewardActivityList)) { + return Collections.emptyList(); + } + + // 2. 查询活动详情 + return rewardActivityList; + } + } diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityService.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityService.java index a47bbec7c..3112a3b80 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityService.java +++ b/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 getSeckillActivityListByConfigIdAndStatus(Long configId, Integer status); /** - * 通过活动时段获取秒杀活动 + * 通过活动时段获取开始的秒杀活动 * * @param pageReqVO 请求 * @return 秒杀活动列表 diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityServiceImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityServiceImpl.java index dff4d7c7b..e79429857 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityServiceImpl.java +++ b/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 getSeckillActivityAppPageByConfigId(AppSeckillActivityPageReqVO pageReqVO) { - return seckillActivityMapper.selectPage(pageReqVO, CommonStatusEnum.ENABLE.getStatus()); + return seckillActivityMapper.selectPage(pageReqVO, CommonStatusEnum.ENABLE.getStatus(),LocalDateTime.now()); } @Override diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/resources/mapper/discount/DiscountProductMapper.xml b/yudao-module-mall/yudao-module-promotion-biz/src/main/resources/mapper/discount/DiscountProductMapper.xml index 76af37db2..762ae1358 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/resources/mapper/discount/DiscountProductMapper.xml +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/resources/mapper/discount/DiscountProductMapper.xml @@ -3,8 +3,8 @@ - + 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 @@ AND pda.start_time <= CURRENT_TIME AND pda.end_time >= CURRENT_TIME - AND pda.`status` = 20 - AND pda.deleted != 1 + AND pda.`status` = 0 + AND pda.deleted =0 + AND pdp.deleted = 0 + ORDER BY pdp.id DESC diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java index b1280d8c1..dcf380981 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java +++ b/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> settlementProduct(@RequestParam("ids") Set ids) { + List appTradeProductSettlementRespVOS = new ArrayList<>(); + MemberLevelRespDTO memberLevel = getMemberLevel(); + ids.forEach(spuId -> { + List skus = new ArrayList<>(); + List 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 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 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; + } + } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementRespVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementRespVO.java index 9aab1b68b..576703794 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementRespVO.java +++ b/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 promotions; + @Schema(description = "购物项") @Data public static class Item { diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeProductSettlementRespVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeProductSettlementRespVO.java new file mode 100644 index 000000000..773b49617 --- /dev/null +++ b/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 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; + + } +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/aftersale/AfterSaleConvert.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/aftersale/AfterSaleConvert.java index 086cb6370..b1f3b2782 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/aftersale/AfterSaleConvert.java +++ b/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); diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/AfterSaleServiceImpl.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/AfterSaleServiceImpl.java index df3d2db60..2a332aad2 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/AfterSaleServiceImpl.java +++ b/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)); diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateRespBO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateRespBO.java index 4f65f33d1..2ad327208 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateRespBO.java +++ b/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; /** * 赠送的优惠劵 diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDeliveryPriceCalculator.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDeliveryPriceCalculator.java index 8c0829f9a..5152b3edc 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDeliveryPriceCalculator.java +++ b/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 selectedSkus, diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDiscountActivityPriceCalculator.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDiscountActivityPriceCalculator.java index 844d5266e..37869b6f7 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDiscountActivityPriceCalculator.java +++ b/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 discountProducts = discountActivityApi.getMatchDiscountProductList( convertSet(result.getItems(), TradePriceCalculateRespBO.OrderItem::getSkuId)); if (CollUtil.isEmpty(discountProducts)) { - return; + discount = false; + }else { + discount = true; } Map discountProductMap = convertMap(discountProducts, DiscountProductRespDTO::getSkuId); - // 处理每个 SKU 的限时折扣 - result.getItems().forEach(orderItem -> { - // 1. 获取该 SKU 的优惠信息 - DiscountProductRespDTO discountProduct = discountProductMap.get(orderItem.getSkuId()); - if (discountProduct == null) { - return; - } - // 2. 计算优惠金额 - Integer newPayPrice = calculatePayPrice(discountProduct, orderItem); - Integer newDiscountPrice = orderItem.getPayPrice() - newPayPrice; - // 3.1 记录优惠明细 - if (orderItem.getSelected()) { - // 注意,只有在选中的情况下,才会记录到优惠明细。否则仅仅是更新 SKU 优惠金额,用于展示 - TradePriceCalculatorHelper.addPromotion(result, orderItem, - discountProduct.getActivityId(), discountProduct.getActivityName(), PromotionTypeEnum.DISCOUNT_ACTIVITY.getType(), - StrUtil.format("限时折扣:省 {} 元", formatPrice(newDiscountPrice)), - newDiscountPrice); + + //----------------------------------会员计算----------------------------------------- + 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; } - // 3.2 更新 SKU 优惠金额 - orderItem.setDiscountPrice(orderItem.getDiscountPrice() + newDiscountPrice); + }else { + level = null; + vip = false; + } + + + // 2. 计算每个 SKU 的优惠金额 + result.getItems().forEach(orderItem -> { + + //----------------------------------限时折扣计算----------------------------------------- + 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.4 记录优惠明细 + // 注意,只有在选中的情况下,才会记录到优惠明细。否则仅仅是更新 SKU 优惠金额,用于展示 + if (orderItem.getSelected()) { + 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); + } + } + 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; + } + } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeMemberLevelPriceCalculator.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeMemberLevelPriceCalculator.java index f4a535065..26fb6721a 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeMemberLevelPriceCalculator.java +++ b/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); } /** diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeRewardActivityPriceCalculator.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeRewardActivityPriceCalculator.java index 261eefd68..a6fd1bbcd 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeRewardActivityPriceCalculator.java +++ b/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 rewardActivities = rewardActivityApi.getRewardActivityListByStatusAndNow( - CommonStatusEnum.ENABLE.getStatus(), LocalDateTime.now()); + List 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 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 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(); } /** diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java index cb9a73ff7..0dc6fa8d1 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java +++ b/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())) { diff --git a/yudao-server/src/main/resources/application-local.yaml b/yudao-server/src/main/resources/application-local.yaml index 40c0919b7..86cfbda91 100644 --- a/yudao-server/src/main/resources/application-local.yaml +++ b/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 的前缀