-
-
Notifications
You must be signed in to change notification settings - Fork 9k
添加直接获取配置的方法,解决多商户管理场景下的 ThreadLocal 限制 #3863
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -785,11 +785,33 @@ default WxPayService switchoverTo(String mchId) { | |
|
|
||
| /** | ||
| * 获取配置. | ||
| * 在多商户配置场景下,会根据 WxPayConfigHolder 中的值获取对应的配置. | ||
| * | ||
| * @return the config | ||
| */ | ||
| WxPayConfig getConfig(); | ||
|
|
||
| /** | ||
| * 根据商户号和 appId 直接获取配置. | ||
| * 此方法不依赖 ThreadLocal,可以在任何上下文中使用,适用于多商户管理场景. | ||
| * | ||
| * @param mchId 商户号 | ||
| * @param appId 微信应用 id | ||
| * @return 对应的配置对象,如果不存在则返回 null | ||
| */ | ||
| WxPayConfig getConfig(String mchId, String appId); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| /** | ||
| * 根据商户号直接获取配置. | ||
| * 此方法不依赖 ThreadLocal,可以在任何上下文中使用. | ||
| * 适用于一个商户号对应多个 appId 的场景,会返回该商户号的任意一个配置. | ||
| * 注意:当存在多个匹配项时返回的配置是不可预测的,建议使用精确匹配方式. | ||
| * | ||
| * @param mchId 商户号 | ||
| * @return 对应的配置对象,如果不存在则返回 null | ||
| */ | ||
| WxPayConfig getConfig(String mchId); | ||
|
|
||
| /** | ||
| * 设置配置对象. | ||
| * | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -154,6 +154,45 @@ public WxPayConfig getConfig() { | |||||||||||||||||||||||||||||||||||||||||
| return this.configMap.get(WxPayConfigHolder.get()); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||||||||||||||||||||
| public WxPayConfig getConfig(String mchId, String appId) { | ||||||||||||||||||||||||||||||||||||||||||
| if (StringUtils.isBlank(mchId)) { | ||||||||||||||||||||||||||||||||||||||||||
| log.warn("商户号mchId不能为空"); | ||||||||||||||||||||||||||||||||||||||||||
| return null; | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| if (StringUtils.isBlank(appId)) { | ||||||||||||||||||||||||||||||||||||||||||
| log.warn("应用ID appId不能为空"); | ||||||||||||||||||||||||||||||||||||||||||
| return null; | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| String configKey = this.getConfigKey(mchId, appId); | ||||||||||||||||||||||||||||||||||||||||||
| return this.configMap.get(configKey); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||||||||||||||||||||
| public WxPayConfig getConfig(String mchId) { | ||||||||||||||||||||||||||||||||||||||||||
| if (StringUtils.isBlank(mchId)) { | ||||||||||||||||||||||||||||||||||||||||||
| log.warn("商户号mchId不能为空"); | ||||||||||||||||||||||||||||||||||||||||||
| return null; | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| // 先尝试精确匹配(针对只有mchId没有appId的配置) | ||||||||||||||||||||||||||||||||||||||||||
| if (this.configMap.containsKey(mchId)) { | ||||||||||||||||||||||||||||||||||||||||||
| return this.configMap.get(mchId); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| // 尝试前缀匹配(查找以 mchId_ 开头的配置) | ||||||||||||||||||||||||||||||||||||||||||
| String prefix = mchId + "_"; | ||||||||||||||||||||||||||||||||||||||||||
| for (Map.Entry<String, WxPayConfig> entry : this.configMap.entrySet()) { | ||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||||||||||||||||||||||||||||||||||||||
| if (entry.getKey().startsWith(prefix)) { | ||||||||||||||||||||||||||||||||||||||||||
| log.debug("根据mchId=【{}】找到配置key=【{}】", mchId, entry.getKey()); | ||||||||||||||||||||||||||||||||||||||||||
| return entry.getValue(); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| log.warn("无法找到对应mchId=【{}】的商户号配置信息", mchId); | ||||||||||||||||||||||||||||||||||||||||||
| return null; | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+185
to
+193
|
||||||||||||||||||||||||||||||||||||||||||
| for (Map.Entry<String, WxPayConfig> entry : this.configMap.entrySet()) { | |
| if (entry.getKey().startsWith(prefix)) { | |
| log.debug("根据mchId=【{}】找到配置key=【{}】", mchId, entry.getKey()); | |
| return entry.getValue(); | |
| } | |
| } | |
| log.warn("无法找到对应mchId=【{}】的商户号配置信息", mchId); | |
| return null; | |
| return this.configMap.entrySet().stream() | |
| .filter(entry -> entry.getKey().startsWith(prefix)) | |
| .findFirst() | |
| .map(entry -> { | |
| log.debug("根据mchId=【{}】找到配置key=【{}】", mchId, entry.getKey()); | |
| return entry.getValue(); | |
| }) | |
| .orElseGet(() -> { | |
| log.warn("无法找到对应mchId=【{}】的商户号配置信息", mchId); | |
| return null; | |
| }); |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getConfig(String mchId) 方法的行为存在不确定性问题。文档中提到"当存在多个匹配项时返回的配置是不可预测的",但实际实现总是返回第一个匹配的配置(取决于 HashMap 的遍历顺序)。
这种不确定性可能导致:
- 不同环境下返回不同的配置
- 相同代码在不同时间执行可能得到不同结果
- 难以调试和定位问题
建议:
- 在文档中更明确地说明这是基于内部存储顺序的,不应依赖
- 或者考虑使用确定性的排序(如字典序)来返回最小的 key 对应的配置
- 在日志中输出实际返回的配置 key,帮助调试
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
文档示例中的使用模式存在不一致。在场景4的第二个例子(processRefund方法)中,先使用 getConfig(mchId) 获取配置,然后仍然调用 switchover 方法切换配置。
这种模式的问题:
建议改进示例:
更好的示例可能是: