|
|
@@ -1,11 +1,26 @@
|
|
|
package com.ylx.massage.service.impl;
|
|
|
|
|
|
|
|
|
+import cn.hutool.core.collection.CollUtil;
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
+import com.ylx.common.exception.ServiceException;
|
|
|
import com.ylx.massage.domain.Coupon;
|
|
|
+import com.ylx.massage.domain.CouponReceive;
|
|
|
import com.ylx.massage.mapper.CouponMapper;
|
|
|
+import com.ylx.massage.service.CouponReceiveService;
|
|
|
import com.ylx.massage.service.CouponService;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+
|
|
|
+import javax.annotation.Resource;
|
|
|
+import java.math.BigDecimal;
|
|
|
+import java.math.RoundingMode;
|
|
|
+import java.time.LocalDate;
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
|
|
|
/**
|
|
|
* 优惠券的规则信息(Coupon)表服务实现类
|
|
|
@@ -13,8 +28,102 @@ import org.springframework.stereotype.Service;
|
|
|
* @author makejava
|
|
|
* @since 2024-05-13 16:32:59
|
|
|
*/
|
|
|
+@Slf4j
|
|
|
@Service("couponService")
|
|
|
public class CouponServiceImpl extends ServiceImpl<CouponMapper, Coupon> implements CouponService {
|
|
|
|
|
|
+ @Resource
|
|
|
+ private CouponReceiveService couponReceiveService;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public BigDecimal calculateDiscountAmount(String couponId, String openId, BigDecimal orderAmount) {
|
|
|
+
|
|
|
+ // 1. 查询优惠券领取详情
|
|
|
+ Map<String, Object> detail = couponReceiveService.selectCouponDetailForCalc(couponId, openId);
|
|
|
+
|
|
|
+ if (CollUtil.isEmpty(detail)) {
|
|
|
+ throw new ServiceException("未查询到该用户的优惠券信息");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 校验优惠券状态(必须是 0:待使用)
|
|
|
+ Integer couponStatus = (Integer) detail.get("coupon_status");
|
|
|
+ if (couponStatus == null || couponStatus != 0) {
|
|
|
+ throw new ServiceException("优惠券状态异常,不可使用");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 校验有效期
|
|
|
+ LocalDate validStart = ((java.sql.Date) detail.get("valid_start_time")).toLocalDate();
|
|
|
+ LocalDate expiration = ((java.sql.Date) detail.get("expiration_time")).toLocalDate();
|
|
|
+ LocalDate today = LocalDate.now();
|
|
|
+ if (today.isBefore(validStart) || today.isAfter(expiration)) {
|
|
|
+ throw new ServiceException("优惠券不在有效期内");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 5. 提取优惠规则字段
|
|
|
+ Integer discountType = (Integer) detail.get("discount_type");
|
|
|
+ BigDecimal discountValue = (BigDecimal) detail.get("discount_value");
|
|
|
+ BigDecimal rebValue = (BigDecimal) detail.get("reb_value");
|
|
|
+ BigDecimal thresholdAmount = (BigDecimal) detail.get("threshold_amount");
|
|
|
+
|
|
|
+ // 6. 根据类型计算抵扣金额
|
|
|
+ BigDecimal discountAmount = BigDecimal.ZERO;
|
|
|
+ switch (discountType) {
|
|
|
+ case 1: // 无门槛
|
|
|
+ discountAmount = discountValue;
|
|
|
+ break;
|
|
|
+ case 2: // 折扣 (reb_value 例如 0.8 代表8折)
|
|
|
+ // 抵扣金额 = 订单金额 * (1 - 折扣值)
|
|
|
+ discountAmount = orderAmount.multiply(BigDecimal.ONE.subtract(rebValue));
|
|
|
+ break;
|
|
|
+ case 3: // 满减
|
|
|
+ if (orderAmount.compareTo(thresholdAmount) < 0) {
|
|
|
+ throw new ServiceException("订单金额未达到满减门槛");
|
|
|
+ }
|
|
|
+ discountAmount = discountValue;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ throw new ServiceException("未知的优惠券类型: " + discountType);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 7. 兜底逻辑:抵扣金额不能大于订单实际金额
|
|
|
+ if (discountAmount.compareTo(orderAmount) > 0) {
|
|
|
+ discountAmount = orderAmount;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 8. 保留两位小数,四舍五入
|
|
|
+ return discountAmount.setScale(2, RoundingMode.HALF_UP);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public void useCoupon(String couponId, String openId, Long orderId, Integer orderType) {
|
|
|
+ // 1. 乐观锁核销用户领取记录
|
|
|
+ int updatedRows = couponReceiveService.useCouponOptimisticLock(couponId, openId, orderId, orderType, LocalDateTime.now());
|
|
|
+ if (updatedRows == 0) {
|
|
|
+ throw new ServiceException("优惠券核销失败:状态异常或已被使用");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 规则表使用数量 +1
|
|
|
+ int ruleUpdatedRows = couponReceiveService.incrementUsedNum(couponId);
|
|
|
+ if (ruleUpdatedRows == 0) {
|
|
|
+ throw new ServiceException("优惠券规则状态异常,核销中止");
|
|
|
+ }
|
|
|
+ log.info("优惠券核销成功: couponId={}, openId={}, orderId={}", couponId, openId, orderId);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public void returnCoupon(String couponId, String openId, Long orderId) {
|
|
|
+ // 1. 将已使用的券退回到待使用状态
|
|
|
+ int rows = couponReceiveService.returnCouponOptimisticLock(couponId, openId, orderId);
|
|
|
+ if (rows == 0) {
|
|
|
+ log.warn("退还优惠券失败,可能券已过期或不属于该订单: couponId={}, orderId={}", couponId, orderId);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 规则表使用数量 -1
|
|
|
+ this.couponReceiveService.decrementUsedNum(couponId);
|
|
|
+ log.info("优惠券退还成功: couponId={}, orderId={}", couponId, orderId);
|
|
|
+ }
|
|
|
}
|
|
|
|