diff --git a/src/main/java/com/ycwl/basic/pricing/dto/DiscountDetail.java b/src/main/java/com/ycwl/basic/pricing/dto/DiscountDetail.java index 1ffff6c..72621a4 100644 --- a/src/main/java/com/ycwl/basic/pricing/dto/DiscountDetail.java +++ b/src/main/java/com/ycwl/basic/pricing/dto/DiscountDetail.java @@ -75,15 +75,16 @@ public class DiscountDetail { } /** - * 创建一口价折扣明细 + * 创建打包购买折扣明细 */ public static DiscountDetail createBundleDiscount(BigDecimal discountAmount) { DiscountDetail detail = new DiscountDetail(); detail.setDiscountType("BUNDLE"); - detail.setDiscountName("一口价优惠"); + detail.setDiscountName("打包购买优惠"); detail.setDiscountAmount(discountAmount); - detail.setDescription("一口价购买更优惠"); - detail.setSortOrder(4); // 一口价排在最后 + detail.setDescription("多商品打包购买更优惠"); + detail.setSortOrder(5); // 打包购买排在后面 return detail; } + } \ No newline at end of file diff --git a/src/main/java/com/ycwl/basic/pricing/dto/DiscountInfo.java b/src/main/java/com/ycwl/basic/pricing/dto/DiscountInfo.java index f5d59c3..2df8aab 100644 --- a/src/main/java/com/ycwl/basic/pricing/dto/DiscountInfo.java +++ b/src/main/java/com/ycwl/basic/pricing/dto/DiscountInfo.java @@ -79,4 +79,9 @@ public class DiscountInfo { * 优惠券ID(如果是coupon类型) */ private Long couponId; + + /** + * 一口价信息(如果是一口价优惠) + */ + private OnePriceInfo onePriceInfo; } \ No newline at end of file diff --git a/src/main/java/com/ycwl/basic/pricing/entity/PriceBundleConfig.java b/src/main/java/com/ycwl/basic/pricing/entity/PriceBundleConfig.java index 219a55a..5eedc07 100644 --- a/src/main/java/com/ycwl/basic/pricing/entity/PriceBundleConfig.java +++ b/src/main/java/com/ycwl/basic/pricing/entity/PriceBundleConfig.java @@ -60,6 +60,16 @@ public class PriceBundleConfig { */ private Boolean isActive; + /** + * 是否可使用优惠券 + */ + private Boolean canUseCoupon; + + /** + * 是否可使用券码 + */ + private Boolean canUseVoucher; + @TableField("create_time") private Date createTime; diff --git a/src/main/java/com/ycwl/basic/pricing/entity/PriceProductConfig.java b/src/main/java/com/ycwl/basic/pricing/entity/PriceProductConfig.java index 4677774..aa4452a 100644 --- a/src/main/java/com/ycwl/basic/pricing/entity/PriceProductConfig.java +++ b/src/main/java/com/ycwl/basic/pricing/entity/PriceProductConfig.java @@ -59,6 +59,16 @@ public class PriceProductConfig { */ private Boolean isActive; + /** + * 是否可使用优惠券 + */ + private Boolean canUseCoupon; + + /** + * 是否可使用券码 + */ + private Boolean canUseVoucher; + @TableField("create_time") private Date createTime; diff --git a/src/main/java/com/ycwl/basic/pricing/mapper/PriceBundleConfigMapper.java b/src/main/java/com/ycwl/basic/pricing/mapper/PriceBundleConfigMapper.java index f65e4f0..2db5b9c 100644 --- a/src/main/java/com/ycwl/basic/pricing/mapper/PriceBundleConfigMapper.java +++ b/src/main/java/com/ycwl/basic/pricing/mapper/PriceBundleConfigMapper.java @@ -17,7 +17,7 @@ public interface PriceBundleConfigMapper extends BaseMapper { */ @Select("SELECT id, bundle_name, scenic_id, bundle_price, " + "included_products, excluded_products, " + - "description, is_active, create_time, update_time " + + "description, is_active, can_use_coupon, can_use_voucher, create_time, update_time " + "FROM price_bundle_config WHERE is_active = 1") @Results({ @Result(column = "included_products", property = "includedProducts", @@ -32,7 +32,7 @@ public interface PriceBundleConfigMapper extends BaseMapper { */ @Select("SELECT id, bundle_name, scenic_id, bundle_price, " + "included_products, excluded_products, " + - "description, is_active, create_time, update_time " + + "description, is_active, can_use_coupon, can_use_voucher, create_time, update_time " + "FROM price_bundle_config WHERE id = #{id} AND is_active = 1") @Results({ @Result(column = "included_products", property = "includedProducts", @@ -49,7 +49,7 @@ public interface PriceBundleConfigMapper extends BaseMapper { */ @Select("SELECT id, bundle_name, scenic_id, bundle_price, " + "included_products, excluded_products, " + - "description, is_active, create_time, update_time " + + "description, is_active, can_use_coupon, can_use_voucher, create_time, update_time " + "FROM price_bundle_config ORDER BY is_active DESC, bundle_name ASC") @Results({ @Result(column = "included_products", property = "includedProducts", @@ -63,9 +63,9 @@ public interface PriceBundleConfigMapper extends BaseMapper { * 插入一口价配置 */ @Insert("INSERT INTO price_bundle_config (bundle_name, scenic_id, bundle_price, included_products, excluded_products, " + - "description, is_active, create_time, update_time) VALUES " + + "description, is_active, can_use_coupon, can_use_voucher, create_time, update_time) VALUES " + "(#{bundleName}, #{scenicId}, #{bundlePrice}, #{includedProducts,typeHandler=com.ycwl.basic.pricing.handler.BundleProductListTypeHandler}, #{excludedProducts,typeHandler=com.ycwl.basic.pricing.handler.BundleProductListTypeHandler}, " + - "#{description}, #{isActive}, NOW(), NOW())") + "#{description}, #{isActive}, #{canUseCoupon}, #{canUseVoucher}, NOW(), NOW())") int insertBundleConfig(PriceBundleConfig config); /** @@ -73,7 +73,7 @@ public interface PriceBundleConfigMapper extends BaseMapper { */ @Update("UPDATE price_bundle_config SET bundle_name = #{bundleName}, scenic_id = #{scenicId}, bundle_price = #{bundlePrice}, " + "included_products = #{includedProducts,typeHandler=com.ycwl.basic.pricing.handler.BundleProductListTypeHandler}, excluded_products = #{excludedProducts,typeHandler=com.ycwl.basic.pricing.handler.BundleProductListTypeHandler}, " + - "description = #{description}, is_active = #{isActive}, update_time = NOW() WHERE id = #{id}") + "description = #{description}, is_active = #{isActive}, can_use_coupon = #{canUseCoupon}, can_use_voucher = #{canUseVoucher}, update_time = NOW() WHERE id = #{id}") int updateBundleConfig(PriceBundleConfig config); /** diff --git a/src/main/java/com/ycwl/basic/pricing/mapper/PriceProductConfigMapper.java b/src/main/java/com/ycwl/basic/pricing/mapper/PriceProductConfigMapper.java index e4769a0..eec4d5f 100644 --- a/src/main/java/com/ycwl/basic/pricing/mapper/PriceProductConfigMapper.java +++ b/src/main/java/com/ycwl/basic/pricing/mapper/PriceProductConfigMapper.java @@ -57,15 +57,15 @@ public interface PriceProductConfigMapper extends BaseMapper /** * 插入商品价格配置 */ - @Insert("INSERT INTO price_product_config (product_type, product_id, scenic_id, product_name, base_price, original_price, unit, is_active, create_time, update_time) " + - "VALUES (#{productType}, #{productId}, #{scenicId}, #{productName}, #{basePrice}, #{originalPrice}, #{unit}, #{isActive}, NOW(), NOW())") + @Insert("INSERT INTO price_product_config (product_type, product_id, scenic_id, product_name, base_price, original_price, unit, is_active, can_use_coupon, can_use_voucher, create_time, update_time) " + + "VALUES (#{productType}, #{productId}, #{scenicId}, #{productName}, #{basePrice}, #{originalPrice}, #{unit}, #{isActive}, #{canUseCoupon}, #{canUseVoucher}, NOW(), NOW())") int insertProductConfig(PriceProductConfig config); /** * 更新商品价格配置 */ @Update("UPDATE price_product_config SET product_id = #{productId}, scenic_id = #{scenicId}, product_name = #{productName}, base_price = #{basePrice}, " + - "original_price = #{originalPrice}, unit = #{unit}, is_active = #{isActive}, update_time = NOW() WHERE id = #{id}") + "original_price = #{originalPrice}, unit = #{unit}, is_active = #{isActive}, can_use_coupon = #{canUseCoupon}, can_use_voucher = #{canUseVoucher}, update_time = NOW() WHERE id = #{id}") int updateProductConfig(PriceProductConfig config); /** diff --git a/src/main/java/com/ycwl/basic/pricing/service/IPriceBundleService.java b/src/main/java/com/ycwl/basic/pricing/service/IPriceBundleService.java index 03a33c0..828e1c5 100644 --- a/src/main/java/com/ycwl/basic/pricing/service/IPriceBundleService.java +++ b/src/main/java/com/ycwl/basic/pricing/service/IPriceBundleService.java @@ -29,6 +29,14 @@ public interface IPriceBundleService { */ BigDecimal getBundlePrice(List products); + /** + * 获取适用的一口价配置 + * + * @param products 商品列表 + * @return 适用的一口价配置,如果不适用则返回null + */ + PriceBundleConfig getBundleConfig(List products); + /** * 获取所有启用的一口价配置 * diff --git a/src/main/java/com/ycwl/basic/pricing/service/impl/CouponDiscountProvider.java b/src/main/java/com/ycwl/basic/pricing/service/impl/CouponDiscountProvider.java index 1e88cf8..2016acd 100644 --- a/src/main/java/com/ycwl/basic/pricing/service/impl/CouponDiscountProvider.java +++ b/src/main/java/com/ycwl/basic/pricing/service/impl/CouponDiscountProvider.java @@ -1,8 +1,12 @@ package com.ycwl.basic.pricing.service.impl; import com.ycwl.basic.pricing.dto.*; +import com.ycwl.basic.pricing.entity.PriceBundleConfig; +import com.ycwl.basic.pricing.entity.PriceProductConfig; import com.ycwl.basic.pricing.service.ICouponService; import com.ycwl.basic.pricing.service.IDiscountProvider; +import com.ycwl.basic.pricing.service.IPriceBundleService; +import com.ycwl.basic.pricing.service.IProductConfigService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -20,6 +24,8 @@ import java.util.List; public class CouponDiscountProvider implements IDiscountProvider { private final ICouponService couponService; + private final IProductConfigService productConfigService; + private final IPriceBundleService bundleService; @Override public String getProviderType() { @@ -39,6 +45,12 @@ public class CouponDiscountProvider implements IDiscountProvider { return discounts; } + // 检查商品配置和打包配置是否允许使用优惠券 + if (!checkCanUseCoupon(context)) { + log.debug("商品配置不允许使用优惠券,跳过优惠券检测"); + return discounts; + } + try { CouponInfo bestCoupon = couponService.selectBestCoupon( context.getUserId(), @@ -101,6 +113,70 @@ public class CouponDiscountProvider implements IDiscountProvider { public boolean canApply(DiscountInfo discountInfo, DiscountDetectionContext context) { return "COUPON".equals(discountInfo.getDiscountType()) && Boolean.TRUE.equals(context.getAutoUseCoupon()) && - context.getUserId() != null; + context.getUserId() != null && + checkCanUseCoupon(context); + } + + /** + * 检查商品配置和打包配置是否允许使用优惠券 + */ + private boolean checkCanUseCoupon(DiscountDetectionContext context) { + if (context.getProducts() == null || context.getProducts().isEmpty()) { + return true; // 如果没有商品信息,默认允许 + } + + try { + // 检查是否使用了打包购买 + BigDecimal bundlePrice = bundleService.getBundlePrice(context.getProducts()); + if (bundlePrice != null) { + // 如果使用了打包购买,检查打包配置的优惠券使用开关 + PriceBundleConfig bundleConfig = bundleService.getBundleConfig(context.getProducts()); + if (bundleConfig != null) { + boolean canUseCoupon = Boolean.TRUE.equals(bundleConfig.getCanUseCoupon()); + log.debug("打包配置优惠券开关检查: bundleId={}, canUseCoupon={}", bundleConfig.getId(), canUseCoupon); + return canUseCoupon; + } + } + + // 检查单个商品的优惠券使用开关 + for (ProductItem product : context.getProducts()) { + String productId = product.getProductId() != null ? product.getProductId() : "default"; + + try { + PriceProductConfig productConfig = productConfigService.getProductConfig( + product.getProductType().getCode(), productId); + + if (productConfig != null) { + if (!Boolean.TRUE.equals(productConfig.getCanUseCoupon())) { + log.debug("商品配置不允许使用优惠券: productType={}, productId={}", + product.getProductType().getCode(), productId); + return false; + } + } + } catch (Exception e) { + // 如果获取具体商品配置失败,尝试获取default配置 + try { + PriceProductConfig defaultConfig = productConfigService.getProductConfig( + product.getProductType().getCode(), "default"); + + if (defaultConfig != null) { + if (!Boolean.TRUE.equals(defaultConfig.getCanUseCoupon())) { + log.debug("商品默认配置不允许使用优惠券: productType={}", + product.getProductType().getCode()); + return false; + } + } + } catch (Exception ex) { + log.warn("获取商品配置失败,默认允许使用优惠券: productType={}, productId={}", + product.getProductType().getCode(), productId); + } + } + } + + } catch (Exception e) { + log.error("检查优惠券使用开关时发生异常,默认允许使用", e); + } + + return true; // 默认允许使用优惠券 } } \ No newline at end of file diff --git a/src/main/java/com/ycwl/basic/pricing/service/impl/PriceBundleServiceImpl.java b/src/main/java/com/ycwl/basic/pricing/service/impl/PriceBundleServiceImpl.java index 6448874..8aeca90 100644 --- a/src/main/java/com/ycwl/basic/pricing/service/impl/PriceBundleServiceImpl.java +++ b/src/main/java/com/ycwl/basic/pricing/service/impl/PriceBundleServiceImpl.java @@ -75,6 +75,27 @@ public class PriceBundleServiceImpl implements IPriceBundleService { return null; } + @Override + public PriceBundleConfig getBundleConfig(List products) { + if (!isBundleApplicable(products)) { + return null; + } + + List bundles = getActiveBundles(); + Set productTypes = new HashSet<>(); + for (ProductItem product : products) { + productTypes.add(product.getProductType().getCode()); + } + + for (PriceBundleConfig bundle : bundles) { + if (isProductsMatchBundle(productTypes, bundle)) { + return bundle; + } + } + + return null; + } + @Override // @Cacheable(value = "active-bundles") public List getActiveBundles() { diff --git a/src/main/java/com/ycwl/basic/pricing/service/impl/PriceCalculationServiceImpl.java b/src/main/java/com/ycwl/basic/pricing/service/impl/PriceCalculationServiceImpl.java index 4eaac53..d4fd148 100644 --- a/src/main/java/com/ycwl/basic/pricing/service/impl/PriceCalculationServiceImpl.java +++ b/src/main/java/com/ycwl/basic/pricing/service/impl/PriceCalculationServiceImpl.java @@ -49,13 +49,13 @@ public class PriceCalculationServiceImpl implements IPriceCalculationService { discountDetails.add(DiscountDetail.createLimitedTimeDiscount(limitedTimeDiscount)); } - // 检查一口价优惠 - BigDecimal bundlePrice = bundleService.getBundlePrice(request.getProducts()); - if (bundlePrice != null && bundlePrice.compareTo(totalAmount) < 0) { - BigDecimal bundleDiscount = totalAmount.subtract(bundlePrice); - discountDetails.add(DiscountDetail.createBundleDiscount(bundleDiscount)); - totalAmount = bundlePrice; - log.info("使用一口价: {}, 优惠: {}", bundlePrice, bundleDiscount); + // 检查打包购买优惠 + BigDecimal packagePrice = bundleService.getBundlePrice(request.getProducts()); + if (packagePrice != null && packagePrice.compareTo(totalAmount) < 0) { + BigDecimal packageDiscount = totalAmount.subtract(packagePrice); + discountDetails.add(DiscountDetail.createBundleDiscount(packageDiscount)); + totalAmount = packagePrice; + log.info("使用打包购买: {}, 优惠: {}", packagePrice, packageDiscount); } // 构建价格计算结果 @@ -77,7 +77,7 @@ public class PriceCalculationServiceImpl implements IPriceCalculationService { // 重新排序 allDiscountDetails.sort(Comparator.comparing(DiscountDetail::getSortOrder)); - // 计算总优惠金额(包括限时立减、一口价和其他优惠) + // 计算总优惠金额(包括限时立减、打包购买和其他优惠) BigDecimal totalDiscountAmount = allDiscountDetails.stream() .map(DiscountDetail::getDiscountAmount) .reduce(BigDecimal.ZERO, BigDecimal::add); @@ -98,7 +98,7 @@ public class PriceCalculationServiceImpl implements IPriceCalculationService { } else { log.warn("优惠计算失败: {}", discountResult.getErrorMessage()); - // 降级处理:仅使用基础优惠(限时立减、一口价) + // 降级处理:仅使用基础优惠(限时立减、打包购买) BigDecimal totalDiscountAmount = discountDetails.stream() .map(DiscountDetail::getDiscountAmount) .reduce(BigDecimal.ZERO, BigDecimal::add); diff --git a/src/main/java/com/ycwl/basic/pricing/service/impl/VoucherDiscountProvider.java b/src/main/java/com/ycwl/basic/pricing/service/impl/VoucherDiscountProvider.java index 32e5643..250713b 100644 --- a/src/main/java/com/ycwl/basic/pricing/service/impl/VoucherDiscountProvider.java +++ b/src/main/java/com/ycwl/basic/pricing/service/impl/VoucherDiscountProvider.java @@ -1,8 +1,12 @@ package com.ycwl.basic.pricing.service.impl; import com.ycwl.basic.pricing.dto.*; +import com.ycwl.basic.pricing.entity.PriceBundleConfig; +import com.ycwl.basic.pricing.entity.PriceProductConfig; import com.ycwl.basic.pricing.enums.VoucherDiscountType; import com.ycwl.basic.pricing.service.IDiscountProvider; +import com.ycwl.basic.pricing.service.IPriceBundleService; +import com.ycwl.basic.pricing.service.IProductConfigService; import com.ycwl.basic.pricing.service.IVoucherService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -22,6 +26,8 @@ import java.util.List; public class VoucherDiscountProvider implements IDiscountProvider { private final IVoucherService voucherService; + private final IProductConfigService productConfigService; + private final IPriceBundleService bundleService; @Override public String getProviderType() { @@ -41,6 +47,12 @@ public class VoucherDiscountProvider implements IDiscountProvider { return discounts; } + // 检查商品配置和打包配置是否允许使用券码 + if (!checkCanUseVoucher(context)) { + log.debug("商品配置不允许使用券码,跳过券码检测"); + return discounts; + } + try { VoucherInfo voucherInfo = null; @@ -149,7 +161,8 @@ public class VoucherDiscountProvider implements IDiscountProvider { return "VOUCHER".equals(discountInfo.getDiscountType()) && context.getFaceId() != null && context.getScenicId() != null && - StringUtils.hasText(discountInfo.getVoucherCode()); + StringUtils.hasText(discountInfo.getVoucherCode()) && + checkCanUseVoucher(context); } /** @@ -172,4 +185,67 @@ public class VoucherDiscountProvider implements IDiscountProvider { // 全场免费券码不可与其他优惠叠加 return voucherInfo.getDiscountType() != VoucherDiscountType.FREE_ALL; } + + /** + * 检查商品配置和打包配置是否允许使用券码 + */ + private boolean checkCanUseVoucher(DiscountDetectionContext context) { + if (context.getProducts() == null || context.getProducts().isEmpty()) { + return true; // 如果没有商品信息,默认允许 + } + + try { + // 检查是否使用了打包购买 + BigDecimal bundlePrice = bundleService.getBundlePrice(context.getProducts()); + if (bundlePrice != null) { + // 如果使用了打包购买,检查打包配置的券码使用开关 + PriceBundleConfig bundleConfig = bundleService.getBundleConfig(context.getProducts()); + if (bundleConfig != null) { + boolean canUseVoucher = Boolean.TRUE.equals(bundleConfig.getCanUseVoucher()); + log.debug("打包配置券码开关检查: bundleId={}, canUseVoucher={}", bundleConfig.getId(), canUseVoucher); + return canUseVoucher; + } + } + + // 检查单个商品的券码使用开关 + for (ProductItem product : context.getProducts()) { + String productId = product.getProductId() != null ? product.getProductId() : "default"; + + try { + PriceProductConfig productConfig = productConfigService.getProductConfig( + product.getProductType().getCode(), productId); + + if (productConfig != null) { + if (!Boolean.TRUE.equals(productConfig.getCanUseVoucher())) { + log.debug("商品配置不允许使用券码: productType={}, productId={}", + product.getProductType().getCode(), productId); + return false; + } + } + } catch (Exception e) { + // 如果获取具体商品配置失败,尝试获取default配置 + try { + PriceProductConfig defaultConfig = productConfigService.getProductConfig( + product.getProductType().getCode(), "default"); + + if (defaultConfig != null) { + if (!Boolean.TRUE.equals(defaultConfig.getCanUseVoucher())) { + log.debug("商品默认配置不允许使用券码: productType={}", + product.getProductType().getCode()); + return false; + } + } + } catch (Exception ex) { + log.warn("获取商品配置失败,默认允许使用券码: productType={}, productId={}", + product.getProductType().getCode(), productId); + } + } + } + + } catch (Exception e) { + log.error("检查券码使用开关时发生异常,默认允许使用", e); + } + + return true; // 默认允许使用券码 + } } \ No newline at end of file