From 31ee4c0013a654d5d82dbeb98d64bcbb16aa9470 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 09:14:49 +0000 Subject: [PATCH 1/5] Initial plan From e9e263a86e9d24168944e211140acc037e8a2018 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 09:29:02 +0000 Subject: [PATCH 2/5] =?UTF-8?q?=E6=96=87=E6=A1=A3=EF=BC=9A=E8=AF=B4?= =?UTF-8?q?=E6=98=8E=20wx-java-cp-multi-spring-boot-starter=20=E4=B8=AD=20?= =?UTF-8?q?corp-secret=20=E7=9A=84=E9=85=8D=E7=BD=AE=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 增加 corp-secret 类型说明章节(自建应用/通讯录同步/客户联系) - 更新配置示例展示多种 Secret 同时使用的方式 - 改善 WxCpSingleProperties 的 Javadoc - 在 AbstractWxCpConfiguration 中增加设计意图注释 Agent-Logs-Url: https://github.com/binarywang/WxJava/sessions/c3b71887-e0c8-4297-b740-a39dc15c7d69 Co-authored-by: binarywang <1343140+binarywang@users.noreply.github.com> --- .../wx-java-cp-multi-solon-plugin/README.md | 77 ++++++++++--------- .../services/AbstractWxCpConfiguration.java | 8 ++ .../properties/WxCpSingleProperties.java | 26 ++++++- .../README.md | 77 ++++++++++--------- .../services/AbstractWxCpConfiguration.java | 8 ++ .../cp/properties/WxCpSingleProperties.java | 26 ++++++- 6 files changed, 144 insertions(+), 78 deletions(-) diff --git a/solon-plugins/wx-java-cp-multi-solon-plugin/README.md b/solon-plugins/wx-java-cp-multi-solon-plugin/README.md index 97bcf0723f..a2f429a249 100644 --- a/solon-plugins/wx-java-cp-multi-solon-plugin/README.md +++ b/solon-plugins/wx-java-cp-multi-solon-plugin/README.md @@ -6,6 +6,22 @@ - 未实现 WxCpTpService 初始化,需要的小伙伴可以参考多 WxCpService 配置的实现。 - 未实现 WxCpCgService 初始化,需要的小伙伴可以参考多 WxCpService 配置的实现。 +## 关于 corp-secret 的说明 + +企业微信中不同功能模块对应不同的 `corp-secret`,每种 Secret 只对对应模块的接口具有调用权限: + +| Secret 类型 | 获取位置 | 可调用的接口 | 是否需要 agent-id | +|---|---|---|---| +| 自建应用 Secret | 应用管理 → 自建应用 → 选择应用 → 查看 Secret | 该应用有权限的接口 | **必填** | +| 通讯录同步 Secret | 管理工具 → 通讯录同步 → 查看 Secret | 部门/成员增删改查等通讯录接口 | **不填** | +| 客户联系 Secret | 客户联系 → API → Secret | 客户联系相关接口 | 不填 | + +> **常见问题**: +> - 使用自建应用 Secret + agent-id 可以获取部门列表,但**无法更新部门**(因为写接口需要通讯录同步权限) +> - 使用通讯录同步 Secret 可以同步部门,但**调用某些需要 agent-id 的应用接口会报错** + +如需同时使用多种权限范围,可在 `wx.cp.corps` 下配置多个条目,每个条目使用对应权限的 Secret,通过不同的 `tenantId` 区分后使用。 + ## 快速开始 1. 引入依赖 @@ -18,25 +34,21 @@ ``` 2. 添加配置(app.properties) ```properties - # 应用 1 配置 - wx.cp.corps.tenantId1.corp-id = @corp-id - wx.cp.corps.tenantId1.corp-secret = @corp-secret + # 自建应用 1 配置(使用自建应用 Secret,需填写 agent-id) + wx.cp.corps.app1.corp-id = @corp-id + wx.cp.corps.app1.corp-secret = @自建应用的Secret(在"应用管理-自建应用"中查看) + wx.cp.corps.app1.agent-id = @自建应用的AgentId ## 选填 - wx.cp.corps.tenantId1.agent-id = @agent-id - wx.cp.corps.tenantId1.token = @token - wx.cp.corps.tenantId1.aes-key = @aes-key - wx.cp.corps.tenantId1.msg-audit-priKey = @msg-audit-priKey - wx.cp.corps.tenantId1.msg-audit-lib-path = @msg-audit-lib-path - - # 应用 2 配置 - wx.cp.corps.tenantId2.corp-id = @corp-id - wx.cp.corps.tenantId2.corp-secret = @corp-secret - ## 选填 - wx.cp.corps.tenantId2.agent-id = @agent-id - wx.cp.corps.tenantId2.token = @token - wx.cp.corps.tenantId2.aes-key = @aes-key - wx.cp.corps.tenantId2.msg-audit-priKey = @msg-audit-priKey - wx.cp.corps.tenantId2.msg-audit-lib-path = @msg-audit-lib-path + wx.cp.corps.app1.token = @token + wx.cp.corps.app1.aes-key = @aes-key + wx.cp.corps.app1.msg-audit-priKey = @msg-audit-priKey + wx.cp.corps.app1.msg-audit-lib-path = @msg-audit-lib-path + + # 通讯录同步配置(使用通讯录同步 Secret,不需要填写 agent-id) + # 此配置用于部门、成员的增删改查等通讯录管理操作 + wx.cp.corps.contact.corp-id = @corp-id + wx.cp.corps.contact.corp-secret = @通讯录同步的Secret(在"管理工具-通讯录同步"中查看) + ## agent-id 不填,通讯录同步不需要 agentId # 公共配置 ## ConfigStorage 配置(选填) @@ -59,6 +71,7 @@ ```java import com.binarywang.solon.wxjava.cp_multi.service.WxCpMultiServices; +import me.chanjar.weixin.cp.api.WxCpDepartmentService; import me.chanjar.weixin.cp.api.WxCpService; import me.chanjar.weixin.cp.api.WxCpUserService; import org.noear.solon.annotation.Component; @@ -70,27 +83,17 @@ public class DemoService { private WxCpMultiServices wxCpMultiServices; public void test() { - // 应用 1 的 WxCpService - WxCpService wxCpService1 = wxCpMultiServices.getWxCpService("tenantId1"); - WxCpUserService userService1 = wxCpService1.getUserService(); - userService1.getUserId("xxx"); - // todo ... - - // 应用 2 的 WxCpService - WxCpService wxCpService2 = wxCpMultiServices.getWxCpService("tenantId2"); - WxCpUserService userService2 = wxCpService2.getUserService(); - userService2.getUserId("xxx"); + // 使用自建应用的 WxCpService(对应 corp-secret 为自建应用 Secret) + WxCpService appService = wxCpMultiServices.getWxCpService("app1"); + WxCpUserService userService = appService.getUserService(); + userService.getUserId("xxx"); // todo ... - // 应用 3 的 WxCpService - WxCpService wxCpService3 = wxCpMultiServices.getWxCpService("tenantId3"); - // 判断是否为空 - if (wxCpService3 == null) { - // todo wxCpService3 为空,请先配置 tenantId3 企业微信应用参数 - return; - } - WxCpUserService userService3 = wxCpService3.getUserService(); - userService3.getUserId("xxx"); + // 使用通讯录同步的 WxCpService(对应 corp-secret 为通讯录同步 Secret) + // 通讯录同步 Secret 具有部门/成员增删改查等权限 + WxCpService contactService = wxCpMultiServices.getWxCpService("contact"); + WxCpDepartmentService departmentService = contactService.getDepartmentService(); + departmentService.update(department); // todo ... } } diff --git a/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/configuration/services/AbstractWxCpConfiguration.java b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/configuration/services/AbstractWxCpConfiguration.java index ada4ac504c..3a5885037b 100644 --- a/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/configuration/services/AbstractWxCpConfiguration.java +++ b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/configuration/services/AbstractWxCpConfiguration.java @@ -37,6 +37,14 @@ protected WxCpMultiServices wxCpMultiServices(WxCpMultiProperties wxCpMultiPrope /** * 校验同一个企业下,agentId 是否唯一,避免使用 redis 缓存 token、ticket 时错乱。 * + *

