refactor(route): 删除冗余字段enable/name与废弃i18n, 优化能力候选批量接口

PayRouteStrategy 删除开源版不需要的 enable/name 字段(实体/param/result/convert/创建逻辑/运行时isEnable检查); 删除 strategyDisabled 及 18 个历次重构遗留废弃 i18n key; routeProductSupportsMethod 2参版降 private; listSceneCapabilityCandidatesBatch 预加载优化(删 mchNo 无用参数, 循环内零查库); PayRouteStrategyConvert 删重复 import; update-tables.sql 追加 DROP COLUMN。
This commit is contained in:
DaxPay Dev
2026-06-28 17:51:44 +08:00
parent be45cfd201
commit 68684bde2e
11 changed files with 52 additions and 72 deletions

View File

@@ -0,0 +1,3 @@
-- 支付通道路由策略表:移除开源版不需要的 enable/name 字段
ALTER TABLE pay_route_strategy DROP COLUMN IF EXISTS enable;
ALTER TABLE pay_route_strategy DROP COLUMN IF EXISTS name;

View File

@@ -1,7 +1,5 @@
package cn.daxpay.open.payment.merchant.convert.route.strategy;
import cn.daxpay.open.payment.merchant.entity.route.strategy.PayRouteStrategy;
import cn.daxpay.open.payment.merchant.result.route.strategy.PayRouteStrategyResult;
import cn.daxpay.open.payment.merchant.entity.route.strategy.PayRouteStrategy;
import cn.daxpay.open.payment.merchant.param.route.strategy.PayRouteStrategyParam;
import cn.daxpay.open.payment.merchant.result.route.strategy.PayRouteStrategyResult;

View File

@@ -29,12 +29,6 @@ public class PayRouteStrategy extends MchBaseEntity implements ToResult<PayRoute
/// @see PayRouteModeEnum
private String mode;
/// 是否启用
private boolean enable;
/// 策略名称
private String name;
@Override
public PayRouteStrategyResult toResult() {
return PayRouteStrategyConvert.CONVERT.toResult(this);

View File

@@ -13,9 +13,6 @@ import lombok.experimental.Accessors;
@Schema(title = "支付通道路由策略参数")
public class PayRouteStrategyParam {
@Schema(description = "主键,更新时传入")
private Long id;
@NotBlank(message = "{validation.field.appId.notBlank}")
@Size(max = 32)
@Schema(description = "应用号")
@@ -23,10 +20,4 @@ public class PayRouteStrategyParam {
@Schema(description = "路由模式basic/scene")
private String mode;
@Schema(description = "是否启用")
private Boolean enable;
@Schema(description = "策略名称")
private String name;
}

View File

@@ -22,10 +22,4 @@ public class PayRouteStrategyResult extends BaseResult {
@Schema(description = "路由模式")
private String mode;
@Schema(description = "是否启用")
private boolean enable;
@Schema(description = "策略名称")
private String name;
}

View File

@@ -50,8 +50,6 @@ public class PayRouteConfigService {
strategy.setAppId(appId);
strategy.setMchNo(mchAppInfoManager.requireMchNoByAppIdNotTenant(appId));
strategy.setMode(PayRouteModeEnum.BASIC.getCode());
strategy.setEnable(true);
strategy.setName("默认策略");
strategyManager.save(strategy);
return strategy.toResult();
}

View File

@@ -60,9 +60,6 @@ public class PayRouteService implements PayRouteFacade {
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR, "pay.route.error.strategyNotFound");
}
PayRouteStrategy strategy = bundle.getStrategy();
if (!strategy.isEnable()) {
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR, "pay.route.error.strategyDisabled");
}
RouteHit hit = resolveByMode(bundle, payParam, strategy.getMode());
if (hit == null) {
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR, "pay.route.error.noMatch");

View File

