-
-
Notifications
You must be signed in to change notification settings - Fork 8.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
🆕 #3372【视频号】增加支持多视频号账号的spring-boot-starter组件
- Loading branch information
1 parent
b0b487f
commit 0ac68ee
Showing
19 changed files
with
897 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
123 changes: 123 additions & 0 deletions
123
spring-boot-starters/wx-java-channel-multi-spring-boot-starter/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
# wx-java-channel-multi-spring-boot-starter | ||
|
||
## 快速开始 | ||
|
||
1. 引入依赖 | ||
```xml | ||
<dependencies> | ||
<dependency> | ||
<groupId>com.github.binarywang</groupId> | ||
<artifactId>wx-java-channel-multi-spring-boot-starter</artifactId> | ||
<version>${version}</version> | ||
</dependency> | ||
|
||
<!-- 配置存储方式为jedis 则引入jedis --> | ||
<dependency> | ||
<groupId>redis.clients</groupId> | ||
<artifactId>jedis</artifactId> | ||
<version>${jedis.version}</version> | ||
</dependency> | ||
|
||
<!-- 配置存储方式为redisson 则引入redisson --> | ||
<dependency> | ||
<groupId>org.redisson</groupId> | ||
<artifactId>redisson</artifactId> | ||
<version>${redisson.version}</version> | ||
</dependency> | ||
|
||
<!-- 配置存储方式为redis_template 则引入spring data redis --> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter-data-redis</artifactId> | ||
</dependency> | ||
</dependencies> | ||
``` | ||
2. 添加配置(application.properties) | ||
```properties | ||
# 视频号配置 | ||
## 应用 1 配置(必填) | ||
wx.channel.apps.tenantId1.app-id=@appId | ||
wx.channel.apps.tenantId1.secret=@secret | ||
## 选填 | ||
wx.channel.apps.tenantId1.use-stable-access-token=false | ||
wx.channel.apps.tenantId1.token= | ||
wx.channel.apps.tenantId1.aes-key= | ||
## 应用 2 配置(必填) | ||
wx.channel.apps.tenantId2.app-id=@appId | ||
wx.channel.apps.tenantId2.secret=@secret | ||
## 选填 | ||
wx.channel.apps.tenantId2.use-stable-access-token=false | ||
wx.channel.apps.tenantId2.token= | ||
wx.channel.apps.tenantId2.aes-key= | ||
|
||
# ConfigStorage 配置(选填) | ||
## 配置类型: memory(默认), jedis, redisson, redis_template | ||
wx.channel.config-storage.type=memory | ||
## 相关redis前缀配置: wx:channel:multi(默认) | ||
wx.channel.config-storage.key-prefix=wx:channel:multi | ||
wx.channel.config-storage.redis.host=127.0.0.1 | ||
wx.channel.config-storage.redis.port=6379 | ||
wx.channel.config-storage.redis.password=123456 | ||
|
||
# redis_template 方式使用spring data redis配置 | ||
spring.data.redis.database=0 | ||
spring.data.redis.host=127.0.0.1 | ||
spring.data.redis.password=123456 | ||
spring.data.redis.port=6379 | ||
|
||
# http 客户端配置(选填) | ||
## # http客户端类型: http_client(默认) | ||
wx.channel.config-storage.http-client-type=http_client | ||
wx.channel.config-storage.http-proxy-host= | ||
wx.channel.config-storage.http-proxy-port= | ||
wx.channel.config-storage.http-proxy-username= | ||
wx.channel.config-storage.http-proxy-password= | ||
## 最大重试次数,默认:5 次,如果小于 0,则为 0 | ||
wx.channel.config-storage.max-retry-times=5 | ||
## 重试时间间隔步进,默认:1000 毫秒,如果小于 0,则为 1000 | ||
wx.channel.config-storage.retry-sleep-millis=1000 | ||
``` | ||
3. 自动注入的类型:`WxChannelMultiServices` | ||
|
||
4. 使用样例 | ||
|
||
```java | ||
import com.binarywang.spring.starter.wxjava.channel.service.WxChannelMultiServices; | ||
import me.chanjar.weixin.channel.api.WxChannelService; | ||
import me.chanjar.weixin.channel.api.WxFinderLiveService; | ||
import me.chanjar.weixin.channel.bean.lead.component.response.FinderAttrResponse; | ||
import me.chanjar.weixin.common.error.WxErrorException; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.stereotype.Service; | ||
|
||
@Service | ||
public class DemoService { | ||
@Autowired | ||
private WxChannelMultiServices wxChannelMultiServices; | ||
|
||
public void test() throws WxErrorException { | ||
// 应用 1 的 WxChannelService | ||
WxChannelService wxChannelService1 = wxChannelMultiServices.getWxChannelService("tenantId1"); | ||
WxFinderLiveService finderLiveService = wxChannelService1.getFinderLiveService(); | ||
FinderAttrResponse response1 = finderLiveService.getFinderAttrByAppid(); | ||
// todo ... | ||
|
||
// 应用 2 的 WxChannelService | ||
WxChannelService wxChannelService2 = wxChannelMultiServices.getWxChannelService("tenantId2"); | ||
WxFinderLiveService finderLiveService2 = wxChannelService2.getFinderLiveService(); | ||
FinderAttrResponse response2 = finderLiveService2.getFinderAttrByAppid(); | ||
// todo ... | ||
|
||
// 应用 3 的 WxChannelService | ||
WxChannelService wxChannelService3 = wxChannelMultiServices.getWxChannelService("tenantId3"); | ||
// 判断是否为空 | ||
if (wxChannelService3 == null) { | ||
// todo wxChannelService3 为空,请先配置 tenantId3 微信视频号应用参数 | ||
return; | ||
} | ||
WxFinderLiveService finderLiveService3 = wxChannelService3.getFinderLiveService(); | ||
FinderAttrResponse response3 = finderLiveService3.getFinderAttrByAppid(); | ||
// todo ... | ||
} | ||
} | ||
``` |
71 changes: 71 additions & 0 deletions
71
spring-boot-starters/wx-java-channel-multi-spring-boot-starter/pom.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<parent> | ||
<artifactId>wx-java-spring-boot-starters</artifactId> | ||
<groupId>com.github.binarywang</groupId> | ||
<version>4.6.5.B</version> | ||
</parent> | ||
<modelVersion>4.0.0</modelVersion> | ||
|
||
<artifactId>wx-java-channel-multi-spring-boot-starter</artifactId> | ||
<name>WxJava - Spring Boot Starter for Channel::支持多账号配置</name> | ||
<description>微信视频号开发的 Spring Boot Starter::支持多账号配置</description> | ||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>com.github.binarywang</groupId> | ||
<artifactId>weixin-java-channel</artifactId> | ||
<version>${project.version}</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>redis.clients</groupId> | ||
<artifactId>jedis</artifactId> | ||
<scope>provided</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.redisson</groupId> | ||
<artifactId>redisson</artifactId> | ||
<scope>provided</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.springframework.data</groupId> | ||
<artifactId>spring-data-redis</artifactId> | ||
<scope>provided</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.jodd</groupId> | ||
<artifactId>jodd-http</artifactId> | ||
<scope>provided</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>com.squareup.okhttp3</groupId> | ||
<artifactId>okhttp</artifactId> | ||
<scope>provided</scope> | ||
</dependency> | ||
</dependencies> | ||
|
||
<build> | ||
<plugins> | ||
<plugin> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-maven-plugin</artifactId> | ||
<version>${spring.boot.version}</version> | ||
</plugin> | ||
<plugin> | ||
<groupId>org.apache.maven.plugins</groupId> | ||
<artifactId>maven-source-plugin</artifactId> | ||
<version>2.2.1</version> | ||
<executions> | ||
<execution> | ||
<id>attach-sources</id> | ||
<goals> | ||
<goal>jar-no-fork</goal> | ||
</goals> | ||
</execution> | ||
</executions> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
</project> |
15 changes: 15 additions & 0 deletions
15
...narywang/spring/starter/wxjava/channel/autoconfigure/WxChannelMultiAutoConfiguration.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package com.binarywang.spring.starter.wxjava.channel.autoconfigure; | ||
|
||
import com.binarywang.spring.starter.wxjava.channel.configuration.WxChannelMultiServiceConfiguration; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.context.annotation.Import; | ||
|
||
/** | ||
* 微信视频号自动注册 | ||
* | ||
* @author <a href="https://github.com/Winnie-by996">Winnie</a> | ||
* @date 2024/9/13 | ||
*/ | ||
@Configuration | ||
@Import(WxChannelMultiServiceConfiguration.class) | ||
public class WxChannelMultiAutoConfiguration {} |
21 changes: 21 additions & 0 deletions
21
...ywang/spring/starter/wxjava/channel/configuration/WxChannelMultiServiceConfiguration.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package com.binarywang.spring.starter.wxjava.channel.configuration; | ||
|
||
import com.binarywang.spring.starter.wxjava.channel.configuration.services.WxChannelInJedisConfiguration; | ||
import com.binarywang.spring.starter.wxjava.channel.configuration.services.WxChannelInMemoryConfiguration; | ||
import com.binarywang.spring.starter.wxjava.channel.configuration.services.WxChannelInRedisTemplateConfiguration; | ||
import com.binarywang.spring.starter.wxjava.channel.configuration.services.WxChannelInRedissonConfiguration; | ||
import com.binarywang.spring.starter.wxjava.channel.properties.WxChannelMultiProperties; | ||
import org.springframework.boot.context.properties.EnableConfigurationProperties; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.context.annotation.Import; | ||
|
||
/** | ||
* 微信视频号相关服务自动注册 | ||
* | ||
* @author <a href="https://github.com/Winnie-by996">Winnie</a> | ||
* @date 2024/9/13 | ||
*/ | ||
@Configuration | ||
@EnableConfigurationProperties(WxChannelMultiProperties.class) | ||
@Import({WxChannelInJedisConfiguration.class, WxChannelInMemoryConfiguration.class, WxChannelInRedissonConfiguration.class, WxChannelInRedisTemplateConfiguration.class}) | ||
public class WxChannelMultiServiceConfiguration {} |
140 changes: 140 additions & 0 deletions
140
.../spring/starter/wxjava/channel/configuration/services/AbstractWxChannelConfiguration.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
package com.binarywang.spring.starter.wxjava.channel.configuration.services; | ||
|
||
import com.binarywang.spring.starter.wxjava.channel.enums.HttpClientType; | ||
import com.binarywang.spring.starter.wxjava.channel.properties.WxChannelMultiProperties; | ||
import com.binarywang.spring.starter.wxjava.channel.properties.WxChannelSingleProperties; | ||
import com.binarywang.spring.starter.wxjava.channel.service.WxChannelMultiServices; | ||
import com.binarywang.spring.starter.wxjava.channel.service.WxChannelMultiServicesImpl; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import me.chanjar.weixin.channel.api.WxChannelService; | ||
import me.chanjar.weixin.channel.api.impl.WxChannelServiceHttpClientImpl; | ||
import me.chanjar.weixin.channel.api.impl.WxChannelServiceImpl; | ||
import me.chanjar.weixin.channel.config.WxChannelConfig; | ||
import me.chanjar.weixin.channel.config.impl.WxChannelDefaultConfigImpl; | ||
import org.apache.commons.lang3.StringUtils; | ||
|
||
import java.util.Collection; | ||
import java.util.Map; | ||
import java.util.Set; | ||
import java.util.stream.Collectors; | ||
|
||
/** | ||
* WxChannelConfigStorage 抽象配置类 | ||
* | ||
* @author <a href="https://github.com/Winnie-by996">Winnie</a> | ||
* @date 2024/9/13 | ||
*/ | ||
@RequiredArgsConstructor | ||
@Slf4j | ||
public abstract class AbstractWxChannelConfiguration { | ||
protected WxChannelMultiServices wxChannelMultiServices(WxChannelMultiProperties wxChannelMultiProperties) { | ||
Map<String, WxChannelSingleProperties> appsMap = wxChannelMultiProperties.getApps(); | ||
if (appsMap == null || appsMap.isEmpty()) { | ||
log.warn("微信视频号应用参数未配置,通过 WxChannelMultiServices#getWxChannelService(\"tenantId\")获取实例将返回空"); | ||
return new WxChannelMultiServicesImpl(); | ||
} | ||
/** | ||
* 校验 appId 是否唯一,避免使用 redis 缓存 token、ticket 时错乱。 | ||
* | ||
* 查看 {@link me.chanjar.weixin.channel.config.impl.WxChannelRedisConfigImpl#setAppid(String)} | ||
*/ | ||
Collection<WxChannelSingleProperties> apps = appsMap.values(); | ||
if (apps.size() > 1) { | ||
// 校验 appId 是否唯一 | ||
boolean multi = apps.stream() | ||
// 没有 appId,如果不判断是否为空,这里会报 NPE 异常 | ||
.collect(Collectors.groupingBy(c -> c.getAppId() == null ? 0 : c.getAppId(), Collectors.counting())) | ||
.entrySet().stream().anyMatch(e -> e.getValue() > 1); | ||
if (multi) { | ||
throw new RuntimeException("请确保微信视频号配置 appId 的唯一性"); | ||
} | ||
} | ||
WxChannelMultiServicesImpl services = new WxChannelMultiServicesImpl(); | ||
|
||
Set<Map.Entry<String, WxChannelSingleProperties>> entries = appsMap.entrySet(); | ||
for (Map.Entry<String, WxChannelSingleProperties> entry : entries) { | ||
String tenantId = entry.getKey(); | ||
WxChannelSingleProperties wxChannelSingleProperties = entry.getValue(); | ||
WxChannelDefaultConfigImpl storage = this.wxChannelConfigStorage(wxChannelMultiProperties); | ||
this.configApp(storage, wxChannelSingleProperties); | ||
this.configHttp(storage, wxChannelMultiProperties.getConfigStorage()); | ||
WxChannelService wxChannelService = this.wxChannelService(storage, wxChannelMultiProperties, wxChannelSingleProperties.isUseStableAccessToken()); | ||
services.addWxChannelService(tenantId, wxChannelService); | ||
} | ||
return services; | ||
} | ||
|
||
/** | ||
* 配置 WxChannelDefaultConfigImpl | ||
* | ||
* @param wxChannelMultiProperties 参数 | ||
* @return WxChannelDefaultConfigImpl | ||
*/ | ||
protected abstract WxChannelDefaultConfigImpl wxChannelConfigStorage(WxChannelMultiProperties wxChannelMultiProperties); | ||
|
||
public WxChannelService wxChannelService(WxChannelConfig wxChannelConfig, WxChannelMultiProperties wxChannelMultiProperties, boolean useStableAccessToken) { | ||
WxChannelMultiProperties.ConfigStorage storage = wxChannelMultiProperties.getConfigStorage(); | ||
HttpClientType httpClientType = storage.getHttpClientType(); | ||
WxChannelService wxChannelService; | ||
switch (httpClientType) { | ||
// case OK_HTTP: | ||
// wxChannelService = new WxChannelServiceOkHttpImpl(false, false); | ||
// break; | ||
case HTTP_CLIENT: | ||
wxChannelService = new WxChannelServiceHttpClientImpl(useStableAccessToken, false); | ||
break; | ||
default: | ||
wxChannelService = new WxChannelServiceImpl(); | ||
break; | ||
} | ||
|
||
wxChannelService.setConfig(wxChannelConfig); | ||
int maxRetryTimes = storage.getMaxRetryTimes(); | ||
if (maxRetryTimes < 0) { | ||
maxRetryTimes = 0; | ||
} | ||
int retrySleepMillis = storage.getRetrySleepMillis(); | ||
if (retrySleepMillis < 0) { | ||
retrySleepMillis = 1000; | ||
} | ||
wxChannelService.setRetrySleepMillis(retrySleepMillis); | ||
wxChannelService.setMaxRetryTimes(maxRetryTimes); | ||
return wxChannelService; | ||
} | ||
|
||
private void configApp(WxChannelDefaultConfigImpl config, WxChannelSingleProperties wxChannelSingleProperties) { | ||
String appId = wxChannelSingleProperties.getAppId(); | ||
String appSecret = wxChannelSingleProperties.getSecret(); | ||
String token = wxChannelSingleProperties.getToken(); | ||
String aesKey = wxChannelSingleProperties.getAesKey(); | ||
|
||
config.setAppid(appId); | ||
config.setSecret(appSecret); | ||
if (StringUtils.isNotBlank(token)) { | ||
config.setToken(token); | ||
} | ||
if (StringUtils.isNotBlank(aesKey)) { | ||
config.setAesKey(aesKey); | ||
} | ||
} | ||
|
||
private void configHttp(WxChannelDefaultConfigImpl config, WxChannelMultiProperties.ConfigStorage storage) { | ||
String httpProxyHost = storage.getHttpProxyHost(); | ||
Integer httpProxyPort = storage.getHttpProxyPort(); | ||
String httpProxyUsername = storage.getHttpProxyUsername(); | ||
String httpProxyPassword = storage.getHttpProxyPassword(); | ||
if (StringUtils.isNotBlank(httpProxyHost)) { | ||
config.setHttpProxyHost(httpProxyHost); | ||
if (httpProxyPort != null) { | ||
config.setHttpProxyPort(httpProxyPort); | ||
} | ||
if (StringUtils.isNotBlank(httpProxyUsername)) { | ||
config.setHttpProxyUsername(httpProxyUsername); | ||
} | ||
if (StringUtils.isNotBlank(httpProxyPassword)) { | ||
config.setHttpProxyPassword(httpProxyPassword); | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.