diff --git a/src/main/java/com/ycwl/basic/pricing/CLAUDE.md b/src/main/java/com/ycwl/basic/pricing/CLAUDE.md index 383380bf..0742dcdb 100644 --- a/src/main/java/com/ycwl/basic/pricing/CLAUDE.md +++ b/src/main/java/com/ycwl/basic/pricing/CLAUDE.md @@ -128,40 +128,54 @@ public enum CouponStatus { CLAIMED("claimed", ...), USED("used", ...), EXPIRED(" #### 优惠券数量管理机制 (v1.0.0更新) ```java -// PriceCouponConfig 实体新增字段 -private Integer userClaimLimit; // 每个用户可领取数量限制(NULL表示不限制) +// PriceCouponConfig 实体字段 +private Integer totalQuantity; // 发行总量(NULL或0表示不限制) +private Integer userClaimLimit; // 每个用户可领取数量限制(NULL或0表示不限制) private Integer claimedQuantity; // 已领取数量(区分于已使用数量) private Integer usedQuantity; // 已使用数量(实际消耗时更新) +// 字段语义 +totalQuantity: NULL/0=不限总量, >0=限制总量 +userClaimLimit: NULL/0=不限用户, >0=限制单用户 +claimedQuantity: 仅在totalQuantity>0时更新 +usedQuantity: 实际使用时更新 + // 领取流程检查顺序 1. 检查优惠券是否存在且启用 2. 检查有效期 -3. 检查总量库存: claimedQuantity < totalQuantity -4. 检查用户领取限制: countUserClaims(userId, couponId) < userClaimLimit -5. 创建领取记录并更新 claimedQuantity + 1 +3. 检查总量库存: totalQuantity>0 时,检查 claimedQuantity < totalQuantity +4. 检查用户领取限制: userClaimLimit>0 时,检查 countUserClaims(userId, couponId) < userClaimLimit +5. 创建领取记录并更新 claimedQuantity (仅当totalQuantity>0时) ``` #### 用户领取限制配置示例 ```java -// 场景1: 新人专享券,每人限领1张 +// 场景1: 新人专享券,每人限领1张,总量1000张 { "couponName": "新人专享券", "userClaimLimit": 1, "totalQuantity": 1000 } -// 场景2: 活动券,每人限领3张 +// 场景2: 活动券,每人限领3张,总量5000张 { "couponName": "618促销券", "userClaimLimit": 3, "totalQuantity": 5000 } -// 场景3: 不限制领取次数 +// 场景3: 不限制领取次数,不限制总量 { "couponName": "会员专享券", "userClaimLimit": null, // 或 0 - "totalQuantity": 10000 + "totalQuantity": null // 或 0,表示无限量 +} + +// 场景4: 不限用户次数,但限制总量 +{ + "couponName": "限量抢购券", + "userClaimLimit": null, // 不限单用户 + "totalQuantity": 500 // 总共500张 } ``` @@ -602,14 +616,20 @@ public class PriceCalculationResult { ### price_coupon_config 表字段说明 (v1.0.0更新) ```sql -- 核心数量管理字段 -total_quantity INT -- 发行总量 -claimed_quantity INT -- 已领取数量(v1.0.0新增,领取时+1) +total_quantity INT -- 发行总量(NULL或0表示不限制,>0表示限量) +claimed_quantity INT -- 已领取数量(v1.0.0新增,领取时+1,仅在total_quantity>0时更新) used_quantity INT -- 已使用数量(实际使用时+1) -user_claim_limit INT -- 每个用户可领取数量限制(v1.0.0新增,NULL表示不限制) +user_claim_limit INT -- 每个用户可领取数量限制(v1.0.0新增,NULL或0表示不限制,>0表示限制) -- 字段关系 -- claimed_quantity >= used_quantity (已领取 >= 已使用) --- claimed_quantity <= total_quantity (已领取 <= 总量) +-- claimed_quantity <= total_quantity (已领取 <= 总量,仅当total_quantity>0时适用) + +-- 典型配置组合 +-- 1. 无限量发行: total_quantity=NULL/0, user_claim_limit=NULL/0 +-- 2. 限量限人: total_quantity=1000, user_claim_limit=1 +-- 3. 限量不限人: total_quantity=500, user_claim_limit=NULL/0 +-- 4. 不限量但限人: total_quantity=NULL/0, user_claim_limit=3 ``` ### 新增表结构 diff --git a/src/main/java/com/ycwl/basic/pricing/service/impl/CouponServiceImpl.java b/src/main/java/com/ycwl/basic/pricing/service/impl/CouponServiceImpl.java index 6deaa80d..904e1b10 100644 --- a/src/main/java/com/ycwl/basic/pricing/service/impl/CouponServiceImpl.java +++ b/src/main/java/com/ycwl/basic/pricing/service/impl/CouponServiceImpl.java @@ -231,8 +231,10 @@ public class CouponServiceImpl implements ICouponService { } // 5. 检查库存(如果有总量限制) - if (coupon.getTotalQuantity() != null && coupon.getClaimedQuantity() != null) { - if (coupon.getClaimedQuantity() >= coupon.getTotalQuantity()) { + // totalQuantity为NULL或0表示不限制总量 + if (coupon.getTotalQuantity() != null && coupon.getTotalQuantity() > 0) { + int currentClaimed = (coupon.getClaimedQuantity() == null ? 0 : coupon.getClaimedQuantity()); + if (currentClaimed >= coupon.getTotalQuantity()) { return CouponClaimResult.failure(CouponClaimResult.ERROR_COUPON_OUT_OF_STOCK, "优惠券已领完"); } } @@ -276,7 +278,8 @@ public class CouponServiceImpl implements ICouponService { } // 10. 更新优惠券已领取数量(区分于已使用数量) - if (coupon.getTotalQuantity() != null) { + // 仅在有总量限制时才更新claimedQuantity(totalQuantity为正整数) + if (coupon.getTotalQuantity() != null && coupon.getTotalQuantity() > 0) { int updatedClaimedQuantity = (coupon.getClaimedQuantity() == null ? 0 : coupon.getClaimedQuantity()) + 1; coupon.setClaimedQuantity(updatedClaimedQuantity); couponConfigMapper.updateById(coupon);