Skip to content

Commit 9486f9a

Browse files
Copilotbinarywang
andcommitted
新增 wx-java-open-multi-spring-boot-starter 模块以支持多个微信开放平台配置
Co-authored-by: binarywang <1343140+binarywang@users.noreply.github.com>
1 parent a6de21c commit 9486f9a

17 files changed

Lines changed: 877 additions & 0 deletions

spring-boot-starters/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
<module>wx-java-mp-multi-spring-boot-starter</module>
2424
<module>wx-java-mp-spring-boot-starter</module>
2525
<module>wx-java-pay-spring-boot-starter</module>
26+
<module>wx-java-open-multi-spring-boot-starter</module>
2627
<module>wx-java-open-spring-boot-starter</module>
2728
<module>wx-java-qidian-spring-boot-starter</module>
2829
<module>wx-java-cp-multi-spring-boot-starter</module>
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# wx-java-open-multi-spring-boot-starter
2+
3+
## 快速开始
4+
5+
1. 引入依赖
6+
```xml
7+
<dependency>
8+
<groupId>com.github.binarywang</groupId>
9+
<artifactId>wx-java-open-multi-spring-boot-starter</artifactId>
10+
<version>${version}</version>
11+
</dependency>
12+
```
13+
2. 添加配置(application.properties)
14+
```properties
15+
# 开放平台配置
16+
## 应用 1 配置(必填)
17+
wx.open.apps.tenantId1.app-id=appId
18+
wx.open.apps.tenantId1.secret=@secret
19+
## 选填
20+
wx.open.apps.tenantId1.token=@token
21+
wx.open.apps.tenantId1.aes-key=@aesKey
22+
wx.open.apps.tenantId1.api-host-url=@apiHostUrl
23+
wx.open.apps.tenantId1.access-token-url=@accessTokenUrl
24+
## 应用 2 配置(必填)
25+
wx.open.apps.tenantId2.app-id=@appId
26+
wx.open.apps.tenantId2.secret=@secret
27+
## 选填
28+
wx.open.apps.tenantId2.token=@token
29+
wx.open.apps.tenantId2.aes-key=@aesKey
30+
wx.open.apps.tenantId2.api-host-url=@apiHostUrl
31+
wx.open.apps.tenantId2.access-token-url=@accessTokenUrl
32+
33+
# ConfigStorage 配置(选填)
34+
## 配置类型: memory(默认), jedis, redisson, redis_template
35+
wx.open.config-storage.type=memory
36+
## 相关redis前缀配置: wx:open:multi(默认)
37+
wx.open.config-storage.key-prefix=wx:open:multi
38+
wx.open.config-storage.redis.host=127.0.0.1
39+
wx.open.config-storage.redis.port=6379
40+
## 单机和 sentinel 同时存在时,优先使用sentinel配置
41+
# wx.open.config-storage.redis.sentinel-ips=127.0.0.1:16379,127.0.0.1:26379
42+
# wx.open.config-storage.redis.sentinel-name=mymaster
43+
44+
# http 客户端配置(选填)
45+
## # http客户端类型: http_client(默认)
46+
wx.open.config-storage.http-client-type=http_client
47+
wx.open.config-storage.http-proxy-host=
48+
wx.open.config-storage.http-proxy-port=
49+
wx.open.config-storage.http-proxy-username=
50+
wx.open.config-storage.http-proxy-password=
51+
## 最大重试次数,默认:5 次,如果小于 0,则为 0
52+
wx.open.config-storage.max-retry-times=5
53+
## 重试时间间隔步进,默认:1000 毫秒,如果小于 0,则为 1000
54+
wx.open.config-storage.retry-sleep-millis=1000
55+
## 连接超时时间,单位毫秒,默认:5000
56+
wx.open.config-storage.connection-timeout=5000
57+
## 读数据超时时间,即socketTimeout,单位毫秒,默认:5000
58+
wx.open.config-storage.so-timeout=5000
59+
## 从连接池获取链接的超时时间,单位毫秒,默认:5000
60+
wx.open.config-storage.connection-request-timeout=5000
61+
```
62+
3. 自动注入的类型:`WxOpenMultiServices`
63+
64+
4. 使用样例
65+
66+
```java
67+
import com.binarywang.spring.starter.wxjava.open.service.WxOpenMultiServices;
68+
import me.chanjar.weixin.open.api.WxOpenService;
69+
import me.chanjar.weixin.open.api.WxOpenComponentService;
70+
import org.springframework.beans.factory.annotation.Autowired;
71+
import org.springframework.stereotype.Service;
72+
73+
@Service
74+
public class DemoService {
75+
@Autowired
76+
private WxOpenMultiServices wxOpenMultiServices;
77+
78+
public void test() {
79+
// 应用 1 的 WxOpenService
80+
WxOpenService wxOpenService1 = wxOpenMultiServices.getWxOpenService("tenantId1");
81+
WxOpenComponentService componentService1 = wxOpenService1.getWxOpenComponentService();
82+
// todo ...
83+
84+
// 应用 2 的 WxOpenService
85+
WxOpenService wxOpenService2 = wxOpenMultiServices.getWxOpenService("tenantId2");
86+
WxOpenComponentService componentService2 = wxOpenService2.getWxOpenComponentService();
87+
// todo ...
88+
89+
// 应用 3 的 WxOpenService
90+
WxOpenService wxOpenService3 = wxOpenMultiServices.getWxOpenService("tenantId3");
91+
// 判断是否为空
92+
if (wxOpenService3 == null) {
93+
// todo wxOpenService3 为空,请先配置 tenantId3 微信开放平台应用参数
94+
return;
95+
}
96+
WxOpenComponentService componentService3 = wxOpenService3.getWxOpenComponentService();
97+
// todo ...
98+
}
99+
}
100+
```
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
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.8.0</version>
9+
</parent>
10+
<modelVersion>4.0.0</modelVersion>
11+
12+
<artifactId>wx-java-open-multi-spring-boot-starter</artifactId>
13+
<name>WxJava - Spring Boot Starter for WxOpen::支持多账号配置</name>
14+
<description>微信开放平台开发的 Spring Boot Starter::支持多账号配置</description>
15+
16+
<dependencies>
17+
<dependency>
18+
<groupId>com.github.binarywang</groupId>
19+
<artifactId>weixin-java-open</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+
</dependencies>
38+
39+
<build>
40+
<plugins>
41+
<plugin>
42+
<groupId>org.springframework.boot</groupId>
43+
<artifactId>spring-boot-maven-plugin</artifactId>
44+
<version>${spring.boot.version}</version>
45+
</plugin>
46+
<plugin>
47+
<groupId>org.apache.maven.plugins</groupId>
48+
<artifactId>maven-source-plugin</artifactId>
49+
<version>2.2.1</version>
50+
<executions>
51+
<execution>
52+
<id>attach-sources</id>
53+
<goals>
54+
<goal>jar-no-fork</goal>
55+
</goals>
56+
</execution>
57+
</executions>
58+
</plugin>
59+
</plugins>
60+
</build>
61+
62+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.binarywang.spring.starter.wxjava.open.autoconfigure;
2+
3+
import com.binarywang.spring.starter.wxjava.open.configuration.WxOpenMultiServiceConfiguration;
4+
import org.springframework.context.annotation.Configuration;
5+
import org.springframework.context.annotation.Import;
6+
7+
/**
8+
* 微信开放平台多账号自动配置
9+
*
10+
* @author someone
11+
*/
12+
@Configuration
13+
@Import(WxOpenMultiServiceConfiguration.class)
14+
public class WxOpenMultiAutoConfiguration {
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.binarywang.spring.starter.wxjava.open.configuration;
2+
3+
import com.binarywang.spring.starter.wxjava.open.configuration.services.WxOpenInJedisConfiguration;
4+
import com.binarywang.spring.starter.wxjava.open.configuration.services.WxOpenInMemoryConfiguration;
5+
import com.binarywang.spring.starter.wxjava.open.configuration.services.WxOpenInRedisTemplateConfiguration;
6+
import com.binarywang.spring.starter.wxjava.open.configuration.services.WxOpenInRedissonConfiguration;
7+
import com.binarywang.spring.starter.wxjava.open.properties.WxOpenMultiProperties;
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 someone
16+
*/
17+
@Configuration
18+
@EnableConfigurationProperties(WxOpenMultiProperties.class)
19+
@Import({
20+
WxOpenInJedisConfiguration.class,
21+
WxOpenInMemoryConfiguration.class,
22+
WxOpenInRedissonConfiguration.class,
23+
WxOpenInRedisTemplateConfiguration.class
24+
})
25+
public class WxOpenMultiServiceConfiguration {
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
package com.binarywang.spring.starter.wxjava.open.configuration.services;
2+
3+
import com.binarywang.spring.starter.wxjava.open.properties.WxOpenMultiProperties;
4+
import com.binarywang.spring.starter.wxjava.open.properties.WxOpenSingleProperties;
5+
import com.binarywang.spring.starter.wxjava.open.service.WxOpenMultiServices;
6+
import com.binarywang.spring.starter.wxjava.open.service.WxOpenMultiServicesImpl;
7+
import lombok.RequiredArgsConstructor;
8+
import lombok.extern.slf4j.Slf4j;
9+
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
10+
import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder;
11+
import me.chanjar.weixin.open.api.WxOpenConfigStorage;
12+
import me.chanjar.weixin.open.api.WxOpenService;
13+
import me.chanjar.weixin.open.api.impl.WxOpenInMemoryConfigStorage;
14+
import me.chanjar.weixin.open.api.impl.WxOpenServiceImpl;
15+
import org.apache.commons.lang3.StringUtils;
16+
17+
import java.util.Collection;
18+
import java.util.Map;
19+
import java.util.Set;
20+
import java.util.stream.Collectors;
21+
22+
/**
23+
* WxOpenConfigStorage 抽象配置类
24+
*
25+
* @author someone
26+
*/
27+
@RequiredArgsConstructor
28+
@Slf4j
29+
public abstract class AbstractWxOpenConfiguration {
30+
31+
protected WxOpenMultiServices wxOpenMultiServices(WxOpenMultiProperties wxOpenMultiProperties) {
32+
Map<String, WxOpenSingleProperties> appsMap = wxOpenMultiProperties.getApps();
33+
if (appsMap == null || appsMap.isEmpty()) {
34+
log.warn("微信开放平台应用参数未配置,通过 WxOpenMultiServices#getWxOpenService(\"tenantId\")获取实例将返回空");
35+
return new WxOpenMultiServicesImpl();
36+
}
37+
/**
38+
* 校验 appId 是否唯一,避免使用 redis 缓存 token、ticket 时错乱。
39+
*/
40+
Collection<WxOpenSingleProperties> apps = appsMap.values();
41+
if (apps.size() > 1) {
42+
// 校验 appId 是否唯一
43+
boolean multi = apps.stream()
44+
// 没有 appId,如果不判断是否为空,这里会报 NPE 异常
45+
.collect(Collectors.groupingBy(c -> c.getAppId() == null ? 0 : c.getAppId(), Collectors.counting()))
46+
.entrySet().stream().anyMatch(e -> e.getValue() > 1);
47+
if (multi) {
48+
throw new RuntimeException("请确保微信开放平台配置 appId 的唯一性");
49+
}
50+
}
51+
WxOpenMultiServicesImpl services = new WxOpenMultiServicesImpl();
52+
53+
Set<Map.Entry<String, WxOpenSingleProperties>> entries = appsMap.entrySet();
54+
for (Map.Entry<String, WxOpenSingleProperties> entry : entries) {
55+
String tenantId = entry.getKey();
56+
WxOpenSingleProperties wxOpenSingleProperties = entry.getValue();
57+
WxOpenInMemoryConfigStorage storage = this.wxOpenConfigStorage(wxOpenMultiProperties);
58+
this.configApp(storage, wxOpenSingleProperties);
59+
this.configHttp(storage, wxOpenMultiProperties.getConfigStorage());
60+
WxOpenService wxOpenService = this.wxOpenService(storage, wxOpenMultiProperties);
61+
services.addWxOpenService(tenantId, wxOpenService);
62+
}
63+
return services;
64+
}
65+
66+
/**
67+
* 配置 WxOpenInMemoryConfigStorage
68+
*
69+
* @param wxOpenMultiProperties 参数
70+
* @return WxOpenInMemoryConfigStorage
71+
*/
72+
protected abstract WxOpenInMemoryConfigStorage wxOpenConfigStorage(WxOpenMultiProperties wxOpenMultiProperties);
73+
74+
public WxOpenService wxOpenService(WxOpenConfigStorage configStorage, WxOpenMultiProperties wxOpenMultiProperties) {
75+
WxOpenService wxOpenService = new WxOpenServiceImpl();
76+
wxOpenService.setWxOpenConfigStorage(configStorage);
77+
return wxOpenService;
78+
}
79+
80+
private void configApp(WxOpenInMemoryConfigStorage config, WxOpenSingleProperties appProperties) {
81+
String appId = appProperties.getAppId();
82+
String secret = appProperties.getSecret();
83+
String token = appProperties.getToken();
84+
String aesKey = appProperties.getAesKey();
85+
String apiHostUrl = appProperties.getApiHostUrl();
86+
String accessTokenUrl = appProperties.getAccessTokenUrl();
87+
88+
config.setComponentAppId(appId);
89+
config.setComponentAppSecret(secret);
90+
if (StringUtils.isNotBlank(token)) {
91+
config.setComponentToken(token);
92+
}
93+
if (StringUtils.isNotBlank(aesKey)) {
94+
config.setComponentAesKey(aesKey);
95+
}
96+
// 设置URL配置
97+
config.setApiHostUrl(StringUtils.trimToNull(apiHostUrl));
98+
config.setAccessTokenUrl(StringUtils.trimToNull(accessTokenUrl));
99+
}
100+
101+
private void configHttp(WxOpenInMemoryConfigStorage config, WxOpenMultiProperties.ConfigStorage storage) {
102+
String httpProxyHost = storage.getHttpProxyHost();
103+
Integer httpProxyPort = storage.getHttpProxyPort();
104+
String httpProxyUsername = storage.getHttpProxyUsername();
105+
String httpProxyPassword = storage.getHttpProxyPassword();
106+
if (StringUtils.isNotBlank(httpProxyHost)) {
107+
config.setHttpProxyHost(httpProxyHost);
108+
if (httpProxyPort != null) {
109+
config.setHttpProxyPort(httpProxyPort);
110+
}
111+
if (StringUtils.isNotBlank(httpProxyUsername)) {
112+
config.setHttpProxyUsername(httpProxyUsername);
113+
}
114+
if (StringUtils.isNotBlank(httpProxyPassword)) {
115+
config.setHttpProxyPassword(httpProxyPassword);
116+
}
117+
}
118+
119+
// 设置重试配置
120+
int maxRetryTimes = storage.getMaxRetryTimes();
121+
if (maxRetryTimes < 0) {
122+
maxRetryTimes = 0;
123+
}
124+
int retrySleepMillis = storage.getRetrySleepMillis();
125+
if (retrySleepMillis < 0) {
126+
retrySleepMillis = 1000;
127+
}
128+
config.setRetrySleepMillis(retrySleepMillis);
129+
config.setMaxRetryTimes(maxRetryTimes);
130+
131+
// 设置自定义的HttpClient超时配置
132+
ApacheHttpClientBuilder clientBuilder = config.getApacheHttpClientBuilder();
133+
if (clientBuilder == null) {
134+
clientBuilder = DefaultApacheHttpClientBuilder.get();
135+
}
136+
if (clientBuilder instanceof DefaultApacheHttpClientBuilder) {
137+
DefaultApacheHttpClientBuilder defaultBuilder = (DefaultApacheHttpClientBuilder) clientBuilder;
138+
defaultBuilder.setConnectionTimeout(storage.getConnectionTimeout());
139+
defaultBuilder.setSoTimeout(storage.getSoTimeout());
140+
defaultBuilder.setConnectionRequestTimeout(storage.getConnectionRequestTimeout());
141+
config.setApacheHttpClientBuilder(defaultBuilder);
142+
}
143+
}
144+
}

0 commit comments

Comments
 (0)