feat(pricing): 支持景区维度的价格配置和优惠策略控制

- 新增按景区ID查询商品配置和阶梯价格配置的方法
- 扩展价格计算服务以支持景区级别的优惠策略
- 更新优惠券和代金券提供者以使用景区维度配置
- 修改商品配置服务实现多级查询优先级(景区特定->景区默认->全局特定->全局默认)
- 添加商品类型能力服务测试用例
- 增强价格计算逻辑的容错性和向后兼容性
This commit is contained in:
2025-11-27 13:55:51 +08:00
parent 3ce3972875
commit 8a88c74df2
9 changed files with 357 additions and 77 deletions

View File

@@ -33,6 +33,14 @@ public interface PriceProductConfigMapper extends BaseMapper<PriceProductConfig>
*/ */
@Select("SELECT * FROM price_product_config WHERE product_type = #{productType} AND product_id = #{productId} AND is_active = 1") @Select("SELECT * FROM price_product_config WHERE product_type = #{productType} AND product_id = #{productId} AND is_active = 1")
PriceProductConfig selectByProductTypeAndId(String productType, String productId); PriceProductConfig selectByProductTypeAndId(String productType, String productId);
/**
* 根据商品类型、商品ID和景区ID查询配置(支持景区维度)
*/
@Select("SELECT * FROM price_product_config WHERE product_type = #{productType} AND product_id = #{productId} AND scenic_id = #{scenicId} AND is_active = 1")
PriceProductConfig selectByProductTypeIdAndScenic(@Param("productType") String productType,
@Param("productId") String productId,
@Param("scenicId") String scenicId);
/** /**
* 检查是否存在default配置(包含禁用的) * 检查是否存在default配置(包含禁用的)

View File

@@ -26,6 +26,18 @@ public interface PriceTierConfigMapper extends BaseMapper<PriceTierConfig> {
PriceTierConfig selectByProductTypeAndQuantity(@Param("productType") String productType, PriceTierConfig selectByProductTypeAndQuantity(@Param("productType") String productType,
@Param("productId") String productId, @Param("productId") String productId,
@Param("quantity") Integer quantity); @Param("quantity") Integer quantity);
/**
* 根据商品类型、商品ID、数量和景区ID查询匹配的阶梯价格(支持景区维度)
*/
@Select("SELECT * FROM price_tier_config WHERE product_type = #{productType} " +
"AND product_id = #{productId} AND scenic_id = #{scenicId} " +
"AND #{quantity} >= min_quantity AND #{quantity} <= max_quantity " +
"AND is_active = 1 ORDER BY sort_order ASC LIMIT 1")
PriceTierConfig selectByProductTypeQuantityAndScenic(@Param("productType") String productType,
@Param("productId") String productId,
@Param("quantity") Integer quantity,
@Param("scenicId") String scenicId);
/** /**
* 根据商品类型查询所有阶梯配置 * 根据商品类型查询所有阶梯配置

View File

@@ -23,22 +23,55 @@ public interface IProductConfigService {
/** /**
* 根据商品类型和商品ID获取精确配置 * 根据商品类型和商品ID获取精确配置
* *
* @param productType 商品类型 * @param productType 商品类型
* @param productId 具体商品ID * @param productId 具体商品ID
* @return 商品配置 * @return 商品配置
*/ */
PriceProductConfig getProductConfig(String productType, String productId); PriceProductConfig getProductConfig(String productType, String productId);
/**
* 根据商品类型、商品ID和景区ID获取精确配置(支持景区维度的优惠策略控制)
*
* 查询优先级:
* 1. 景区+商品ID: (productType, productId, scenicId)
* 2. 景区+默认: (productType, "default", scenicId)
* 3. 全局+商品ID: (productType, productId, null)
* 4. 全局+默认: (productType, "default", null)
*
* @param productType 商品类型
* @param productId 具体商品ID
* @param scenicId 景区ID
* @return 商品配置(包含优惠策略控制字段)
*/
PriceProductConfig getProductConfig(String productType, String productId, Long scenicId);
/** /**
* 根据商品类型、商品ID和数量获取阶梯价格配置 * 根据商品类型、商品ID和数量获取阶梯价格配置
* *
* @param productType 商品类型 * @param productType 商品类型
* @param productId 具体商品ID * @param productId 具体商品ID
* @param quantity 数量 * @param quantity 数量
* @return 阶梯价格配置 * @return 阶梯价格配置
*/ */
PriceTierConfig getTierConfig(String productType, String productId, Integer quantity); PriceTierConfig getTierConfig(String productType, String productId, Integer quantity);
/**
* 根据商品类型、商品ID、数量和景区ID获取阶梯价格配置(支持景区维度)
*
* 查询优先级:
* 1. 景区+商品ID: (productType, productId, quantity, scenicId)
* 2. 景区+默认: (productType, "default", quantity, scenicId)
* 3. 全局+商品ID: (productType, productId, quantity, null)
* 4. 全局+默认: (productType, "default", quantity, null)
*
* @param productType 商品类型
* @param productId 具体商品ID
* @param quantity 数量
* @param scenicId 景区ID
* @return 阶梯价格配置
*/
PriceTierConfig getTierConfig(String productType, String productId, Integer quantity, Long scenicId);
/** /**
* 获取所有启用的商品配置 * 获取所有启用的商品配置

View File

@@ -138,18 +138,18 @@ public class CouponDiscountProvider implements IDiscountProvider {
} }
} }
// 检查单个商品的优惠券使用开关 // 检查单个商品的优惠券使用开关(使用景区维度配置)
for (ProductItem product : context.getProducts()) { for (ProductItem product : context.getProducts()) {
String productId = product.getProductId() != null ? product.getProductId() : "default"; String productId = product.getProductId() != null ? product.getProductId() : "default";
try { try {
PriceProductConfig productConfig = productConfigService.getProductConfig( PriceProductConfig productConfig = productConfigService.getProductConfig(
product.getProductType().getCode(), productId); product.getProductType().getCode(), productId, context.getScenicId());
if (productConfig != null) { if (productConfig != null) {
if (!Boolean.TRUE.equals(productConfig.getCanUseCoupon())) { if (!Boolean.TRUE.equals(productConfig.getCanUseCoupon())) {
log.debug("商品配置不允许使用优惠券: productType={}, productId={}", log.debug("商品配置不允许使用优惠券: productType={}, productId={}, scenicId={}",
product.getProductType().getCode(), productId); product.getProductType().getCode(), productId, context.getScenicId());
return false; return false;
} }
} }
@@ -157,18 +157,18 @@ public class CouponDiscountProvider implements IDiscountProvider {
// 如果获取具体商品配置失败,尝试获取default配置 // 如果获取具体商品配置失败,尝试获取default配置
try { try {
PriceProductConfig defaultConfig = productConfigService.getProductConfig( PriceProductConfig defaultConfig = productConfigService.getProductConfig(
product.getProductType().getCode(), "default"); product.getProductType().getCode(), "default", context.getScenicId());
if (defaultConfig != null) { if (defaultConfig != null) {
if (!Boolean.TRUE.equals(defaultConfig.getCanUseCoupon())) { if (!Boolean.TRUE.equals(defaultConfig.getCanUseCoupon())) {
log.debug("商品默认配置不允许使用优惠券: productType={}", log.debug("商品默认配置不允许使用优惠券: productType={}, scenicId={}",
product.getProductType().getCode()); product.getProductType().getCode(), context.getScenicId());
return false; return false;
} }
} }
} catch (Exception ex) { } catch (Exception ex) {
log.warn("获取商品配置失败,默认允许使用优惠券: productType={}, productId={}", log.warn("获取商品配置失败,默认允许使用优惠券: productType={}, productId={}, scenicId={}",
product.getProductType().getCode(), productId); product.getProductType().getCode(), productId, context.getScenicId());
} }
} }
} }

View File

@@ -54,8 +54,8 @@ public class OnePricePurchaseDiscountProvider implements IDiscountProvider {
return discounts; return discounts;
} }
// 检查商品是否支持一口价优惠 // 检查商品是否支持一口价优惠(使用景区维度配置)
if (!areAllProductsSupportOnePrice(context.getProducts())) { if (!areAllProductsSupportOnePrice(context.getProducts(), context.getScenicId())) {
log.debug("存在不支持一口价优惠的商品,跳过一口价检测"); log.debug("存在不支持一口价优惠的商品,跳过一口价检测");
return discounts; return discounts;
} }
@@ -182,49 +182,48 @@ public class OnePricePurchaseDiscountProvider implements IDiscountProvider {
} }
/** /**
* 检查购物车中的所有商品是否都支持一口价优惠 * 检查购物车中的所有商品是否都支持一口价优惠(使用景区维度配置)
*/ */
private boolean areAllProductsSupportOnePrice(List<ProductItem> products) { private boolean areAllProductsSupportOnePrice(List<ProductItem> products, Long scenicId) {
if (products == null || products.isEmpty()) { if (products == null || products.isEmpty()) {
return true; // 空购物车时默认支持 return true; // 空购物车时默认支持
} }
for (ProductItem product : products) { for (ProductItem product : products) {
try { try {
// 查询商品配置 // 查询商品配置(使用景区维度)
PriceProductConfig productConfig = productConfigService.getProductConfig( PriceProductConfig productConfig = productConfigService.getProductConfig(
product.getProductType().getCode(), product.getProductId()); product.getProductType().getCode(), product.getProductId(), scenicId);
if (productConfig != null) { if (productConfig != null) {
// 检查商品是否支持一口价优惠 // 检查商品是否支持一口价优惠
if (Boolean.FALSE.equals(productConfig.getCanUseOnePrice())) { if (Boolean.FALSE.equals(productConfig.getCanUseOnePrice())) {
log.debug("商品 {}({}) 不支持一口价优惠", log.debug("商品 {}({}) 在景区 {} 不支持一口价优惠",
product.getProductType().getCode(), product.getProductId()); product.getProductType().getCode(), product.getProductId(), scenicId);
return false; return false;
} }
} else { } else {
// 如果找不到具体商品配置,尝试查询 default 配置 // 如果找不到具体商品配置,尝试查询 default 配置
PriceProductConfig defaultConfig = productConfigService.getProductConfig( PriceProductConfig defaultConfig = productConfigService.getProductConfig(
product.getProductType().getCode(), "default"); product.getProductType().getCode(), "default", scenicId);
if (defaultConfig != null) { if (defaultConfig != null) {
if (Boolean.FALSE.equals(defaultConfig.getCanUseOnePrice())) { if (Boolean.FALSE.equals(defaultConfig.getCanUseOnePrice())) {
log.debug("商品类型 {} 的默认配置不支持一口价优惠", log.debug("商品类型 {} 在景区 {} 的默认配置不支持一口价优惠",
product.getProductType().getCode()); product.getProductType().getCode(), scenicId);
return false; return false;
} }
} else { } else {
// 如果既没有具体配置也没有默认配置,默认支持一口价优惠 // 如果既没有具体配置也没有默认配置,默认支持(保持向后兼容)
log.debug("商品 {}({}) 未找到价格配置,默认支持一口价优惠", log.debug("商品 {}({}) 在景区 {} 未找到价格配置,默认支持一口价优惠",
product.getProductType().getCode(), product.getProductId()); product.getProductType().getCode(), product.getProductId(), scenicId);
return false; // 改为默认支持,避免配置缺失导致一口价功能不可用
} }
} }
} catch (Exception e) { } catch (Exception e) {
log.warn("检查商品 {}({}) 一口价优惠支持情况时发生异常,默认支持", log.warn("检查商品 {}({}) 在景区 {} 一口价优惠支持情况时发生异常,默认支持",
product.getProductType().getCode(), product.getProductId(), e); product.getProductType().getCode(), product.getProductId(), scenicId, e);
// 异常情况下默认支持,避免出现意外情况 // 异常情况下默认支持,确保业务流程不受影响
return false;
} }
} }

View File

@@ -66,8 +66,8 @@ public class PriceCalculationServiceImpl implements IPriceCalculationService {
product.setQuantity(1); product.setQuantity(1);
} }
}); });
// 计算商品价格和原价 // 计算商品价格和原价(传入景区ID以支持景区级优惠策略控制)
PriceDetails priceDetails = calculateProductsPriceWithOriginal(request.getProducts()); PriceDetails priceDetails = calculateProductsPriceWithOriginal(request.getProducts(), request.getScenicId());
BigDecimal totalAmount = priceDetails.getTotalAmount(); BigDecimal totalAmount = priceDetails.getTotalAmount();
BigDecimal originalTotalAmount = priceDetails.getOriginalTotalAmount(); BigDecimal originalTotalAmount = priceDetails.getOriginalTotalAmount();
@@ -161,27 +161,27 @@ public class PriceCalculationServiceImpl implements IPriceCalculationService {
return totalAmount.setScale(2, RoundingMode.HALF_UP); return totalAmount.setScale(2, RoundingMode.HALF_UP);
} }
private PriceDetails calculateProductsPriceWithOriginal(List<ProductItem> products) { private PriceDetails calculateProductsPriceWithOriginal(List<ProductItem> products, Long scenicId) {
BigDecimal totalAmount = BigDecimal.ZERO; BigDecimal totalAmount = BigDecimal.ZERO;
BigDecimal originalTotalAmount = BigDecimal.ZERO; BigDecimal originalTotalAmount = BigDecimal.ZERO;
for (ProductItem product : products) { for (ProductItem product : products) {
// 计算实际价格和原价 // 计算实际价格和原价(传入景区ID)
ProductPriceInfo priceInfo = calculateSingleProductPriceWithOriginal(product); ProductPriceInfo priceInfo = calculateSingleProductPriceWithOriginal(product, scenicId);
product.setUnitPrice(priceInfo.getActualPrice()); product.setUnitPrice(priceInfo.getActualPrice());
product.setOriginalPrice(priceInfo.getOriginalPrice()); product.setOriginalPrice(priceInfo.getOriginalPrice());
BigDecimal subtotal = priceInfo.getActualPrice().multiply(BigDecimal.valueOf(product.getPurchaseCount())); BigDecimal subtotal = priceInfo.getActualPrice().multiply(BigDecimal.valueOf(product.getPurchaseCount()));
BigDecimal originalSubtotal = priceInfo.getOriginalPrice().multiply(BigDecimal.valueOf(product.getPurchaseCount())); BigDecimal originalSubtotal = priceInfo.getOriginalPrice().multiply(BigDecimal.valueOf(product.getPurchaseCount()));
product.setSubtotal(subtotal); product.setSubtotal(subtotal);
totalAmount = totalAmount.add(subtotal); totalAmount = totalAmount.add(subtotal);
originalTotalAmount = originalTotalAmount.add(originalSubtotal); originalTotalAmount = originalTotalAmount.add(originalSubtotal);
} }
return new PriceDetails( return new PriceDetails(
totalAmount.setScale(2, RoundingMode.HALF_UP), totalAmount.setScale(2, RoundingMode.HALF_UP),
originalTotalAmount.setScale(2, RoundingMode.HALF_UP) originalTotalAmount.setScale(2, RoundingMode.HALF_UP)
@@ -244,27 +244,27 @@ public class PriceCalculationServiceImpl implements IPriceCalculationService {
throw new PriceCalculationException("无法计算商品价格: " + productType.getDescription() + ", productId: " + productId); throw new PriceCalculationException("无法计算商品价格: " + productType.getDescription() + ", productId: " + productId);
} }
private ProductPriceInfo calculateSingleProductPriceWithOriginal(ProductItem product) { private ProductPriceInfo calculateSingleProductPriceWithOriginal(ProductItem product, Long scenicId) {
ProductType productType = product.getProductType(); ProductType productType = product.getProductType();
String productId = product.getProductId() != null ? product.getProductId() : "default"; String productId = product.getProductId() != null ? product.getProductId() : "default";
BigDecimal actualPrice; BigDecimal actualPrice;
BigDecimal originalPrice = null; BigDecimal originalPrice = null;
// 优先使用基于product_id的阶梯定价 // 优先使用基于product_id的阶梯定价(带景区ID)
PriceTierConfig tierConfig = productConfigService.getTierConfig( PriceTierConfig tierConfig = productConfigService.getTierConfig(
productType.getCode(), productId, product.getQuantity()); productType.getCode(), productId, product.getQuantity(), scenicId);
if (tierConfig != null) { if (tierConfig != null) {
actualPrice = tierConfig.getPrice(); actualPrice = tierConfig.getPrice();
originalPrice = tierConfig.getOriginalPrice(); originalPrice = tierConfig.getOriginalPrice();
log.debug("使用阶梯定价: productType={}, productId={}, quantity={}, price={}, originalPrice={}", log.debug("使用阶梯定价: productType={}, productId={}, quantity={}, scenicId={}, price={}, originalPrice={}",
productType.getCode(), productId, product.getQuantity(), actualPrice, originalPrice); productType.getCode(), productId, product.getQuantity(), scenicId, actualPrice, originalPrice);
} else { } else {
// 使用基于product_id的基础配置 // 使用基于product_id的基础配置(带景区ID)
try { try {
PriceProductConfig baseConfig = productConfigService.getProductConfig(productType.getCode(), productId); PriceProductConfig baseConfig = productConfigService.getProductConfig(productType.getCode(), productId, scenicId);
if (baseConfig != null) { if (baseConfig != null) {
actualPrice = baseConfig.getBasePrice(); actualPrice = baseConfig.getBasePrice();
originalPrice = baseConfig.getOriginalPrice(); originalPrice = baseConfig.getOriginalPrice();
@@ -279,12 +279,12 @@ public class PriceCalculationServiceImpl implements IPriceCalculationService {
throw new PriceCalculationException("无法找到具体商品配置"); throw new PriceCalculationException("无法找到具体商品配置");
} }
} catch (Exception e) { } catch (Exception e) {
log.warn("未找到具体商品配置: productType={}, productId={}, 尝试使用通用配置", log.warn("未找到具体商品配置: productType={}, productId={}, scenicId={}, 尝试使用通用配置",
productType, productId); productType, productId, scenicId);
// 兜底:使用default配置 // 兜底:使用default配置(带景区ID)
try { try {
PriceProductConfig defaultConfig = productConfigService.getProductConfig(productType.getCode(), "default"); PriceProductConfig defaultConfig = productConfigService.getProductConfig(productType.getCode(), "default", scenicId);
if (defaultConfig != null) { if (defaultConfig != null) {
actualPrice = defaultConfig.getBasePrice(); actualPrice = defaultConfig.getBasePrice();
originalPrice = defaultConfig.getOriginalPrice(); originalPrice = defaultConfig.getOriginalPrice();
@@ -299,8 +299,8 @@ public class PriceCalculationServiceImpl implements IPriceCalculationService {
throw new PriceCalculationException("无法找到default配置"); throw new PriceCalculationException("无法找到default配置");
} }
} catch (Exception defaultEx) { } catch (Exception defaultEx) {
log.warn("未找到default配置: productType={}", productType.getCode()); log.warn("未找到default配置: productType={}, scenicId={}", productType.getCode(), scenicId);
// 最后兜底:使用通用配置(向后兼容) // 最后兜底:使用通用配置(向后兼容)
List<PriceProductConfig> configs = productConfigService.getProductConfig(productType.getCode()); List<PriceProductConfig> configs = productConfigService.getProductConfig(productType.getCode());
if (!configs.isEmpty()) { if (!configs.isEmpty()) {
@@ -320,7 +320,7 @@ public class PriceCalculationServiceImpl implements IPriceCalculationService {
} }
} }
} }
return new ProductPriceInfo(actualPrice, originalPrice); return new ProductPriceInfo(actualPrice, originalPrice);
} }

View File

@@ -71,6 +71,107 @@ public class ProductConfigServiceImpl implements IProductConfigService {
productType, productId, quantity, config); productType, productId, quantity, config);
return config; return config;
} }
@Override
// @Cacheable(value = "product-config", key = "#productType + '_' + #productId + '_' + #scenicId")
public PriceProductConfig getProductConfig(String productType, String productId, Long scenicId) {
if (scenicId == null) {
// 如果没有景区ID,使用原有逻辑
return getProductConfig(productType, productId);
}
// 查询优先级:
// 1. 景区+商品ID
PriceProductConfig config = productConfigMapper.selectByProductTypeIdAndScenic(
productType, productId, scenicId.toString());
if (config != null) {
log.debug("使用景区特定商品配置: productType={}, productId={}, scenicId={}",
productType, productId, scenicId);
return config;
}
// 2. 景区+默认
if (!"default".equals(productId)) {
config = productConfigMapper.selectByProductTypeIdAndScenic(
productType, "default", scenicId.toString());
if (config != null) {
log.debug("使用景区默认配置: productType={}, scenicId={}", productType, scenicId);
return config;
}
}
// 3. 全局+商品ID (兜底)
try {
config = productConfigMapper.selectByProductTypeAndId(productType, productId);
if (config != null) {
log.debug("使用全局商品配置: productType={}, productId={}", productType, productId);
return config;
}
} catch (Exception e) {
log.debug("全局商品配置未找到: productType={}, productId={}", productType, productId);
}
// 4. 全局+默认 (最后兜底)
config = productConfigMapper.selectByProductTypeAndId(productType, "default");
if (config != null) {
log.debug("使用全局默认配置: productType={}", productType);
return config;
}
throw new ProductConfigNotFoundException(
String.format("商品配置未找到: productType=%s, productId=%s, scenicId=%s",
productType, productId, scenicId));
}
@Override
// @Cacheable(value = "tier-config", key = "#productType + '_' + #productId + '_' + #quantity + '_' + #scenicId")
public PriceTierConfig getTierConfig(String productType, String productId, Integer quantity, Long scenicId) {
if (quantity == null || quantity <= 0) {
return null;
}
if (scenicId == null) {
// 如果没有景区ID,使用原有逻辑
return getTierConfig(productType, productId, quantity);
}
// 查询优先级:
// 1. 景区+商品ID
PriceTierConfig config = tierConfigMapper.selectByProductTypeQuantityAndScenic(
productType, productId, quantity, scenicId.toString());
if (config != null) {
log.debug("使用景区特定阶梯定价: productType={}, productId={}, quantity={}, scenicId={}",
productType, productId, quantity, scenicId);
return config;
}
// 2. 景区+默认
if (!"default".equals(productId)) {
config = tierConfigMapper.selectByProductTypeQuantityAndScenic(
productType, "default", quantity, scenicId.toString());
if (config != null) {
log.debug("使用景区默认阶梯定价: productType={}, quantity={}, scenicId={}",
productType, quantity, scenicId);
return config;
}
}
// 3. 全局+商品ID (兜底)
config = tierConfigMapper.selectByProductTypeAndQuantity(productType, productId, quantity);
if (config != null) {
log.debug("使用全局阶梯定价: productType={}, productId={}, quantity={}",
productType, productId, quantity);
return config;
}
// 4. 全局+默认 (最后兜底)
config = tierConfigMapper.selectByProductTypeAndQuantity(productType, "default", quantity);
if (config != null) {
log.debug("使用全局默认阶梯定价: productType={}, quantity={}", productType, quantity);
}
return config;
}
@Override @Override
// @Cacheable(value = "active-product-configs") // @Cacheable(value = "active-product-configs")

View File

@@ -224,18 +224,18 @@ public class VoucherDiscountProvider implements IDiscountProvider {
} }
} }
// 检查单个商品的券码使用开关 // 检查单个商品的券码使用开关(使用景区维度配置)
for (ProductItem product : context.getProducts()) { for (ProductItem product : context.getProducts()) {
String productId = product.getProductId() != null ? product.getProductId() : "default"; String productId = product.getProductId() != null ? product.getProductId() : "default";
try { try {
PriceProductConfig productConfig = productConfigService.getProductConfig( PriceProductConfig productConfig = productConfigService.getProductConfig(
product.getProductType().getCode(), productId); product.getProductType().getCode(), productId, context.getScenicId());
if (productConfig != null) { if (productConfig != null) {
if (!Boolean.TRUE.equals(productConfig.getCanUseVoucher())) { if (!Boolean.TRUE.equals(productConfig.getCanUseVoucher())) {
log.info("商品配置不允许使用券码: productType={}, productId={}", log.info("商品配置不允许使用券码: productType={}, productId={}, scenicId={}",
product.getProductType().getCode(), productId); product.getProductType().getCode(), productId, context.getScenicId());
return false; return false;
} }
} }
@@ -243,18 +243,18 @@ public class VoucherDiscountProvider implements IDiscountProvider {
// 如果获取具体商品配置失败,尝试获取default配置 // 如果获取具体商品配置失败,尝试获取default配置
try { try {
PriceProductConfig defaultConfig = productConfigService.getProductConfig( PriceProductConfig defaultConfig = productConfigService.getProductConfig(
product.getProductType().getCode(), "default"); product.getProductType().getCode(), "default", context.getScenicId());
if (defaultConfig != null) { if (defaultConfig != null) {
if (!Boolean.TRUE.equals(defaultConfig.getCanUseVoucher())) { if (!Boolean.TRUE.equals(defaultConfig.getCanUseVoucher())) {
log.debug("商品默认配置不允许使用券码: productType={}", log.debug("商品默认配置不允许使用券码: productType={}, scenicId={}",
product.getProductType().getCode()); product.getProductType().getCode(), context.getScenicId());
return false; return false;
} }
} }
} catch (Exception ex) { } catch (Exception ex) {
log.warn("获取商品配置失败,默认允许使用券码: productType={}, productId={}", log.warn("获取商品配置失败,默认允许使用券码: productType={}, productId={}, scenicId={}",
product.getProductType().getCode(), productId); product.getProductType().getCode(), productId, context.getScenicId());
} }
} }
} }

View File

@@ -0,0 +1,127 @@
package com.ycwl.basic.product.service;
import com.ycwl.basic.product.capability.DuplicateCheckStrategy;
import com.ycwl.basic.product.capability.PricingMode;
import com.ycwl.basic.product.capability.ProductTypeCapability;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assertions.*;
/**
* 商品类型能力服务测试
*/
@SpringBootTest
public class ProductTypeCapabilityServiceTest {
@Autowired(required = false)
private IProductTypeCapabilityService productTypeCapabilityService;
@Test
public void testGetCapability_VLOG_VIDEO() {
if (productTypeCapabilityService == null) {
System.out.println("服务未注入,跳过测试(可能数据库未迁移)");
return;
}
ProductTypeCapability capability = productTypeCapabilityService.getCapability("VLOG_VIDEO");
assertNotNull(capability);
assertEquals("VLOG_VIDEO", capability.getProductType());
assertEquals("Vlog视频", capability.getDisplayName());
assertEquals("VIDEO", capability.getCategory());
assertEquals(PricingMode.FIXED, capability.getPricingModeEnum());
assertEquals(false, capability.getAllowDuplicatePurchase());
assertEquals(DuplicateCheckStrategy.CHECK_BY_VIDEO_ID, capability.getDuplicateCheckStrategyEnum());
}
@Test
public void testGetCapability_PHOTO_PRINT() {
if (productTypeCapabilityService == null) {
System.out.println("服务未注入,跳过测试(可能数据库未迁移)");
return;
}
ProductTypeCapability capability = productTypeCapabilityService.getCapability("PHOTO_PRINT");
assertNotNull(capability);
assertEquals("PHOTO_PRINT", capability.getProductType());
assertEquals("照片打印", capability.getDisplayName());
assertEquals("PRINT", capability.getCategory());
assertEquals(PricingMode.QUANTITY_BASED, capability.getPricingModeEnum());
assertEquals(true, capability.getAllowDuplicatePurchase());
assertEquals(DuplicateCheckStrategy.NO_CHECK, capability.getDuplicateCheckStrategyEnum());
}
@Test
public void testGetDisplayName() {
if (productTypeCapabilityService == null) {
System.out.println("服务未注入,跳过测试(可能数据库未迁移)");
return;
}
String displayName = productTypeCapabilityService.getDisplayName("RECORDING_SET");
assertEquals("录像集", displayName);
}
@Test
public void testAllowDuplicatePurchase() {
if (productTypeCapabilityService == null) {
System.out.println("服务未注入,跳过测试(可能数据库未迁移)");
return;
}
// 视频类不允许重复购买
assertFalse(productTypeCapabilityService.allowDuplicatePurchase("VLOG_VIDEO"));
// 打印类允许重复购买
assertTrue(productTypeCapabilityService.allowDuplicatePurchase("PHOTO_PRINT"));
}
@Test
public void testGetPricingMode() {
if (productTypeCapabilityService == null) {
System.out.println("服务未注入,跳过测试(可能数据库未迁移)");
return;
}
// 视频类固定价格
assertEquals(PricingMode.FIXED, productTypeCapabilityService.getPricingMode("VLOG_VIDEO"));
// 打印类基于数量
assertEquals(PricingMode.QUANTITY_BASED, productTypeCapabilityService.getPricingMode("PHOTO_PRINT"));
}
@Test
public void testGetCapability_NotFound_ShouldReturnDefault() {
if (productTypeCapabilityService == null) {
System.out.println("服务未注入,跳过测试(可能数据库未迁移)");
return;
}
ProductTypeCapability capability = productTypeCapabilityService.getCapability("NOT_EXIST_TYPE");
// 应该返回默认配置而不是 null
assertNotNull(capability);
assertEquals("NOT_EXIST_TYPE", capability.getProductType());
assertEquals("景区商品", capability.getDisplayName());
assertEquals(PricingMode.FIXED, capability.getPricingModeEnum());
}
@Test
public void testGetCapability_NullOrEmpty_ShouldReturnDefault() {
if (productTypeCapabilityService == null) {
System.out.println("服务未注入,跳过测试(可能数据库未迁移)");
return;
}
ProductTypeCapability capability1 = productTypeCapabilityService.getCapability(null);
assertNotNull(capability1);
assertEquals("景区商品", capability1.getDisplayName());
ProductTypeCapability capability2 = productTypeCapabilityService.getCapability("");
assertNotNull(capability2);
assertEquals("景区商品", capability2.getDisplayName());
}
}