Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
ea5888d
:new: 完成 wx-java-channel-solon-plugin 适配
noear Sep 2, 2024
26c312b
:new: 完成 wx-java-cp-multi-solon-plugin 适配
noear Sep 2, 2024
1890e0a
:new: 完成 wx-java-cp-solon-plugin 适配
noear Sep 2, 2024
17be5c8
:new: 完成 wx-java-miniapp-solon-plugin 适配
noear Sep 2, 2024
027918c
:new: 完成 wx-java-mp-multi-solon-plugin 适配
noear Sep 2, 2024
0ea9c63
:new: 完成 wx-java-mp-solon-plugin 适配
noear Sep 2, 2024
e4c16dc
:new: 完成 wx-java-open-solon-plugin 适配
noear Sep 2, 2024
5403878
:new: 完成 wx-java-pay-solon-plugin 适配
noear Sep 2, 2024
1cf2da1
:new: 完成 wx-java-qidian-solon-plugin 适配
noear Sep 2, 2024
bf52cc4
:new: 完成 solon-plugins 适配
noear Sep 2, 2024
2840c70
:new: 完成 solon-plugins 适配(优化注释细节)
noear Sep 2, 2024
1af217a
Merge branch 'Wechat-Group:develop' into develop
noear Sep 4, 2024
b5faaf4
:new: 添加 solon-plugins 单测,可触发必要错误用于人工核对
noear Sep 4, 2024
0bc55a3
:art: 修正 wx-java-qidian-solon-plugin 和 wx-java-qidian-spring-boot-sta…
noear Sep 7, 2024
484bb9e
Merge branch 'Wechat-Group:develop' into develop
noear Sep 7, 2024
81ab551
Merge branch 'Wechat-Group:develop' into develop
noear Sep 9, 2024
4372ba5
Merge branch 'Wechat-Group:develop' into develop
noear Oct 9, 2024
59f086f
:new: 添加 solon-plugins / wx-java-channel-multi-solon-plugin 模块
noear Oct 9, 2024
a154531
:new: 添加 solon-plugins / wx-java-miniapp-multi-solon-plugin 模块
noear Oct 9, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion solon-plugins/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@
<description>WxJava 各个模块的 Solon Plugin</description>

<properties>
<solon.version>2.9.2</solon.version>
<solon.version>3.0.1</solon.version>
</properties>

<modules>
<module>wx-java-miniapp-multi-solon-plugin</module>
<module>wx-java-miniapp-solon-plugin</module>
<module>wx-java-mp-multi-solon-plugin</module>
<module>wx-java-mp-solon-plugin</module>
Expand All @@ -27,6 +28,7 @@
<module>wx-java-cp-multi-solon-plugin</module>
<module>wx-java-cp-solon-plugin</module>
<module>wx-java-channel-solon-plugin</module>
<module>wx-java-channel-multi-solon-plugin</module>
</modules>

<dependencies>
Expand Down
111 changes: 111 additions & 0 deletions solon-plugins/wx-java-channel-multi-solon-plugin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# wx-java-channel-multi-solon-plugin

## 快速开始

