Browse Source

优惠券:重构是否领取检查方式

owen 1 year ago
parent
commit
d2c5eaf916

+ 28 - 55
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/coupon/AppCouponTemplateController.java

@@ -1,6 +1,5 @@
 package cn.iocoder.yudao.module.promotion.controller.app.coupon;
 
-import cn.hutool.core.util.ObjUtil;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
@@ -25,11 +24,9 @@ import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
 import javax.annotation.Resource;
-import java.time.LocalDateTime;
 import java.util.*;
 
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
 import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
 
 @Tag(name = "用户 App - 优惠劵模板")
@@ -46,7 +43,6 @@ public class AppCouponTemplateController {
     @Resource
     private ProductSpuApi productSpuApi;
 
-    // TODO 疯狂:这里应该还有个 list 接口哈;获得优惠劵模版列表,用于首页、商品页的优惠劵
     @GetMapping("/list")
     @Operation(summary = "获得优惠劵模版列表")
     @Parameters({
@@ -56,46 +52,28 @@ public class AppCouponTemplateController {
     })
     public CommonResult<List<AppCouponTemplateRespVO>> getCouponTemplateList(
             @RequestParam(value = "spuId", required = false) Long spuId,
-            @RequestParam(value = "useType", required = false) Integer useType,
+            @RequestParam(value = "productScope", required = false) Integer productScope,
             @RequestParam(value = "count", required = false, defaultValue = "10") Integer count) {
-        List<AppCouponTemplateRespVO> list = new ArrayList<>();
-        Random random = new Random();
-        for (int i = 0; i < 10; i++) {
-            AppCouponTemplateRespVO vo = new AppCouponTemplateRespVO();
-            vo.setId(i + 1L);
-            vo.setName("优惠劵" + (i + 1));
-            vo.setTakeLimitCount(random.nextInt(10) + 1);
-            vo.setUsePrice(random.nextInt(100) * 100);
-            vo.setValidityType(random.nextInt(2) + 1);
-            if (vo.getValidityType() == 1) {
-                vo.setValidStartTime(LocalDateTime.now().plusDays(random.nextInt(10)));
-                vo.setValidEndTime(LocalDateTime.now().plusDays(random.nextInt(20) + 10));
-            } else {
-                vo.setFixedStartTerm(random.nextInt(10));
-                vo.setFixedEndTerm(random.nextInt(10) + vo.getFixedStartTerm() + 1);
-            }
-            vo.setDiscountType(random.nextInt(2) + 1);
-            if (vo.getDiscountType() == 1) {
-                vo.setDiscountPercent(null);
-                vo.setDiscountPrice(random.nextInt(50) * 100);
-                vo.setDiscountLimitPrice(null);
-            } else {
-                vo.setDiscountPercent(random.nextInt(90) + 10);
-                vo.setDiscountPrice(null);
-                vo.setDiscountLimitPrice(random.nextInt(200) * 100);
-            }
-            // TODO @疯狂:是否已领取,要不在 TemplateService 搞个 static 方法,让它基于 countMap 这种去计算,这样好点?
-            vo.setTakeStatus(random.nextBoolean());
-            list.add(vo);
-        }
-        return success(list);
+        // 1.1 处理查询条件:商品范围编号
+        Long productScopeValue = getProductScopeValue(productScope, spuId);
+        // 1.2 处理查询条件:领取方式 = 直接领取
+        List<Integer> canTakeTypes = Collections.singletonList(CouponTakeTypeEnum.USER.getValue());
+
+        // 2. 查询
+        List<CouponTemplateDO> list = couponTemplateService.getCouponTemplateList(canTakeTypes, productScope,
+                productScopeValue, count);
+
+        // 3.1 领取数量
+        Map<Long, Boolean> canCanTakeMap = couponService.getUserCanCanTakeMap(getLoginUserId(), list);
+        // 3.2 拼接返回
+        return success(CouponTemplateConvert.INSTANCE.convertAppList(list, canCanTakeMap));
     }
 
     @GetMapping("/page")
     @Operation(summary = "获得优惠劵模版分页")
     public CommonResult<PageResult<AppCouponTemplateRespVO>> getCouponTemplatePage(AppCouponTemplatePageReqVO pageReqVO) {
         // 1.1 处理查询条件:商品范围编号
-        Long productScopeValue = getProductScopeValue(pageReqVO);
+        Long productScopeValue = getProductScopeValue(pageReqVO.getProductScope(), pageReqVO.getSpuId());
         // 1.2 处理查询条件:领取方式 = 直接领取
         List<Integer> canTakeTypes = Collections.singletonList(CouponTakeTypeEnum.USER.getValue());
 
@@ -104,35 +82,30 @@ public class AppCouponTemplateController {
                 CouponTemplateConvert.INSTANCE.convert(pageReqVO, canTakeTypes, pageReqVO.getProductScope(), productScopeValue));
 
         // 3.1 领取数量
-        Map<Long, Integer> couponTakeCountMap = new HashMap<>(0);
-        Long userId = getLoginUserId();
-        if (userId != null) {
-            List<Long> templateIds = convertList(pageResult.getList(), CouponTemplateDO::getId,
-                    t -> ObjUtil.notEqual(t.getTakeLimitCount(), -1)); // 只查有设置“每人限领个数”的
-            couponTakeCountMap = couponService.getTakeCountMapByTemplateIds(templateIds, userId);
-        }
+        Map<Long, Boolean> canCanTakeMap = couponService.getUserCanCanTakeMap(getLoginUserId(), pageResult.getList());
         // 3.2 拼接返回
-        return success(CouponTemplateConvert.INSTANCE.convertAppPage(pageResult, couponTakeCountMap));
+        return success(CouponTemplateConvert.INSTANCE.convertAppPage(pageResult, canCanTakeMap));
     }
 
     /**
-     * 获得分页查询的商品范围
+     * 获得商品的使用范围编号
      *
-     * @param pageReqVO 分页查询
-     * @return 商品范围
+     * @param productScope 商品范围
+     * @param spuId        商品 SPU 编号
+     * @return 商品范围编号
      */
-    private Long getProductScopeValue(AppCouponTemplatePageReqVO pageReqVO) {
-        // 通用券:清除商品范围
-        if (pageReqVO.getProductScope() == null || ObjectUtils.equalsAny(pageReqVO.getProductScope(), PromotionProductScopeEnum.ALL.getScope(), null)) {
+    private Long getProductScopeValue(Integer productScope, Long spuId) {
+        // 通用券:没有商品范围
+        if (productScope == null || ObjectUtils.equalsAny(productScope, PromotionProductScopeEnum.ALL.getScope(), null)) {
             return null;
         }
-        // 品类券:查询商品的品类
-        if (Objects.equals(pageReqVO.getProductScope(), PromotionProductScopeEnum.CATEGORY.getScope()) && pageReqVO.getSpuId() != null) {
-            return Optional.ofNullable(productSpuApi.getSpu(pageReqVO.getSpuId()))
+        // 品类券:查询商品的品类编号
+        if (Objects.equals(productScope, PromotionProductScopeEnum.CATEGORY.getScope()) && spuId != null) {
+            return Optional.ofNullable(productSpuApi.getSpu(spuId))
                     .map(ProductSpuRespDTO::getCategoryId).orElse(null);
         }
         // 商品卷:直接返回
-        return pageReqVO.getSpuId();
+        return spuId;
     }
 
 }

+ 2 - 2
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/coupon/vo/template/AppCouponTemplateRespVO.java

@@ -55,7 +55,7 @@ public class AppCouponTemplateRespVO {
 
     // ========== 用户相关字段 ==========
 
-    @Schema(description = "是否领取", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
-    private Boolean takeStatus;
+    @Schema(description = "是否可以领取", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+    private Boolean canTake;
 
 }

+ 16 - 12
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/coupon/CouponTemplateConvert.java

@@ -37,21 +37,25 @@ public interface CouponTemplateConvert {
 
     PageResult<AppCouponTemplateRespVO> convertAppPage(PageResult<CouponTemplateDO> pageResult);
 
-    default PageResult<AppCouponTemplateRespVO> convertAppPage(PageResult<CouponTemplateDO> pageResult, Map<Long, Integer> couponTakeCountMap) {
+    List<AppCouponTemplateRespVO> convertAppList(List<CouponTemplateDO> list);
+
+    default PageResult<AppCouponTemplateRespVO> convertAppPage(PageResult<CouponTemplateDO> pageResult, Map<Long, Boolean> userCanTakeMap) {
         PageResult<AppCouponTemplateRespVO> result = convertAppPage(pageResult);
-        if (MapUtil.isEmpty(couponTakeCountMap)) {
-            return result;
-        }
-        for (AppCouponTemplateRespVO template : result.getList()) {
-            // 每人领取数量无限制
-            if (template.getTakeLimitCount() == -1) {
-                template.setTakeStatus(false);
-                continue;
-            }
+        copyTo(result.getList(), userCanTakeMap);
+        return result;
+    }
+
+    default List<AppCouponTemplateRespVO> convertAppList(List<CouponTemplateDO> list, Map<Long, Boolean> userCanTakeMap) {
+        List<AppCouponTemplateRespVO> result = convertAppList(list);
+        copyTo(result, userCanTakeMap);
+        return result;
+    }
+
+    default void copyTo(List<AppCouponTemplateRespVO> list, Map<Long, Boolean> userCanTakeMap) {
+        for (AppCouponTemplateRespVO template : list) {
             // 检查已领取数量是否超过限领数量
-            template.setTakeStatus(MapUtil.getInt(couponTakeCountMap, template.getId(), 0) >= template.getTakeLimitCount());
+            template.setCanTake(MapUtil.getBool(userCanTakeMap, template.getId(), false));
         }
-        return result;
     }
 
 }

+ 28 - 10
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponTemplateMapper.java

@@ -24,16 +24,8 @@ import java.util.function.Consumer;
 public interface CouponTemplateMapper extends BaseMapperX<CouponTemplateDO> {
 
     default PageResult<CouponTemplateDO> selectPage(CouponTemplatePageReqVO reqVO) {
-        // 构建可领取的查询条件, 好啰嗦  ( ╯-_-)╯┴—┴
-        Consumer<LambdaQueryWrapper<CouponTemplateDO>> canTakeConsumer = null;
-        if (CollUtil.isNotEmpty(reqVO.getCanTakeTypes())) {
-            canTakeConsumer = w ->
-                    w.eq(CouponTemplateDO::getStatus, CommonStatusEnum.ENABLE.getStatus()) // 1. 状态为可用的
-                            .in(CouponTemplateDO::getTakeType, reqVO.getCanTakeTypes()) // 2. 领取方式一致
-                            .and(ww -> ww.isNull(CouponTemplateDO::getValidEndTime)  // 3. 未过期
-                                    .or().gt(CouponTemplateDO::getValidEndTime, LocalDateTime.now()))
-                            .apply(" take_count < total_count "); // 4. 剩余数量大于 0
-        }
+        // 构建可领取的查询条件
+        Consumer<LambdaQueryWrapper<CouponTemplateDO>> canTakeConsumer = buildCanTakeQueryConsumer(reqVO.getCanTakeTypes());
         // 执行分页查询
         return selectPage(reqVO, new LambdaQueryWrapperX<CouponTemplateDO>()
                 .likeIfPresent(CouponTemplateDO::getName, reqVO.getName())
@@ -52,4 +44,30 @@ public interface CouponTemplateMapper extends BaseMapperX<CouponTemplateDO> {
     default List<CouponTemplateDO> selectListByTakeType(Integer takeType) {
         return selectList(CouponTemplateDO::getTakeType, takeType);
     }
+
+    default List<CouponTemplateDO> selectList(List<Integer> canTakeTypes, Integer productScope, Long productScopeValue, Integer count) {
+        // 构建可领取的查询条件
+        Consumer<LambdaQueryWrapper<CouponTemplateDO>> canTakeConsumer = buildCanTakeQueryConsumer(canTakeTypes);
+        return selectList(new LambdaQueryWrapperX<CouponTemplateDO>()
+                .eqIfPresent(CouponTemplateDO::getProductScope, productScope)
+                .and(productScopeValue != null, w -> w.apply("FIND_IN_SET({0}, product_scope_values)",
+                        productScopeValue))
+                .and(canTakeConsumer != null, canTakeConsumer)
+                .last(" LIMIT " + count)
+                .orderByDesc(CouponTemplateDO::getId));
+    }
+
+    static Consumer<LambdaQueryWrapper<CouponTemplateDO>> buildCanTakeQueryConsumer(List<Integer> canTakeTypes) {
+        Consumer<LambdaQueryWrapper<CouponTemplateDO>> canTakeConsumer = null;
+        if (CollUtil.isNotEmpty(canTakeTypes)) {
+            canTakeConsumer = w ->
+                    w.eq(CouponTemplateDO::getStatus, CommonStatusEnum.ENABLE.getStatus()) // 1. 状态为可用的
+                            .in(CouponTemplateDO::getTakeType, canTakeTypes) // 2. 领取方式一致
+                            .and(ww -> ww.isNull(CouponTemplateDO::getValidEndTime)  // 3. 未过期
+                                    .or().gt(CouponTemplateDO::getValidEndTime, LocalDateTime.now()))
+                            .apply(" take_count < total_count "); // 4. 剩余数量大于 0
+        }
+        return canTakeConsumer;
+    }
+
 }

+ 10 - 0
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.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.dal.dataobject.coupon.CouponDO;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
 import cn.iocoder.yudao.module.promotion.service.coupon.bo.CouponTakeCountBO;
 
@@ -175,4 +176,13 @@ public interface CouponService {
      */
     int expireCoupon();
 
+    /**
+     * 获取用户是否可以领取优惠券
+     *
+     * @param userId    用户编号
+     * @param templates 优惠券列表
+     * @return 是否可以领取
+     */
+    Map<Long, Boolean> getUserCanCanTakeMap(Long userId, List<CouponTemplateDO> templates);
+
 }

+ 24 - 1
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java

@@ -36,7 +36,7 @@ import java.util.Set;
 import java.util.stream.Collectors;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
 import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
 import static java.util.Arrays.asList;
 
@@ -229,6 +229,29 @@ public class CouponServiceImpl implements CouponService {
         return count;
     }
 
+    @Override
+    public Map<Long, Boolean> getUserCanCanTakeMap(Long userId, List<CouponTemplateDO> templates) {
+        Map<Long, Boolean> userCanTakeMap = convertMap(templates, CouponTemplateDO::getId, templateId -> true);
+        // 未登录时,都显示可以领取
+        if (userId == null) {
+            return userCanTakeMap;
+        }
+
+        // 过滤领取数量无限制的
+        Set<Long> templateIds = convertSet(templates, CouponTemplateDO::getId, template -> template.getTakeLimitCount() != -1);
+
+        // 检查用户领取的数量是否超过限制
+        if (CollUtil.isNotEmpty(templateIds)) {
+            Map<Long, Integer> couponTakeCountMap = this.getTakeCountMapByTemplateIds(templateIds, userId);
+            for (CouponTemplateDO template : templates) {
+                Integer takeCount = couponTakeCountMap.get(template.getId());
+                userCanTakeMap.put(template.getId(), takeCount == null || takeCount < template.getTakeLimitCount());
+            }
+        }
+
+        return userCanTakeMap;
+    }
+
     /**
      * 过期单个优惠劵
      *

+ 13 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponTemplateService.java

@@ -78,4 +78,17 @@ public interface CouponTemplateService {
      * @return 优惠券模板列表
      */
     List<CouponTemplateDO> getCouponTemplateByTakeType(CouponTakeTypeEnum takeType);
+
+    /**
+     * 获得优惠券模板列表
+     *
+     * @param canTakeTypes      可领取的类型列表
+     * @param productScope      商品使用范围类型
+     * @param productScopeValue 商品使用范围编号
+     * @param count             查询数量
+     * @return 优惠券模板列表
+     */
+    List<CouponTemplateDO> getCouponTemplateList(List<Integer> canTakeTypes, Integer productScope,
+                                                 Long productScopeValue, Integer count);
+
 }

+ 6 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponTemplateServiceImpl.java

@@ -120,4 +120,10 @@ public class CouponTemplateServiceImpl implements CouponTemplateService {
         return couponTemplateMapper.selectListByTakeType(takeType.getValue());
     }
 
+    @Override
+    public List<CouponTemplateDO> getCouponTemplateList(List<Integer> canTakeTypes, Integer productScope,
+                                                        Long productScopeValue, Integer count) {
+        return couponTemplateMapper.selectList(canTakeTypes, productScope, productScopeValue, count);
+    }
+
 }