Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 15 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,21 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.3.0</version> <!-- 建议使用最新版本 -->
<executions>
<execution>
<id>attach-sources</id>
<!-- 绑定到 verify 阶段,在 install 之前执行 -->
<phase>verify</phase>
<goals>
<goal>jar-no-fork</goal> <!-- 使用 jar-no-fork 效率更高 -->
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

Expand Down
1 change: 1 addition & 0 deletions spring-boot-starters/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
<module>wx-java-open-spring-boot-starter</module>
<module>wx-java-qidian-spring-boot-starter</module>
<module>wx-java-cp-multi-spring-boot-starter</module>
<module>wx-java-cp-tp-multi-spring-boot-starter</module>
<module>wx-java-cp-spring-boot-starter</module>
<module>wx-java-channel-spring-boot-starter</module>
<module>wx-java-channel-multi-spring-boot-starter</module>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# wx-java-cp-multi-spring-boot-starter

企业微信多账号配置

- 实现多 WxCpService 初始化。
- 未实现 WxCpTpService 初始化,需要的小伙伴可以参考多 WxCpService 配置的实现。
- 未实现 WxCpCgService 初始化,需要的小伙伴可以参考多 WxCpService 配置的实现。

## 快速开始

1. 引入依赖
```xml
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java-cp-multi-spring-boot-starter</artifactId>
<version>${version}</version>
</dependency>
```
2. 添加配置(application.properties)
```properties
# 应用 1 配置
wx.cp.corps.tenantId1.corp-id = @corp-id
wx.cp.corps.tenantId1.corp-secret = @corp-secret
## 选填
wx.cp.corps.tenantId1.agent-id = @agent-id
wx.cp.corps.tenantId1.token = @token
wx.cp.corps.tenantId1.aes-key = @aes-key
wx.cp.corps.tenantId1.msg-audit-priKey = @msg-audit-priKey
wx.cp.corps.tenantId1.msg-audit-lib-path = @msg-audit-lib-path

# 应用 2 配置
wx.cp.corps.tenantId2.corp-id = @corp-id
wx.cp.corps.tenantId2.corp-secret = @corp-secret
## 选填
wx.cp.corps.tenantId2.agent-id = @agent-id
wx.cp.corps.tenantId2.token = @token
wx.cp.corps.tenantId2.aes-key = @aes-key
wx.cp.corps.tenantId2.msg-audit-priKey = @msg-audit-priKey
wx.cp.corps.tenantId2.msg-audit-lib-path = @msg-audit-lib-path

# 公共配置
## ConfigStorage 配置(选填)
wx.cp.config-storage.type=memory # 配置类型: memory(默认), jedis, redisson, redistemplate
## http 客户端配置(选填)
## # http客户端类型: http_client(默认), ok_http, jodd_http
wx.cp.config-storage.http-client-type=http_client
wx.cp.config-storage.http-proxy-host=
wx.cp.config-storage.http-proxy-port=
wx.cp.config-storage.http-proxy-username=
wx.cp.config-storage.http-proxy-password=
## 最大重试次数,默认:5 次,如果小于 0,则为 0
wx.cp.config-storage.max-retry-times=5
## 重试时间间隔步进,默认:1000 毫秒,如果小于 0,则为 1000
wx.cp.config-storage.retry-sleep-millis=1000
```
3. 支持自动注入的类型: `WxCpMultiServices`

4. 使用样例