@@ -120,8 +120,8 @@ public class PayRouteSceneConfigService {
/// 按目录项+通道商户批量返回支付能力候选
public Map<String, List<LabelValue>> listSceneCapabilityCandidatesBatch(PayRouteSceneCapabilityBatchParam param) {
String mchNo = mchAppInfoManager.requireMchNoByAppIdNotTenant(param.getAppId());
return payRouteStrategyCapabilitySupport.listSceneCapabilityCandidatesBatch(mchNo, param.getItems());
mchAppInfoManager.requireMchNoByAppIdNotTenant(param.getAppId());
return payRouteStrategyCapabilitySupport.listSceneCapabilityCandidatesBatch(param.getItems());
}
/// 按目录项(支付渠道+支付方式)筛选商户已开通的通道商户候选

View File

@@ -31,6 +31,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
/// # 通道路由:策略方式→通道商户与能力候选
///
@@ -67,22 +68,64 @@ public class PayRouteStrategyCapabilitySupport {
}
/// 按目录项+通道商户批量返回支付能力候选
public Map<String, List<LabelValue>> listSceneCapabilityCandidatesBatch(
String mchNo, List<PayRouteSceneCapabilityBatchItem> items) {
///
/// 优化:目录、策略、产品能力及 channelMchNo→product 映射在循环外一次性预加载,
/// 循环内仅做内存集合求交,避免逐 item 重复查库与扫描 Spring 容器。
public Map<String, List<LabelValue>> listSceneCapabilityCandidatesBatch(List<PayRouteSceneCapabilityBatchItem> items) {
Map<String, List<LabelValue>> index = new LinkedHashMap<>();
if (CollUtil.isEmpty(items)) {
return index;
}
RouteBatchContext ctx = buildRouteBatchContext(payProviderMethodService.listDirectoryEntries());
// 批量预加载 channelMchNo → product替代逐次 productOfChannelMchNo 查库
Set<String> channelMchNos = new HashSet<>();
for (PayRouteSceneCapabilityBatchItem item : items) {
if (item != null && StrUtil.isNotBlank(item.getChannelMchNo())) {
channelMchNos.add(item.getChannelMchNo());
}
}
Map<String, String> productByChannelMchNo = channelMerchantManager.lambdaQuery()
.select(ChannelMerchant::getChannelMchNo, ChannelMerchant::getProduct)
.in(ChannelMerchant::getChannelMchNo, channelMchNos)
.list()
.stream()
.collect(Collectors.toMap(ChannelMerchant::getChannelMchNo, ChannelMerchant::getProduct, (a, b) -> a));
for (PayRouteSceneCapabilityBatchItem item : items) {
if (item == null || StrUtil.hasBlank(item.getProvider(), item.getMethod(), item.getChannelMchNo())) {
continue;
}
String key = capabilityBatchKey(item.getProvider(), item.getMethod(), item.getChannelMchNo());
index.put(key, listSceneCapabilityCandidates(item.getProvider(), item.getMethod(), item.getChannelMchNo()));
if (!ctx.directoryPairKeys().contains(PayProviderMethodManager.pairKey(item.getProvider(), item.getMethod()))) {
index.put(key, List.of());
continue;
}
String product = productByChannelMchNo.get(item.getChannelMchNo());
if (StrUtil.isBlank(product)) {
index.put(key, List.of());
continue;
}
index.put(key, capabilitiesForMethodWithCtx(ctx, product, PayMethodEnum.findByCode(item.getMethod())));
}
return index;
}
/// 用预加载上下文计算支付能力候选(策略声明能力 ∩ DB 挂载启用),零查库
private List<LabelValue> capabilitiesForMethodWithCtx(RouteBatchContext ctx, String product, PayMethodEnum method) {
AbsProductStrategy strategy = ctx.strategyByProduct().get(product);
if (strategy == null) {
return List.of();
}
List<PayCapabilityEnum> declared = ProductStrategySupport.capabilitiesForMethod(strategy, method);
if (CollUtil.isEmpty(declared)) {
return List.of();
}
Set<String> mounted = ctx.capabilityCodesByProduct().getOrDefault(product, Set.of());
return declared.stream()
.filter(capability -> mounted.contains(capability.getCode()))
.map(capability -> new LabelValue(I18nUtil.getEnumName(capability), capability.getCode()))
.toList();
}
/// 能力批量候选 Map 的 keyprovider|method|channelMchNo
public static String capabilityBatchKey(String provider, String method, String channelMchNo) {
return provider + "|" + method + "|" + channelMchNo;
@@ -206,7 +249,7 @@ public class PayRouteStrategyCapabilitySupport {
}
/// 通道路由:产品是否支持目录支付方式(策略 Map ∧ DB
public boolean routeProductSupportsMethod(String product, PayMethodEnum method) {
private boolean routeProductSupportsMethod(String product, PayMethodEnum method) {
if (!PaymentStrategyFactory.existsByProduct(product, AbsProductStrategy.class)) {
return false;
}

View File

@@ -1,37 +1,18 @@
{
"strategyNotFound": "Payment route strategy not configured for this app",
"strategyDisabled": "Payment route strategy is disabled",
"noMatch": "No available payment product matched",
"providerRequired": "Pay brand is required when product is not specified",
"methodRequiredWhenProductSet": "Pay method is required when product is specified",
"basicProviderInvalid": "Invalid pay brand",
"basicProductNotAvailable": "No available product for pay brand [{0}]; check merchant product, channel merchant and app channel config",
"basicProductNotConfigured": "No payment product configured for pay brand [{0}]; configure it in basic route mode",
"basicProductAmbiguous": "Multiple available products for pay brand [{0}]; specify product explicitly",
"methodNotSupportedForProduct": "Product \"{0}\" has no available payment methods",
"duplicateSceneConfig": "Duplicate pay brand configuration in scene mode",
"duplicatePayProvider": "Duplicate configuration for pay brand [{0}]",
"duplicatePayProviderMethod": "Duplicate configuration for pay brand [{0}] and method [{1}]",
"sceneDirectoryIncomplete": "Payment product is required for pay brand [{0}] and method [{1}]",
"scenePayProviderRequiredForMethod": "Pay brand is required for method [{0}]",
"sceneGenericMethodNoPayProvider": "Generic pay method [{0}] cannot be bound to a pay brand",
"sceneMethodPayProviderMismatch": "Pay method [{0}] does not match pay brand [{1}]",
"sceneProductRequired": "Payment product is required in scene routing mode",
"sceneMethodRequired": "Pay method is required in scene routing mode",
"sceneMethodProductMismatch": "Pay method [{0}] is not supported by product [{1}]",
"sceneProductCapabilityPair": "For pay brand [{0}] and method [{1}], select both payment product and capability, or leave both empty",
"sceneCapabilityRequired": "Payment capability is required in scene routing mode",
"sceneCapabilityProductMismatch": "Payment capability [{0}] does not match product [{1}] and method [{2}]",
"sceneChannelMethodRequired": "Generic pay route requires channel and pay method",
"providerRequiredForScene": "Pay brand is required in scene routing mode",
"productNotResolved": "Cannot resolve product for channel [{0}] and method [{1}]",
"productMismatch": "Specified product [{0}] does not match resolved [{1}]",
"productInvalid": "Invalid payment product [{0}]",
"productStrategyMissing": "Payment product strategy not found",
"routeStrategyNotExist": "Route strategy not found",
"routeModeNotExist": "Route mode not found: {0}",
"businessSceneNotExist": "Business scene not found: {0}",
"terminalSceneNotExist": "Terminal scene not found: {0}",
"channelMchNoRequired": "Channel merchant no cannot be blank",
"channelMchNotExist": "Channel merchant [{0}] not found",
"channelMchDisabled": "Channel merchant [{0}] is disabled",

View File

@@ -1,37 +1,18 @@
{
"strategyNotFound": "应用未配置通道路由策略",
"strategyDisabled": "通道路由策略已禁用",
"noMatch": "未匹配到可用支付产品",
"providerRequired": "未指定支付产品时,支付渠道不能为空",
"methodRequiredWhenProductSet": "指定支付产品时,支付方式不能为空",
"basicProviderInvalid": "支付渠道无效",
"basicProductNotAvailable": "支付渠道[{0}]下无可用支付产品,请检查商户产品、通道商户及应用通道配置",
"basicProductNotConfigured": "支付渠道[{0}]未配置支付产品,请在通道路由基础模式中完成配置",
"basicProductAmbiguous": "支付渠道[{0}]下存在多个可用支付产品,请指定 product 参数",
"methodNotSupportedForProduct": "产品「{0}」无可用支付方式",
"duplicateSceneConfig": "场景模式下同一支付渠道存在多条配置",
"duplicatePayProvider": "支付渠道[{0}]配置重复",
"duplicatePayProviderMethod": "支付渠道[{0}]下支付方式[{1}]配置重复",
"sceneDirectoryIncomplete": "须为支付渠道[{0}]的支付方式[{1}]配置支付产品",
"scenePayProviderRequiredForMethod": "非通用支付方式[{0}]须指定支付渠道",
"sceneGenericMethodNoPayProvider": "通用支付方式[{0}]不能绑定支付渠道",
"sceneMethodPayProviderMismatch": "支付方式[{0}]与支付渠道[{1}]不匹配",
"sceneProductRequired": "场景模式下须选择支付产品",
"sceneMethodRequired": "场景模式下须选择支付方式",
"sceneMethodProductMismatch": "支付方式[{0}]与产品[{1}]不匹配",
"sceneProductCapabilityPair": "支付渠道[{0}]支付方式[{1}]须同时选择支付产品与支付能力,或同时留空",
"sceneCapabilityRequired": "场景模式下须选择支付能力",
"sceneCapabilityProductMismatch": "支付能力[{0}]与产品[{1}]、支付方式[{2}]不匹配",
"sceneChannelMethodRequired": "通用支付配置须指定通道与支付方式",
"providerRequiredForScene": "场景模式下,支付渠道不能为空",
"productNotResolved": "通道[{0}]与支付方式[{1}]无法解析支付产品",
"productMismatch": "指定产品[{0}]与解析结果[{1}]不一致",
"productInvalid": "支付产品[{0}]无效",
"productStrategyMissing": "支付产品策略不存在",
"routeStrategyNotExist": "路由策略不存在",
"routeModeNotExist": "路由模式不存在: {0}",
"businessSceneNotExist": "业务场景不存在: {0}",
"terminalSceneNotExist": "终端场景不存在: {0}",
"channelMchNoRequired": "通道商户号不能为空",
"channelMchNotExist": "通道商户号[{0}]不存在",
"channelMchDisabled": "通道商户号[{0}]未启用",