refactor(pricing): 优化优惠提供者初始化和依赖注入

- 使用 @Lazy 注解解决循环依赖问题
- 重构 DiscountDetectionServiceImpl 以延迟加载优惠提供者
- 移除构造函数中的直接依赖注入,改用 ObjectProvider
- 添加线程安全的提供者初始化机制
- 移除不必要的缓存注释
This commit is contained in:
2025-12-05 16:03:00 +08:00
parent 33c3a194ca
commit ee13ef09f7
4 changed files with 81 additions and 62 deletions

View File

@@ -7,8 +7,8 @@ import com.ycwl.basic.pricing.service.ICouponService;
import com.ycwl.basic.pricing.service.IDiscountProvider; import com.ycwl.basic.pricing.service.IDiscountProvider;
import com.ycwl.basic.pricing.service.IPriceBundleService; import com.ycwl.basic.pricing.service.IPriceBundleService;
import com.ycwl.basic.pricing.service.IProductConfigService; import com.ycwl.basic.pricing.service.IProductConfigService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.math.BigDecimal; import java.math.BigDecimal;
@@ -20,13 +20,20 @@ import java.util.List;
*/ */
@Slf4j @Slf4j
@Component @Component
@RequiredArgsConstructor
public class CouponDiscountProvider implements IDiscountProvider { public class CouponDiscountProvider implements IDiscountProvider {
private final ICouponService couponService; private final ICouponService couponService;
private final IProductConfigService productConfigService; private final IProductConfigService productConfigService;
private final IPriceBundleService bundleService; 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 @Override
public String getProviderType() { public String getProviderType() {
return "COUPON"; return "COUPON";

View File

@@ -4,6 +4,7 @@ import com.ycwl.basic.pricing.dto.*;
import com.ycwl.basic.pricing.service.IDiscountDetectionService; import com.ycwl.basic.pricing.service.IDiscountDetectionService;
import com.ycwl.basic.pricing.service.IDiscountProvider; import com.ycwl.basic.pricing.service.IDiscountProvider;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -19,24 +20,22 @@ import java.util.stream.Collectors;
@Slf4j @Slf4j
@Service @Service
public class DiscountDetectionServiceImpl implements IDiscountDetectionService { public class DiscountDetectionServiceImpl implements IDiscountDetectionService {
private final ObjectProvider<IDiscountProvider> discountProviderProvider;
private final List<IDiscountProvider> discountProviders = new ArrayList<>(); private final List<IDiscountProvider> discountProviders = new ArrayList<>();
private final Object providerInitLock = new Object();
private volatile boolean providersInitialized;
@Autowired @Autowired
public DiscountDetectionServiceImpl(List<IDiscountProvider> providers) { public DiscountDetectionServiceImpl(ObjectProvider<IDiscountProvider> discountProviderProvider) {
this.discountProviders.addAll(providers); this.discountProviderProvider = discountProviderProvider;
// 按优先级排序(优先级高的在前)
this.discountProviders.sort(Comparator.comparing(IDiscountProvider::getPriority).reversed());
log.info("注册了 {} 个优惠提供者: {}",
providers.size(),
providers.stream().map(IDiscountProvider::getProviderType).collect(Collectors.toList()));
} }
@Override @Override
public List<DiscountInfo> detectAllAvailableDiscounts(DiscountDetectionContext context) { public List<DiscountInfo> detectAllAvailableDiscounts(DiscountDetectionContext context) {
initializeProvidersIfNecessary();
List<DiscountInfo> allDiscounts = new ArrayList<>(); List<DiscountInfo> allDiscounts = new ArrayList<>();
for (IDiscountProvider provider : discountProviders) { for (IDiscountProvider provider : discountProviders) {
try { try {
List<DiscountInfo> providerDiscounts = provider.detectAvailableDiscounts(context); List<DiscountInfo> providerDiscounts = provider.detectAvailableDiscounts(context);
@@ -48,22 +47,22 @@ public class DiscountDetectionServiceImpl implements IDiscountDetectionService {
log.error("优惠提供者 {} 检测失败", provider.getProviderType(), e); log.error("优惠提供者 {} 检测失败", provider.getProviderType(), e);
} }
} }
// 按优先级排序 // 按优先级排序
allDiscounts.sort(Comparator.comparing(DiscountInfo::getPriority).reversed()); allDiscounts.sort(Comparator.comparing(DiscountInfo::getPriority).reversed());
return allDiscounts; return allDiscounts;
} }
@Override @Override
public DiscountCombinationResult calculateOptimalCombination(DiscountDetectionContext context) { public DiscountCombinationResult calculateOptimalCombination(DiscountDetectionContext context) {
DiscountCombinationResult result = new DiscountCombinationResult(); DiscountCombinationResult result = new DiscountCombinationResult();
result.setOriginalAmount(context.getCurrentAmount()); result.setOriginalAmount(context.getCurrentAmount());
try { try {
List<DiscountInfo> availableDiscounts = detectAllAvailableDiscounts(context); List<DiscountInfo> availableDiscounts = detectAllAvailableDiscounts(context);
result.setAvailableDiscounts(availableDiscounts); result.setAvailableDiscounts(availableDiscounts);
if (availableDiscounts.isEmpty()) { if (availableDiscounts.isEmpty()) {
result.setFinalAmount(context.getCurrentAmount()); result.setFinalAmount(context.getCurrentAmount());
result.setTotalDiscountAmount(BigDecimal.ZERO); result.setTotalDiscountAmount(BigDecimal.ZERO);
@@ -72,66 +71,66 @@ public class DiscountDetectionServiceImpl implements IDiscountDetectionService {
result.setSuccess(true); result.setSuccess(true);
return result; return result;
} }
List<DiscountResult> appliedDiscounts = new ArrayList<>(); List<DiscountResult> appliedDiscounts = new ArrayList<>();
List<DiscountDetail> discountDetails = new ArrayList<>(); List<DiscountDetail> discountDetails = new ArrayList<>();
BigDecimal currentAmount = context.getCurrentAmount(); BigDecimal currentAmount = context.getCurrentAmount();
// 按优先级应用优惠 // 按优先级应用优惠
for (DiscountInfo discountInfo : availableDiscounts) { for (DiscountInfo discountInfo : availableDiscounts) {
IDiscountProvider provider = findProvider(discountInfo.getProviderType()); IDiscountProvider provider = findProvider(discountInfo.getProviderType());
if (provider == null || !provider.canApply(discountInfo, context)) { if (provider == null || !provider.canApply(discountInfo, context)) {
continue; continue;
} }
// 更新上下文中的当前金额 // 更新上下文中的当前金额
context.setCurrentAmount(currentAmount); context.setCurrentAmount(currentAmount);
DiscountResult discountResult = provider.applyDiscount(discountInfo, context); DiscountResult discountResult = provider.applyDiscount(discountInfo, context);
if (Boolean.TRUE.equals(discountResult.getSuccess())) { if (Boolean.TRUE.equals(discountResult.getSuccess())) {
appliedDiscounts.add(discountResult); appliedDiscounts.add(discountResult);
// 创建显示用的优惠详情 // 创建显示用的优惠详情
DiscountDetail detail = createDiscountDetail(discountResult); DiscountDetail detail = createDiscountDetail(discountResult);
if (detail != null) { if (detail != null) {
discountDetails.add(detail); discountDetails.add(detail);
} }
// 更新当前金额 // 更新当前金额
currentAmount = discountResult.getFinalAmount(); currentAmount = discountResult.getFinalAmount();
log.info("成功应用优惠: {} - {}, 优惠金额: {}", log.info("成功应用优惠: {} - {}, 优惠金额: {}",
discountInfo.getProviderType(), discountInfo.getProviderType(),
discountInfo.getDiscountName(), discountInfo.getDiscountName(),
discountResult.getActualDiscountAmount()); discountResult.getActualDiscountAmount());
// 如果是不可叠加的优惠(如全场免费),则停止应用其他优惠 // 如果是不可叠加的优惠(如全场免费),则停止应用其他优惠
if (!Boolean.TRUE.equals(discountInfo.getStackable())) { if (!Boolean.TRUE.equals(discountInfo.getStackable())) {
log.info("遇到不可叠加优惠,停止应用其他优惠: {}", discountInfo.getDiscountName()); log.info("遇到不可叠加优惠,停止应用其他优惠: {}", discountInfo.getDiscountName());
break; break;
} }
} else { } else {
log.warn("优惠应用失败: {} - {}, 原因: {}", log.warn("优惠应用失败: {} - {}, 原因: {}",
discountInfo.getProviderType(), discountInfo.getProviderType(),
discountInfo.getDiscountName(), discountInfo.getDiscountName(),
discountResult.getFailureReason()); discountResult.getFailureReason());
} }
} }
// 计算总优惠金额 // 计算总优惠金额
BigDecimal totalDiscountAmount = appliedDiscounts.stream() BigDecimal totalDiscountAmount = appliedDiscounts.stream()
.map(DiscountResult::getActualDiscountAmount) .map(DiscountResult::getActualDiscountAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add); .reduce(BigDecimal.ZERO, BigDecimal::add);
// 按显示顺序排序折扣详情 // 按显示顺序排序折扣详情
discountDetails.sort(Comparator.comparing(DiscountDetail::getSortOrder)); discountDetails.sort(Comparator.comparing(DiscountDetail::getSortOrder));
result.setFinalAmount(currentAmount); result.setFinalAmount(currentAmount);
result.setTotalDiscountAmount(totalDiscountAmount); result.setTotalDiscountAmount(totalDiscountAmount);
result.setAppliedDiscounts(appliedDiscounts); result.setAppliedDiscounts(appliedDiscounts);
result.setDiscountDetails(discountDetails); result.setDiscountDetails(discountDetails);
result.setSuccess(true); result.setSuccess(true);
} catch (Exception e) { } catch (Exception e) {
log.error("计算最优优惠组合失败", e); log.error("计算最优优惠组合失败", e);
result.setSuccess(false); result.setSuccess(false);
@@ -139,48 +138,55 @@ public class DiscountDetectionServiceImpl implements IDiscountDetectionService {
result.setFinalAmount(context.getCurrentAmount()); result.setFinalAmount(context.getCurrentAmount());
result.setTotalDiscountAmount(BigDecimal.ZERO); result.setTotalDiscountAmount(BigDecimal.ZERO);
} }
return result; return result;
} }
@Override @Override
public DiscountCombinationResult previewOptimalCombination(DiscountDetectionContext context) { public DiscountCombinationResult previewOptimalCombination(DiscountDetectionContext context) {
// 预览模式与正常计算相同,但不会实际标记优惠为已使用 // 预览模式与正常计算相同,但不会实际标记优惠为已使用
return calculateOptimalCombination(context); return calculateOptimalCombination(context);
} }
@Override @Override
public void registerProvider(IDiscountProvider provider) { public void registerProvider(IDiscountProvider provider) {
if (provider != null && !discountProviders.contains(provider)) { if (provider == null) {
discountProviders.add(provider); return;
// 重新排序 }
discountProviders.sort(Comparator.comparing(IDiscountProvider::getPriority).reversed()); synchronized (providerInitLock) {
log.info("注册新的优惠提供者: {}", provider.getProviderType()); initializeProvidersIfNecessary();
if (!discountProviders.contains(provider)) {
discountProviders.add(provider);
discountProviders.sort(Comparator.comparing(IDiscountProvider::getPriority).reversed());
log.info("注册新的优惠提供者: {}", provider.getProviderType());
}
} }
} }
@Override @Override
public List<IDiscountProvider> getAllProviders() { public List<IDiscountProvider> getAllProviders() {
initializeProvidersIfNecessary();
return new ArrayList<>(discountProviders); return new ArrayList<>(discountProviders);
} }
/** /**
* 查找指定类型的优惠提供者 * 查找指定类型的优惠提供者
*/ */
private IDiscountProvider findProvider(String providerType) { private IDiscountProvider findProvider(String providerType) {
initializeProvidersIfNecessary();
return discountProviders.stream() return discountProviders.stream()
.filter(provider -> providerType.equals(provider.getProviderType())) .filter(provider -> providerType.equals(provider.getProviderType()))
.findFirst() .findFirst()
.orElse(null); .orElse(null);
} }
/** /**
* 创建显示用的优惠详情 * 创建显示用的优惠详情
*/ */
private DiscountDetail createDiscountDetail(DiscountResult discountResult) { private DiscountDetail createDiscountDetail(DiscountResult discountResult) {
DiscountInfo discountInfo = discountResult.getDiscountInfo(); DiscountInfo discountInfo = discountResult.getDiscountInfo();
String providerType = discountInfo.getProviderType(); String providerType = discountInfo.getProviderType();
return switch (providerType) { return switch (providerType) {
case "VOUCHER" -> DiscountDetail.createVoucherDiscount( case "VOUCHER" -> DiscountDetail.createVoucherDiscount(
discountInfo.getVoucherCode(), discountInfo.getVoucherCode(),
@@ -203,4 +209,22 @@ public class DiscountDetectionServiceImpl implements IDiscountDetectionService {
} }
}; };
} }
}
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;
}
}
}

View File

@@ -97,13 +97,11 @@ public class PriceBundleServiceImpl implements IPriceBundleService {
} }
@Override @Override
// @Cacheable(value = "active-bundles")
public List<PriceBundleConfig> getActiveBundles() { public List<PriceBundleConfig> getActiveBundles() {
return bundleConfigMapper.selectActiveBundles(); return bundleConfigMapper.selectActiveBundles();
} }
@Override @Override
// @Cacheable(value = "all-bundles")
public List<PriceBundleConfig> getAllBundles() { public List<PriceBundleConfig> getAllBundles() {
return bundleConfigMapper.selectActiveBundles(); return bundleConfigMapper.selectActiveBundles();
} }

View File

@@ -30,13 +30,11 @@ public class ProductConfigServiceImpl implements IProductConfigService {
private final PriceTierConfigMapper tierConfigMapper; private final PriceTierConfigMapper tierConfigMapper;
@Override @Override
// @Cacheable(value = "product-config", key = "#productType")
public List<PriceProductConfig> getProductConfig(String productType) { public List<PriceProductConfig> getProductConfig(String productType) {
return productConfigMapper.selectByProductType(productType); return productConfigMapper.selectByProductType(productType);
} }
@Override @Override
// @Cacheable(value = "product-config", key = "#productType + '_' + #productId")
public PriceProductConfig getProductConfig(String productType, String productId) { public PriceProductConfig getProductConfig(String productType, String productId) {
PriceProductConfig config = productConfigMapper.selectByProductTypeAndId(productType, productId); PriceProductConfig config = productConfigMapper.selectByProductTypeAndId(productType, productId);
if (config == null) { if (config == null) {
@@ -46,7 +44,6 @@ public class ProductConfigServiceImpl implements IProductConfigService {
} }
@Override @Override
// @Cacheable(value = "tier-config", key = "#productType + '_' + #productId + '_' + #quantity")
public PriceTierConfig getTierConfig(String productType, String productId, Integer quantity) { public PriceTierConfig getTierConfig(String productType, String productId, Integer quantity) {
if (quantity == null || quantity <= 0) { if (quantity == null || quantity <= 0) {
return null; return null;
@@ -73,7 +70,6 @@ public class ProductConfigServiceImpl implements IProductConfigService {
} }
@Override @Override
// @Cacheable(value = "product-config", key = "#productType + '_' + #productId + '_' + #scenicId")
public PriceProductConfig getProductConfig(String productType, String productId, Long scenicId) { public PriceProductConfig getProductConfig(String productType, String productId, Long scenicId) {
if (scenicId == null) { if (scenicId == null) {
// 如果没有景区ID,使用原有逻辑 // 如果没有景区ID,使用原有逻辑
@@ -124,7 +120,6 @@ public class ProductConfigServiceImpl implements IProductConfigService {
} }
@Override @Override
// @Cacheable(value = "tier-config", key = "#productType + '_' + #productId + '_' + #quantity + '_' + #scenicId")
public PriceTierConfig getTierConfig(String productType, String productId, Integer quantity, Long scenicId) { public PriceTierConfig getTierConfig(String productType, String productId, Integer quantity, Long scenicId) {
if (quantity == null || quantity <= 0) { if (quantity == null || quantity <= 0) {
return null; return null;
@@ -174,31 +169,26 @@ public class ProductConfigServiceImpl implements IProductConfigService {
} }
@Override @Override
// @Cacheable(value = "active-product-configs")
public List<PriceProductConfig> getActiveProductConfigs() { public List<PriceProductConfig> getActiveProductConfigs() {
return productConfigMapper.selectActiveConfigs(); return productConfigMapper.selectActiveConfigs();
} }
@Override @Override
// @Cacheable(value = "all-product-configs")
public List<PriceProductConfig> getAllProductConfigs() { public List<PriceProductConfig> getAllProductConfigs() {
return productConfigMapper.selectActiveConfigs(); return productConfigMapper.selectActiveConfigs();
} }
@Override @Override
// @Cacheable(value = "tier-configs", key = "#productType")
public List<PriceTierConfig> getTierConfigs(String productType) { public List<PriceTierConfig> getTierConfigs(String productType) {
return tierConfigMapper.selectByProductType(productType); return tierConfigMapper.selectByProductType(productType);
} }
@Override @Override
// @Cacheable(value = "tier-configs", key = "#productType + '_' + #productId")
public List<PriceTierConfig> getTierConfigs(String productType, String productId) { public List<PriceTierConfig> getTierConfigs(String productType, String productId) {
return tierConfigMapper.selectByProductTypeAndId(productType, productId); return tierConfigMapper.selectByProductTypeAndId(productType, productId);
} }
@Override @Override
// @Cacheable(value = "all-tier-configs")
public List<PriceTierConfig> getAllTierConfigs() { public List<PriceTierConfig> getAllTierConfigs() {
return tierConfigMapper.selectAllActiveConfigs(); return tierConfigMapper.selectAllActiveConfigs();
} }