# 价格查询系统 (Pricing Module) 开发指南 此文档为 pricing 包的专用开发指南,基于当前代码实际结构进行说明与示例。 ## 模块概览 `com.ycwl.basic.pricing` 提供商品定价、分层/套餐与一口价配置、优惠券管理、券码管理与使用记录、统一优惠检测与价格计算等能力。采用分层架构(controller/dto/entity/mapper/service),同时整合了 PageHelper 与 MyBatis‑Plus 的分页能力。优惠叠加采用统一检测与排序:券码 > 优惠券 > 一口价(受配置限制)。 ## 目录结构(已对齐当前代码) ``` com.ycwl.basic.pricing/ ├── controller/ # REST API 控制器层 │ ├── PriceCalculationController.java # 价格计算 + 用户可用券查询 │ ├── PricingConfigController.java # 产品/阶梯/套餐(一口价)配置管理(含 admin 查询) │ ├── CouponManagementController.java # 优惠券配置/领取记录/统计(admin) │ ├── OnePricePurchaseController.java # 一口价配置管理(admin) │ ├── VoucherManagementController.java # 券码批次/券码/移动端领取与查询 │ └── VoucherUsageController.java # 券码使用记录与统计查询 ├── dto/ # 数据传输对象 │ ├── PriceCalculationRequest.java # 价格计算请求(含voucherCode/faceId/scenicId...) │ ├── PriceCalculationResult.java # 价格计算结果(含usedCoupon/usedVoucher/availableDiscounts) │ ├── ProductItem.java, ProductPriceInfo.java, PriceDetails.java │ ├── DiscountDetectionContext.java, DiscountInfo.java, DiscountDetail.java, │ │ DiscountResult.java, DiscountCombinationResult.java │ ├── BundleProductItem.java, MobilePriceCalculationRequest.java │ ├── OnePriceConfigRequest.java, OnePriceInfo.java │ ├── BundleDiscountInfo.java # 打包购买优惠信息 │ ├── req/ # 券码管理请求DTO │ │ ├── VoucherBatchCreateReq(.java|V2) │ │ ├── VoucherBatchQueryReq.java, VoucherCodeQueryReq.java, VoucherClaimReq.java │ │ └── VoucherUsageHistoryReq.java │ └── resp/ # 券码管理响应DTO │ ├── VoucherBatchResp.java, VoucherBatchStatsResp.java │ ├── VoucherCodeResp.java, VoucherValidationResp.java │ ├── VoucherUsageRecordResp.java, VoucherUsageStatsResp.java, VoucherUsageSummaryResp.java │ └── VoucherPrintResp.java ├── entity/ # 数据库实体类(MyBatis‑Plus) │ ├── PriceProductConfig.java, PriceTierConfig.java, PriceBundleConfig.java │ ├── PriceCouponConfig.java, PriceCouponClaimRecord.java │ ├── PriceVoucherBatchConfig.java, PriceVoucherCode.java, PriceVoucherUsageRecord.java │ ├── PriceOnePriceConfig.java # 一口价配置 │ └── VoucherPrintRecord.java # 券码打印记录 ├── enums/ # 枚举类 │ ├── ProductType.java, CouponType.java, CouponStatus.java │ ├── VoucherDiscountType.java, VoucherCodeStatus.java ├── exception/ # 统一异常与业务异常 │ ├── PricingExceptionHandler.java, PriceCalculationException.java │ ├── ProductConfigNotFoundException.java, DiscountDetectionException.java │ ├── CouponInvalidException.java, VoucherInvalidException.java, │ ├── VoucherAlreadyUsedException.java, VoucherNotClaimableException.java ├── handler/ # MyBatis 类型处理器 │ ├── BundleProductListTypeHandler.java # 套餐商品列表 JSON ↔ 对象列表 │ └── ProductTypeListTypeHandler.java # 商品类型列表 JSON ↔ 枚举列表 ├── mapper/ # 数据访问接口(多为 MyBatis‑Plus Mapper) │ ├── PriceProductConfigMapper.java, PriceTierConfigMapper.java, PriceBundleConfigMapper.java │ ├── PriceCouponConfigMapper.java, PriceCouponClaimRecordMapper.java │ ├── PriceVoucherBatchConfigMapper.java, PriceVoucherCodeMapper.java, PriceVoucherUsageRecordMapper.java │ └── PriceOnePriceConfigMapper.java, VoucherPrintRecordMapper.java └── service/ # 业务层接口与实现 ├── IPriceCalculationService.java, IDiscountDetectionService.java, IDiscountProvider.java ├── IBundleDiscountService.java # 打包购买优惠服务接口 ├── IProductConfigService.java, IPricingManagementService.java, IPriceBundleService.java ├── ICouponService.java, ICouponManagementService.java ├── IOnePricePurchaseService.java, IVoucherService.java, IVoucherUsageService.java ├── VoucherBatchService.java, VoucherCodeService.java, VoucherPrintService.java └── impl/ ├── PriceCalculationServiceImpl.java, DiscountDetectionServiceImpl.java ├── ProductConfigServiceImpl.java, PricingManagementServiceImpl.java, PriceBundleServiceImpl.java ├── CouponServiceImpl.java, CouponManagementServiceImpl.java, CouponDiscountProvider.java ├── VoucherServiceImpl.java, VoucherDiscountProvider.java, ├── VoucherBatchServiceImpl.java, VoucherCodeServiceImpl.java, VoucherPrintServiceImpl.java, ├── VoucherUsageServiceImpl.java, ├── OnePricePurchaseServiceImpl.java, OnePricePurchaseDiscountProvider.java └── BundleDiscountServiceImpl.java, BundleDiscountProvider.java # 打包购买优惠实现 ``` ## 核心功能 ### 1. 价格计算引擎 #### API端点 - `POST /api/pricing/calculate` — 执行价格计算(预览模式默认开启) - `GET /api/pricing/coupons/my-coupons` — 查询用户可用优惠券 #### 计算流程 ```java // 价格计算核心流程(IPriceCalculationService / PriceCalculationServiceImpl) 1. 校验 PriceCalculationRequest(包含 voucherCode / scenicId / faceId / autoUseXXX) 2. 加载商品基础配置 (PriceProductConfig),匹配分层规则 (PriceTierConfig)、套餐等 3. 构造 DiscountDetectionContext,统一检测可用优惠(券码/优惠券/一口价) 4. 按优先级与可叠加规则应用优惠:券码 > 优惠券 > 一口价(受 canUseCoupon/canUseVoucher 控制) 5. 汇总 DiscountDetail,计算 finalAmount / discountAmount 并返回 PriceCalculationResult ``` #### 关键类 - `IPriceCalculationService` / `PriceCalculationServiceImpl`: 价格计算核心逻辑 - `PriceCalculationRequest`: 计算请求DTO - `PriceCalculationResult`: 计算结果DTO - `IDiscountDetectionService`: 统一优惠检测与组合 ### 2. 优惠券管理系统 #### API端点(管理端,见 `CouponManagementController`) - `POST /api/pricing/admin/coupons/configs` — 创建配置 - `PUT /api/pricing/admin/coupons/configs/{id}` — 更新配置 - `DELETE /api/pricing/admin/coupons/configs/{id}` — 删除配置 - `PUT /api/pricing/admin/coupons/configs/{id}/status` — 启用/禁用 - `GET /api/pricing/admin/coupons/configs` — 全量配置(含禁用) - `GET /api/pricing/admin/coupons/configs/page` — 分页查询 - `GET /api/pricing/admin/coupons/claim-records[/*]` — 领取记录列表/分页/按条件查询 - `GET /api/pricing/admin/coupons/stats/{couponId}[/*]` — 使用统计/明细/概览 #### 枚举定义(简述) ```java // 实际枚举包含 code/description 字段,并提供 fromCode 工具方法 public enum CouponType { PERCENTAGE("percentage", ...), FIXED_AMOUNT("fixed_amount", ...) } public enum CouponStatus { CLAIMED("claimed", ...), USED("used", ...), EXPIRED("expired", ...) } ``` #### 关键特性 - 商品类型限制:通过 JSON 字段(结合 `ProductTypeListTypeHandler`)控制适用商品 - 消费限制:支持最小消费金额、最大折扣限制 - 时效性:基于时间的有效期控制 - 统计分析:完整的使用统计与分析能力 ### 3. 商品配置管理 #### API端点(摘) - `GET /api/pricing/config/products` — 查询商品配置 - `POST /api/pricing/config/products` — 创建商品配置 - `PUT /api/pricing/config/products/{id}` — 更新商品配置 - `GET /api/pricing/config/tiers[/*]`、`/bundles[/*]` — 阶梯/一口价配置查询 #### 商品类型定义(对齐实际) ```java // 实际定义(带 code/description,并提供 fromCode): public enum ProductType { VLOG_VIDEO("VLOG_VIDEO", "Vlog视频"), RECORDING_SET("RECORDING_SET", "录像集"), PHOTO_SET("PHOTO_SET", "照相集"), PHOTO_PRINT("PHOTO_PRINT", "照片打印"), MACHINE_PRINT("MACHINE_PRINT", "一体机打印"); } ``` #### 商品价格配置控制字段 `PriceProductConfig` 实体包含以下优惠控制字段: - `canUseCoupon`: 是否可使用优惠券 - `canUseVoucher`: 是否可使用券码 - `canUseOnePrice`: 是否可使用一口价优惠(新增) #### 一口价优惠控制机制 - 当购物车中任何商品的 `canUseOnePrice` 为 `false` 时,将跳过整个购物车的一口价优惠检测 - 配置优先级:具体商品配置 > 商品类型默认配置 > 系统默认(支持) - 异常情况下默认支持一口价优惠,确保业务流程不受影响 #### 分层定价 支持基于数量的分层定价策略,通过 `PriceTierConfig` 配置不同数量区间的单价。 ## 开发最佳实践 ### 1. 添加新商品类型 ```java // 步骤1: 在 ProductType 枚举中添加新类型(含 code/description) public enum ProductType { // 现有类型... NEW_PRODUCT("NEW_PRODUCT", "新商品类型"); } // 步骤2: 在数据库中添加 default 配置 INSERT INTO price_product_config (product_type, base_price, ...) VALUES ('NEW_PRODUCT', 100.00, ...); // 步骤3: 添加分层定价配置(可选) INSERT INTO price_tier_config (product_type, min_quantity, max_quantity, unit_price, ...) VALUES ('NEW_PRODUCT', 1, 10, 95.00, ...); // 步骤4: 更新前端产品类型映射 ``` ### 2. 扩展优惠券类型 ```java // 步骤1: 在 CouponType 中添加新类型 public enum CouponType { PERCENTAGE, FIXED_AMOUNT, NEW_COUPON_TYPE // 新增类型 } // 步骤2: 在 CouponServiceImpl 中实现计算逻辑 @Override public BigDecimal calculateDiscount(CouponConfig coupon, BigDecimal originalPrice) { return switch (coupon.getCouponType()) { case PERCENTAGE -> calculatePercentageDiscount(coupon, originalPrice); case FIXED_AMOUNT -> calculateFixedAmountDiscount(coupon, originalPrice); case NEW_COUPON_TYPE -> calculateNewTypeDiscount(coupon, originalPrice); }; } // 步骤3: 更新 applicableProducts 验证规则(如有) ``` ### 3. 自定义 TypeHandler 使用 ```java // BundleProductListTypeHandler 处理套餐商品列表 @MappedTypes(List.class) @MappedJdbcTypes(JdbcType.VARCHAR) public class BundleProductListTypeHandler extends BaseTypeHandler> { // JSON 序列化/反序列化逻辑 } // 在实体类中使用 public class SomeEntity { @TableField(typeHandler = BundleProductListTypeHandler.class) private List bundleProducts; } // ProductTypeListTypeHandler 处理商品类型列表(券码可用商品类型) public class ProductTypeListTypeHandler extends BaseTypeHandler> { /* ... */ } ``` ### 4. 异常处理模式 ```java // 自定义异常类 public class PriceCalculationException extends RuntimeException { public PriceCalculationException(String message) { super(message); } } // 在 PricingExceptionHandler 中统一处理 @ExceptionHandler(PriceCalculationException.class) public ApiResponse handlePriceCalculationException(PriceCalculationException e) { return ApiResponse.fail(ErrorCode.PRICE_CALCULATION_ERROR, e.getMessage()); } ``` ### 5. 分页查询实现 ``` // 使用 PageHelper 实现分页(优惠券相关) @Override public PageInfo getCouponsByPage(int pageNum, int pageSize, String status, String name) { PageHelper.startPage(pageNum, pageSize); // ... } ``` ``` // 使用 MyBatis‑Plus 的 Page(券码相关) Page page = voucherBatchService.queryBatchList(req); Page page = voucherCodeService.queryCodeList(req); ``` ## 券码管理系统 (Voucher System) ### 1. 核心特性 券码系统由原独立模块迁移并完全集成到 pricing 包中,与优惠券系统并行工作。 #### 券码优惠类型(简述) ```java public enum VoucherDiscountType { FREE_ALL(0, "全场免费"), // 优先级最高,且不可叠加 REDUCE_PRICE(1, "商品降价"), // 每个商品减免固定金额 DISCOUNT(2, "商品打折"); // 每个商品按百分比打折 } ``` #### 券码状态(简述) ``` UNCLAIMED(0) → CLAIMED_AVAILABLE/CLAIMED_UNUSED(1) → USED(2) → CLAIMED_EXHAUSTED(3) → EXPIRED(4) ``` ### 2. 数据库表结构 #### 券码批次配置表 (price_voucher_batch_config) - 批次管理:支持按景区、推客创建券码批次 - 统计功能:实时统计已领取数、已使用数 - 状态控制:支持启用/禁用批次 #### 券码表 (price_voucher_code) - 唯一性约束:每个券码全局唯一 - 用户限制:同一用户在同一景区只能领取一次券码 - 时间追踪:记录领取时间、使用时间 ### 3. API 概览(对齐当前控制器) 管理端/服务端:`VoucherManagementController` - `POST /api/pricing/voucher/batch/create`、`/batch/create/v2` — 创建券码批次 - `POST /api/pricing/voucher/batch/list` — 批次分页列表 - `GET /api/pricing/voucher/batch/{id}` — 批次详情 - `GET /api/pricing/voucher/batch/{id}/stats` — 批次统计 - `PUT /api/pricing/voucher/batch/{id}/status` — 批次启用/禁用 - `POST /api/pricing/voucher/codes` — 券码分页列表 - `PUT /api/pricing/voucher/code/{id}/use` — 标记某券码为已使用 - `GET /api/pricing/voucher/scenic/{scenicId}/users` — 景区用户券码概览 移动端:`VoucherManagementController` - `POST /api/pricing/voucher/mobile/claim` — 领取券码 - `GET /api/pricing/voucher/mobile/my-codes` — 我的券码列表 使用记录:`VoucherUsageController` - `POST /api/pricing/voucher/usage/history` — 使用记录分页 - `GET /api/pricing/voucher/usage/{voucherCode}/records` — 按券码查询记录 - `GET /api/pricing/voucher/usage/{voucherCode}/stats` — 券码统计 - `GET /api/pricing/voucher/usage/user/{faceId}/scenic/{scenicId}` — 用户在景区的记录 - `GET /api/pricing/voucher/usage/batch/{batchId}/stats` — 批次使用统计 ### 4. 关键业务规则 #### 领取限制 - 同一 `faceId` 在同一 `scenicId` 中只能领取一次券码 - 只有启用状态的批次才能领取券码 - 批次必须有可用券码才能成功领取 #### 使用验证 - 券码必须为可用状态(CLAIMED_AVAILABLE/CLAIMED_UNUSED) - 必须验证券码与景区的匹配关系 - 使用后自动更新批次统计数据 ## 统一优惠检测系统 (Unified Discount Detection) ### 1. 架构设计 采用策略模式的可扩展优惠检测系统,统一管理并自动组合多种优惠类型。 #### 核心接口 ```java // 优惠提供者接口 public interface IDiscountProvider { String getProviderType(); // 提供者类型 int getPriority(); // 优先级(数字越大越高) List detectAvailableDiscounts(); // 检测可用优惠 DiscountResult applyDiscount(); // 应用优惠 } // 优惠检测服务接口 public interface IDiscountDetectionService { DiscountCombinationResult calculateOptimalCombination(); // 计算最优组合 DiscountCombinationResult previewOptimalCombination(); // 预览优惠组合 } ``` ### 2. 优惠提供者实现(当前实现与优先级) #### OnePricePurchaseDiscountProvider (优先级: 120) - 处理一口价优惠逻辑(景区级统一价格) - **最高优先级**,优先于所有其他优惠类型 - 商品级别控制:检查购物车中所有商品的 `canUseOnePrice` 配置,任一商品不支持则跳过检测 - 仅当一口价小于当前金额时产生优惠;是否可与券码/优惠券叠加由配置 `canUseCoupon/canUseVoucher` 决定 #### BundleDiscountProvider (优先级: 100) - 处理打包购买优惠逻辑(多商品组合优惠) - 支持多种优惠类型:固定减免、百分比折扣、固定价格 - 可配置叠加规则(与优惠券、券码、一口价的组合限制) - 自动检测购物车中符合条件的商品组合 #### VoucherDiscountProvider (优先级: 80) - 处理券码优惠逻辑 - 支持用户主动输入券码或自动选择最优券码 - 全场免费券码不可与其他优惠叠加 #### CouponDiscountProvider (优先级: 60) - 处理优惠券优惠逻辑 - **最低优先级**,在所有其他优惠之后应用 - 自动选择最优优惠券 ### 3. 优惠应用策略 #### 优先级规则 ``` 一口价 (120) → 打包购买 (100) → 券码 (80) → 优惠券 (60) ``` #### 叠加逻辑 ```java 原价 → 一口价 → 打包购买 → 券码 → 优惠券 → 最终价格 特殊情况: - 全场免费券码:直接最终价=0,停止后续优惠 - 一口价:可叠加性由配置 canUseCoupon / canUseVoucher 控制;商品级别由 canUseOnePrice 控制参与检测 ``` #### 扩展支持 ##### 添加新优惠类型 ```java @Component public class FlashSaleDiscountProvider implements IDiscountProvider { @Override public String getProviderType() { return "LIMITED_TIME"; } @Override public int getPriority() { return 90; } // 示例:介于券码和优惠券之间 // 实现其他方法... } ``` ##### 动态注册 ```java // 系统启动时自动扫描所有 IDiscountProvider 实现类 // 按优先级排序并注册到 DiscountDetectionService 中 ``` ## 打包购买优惠系统 (Bundle Discount System) ### 1. 核心特性 打包购买优惠系统是新增的优惠类型,支持多商品组合优惠策略,具有第二高优先级(仅次于一口价)。 #### 优惠类型支持 - **FIXED_DISCOUNT**: 固定减免金额(如满2件减50元) - **PERCENTAGE_DISCOUNT**: 百分比折扣(如多商品组合9折) - **FIXED_PRICE**: 固定套餐价格(如照片+视频套餐199元) #### 触发条件 - **商品数量要求**: 最低购买数量限制 - **商品金额要求**: 最低购买金额限制 - **商品类型组合**: 特定商品类型的组合(如照片+视频) ### 2. 业务规则 #### 自动检测规则 ```java // 多商品类型组合优惠 - 条件:购买不同类型商品 >= 2种 - 优惠:9折优惠 - 可叠加:可与优惠券、券码叠加,不可与一口价叠加 // 大批量购买优惠 - 条件:总数量 >= 10件 且 总金额 >= 500元 - 优惠:减免50元 - 可叠加:可与优惠券、券码叠加,不可与一口价叠加 // 特定组合套餐 - 条件:同时购买照片集和Vlog视频 - 优惠:套餐价199元 - 可叠加:不可与其他优惠叠加 ``` #### 叠加规则配置 每个打包优惠规则都可以独立配置与其他优惠的叠加关系: - `canUseWithCoupon`: 是否可与优惠券叠加 - `canUseWithVoucher`: 是否可与券码叠加 - `canUseWithOnePrice`: 是否可与一口价叠加 ### 3. 核心接口 #### IBundleDiscountService ```java // 检测可用的打包优惠 List detectAvailableBundleDiscounts(DiscountDetectionContext context); // 计算打包优惠金额 BigDecimal calculateBundleDiscount(BundleDiscountInfo bundleDiscount, List products); // 获取最优的打包优惠组合 BundleDiscountInfo getBestBundleDiscount(List products, Long scenicId); ``` #### BundleDiscountProvider - 实现 `IDiscountProvider` 接口 - 优先级:100(第二高,仅次于一口价的120) - 自动集成到统一优惠检测系统 ### 4. 扩展开发 #### 添加新的打包规则 ```java // 在 BundleDiscountServiceImpl 中添加新规则 private BundleDiscountInfo createNewBundleRule() { BundleDiscountInfo bundle = new BundleDiscountInfo(); bundle.setBundleConfigId(4L); bundle.setBundleName("新打包规则"); bundle.setDiscountType("PERCENTAGE_DISCOUNT"); bundle.setDiscountValue(new BigDecimal("0.85")); // 8.5折 bundle.setMinQuantity(5); bundle.setMinAmount(new BigDecimal("300")); // 配置叠加规则... return bundle; } ``` #### 数据库配置支持 后续可以扩展为从数据库加载打包规则配置,替换当前的硬编码规则。 ## API 接口扩展 ### 1. 价格计算接口扩展 #### 新增请求参数(已存在) ```java public class PriceCalculationRequest { // 原有字段... private String voucherCode; // 用户输入的券码 private Long scenicId; // 景区ID private Long faceId; // 用户faceId private Boolean autoUseVoucher; // 是否自动使用券码 private Boolean previewOnly; // 是否仅预览优惠 } ``` #### 新增响应字段(已存在) ```java public class PriceCalculationResult { // 原有字段... private VoucherInfo usedVoucher; // 使用的券码信息 private List availableDiscounts; // 可用优惠列表(预览模式) } ``` ### 2. 一口价配置管理(OnePrice) `OnePricePurchaseController`(管理端): - `GET /api/pricing/admin/one-price/` — 分页查询 - `GET /api/pricing/admin/one-price/all` — 全量查询 - `GET /api/pricing/admin/one-price/{id}` — 详情 - `POST /api/pricing/admin/one-price/` — 创建 - `PUT /api/pricing/admin/one-price/{id}` — 更新 - `DELETE /api/pricing/admin/one-price/{id}` — 删除 - `PUT /api/pricing/admin/one-price/{id}/status` — 启用/禁用 - `GET /api/pricing/admin/one-price/scenic/{scenicId}` — 按景区查询启用配置 - `GET /api/pricing/admin/one-price/check/{scenicId}` — 景区是否适用一口价 ## 测试策略 ### 1. 单元测试 建议覆盖: - 价格计算核心流程与边界 - 优惠券/券码/一口价适用性与叠加规则 - 异常场景与异常处理器 ### 2. 集成测试 - 数据库读写与分页 - JSON 序列化/反序列化(TypeHandler) - API 端点的入参/出参校验 ### 3. 配置校验 - 校验各 ProductType 的默认配置完整性 - 关键枚举与配置代码路径的兼容性 ## 数据库设计 ### 核心表结构(摘) - `price_product_config`: 商品价格基础配置(包含 `can_use_coupon`、`can_use_voucher`、`can_use_one_price` 优惠控制字段) - `price_tier_config`: 分层定价配置 - `price_bundle_config`: 套餐配置 - `price_coupon_config`: 优惠券配置 - `price_coupon_claim_record`: 优惠券领取记录 ### 新增表结构 - `price_voucher_batch_config`: 券码批次配置表 - `price_voucher_code`: 券码表 - `price_voucher_usage_record`: 券码使用记录表 - `voucher_print_record`: 券码打印记录表 - `price_one_price_config`: 一口价配置表 ### 索引优化(示例) ```sql -- 券码查询优化 CREATE INDEX idx_voucher_code ON price_voucher_code(code); CREATE INDEX idx_face_scenic ON price_voucher_code(face_id, scenic_id); -- 批次查询优化 CREATE INDEX idx_scenic_broker ON price_voucher_batch_config(scenic_id, broker_id); -- 使用记录与打印记录查询优化(示例) CREATE INDEX idx_usage_code ON price_voucher_usage_record(voucher_code); CREATE INDEX idx_usage_face_scenic ON price_voucher_usage_record(face_id, scenic_id); CREATE INDEX idx_print_face_scenic ON voucher_print_record(face_id, scenic_id); ``` ### 性能考虑 - 券码表可能数据量较大,考虑按景区维度分表或归档 - 定期清理已删除的过期数据 - 使用数据完整性检查 SQL 验证统计数据准确性 ## 兼容性与注意事项 - 本模块使用 PageHelper(优惠券相关)与 MyBatis‑Plus(券码/一口价等)并存,请根据对应 Service/Mapper 选择分页与查询方式。 - 优惠优先级及叠加规则以各 Provider 与业务配置为准,避免在外层重复实现优先级判断逻辑。 - 若扩展新的优惠类型,务必实现 `IDiscountProvider` 并在 `IDiscountDetectionService` 中完成注册(当前实现通过组件扫描自动注册并排序)。