You've already forked FrameTour-BE
refactor(pricing): 优化优惠提供者初始化和依赖注入
- 使用 @Lazy 注解解决循环依赖问题 - 重构 DiscountDetectionServiceImpl 以延迟加载优惠提供者 - 移除构造函数中的直接依赖注入,改用 ObjectProvider - 添加线程安全的提供者初始化机制 - 移除不必要的缓存注释
This commit is contained in:
@@ -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";
|
||||
|
||||
@@ -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<IDiscountProvider> discountProviderProvider;
|
||||
private final List<IDiscountProvider> discountProviders = new ArrayList<>();
|
||||
|
||||
private final Object providerInitLock = new Object();
|
||||
private volatile boolean providersInitialized;
|
||||
|
||||
@Autowired
|
||||
public DiscountDetectionServiceImpl(List<IDiscountProvider> 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<IDiscountProvider> discountProviderProvider) {
|
||||
this.discountProviderProvider = discountProviderProvider;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<DiscountInfo> detectAllAvailableDiscounts(DiscountDetectionContext context) {
|
||||
initializeProvidersIfNecessary();
|
||||
List<DiscountInfo> allDiscounts = new ArrayList<>();
|
||||
|
||||
|
||||
for (IDiscountProvider provider : discountProviders) {
|
||||
try {
|
||||
List<DiscountInfo> 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<DiscountInfo> 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<DiscountResult> appliedDiscounts = new ArrayList<>();
|
||||
List<DiscountDetail> 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<IDiscountProvider> 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 {
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,13 +97,11 @@ public class PriceBundleServiceImpl implements IPriceBundleService {
|
||||
}
|
||||
|
||||
@Override
|
||||
// @Cacheable(value = "active-bundles")
|
||||
public List<PriceBundleConfig> getActiveBundles() {
|
||||
return bundleConfigMapper.selectActiveBundles();
|
||||
}
|
||||
|
||||
@Override
|
||||
// @Cacheable(value = "all-bundles")
|
||||
public List<PriceBundleConfig> getAllBundles() {
|
||||
return bundleConfigMapper.selectActiveBundles();
|
||||
}
|
||||
|
||||
@@ -30,13 +30,11 @@ public class ProductConfigServiceImpl implements IProductConfigService {
|
||||
private final PriceTierConfigMapper tierConfigMapper;
|
||||
|
||||
@Override
|
||||
// @Cacheable(value = "product-config", key = "#productType")
|
||||
public List<PriceProductConfig> 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<PriceProductConfig> getActiveProductConfigs() {
|
||||
return productConfigMapper.selectActiveConfigs();
|
||||
}
|
||||
|
||||
@Override
|
||||
// @Cacheable(value = "all-product-configs")
|
||||
public List<PriceProductConfig> getAllProductConfigs() {
|
||||
return productConfigMapper.selectActiveConfigs();
|
||||
}
|
||||
|
||||
@Override
|
||||
// @Cacheable(value = "tier-configs", key = "#productType")
|
||||
public List<PriceTierConfig> getTierConfigs(String productType) {
|
||||
return tierConfigMapper.selectByProductType(productType);
|
||||
}
|
||||
|
||||
@Override
|
||||
// @Cacheable(value = "tier-configs", key = "#productType + '_' + #productId")
|
||||
public List<PriceTierConfig> getTierConfigs(String productType, String productId) {
|
||||
return tierConfigMapper.selectByProductTypeAndId(productType, productId);
|
||||
}
|
||||
|
||||
@Override
|
||||
// @Cacheable(value = "all-tier-configs")
|
||||
public List<PriceTierConfig> getAllTierConfigs() {
|
||||
return tierConfigMapper.selectAllActiveConfigs();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user