Просмотр исходного кода

fix(coupon): apply() 增加商品维度校验,防止商品券被用于非预期商品

- CouponService.apply 增加 5 参重载(带 productIds 集合),券绑定 product_id 时仅当订单包含该商品才可用
- 4 参 apply 委托 5 参(无商品上下文),会员券/MEMBERSHIP 流程不受影响
- ProductOrderService 三个调用点传入订单商品集合(单商品 singleton / 多商品 items 集合)
Xiaogang Liao 2 недель назад
Родитель
Сommit
e27934c51c

+ 19 - 0
cfc-backend/src/main/java/com/etotem/cfc/service/CouponService.java

@@ -16,6 +16,7 @@ import org.springframework.transaction.annotation.Transactional;
 
 import javax.annotation.Resource;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
@@ -170,6 +171,19 @@ public class CouponService {
     }
 
     public Integer apply(Long userId, Long userCouponId, String orderType, Integer orderAmount) {
+        return apply(userId, userCouponId, orderType, orderAmount, null);
+    }
+
+    /**
+     * 应用优惠券(带商品维度校验)。
+     *
+     * <p>{@code productIds} 为本次订单包含的商品 ID 集合。券若绑定了特定商品
+     * ({@code coupon.product_id != null}),只有订单包含该商品时才可用,防止商品券
+     * 被用于非预期商品;{@code productIds} 为 null 表示无商品上下文(如会员订单),
+     * 此时绑定商品的券一律不可用。</p>
+     */
+    public Integer apply(Long userId, Long userCouponId, String orderType, Integer orderAmount,
+                         Collection<Long> productIds) {
         Long familyId = getFamilyId(userId);
         if (familyId == null) return null;
         FamilyCoupon fc = familyCouponMapper.selectById(userCouponId);
@@ -193,6 +207,11 @@ public class CouponService {
         if (!"ALL".equals(coupon.getApplicableTo()) && !coupon.getApplicableTo().equals(orderType)) {
             return null;
         }
+        // 商品券只能用于指定商品(防止商品券被用于非预期商品)
+        if (coupon.getProductId() != null
+                && (productIds == null || !productIds.contains(coupon.getProductId()))) {
+            return null;
+        }
         // 新增:会员专属券(applicableTo=MEMBERSHIP)仅允许会员使用
         // 例外:手动发放的券(grantType=MANUAL)允许非会员使用,用于赠送升级场景
         if ("MEMBERSHIP".equals(coupon.getApplicableTo())

+ 6 - 3
cfc-backend/src/main/java/com/etotem/cfc/service/ProductOrderService.java

@@ -270,7 +270,8 @@ public class ProductOrderService {
         // 优惠券抵扣:校验并计入优惠金额(orderType=PRODUCT)
         Long appliedCouponId = null;
         if (dto.getCouponId() != null) {
-            Integer couponDiscount = couponService.apply(buyerId, dto.getCouponId(), "PRODUCT", order.getTotalAmount());
+            Integer couponDiscount = couponService.apply(buyerId, dto.getCouponId(), "PRODUCT", order.getTotalAmount(),
+                    java.util.Collections.singleton(order.getProductId()));
             if (couponDiscount != null && couponDiscount > 0) {
                 discountAmount += couponDiscount;
                 appliedCouponId = dto.getCouponId();
@@ -429,7 +430,8 @@ public class ProductOrderService {
         int discountAmount = dto.getDiscountAmount() != null ? dto.getDiscountAmount() : 0;
         Long appliedCouponId = null;
         if (dto.getCouponId() != null) {
-            Integer couponDiscount = couponService.apply(buyerId, dto.getCouponId(), "PRODUCT", order.getTotalAmount());
+            Integer couponDiscount = couponService.apply(buyerId, dto.getCouponId(), "PRODUCT", order.getTotalAmount(),
+                    java.util.Collections.singleton(order.getProductId()));
             if (couponDiscount != null && couponDiscount > 0) {
                 discountAmount += couponDiscount;
                 appliedCouponId = dto.getCouponId();
@@ -817,7 +819,8 @@ public class ProductOrderService {
         // 优惠券抵扣:校验并计入优惠金额(orderType=PRODUCT)
         Long appliedCouponId = null;
         if (dto.getCouponId() != null) {
-            Integer couponDiscount = couponService.apply(buyerId, dto.getCouponId(), "PRODUCT", totalAmount);
+            Integer couponDiscount = couponService.apply(buyerId, dto.getCouponId(), "PRODUCT", totalAmount,
+                    dto.getItems().stream().map(OrderItemVO::getProductId).collect(java.util.stream.Collectors.toSet()));
             if (couponDiscount != null && couponDiscount > 0) {
                 multiDiscountAmount += couponDiscount;
                 appliedCouponId = dto.getCouponId();