mirror of
https://gitee.com/dromara/dax-pay
synced 2026-08-10 23:01:58 +08:00
perf(route): 场景模式通道商户候选批量接口预加载目录与产品能力
listSceneChannelMchCandidatesBatch 在循环外一次性预加载目录键集合、产品策略索引与产品能力映射,循环内改为纯内存求交,避免逐(目录项×通道商户)重复查库与扫描 Spring 容器;单 method 版同步复用上下文。
This commit is contained in:
@@ -24,10 +24,13 @@ import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/// # 通道路由:策略方式→通道商户与能力候选
|
||||
///
|
||||
@@ -45,16 +48,20 @@ public class PayRouteStrategyCapabilitySupport {
|
||||
private final ChannelMerchantManager channelMerchantManager;
|
||||
|
||||
/// 通道路由白名单目录下全部 (provider,method) 的通道商户候选(单次请求聚合)
|
||||
///
|
||||
/// 优化:目录、策略、产品能力在循环外一次性预加载,循环内仅做内存集合求交,
|
||||
/// 避免逐 (目录项 × 通道商户) 重复查库与扫描 Spring 容器。
|
||||
public Map<String, List<LabelValue>> listSceneChannelMchCandidatesBatch(String mchNo) {
|
||||
Map<String, List<LabelValue>> index = new LinkedHashMap<>();
|
||||
// 通道商户是商户级数据,目录循环外一次性加载
|
||||
List<PayProviderMethodEntry> entries = payProviderMethodService.listDirectoryEntries();
|
||||
RouteBatchContext ctx = buildRouteBatchContext(entries);
|
||||
List<ChannelMerchant> mchants = channelMerchantManager.findAllByMchNo(mchNo);
|
||||
for (PayProviderMethodEntry entry : payProviderMethodService.listDirectoryEntries()) {
|
||||
Map<String, List<LabelValue>> index = new LinkedHashMap<>();
|
||||
for (PayProviderMethodEntry entry : entries) {
|
||||
if (!PayRouteConfigProviders.contains(entry.getProviderCode())) {
|
||||
continue;
|
||||
}
|
||||
String key = PayProviderMethodManager.pairKey(entry.getProviderCode(), entry.getMethodCode());
|
||||
index.put(key, filterChannelMchForDirectory(mchants, entry.getProviderCode(), entry.getMethodCode()));
|
||||
index.put(key, filterChannelMchForDirectory(ctx, mchants, entry.getProviderCode(), entry.getMethodCode()));
|
||||
}
|
||||
return index;
|
||||
}
|
||||
@@ -86,18 +93,26 @@ public class PayRouteStrategyCapabilitySupport {
|
||||
if (!payProviderMethodService.contains(provider, method)) {
|
||||
return List.of();
|
||||
}
|
||||
return filterChannelMchForDirectory(channelMerchantManager.findAllByMchNo(mchNo), provider, method);
|
||||
// 单次请求也复用批量上下文,避免内层逐商户查库 / 扫容器
|
||||
RouteBatchContext ctx = buildRouteBatchContext(payProviderMethodService.listDirectoryEntries());
|
||||
return filterChannelMchForDirectory(ctx, channelMerchantManager.findAllByMchNo(mchNo), provider, method);
|
||||
}
|
||||
|
||||
/// 从商户全部通道商户中筛出启用且其产品支持该(provider,method)的候选
|
||||
private List<LabelValue> filterChannelMchForDirectory(List<ChannelMerchant> mchants, String provider, String method) {
|
||||
/// 从商户全部通道商户中筛出启用且其产品支持该(provider,method)的候选(用预加载上下文,零查库)
|
||||
private List<LabelValue> filterChannelMchForDirectory(
|
||||
RouteBatchContext ctx, List<ChannelMerchant> mchants, String provider, String method) {
|
||||
List<LabelValue> results = new ArrayList<>();
|
||||
// 目录键与支付方式枚举预解析,避免逐商户重复计算
|
||||
if (!ctx.directoryPairKeys().contains(PayProviderMethodManager.pairKey(provider, method))) {
|
||||
return results;
|
||||
}
|
||||
PayMethodEnum methodEnum = PayMethodEnum.findByCode(method);
|
||||
for (ChannelMerchant mch : mchants) {
|
||||
if (!Boolean.TRUE.equals(mch.getEnable())) {
|
||||
continue;
|
||||
}
|
||||
String product = mch.getProduct();
|
||||
if (!routeProductSupportsMethod(product, provider, method)) {
|
||||
if (!routeProductSupportsMethod(ctx, product, methodEnum)) {
|
||||
continue;
|
||||
}
|
||||
String label = StrUtil.isNotBlank(mch.getChannelMerchantName())
|
||||
@@ -210,6 +225,54 @@ public class PayRouteStrategyCapabilitySupport {
|
||||
return routeProductSupportsMethod(product, PayMethodEnum.findByCode(methodCode));
|
||||
}
|
||||
|
||||
/// 批量判断上下文:循环外一次性预加载目录 / 策略 / 产品能力,循环内零查库
|
||||
private record RouteBatchContext(
|
||||
// 目录有效 (provider,method) 对键集合(替代逐次 contains 重查目录)
|
||||
Set<String> directoryPairKeys,
|
||||
// product → 策略实例(替代逐次 SpringUtil.getBeansOfType 扫描容器)
|
||||
Map<String, AbsProductStrategy> strategyByProduct,
|
||||
// product → 已挂载且启用能力编码集合(替代逐次 listByProduct + findByCode)
|
||||
Map<String, Set<String>> capabilityCodesByProduct) {
|
||||
}
|
||||
|
||||
/// 构建批量判断上下文:目录键集合 + 策略索引 + 产品能力映射
|
||||
private RouteBatchContext buildRouteBatchContext(List<PayProviderMethodEntry> entries) {
|
||||
Set<String> pairKeys = new HashSet<>();
|
||||
for (PayProviderMethodEntry entry : entries) {
|
||||
pairKeys.add(PayProviderMethodManager.pairKey(entry.getProviderCode(), entry.getMethodCode()));
|
||||
}
|
||||
// 一次性获取全部产品策略 bean,建立 product 索引(替代逐次 getBeansOfType 扫描容器)
|
||||
Map<String, AbsProductStrategy> strategyByProduct = new HashMap<>();
|
||||
for (AbsProductStrategy strategy : PaymentStrategyFactory.createGroup(AbsProductStrategy.class)) {
|
||||
strategyByProduct.put(strategy.getProduct().getCode(), strategy);
|
||||
}
|
||||
// 预加载「产品 → 能力编码」(替代逐次 listByProduct + 逐能力 findByCode)
|
||||
Map<String, Set<String>> capabilityCodesByProduct = payProductCapabilityService.loadCapabilityCodesByProduct();
|
||||
return new RouteBatchContext(pairKeys, strategyByProduct, capabilityCodesByProduct);
|
||||
}
|
||||
|
||||
/// 用预加载上下文判断产品是否支持目录支付方式(策略声明能力 ∩ DB 挂载启用),零查库
|
||||
private boolean routeProductSupportsMethod(RouteBatchContext ctx, String product, PayMethodEnum method) {
|
||||
if (method == null || StrUtil.isBlank(product)) {
|
||||
return false;
|
||||
}
|
||||
AbsProductStrategy strategy = ctx.strategyByProduct().get(product);
|
||||
if (strategy == null) {
|
||||
return false;
|
||||
}
|
||||
List<PayCapabilityEnum> declared = ProductStrategySupport.capabilitiesForMethod(strategy, method);
|
||||
if (CollUtil.isEmpty(declared)) {
|
||||
return false;
|
||||
}
|
||||
Set<String> mounted = ctx.capabilityCodesByProduct().getOrDefault(product, Set.of());
|
||||
for (PayCapabilityEnum capability : declared) {
|
||||
if (mounted.contains(capability.getCode())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// 通道商户号→产品编码(不存在返回 null)
|
||||
private String productOfChannelMchNo(String channelMchNo) {
|
||||
return channelMerchantManager.lambdaQuery()
|
||||
|
||||
Reference in New Issue
Block a user