mirror of
https://gitee.com/binary/weixin-java-tools.git
synced 2026-03-10 00:13:40 +08:00
🎨 #3898 【微信支付】支持单参数 switchover 自定义键及通知回调空 appId 降级处理
This commit is contained in:
@@ -38,6 +38,7 @@ public interface WxPayService {
|
||||
|
||||
/**
|
||||
* Map里 加入新的 {@link WxPayConfig},适用于动态添加新的微信商户配置.
|
||||
* 配置键将使用 mchId + "_" + appId 的格式.
|
||||
*
|
||||
* @param mchId 商户id
|
||||
* @param appId 微信应用id
|
||||
@@ -45,6 +46,15 @@ public interface WxPayService {
|
||||
*/
|
||||
void addConfig(String mchId, String appId, WxPayConfig wxPayConfig);
|
||||
|
||||
/**
|
||||
* Map里 加入新的 {@link WxPayConfig},使用自定义配置键,适用于动态添加新的微信商户配置.
|
||||
* 此方法允许使用任意唯一标识符(如租户ID)作为配置键,兼容单参数 switchover 使用方式.
|
||||
*
|
||||
* @param configKey 自定义的配置键(全局唯一标识符,如租户ID)
|
||||
* @param wxPayConfig 新的微信配置
|
||||
*/
|
||||
void addConfig(String configKey, WxPayConfig wxPayConfig);
|
||||
|
||||
/**
|
||||
* 从 Map中 移除 {@link String mchId} 和 {@link String appId} 所对应的 {@link WxPayConfig},适用于动态移除微信商户配置.
|
||||
*
|
||||
@@ -53,6 +63,14 @@ public interface WxPayService {
|
||||
*/
|
||||
void removeConfig(String mchId, String appId);
|
||||
|
||||
/**
|
||||
* 从 Map中 移除指定配置键所对应的 {@link WxPayConfig},适用于动态移除微信商户配置.
|
||||
* 此方法允许使用任意唯一标识符(如租户ID)删除配置,兼容单参数 switchover 使用方式.
|
||||
*
|
||||
* @param configKey 自定义的配置键(全局唯一标识符,如租户ID)
|
||||
*/
|
||||
void removeConfig(String configKey);
|
||||
|
||||
/**
|
||||
* 注入多个 {@link WxPayConfig} 的实现. 并为每个 {@link WxPayConfig} 赋予不同的 {@link String mchId} 值
|
||||
* 随机采用一个{@link String mchId}进行Http初始化操作
|
||||
@@ -79,14 +97,17 @@ public interface WxPayService {
|
||||
boolean switchover(String mchId, String appId);
|
||||
|
||||
/**
|
||||
* 仅根据商户号进行切换.
|
||||
* 适用于一个商户号对应多个appId的场景,切换时会匹配符合该商户号的配置.
|
||||
* 注意:由于HashMap迭代顺序不确定,当存在多个匹配项时返回的配置是不可预测的,建议使用精确匹配方式.
|
||||
* 根据商户号或自定义配置键进行切换.
|
||||
* <ul>
|
||||
* <li>当传入商户号(mchId)时,会先尝试精确匹配,若未找到则前缀匹配(mchId_*)。</li>
|
||||
* <li>也可传入通过 {@link #addConfig(String, WxPayConfig)} 或 {@link #setMultiConfig(Map)} 注册的任意自定义配置键,此时直接精确匹配。</li>
|
||||
* </ul>
|
||||
* 注意:当存在多个前缀匹配项时返回的配置是不可预测的,建议使用精确匹配方式.
|
||||
*
|
||||
* @param mchId 商户标识
|
||||
* @param mchIdOrConfigKey 商户标识或自定义配置键
|
||||
* @return 切换是否成功,如果找不到匹配的配置则返回false
|
||||
*/
|
||||
default boolean switchover(String mchId) {
|
||||
default boolean switchover(String mchIdOrConfigKey) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -100,15 +121,18 @@ public interface WxPayService {
|
||||
WxPayService switchoverTo(String mchId, String appId);
|
||||
|
||||
/**
|
||||
* 仅根据商户号进行切换.
|
||||
* 适用于一个商户号对应多个appId的场景,切换时会匹配符合该商户号的配置.
|
||||
* 注意:由于HashMap迭代顺序不确定,当存在多个匹配项时返回的配置是不可预测的,建议使用精确匹配方式.
|
||||
* 根据商户号或自定义配置键进行切换,支持链式调用.
|
||||
* <ul>
|
||||
* <li>当传入商户号(mchId)时,会先尝试精确匹配,若未找到则前缀匹配(mchId_*)。</li>
|
||||
* <li>也可传入通过 {@link #addConfig(String, WxPayConfig)} 或 {@link #setMultiConfig(Map)} 注册的任意自定义配置键,此时直接精确匹配。</li>
|
||||
* </ul>
|
||||
* 注意:当存在多个前缀匹配项时返回的配置是不可预测的,建议使用精确匹配方式.
|
||||
*
|
||||
* @param mchId 商户标识
|
||||
* @param mchIdOrConfigKey 商户标识或自定义配置键
|
||||
* @return 切换成功,则返回当前对象,方便链式调用
|
||||
* @throws me.chanjar.weixin.common.error.WxRuntimeException 如果找不到匹配的配置
|
||||
*/
|
||||
default WxPayService switchoverTo(String mchId) {
|
||||
default WxPayService switchoverTo(String mchIdOrConfigKey) {
|
||||
throw new me.chanjar.weixin.common.error.WxRuntimeException("子类需要实现此方法");
|
||||
}
|
||||
|
||||
|
||||
@@ -214,6 +214,18 @@ public abstract class BaseWxPayServiceImpl implements WxPayService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addConfig(String configKey, WxPayConfig wxPayConfig) {
|
||||
synchronized (this) {
|
||||
if (this.configMap == null) {
|
||||
this.setMultiConfig(ImmutableMap.of(configKey, wxPayConfig), configKey);
|
||||
} else {
|
||||
WxPayConfigHolder.set(configKey);
|
||||
this.configMap.put(configKey, wxPayConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeConfig(String mchId, String appId) {
|
||||
synchronized (this) {
|
||||
@@ -231,6 +243,22 @@ public abstract class BaseWxPayServiceImpl implements WxPayService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeConfig(String configKey) {
|
||||
synchronized (this) {
|
||||
this.configMap.remove(configKey);
|
||||
if (this.configMap.isEmpty()) {
|
||||
log.warn("已删除最后一个商户号配置:configKey[{}],须立即使用setConfig或setMultiConfig添加配置", configKey);
|
||||
return;
|
||||
}
|
||||
if (WxPayConfigHolder.get().equals(configKey)) {
|
||||
final String nextConfigKey = this.configMap.keySet().iterator().next();
|
||||
WxPayConfigHolder.set(nextConfigKey);
|
||||
log.warn("已删除默认商户号配置,商户号【{}】被设为默认配置", nextConfigKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMultiConfig(Map<String, WxPayConfig> wxPayConfigs) {
|
||||
this.setMultiConfig(wxPayConfigs, wxPayConfigs.keySet().iterator().next());
|
||||
@@ -244,6 +272,10 @@ public abstract class BaseWxPayServiceImpl implements WxPayService {
|
||||
|
||||
@Override
|
||||
public boolean switchover(String mchId, String appId) {
|
||||
// 如果appId为空,则降级为仅使用mchId进行切换
|
||||
if (StringUtils.isBlank(appId)) {
|
||||
return this.switchover(mchId);
|
||||
}
|
||||
String configKey = this.getConfigKey(mchId, appId);
|
||||
if (this.configMap.containsKey(configKey)) {
|
||||
WxPayConfigHolder.set(configKey);
|
||||
@@ -283,6 +315,10 @@ public abstract class BaseWxPayServiceImpl implements WxPayService {
|
||||
|
||||
@Override
|
||||
public WxPayService switchoverTo(String mchId, String appId) {
|
||||
// 如果appId为空,则降级为仅使用mchId进行切换
|
||||
if (StringUtils.isBlank(appId)) {
|
||||
return this.switchoverTo(mchId);
|
||||
}
|
||||
String configKey = this.getConfigKey(mchId, appId);
|
||||
if (this.configMap.containsKey(configKey)) {
|
||||
WxPayConfigHolder.set(configKey);
|
||||
|
||||
@@ -414,4 +414,133 @@ public class MultiAppIdSwitchoverTest {
|
||||
assertTrue(success);
|
||||
assertEquals(testService.getConfig().getAppId(), appId2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试使用自定义唯一键(非mchId格式)添加配置并切换.
|
||||
* 验证向后兼容性:支持使用任意唯一标识符(如租户ID)管理配置
|
||||
*/
|
||||
@Test
|
||||
public void testAddConfigWithCustomKey() {
|
||||
WxPayService testService = new WxPayServiceImpl();
|
||||
|
||||
String customKey1 = "tenant_001";
|
||||
String customKey2 = "tenant_002";
|
||||
|
||||
WxPayConfig config1 = new WxPayConfig();
|
||||
config1.setMchId("mch001");
|
||||
config1.setAppId("wxabc");
|
||||
config1.setMchKey("key_tenant_001");
|
||||
|
||||
WxPayConfig config2 = new WxPayConfig();
|
||||
config2.setMchId("mch002");
|
||||
config2.setAppId("wxdef");
|
||||
config2.setMchKey("key_tenant_002");
|
||||
|
||||
// 使用自定义键添加配置
|
||||
testService.addConfig(customKey1, config1);
|
||||
testService.addConfig(customKey2, config2);
|
||||
|
||||
// 使用自定义键切换配置
|
||||
boolean success = testService.switchover(customKey1);
|
||||
assertTrue(success, "应该能够使用自定义键切换配置");
|
||||
assertEquals(testService.getConfig().getMchKey(), "key_tenant_001");
|
||||
|
||||
success = testService.switchover(customKey2);
|
||||
assertTrue(success, "应该能够切换到第二个自定义键配置");
|
||||
assertEquals(testService.getConfig().getMchKey(), "key_tenant_002");
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试使用自定义唯一键删除配置.
|
||||
*/
|
||||
@Test
|
||||
public void testRemoveConfigWithCustomKey() {
|
||||
WxPayService testService = new WxPayServiceImpl();
|
||||
|
||||
String customKey1 = "tenant_A";
|
||||
String customKey2 = "tenant_B";
|
||||
|
||||
WxPayConfig config1 = new WxPayConfig();
|
||||
config1.setMchId("mchA");
|
||||
config1.setAppId("wxA");
|
||||
config1.setMchKey("key_A");
|
||||
|
||||
WxPayConfig config2 = new WxPayConfig();
|
||||
config2.setMchId("mchB");
|
||||
config2.setAppId("wxB");
|
||||
config2.setMchKey("key_B");
|
||||
|
||||
Map<String, WxPayConfig> configMap = new HashMap<>();
|
||||
configMap.put(customKey1, config1);
|
||||
configMap.put(customKey2, config2);
|
||||
testService.setMultiConfig(configMap);
|
||||
|
||||
// 删除第一个自定义键配置
|
||||
testService.removeConfig(customKey1);
|
||||
|
||||
// 尝试切换到已删除的配置应该失败
|
||||
boolean success = testService.switchover(customKey1);
|
||||
assertFalse(success, "切换到已删除的配置应该失败");
|
||||
|
||||
// 但仍然能够切换到第二个配置
|
||||
success = testService.switchover(customKey2);
|
||||
assertTrue(success, "切换到未删除的配置应该成功");
|
||||
assertEquals(testService.getConfig().getMchKey(), "key_B");
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试 switchover(mchId, appId) 当 appId 为 null 时降级为 switchover(mchId).
|
||||
* 模拟通知回调中 appId 可能为空的场景
|
||||
*/
|
||||
@Test
|
||||
public void testSwitchoverWithNullAppIdFallsBackToMchId() {
|
||||
// 切换到 appId 为 null 时,应该降级为只使用 mchId 匹配
|
||||
boolean success = payService.switchover(testMchId, null);
|
||||
assertTrue(success, "appId为null时应该降级为仅mchId匹配");
|
||||
assertEquals(payService.getConfig().getMchId(), testMchId);
|
||||
|
||||
// appId 为空字符串时同样应该降级
|
||||
success = payService.switchover(testMchId, "");
|
||||
assertTrue(success, "appId为空字符串时应该降级为仅mchId匹配");
|
||||
assertEquals(payService.getConfig().getMchId(), testMchId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试 switchoverTo(mchId, appId) 当 appId 为 null 时降级为 switchoverTo(mchId).
|
||||
*/
|
||||
@Test
|
||||
public void testSwitchoverToWithNullAppIdFallsBackToMchId() {
|
||||
WxPayService result = payService.switchoverTo(testMchId, null);
|
||||
assertNotNull(result);
|
||||
assertEquals(result, payService);
|
||||
assertEquals(payService.getConfig().getMchId(), testMchId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试使用自定义键通过 setMultiConfig 注册后可以直接 switchover.
|
||||
*/
|
||||
@Test
|
||||
public void testSwitchoverWithCustomKeyViaSetMultiConfig() {
|
||||
WxPayService testService = new WxPayServiceImpl();
|
||||
|
||||
String tenantId = "my-unique-tenant-id";
|
||||
WxPayConfig config = new WxPayConfig();
|
||||
config.setMchId("mchTenant");
|
||||
config.setAppId("wxTenant");
|
||||
config.setMchKey("key_tenant");
|
||||
|
||||
Map<String, WxPayConfig> configMap = new HashMap<>();
|
||||
configMap.put(tenantId, config);
|
||||
testService.setMultiConfig(configMap);
|
||||
|
||||
// 使用自定义租户ID切换
|
||||
boolean success = testService.switchover(tenantId);
|
||||
assertTrue(success, "应该能够使用自定义租户ID切换配置");
|
||||
assertEquals(testService.getConfig().getMchKey(), "key_tenant");
|
||||
|
||||
// switchoverTo 链式调用也应该支持
|
||||
WxPayService result = testService.switchoverTo(tenantId);
|
||||
assertNotNull(result);
|
||||
assertEquals(result, testService);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user