mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2024-11-22 23:31:52 +08:00
promotion:增加获得优惠劵匹配结果的列表
This commit is contained in:
parent
fb45f22533
commit
edc5d0f1d0
@ -135,4 +135,19 @@ public class DateUtils {
|
||||
return DateUtil.isSameDay(date, new Date());
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前时间是否在该时间范围内
|
||||
*
|
||||
* @param startTime 开始时间
|
||||
* @param endTime 结束时间
|
||||
* @return 是否
|
||||
*/
|
||||
public static boolean isBetween(Date startTime, Date endTime) {
|
||||
if (startTime == null || endTime == null) {
|
||||
return false;
|
||||
}
|
||||
return startTime.getTime() <= System.currentTimeMillis()
|
||||
&& endTime.getTime() >= System.currentTimeMillis();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,35 @@
|
||||
package cn.iocoder.yudao.module.promotion.api.price.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 优惠劵的匹配信息 Response DTO
|
||||
*
|
||||
* why 放在 price 包下?主要获取的时候,需要涉及到较多的价格计算逻辑,放在 price 可以更好的复用逻辑
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
public class CouponMeetRespDTO {
|
||||
|
||||
/**
|
||||
* 优惠劵编号
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
// ========== 非优惠劵的基本信息字段 ==========
|
||||
/**
|
||||
* 是否匹配
|
||||
*/
|
||||
private Boolean meet;
|
||||
/**
|
||||
* 不匹配的提示,即 {@link #meet} = true 才有值
|
||||
*
|
||||
* 例如说:
|
||||
* 1. 所结算商品没有符合条件的商品
|
||||
* 2. 差 XXX 元可用优惠劵
|
||||
* 3. 优惠劵未到使用时间
|
||||
*/
|
||||
private String meetTip;
|
||||
|
||||
}
|
@ -31,6 +31,8 @@ public interface ErrorCodeConstants {
|
||||
// ========== 优惠劵模板 1003005000 ==========
|
||||
ErrorCode COUPON_NOT_EXISTS = new ErrorCode(1003005000, "优惠劵不存在");
|
||||
ErrorCode COUPON_DELETE_FAIL_USED = new ErrorCode(1003005001, "回收优惠劵失败,优惠劵已被使用");
|
||||
ErrorCode COUPON_STATUS_NOT_UNUSED = new ErrorCode(1006003003, "优惠劵不处于待使用状态");
|
||||
ErrorCode COUPON_VALID_TIME_NOT_NOW = new ErrorCode(1006003004, "优惠劵不在有效期内");
|
||||
|
||||
// ========== 满减送活动 1003006000 ==========
|
||||
ErrorCode REWARD_ACTIVITY_NOT_EXISTS = new ErrorCode(1003006000, "满减送活动不存在");
|
||||
|
@ -1,9 +1,11 @@
|
||||
package cn.iocoder.yudao.module.promotion.convert.price;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.module.promotion.api.price.dto.CouponMeetRespDTO;
|
||||
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO;
|
||||
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO;
|
||||
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@ -42,4 +44,6 @@ public interface PriceConvert {
|
||||
return priceCalculate;
|
||||
}
|
||||
|
||||
CouponMeetRespDTO convert(CouponDO coupon);
|
||||
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 优惠劵 Mapper
|
||||
@ -27,10 +28,25 @@ public interface CouponMapper extends BaseMapperX<CouponDO> {
|
||||
.orderByDesc(CouponDO::getId));
|
||||
}
|
||||
|
||||
default List<CouponDO> selectListByUserIdAndStatus(Long userId, Integer status) {
|
||||
return selectList(new LambdaQueryWrapperX<CouponDO>()
|
||||
.eq(CouponDO::getUserId, userId).eq(CouponDO::getStatus, status));
|
||||
}
|
||||
|
||||
default CouponDO selectByIdAndUserId(Long id, Long userId) {
|
||||
return selectOne(new LambdaQueryWrapperX<CouponDO>()
|
||||
.eq(CouponDO::getId, id).eq(CouponDO::getUserId, userId));
|
||||
}
|
||||
|
||||
default int delete(Long id, Collection<Integer> whereStatuses) {
|
||||
return update(null, new LambdaUpdateWrapper<CouponDO>()
|
||||
.eq(CouponDO::getId, id).in(CouponDO::getStatus, whereStatuses)
|
||||
.set(CouponDO::getDeleted, 1));
|
||||
}
|
||||
|
||||
default int updateByIdAndStatus(Long id, Integer status, CouponDO updateObj) {
|
||||
return update(updateObj, new LambdaUpdateWrapper<CouponDO>()
|
||||
.eq(CouponDO::getId, id).eq(CouponDO::getStatus, status));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ 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.dal.dataobject.coupon.CouponDO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 优惠劵 Service 接口
|
||||
*
|
||||
@ -23,6 +25,15 @@ public interface CouponService {
|
||||
*/
|
||||
CouponDO validCoupon(Long id, Long userId);
|
||||
|
||||
/**
|
||||
* 校验优惠劵,包括状态、有限期
|
||||
*
|
||||
* @see #validCoupon(Long, Long) 逻辑相同,只是入参不同
|
||||
*
|
||||
* @param coupon 优惠劵
|
||||
*/
|
||||
void validCoupon(CouponDO coupon);
|
||||
|
||||
/**
|
||||
* 获得优惠劵分页
|
||||
*
|
||||
@ -31,6 +42,14 @@ public interface CouponService {
|
||||
*/
|
||||
PageResult<CouponDO> getCouponPage(CouponPageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 使用优惠劵
|
||||
*
|
||||
* @param id 优惠劵编号
|
||||
* @param userId 用户编号
|
||||
*/
|
||||
void useCoupon(Long id, Long userId);
|
||||
|
||||
/**
|
||||
* 回收优惠劵
|
||||
*
|
||||
@ -38,4 +57,13 @@ public interface CouponService {
|
||||
*/
|
||||
void deleteCoupon(Long id);
|
||||
|
||||
/**
|
||||
* 获得用户的优惠劵列表
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param status 优惠劵状态
|
||||
* @return 优惠劵列表
|
||||
*/
|
||||
List<CouponDO> getCouponList(Long userId, Integer status);
|
||||
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
package cn.iocoder.yudao.module.promotion.service.coupon;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
|
||||
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
|
||||
import cn.iocoder.yudao.module.member.api.user.dto.UserRespDTO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
|
||||
@ -15,11 +17,12 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COUPON_DELETE_FAIL_USED;
|
||||
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COUPON_NOT_EXISTS;
|
||||
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
/**
|
||||
@ -40,10 +43,26 @@ public class CouponServiceImpl implements CouponService {
|
||||
@Resource
|
||||
private MemberUserApi memberUserApi;
|
||||
|
||||
// TODO 芋艿:待实现
|
||||
@Override
|
||||
public CouponDO validCoupon(Long id, Long userId) {
|
||||
return null;
|
||||
CouponDO coupon = couponMapper.selectByIdAndUserId(id, userId);
|
||||
if (coupon == null) {
|
||||
throw exception(COUPON_NOT_EXISTS);
|
||||
}
|
||||
validCoupon(coupon);
|
||||
return coupon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validCoupon(CouponDO coupon) {
|
||||
// 校验状态
|
||||
if (ObjectUtil.notEqual(coupon.getStatus(), CouponStatusEnum.UNUSED.getStatus())) {
|
||||
throw exception(COUPON_STATUS_NOT_UNUSED);
|
||||
}
|
||||
// 校验有效期;为避免定时器没跑,实际优惠劵已经过期
|
||||
if (DateUtils.isBetween(coupon.getValidStartTime(), coupon.getValidEndTime())) {
|
||||
throw exception(COUPON_VALID_TIME_NOT_NOW);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -61,6 +80,18 @@ public class CouponServiceImpl implements CouponService {
|
||||
return couponMapper.selectPage(pageReqVO, userIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void useCoupon(Long id, Long userId) {
|
||||
// 校验优惠劵
|
||||
validCoupon(id, userId);
|
||||
// 更新状态
|
||||
int updateCount = couponMapper.updateByIdAndStatus(id, CouponStatusEnum.UNUSED.getStatus(),
|
||||
new CouponDO().setStatus(CouponStatusEnum.USED.getStatus()).setUseTime(new Date()));
|
||||
if (updateCount == 0) {
|
||||
throw exception(COUPON_STATUS_NOT_UNUSED);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void deleteCoupon(Long id) {
|
||||
@ -77,6 +108,11 @@ public class CouponServiceImpl implements CouponService {
|
||||
couponTemplateService.updateCouponTemplateTakeCount(id, -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CouponDO> getCouponList(Long userId, Integer status) {
|
||||
return couponMapper.selectListByUserIdAndStatus(userId, status);
|
||||
}
|
||||
|
||||
private void validateCouponExists(Long id) {
|
||||
if (couponMapper.selectById(id) == null) {
|
||||
throw exception(COUPON_NOT_EXISTS);
|
||||
|
@ -1,8 +1,11 @@
|
||||
package cn.iocoder.yudao.module.promotion.service.price;
|
||||
|
||||
import cn.iocoder.yudao.module.promotion.api.price.dto.CouponMeetRespDTO;
|
||||
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO;
|
||||
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 价格计算 Service 接口
|
||||
*
|
||||
@ -14,8 +17,16 @@ public interface PriceService {
|
||||
* 计算商品的价格
|
||||
*
|
||||
* @param calculateReqDTO 价格请求
|
||||
* @return 价格相应
|
||||
* @return 价格响应
|
||||
*/
|
||||
PriceCalculateRespDTO calculatePrice(PriceCalculateReqDTO calculateReqDTO);
|
||||
|
||||
/**
|
||||
* 获得优惠劵的匹配信息列表
|
||||
*
|
||||
* @param calculateReqDTO 价格请求
|
||||
* @return 价格响应
|
||||
*/
|
||||
List<CouponMeetRespDTO> getMeetCouponList(PriceCalculateReqDTO calculateReqDTO);
|
||||
|
||||
}
|
||||
|
@ -3,15 +3,18 @@ package cn.iocoder.yudao.module.promotion.service.price;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
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.price.dto.CouponMeetRespDTO;
|
||||
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO;
|
||||
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO;
|
||||
import cn.iocoder.yudao.module.promotion.convert.price.PriceConvert;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;
|
||||
import cn.iocoder.yudao.module.promotion.enums.common.*;
|
||||
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;
|
||||
import cn.iocoder.yudao.module.promotion.service.coupon.CouponService;
|
||||
import cn.iocoder.yudao.module.promotion.service.discount.DiscountActivityService;
|
||||
import cn.iocoder.yudao.module.promotion.service.discount.bo.DiscountProductDetailBO;
|
||||
@ -22,10 +25,7 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
@ -87,6 +87,52 @@ public class PriceServiceImpl implements PriceService {
|
||||
return priceCalculate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CouponMeetRespDTO> getMeetCouponList(PriceCalculateReqDTO calculateReqDTO) {
|
||||
// 先计算一轮价格
|
||||
PriceCalculateRespDTO priceCalculate = calculatePrice(calculateReqDTO);
|
||||
|
||||
// 获得用户的待使用优惠劵
|
||||
List<CouponDO> couponList = couponService.getCouponList(calculateReqDTO.getUserId(), CouponStatusEnum.UNUSED.getStatus());
|
||||
if (CollUtil.isEmpty(couponList)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// 获得优惠劵的匹配信息
|
||||
return CollectionUtils.convertList(couponList, coupon -> {
|
||||
CouponMeetRespDTO couponMeetRespDTO = PriceConvert.INSTANCE.convert(coupon);
|
||||
try {
|
||||
// 校验优惠劵
|
||||
couponService.validCoupon(coupon);
|
||||
|
||||
// 获得匹配的商品 SKU 数组
|
||||
List<PriceCalculateRespDTO.OrderItem> orderItems = getMatchCouponOrderItems(priceCalculate, coupon);
|
||||
if (CollUtil.isEmpty(orderItems)) {
|
||||
return couponMeetRespDTO.setMeet(false).setMeetTip("所结算商品没有符合条件的商品");
|
||||
}
|
||||
|
||||
// 计算是否满足优惠劵的使用金额
|
||||
Integer originPrice = getSumValue(orderItems, PriceCalculateRespDTO.OrderItem::getOrderDividePrice, Integer::sum);
|
||||
assert originPrice != null;
|
||||
if (originPrice < coupon.getUsePrice()) {
|
||||
return couponMeetRespDTO.setMeet(false)
|
||||
.setMeetTip(String.format("差 %s 元可用优惠劵", formatPrice(coupon.getUsePrice() - originPrice)));
|
||||
}
|
||||
} catch (ServiceException serviceException) {
|
||||
couponMeetRespDTO.setMeet(false);
|
||||
if (serviceException.getCode().equals(COUPON_VALID_TIME_NOT_NOW.getCode())) {
|
||||
couponMeetRespDTO.setMeetTip("优惠劵未到使用时间");
|
||||
} else {
|
||||
log.error("[getMeetCouponList][calculateReqDTO({}) 获得优惠劵匹配信息异常]", calculateReqDTO, serviceException);
|
||||
couponMeetRespDTO.setMeetTip("优惠劵不满足使用条件");
|
||||
}
|
||||
return couponMeetRespDTO;
|
||||
}
|
||||
// 满足
|
||||
return couponMeetRespDTO.setMeet(true);
|
||||
});
|
||||
}
|
||||
|
||||
private List<ProductSkuRespDTO> checkSkus(PriceCalculateReqDTO calculateReqDTO) {
|
||||
// 获得商品 SKU 数组
|
||||
Map<Long, Integer> skuIdCountMap = CollectionUtils.convertMap(calculateReqDTO.getItems(),
|
||||
|
Loading…
Reference in New Issue
Block a user