From ee13ef09f7a378f99cd0ecacc5faabe7a7e6958e Mon Sep 17 00:00:00 2001 From: Jerry Yan <792602257@qq.com> Date: Fri, 5 Dec 2025 16:03:00 +0800 Subject: [PATCH] =?UTF-8?q?refactor(pricing):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E4=BC=98=E6=83=A0=E6=8F=90=E4=BE=9B=E8=80=85=E5=88=9D=E5=A7=8B?= =?UTF-8?q?=E5=8C=96=E5=92=8C=E4=BE=9D=E8=B5=96=E6=B3=A8=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 使用 @Lazy 注解解决循环依赖问题 - 重构 DiscountDetectionServiceImpl 以延迟加载优惠提供者 - 移除构造函数中的直接依赖注入,改用 ObjectProvider - 添加线程安全的提供者初始化机制 - 移除不必要的缓存注释 --- .../service/impl/CouponDiscountProvider.java | 11 +- .../impl/DiscountDetectionServiceImpl.java | 120 +++++++++++------- .../service/impl/PriceBundleServiceImpl.java | 2 - .../impl/ProductConfigServiceImpl.java | 10 -- 4 files changed, 81 insertions(+), 62 deletions(-) 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 96d52b9f..55be9786 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 @@ -7,8 +7,8 @@ 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.context.annotation.Lazy; import org.springframework.stereotype.Component; import java.math.BigDecimal; @@ -20,13 +20,20 @@ import java.util.List; */ @Slf4j @Component -@RequiredArgsConstructor public class CouponDiscountProvider implements IDiscountProvider { private final ICouponService couponService; private final IProductConfigService productConfigService; private final IPriceBundleService bundleService; + public CouponDiscountProvider(@Lazy ICouponService couponService, + @Lazy IProductConfigService productConfigService, + @Lazy IPriceBundleService bundleService) { + this.couponService = couponService; + this.productConfigService = productConfigService; + this.bundleService = bundleService; + } + @Override public String getProviderType() { return "COUPON"; diff --git a/src/main/java/com/ycwl/basic/pricing/service/impl/DiscountDetectionServiceImpl.java b/src/main/java/com/ycwl/basic/pricing/service/impl/DiscountDetectionServiceImpl.java index 111c7efa..9e121cc0 100644 --- a/src/main/java/com/ycwl/basic/pricing/service/impl/DiscountDetectionServiceImpl.java +++ b/src/main/java/com/ycwl/basic/pricing/service/impl/DiscountDetectionServiceImpl.java @@ -4,6 +4,7 @@ import com.ycwl.basic.pricing.dto.*; import com.ycwl.basic.pricing.service.IDiscountDetectionService; import com.ycwl.basic.pricing.service.IDiscountProvider; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -19,24 +20,22 @@ import java.util.stream.Collectors; @Slf4j @Service public class DiscountDetectionServiceImpl implements IDiscountDetectionService { - + + private final ObjectProvider discountProviderProvider; private final List discountProviders = new ArrayList<>(); - + private final Object providerInitLock = new Object(); + private volatile boolean providersInitialized; + @Autowired - public DiscountDetectionServiceImpl(List providers) { - this.discountProviders.addAll(providers); - // 按优先级排序(优先级高的在前) - this.discountProviders.sort(Comparator.comparing(IDiscountProvider::getPriority).reversed()); - - log.info("注册了 {} 个优惠提供者: {}", - providers.size(), - providers.stream().map(IDiscountProvider::getProviderType).collect(Collectors.toList())); + public DiscountDetectionServiceImpl(ObjectProvider discountProviderProvider) { + this.discountProviderProvider = discountProviderProvider; } - + @Override public List detectAllAvailableDiscounts(DiscountDetectionContext context) { + initializeProvidersIfNecessary(); List allDiscounts = new ArrayList<>(); - + for (IDiscountProvider provider : discountProviders) { try { List providerDiscounts = provider.detectAvailableDiscounts(context); @@ -48,22 +47,22 @@ public class DiscountDetectionServiceImpl implements IDiscountDetectionService { log.error("优惠提供者 {} 检测失败", provider.getProviderType(), e); } } - + // 按优先级排序 allDiscounts.sort(Comparator.comparing(DiscountInfo::getPriority).reversed()); - + return allDiscounts; } - + @Override public DiscountCombinationResult calculateOptimalCombination(DiscountDetectionContext context) { DiscountCombinationResult result = new DiscountCombinationResult(); result.setOriginalAmount(context.getCurrentAmount()); - + try { List availableDiscounts = detectAllAvailableDiscounts(context); result.setAvailableDiscounts(availableDiscounts); - + if (availableDiscounts.isEmpty()) { result.setFinalAmount(context.getCurrentAmount()); result.setTotalDiscountAmount(BigDecimal.ZERO); @@ -72,66 +71,66 @@ public class DiscountDetectionServiceImpl implements IDiscountDetectionService { result.setSuccess(true); return result; } - + List appliedDiscounts = new ArrayList<>(); List discountDetails = new ArrayList<>(); BigDecimal currentAmount = context.getCurrentAmount(); - + // 按优先级应用优惠 for (DiscountInfo discountInfo : availableDiscounts) { IDiscountProvider provider = findProvider(discountInfo.getProviderType()); if (provider == null || !provider.canApply(discountInfo, context)) { continue; } - + // 更新上下文中的当前金额 context.setCurrentAmount(currentAmount); - + DiscountResult discountResult = provider.applyDiscount(discountInfo, context); if (Boolean.TRUE.equals(discountResult.getSuccess())) { appliedDiscounts.add(discountResult); - + // 创建显示用的优惠详情 DiscountDetail detail = createDiscountDetail(discountResult); if (detail != null) { discountDetails.add(detail); } - + // 更新当前金额 currentAmount = discountResult.getFinalAmount(); - - log.info("成功应用优惠: {} - {}, 优惠金额: {}", - discountInfo.getProviderType(), - discountInfo.getDiscountName(), + + log.info("成功应用优惠: {} - {}, 优惠金额: {}", + discountInfo.getProviderType(), + discountInfo.getDiscountName(), discountResult.getActualDiscountAmount()); - + // 如果是不可叠加的优惠(如全场免费),则停止应用其他优惠 if (!Boolean.TRUE.equals(discountInfo.getStackable())) { log.info("遇到不可叠加优惠,停止应用其他优惠: {}", discountInfo.getDiscountName()); break; } } else { - log.warn("优惠应用失败: {} - {}, 原因: {}", - discountInfo.getProviderType(), - discountInfo.getDiscountName(), + log.warn("优惠应用失败: {} - {}, 原因: {}", + discountInfo.getProviderType(), + discountInfo.getDiscountName(), discountResult.getFailureReason()); } } - + // 计算总优惠金额 BigDecimal totalDiscountAmount = appliedDiscounts.stream() .map(DiscountResult::getActualDiscountAmount) .reduce(BigDecimal.ZERO, BigDecimal::add); - + // 按显示顺序排序折扣详情 discountDetails.sort(Comparator.comparing(DiscountDetail::getSortOrder)); - + result.setFinalAmount(currentAmount); result.setTotalDiscountAmount(totalDiscountAmount); result.setAppliedDiscounts(appliedDiscounts); result.setDiscountDetails(discountDetails); result.setSuccess(true); - + } catch (Exception e) { log.error("计算最优优惠组合失败", e); result.setSuccess(false); @@ -139,48 +138,55 @@ public class DiscountDetectionServiceImpl implements IDiscountDetectionService { result.setFinalAmount(context.getCurrentAmount()); result.setTotalDiscountAmount(BigDecimal.ZERO); } - + return result; } - + @Override public DiscountCombinationResult previewOptimalCombination(DiscountDetectionContext context) { // 预览模式与正常计算相同,但不会实际标记优惠为已使用 return calculateOptimalCombination(context); } - + @Override public void registerProvider(IDiscountProvider provider) { - if (provider != null && !discountProviders.contains(provider)) { - discountProviders.add(provider); - // 重新排序 - discountProviders.sort(Comparator.comparing(IDiscountProvider::getPriority).reversed()); - log.info("注册新的优惠提供者: {}", provider.getProviderType()); + if (provider == null) { + return; + } + synchronized (providerInitLock) { + initializeProvidersIfNecessary(); + if (!discountProviders.contains(provider)) { + discountProviders.add(provider); + discountProviders.sort(Comparator.comparing(IDiscountProvider::getPriority).reversed()); + log.info("注册新的优惠提供者: {}", provider.getProviderType()); + } } } - + @Override public List getAllProviders() { + initializeProvidersIfNecessary(); return new ArrayList<>(discountProviders); } - + /** * 查找指定类型的优惠提供者 */ private IDiscountProvider findProvider(String providerType) { + initializeProvidersIfNecessary(); return discountProviders.stream() .filter(provider -> providerType.equals(provider.getProviderType())) .findFirst() .orElse(null); } - + /** * 创建显示用的优惠详情 */ private DiscountDetail createDiscountDetail(DiscountResult discountResult) { DiscountInfo discountInfo = discountResult.getDiscountInfo(); String providerType = discountInfo.getProviderType(); - + return switch (providerType) { case "VOUCHER" -> DiscountDetail.createVoucherDiscount( discountInfo.getVoucherCode(), @@ -203,4 +209,22 @@ public class DiscountDetectionServiceImpl implements IDiscountDetectionService { } }; } -} \ No newline at end of file + + private void initializeProvidersIfNecessary() { + if (providersInitialized) { + return; + } + synchronized (providerInitLock) { + if (providersInitialized) { + return; + } + discountProviders.clear(); + discountProviderProvider.stream().forEach(discountProviders::add); + discountProviders.sort(Comparator.comparing(IDiscountProvider::getPriority).reversed()); + log.info("注册了 {} 个优惠提供者: {}", + discountProviders.size(), + discountProviders.stream().map(IDiscountProvider::getProviderType).collect(Collectors.toList())); + providersInitialized = true; + } + } +} 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 8aeca90c..58a1e45d 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 @@ -97,13 +97,11 @@ public class PriceBundleServiceImpl implements IPriceBundleService { } @Override -// @Cacheable(value = "active-bundles") public List getActiveBundles() { return bundleConfigMapper.selectActiveBundles(); } @Override -// @Cacheable(value = "all-bundles") public List getAllBundles() { return bundleConfigMapper.selectActiveBundles(); } diff --git a/src/main/java/com/ycwl/basic/pricing/service/impl/ProductConfigServiceImpl.java b/src/main/java/com/ycwl/basic/pricing/service/impl/ProductConfigServiceImpl.java index 5b1477e0..04d7012a 100644 --- a/src/main/java/com/ycwl/basic/pricing/service/impl/ProductConfigServiceImpl.java +++ b/src/main/java/com/ycwl/basic/pricing/service/impl/ProductConfigServiceImpl.java @@ -30,13 +30,11 @@ public class ProductConfigServiceImpl implements IProductConfigService { private final PriceTierConfigMapper tierConfigMapper; @Override -// @Cacheable(value = "product-config", key = "#productType") public List getProductConfig(String productType) { return productConfigMapper.selectByProductType(productType); } @Override -// @Cacheable(value = "product-config", key = "#productType + '_' + #productId") public PriceProductConfig getProductConfig(String productType, String productId) { PriceProductConfig config = productConfigMapper.selectByProductTypeAndId(productType, productId); if (config == null) { @@ -46,7 +44,6 @@ public class ProductConfigServiceImpl implements IProductConfigService { } @Override -// @Cacheable(value = "tier-config", key = "#productType + '_' + #productId + '_' + #quantity") public PriceTierConfig getTierConfig(String productType, String productId, Integer quantity) { if (quantity == null || quantity <= 0) { return null; @@ -73,7 +70,6 @@ public class ProductConfigServiceImpl implements IProductConfigService { } @Override -// @Cacheable(value = "product-config", key = "#productType + '_' + #productId + '_' + #scenicId") public PriceProductConfig getProductConfig(String productType, String productId, Long scenicId) { if (scenicId == null) { // 如果没有景区ID,使用原有逻辑 @@ -124,7 +120,6 @@ public class ProductConfigServiceImpl implements IProductConfigService { } @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; @@ -174,31 +169,26 @@ public class ProductConfigServiceImpl implements IProductConfigService { } @Override -// @Cacheable(value = "active-product-configs") public List getActiveProductConfigs() { return productConfigMapper.selectActiveConfigs(); } @Override -// @Cacheable(value = "all-product-configs") public List getAllProductConfigs() { return productConfigMapper.selectActiveConfigs(); } @Override -// @Cacheable(value = "tier-configs", key = "#productType") public List getTierConfigs(String productType) { return tierConfigMapper.selectByProductType(productType); } @Override -// @Cacheable(value = "tier-configs", key = "#productType + '_' + #productId") public List getTierConfigs(String productType, String productId) { return tierConfigMapper.selectByProductTypeAndId(productType, productId); } @Override -// @Cacheable(value = "all-tier-configs") public List getAllTierConfigs() { return tierConfigMapper.selectAllActiveConfigs(); }