{"record":{"id":"67391ddd66535238","repo":"binarywang/WxJava","slug":"appid-67391d","errorCode":null,"errorMessage":"请确保微信公众号配置 appId 的唯一性","messagePattern":"请确保微信公众号配置 appId 的唯一性","errorType":"validation","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"spring-boot-starters/wx-java-mp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/configuration/services/AbstractWxMpConfiguration.java","lineNumber":58,"sourceCode":"    if (appsMap == null || appsMap.isEmpty()) {\n      log.warn(\"微信公众号应用参数未配置，通过 WxMpMultiServices#getWxMpService(\\\"tenantId\\\")获取实例将返回空\");\n      return new WxMpMultiServicesImpl();\n    }\n    \n    /**\n     * 校验 appId 是否唯一，避免使用 redis 缓存 token、ticket 时错乱。\n     *\n     * 查看 {@link me.chanjar.weixin.mp.config.impl.WxMpRedisConfigImpl#setAppId(String)}\n     */\n    Collection<WxMpSingleProperties> apps = appsMap.values();\n    if (apps.size() > 1) {\n      // 校验 appId 是否唯一\n      boolean multi = apps.stream()\n        // 没有 appId，如果不判断是否为空，这里会报 NPE 异常\n        .collect(Collectors.groupingBy(c -> c.getAppId() == null ? 0 : c.getAppId(), Collectors.counting()))\n        .entrySet().stream().anyMatch(e -> e.getValue() > 1);\n      if (multi) {\n        throw new RuntimeException(\"请确保微信公众号配置 appId 的唯一性\");\n      }\n    }\n\n    // 根据配置选择多租户模式\n    WxMpMultiProperties.MultiTenantMode mode = wxMpMultiProperties.getConfigStorage().getMultiTenantMode();\n    if (mode == WxMpMultiProperties.MultiTenantMode.SHARED) {\n      return createSharedMultiServices(appsMap, wxMpMultiProperties);\n    } else {\n      return createIsolatedMultiServices(appsMap, wxMpMultiProperties);\n    }\n  }\n\n  /**\n   * 创建隔离模式的多租户服务（每个租户独立 WxMpService 实例）\n   */\n  private WxMpMultiServices createIsolatedMultiServices(\n    Map<String, WxMpSingleProperties> appsMap,\n    WxMpMultiProperties wxMpMultiProperties) {","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/binarywang/WxJava/blob/1c43293a3c2c9d7e91304b6d037fb017f680d0c6/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/configuration/services/AbstractWxMpConfiguration.java#L40-L76","documentation":"Thrown by the Spring Boot multi-account Official Account (公众号/MP) starter when two tenants share the same appId, to avoid Redis token/ticket cache collisions (see WxMpRedisConfigImpl#setAppId). After the uniqueness check the starter branches into SHARED vs ISOLATED multi-tenant mode.","triggerScenarios":"Two entries under `wx.mp.apps` with the same `appId`; two entries with null appId (collapsed to key `0`).","commonSituations":"Same Official Account appId configured twice; templated YAML where appId placeholder was not replaced.","solutions":["Make each `wx.mp.apps.*.appId` distinct and non-blank.","Restart; then confirm the `multiTenantMode` setting is correct for your setup."],"exampleFix":"# before\nwx.mp.apps:\n  a: { appId: wxmp001 }\n  b: { appId: wxmp001 }\n# after\nwx.mp.apps:\n  a: { appId: wxmp001 }\n  b: { appId: wxmp007 }","handlingStrategy":"validation","validationCode":"// Spring Boot MP multi: pre-flight appId uniqueness\nSet<String> seen = new HashSet<>();\nfor (WxMpSingleProperties p : appsMap.values()) {\n    if (p.getAppId() == null || !seen.add(p.getAppId())) {\n        throw new IllegalStateException(\"Duplicate/null MP appId\");\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Assign a unique Official Account appId per tenant.","Decide SHARED vs ISOLATED mode based on whether tenants share cache infrastructure.","Catch duplicate appIds in a config-validation test."],"tags":["config","multi-tenant","mp","official-account","spring-boot","startup"],"backgroundTag":null,"analyzedSha":"1c43293a3c2c9d7e91304b6d037fb017f680d0c6","analyzedAt":"2026-08-14T02:29:11.060Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}