```java
import com.binarywang.spring.starter.wxjava.cp.service.WxCpTpMultiServices;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.api.WxCpUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class DemoService {
@Autowired
private WxCpTpMultiServices wxCpTpMultiServices;

public void test() {
// 应用 1 的 WxCpService
WxCpService wxCpService1 = wxCpMultiServices.getWxCpService("tenantId1");
WxCpUserService userService1 = wxCpService1.getUserService();
userService1.getUserId("xxx");
// todo ...

// 应用 2 的 WxCpService
WxCpService wxCpService2 = wxCpMultiServices.getWxCpService("tenantId2");
WxCpUserService userService2 = wxCpService2.getUserService();
userService2.getUserId("xxx");
// todo ...

// 应用 3 的 WxCpService
WxCpService wxCpService3 = wxCpMultiServices.getWxCpService("tenantId3");
// 判断是否为空
if (wxCpService3 == null) {
// todo wxCpService3 为空,请先配置 tenantId3 企业微信应用参数
return;
}
WxCpUserService userService3 = wxCpService3.getUserService();
userService3.getUserId("xxx");
// todo ...
}
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?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.7.7.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>wx-java-cp-tp-multi-spring-boot-starter</artifactId>
<name>WxJava - Spring Boot Starter for WxCp::支持多账号配置</name>
<description>微信企业号开发的 Spring Boot Starter::支持多账号配置</description>

<dependencies>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-cp</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>
</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>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.binarywang.spring.starter.wxjava.cp.autoconfigure;

import com.binarywang.spring.starter.wxjava.cp.configuration.WxCpTpMultiServicesAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

/**
* 企业微信自动注册
*
* @author yl
* created on 2023/10/16
*/
@Configuration
@Import(WxCpTpMultiServicesAutoConfiguration.class)
public class WxCpTpMultiAutoConfiguration {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.binarywang.spring.starter.wxjava.cp.configuration;

import com.binarywang.spring.starter.wxjava.cp.configuration.services.WxCpTpInJedisTpConfiguration;
import com.binarywang.spring.starter.wxjava.cp.configuration.services.WxCpTpInMemoryTpConfiguration;
import com.binarywang.spring.starter.wxjava.cp.configuration.services.WxCpTpInRedisTemplateTpConfiguration;
import com.binarywang.spring.starter.wxjava.cp.configuration.services.WxCpTpInRedissonTpConfiguration;
import com.binarywang.spring.starter.wxjava.cp.properties.WxCpTpMultiProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

/**
* 企业微信平台相关服务自动注册
*
* @author yl
* created on 2023/10/16
*/
@Configuration
@EnableConfigurationProperties(WxCpTpMultiProperties.class)
@Import({
WxCpTpInJedisTpConfiguration.class,
WxCpTpInMemoryTpConfiguration.class,
WxCpTpInRedissonTpConfiguration.class,
WxCpTpInRedisTemplateTpConfiguration.class
})
public class WxCpTpMultiServicesAutoConfiguration {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package com.binarywang.spring.starter.wxjava.cp.configuration.services;

import com.binarywang.spring.starter.wxjava.cp.properties.WxCpTpMultiProperties;
import com.binarywang.spring.starter.wxjava.cp.properties.WxCpTpSingleProperties;
import com.binarywang.spring.starter.wxjava.cp.service.WxCpTpMultiServices;
import com.binarywang.spring.starter.wxjava.cp.service.WxCpTpMultiServicesImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.cp.config.WxCpTpConfigStorage;
import me.chanjar.weixin.cp.config.impl.WxCpTpDefaultConfigImpl;
import me.chanjar.weixin.cp.tp.service.WxCpTpService;
import me.chanjar.weixin.cp.tp.service.impl.WxCpTpServiceApacheHttpClientImpl;
import me.chanjar.weixin.cp.tp.service.impl.WxCpTpServiceImpl;
import me.chanjar.weixin.cp.tp.service.impl.WxCpTpServiceJoddHttpImpl;
import me.chanjar.weixin.cp.tp.service.impl.WxCpTpServiceOkHttpImpl;
import org.apache.commons.lang3.StringUtils;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
* WxCpConfigStorage 抽象配置类
*
* @author yl
* created on 2023/10/16
*/
@RequiredArgsConstructor
@Slf4j
public abstract class AbstractWxCpTpConfiguration {

/**
*
* @param wxCpTpMultiProperties 应用列表配置
* @param services 用于支持,应用启动之后,可以调用这个接口添加新服务对象。主要是配置是从数据库中读取的
* @return
*/
public WxCpTpMultiServices wxCpMultiServices(WxCpTpMultiProperties wxCpTpMultiProperties,WxCpTpMultiServices services) {
Map<String, WxCpTpSingleProperties> corps = wxCpTpMultiProperties.getCorps();
if (corps == null || corps.isEmpty()) {
log.warn("企业微信应用参数未配置,通过 WxCpMultiServices#getWxCpTpService(\"tenantId\")获取实例将返回空");
return new WxCpTpMultiServicesImpl();
}

if (services == null) {
services = new WxCpTpMultiServicesImpl();
}

Set<Map.Entry<String, WxCpTpSingleProperties>> entries = corps.entrySet();
for (Map.Entry<String, WxCpTpSingleProperties> entry : entries) {
String tenantId = entry.getKey();
WxCpTpSingleProperties wxCpTpSingleProperties = entry.getValue();
WxCpTpDefaultConfigImpl storage = this.wxCpTpConfigStorage(wxCpTpMultiProperties);
this.configCorp(storage, wxCpTpSingleProperties);
this.configHttp(storage, wxCpTpMultiProperties.getConfigStorage());
WxCpTpService wxCpTpService = this.wxCpTpService(storage, wxCpTpMultiProperties.getConfigStorage());
if (services.getWxCpTpService(tenantId) == null) {
// 不存在的才会添加到服务列表中
services.addWxCpTpService(tenantId, wxCpTpService);
}
}
return services;
}

/**
* 配置 WxCpDefaultConfigImpl
*
* @param wxCpTpMultiProperties 参数
* @return WxCpDefaultConfigImpl
*/
protected abstract WxCpTpDefaultConfigImpl wxCpTpConfigStorage(WxCpTpMultiProperties wxCpTpMultiProperties);

private WxCpTpService wxCpTpService(WxCpTpConfigStorage wxCpTpConfigStorage, WxCpTpMultiProperties.ConfigStorage storage) {
WxCpTpMultiProperties.HttpClientType httpClientType = storage.getHttpClientType();
WxCpTpService cpTpService;
switch (httpClientType) {
case OK_HTTP:
cpTpService = new WxCpTpServiceOkHttpImpl();
break;
case JODD_HTTP:
cpTpService = new WxCpTpServiceJoddHttpImpl();
break;
case HTTP_CLIENT:
cpTpService = new WxCpTpServiceApacheHttpClientImpl();
break;
default:
cpTpService = new WxCpTpServiceImpl();
break;
}
cpTpService.setWxCpTpConfigStorage(wxCpTpConfigStorage);
int maxRetryTimes = storage.getMaxRetryTimes();
if (maxRetryTimes < 0) {
maxRetryTimes = 0;
}
int retrySleepMillis = storage.getRetrySleepMillis();
if (retrySleepMillis < 0) {
retrySleepMillis = 1000;
}
cpTpService.setRetrySleepMillis(retrySleepMillis);
cpTpService.setMaxRetryTimes(maxRetryTimes);
return cpTpService;
}

private void configCorp(WxCpTpDefaultConfigImpl config, WxCpTpSingleProperties wxCpTpSingleProperties) {
String corpId = wxCpTpSingleProperties.getCorpId();
String providerSecret = wxCpTpSingleProperties.getProviderSecret();
String suiteId = wxCpTpSingleProperties.getSuiteId();
String token = wxCpTpSingleProperties.getToken();
String suiteSecret = wxCpTpSingleProperties.getSuiteSecret();
// 企业微信,私钥,会话存档路径
config.setCorpId(corpId);
config.setProviderSecret(providerSecret);
config.setEncodingAESKey(wxCpTpSingleProperties.getEncodingAESKey());
config.setSuiteId(suiteId);
config.setToken(token);
config.setSuiteSecret(suiteSecret);
}

private void configHttp(WxCpTpDefaultConfigImpl config, WxCpTpMultiProperties.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);
}
}
}
}
Loading