CouponServiceImpl.java 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. package com.ylx.massage.service.impl;
  2. import cn.hutool.core.collection.CollUtil;
  3. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  4. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  5. import com.ylx.common.exception.ServiceException;
  6. import com.ylx.massage.domain.Coupon;
  7. import com.ylx.massage.domain.CouponReceive;
  8. import com.ylx.massage.mapper.CouponMapper;
  9. import com.ylx.massage.service.CouponReceiveService;
  10. import com.ylx.massage.service.CouponService;
  11. import lombok.extern.slf4j.Slf4j;
  12. import org.springframework.stereotype.Service;
  13. import org.springframework.transaction.annotation.Transactional;
  14. import javax.annotation.Resource;
  15. import java.math.BigDecimal;
  16. import java.math.RoundingMode;
  17. import java.time.LocalDate;
  18. import java.time.LocalDateTime;
  19. import java.util.List;
  20. import java.util.Map;
  21. /**
  22. * 优惠券的规则信息(Coupon)表服务实现类
  23. *
  24. * @author makejava
  25. * @since 2024-05-13 16:32:59
  26. */
  27. @Slf4j
  28. @Service("couponService")
  29. public class CouponServiceImpl extends ServiceImpl<CouponMapper, Coupon> implements CouponService {
  30. @Resource
  31. private CouponReceiveService couponReceiveService;
  32. @Override
  33. public BigDecimal calculateDiscountAmount(String couponId, String openId, BigDecimal orderAmount) {
  34. // 1. 查询优惠券领取详情
  35. Map<String, Object> detail = couponReceiveService.selectCouponDetailForCalc(couponId, openId);
  36. if (CollUtil.isEmpty(detail)) {
  37. throw new ServiceException("未查询到该用户的优惠券信息");
  38. }
  39. // 3. 校验优惠券状态(必须是 0:待使用)
  40. Integer couponStatus = (Integer) detail.get("coupon_status");
  41. if (couponStatus == null || couponStatus != 0) {
  42. throw new ServiceException("优惠券状态异常,不可使用");
  43. }
  44. // 4. 校验有效期
  45. LocalDate validStart = ((java.sql.Date) detail.get("valid_start_time")).toLocalDate();
  46. LocalDate expiration = ((java.sql.Date) detail.get("expiration_time")).toLocalDate();
  47. LocalDate today = LocalDate.now();
  48. if (today.isBefore(validStart) || today.isAfter(expiration)) {
  49. throw new ServiceException("优惠券不在有效期内");
  50. }
  51. // 5. 提取优惠规则字段
  52. Integer discountType = (Integer) detail.get("discount_type");
  53. BigDecimal discountValue = (BigDecimal) detail.get("discount_value");
  54. BigDecimal rebValue = (BigDecimal) detail.get("reb_value");
  55. BigDecimal thresholdAmount = (BigDecimal) detail.get("threshold_amount");
  56. // 6. 根据类型计算抵扣金额
  57. BigDecimal discountAmount = BigDecimal.ZERO;
  58. switch (discountType) {
  59. case 1: // 无门槛
  60. discountAmount = discountValue;
  61. break;
  62. case 2: // 折扣 (reb_value 例如 0.8 代表8折)
  63. // 抵扣金额 = 订单金额 * (1 - 折扣值)
  64. discountAmount = orderAmount.multiply(BigDecimal.ONE.subtract(rebValue));
  65. break;
  66. case 3: // 满减
  67. if (orderAmount.compareTo(thresholdAmount) < 0) {
  68. throw new ServiceException("订单金额未达到满减门槛");
  69. }
  70. discountAmount = discountValue;
  71. break;
  72. default:
  73. throw new ServiceException("未知的优惠券类型: " + discountType);
  74. }
  75. // 7. 兜底逻辑:抵扣金额不能大于订单实际金额
  76. if (discountAmount.compareTo(orderAmount) > 0) {
  77. discountAmount = orderAmount;
  78. }
  79. // 8. 保留两位小数,四舍五入
  80. return discountAmount.setScale(2, RoundingMode.HALF_UP);
  81. }
  82. @Override
  83. @Transactional(rollbackFor = Exception.class)
  84. public void useCoupon(String couponId, String openId, Long orderId, Integer orderType) {
  85. // 1. 乐观锁核销用户领取记录
  86. int updatedRows = couponReceiveService.useCouponOptimisticLock(couponId, openId, orderId, orderType, LocalDateTime.now());
  87. if (updatedRows == 0) {
  88. throw new ServiceException("优惠券核销失败:状态异常或已被使用");
  89. }
  90. // 2. 规则表使用数量 +1
  91. int ruleUpdatedRows = couponReceiveService.incrementUsedNum(couponId);
  92. if (ruleUpdatedRows == 0) {
  93. throw new ServiceException("优惠券规则状态异常,核销中止");
  94. }
  95. log.info("优惠券核销成功: couponId={}, openId={}, orderId={}", couponId, openId, orderId);
  96. }
  97. @Override
  98. @Transactional(rollbackFor = Exception.class)
  99. public void returnCoupon(String couponId, String openId, Long orderId) {
  100. // 1. 将已使用的券退回到待使用状态
  101. int rows = couponReceiveService.returnCouponOptimisticLock(couponId, openId, orderId);
  102. if (rows == 0) {
  103. log.warn("退还优惠券失败,可能券已过期或不属于该订单: couponId={}, orderId={}", couponId, orderId);
  104. return;
  105. }
  106. // 2. 规则表使用数量 -1
  107. this.couponReceiveService.decrementUsedNum(couponId);
  108. log.info("优惠券退还成功: couponId={}, orderId={}", couponId, orderId);
  109. }
  110. }