# 任务6验证报告:指定商品会员价机制 > **执行时间:** 2026-10-24 > **执行智能体:** 任务6专用 > **核验状态:** 待独立核验 --- ## 一、需求回顾(来自 `2026-10-23-basic-membership-spec-revision.md`) ### 需求口径(第4条) | # | 权益 | 口径 | 对应实现 | |---|------|------|----------| | 4 | 指定商品会员价 | **享受**(原无) | `discount_purchase` 生效:`products.member_price` + `min_discount_level`(NULL 时默认 BASIC)| ### 任务6步骤 - [x] `ProductOrderService.getEligibleMemberPrice`:`min_discount_level` 为 NULL 时默认按 **BASIC** 判定(修复 FREE 也可享的漏洞);等级比较用 `getLevelOrder` - [x] `ActivityDTO.resolveMemberPrice`:会员等级集合纳入 `BASIC`(FAMILY/PREMIUM 保持不变;PROVIDER 仍不享) - [x] `mvn clean compile` 通过 ### 验收标准 - BASIC 购买"设置 member_price 且 min_discount_level 为空/BASIC"的商品得会员价 - FREE 得原价 - FAMILY+ 得会员价 - `min_discount_level=FAMILY` 的商品 BASIC 得原价 - 活动(activities)会员价对 BASIC 生效 --- ## 二、逐条验收结论 ### 验收项 1:ProductOrderService.getEligibleMemberPrice **代码位置:** `ProductOrderService.java` 第 2166-2188 行 **当前实现:** ```java private Integer getEligibleMemberPrice(Product product, Long buyerId) { if (product == null || product.getMemberPrice() == null || product.getMemberPrice() <= 0) { return product != null ? product.getPrice() : 0; } // 用户会员等级(FREE 兜底) String userLevel = "FREE"; if (buyerId != null) { User user = userMapper.selectById(buyerId); if (user != null) { userLevel = membershipService.getMemberLevel(buyerId); } } // 产品未设置最低级别时默认 BASIC String minLevel = (product.getMinDiscountLevel() == null || product.getMinDiscountLevel().isEmpty()) ? "BASIC" : product.getMinDiscountLevel(); // 比较级别数字:级别数字越小越低 int userLevelOrder = membershipService.getLevelOrder(userLevel); int minLevelOrder = membershipService.getLevelOrder(minLevel); // 用户级别 >= 最低要求级别,则可享会员价 if (userLevelOrder >= minLevelOrder) { return product.getMemberPrice(); } return product.getPrice(); } ``` **逻辑分析:** | 场景 | min_discount_level | userLevel | userLevelOrder | minLevelOrder | 结果 | |------|-------------------|-----------|----------------|---------------|------| | FREE 买 min_level=NULL 商品 | NULL → "BASIC" | "FREE" | 1 | 2 | ❌ FREE < BASIC → 原价 ✓ | | BASIC 买 min_level=NULL 商品 | NULL → "BASIC" | "BASIC" | 2 | 2 | ✅ BASIC >= BASIC → 会员价 ✓ | | FAMILY 买 min_level=NULL 商品 | NULL → "BASIC" | "FAMILY" | 3 | 2 | ✅ FAMILY >= BASIC → 会员价 ✓ | | BASIC 买 min_level=FAMILY 商品 | "FAMILY" | "BASIC" | 2 | 3 | ❌ BASIC < FAMILY → 原价 ✓ | | FAMILY 买 min_level=FAMILY 商品 | "FAMILY" | "FAMILY" | 3 | 3 | ✅ FAMILY >= FAMILY → 会员价 ✓ | **结论:✅ PASS** - min_discount_level 为 NULL 或空字符串时,默认按 BASIC 判定 ✓ - 使用 `getLevelOrder` 进行等级比较 ✓ - FREE 用户不再能享受会员价(已修复漏洞)✓ - 处理了空字符串边界情况(比草稿更严谨)✓ --- ### 验收项 2:ActivityDTO.resolveMemberPrice **代码位置:** `ActivityDTO.java` 第 137-146 行 **当前实现:** ```java public static Integer resolveMemberPrice(Integer basePrice, Integer memberPrice, String memberLevel) { if (basePrice == null) return null; // If user has member level and a memberPrice is set, use memberPrice if (memberLevel != null && ("BASIC".equals(memberLevel) || "FAMILY".equals(memberLevel) || "PREMIUM".equals(memberLevel))) { if (memberPrice != null) { return memberPrice; } } return basePrice; } ``` **逻辑分析:** | memberLevel | 是否享会员价 | 说明 | |-------------|-------------|------| | null / "" | ❌ | 非会员 | | "FREE" | ❌ | 免费用户不享受 | | "BASIC" | ✅ | 9.9元体验会员纳入 ✓ | | "FAMILY" | ✅ | 保持不变 ✓ | | "PREMIUM" | ✅ | 保持不变 ✓ | | "PROVIDER" | ❌ | 服务商不享受 ✓ | **结论:✅ PASS** - BASIC 已纳入会员等级集合 ✓ - FAMILY 与 PREMIUM 保持不变 ✓ - PROVIDER 仍然不享受会员价 ✓ --- ### 验收项 3:两个调用点行为一致 **调用点1:** `ProductOrderService.java:263`(单商品下单) ```java order.setUnitPrice(getEligibleMemberPrice(product, buyerId)); ``` **调用点2:** `ProductOrderService.java:762`(多商品下单/购物车) ```java int unitPrice = getEligibleMemberPrice(product, buyerId); ``` **结论:✅ PASS** 两个调用点均调用同一方法 `getEligibleMemberPrice`,逻辑完全一致。 --- ### 验收项 4:不波及特价活动流程 **特价订单流程(`createSpecialDealOrder`):** ```java // 第422-423行 order.setUnitPrice(deal.getSpecialPrice()); order.setTotalAmount(deal.getSpecialPrice() * quantity); ``` 特价订单直接使用 `deal.getSpecialPrice()` 作为单价,**完全不调用** `getEligibleMemberPrice`。 **结论:✅ PASS** 特价覆盖会员价的原有逻辑保持不变。 --- ### 验收项 5:Java 8 兼容 + 编译通过 - 代码使用标准 Java 8 语法,无 `var`、`List.of()` 等新特性 - `mvn clean compile -q` 退出码为 0,编译通过 **结论:✅ PASS** --- ## 三、发现的缺陷与修复 ### 发现的缺陷 **原草稿实现:** ```java String minLevel = product.getMinDiscountLevel() != null ? product.getMinDiscountLevel() : "BASIC"; ``` **问题:** 未处理 `min_discount_level` 为空字符串(`""`)的情况。虽然数据库字段为 VARCHAR(50) 且默认值为 NULL,但为保险起见应同时处理空字符串。 **修复后的代码:** ```java String minLevel = (product.getMinDiscountLevel() == null || product.getMinDiscountLevel().isEmpty()) ? "BASIC" : product.getMinDiscountLevel(); ``` **影响:** 增强了代码的健壮性,防止未来数据异常时出现 NPE 或逻辑错误。 --- ## 四、mvn 编译关键输出 ``` EXIT_CODE=0 ``` 编译成功,无任何 error 或 warning。 --- ## 五、git diff --stat ``` cfc-backend/src/main/java/com/etotem/cfc/dto/ActivityDTO.java | 5 +++-- cfc-backend/src/main/java/com/etotem/cfc/service/ProductOrderService.java | 16 ++++++++-------- 2 files changed, 11 insertions(+), 10 deletions(-) ``` --- ## 六、总结 | 验收项 | 结论 | |--------|------| | 1. getEligibleMemberPrice 逻辑正确性 | ✅ PASS | | 2. ActivityDTO.resolveMemberPrice 纳入 BASIC | ✅ PASS | | 3. 单商品/多商品调用点一致 | ✅ PASS | | 4. 特价活动流程不受影响 | ✅ PASS | | 5. Java 8 兼容 + 编译通过 | ✅ PASS | **任务状态:** ✅ 已完成,等待独立核验智能体验收