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)表服务实现类 * * @author makejava * @since 2024-05-13 16:32:59 */ @Slf4j @Service("couponService") public class CouponServiceImpl extends ServiceImpl implements CouponService { @Resource private CouponReceiveService couponReceiveService; @Override public BigDecimal calculateDiscountAmount(String couponId, String openId, BigDecimal orderAmount) { // 1. 查询优惠券领取详情 Map 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); } }