1. 引入依赖
```xml
<dependencies>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java-channel-multi-solon-plugin</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>
</dependencies>
```
2. 添加配置(app.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

# 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.solon.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.noear.solon.annotation.Component;
import org.noear.solon.annotation.Inject;

@Component
public class DemoService {
@Inject
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 ...
}
}
```
43 changes: 43 additions & 0 deletions solon-plugins/wx-java-channel-multi-solon-plugin/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?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-solon-plugins</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.6.5.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>wx-java-channel-multi-solon-plugin</artifactId>
<name>WxJava - Solon Plugin for Channel::支持多账号配置</name>
<description>微信视频号开发的 Solon Plugin::支持多账号配置</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.jodd</groupId>
<artifactId>jodd-http</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package com.binarywang.solon.wxjava.channel.configuration.services;

import com.binarywang.solon.wxjava.channel.enums.HttpClientType;
import com.binarywang.solon.wxjava.channel.properties.WxChannelMultiProperties;
import com.binarywang.solon.wxjava.channel.properties.WxChannelSingleProperties;
import com.binarywang.solon.wxjava.channel.service.WxChannelMultiServices;
import com.binarywang.solon.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> 2024/9/13
* @author noear
*/
@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);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.binarywang.solon.wxjava.channel.configuration.services;

import com.binarywang.solon.wxjava.channel.properties.WxChannelMultiProperties;
import com.binarywang.solon.wxjava.channel.properties.WxChannelMultiRedisProperties;
import com.binarywang.solon.wxjava.channel.service.WxChannelMultiServices;
import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.channel.config.impl.WxChannelDefaultConfigImpl;
import me.chanjar.weixin.channel.config.impl.WxChannelRedisConfigImpl;
import me.chanjar.weixin.common.redis.JedisWxRedisOps;
import org.apache.commons.lang3.StringUtils;
import org.noear.solon.annotation.Bean;
import org.noear.solon.annotation.Condition;
import org.noear.solon.annotation.Configuration;
import org.noear.solon.core.AppContext;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

/**
* 自动装配基于 jedis 策略配置
*
* @author <a href="https://github.com/Winnie-by996">Winnie</a> 2024/9/13
* @author noear
*/
@Configuration
@Condition(
onProperty = "${"+WxChannelMultiProperties.PREFIX + ".configStorage.type} = jedis",
onClass = JedisPool.class
)
@RequiredArgsConstructor
public class WxChannelInJedisConfiguration extends AbstractWxChannelConfiguration {
private final WxChannelMultiProperties wxChannelMultiProperties;
private final AppContext applicationContext;

@Bean
public WxChannelMultiServices wxChannelMultiServices() {
return this.wxChannelMultiServices(wxChannelMultiProperties);
}

@Override
protected WxChannelDefaultConfigImpl wxChannelConfigStorage(WxChannelMultiProperties wxChannelMultiProperties) {
return this.configRedis(wxChannelMultiProperties);
}

private WxChannelDefaultConfigImpl configRedis(WxChannelMultiProperties wxChannelMultiProperties) {
WxChannelMultiRedisProperties wxChannelMultiRedisProperties = wxChannelMultiProperties.getConfigStorage().getRedis();
JedisPool jedisPool;
if (wxChannelMultiRedisProperties != null && StringUtils.isNotEmpty(wxChannelMultiRedisProperties.getHost())) {
jedisPool = getJedisPool(wxChannelMultiProperties);
} else {
jedisPool = applicationContext.getBean(JedisPool.class);
}
return new WxChannelRedisConfigImpl(new JedisWxRedisOps(jedisPool), wxChannelMultiProperties.getConfigStorage().getKeyPrefix());
}

private JedisPool getJedisPool(WxChannelMultiProperties wxChannelMultiProperties) {
WxChannelMultiProperties.ConfigStorage storage = wxChannelMultiProperties.getConfigStorage();
WxChannelMultiRedisProperties redis = storage.getRedis();

JedisPoolConfig config = new JedisPoolConfig();
if (redis.getMaxActive() != null) {
config.setMaxTotal(redis.getMaxActive());
}
if (redis.getMaxIdle() != null) {
config.setMaxIdle(redis.getMaxIdle());
}
if (redis.getMaxWaitMillis() != null) {
config.setMaxWaitMillis(redis.getMaxWaitMillis());
}
if (redis.getMinIdle() != null) {
config.setMinIdle(redis.getMinIdle());
}
config.setTestOnBorrow(true);
config.setTestWhileIdle(true);

return new JedisPool(config, redis.getHost(), redis.getPort(), redis.getTimeout(), redis.getPassword(), redis.getDatabase());
}
}
Loading