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

This commit is contained in:
owen 2023-09-30 22:05:11 +08:00
parent aaa01de21d
commit d2c5eaf916
8 changed files with 128 additions and 81 deletions

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;
}
// 检查已领取数量是否超过限领数量
template.setTakeStatus(MapUtil.getInt(couponTakeCountMap, template.getId(), 0) >= template.getTakeLimitCount());
}
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.setCanTake(MapUtil.getBool(userCanTakeMap, template.getId(), false));
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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;
}
/**
* 过期单个优惠劵
*

View File

@ -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);
}

View File

@ -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);
}
}