同一企业(corpId 相同)下可配置多个条目以使用不同的权限 Secret,例如: + *

+ * 但同一 corpId 下不允许出现重复的 agentId(包括多个 null)。 + *

+ * * 查看 {@link me.chanjar.weixin.cp.config.impl.AbstractWxCpInRedisConfigImpl#setAgentId(Integer)} */ Collection corpList = corps.values(); diff --git a/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/properties/WxCpSingleProperties.java b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/properties/WxCpSingleProperties.java index e761a09062..6f7f633c3f 100644 --- a/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/properties/WxCpSingleProperties.java +++ b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/properties/WxCpSingleProperties.java @@ -8,6 +8,16 @@ /** * 企业微信企业相关配置属性 * + *

企业微信中不同的 corpSecret 对应不同的权限范围,常见的有:

+ * + *

如需同时使用多种权限范围(例如:既要操作通讯录,又要调用自建应用接口), + * 可在 {@code wx.cp.corps} 下配置多个条目,每个条目使用对应权限的 {@code corpSecret}, + * 其中通讯录同步的条目无需填写 {@code agentId}。

+ * * @author yl * created on 2023/10/16 */ @@ -20,7 +30,16 @@ public class WxCpSingleProperties implements Serializable { */ private String corpId; /** - * 微信企业号 corpSecret + * 微信企业号 corpSecret(权限密钥) + * + *

企业微信针对不同的功能模块提供了不同的 Secret,每种 Secret 只对对应模块的接口有调用权限:

+ * */ private String corpSecret; /** @@ -28,7 +47,10 @@ public class WxCpSingleProperties implements Serializable { */ private String token; /** - * 微信企业号应用 ID + * 微信企业号应用 ID(AgentId) + * + *

使用自建应用 Secret 时,需要填写对应应用的 AgentId。

+ *

使用通讯录同步 Secret 时,无需填写此字段。

*/ private Integer agentId; /** diff --git a/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/README.md b/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/README.md index e3ea7bf0f8..db54ad96c7 100644 --- a/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/README.md +++ b/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/README.md @@ -6,6 +6,22 @@ - 未实现 WxCpTpService 初始化,需要的小伙伴可以参考多 WxCpService 配置的实现。 - 未实现 WxCpCgService 初始化,需要的小伙伴可以参考多 WxCpService 配置的实现。 +## 关于 corp-secret 的说明 + +企业微信中不同功能模块对应不同的 `corp-secret`,每种 Secret 只对对应模块的接口具有调用权限: + +| Secret 类型 | 获取位置 | 可调用的接口 | 是否需要 agent-id | +|---|---|---|---| +| 自建应用 Secret | 应用管理 → 自建应用 → 选择应用 → 查看 Secret | 该应用有权限的接口 | **必填** | +| 通讯录同步 Secret | 管理工具 → 通讯录同步 → 查看 Secret | 部门/成员增删改查等通讯录接口 | **不填** | +| 客户联系 Secret | 客户联系 → API → Secret | 客户联系相关接口 | 不填 | + +> **常见问题**: +> - 使用自建应用 Secret + agent-id 可以获取部门列表,但**无法更新部门**(因为写接口需要通讯录同步权限) +> - 使用通讯录同步 Secret 可以同步部门,但**调用某些需要 agent-id 的应用接口会报错** + +如需同时使用多种权限范围,可在 `wx.cp.corps` 下配置多个条目,每个条目使用对应权限的 Secret,通过不同的 `tenantId` 区分后使用。 + ## 快速开始 1. 引入依赖 @@ -18,25 +34,21 @@ ``` 2. 添加配置(application.properties) ```properties - # 应用 1 配置 - wx.cp.corps.tenantId1.corp-id = @corp-id - wx.cp.corps.tenantId1.corp-secret = @corp-secret + # 自建应用 1 配置(使用自建应用 Secret,需填写 agent-id) + wx.cp.corps.app1.corp-id = @corp-id + wx.cp.corps.app1.corp-secret = @自建应用的Secret(在"应用管理-自建应用"中查看) + wx.cp.corps.app1.agent-id = @自建应用的AgentId ## 选填 - wx.cp.corps.tenantId1.agent-id = @agent-id - wx.cp.corps.tenantId1.token = @token - wx.cp.corps.tenantId1.aes-key = @aes-key - wx.cp.corps.tenantId1.msg-audit-priKey = @msg-audit-priKey - wx.cp.corps.tenantId1.msg-audit-lib-path = @msg-audit-lib-path - - # 应用 2 配置 - wx.cp.corps.tenantId2.corp-id = @corp-id - wx.cp.corps.tenantId2.corp-secret = @corp-secret - ## 选填 - wx.cp.corps.tenantId2.agent-id = @agent-id - wx.cp.corps.tenantId2.token = @token - wx.cp.corps.tenantId2.aes-key = @aes-key - wx.cp.corps.tenantId2.msg-audit-priKey = @msg-audit-priKey - wx.cp.corps.tenantId2.msg-audit-lib-path = @msg-audit-lib-path + wx.cp.corps.app1.token = @token + wx.cp.corps.app1.aes-key = @aes-key + wx.cp.corps.app1.msg-audit-priKey = @msg-audit-priKey + wx.cp.corps.app1.msg-audit-lib-path = @msg-audit-lib-path + + # 通讯录同步配置(使用通讯录同步 Secret,不需要填写 agent-id) + # 此配置用于部门、成员的增删改查等通讯录管理操作 + wx.cp.corps.contact.corp-id = @corp-id + wx.cp.corps.contact.corp-secret = @通讯录同步的Secret(在"管理工具-通讯录同步"中查看) + ## agent-id 不填,通讯录同步不需要 agentId # 公共配置 ## ConfigStorage 配置(选填) @@ -59,6 +71,7 @@ ```java import com.binarywang.spring.starter.wxjava.cp.service.WxCpMultiServices; +import me.chanjar.weixin.cp.api.WxCpDepartmentService; import me.chanjar.weixin.cp.api.WxCpService; import me.chanjar.weixin.cp.api.WxCpUserService; import org.springframework.beans.factory.annotation.Autowired; @@ -70,27 +83,17 @@ public class DemoService { private WxCpMultiServices wxCpMultiServices; public void test() { - // 应用 1 的 WxCpService - WxCpService wxCpService1 = wxCpMultiServices.getWxCpService("tenantId1"); - WxCpUserService userService1 = wxCpService1.getUserService(); - userService1.getUserId("xxx"); - // todo ... - - // 应用 2 的 WxCpService - WxCpService wxCpService2 = wxCpMultiServices.getWxCpService("tenantId2"); - WxCpUserService userService2 = wxCpService2.getUserService(); - userService2.getUserId("xxx"); + // 使用自建应用的 WxCpService(对应 corp-secret 为自建应用 Secret) + WxCpService appService = wxCpMultiServices.getWxCpService("app1"); + WxCpUserService userService = appService.getUserService(); + userService.getUserId("xxx"); // todo ... - // 应用 3 的 WxCpService - WxCpService wxCpService3 = wxCpMultiServices.getWxCpService("tenantId3"); - // 判断是否为空 - if (wxCpService3 == null) { - // todo wxCpService3 为空,请先配置 tenantId3 企业微信应用参数 - return; - } - WxCpUserService userService3 = wxCpService3.getUserService(); - userService3.getUserId("xxx"); + // 使用通讯录同步的 WxCpService(对应 corp-secret 为通讯录同步 Secret) + // 通讯录同步 Secret 具有部门/成员增删改查等权限 + WxCpService contactService = wxCpMultiServices.getWxCpService("contact"); + WxCpDepartmentService departmentService = contactService.getDepartmentService(); + departmentService.update(department); // todo ... } } diff --git a/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/configuration/services/AbstractWxCpConfiguration.java b/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/configuration/services/AbstractWxCpConfiguration.java index 9b959222e0..2fd1d13064 100644 --- a/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/configuration/services/AbstractWxCpConfiguration.java +++ b/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/configuration/services/AbstractWxCpConfiguration.java @@ -40,6 +40,14 @@ protected WxCpMultiServices wxCpMultiServices(WxCpMultiProperties wxCpMultiPrope /** * 校验同一个企业下,agentId 是否唯一,避免使用 redis 缓存 token、ticket 时错乱。 * + *

同一企业(corpId 相同)下可配置多个条目以使用不同的权限 Secret,例如: + *

+ * 但同一 corpId 下不允许出现重复的 agentId(包括多个 null)。 + *

+ * * 查看 {@link me.chanjar.weixin.cp.config.impl.AbstractWxCpInRedisConfigImpl#setAgentId(Integer)} */ Collection corpList = corps.values(); diff --git a/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/properties/WxCpSingleProperties.java b/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/properties/WxCpSingleProperties.java index 8ad7149fe6..fcfa654a15 100644 --- a/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/properties/WxCpSingleProperties.java +++ b/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/properties/WxCpSingleProperties.java @@ -8,6 +8,16 @@ /** * 企业微信企业相关配置属性 * + *

企业微信中不同的 corpSecret 对应不同的权限范围,常见的有:

+ * + *

如需同时使用多种权限范围(例如:既要操作通讯录,又要调用自建应用接口), + * 可在 {@code wx.cp.corps} 下配置多个条目,每个条目使用对应权限的 {@code corpSecret}, + * 其中通讯录同步的条目无需填写 {@code agentId}。

+ * * @author yl * created on 2023/10/16 */ @@ -20,7 +30,16 @@ public class WxCpSingleProperties implements Serializable { */ private String corpId; /** - * 微信企业号 corpSecret + * 微信企业号 corpSecret(权限密钥) + * + *

企业微信针对不同的功能模块提供了不同的 Secret,每种 Secret 只对对应模块的接口有调用权限:

+ * */ private String corpSecret; /** @@ -28,7 +47,10 @@ public class WxCpSingleProperties implements Serializable { */ private String token; /** - * 微信企业号应用 ID + * 微信企业号应用 ID(AgentId) + * + *

使用自建应用 Secret 时,需要填写对应应用的 AgentId。

+ *

使用通讯录同步 Secret 时,无需填写此字段。

*/ private Integer agentId; /** From 65da91f5313762aaef45153c5666bc2938f66ba3 Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Sat, 11 Apr 2026 17:17:25 +0800 Subject: [PATCH 3/5] Update spring-boot-starters/wx-java-cp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/configuration/services/AbstractWxCpConfiguration.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../cp/configuration/services/AbstractWxCpConfiguration.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/configuration/services/AbstractWxCpConfiguration.java b/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/configuration/services/AbstractWxCpConfiguration.java index 2fd1d13064..d94342315a 100644 --- a/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/configuration/services/AbstractWxCpConfiguration.java +++ b/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/configuration/services/AbstractWxCpConfiguration.java @@ -40,13 +40,12 @@ protected WxCpMultiServices wxCpMultiServices(WxCpMultiProperties wxCpMultiPrope /** * 校验同一个企业下,agentId 是否唯一,避免使用 redis 缓存 token、ticket 时错乱。 * - *

同一企业(corpId 相同)下可配置多个条目以使用不同的权限 Secret,例如: + *

同一企业(corpId 相同)下可配置多个条目以使用不同的权限 Secret,例如:

*
    *
  • 自建应用条目:填写应用对应的 corpSecret 和 agentId
  • *
  • 通讯录同步条目:填写通讯录同步 Secret,agentId 可不填(null)
  • *
- * 但同一 corpId 下不允许出现重复的 agentId(包括多个 null)。 - *

+ *

但同一 corpId 下不允许出现重复的 agentId(包括多个 null)。

* * 查看 {@link me.chanjar.weixin.cp.config.impl.AbstractWxCpInRedisConfigImpl#setAgentId(Integer)} */ From 8ffe58517b46472d15d96b9b07ec148da06f218f Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Sat, 11 Apr 2026 17:17:37 +0800 Subject: [PATCH 4/5] Update solon-plugins/wx-java-cp-multi-solon-plugin/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- solon-plugins/wx-java-cp-multi-solon-plugin/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/solon-plugins/wx-java-cp-multi-solon-plugin/README.md b/solon-plugins/wx-java-cp-multi-solon-plugin/README.md index a2f429a249..9904da3029 100644 --- a/solon-plugins/wx-java-cp-multi-solon-plugin/README.md +++ b/solon-plugins/wx-java-cp-multi-solon-plugin/README.md @@ -22,6 +22,9 @@ 如需同时使用多种权限范围,可在 `wx.cp.corps` 下配置多个条目,每个条目使用对应权限的 Secret,通过不同的 `tenantId` 区分后使用。 +> **注意**: +> 当前 starter/插件实现会校验同一 `corp-id` 下的 `agent-id` **必须唯一**,并且 **只能有一个条目不填写 `agent-id`**。 +> 如果在同一 `corp-id` 下同时配置多个未填写 `agent-id` 的条目,会因 token/ticket 缓存 key 冲突而在启动时直接抛异常。 ## 快速开始 1. 引入依赖 From aaa3e7e9fc57273dbacd10632a28138a864f8951 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 11 Apr 2026 09:21:49 +0000 Subject: [PATCH 5/5] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=AF=84=E5=AE=A1?= =?UTF-8?q?=E6=84=8F=E8=A7=81=EF=BC=9A=E4=BF=AE=E5=A4=8D=20agentId=20?= =?UTF-8?q?=E5=88=86=E7=BB=84=20null=20=E5=86=B2=E7=AA=81=20bug=EF=BC=8C?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3=20Javadoc=20HTML=20=E7=BB=93=E6=9E=84?= =?UTF-8?q?=EF=BC=8C=E5=AE=8C=E5=96=84=20README=20=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E9=99=90=E5=88=B6=E8=AF=B4=E6=98=8E=E5=92=8C=E7=A4=BA=E4=BE=8B?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Agent-Logs-Url: https://github.com/binarywang/WxJava/sessions/dcb81708-f62f-4f63-837b-0e62c1da2883 Co-authored-by: binarywang <1343140+binarywang@users.noreply.github.com> --- .../wx-java-cp-multi-solon-plugin/README.md | 9 +++++++-- .../services/AbstractWxCpConfiguration.java | 10 +++++----- .../wx-java-cp-multi-spring-boot-starter/README.md | 14 +++++++++++++- .../services/AbstractWxCpConfiguration.java | 5 +++-- 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/solon-plugins/wx-java-cp-multi-solon-plugin/README.md b/solon-plugins/wx-java-cp-multi-solon-plugin/README.md index 9904da3029..8eb467f98f 100644 --- a/solon-plugins/wx-java-cp-multi-solon-plugin/README.md +++ b/solon-plugins/wx-java-cp-multi-solon-plugin/README.md @@ -23,7 +23,7 @@ 如需同时使用多种权限范围,可在 `wx.cp.corps` 下配置多个条目,每个条目使用对应权限的 Secret,通过不同的 `tenantId` 区分后使用。 > **注意**: -> 当前 starter/插件实现会校验同一 `corp-id` 下的 `agent-id` **必须唯一**,并且 **只能有一个条目不填写 `agent-id`**。 +> 当前插件实现会校验同一 `corp-id` 下的 `agent-id` **必须唯一**,并且 **只能有一个条目不填写 `agent-id`**。 > 如果在同一 `corp-id` 下同时配置多个未填写 `agent-id` 的条目,会因 token/ticket 缓存 key 冲突而在启动时直接抛异常。 ## 快速开始 @@ -77,6 +77,7 @@ import com.binarywang.solon.wxjava.cp_multi.service.WxCpMultiServices; import me.chanjar.weixin.cp.api.WxCpDepartmentService; import me.chanjar.weixin.cp.api.WxCpService; import me.chanjar.weixin.cp.api.WxCpUserService; +import me.chanjar.weixin.cp.bean.WxCpDepart; import org.noear.solon.annotation.Component; import org.noear.solon.annotation.Inject; @@ -96,7 +97,11 @@ public class DemoService { // 通讯录同步 Secret 具有部门/成员增删改查等权限 WxCpService contactService = wxCpMultiServices.getWxCpService("contact"); WxCpDepartmentService departmentService = contactService.getDepartmentService(); - departmentService.update(department); + // 更新部门示例(WxCpDepart 包含 id、name、parentId 等字段) + WxCpDepart depart = new WxCpDepart(); + depart.setId(100L); + depart.setName("新部门名称"); + departmentService.update(depart); // todo ... } } diff --git a/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/configuration/services/AbstractWxCpConfiguration.java b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/configuration/services/AbstractWxCpConfiguration.java index 3a5885037b..25b4ab3747 100644 --- a/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/configuration/services/AbstractWxCpConfiguration.java +++ b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/configuration/services/AbstractWxCpConfiguration.java @@ -15,6 +15,7 @@ import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; @@ -37,13 +38,12 @@ protected WxCpMultiServices wxCpMultiServices(WxCpMultiProperties wxCpMultiPrope /** * 校验同一个企业下,agentId 是否唯一,避免使用 redis 缓存 token、ticket 时错乱。 * - *

同一企业(corpId 相同)下可配置多个条目以使用不同的权限 Secret,例如: + *

同一企业(corpId 相同)下可配置多个条目以使用不同的权限 Secret,例如:

*
    *
  • 自建应用条目:填写应用对应的 corpSecret 和 agentId
  • *
  • 通讯录同步条目:填写通讯录同步 Secret,agentId 可不填(null)
  • *
- * 但同一 corpId 下不允许出现重复的 agentId(包括多个 null)。 - *

+ *

但同一 corpId 下不允许出现重复的 agentId(包括多个 null)。

* * 查看 {@link me.chanjar.weixin.cp.config.impl.AbstractWxCpInRedisConfigImpl#setAgentId(Integer)} */ @@ -57,8 +57,8 @@ protected WxCpMultiServices wxCpMultiServices(WxCpMultiProperties wxCpMultiPrope String corpId = entry.getKey(); // 校验每个企业下,agentId 是否唯一 boolean multi = entry.getValue().stream() - // 通讯录没有 agentId,如果不判断是否为空,这里会报 NPE 异常 - .collect(Collectors.groupingBy(c -> c.getAgentId() == null ? 0 : c.getAgentId(), Collectors.counting())) + // 通讯录没有 agentId,使用字符串转换避免 null 与 agentId=0 冲突 + .collect(Collectors.groupingBy(c -> Objects.toString(c.getAgentId(), "null"), Collectors.counting())) .entrySet().stream().anyMatch(e -> e.getValue() > 1); if (multi) { throw new RuntimeException("请确保企业微信配置唯一性[" + corpId + "]"); diff --git a/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/README.md b/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/README.md index db54ad96c7..0f0b74695e 100644 --- a/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/README.md +++ b/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/README.md @@ -22,6 +22,13 @@ 如需同时使用多种权限范围,可在 `wx.cp.corps` 下配置多个条目,每个条目使用对应权限的 Secret,通过不同的 `tenantId` 区分后使用。 +> **配置限制说明**: +> - 当前 starter 实现会校验:同一 `corp-id` 下,`agent-id` **必须唯一** +> - 同一 `corp-id` 下,**只能有一个条目不填 `agent-id`** +> - 否则会因为 token/ticket 缓存 key 冲突而在启动时直接抛异常 +> +> 因此,像"通讯录同步 Secret""客户联系 Secret"这类通常不填写 `agent-id` 的配置,**不能**在同一个 `corp-id` 下同时配置多个 `agent-id` 均为空的条目;如确有多个条目,请确保其中最多只有一个未填写 `agent-id`。 + ## 快速开始 1. 引入依赖 @@ -74,6 +81,7 @@ import com.binarywang.spring.starter.wxjava.cp.service.WxCpMultiServices; import me.chanjar.weixin.cp.api.WxCpDepartmentService; import me.chanjar.weixin.cp.api.WxCpService; import me.chanjar.weixin.cp.api.WxCpUserService; +import me.chanjar.weixin.cp.bean.WxCpDepart; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -93,7 +101,11 @@ public class DemoService { // 通讯录同步 Secret 具有部门/成员增删改查等权限 WxCpService contactService = wxCpMultiServices.getWxCpService("contact"); WxCpDepartmentService departmentService = contactService.getDepartmentService(); - departmentService.update(department); + // 更新部门示例(WxCpDepart 包含 id、name、parentId 等字段) + WxCpDepart depart = new WxCpDepart(); + depart.setId(100L); + depart.setName("新部门名称"); + departmentService.update(depart); // todo ... } } diff --git a/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/configuration/services/AbstractWxCpConfiguration.java b/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/configuration/services/AbstractWxCpConfiguration.java index d94342315a..a10bdf9bed 100644 --- a/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/configuration/services/AbstractWxCpConfiguration.java +++ b/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/configuration/services/AbstractWxCpConfiguration.java @@ -18,6 +18,7 @@ import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; @@ -59,8 +60,8 @@ protected WxCpMultiServices wxCpMultiServices(WxCpMultiProperties wxCpMultiPrope String corpId = entry.getKey(); // 校验每个企业下,agentId 是否唯一 boolean multi = entry.getValue().stream() - // 通讯录没有 agentId,如果不判断是否为空,这里会报 NPE 异常 - .collect(Collectors.groupingBy(c -> c.getAgentId() == null ? 0 : c.getAgentId(), Collectors.counting())) + // 通讯录没有 agentId,使用字符串转换避免 null 与 agentId=0 冲突 + .collect(Collectors.groupingBy(c -> Objects.toString(c.getAgentId(), "null"), Collectors.counting())) .entrySet().stream().anyMatch(e -> e.getValue() > 1); if (multi) { throw new RuntimeException("请确保企业微信配置唯一性[" + corpId + "]");