From dc4091e0586964dded6b6a8c6f351754a0374726 Mon Sep 17 00:00:00 2001 From: Jerry Yan <792602257@qq.com> Date: Sat, 11 Oct 2025 21:09:36 +0800 Subject: [PATCH] =?UTF-8?q?feat(pricing):=20=E6=96=B0=E5=A2=9E=E5=8D=87?= =?UTF-8?q?=E5=8D=95=E6=A3=80=E6=B5=8B=E5=8A=9F=E8=83=BD-=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E5=8D=87=E5=8D=95=E6=A3=80=E6=B5=8BAPI=E7=AB=AF?= =?UTF-8?q?=E7=82=B9=20`/api/pricing/upgrade-check`=20-=20=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=20`checkUpgrade`=20=E6=A0=B8=E5=BF=83=E6=96=B9?= =?UTF-8?q?=E6=B3=95=EF=BC=8C=E7=94=A8=E4=BA=8E=E6=A3=80=E6=B5=8B=E5=B7=B2?= =?UTF-8?q?=E8=B4=AD=E4=B8=8E=E5=BE=85=E8=B4=AD=E5=95=86=E5=93=81=E7=BB=84?= =?UTF-8?q?=E5=90=88=E4=BC=98=E6=83=A0=20-=20=E6=94=AF=E6=8C=81=E4=B8=80?= =?UTF-8?q?=E5=8F=A3=E4=BB=B7=E5=92=8C=E6=89=93=E5=8C=85=E4=BC=98=E6=83=A0?= =?UTF-8?q?=E7=9A=84=E7=BB=BC=E5=90=88=E8=AF=84=E4=BC=B0=E9=80=BB=E8=BE=91?= =?UTF-8?q?-=20=E6=8F=90=E4=BE=9B=E8=AF=A6=E7=BB=86=E7=9A=84=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E5=8F=82=E6=95=B0=E4=B8=8E=E5=93=8D=E5=BA=94=E7=BB=93?= =?UTF-8?q?=E6=9E=9C=E7=BB=93=E6=9E=84=E5=AE=9A=E4=B9=89=20-=20=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E6=96=87=E6=A1=A3=E8=AF=B4=E6=98=8E=E5=8D=87=E5=8D=95?= =?UTF-8?q?=E6=A3=80=E6=B5=8B=E7=9A=84=E4=B8=9A=E5=8A=A1=E4=BB=B7=E5=80=BC?= =?UTF-8?q?=E4=B8=8E=E4=BD=BF=E7=94=A8=E5=9C=BA=E6=99=AF-=20=E8=A1=A5?= =?UTF-8?q?=E5=85=85=E5=85=B3=E9=94=AE=E6=9E=B6=E6=9E=84=E5=8F=98=E6=9B=B4?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=E4=B8=8E=E5=85=BC=E5=AE=B9=E6=80=A7=E6=B3=A8?= =?UTF-8?q?=E6=84=8F=E4=BA=8B=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/ycwl/basic/pricing/CLAUDE.md | 158 ++++++++++++++++-- 1 file changed, 146 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/ycwl/basic/pricing/CLAUDE.md b/src/main/java/com/ycwl/basic/pricing/CLAUDE.md index 396d59e2..23cb86d4 100644 --- a/src/main/java/com/ycwl/basic/pricing/CLAUDE.md +++ b/src/main/java/com/ycwl/basic/pricing/CLAUDE.md @@ -82,6 +82,7 @@ com.ycwl.basic.pricing/ #### API端点 - `POST /api/pricing/calculate` — 执行价格计算(预览模式默认开启) - `GET /api/pricing/coupons/my-coupons` — 查询用户可用优惠券 +- `POST /api/pricing/upgrade-check` — 升单检测:综合已购与待购商品,判断是否命中一口价或打包优惠 #### 计算流程 ```java @@ -515,22 +516,146 @@ public class PriceCalculationResult { - `GET /api/pricing/admin/one-price/scenic/{scenicId}` — 按景区查询启用配置 - `GET /api/pricing/admin/one-price/check/{scenicId}` — 景区是否适用一口价 +## 升单检测功能 (Upgrade Detection) + +### 1. 功能概述 + +升单检测功能是最新新增的功能,用于综合已购商品与待购商品,判断是否满足一口价或打包购买优惠条件,为用户提供购买建议。 + +### 2. 核心接口 + +#### IPriceCalculationService 升单检测方法 +```java +/** + * 升单检测:综合已购与待购商品,判断是否命中一口价或打包优惠 + * @param request 升单检测请求 + * @return 升单检测结果 + */ +UpgradeCheckResult checkUpgrade(UpgradeCheckRequest request); +``` + +#### API 端点 +- `POST /api/pricing/upgrade-check` — 升单检测接口 + +### 3. 检测逻辑 + +#### 请求参数 (UpgradeCheckRequest) +- `scenicId`: 景区ID +- `purchasedProducts`: 已购商品列表 +- `intendingProducts`: 待购商品列表 + +#### 检测流程 +1. **商品规范化**: 对已购和待购商品进行规范化处理 +2. **价格汇总**: 分别计算已购和待购商品的总价格 +3. **一口价评估**: 判断合并商品是否满足一口价条件 +4. **打包优惠评估**: 检测是否满足打包购买优惠条件 +5. **结果汇总**: 生成升单检测结果和建议 + +#### 响应结果 (UpgradeCheckResult) +- `summary`: 价格汇总信息(原价、优惠价、最终价) +- `onePriceResult`: 一口价检测结果(如适用) +- `bundleResult`: 打包优惠检测结果(如适用) +- `upgradeAvailable`: 是否可升单(布尔值) +- `savingsAmount`: 升单可节省金额 + +### 4. 业务价值 + +#### 用户体验提升 +- 为用户提供购买建议,提高客单价 +- 自动检测最优购买组合 +- 清晰展示升单优惠金额 + +#### 销售支持 +- 促进多商品销售 +- 提高打包购买和一口价利用率 +- 增加用户购买决策信心 + +### 5. 使用场景 + +#### 典型场景 +- 用户已购买照片,建议加购视频享受打包优惠 +- 用户购买多件商品,建议升级为一口价套餐 +- 用户购买数量接近打包优惠门槛,建议增加数量 + +#### 实现细节 +```java +// 升单检测核心逻辑 +@Override +public UpgradeCheckResult checkUpgrade(UpgradeCheckRequest request) { + // 1. 参数验证 + if (request == null) { + throw new PriceCalculationException("升单检测请求不能为空"); + } + + // 2. 商品规范化 + List purchased = normalizeProducts(request.getPurchasedProducts()); + List intending = normalizeProducts(request.getIntendingProducts()); + + // 3. 合并商品列表 + List allProducts = new ArrayList<>(); + allProducts.addAll(purchased); + allProducts.addAll(intending); + + // 4. 价格计算 + PriceDetails purchasedPrice = calculateProductsPriceWithOriginal(purchased); + PriceDetails intendingPrice = calculateProductsPriceWithOriginal(intending); + PriceDetails totalPrice = calculateProductsPrice(allProducts); + + // 5. 优惠评估 + UpgradeOnePriceResult onePriceResult = evaluateOnePrice(request.getScenicId(), allProducts, totalPrice); + UpgradeBundleDiscountResult bundleResult = evaluateBundleDiscount(request.getScenicId(), allProducts, totalPrice); + + // 6. 结果汇总 + return buildUpgradeResult(purchasedPrice, intendingPrice, onePriceResult, bundleResult); +} +``` + ## 测试策略 -### 1. 单元测试 -建议覆盖: -- 价格计算核心流程与边界 -- 优惠券/券码/一口价适用性与叠加规则 -- 异常场景与异常处理器 +### 单元测试类型 +- **服务层测试**:每个服务类都有对应测试类 + - `PriceBundleServiceTest` - 套餐价格计算测试 + - `ReusableVoucherServiceTest` - 可重复使用券码测试 + - `VoucherTimeRangeTest` - 券码时间范围功能测试 + - `VoucherPrintServiceCodeGenerationTest` - 券码生成测试 +- **实体映射测试**:验证数据库映射和JSON序列化 + - `PriceBundleConfigStructureTest` - 实体结构测试 + - `PriceBundleConfigJsonTest` - JSON序列化测试 + - `CouponSwitchFieldsMappingTest` - 字段映射测试 +- **类型处理器测试**:验证自定义TypeHandler + - `BundleProductListTypeHandlerTest` - 套餐商品列表序列化测试 +- **配置验证测试**:验证系统配置完整性 + - `DefaultConfigValidationTest` - 验证所有ProductType的default配置 + - `CodeGenerationStandaloneTest` - 独立代码生成测试 -### 2. 集成测试 -- 数据库读写与分页 -- JSON 序列化/反序列化(TypeHandler) -- API 端点的入参/出参校验 +### 测试执行命令 +```bash +# 运行单个测试类 +mvn test -Dtest=VoucherTimeRangeTest +mvn test -Dtest=ReusableVoucherServiceTest +mvn test -Dtest=BundleProductListTypeHandlerTest -### 3. 配置校验 -- 校验各 ProductType 的默认配置完整性 -- 关键枚举与配置代码路径的兼容性 +# 运行整个pricing模块测试 +mvn test -Dtest="com.ycwl.basic.pricing.*Test" + +# 运行特定分类的测试 +mvn test -Dtest="com.ycwl.basic.pricing.service.*Test" # 服务层测试 +mvn test -Dtest="com.ycwl.basic.pricing.handler.*Test" # TypeHandler测试 +mvn test -Dtest="com.ycwl.basic.pricing.entity.*Test" # 实体测试 +mvn test -Dtest="com.ycwl.basic.pricing.mapper.*Test" # Mapper测试 + +# 运行带详细报告的测试 +mvn test -Dtest="com.ycwl.basic.pricing.*Test" -Dsurefire.printSummary=true +``` + +### 重点测试场景 +- **价格计算核心流程**:验证统一优惠检测和组合逻辑 +- **可重复使用券码**:验证多次使用、时间间隔、用户限制逻辑 +- **时间范围控制**:验证券码有效期开始和结束时间 +- **优惠叠加规则**:验证券码、优惠券、一口价的叠加逻辑 +- **JSON序列化**:验证复杂对象在数据库中的存储和读取 +- **分页功能**:验证PageHelper和MyBatis-Plus分页集成 +- **异常处理**:验证业务异常和全局异常处理器 ## 数据库设计 @@ -568,9 +693,18 @@ CREATE INDEX idx_print_face_scenic ON voucher_print_record(face_id, scenic_id); - 定期清理已删除的过期数据 - 使用数据完整性检查 SQL 验证统计数据准确性 +### 关键架构变更 + +#### 最近重要更新 (2025-09-18) +1. **新增升单检测功能** - 添加了`/api/pricing/upgrade-check`接口,支持已购和待购商品的优惠组合检测 +2. **新增打包购买优惠功能** - 实现了多商品组合优惠策略,优先级100(仅次于一口价) +3. **优惠优先级调整** - 确立了"一口价 > 打包购买 > 券码 > 优惠券"的优先级顺序 +4. **PrinterServiceImpl重构** - 移除对PriceRepository的依赖,统一使用IPriceCalculationService + ## 兼容性与注意事项 - 本模块使用 PageHelper(优惠券相关)与 MyBatis‑Plus(券码/一口价等)并存,请根据对应 Service/Mapper 选择分页与查询方式。 - 优惠优先级及叠加规则以各 Provider 与业务配置为准,避免在外层重复实现优先级判断逻辑。 - 若扩展新的优惠类型,务必实现 `IDiscountProvider` 并在 `IDiscountDetectionService` 中完成注册(当前实现通过组件扫描自动注册并排序)。 +- 升单检测功能依赖完整的价格计算和优惠检测服务,确保相关依赖正常注入。