Skip to content

Commit 3590de1

Browse files
foreveryang321binarywang
authored andcommitted
🆕 【公众号】新增多公众号配置 wx-java-mp-multi-spring-boot-starter
1 parent b86144c commit 3590de1

17 files changed

Lines changed: 906 additions & 0 deletions

spring-boot-starters/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
<modules>
2121
<module>wx-java-miniapp-spring-boot-starter</module>
22+
<module>wx-java-mp-multi-spring-boot-starter</module>
2223
<module>wx-java-mp-spring-boot-starter</module>
2324
<module>wx-java-pay-spring-boot-starter</module>
2425
<module>wx-java-open-spring-boot-starter</module>
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# wx-java-mp-spring-boot-starter
2+
3+
## 快速开始
4+
5+
1. 引入依赖
6+
```xml
7+
<dependency>
8+
<groupId>com.github.binarywang</groupId>
9+
<artifactId>wx-java-mp-multi-spring-boot-starter</artifactId>
10+
<version>${version}</version>
11+
</dependency>
12+
```
13+
2. 添加配置(application.properties)
14+
```properties
15+
# 公众号配置
16+
## 应用 1 配置(必填)
17+
wx.mp.tenantId1.app-id=appId
18+
wx.mp.tenantId1.app-secret=@secret
19+
## 选填
20+
wx.mp.tenantId1.token=@token
21+
wx.mp.tenantId1.aes-key=@aesKey
22+
## 应用 2 配置(必填)
23+
wx.mp.tenantId2.app-id=@appId
24+
wx.mp.tenantId2.app-secret =@secret
25+
## 选填
26+
wx.mp.tenantId2.token=@token
27+
wx.mp.tenantId2.aes-key=@aesKey
28+
29+
# ConfigStorage 配置(选填)
30+
## 配置类型: memory(默认), jedis, redisson, redis_template
31+
wx.mp.config-storage.type=memory
32+
## 相关redis前缀配置: wx:mp:multi(默认)
33+
wx.mp.config-storage.key-prefix=wx:mp:multi
34+
wx.mp.config-storage.redis.host=127.0.0.1
35+
wx.mp.config-storage.redis.port=6379
36+
## 单机和 sentinel 同时存在时,优先使用sentinel配置
37+
# wx.mp.config-storage.redis.sentinel-ips=127.0.0.1:16379,127.0.0.1:26379
38+
# wx.mp.config-storage.redis.sentinel-name=mymaster
39+
40+
# http 客户端配置(选填)
41+
## # http客户端类型: http_client(默认), ok_http, jodd_http
42+
wx.mp.config-storage.http-client-type=http_client
43+
wx.mp.config-storage.http-proxy-host=
44+
wx.mp.config-storage.http-proxy-port=
45+
wx.mp.config-storage.http-proxy-username=
46+
wx.mp.config-storage.http-proxy-password=
47+
## 最大重试次数,默认:5 次,如果小于 0,则为 0
48+
wx.mp.config-storage.max-retry-times=5
49+
## 重试时间间隔步进,默认:1000 毫秒,如果小于 0,则为 1000
50+
wx.mp.config-storage.retry-sleep-millis=1000
51+
52+
# 公众号地址 host 配置
53+
# wx.mp.hosts.api-host=http://proxy.com/
54+
# wx.mp.hosts.open-host=http://proxy.com/
55+
# wx.mp.hosts.mp-host=http://proxy.com/
56+
```
57+
3. 自动注入的类型:`WxMpMultiServices`
58+
59+
4. 使用样例
60+
61+
```java
62+
import com.binarywang.spring.starter.wxjava.mp.service.WxMpMultiServices;
63+
import me.chanjar.weixin.mp.api.WxMpService;
64+
import me.chanjar.weixin.mp.api.WxMpUserService;
65+
import org.springframework.beans.factory.annotation.Autowired;
66+
import org.springframework.stereotype.Service;
67+
68+
@Service
69+
public class DemoService {
70+
@Autowired
71+
private WxMpMultiServices wxMpMultiServices;
72+
73+
public void test() {
74+
// 应用 1 的 WxMpService
75+
WxMpService wxMpService1 = wxMpMultiServices.getWxMpService("tenantId1");
76+
WxMpUserService userService1 = wxMpService1.getUserService();
77+
userService1.userInfo("xxx");
78+
// todo ...
79+
80+
// 应用 2 的 WxMpService
81+
WxMpService wxMpService2 = wxMpMultiServices.getWxMpService("tenantId2");
82+
WxMpUserService userService2 = wxMpService2.getUserService();
83+
userService2.userInfo("xxx");
84+
// todo ...
85+
86+
// 应用 3 的 WxMpService
87+
WxMpService wxMpService3 = wxMpMultiServices.getWxMpService("tenantId3");
88+
// 判断是否为空
89+
if (wxMpService3 == null) {
90+
// todo wxMpService3 为空,请先配置 tenantId3 微信公众号应用参数
91+
return;
92+
}
93+
WxMpUserService userService3 = wxMpService3.getUserService();
94+
userService3.userInfo("xxx");
95+
// todo ...
96+
}
97+
}
98+
```
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<parent>
6+
<artifactId>wx-java-spring-boot-starters</artifactId>
7+
<groupId>com.github.binarywang</groupId>
8+
<version>4.6.0</version>
9+
</parent>
10+
<modelVersion>4.0.0</modelVersion>
11+
12+
<artifactId>wx-java-mp-multi-spring-boot-starter</artifactId>
13+
<name>WxJava - Spring Boot Starter for MP::支持多账号配置</name>
14+
<description>微信公众号开发的 Spring Boot Starter::支持多账号配置</description>
15+
16+
<dependencies>
17+
<dependency>
18+
<groupId>com.github.binarywang</groupId>
19+
<artifactId>weixin-java-mp</artifactId>
20+
<version>${project.version}</version>
21+
</dependency>
22+
<dependency>
23+
<groupId>redis.clients</groupId>
24+
<artifactId>jedis</artifactId>
25+
<scope>provided</scope>
26+
</dependency>
27+
<dependency>
28+
<groupId>org.redisson</groupId>
29+
<artifactId>redisson</artifactId>
30+
<scope>provided</scope>
31+
</dependency>
32+
<dependency>
33+
<groupId>org.springframework.data</groupId>
34+
<artifactId>spring-data-redis</artifactId>
35+
<scope>provided</scope>
36+
</dependency>
37+
<dependency>
38+
<groupId>org.jodd</groupId>
39+
<artifactId>jodd-http</artifactId>
40+
<scope>provided</scope>
41+
</dependency>
42+
<dependency>
43+
<groupId>com.squareup.okhttp3</groupId>
44+
<artifactId>okhttp</artifactId>
45+
<scope>provided</scope>
46+
</dependency>
47+
</dependencies>
48+
49+
<build>
50+
<plugins>
51+
<plugin>
52+
<groupId>org.springframework.boot</groupId>
53+
<artifactId>spring-boot-maven-plugin</artifactId>
54+
<version>${spring.boot.version}</version>
55+
</plugin>
56+
<plugin>
57+
<groupId>org.apache.maven.plugins</groupId>
58+
<artifactId>maven-source-plugin</artifactId>
59+
<version>2.2.1</version>
60+
<executions>
61+
<execution>
62+
<id>attach-sources</id>
63+
<goals>
64+
<goal>jar-no-fork</goal>
65+
</goals>
66+
</execution>
67+
</executions>
68+
</plugin>
69+
</plugins>
70+
</build>
71+
72+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.binarywang.spring.starter.wxjava.mp.autoconfigure;
2+
3+
import com.binarywang.spring.starter.wxjava.mp.configuration.WxMpMultiServiceConfiguration;
4+
import org.springframework.context.annotation.Configuration;
5+
import org.springframework.context.annotation.Import;
6+
7+
/**
8+
* @author yl
9+
* created on 2024/1/23
10+
*/
11+
@Configuration
12+
@Import(WxMpMultiServiceConfiguration.class)
13+
public class WxMpMultiAutoConfiguration {
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.binarywang.spring.starter.wxjava.mp.configuration;
2+
3+
import com.binarywang.spring.starter.wxjava.mp.configuration.services.WxMpInJedisConfiguration;
4+
import com.binarywang.spring.starter.wxjava.mp.configuration.services.WxMpInMemoryConfiguration;
5+
import com.binarywang.spring.starter.wxjava.mp.configuration.services.WxMpInRedisTemplateConfiguration;
6+
import com.binarywang.spring.starter.wxjava.mp.configuration.services.WxMpInRedissonConfiguration;
7+
import com.binarywang.spring.starter.wxjava.mp.properties.WxMpMultiProperties;
8+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
9+
import org.springframework.context.annotation.Configuration;
10+
import org.springframework.context.annotation.Import;
11+
12+
/**
13+
* 微信公众号相关服务自动注册
14+
*
15+
* @author yl
16+
* created on 2024/1/23
17+
*/
18+
@Configuration
19+
@EnableConfigurationProperties(WxMpMultiProperties.class)
20+
@Import({
21+
WxMpInJedisConfiguration.class,
22+
WxMpInMemoryConfiguration.class,
23+
WxMpInRedissonConfiguration.class,
24+
WxMpInRedisTemplateConfiguration.class
25+
})
26+
public class WxMpMultiServiceConfiguration {
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
package com.binarywang.spring.starter.wxjava.mp.configuration.services;
2+
3+
import com.binarywang.spring.starter.wxjava.mp.properties.WxMpMultiProperties;
4+
import com.binarywang.spring.starter.wxjava.mp.properties.WxMpSingleProperties;
5+
import com.binarywang.spring.starter.wxjava.mp.service.WxMpMultiServices;
6+
import com.binarywang.spring.starter.wxjava.mp.service.WxMpMultiServicesImpl;
7+
import lombok.RequiredArgsConstructor;
8+
import lombok.extern.slf4j.Slf4j;
9+
import me.chanjar.weixin.mp.api.WxMpService;
10+
import me.chanjar.weixin.mp.api.impl.WxMpServiceHttpClientImpl;
11+
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
12+
import me.chanjar.weixin.mp.api.impl.WxMpServiceJoddHttpImpl;
13+
import me.chanjar.weixin.mp.api.impl.WxMpServiceOkHttpImpl;
14+
import me.chanjar.weixin.mp.config.WxMpConfigStorage;
15+
import me.chanjar.weixin.mp.config.WxMpHostConfig;
16+
import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
17+
import org.apache.commons.lang3.StringUtils;
18+
19+
import java.util.Collection;
20+
import java.util.Map;
21+
import java.util.Set;
22+
import java.util.stream.Collectors;
23+
24+
/**
25+
* WxMpConfigStorage 抽象配置类
26+
*
27+
* @author yl
28+
* created on 2024/1/23
29+
*/
30+
@RequiredArgsConstructor
31+
@Slf4j
32+
public abstract class AbstractWxMpConfiguration {
33+
34+
protected WxMpMultiServices wxMpMultiServices(WxMpMultiProperties wxCpMultiProperties) {
35+
Map<String, WxMpSingleProperties> appsMap = wxCpMultiProperties.getApps();
36+
if (appsMap == null || appsMap.isEmpty()) {
37+
log.warn("微信公众号应用参数未配置,通过 WxMpMultiServices#getWxMpService(\"tenantId\")获取实例将返回空");
38+
return new WxMpMultiServicesImpl();
39+
}
40+
/**
41+
* 校验 appId 是否唯一,避免使用 redis 缓存 token、ticket 时错乱。
42+
*
43+
* 查看 {@link me.chanjar.weixin.mp.config.impl.WxMpRedisConfigImpl#setAppId(String)}
44+
*/
45+
Collection<WxMpSingleProperties> apps = appsMap.values();
46+
if (apps.size() > 1) {
47+
// 校验 appId 是否唯一
48+
boolean multi = apps.stream()
49+
// 没有 appId,如果不判断是否为空,这里会报 NPE 异常
50+
.collect(Collectors.groupingBy(c -> c.getAppId() == null ? 0 : c.getAppId(), Collectors.counting()))
51+
.entrySet().stream().anyMatch(e -> e.getValue() > 1);
52+
if (multi) {
53+
throw new RuntimeException("请确保微信公众号配置 appId 的唯一性");
54+
}
55+
}
56+
WxMpMultiServicesImpl services = new WxMpMultiServicesImpl();
57+
58+
Set<Map.Entry<String, WxMpSingleProperties>> entries = appsMap.entrySet();
59+
for (Map.Entry<String, WxMpSingleProperties> entry : entries) {
60+
String tenantId = entry.getKey();
61+
WxMpSingleProperties wxMpSingleProperties = entry.getValue();
62+
WxMpDefaultConfigImpl storage = this.wxMpConfigStorage(wxCpMultiProperties);
63+
this.configApp(storage, wxMpSingleProperties);
64+
this.configHttp(storage, wxCpMultiProperties.getConfigStorage());
65+
this.configHost(storage, wxCpMultiProperties.getHosts());
66+
WxMpService wxCpService = this.wxMpService(storage, wxCpMultiProperties);
67+
services.addWxMpService(tenantId, wxCpService);
68+
}
69+
return services;
70+
}
71+
72+
/**
73+
* 配置 WxMpDefaultConfigImpl
74+
*
75+
* @param wxMpMultiProperties 参数
76+
* @return WxMpDefaultConfigImpl
77+
*/
78+
protected abstract WxMpDefaultConfigImpl wxMpConfigStorage(WxMpMultiProperties wxMpMultiProperties);
79+
80+
public WxMpService wxMpService(WxMpConfigStorage configStorage, WxMpMultiProperties wxMpMultiProperties) {
81+
WxMpMultiProperties.ConfigStorage storage = wxMpMultiProperties.getConfigStorage();
82+
WxMpMultiProperties.HttpClientType httpClientType = storage.getHttpClientType();
83+
WxMpService wxMpService;
84+
switch (httpClientType) {
85+
case OK_HTTP:
86+
wxMpService = new WxMpServiceOkHttpImpl();
87+
break;
88+
case JODD_HTTP:
89+
wxMpService = new WxMpServiceJoddHttpImpl();
90+
break;
91+
case HTTP_CLIENT:
92+
wxMpService = new WxMpServiceHttpClientImpl();
93+
break;
94+
default:
95+
wxMpService = new WxMpServiceImpl();
96+
break;
97+
}
98+
99+
wxMpService.setWxMpConfigStorage(configStorage);
100+
int maxRetryTimes = storage.getMaxRetryTimes();
101+
if (maxRetryTimes < 0) {
102+
maxRetryTimes = 0;
103+
}
104+
int retrySleepMillis = storage.getRetrySleepMillis();
105+
if (retrySleepMillis < 0) {
106+
retrySleepMillis = 1000;
107+
}
108+
wxMpService.setRetrySleepMillis(retrySleepMillis);
109+
wxMpService.setMaxRetryTimes(maxRetryTimes);
110+
return wxMpService;
111+
}
112+
113+
private void configApp(WxMpDefaultConfigImpl config, WxMpSingleProperties corpProperties) {
114+
String appId = corpProperties.getAppId();
115+
String appSecret = corpProperties.getAppSecret();
116+
String token = corpProperties.getToken();
117+
String aesKey = corpProperties.getAesKey();
118+
119+
config.setAppId(appId);
120+
config.setSecret(appSecret);
121+
if (StringUtils.isNotBlank(token)) {
122+
config.setToken(token);
123+
}
124+
if (StringUtils.isNotBlank(aesKey)) {
125+
config.setAesKey(aesKey);
126+
}
127+
}
128+
129+
private void configHttp(WxMpDefaultConfigImpl config, WxMpMultiProperties.ConfigStorage storage) {
130+
String httpProxyHost = storage.getHttpProxyHost();
131+
Integer httpProxyPort = storage.getHttpProxyPort();
132+
String httpProxyUsername = storage.getHttpProxyUsername();
133+
String httpProxyPassword = storage.getHttpProxyPassword();
134+
if (StringUtils.isNotBlank(httpProxyHost)) {
135+
config.setHttpProxyHost(httpProxyHost);
136+
if (httpProxyPort != null) {
137+
config.setHttpProxyPort(httpProxyPort);
138+
}
139+
if (StringUtils.isNotBlank(httpProxyUsername)) {
140+
config.setHttpProxyUsername(httpProxyUsername);
141+
}
142+
if (StringUtils.isNotBlank(httpProxyPassword)) {
143+
config.setHttpProxyPassword(httpProxyPassword);
144+
}
145+
}
146+
}
147+
148+
/**
149+
* wx host config
150+
*/
151+
private void configHost(WxMpDefaultConfigImpl config, WxMpMultiProperties.HostConfig hostConfig) {
152+
if (hostConfig != null) {
153+
String apiHost = hostConfig.getApiHost();
154+
String mpHost = hostConfig.getMpHost();
155+
String openHost = hostConfig.getOpenHost();
156+
WxMpHostConfig wxMpHostConfig = new WxMpHostConfig();
157+
wxMpHostConfig.setApiHost(StringUtils.isNotBlank(apiHost) ? apiHost : null);
158+
wxMpHostConfig.setMpHost(StringUtils.isNotBlank(mpHost) ? mpHost : null);
159+
wxMpHostConfig.setOpenHost(StringUtils.isNotBlank(openHost) ? openHost : null);
160+
config.setHostConfig(wxMpHostConfig);
161+
}
162+
}
163+
}

0 commit comments

Comments
 (0)