diff --git a/pom.xml b/pom.xml
index 9e91f60778..13cabe0e47 100644
--- a/pom.xml
+++ b/pom.xml
@@ -126,6 +126,7 @@
weixin-java-qidian
weixin-java-channel
spring-boot-starters
+ solon-plugins
diff --git a/solon-plugins/pom.xml b/solon-plugins/pom.xml
new file mode 100644
index 0000000000..278e26dc9f
--- /dev/null
+++ b/solon-plugins/pom.xml
@@ -0,0 +1,44 @@
+
+
+ 4.0.0
+
+ com.github.binarywang
+ wx-java
+ 4.6.4.B
+
+ pom
+ wx-java-solon-plugins
+ WxJava - Solon Plugins
+ WxJava 各个模块的 Solon Plugin
+
+
+ 2.9.2
+
+
+
+ wx-java-miniapp-solon-plugin
+ wx-java-mp-multi-solon-plugin
+ wx-java-mp-solon-plugin
+ wx-java-pay-solon-plugin
+ wx-java-open-solon-plugin
+ wx-java-qidian-solon-plugin
+ wx-java-cp-multi-solon-plugin
+ wx-java-cp-solon-plugin
+ wx-java-channel-solon-plugin
+
+
+
+
+ org.noear
+ solon
+ ${solon.version}
+
+
+ org.projectlombok
+ lombok
+ provided
+
+
+
diff --git a/solon-plugins/wx-java-channel-solon-plugin/pom.xml b/solon-plugins/wx-java-channel-solon-plugin/pom.xml
new file mode 100644
index 0000000000..fbd17094ae
--- /dev/null
+++ b/solon-plugins/wx-java-channel-solon-plugin/pom.xml
@@ -0,0 +1,31 @@
+
+
+ wx-java-solon-plugins
+ com.github.binarywang
+ 4.6.4.B
+
+ 4.0.0
+
+ wx-java-channel-solon-plugin
+ WxJava - Solon Plugin for Channel
+ 微信视频号开发的 Solon Plugin
+
+
+
+ com.github.binarywang
+ weixin-java-channel
+ ${project.version}
+
+
+ redis.clients
+ jedis
+ provided
+
+
+ org.redisson
+ redisson
+ provided
+
+
+
diff --git a/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/config/WxChannelServiceAutoConfiguration.java b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/config/WxChannelServiceAutoConfiguration.java
new file mode 100644
index 0000000000..9ffccc64bf
--- /dev/null
+++ b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/config/WxChannelServiceAutoConfiguration.java
@@ -0,0 +1,35 @@
+package com.binarywang.solon.wxjava.channel.config;
+
+
+import com.binarywang.solon.wxjava.channel.properties.WxChannelProperties;
+import lombok.AllArgsConstructor;
+import me.chanjar.weixin.channel.api.WxChannelService;
+import me.chanjar.weixin.channel.api.impl.WxChannelServiceImpl;
+import me.chanjar.weixin.channel.config.WxChannelConfig;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+
+/**
+ * 微信小程序平台相关服务自动注册
+ *
+ * @author Zeyes
+ */
+@Configuration
+@AllArgsConstructor
+public class WxChannelServiceAutoConfiguration {
+ private final WxChannelProperties properties;
+
+ /**
+ * Channel Service
+ *
+ * @return Channel Service
+ */
+ @Bean
+ @Condition(onMissingBean=WxChannelService.class, onBean = WxChannelConfig.class)
+ public WxChannelService wxChannelService(WxChannelConfig wxChannelConfig) {
+ WxChannelService wxChannelService = new WxChannelServiceImpl();
+ wxChannelService.setConfig(wxChannelConfig);
+ return wxChannelService;
+ }
+}
diff --git a/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/config/storage/AbstractWxChannelConfigStorageConfiguration.java b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/config/storage/AbstractWxChannelConfigStorageConfiguration.java
new file mode 100644
index 0000000000..41d002b419
--- /dev/null
+++ b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/config/storage/AbstractWxChannelConfigStorageConfiguration.java
@@ -0,0 +1,39 @@
+package com.binarywang.solon.wxjava.channel.config.storage;
+
+import com.binarywang.solon.wxjava.channel.properties.WxChannelProperties;
+import me.chanjar.weixin.channel.config.impl.WxChannelDefaultConfigImpl;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * @author Zeyes
+ */
+public abstract class AbstractWxChannelConfigStorageConfiguration {
+
+ protected WxChannelDefaultConfigImpl config(WxChannelDefaultConfigImpl config, WxChannelProperties properties) {
+ config.setAppid(StringUtils.trimToNull(properties.getAppid()));
+ config.setSecret(StringUtils.trimToNull(properties.getSecret()));
+ config.setToken(StringUtils.trimToNull(properties.getToken()));
+ config.setAesKey(StringUtils.trimToNull(properties.getAesKey()));
+ config.setMsgDataFormat(StringUtils.trimToNull(properties.getMsgDataFormat()));
+
+ WxChannelProperties.ConfigStorage configStorageProperties = properties.getConfigStorage();
+ config.setHttpProxyHost(configStorageProperties.getHttpProxyHost());
+ config.setHttpProxyUsername(configStorageProperties.getHttpProxyUsername());
+ config.setHttpProxyPassword(configStorageProperties.getHttpProxyPassword());
+ if (configStorageProperties.getHttpProxyPort() != null) {
+ config.setHttpProxyPort(configStorageProperties.getHttpProxyPort());
+ }
+
+ int maxRetryTimes = configStorageProperties.getMaxRetryTimes();
+ if (configStorageProperties.getMaxRetryTimes() < 0) {
+ maxRetryTimes = 0;
+ }
+ int retrySleepMillis = configStorageProperties.getRetrySleepMillis();
+ if (retrySleepMillis < 0) {
+ retrySleepMillis = 1000;
+ }
+ config.setRetrySleepMillis(retrySleepMillis);
+ config.setMaxRetryTimes(maxRetryTimes);
+ return config;
+ }
+}
diff --git a/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/config/storage/WxChannelInJedisConfigStorageConfiguration.java b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/config/storage/WxChannelInJedisConfigStorageConfiguration.java
new file mode 100644
index 0000000000..f074241914
--- /dev/null
+++ b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/config/storage/WxChannelInJedisConfigStorageConfiguration.java
@@ -0,0 +1,74 @@
+package com.binarywang.solon.wxjava.channel.config.storage;
+
+
+import com.binarywang.solon.wxjava.channel.properties.RedisProperties;
+import com.binarywang.solon.wxjava.channel.properties.WxChannelProperties;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.channel.config.WxChannelConfig;
+import me.chanjar.weixin.channel.config.impl.WxChannelRedisConfigImpl;
+import me.chanjar.weixin.common.redis.JedisWxRedisOps;
+import me.chanjar.weixin.common.redis.WxRedisOps;
+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;
+
+/**
+ * @author Zeyes
+ * @author noear
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxChannelProperties.PREFIX + ".configStorage.type} = jedis",
+ onClass = JedisPool.class
+)
+@RequiredArgsConstructor
+public class WxChannelInJedisConfigStorageConfiguration extends AbstractWxChannelConfigStorageConfiguration {
+ private final WxChannelProperties properties;
+ private final AppContext applicationContext;
+
+ @Bean
+ @Condition(onMissingBean=WxChannelConfig.class)
+ public WxChannelConfig wxChannelConfig() {
+ WxChannelRedisConfigImpl config = getWxChannelRedisConfig();
+ return this.config(config, properties);
+ }
+
+ private WxChannelRedisConfigImpl getWxChannelRedisConfig() {
+ RedisProperties redisProperties = properties.getConfigStorage().getRedis();
+ JedisPool jedisPool;
+ if (redisProperties != null && StringUtils.isNotEmpty(redisProperties.getHost())) {
+ jedisPool = getJedisPool();
+ } else {
+ jedisPool = applicationContext.getBean(JedisPool.class);
+ }
+ WxRedisOps redisOps = new JedisWxRedisOps(jedisPool);
+ return new WxChannelRedisConfigImpl(redisOps, properties.getConfigStorage().getKeyPrefix());
+ }
+
+ private JedisPool getJedisPool() {
+ WxChannelProperties.ConfigStorage storage = properties.getConfigStorage();
+ RedisProperties 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());
+ }
+}
diff --git a/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/config/storage/WxChannelInMemoryConfigStorageConfiguration.java b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/config/storage/WxChannelInMemoryConfigStorageConfiguration.java
new file mode 100644
index 0000000000..a560db29ac
--- /dev/null
+++ b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/config/storage/WxChannelInMemoryConfigStorageConfiguration.java
@@ -0,0 +1,29 @@
+package com.binarywang.solon.wxjava.channel.config.storage;
+
+
+import com.binarywang.solon.wxjava.channel.properties.WxChannelProperties;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.channel.config.WxChannelConfig;
+import me.chanjar.weixin.channel.config.impl.WxChannelDefaultConfigImpl;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+
+/**
+ * @author Zeyes
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxChannelProperties.PREFIX + ".configStorage.type:memory} = memory"
+)
+@RequiredArgsConstructor
+public class WxChannelInMemoryConfigStorageConfiguration extends AbstractWxChannelConfigStorageConfiguration {
+ private final WxChannelProperties properties;
+
+ @Bean
+ @Condition(onMissingBean = WxChannelProperties.class)
+ public WxChannelConfig wxChannelConfig() {
+ WxChannelDefaultConfigImpl config = new WxChannelDefaultConfigImpl();
+ return this.config(config, properties);
+ }
+}
diff --git a/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/config/storage/WxChannelInRedissonConfigStorageConfiguration.java b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/config/storage/WxChannelInRedissonConfigStorageConfiguration.java
new file mode 100644
index 0000000000..cd4de68f21
--- /dev/null
+++ b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/config/storage/WxChannelInRedissonConfigStorageConfiguration.java
@@ -0,0 +1,62 @@
+package com.binarywang.solon.wxjava.channel.config.storage;
+
+
+import com.binarywang.solon.wxjava.channel.properties.RedisProperties;
+import com.binarywang.solon.wxjava.channel.properties.WxChannelProperties;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.channel.config.WxChannelConfig;
+import me.chanjar.weixin.channel.config.impl.WxChannelRedissonConfigImpl;
+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 org.redisson.Redisson;
+import org.redisson.api.RedissonClient;
+import org.redisson.config.Config;
+import org.redisson.config.TransportMode;
+
+/**
+ * @author Zeyes
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxChannelProperties.PREFIX + ".configStorage.type} = redisson",
+ onClass = Redisson.class
+)
+@RequiredArgsConstructor
+public class WxChannelInRedissonConfigStorageConfiguration extends AbstractWxChannelConfigStorageConfiguration {
+ private final WxChannelProperties properties;
+ private final AppContext applicationContext;
+
+ @Bean
+ @Condition(onMissingBean=WxChannelConfig.class)
+ public WxChannelConfig wxChannelConfig() {
+ WxChannelRedissonConfigImpl config = getWxChannelRedissonConfig();
+ return this.config(config, properties);
+ }
+
+ private WxChannelRedissonConfigImpl getWxChannelRedissonConfig() {
+ RedisProperties redisProperties = properties.getConfigStorage().getRedis();
+ RedissonClient redissonClient;
+ if (redisProperties != null && StringUtils.isNotEmpty(redisProperties.getHost())) {
+ redissonClient = getRedissonClient();
+ } else {
+ redissonClient = applicationContext.getBean(RedissonClient.class);
+ }
+ return new WxChannelRedissonConfigImpl(redissonClient, properties.getConfigStorage().getKeyPrefix());
+ }
+
+ private RedissonClient getRedissonClient() {
+ WxChannelProperties.ConfigStorage storage = properties.getConfigStorage();
+ RedisProperties redis = storage.getRedis();
+
+ Config config = new Config();
+ config.useSingleServer()
+ .setAddress("redis://" + redis.getHost() + ":" + redis.getPort())
+ .setDatabase(redis.getDatabase())
+ .setPassword(redis.getPassword());
+ config.setTransportMode(TransportMode.NIO);
+ return Redisson.create(config);
+ }
+}
diff --git a/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/enums/HttpClientType.java b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/enums/HttpClientType.java
new file mode 100644
index 0000000000..0c00dbcaa7
--- /dev/null
+++ b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/enums/HttpClientType.java
@@ -0,0 +1,13 @@
+package com.binarywang.solon.wxjava.channel.enums;
+
+/**
+ * httpclient类型
+ *
+ * @author Zeyes
+ */
+public enum HttpClientType {
+ /**
+ * HttpClient
+ */
+ HttpClient
+}
diff --git a/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/enums/StorageType.java b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/enums/StorageType.java
new file mode 100644
index 0000000000..976f869438
--- /dev/null
+++ b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/enums/StorageType.java
@@ -0,0 +1,25 @@
+package com.binarywang.solon.wxjava.channel.enums;
+
+/**
+ * storage类型
+ *
+ * @author Zeyes
+ */
+public enum StorageType {
+ /**
+ * 内存
+ */
+ Memory,
+ /**
+ * redis(JedisClient)
+ */
+ Jedis,
+ /**
+ * redis(Redisson)
+ */
+ Redisson,
+ /**
+ * redis(RedisTemplate)
+ */
+ RedisTemplate
+}
diff --git a/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/integration/WxChannelPluginImpl.java b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/integration/WxChannelPluginImpl.java
new file mode 100644
index 0000000000..0377bc6f41
--- /dev/null
+++ b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/integration/WxChannelPluginImpl.java
@@ -0,0 +1,25 @@
+package com.binarywang.solon.wxjava.channel.integration;
+
+
+import com.binarywang.solon.wxjava.channel.config.WxChannelServiceAutoConfiguration;
+import com.binarywang.solon.wxjava.channel.config.storage.WxChannelInJedisConfigStorageConfiguration;
+import com.binarywang.solon.wxjava.channel.config.storage.WxChannelInMemoryConfigStorageConfiguration;
+import com.binarywang.solon.wxjava.channel.config.storage.WxChannelInRedissonConfigStorageConfiguration;
+import com.binarywang.solon.wxjava.channel.properties.WxChannelProperties;
+import org.noear.solon.core.AppContext;
+import org.noear.solon.core.Plugin;
+
+/**
+ * @author noear 2024/9/2 created
+ */
+public class WxChannelPluginImpl implements Plugin {
+ @Override
+ public void start(AppContext context) throws Throwable {
+ context.beanMake(WxChannelProperties.class);
+ context.beanMake(WxChannelServiceAutoConfiguration.class);
+
+ context.beanMake(WxChannelInMemoryConfigStorageConfiguration.class);
+ context.beanMake(WxChannelInJedisConfigStorageConfiguration.class);
+ context.beanMake(WxChannelInRedissonConfigStorageConfiguration.class);
+ }
+}
diff --git a/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/properties/RedisProperties.java b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/properties/RedisProperties.java
new file mode 100644
index 0000000000..b74ad89f4e
--- /dev/null
+++ b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/properties/RedisProperties.java
@@ -0,0 +1,42 @@
+package com.binarywang.solon.wxjava.channel.properties;
+
+import lombok.Data;
+
+/**
+ * redis 配置
+ *
+ * @author Zeyes
+ */
+@Data
+public class RedisProperties {
+
+ /**
+ * 主机地址,不填则从solon容器内获取JedisPool
+ */
+ private String host;
+
+ /**
+ * 端口号
+ */
+ private int port = 6379;
+
+ /**
+ * 密码
+ */
+ private String password;
+
+ /**
+ * 超时
+ */
+ private int timeout = 2000;
+
+ /**
+ * 数据库
+ */
+ private int database = 0;
+
+ private Integer maxActive;
+ private Integer maxIdle;
+ private Integer maxWaitMillis;
+ private Integer minIdle;
+}
diff --git a/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/properties/WxChannelProperties.java b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/properties/WxChannelProperties.java
new file mode 100644
index 0000000000..f40aa82115
--- /dev/null
+++ b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/properties/WxChannelProperties.java
@@ -0,0 +1,109 @@
+package com.binarywang.solon.wxjava.channel.properties;
+
+import com.binarywang.solon.wxjava.channel.enums.HttpClientType;
+import com.binarywang.solon.wxjava.channel.enums.StorageType;
+import lombok.Data;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.annotation.Inject;
+
+/**
+ * 属性配置类
+ *
+ * @author Zeyes
+ */
+@Data
+@Configuration
+@Inject("${" + WxChannelProperties.PREFIX +"}")
+public class WxChannelProperties {
+ public static final String PREFIX = "wx.channel";
+
+ /**
+ * 设置视频号小店的appid
+ */
+ private String appid;
+
+ /**
+ * 设置视频号小店的Secret
+ */
+ private String secret;
+
+ /**
+ * 设置视频号小店消息服务器配置的token.
+ */
+ private String token;
+
+ /**
+ * 设置视频号小店消息服务器配置的EncodingAESKey
+ */
+ private String aesKey;
+
+ /**
+ * 消息格式,XML或者JSON
+ */
+ private String msgDataFormat = "JSON";
+
+ /**
+ * 存储策略
+ */
+ private final ConfigStorage configStorage = new ConfigStorage();
+
+ @Data
+ public static class ConfigStorage {
+
+ /**
+ * 存储类型
+ */
+ private StorageType type = StorageType.Memory;
+
+ /**
+ * 指定key前缀
+ */
+ private String keyPrefix = "wh";
+
+ /**
+ * redis连接配置
+ */
+ private final RedisProperties redis = new RedisProperties();
+
+ /**
+ * http客户端类型
+ */
+ private HttpClientType httpClientType = HttpClientType.HttpClient;
+
+ /**
+ * http代理主机
+ */
+ private String httpProxyHost;
+
+ /**
+ * http代理端口
+ */
+ private Integer httpProxyPort;
+
+ /**
+ * http代理用户名
+ */
+ private String httpProxyUsername;
+
+ /**
+ * http代理密码
+ */
+ private String httpProxyPassword;
+
+ /**
+ * http 请求重试间隔
+ *
+ * {@link me.chanjar.weixin.channel.api.BaseWxChannelService#setRetrySleepMillis(int)}
+ *
+ */
+ private int retrySleepMillis = 1000;
+ /**
+ * http 请求最大重试次数
+ *
+ * {@link me.chanjar.weixin.channel.api.BaseWxChannelService#setMaxRetryTimes(int)}
+ *
+ */
+ private int maxRetryTimes = 5;
+ }
+
+}
diff --git a/solon-plugins/wx-java-channel-solon-plugin/src/main/resources/META-INF/solon/wx-java-channel-solon-plugin.properties b/solon-plugins/wx-java-channel-solon-plugin/src/main/resources/META-INF/solon/wx-java-channel-solon-plugin.properties
new file mode 100644
index 0000000000..d8ec8f5112
--- /dev/null
+++ b/solon-plugins/wx-java-channel-solon-plugin/src/main/resources/META-INF/solon/wx-java-channel-solon-plugin.properties
@@ -0,0 +1,2 @@
+solon.plugin=com.binarywang.solon.wxjava.channel.integration.WxChannelPluginImpl
+solon.plugin.priority=10
diff --git a/solon-plugins/wx-java-cp-multi-solon-plugin/README.md b/solon-plugins/wx-java-cp-multi-solon-plugin/README.md
new file mode 100644
index 0000000000..c6acb0889b
--- /dev/null
+++ b/solon-plugins/wx-java-cp-multi-solon-plugin/README.md
@@ -0,0 +1,95 @@
+# wx-java-cp-multi-solon-plugin
+
+企业微信多账号配置
+
+- 实现多 WxCpService 初始化。
+- 未实现 WxCpTpService 初始化,需要的小伙伴可以参考多 WxCpService 配置的实现。
+- 未实现 WxCpCgService 初始化,需要的小伙伴可以参考多 WxCpService 配置的实现。
+
+## 快速开始
+
+1. 引入依赖
+ ```xml
+
+ com.github.binarywang
+ wx-java-cp-multi-solon-plugin
+ ${version}
+
+ ```
+2. 添加配置(app.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.solon.wxjava.cp_multi.service.WxCpMultiServices;
+import me.chanjar.weixin.cp.api.WxCpService;
+import me.chanjar.weixin.cp.api.WxCpUserService;
+
+@Component
+public class DemoService {
+ @Inject
+ private WxCpMultiServices wxCpMultiServices;
+
+ 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 ...
+ }
+}
+```
diff --git a/solon-plugins/wx-java-cp-multi-solon-plugin/pom.xml b/solon-plugins/wx-java-cp-multi-solon-plugin/pom.xml
new file mode 100644
index 0000000000..edca6bda61
--- /dev/null
+++ b/solon-plugins/wx-java-cp-multi-solon-plugin/pom.xml
@@ -0,0 +1,32 @@
+
+
+
+ wx-java-solon-plugins
+ com.github.binarywang
+ 4.6.4.B
+
+ 4.0.0
+
+ wx-java-cp-multi-solon-plugin
+ WxJava - Solon Plugin for WxCp::支持多账号配置
+ 微信企业号开发的 Solon Plugin::支持多账号配置
+
+
+
+ com.github.binarywang
+ weixin-java-cp
+ ${project.version}
+
+
+ redis.clients
+ jedis
+ provided
+
+
+ org.redisson
+ redisson
+ provided
+
+
+
diff --git a/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/configuration/services/AbstractWxCpConfiguration.java b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/configuration/services/AbstractWxCpConfiguration.java
new file mode 100644
index 0000000000..8710bba3ca
--- /dev/null
+++ b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/configuration/services/AbstractWxCpConfiguration.java
@@ -0,0 +1,162 @@
+package com.binarywang.solon.wxjava.cp_multi.configuration.services;
+
+import com.binarywang.solon.wxjava.cp_multi.properties.WxCpMultiProperties;
+import com.binarywang.solon.wxjava.cp_multi.properties.WxCpSingleProperties;
+import com.binarywang.solon.wxjava.cp_multi.service.WxCpMultiServices;
+import com.binarywang.solon.wxjava.cp_multi.service.WxCpMultiServicesImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.cp.api.WxCpService;
+import me.chanjar.weixin.cp.api.impl.WxCpServiceApacheHttpClientImpl;
+import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl;
+import me.chanjar.weixin.cp.api.impl.WxCpServiceJoddHttpImpl;
+import me.chanjar.weixin.cp.api.impl.WxCpServiceOkHttpImpl;
+import me.chanjar.weixin.cp.config.WxCpConfigStorage;
+import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl;
+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 AbstractWxCpConfiguration {
+
+ protected WxCpMultiServices wxCpMultiServices(WxCpMultiProperties wxCpMultiProperties) {
+ Map corps = wxCpMultiProperties.getCorps();
+ if (corps == null || corps.isEmpty()) {
+ log.warn("企业微信应用参数未配置,通过 WxCpMultiServices#getWxCpService(\"tenantId\")获取实例将返回空");
+ return new WxCpMultiServicesImpl();
+ }
+ /**
+ * 校验同一个企业下,agentId 是否唯一,避免使用 redis 缓存 token、ticket 时错乱。
+ *
+ * 查看 {@link me.chanjar.weixin.cp.config.impl.AbstractWxCpInRedisConfigImpl#setAgentId(Integer)}
+ */
+ Collection corpList = corps.values();
+ if (corpList.size() > 1) {
+ // 先按 corpId 分组统计
+ Map> corpsMap = corpList.stream()
+ .collect(Collectors.groupingBy(WxCpSingleProperties::getCorpId));
+ Set>> entries = corpsMap.entrySet();
+ for (Map.Entry> entry : entries) {
+ String corpId = entry.getKey();
+ // 校验每个企业下,agentId 是否唯一
+ boolean multi = entry.getValue().stream()
+ // 通讯录没有 agentId,如果不判断是否为空,这里会报 NPE 异常
+ .collect(Collectors.groupingBy(c -> c.getAgentId() == null ? 0 : c.getAgentId(), Collectors.counting()))
+ .entrySet().stream().anyMatch(e -> e.getValue() > 1);
+ if (multi) {
+ throw new RuntimeException("请确保企业微信配置唯一性[" + corpId + "]");
+ }
+ }
+ }
+ WxCpMultiServicesImpl services = new WxCpMultiServicesImpl();
+
+ Set> entries = corps.entrySet();
+ for (Map.Entry entry : entries) {
+ String tenantId = entry.getKey();
+ WxCpSingleProperties wxCpSingleProperties = entry.getValue();
+ WxCpDefaultConfigImpl storage = this.wxCpConfigStorage(wxCpMultiProperties);
+ this.configCorp(storage, wxCpSingleProperties);
+ this.configHttp(storage, wxCpMultiProperties.getConfigStorage());
+ WxCpService wxCpService = this.wxCpService(storage, wxCpMultiProperties.getConfigStorage());
+ services.addWxCpService(tenantId, wxCpService);
+ }
+ return services;
+ }
+
+ /**
+ * 配置 WxCpDefaultConfigImpl
+ *
+ * @param wxCpMultiProperties 参数
+ * @return WxCpDefaultConfigImpl
+ */
+ protected abstract WxCpDefaultConfigImpl wxCpConfigStorage(WxCpMultiProperties wxCpMultiProperties);
+
+ private WxCpService wxCpService(WxCpConfigStorage wxCpConfigStorage, WxCpMultiProperties.ConfigStorage storage) {
+ WxCpMultiProperties.HttpClientType httpClientType = storage.getHttpClientType();
+ WxCpService wxCpService;
+ switch (httpClientType) {
+ case OK_HTTP:
+ wxCpService = new WxCpServiceOkHttpImpl();
+ break;
+ case JODD_HTTP:
+ wxCpService = new WxCpServiceJoddHttpImpl();
+ break;
+ case HTTP_CLIENT:
+ wxCpService = new WxCpServiceApacheHttpClientImpl();
+ break;
+ default:
+ wxCpService = new WxCpServiceImpl();
+ break;
+ }
+ wxCpService.setWxCpConfigStorage(wxCpConfigStorage);
+ int maxRetryTimes = storage.getMaxRetryTimes();
+ if (maxRetryTimes < 0) {
+ maxRetryTimes = 0;
+ }
+ int retrySleepMillis = storage.getRetrySleepMillis();
+ if (retrySleepMillis < 0) {
+ retrySleepMillis = 1000;
+ }
+ wxCpService.setRetrySleepMillis(retrySleepMillis);
+ wxCpService.setMaxRetryTimes(maxRetryTimes);
+ return wxCpService;
+ }
+
+ private void configCorp(WxCpDefaultConfigImpl config, WxCpSingleProperties wxCpSingleProperties) {
+ String corpId = wxCpSingleProperties.getCorpId();
+ String corpSecret = wxCpSingleProperties.getCorpSecret();
+ Integer agentId = wxCpSingleProperties.getAgentId();
+ String token = wxCpSingleProperties.getToken();
+ String aesKey = wxCpSingleProperties.getAesKey();
+ // 企业微信,私钥,会话存档路径
+ String msgAuditPriKey = wxCpSingleProperties.getMsgAuditPriKey();
+ String msgAuditLibPath = wxCpSingleProperties.getMsgAuditLibPath();
+
+ config.setCorpId(corpId);
+ config.setCorpSecret(corpSecret);
+ config.setAgentId(agentId);
+ if (StringUtils.isNotBlank(token)) {
+ config.setToken(token);
+ }
+ if (StringUtils.isNotBlank(aesKey)) {
+ config.setAesKey(aesKey);
+ }
+ if (StringUtils.isNotBlank(msgAuditPriKey)) {
+ config.setMsgAuditPriKey(msgAuditPriKey);
+ }
+ if (StringUtils.isNotBlank(msgAuditLibPath)) {
+ config.setMsgAuditLibPath(msgAuditLibPath);
+ }
+ }
+
+ private void configHttp(WxCpDefaultConfigImpl config, WxCpMultiProperties.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);
+ }
+ }
+ }
+}
diff --git a/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/configuration/services/WxCpInJedisConfiguration.java b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/configuration/services/WxCpInJedisConfiguration.java
new file mode 100644
index 0000000000..71f5fd6725
--- /dev/null
+++ b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/configuration/services/WxCpInJedisConfiguration.java
@@ -0,0 +1,77 @@
+package com.binarywang.solon.wxjava.cp_multi.configuration.services;
+
+import com.binarywang.solon.wxjava.cp_multi.properties.WxCpMultiProperties;
+import com.binarywang.solon.wxjava.cp_multi.properties.WxCpMultiRedisProperties;
+import com.binarywang.solon.wxjava.cp_multi.service.WxCpMultiServices;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl;
+import me.chanjar.weixin.cp.config.impl.WxCpJedisConfigImpl;
+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 yl
+ * created on 2023/10/16
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxCpMultiProperties.PREFIX + ".configStorage.type} = jedis",
+ onClass = JedisPool.class
+)
+@RequiredArgsConstructor
+public class WxCpInJedisConfiguration extends AbstractWxCpConfiguration {
+ private final WxCpMultiProperties wxCpMultiProperties;
+ private final AppContext applicationContext;
+
+ @Bean
+ public WxCpMultiServices wxCpMultiServices() {
+ return this.wxCpMultiServices(wxCpMultiProperties);
+ }
+
+ @Override
+ protected WxCpDefaultConfigImpl wxCpConfigStorage(WxCpMultiProperties wxCpMultiProperties) {
+ return this.configRedis(wxCpMultiProperties);
+ }
+
+ private WxCpDefaultConfigImpl configRedis(WxCpMultiProperties wxCpMultiProperties) {
+ WxCpMultiRedisProperties wxCpMultiRedisProperties = wxCpMultiProperties.getConfigStorage().getRedis();
+ JedisPool jedisPool;
+ if (wxCpMultiRedisProperties != null && StringUtils.isNotEmpty(wxCpMultiRedisProperties.getHost())) {
+ jedisPool = getJedisPool(wxCpMultiProperties);
+ } else {
+ jedisPool = applicationContext.getBean(JedisPool.class);
+ }
+ return new WxCpJedisConfigImpl(jedisPool, wxCpMultiProperties.getConfigStorage().getKeyPrefix());
+ }
+
+ private JedisPool getJedisPool(WxCpMultiProperties wxCpMultiProperties) {
+ WxCpMultiProperties.ConfigStorage storage = wxCpMultiProperties.getConfigStorage();
+ WxCpMultiRedisProperties 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());
+ }
+}
diff --git a/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/configuration/services/WxCpInMemoryConfiguration.java b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/configuration/services/WxCpInMemoryConfiguration.java
new file mode 100644
index 0000000000..3dfb36e258
--- /dev/null
+++ b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/configuration/services/WxCpInMemoryConfiguration.java
@@ -0,0 +1,38 @@
+package com.binarywang.solon.wxjava.cp_multi.configuration.services;
+
+import com.binarywang.solon.wxjava.cp_multi.properties.WxCpMultiProperties;
+import com.binarywang.solon.wxjava.cp_multi.service.WxCpMultiServices;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+
+/**
+ * 自动装配基于内存策略配置
+ *
+ * @author yl
+ * created on 2023/10/16
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxCpMultiProperties.PREFIX + ".configStorage.type:memory} = memory"
+)
+@RequiredArgsConstructor
+public class WxCpInMemoryConfiguration extends AbstractWxCpConfiguration {
+ private final WxCpMultiProperties wxCpMultiProperties;
+
+ @Bean
+ public WxCpMultiServices wxCpMultiServices() {
+ return this.wxCpMultiServices(wxCpMultiProperties);
+ }
+
+ @Override
+ protected WxCpDefaultConfigImpl wxCpConfigStorage(WxCpMultiProperties wxCpMultiProperties) {
+ return this.configInMemory();
+ }
+
+ private WxCpDefaultConfigImpl configInMemory() {
+ return new WxCpDefaultConfigImpl();
+ }
+}
diff --git a/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/configuration/services/WxCpInRedissonConfiguration.java b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/configuration/services/WxCpInRedissonConfiguration.java
new file mode 100644
index 0000000000..6700570af8
--- /dev/null
+++ b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/configuration/services/WxCpInRedissonConfiguration.java
@@ -0,0 +1,68 @@
+package com.binarywang.solon.wxjava.cp_multi.configuration.services;
+
+import com.binarywang.solon.wxjava.cp_multi.properties.WxCpMultiProperties;
+import com.binarywang.solon.wxjava.cp_multi.properties.WxCpMultiRedisProperties;
+import com.binarywang.solon.wxjava.cp_multi.service.WxCpMultiServices;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl;
+import me.chanjar.weixin.cp.config.impl.WxCpRedissonConfigImpl;
+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 org.redisson.Redisson;
+import org.redisson.api.RedissonClient;
+import org.redisson.config.Config;
+import org.redisson.config.TransportMode;
+
+/**
+ * 自动装配基于 redisson 策略配置
+ *
+ * @author yl
+ * created on 2023/10/16
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxCpMultiProperties.PREFIX + ".configStorage.type} = redisson",
+ onClass = Redisson.class
+)
+@RequiredArgsConstructor
+public class WxCpInRedissonConfiguration extends AbstractWxCpConfiguration {
+ private final WxCpMultiProperties wxCpMultiProperties;
+ private final AppContext applicationContext;
+
+ @Bean
+ public WxCpMultiServices wxCpMultiServices() {
+ return this.wxCpMultiServices(wxCpMultiProperties);
+ }
+
+ @Override
+ protected WxCpDefaultConfigImpl wxCpConfigStorage(WxCpMultiProperties wxCpMultiProperties) {
+ return this.configRedisson(wxCpMultiProperties);
+ }
+
+ private WxCpDefaultConfigImpl configRedisson(WxCpMultiProperties wxCpMultiProperties) {
+ WxCpMultiRedisProperties redisProperties = wxCpMultiProperties.getConfigStorage().getRedis();
+ RedissonClient redissonClient;
+ if (redisProperties != null && StringUtils.isNotEmpty(redisProperties.getHost())) {
+ redissonClient = getRedissonClient(wxCpMultiProperties);
+ } else {
+ redissonClient = applicationContext.getBean(RedissonClient.class);
+ }
+ return new WxCpRedissonConfigImpl(redissonClient, wxCpMultiProperties.getConfigStorage().getKeyPrefix());
+ }
+
+ private RedissonClient getRedissonClient(WxCpMultiProperties wxCpMultiProperties) {
+ WxCpMultiProperties.ConfigStorage storage = wxCpMultiProperties.getConfigStorage();
+ WxCpMultiRedisProperties redis = storage.getRedis();
+
+ Config config = new Config();
+ config.useSingleServer()
+ .setAddress("redis://" + redis.getHost() + ":" + redis.getPort())
+ .setDatabase(redis.getDatabase())
+ .setPassword(redis.getPassword());
+ config.setTransportMode(TransportMode.NIO);
+ return Redisson.create(config);
+ }
+}
diff --git a/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/integration/WxCpMultiPluginImpl.java b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/integration/WxCpMultiPluginImpl.java
new file mode 100644
index 0000000000..b2a078c727
--- /dev/null
+++ b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/integration/WxCpMultiPluginImpl.java
@@ -0,0 +1,21 @@
+package com.binarywang.solon.wxjava.cp_multi.integration;
+
+import com.binarywang.solon.wxjava.cp_multi.configuration.services.WxCpInJedisConfiguration;
+import com.binarywang.solon.wxjava.cp_multi.configuration.services.WxCpInMemoryConfiguration;
+import com.binarywang.solon.wxjava.cp_multi.configuration.services.WxCpInRedissonConfiguration;
+import com.binarywang.solon.wxjava.cp_multi.properties.WxCpMultiProperties;
+import org.noear.solon.core.AppContext;
+import org.noear.solon.core.Plugin;
+
+/**
+ * @author noear 2024/9/2 created
+ */
+public class WxCpMultiPluginImpl implements Plugin {
+ @Override
+ public void start(AppContext context) throws Throwable {
+ context.beanMake(WxCpMultiProperties.class);
+ context.beanMake(WxCpInJedisConfiguration.class);
+ context.beanMake(WxCpInMemoryConfiguration.class);
+ context.beanMake(WxCpInRedissonConfiguration.class);
+ }
+}
diff --git a/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/properties/WxCpMultiProperties.java b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/properties/WxCpMultiProperties.java
new file mode 100644
index 0000000000..5544a92e00
--- /dev/null
+++ b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/properties/WxCpMultiProperties.java
@@ -0,0 +1,129 @@
+package com.binarywang.solon.wxjava.cp_multi.properties;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.annotation.Inject;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 企业微信多企业接入相关配置属性
+ *
+ * @author yl
+ * created on 2023/10/16
+ */
+@Data
+@NoArgsConstructor
+@Configuration
+@Inject("${" + WxCpMultiProperties.PREFIX + "}")
+public class WxCpMultiProperties implements Serializable {
+ private static final long serialVersionUID = -1569510477055668503L;
+ public static final String PREFIX = "wx.cp";
+
+ private Map corps = new HashMap<>();
+
+ /**
+ * 配置存储策略,默认内存
+ */
+ private ConfigStorage configStorage = new ConfigStorage();
+
+ @Data
+ @NoArgsConstructor
+ public static class ConfigStorage implements Serializable {
+ private static final long serialVersionUID = 4815731027000065434L;
+ /**
+ * 存储类型
+ */
+ private StorageType type = StorageType.memory;
+
+ /**
+ * 指定key前缀
+ */
+ private String keyPrefix = "wx:cp";
+
+ /**
+ * redis连接配置
+ */
+ private WxCpMultiRedisProperties redis = new WxCpMultiRedisProperties();
+
+ /**
+ * http客户端类型.
+ */
+ private HttpClientType httpClientType = HttpClientType.HTTP_CLIENT;
+
+ /**
+ * http代理主机
+ */
+ private String httpProxyHost;
+
+ /**
+ * http代理端口
+ */
+ private Integer httpProxyPort;
+
+ /**
+ * http代理用户名
+ */
+ private String httpProxyUsername;
+
+ /**
+ * http代理密码
+ */
+ private String httpProxyPassword;
+
+ /**
+ * http 请求最大重试次数
+ *
+ * {@link me.chanjar.weixin.cp.api.WxCpService#setMaxRetryTimes(int)}
+ * {@link me.chanjar.weixin.cp.api.impl.BaseWxCpServiceImpl#setMaxRetryTimes(int)}
+ *
+ */
+ private int maxRetryTimes = 5;
+
+ /**
+ * http 请求重试间隔
+ *
+ * {@link me.chanjar.weixin.cp.api.WxCpService#setRetrySleepMillis(int)}
+ * {@link me.chanjar.weixin.cp.api.impl.BaseWxCpServiceImpl#setRetrySleepMillis(int)}
+ *
+ */
+ private int retrySleepMillis = 1000;
+ }
+
+ public enum StorageType {
+ /**
+ * 内存
+ */
+ memory,
+ /**
+ * jedis
+ */
+ jedis,
+ /**
+ * redisson
+ */
+ redisson,
+ /**
+ * redistemplate
+ */
+ redistemplate
+ }
+
+ public enum HttpClientType {
+ /**
+ * HttpClient
+ */
+ HTTP_CLIENT,
+ /**
+ * OkHttp
+ */
+ OK_HTTP,
+ /**
+ * JoddHttp
+ */
+ JODD_HTTP
+ }
+}
diff --git a/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/properties/WxCpMultiRedisProperties.java b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/properties/WxCpMultiRedisProperties.java
new file mode 100644
index 0000000000..14952d69d9
--- /dev/null
+++ b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/properties/WxCpMultiRedisProperties.java
@@ -0,0 +1,48 @@
+package com.binarywang.solon.wxjava.cp_multi.properties;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * Redis配置.
+ *
+ * @author yl
+ * created on 2023/10/16
+ */
+@Data
+@NoArgsConstructor
+public class WxCpMultiRedisProperties implements Serializable {
+ private static final long serialVersionUID = -5924815351660074401L;
+
+ /**
+ * 主机地址.
+ */
+ private String host;
+
+ /**
+ * 端口号.
+ */
+ private int port = 6379;
+
+ /**
+ * 密码.
+ */
+ private String password;
+
+ /**
+ * 超时.
+ */
+ private int timeout = 2000;
+
+ /**
+ * 数据库.
+ */
+ private int database = 0;
+
+ private Integer maxActive;
+ private Integer maxIdle;
+ private Integer maxWaitMillis;
+ private Integer minIdle;
+}
diff --git a/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/properties/WxCpSingleProperties.java b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/properties/WxCpSingleProperties.java
new file mode 100644
index 0000000000..e761a09062
--- /dev/null
+++ b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/properties/WxCpSingleProperties.java
@@ -0,0 +1,46 @@
+package com.binarywang.solon.wxjava.cp_multi.properties;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 企业微信企业相关配置属性
+ *
+ * @author yl
+ * created on 2023/10/16
+ */
+@Data
+@NoArgsConstructor
+public class WxCpSingleProperties implements Serializable {
+ private static final long serialVersionUID = -7502823825007859418L;
+ /**
+ * 微信企业号 corpId
+ */
+ private String corpId;
+ /**
+ * 微信企业号 corpSecret
+ */
+ private String corpSecret;
+ /**
+ * 微信企业号应用 token
+ */
+ private String token;
+ /**
+ * 微信企业号应用 ID
+ */
+ private Integer agentId;
+ /**
+ * 微信企业号应用 EncodingAESKey
+ */
+ private String aesKey;
+ /**
+ * 微信企业号应用 会话存档私钥
+ */
+ private String msgAuditPriKey;
+ /**
+ * 微信企业号应用 会话存档类库路径
+ */
+ private String msgAuditLibPath;
+}
diff --git a/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/service/WxCpMultiServices.java b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/service/WxCpMultiServices.java
new file mode 100644
index 0000000000..c66c28233d
--- /dev/null
+++ b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/service/WxCpMultiServices.java
@@ -0,0 +1,26 @@
+package com.binarywang.solon.wxjava.cp_multi.service;
+
+import me.chanjar.weixin.cp.api.WxCpService;
+
+/**
+ * 企业微信 {@link WxCpService} 所有实例存放类.
+ *
+ * @author yl
+ * created on 2023/10/16
+ */
+public interface WxCpMultiServices {
+ /**
+ * 通过租户 Id 获取 WxCpService
+ *
+ * @param tenantId 租户 Id
+ * @return WxCpService
+ */
+ WxCpService getWxCpService(String tenantId);
+
+ /**
+ * 根据租户 Id,从列表中移除一个 WxCpService 实例
+ *
+ * @param tenantId 租户 Id
+ */
+ void removeWxCpService(String tenantId);
+}
diff --git a/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/service/WxCpMultiServicesImpl.java b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/service/WxCpMultiServicesImpl.java
new file mode 100644
index 0000000000..d7833a05f9
--- /dev/null
+++ b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/service/WxCpMultiServicesImpl.java
@@ -0,0 +1,42 @@
+package com.binarywang.solon.wxjava.cp_multi.service;
+
+import me.chanjar.weixin.cp.api.WxCpService;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 企业微信 {@link WxCpMultiServices} 默认实现
+ *
+ * @author yl
+ * created on 2023/10/16
+ */
+public class WxCpMultiServicesImpl implements WxCpMultiServices {
+ private final Map services = new ConcurrentHashMap<>();
+
+ /**
+ * 通过租户 Id 获取 WxCpService
+ *
+ * @param tenantId 租户 Id
+ * @return WxCpService
+ */
+ @Override
+ public WxCpService getWxCpService(String tenantId) {
+ return this.services.get(tenantId);
+ }
+
+ /**
+ * 根据租户 Id,添加一个 WxCpService 到列表
+ *
+ * @param tenantId 租户 Id
+ * @param wxCpService WxCpService 实例
+ */
+ public void addWxCpService(String tenantId, WxCpService wxCpService) {
+ this.services.put(tenantId, wxCpService);
+ }
+
+ @Override
+ public void removeWxCpService(String tenantId) {
+ this.services.remove(tenantId);
+ }
+}
diff --git a/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/resources/META-INF/solon/wx-java-cp-multi-solon-plugin.properties b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/resources/META-INF/solon/wx-java-cp-multi-solon-plugin.properties
new file mode 100644
index 0000000000..eb537e9a66
--- /dev/null
+++ b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/resources/META-INF/solon/wx-java-cp-multi-solon-plugin.properties
@@ -0,0 +1,2 @@
+solon.plugin=com.binarywang.solon.wxjava.cp_multi.integration.WxCpMultiPluginImpl
+solon.plugin.priority=10
diff --git a/solon-plugins/wx-java-cp-solon-plugin/README.md b/solon-plugins/wx-java-cp-solon-plugin/README.md
new file mode 100644
index 0000000000..04d5dfab58
--- /dev/null
+++ b/solon-plugins/wx-java-cp-solon-plugin/README.md
@@ -0,0 +1,41 @@
+# wx-java-cp-solon-plugin
+
+## 快速开始
+
+1. 引入依赖
+ ```xml
+
+ com.github.binarywang
+ wx-java-cp-solon-plugin
+ ${version}
+
+ ```
+2. 添加配置(app.properties)
+ ```properties
+ # 企业微信号配置(必填)
+ wx.cp.corp-id = @corp-id
+ wx.cp.corp-secret = @corp-secret
+ # 选填
+ wx.cp.agent-id = @agent-id
+ wx.cp.token = @token
+ wx.cp.aes-key = @aes-key
+ wx.cp.msg-audit-priKey = @msg-audit-priKey
+ wx.cp.msg-audit-lib-path = @msg-audit-lib-path
+ # ConfigStorage 配置(选填)
+ wx.cp.config-storage.type=memory # 配置类型: memory(默认), jedis, redisson, redistemplate
+ # http 客户端配置(选填)
+ 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. 支持自动注入的类型: `WxCpService`, `WxCpConfigStorage`
+
+4. 覆盖自动配置: 自定义注入的bean会覆盖自动注入的
+
+- WxCpService
+- WxCpConfigStorage
diff --git a/solon-plugins/wx-java-cp-solon-plugin/pom.xml b/solon-plugins/wx-java-cp-solon-plugin/pom.xml
new file mode 100644
index 0000000000..6b71454c68
--- /dev/null
+++ b/solon-plugins/wx-java-cp-solon-plugin/pom.xml
@@ -0,0 +1,30 @@
+
+
+
+ wx-java-solon-plugins
+ com.github.binarywang
+ 4.6.4.B
+
+ 4.0.0
+
+ wx-java-cp-solon-plugin
+ WxJava - Solon Plugin for WxCp
+ 微信企业号开发的 Solon Plugin
+
+
+
+ com.github.binarywang
+ weixin-java-cp
+ ${project.version}
+
+
+ redis.clients
+ jedis
+
+
+ org.redisson
+ redisson
+
+
+
diff --git a/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/config/WxCpServiceAutoConfiguration.java b/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/config/WxCpServiceAutoConfiguration.java
new file mode 100644
index 0000000000..82aeeaf859
--- /dev/null
+++ b/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/config/WxCpServiceAutoConfiguration.java
@@ -0,0 +1,43 @@
+package com.binarywang.solon.wxjava.cp.config;
+
+import com.binarywang.solon.wxjava.cp.properties.WxCpProperties;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.cp.api.WxCpService;
+import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl;
+import me.chanjar.weixin.cp.config.WxCpConfigStorage;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+
+/**
+ * 企业微信平台相关服务自动注册
+ *
+ * @author yl
+ * created on 2021/12/6
+ */
+@Configuration
+@RequiredArgsConstructor
+public class WxCpServiceAutoConfiguration {
+ private final WxCpProperties wxCpProperties;
+
+ @Bean
+ @Condition(onMissingBean = WxCpService.class,
+ onBean = WxCpConfigStorage.class)
+ public WxCpService wxCpService(WxCpConfigStorage wxCpConfigStorage) {
+ WxCpService wxCpService = new WxCpServiceImpl();
+ wxCpService.setWxCpConfigStorage(wxCpConfigStorage);
+
+ WxCpProperties.ConfigStorage storage = wxCpProperties.getConfigStorage();
+ int maxRetryTimes = storage.getMaxRetryTimes();
+ if (maxRetryTimes < 0) {
+ maxRetryTimes = 0;
+ }
+ int retrySleepMillis = storage.getRetrySleepMillis();
+ if (retrySleepMillis < 0) {
+ retrySleepMillis = 1000;
+ }
+ wxCpService.setRetrySleepMillis(retrySleepMillis);
+ wxCpService.setMaxRetryTimes(maxRetryTimes);
+ return wxCpService;
+ }
+}
diff --git a/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/integration/WxCpPluginImpl.java b/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/integration/WxCpPluginImpl.java
new file mode 100644
index 0000000000..fda64b3a17
--- /dev/null
+++ b/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/integration/WxCpPluginImpl.java
@@ -0,0 +1,25 @@
+package com.binarywang.solon.wxjava.cp.integration;
+
+import com.binarywang.solon.wxjava.cp.config.WxCpServiceAutoConfiguration;
+import com.binarywang.solon.wxjava.cp.properties.WxCpProperties;
+import com.binarywang.solon.wxjava.cp.storage.WxCpInJedisConfigStorageConfiguration;
+import com.binarywang.solon.wxjava.cp.storage.WxCpInMemoryConfigStorageConfiguration;
+import com.binarywang.solon.wxjava.cp.storage.WxCpInRedissonConfigStorageConfiguration;
+import org.noear.solon.core.AppContext;
+import org.noear.solon.core.Plugin;
+
+/**
+ * @author noear 2024/9/2 created
+ */
+public class WxCpPluginImpl implements Plugin {
+ @Override
+ public void start(AppContext context) throws Throwable {
+ context.beanMake(WxCpProperties.class);
+
+ context.beanMake(WxCpServiceAutoConfiguration.class);
+
+ context.beanMake(WxCpInMemoryConfigStorageConfiguration.class);
+ context.beanMake(WxCpInJedisConfigStorageConfiguration.class);
+ context.beanMake(WxCpInRedissonConfigStorageConfiguration.class);
+ }
+}
diff --git a/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/properties/WxCpProperties.java b/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/properties/WxCpProperties.java
new file mode 100644
index 0000000000..60524f5228
--- /dev/null
+++ b/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/properties/WxCpProperties.java
@@ -0,0 +1,133 @@
+package com.binarywang.solon.wxjava.cp.properties;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.annotation.Inject;
+
+import java.io.Serializable;
+
+/**
+ * 企业微信接入相关配置属性
+ *
+ * @author yl
+ * created on 2021/12/6
+ */
+@Data
+@NoArgsConstructor
+@Configuration
+@Inject("${" + WxCpProperties.PREFIX + "}")
+public class WxCpProperties {
+ public static final String PREFIX = "wx.cp";
+
+ /**
+ * 微信企业号 corpId
+ */
+ private String corpId;
+ /**
+ * 微信企业号 corpSecret
+ */
+ private String corpSecret;
+ /**
+ * 微信企业号应用 token
+ */
+ private String token;
+ /**
+ * 微信企业号应用 ID
+ */
+ private Integer agentId;
+ /**
+ * 微信企业号应用 EncodingAESKey
+ */
+ private String aesKey;
+ /**
+ * 微信企业号应用 会话存档私钥
+ */
+ private String msgAuditPriKey;
+ /**
+ * 微信企业号应用 会话存档类库路径
+ */
+ private String msgAuditLibPath;
+
+ /**
+ * 配置存储策略,默认内存
+ */
+ private ConfigStorage configStorage = new ConfigStorage();
+
+ @Data
+ @NoArgsConstructor
+ public static class ConfigStorage implements Serializable {
+ private static final long serialVersionUID = 4815731027000065434L;
+ /**
+ * 存储类型
+ */
+ private StorageType type = StorageType.memory;
+
+ /**
+ * 指定key前缀
+ */
+ private String keyPrefix = "wx:cp";
+
+ /**
+ * redis连接配置
+ */
+ private WxCpRedisProperties redis = new WxCpRedisProperties();
+
+ /**
+ * http代理主机
+ */
+ private String httpProxyHost;
+
+ /**
+ * http代理端口
+ */
+ private Integer httpProxyPort;
+
+ /**
+ * http代理用户名
+ */
+ private String httpProxyUsername;
+
+ /**
+ * http代理密码
+ */
+ private String httpProxyPassword;
+
+ /**
+ * http 请求最大重试次数
+ *
+ * {@link me.chanjar.weixin.cp.api.WxCpService#setMaxRetryTimes(int)}
+ * {@link me.chanjar.weixin.cp.api.impl.BaseWxCpServiceImpl#setMaxRetryTimes(int)}
+ *
+ */
+ private int maxRetryTimes = 5;
+
+ /**
+ * http 请求重试间隔
+ *
+ * {@link me.chanjar.weixin.cp.api.WxCpService#setRetrySleepMillis(int)}
+ * {@link me.chanjar.weixin.cp.api.impl.BaseWxCpServiceImpl#setRetrySleepMillis(int)}
+ *
+ */
+ private int retrySleepMillis = 1000;
+ }
+
+ public enum StorageType {
+ /**
+ * 内存
+ */
+ memory,
+ /**
+ * jedis
+ */
+ jedis,
+ /**
+ * redisson
+ */
+ redisson,
+ /**
+ * redistemplate
+ */
+ redistemplate
+ }
+}
diff --git a/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/properties/WxCpRedisProperties.java b/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/properties/WxCpRedisProperties.java
new file mode 100644
index 0000000000..43b8788d3f
--- /dev/null
+++ b/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/properties/WxCpRedisProperties.java
@@ -0,0 +1,46 @@
+package com.binarywang.solon.wxjava.cp.properties;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * Redis配置.
+ *
+ * @author yl
+ * created on 2023/04/23
+ */
+@Data
+public class WxCpRedisProperties implements Serializable {
+ private static final long serialVersionUID = -5924815351660074401L;
+
+ /**
+ * 主机地址.
+ */
+ private String host;
+
+ /**
+ * 端口号.
+ */
+ private int port = 6379;
+
+ /**
+ * 密码.
+ */
+ private String password;
+
+ /**
+ * 超时.
+ */
+ private int timeout = 2000;
+
+ /**
+ * 数据库.
+ */
+ private int database = 0;
+
+ private Integer maxActive;
+ private Integer maxIdle;
+ private Integer maxWaitMillis;
+ private Integer minIdle;
+}
diff --git a/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/storage/AbstractWxCpConfigStorageConfiguration.java b/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/storage/AbstractWxCpConfigStorageConfiguration.java
new file mode 100644
index 0000000000..9fcdd5779a
--- /dev/null
+++ b/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/storage/AbstractWxCpConfigStorageConfiguration.java
@@ -0,0 +1,61 @@
+package com.binarywang.solon.wxjava.cp.storage;
+
+import com.binarywang.solon.wxjava.cp.properties.WxCpProperties;
+import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * WxCpConfigStorage 抽象配置类
+ *
+ * @author yl & Wang_Wong
+ * created on 2021/12/6
+ */
+public abstract class AbstractWxCpConfigStorageConfiguration {
+
+ protected WxCpDefaultConfigImpl config(WxCpDefaultConfigImpl config, WxCpProperties properties) {
+ String corpId = properties.getCorpId();
+ String corpSecret = properties.getCorpSecret();
+ Integer agentId = properties.getAgentId();
+ String token = properties.getToken();
+ String aesKey = properties.getAesKey();
+ // 企业微信,私钥,会话存档路径
+ String msgAuditPriKey = properties.getMsgAuditPriKey();
+ String msgAuditLibPath = properties.getMsgAuditLibPath();
+
+ config.setCorpId(corpId);
+ config.setCorpSecret(corpSecret);
+ config.setAgentId(agentId);
+ if (StringUtils.isNotBlank(token)) {
+ config.setToken(token);
+ }
+ if (StringUtils.isNotBlank(aesKey)) {
+ config.setAesKey(aesKey);
+ }
+ if (StringUtils.isNotBlank(msgAuditPriKey)) {
+ config.setMsgAuditPriKey(msgAuditPriKey);
+ }
+ if (StringUtils.isNotBlank(msgAuditLibPath)) {
+ config.setMsgAuditLibPath(msgAuditLibPath);
+ }
+
+ WxCpProperties.ConfigStorage storage = properties.getConfigStorage();
+ 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);
+ }
+ }
+ return config;
+ }
+
+}
diff --git a/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/storage/WxCpInJedisConfigStorageConfiguration.java b/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/storage/WxCpInJedisConfigStorageConfiguration.java
new file mode 100644
index 0000000000..f6f6931992
--- /dev/null
+++ b/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/storage/WxCpInJedisConfigStorageConfiguration.java
@@ -0,0 +1,74 @@
+package com.binarywang.solon.wxjava.cp.storage;
+
+import com.binarywang.solon.wxjava.cp.properties.WxCpProperties;
+import com.binarywang.solon.wxjava.cp.properties.WxCpRedisProperties;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.cp.config.WxCpConfigStorage;
+import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl;
+import me.chanjar.weixin.cp.config.impl.WxCpJedisConfigImpl;
+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 yl
+ * created on 2023/04/23
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxCpProperties.PREFIX + ".configStorage.type} = jedis",
+ onClass = JedisPool.class
+)
+@RequiredArgsConstructor
+public class WxCpInJedisConfigStorageConfiguration extends AbstractWxCpConfigStorageConfiguration {
+ private final WxCpProperties wxCpProperties;
+ private final AppContext applicationContext;
+
+ @Bean
+ @Condition(onMissingBean=WxCpConfigStorage.class)
+ public WxCpConfigStorage wxCpConfigStorage() {
+ WxCpDefaultConfigImpl config = getConfigStorage();
+ return this.config(config, wxCpProperties);
+ }
+
+ private WxCpJedisConfigImpl getConfigStorage() {
+ WxCpRedisProperties wxCpRedisProperties = wxCpProperties.getConfigStorage().getRedis();
+ JedisPool jedisPool;
+ if (wxCpRedisProperties != null && StringUtils.isNotEmpty(wxCpRedisProperties.getHost())) {
+ jedisPool = getJedisPool();
+ } else {
+ jedisPool = applicationContext.getBean(JedisPool.class);
+ }
+ return new WxCpJedisConfigImpl(jedisPool, wxCpProperties.getConfigStorage().getKeyPrefix());
+ }
+
+ private JedisPool getJedisPool() {
+ WxCpProperties.ConfigStorage storage = wxCpProperties.getConfigStorage();
+ WxCpRedisProperties 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());
+ }
+}
diff --git a/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/storage/WxCpInMemoryConfigStorageConfiguration.java b/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/storage/WxCpInMemoryConfigStorageConfiguration.java
new file mode 100644
index 0000000000..2776fea368
--- /dev/null
+++ b/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/storage/WxCpInMemoryConfigStorageConfiguration.java
@@ -0,0 +1,31 @@
+package com.binarywang.solon.wxjava.cp.storage;
+
+import com.binarywang.solon.wxjava.cp.properties.WxCpProperties;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.cp.config.WxCpConfigStorage;
+import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+
+/**
+ * 自动装配基于内存策略配置
+ *
+ * @author yl
+ * created on 2021/12/6
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxCpProperties.PREFIX + ".configStorage.type:memory} = memory"
+)
+@RequiredArgsConstructor
+public class WxCpInMemoryConfigStorageConfiguration extends AbstractWxCpConfigStorageConfiguration {
+ private final WxCpProperties wxCpProperties;
+
+ @Bean
+ @Condition(onMissingBean=WxCpConfigStorage.class)
+ public WxCpConfigStorage wxCpConfigStorage() {
+ WxCpDefaultConfigImpl config = new WxCpDefaultConfigImpl();
+ return this.config(config, wxCpProperties);
+ }
+}
diff --git a/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/storage/WxCpInRedissonConfigStorageConfiguration.java b/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/storage/WxCpInRedissonConfigStorageConfiguration.java
new file mode 100644
index 0000000000..0aef4d520a
--- /dev/null
+++ b/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/storage/WxCpInRedissonConfigStorageConfiguration.java
@@ -0,0 +1,65 @@
+package com.binarywang.solon.wxjava.cp.storage;
+
+import com.binarywang.solon.wxjava.cp.properties.WxCpProperties;
+import com.binarywang.solon.wxjava.cp.properties.WxCpRedisProperties;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.cp.config.WxCpConfigStorage;
+import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl;
+import me.chanjar.weixin.cp.config.impl.WxCpRedissonConfigImpl;
+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 org.redisson.Redisson;
+import org.redisson.api.RedissonClient;
+import org.redisson.config.Config;
+import org.redisson.config.TransportMode;
+
+/**
+ * 自动装配基于 redisson 策略配置
+ *
+ * @author yl
+ * created on 2023/04/23
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxCpProperties.PREFIX + ".configStorage.type} = redisson",
+ onClass = Redisson.class
+)
+@RequiredArgsConstructor
+public class WxCpInRedissonConfigStorageConfiguration extends AbstractWxCpConfigStorageConfiguration {
+ private final WxCpProperties wxCpProperties;
+ private final AppContext applicationContext;
+
+ @Bean
+ @Condition(onMissingBean=WxCpConfigStorage.class)
+ public WxCpConfigStorage wxCpConfigStorage() {
+ WxCpDefaultConfigImpl config = getConfigStorage();
+ return this.config(config, wxCpProperties);
+ }
+
+ private WxCpRedissonConfigImpl getConfigStorage() {
+ WxCpRedisProperties redisProperties = wxCpProperties.getConfigStorage().getRedis();
+ RedissonClient redissonClient;
+ if (redisProperties != null && StringUtils.isNotEmpty(redisProperties.getHost())) {
+ redissonClient = getRedissonClient();
+ } else {
+ redissonClient = applicationContext.getBean(RedissonClient.class);
+ }
+ return new WxCpRedissonConfigImpl(redissonClient, wxCpProperties.getConfigStorage().getKeyPrefix());
+ }
+
+ private RedissonClient getRedissonClient() {
+ WxCpProperties.ConfigStorage storage = wxCpProperties.getConfigStorage();
+ WxCpRedisProperties redis = storage.getRedis();
+
+ Config config = new Config();
+ config.useSingleServer()
+ .setAddress("redis://" + redis.getHost() + ":" + redis.getPort())
+ .setDatabase(redis.getDatabase())
+ .setPassword(redis.getPassword());
+ config.setTransportMode(TransportMode.NIO);
+ return Redisson.create(config);
+ }
+}
diff --git a/solon-plugins/wx-java-cp-solon-plugin/src/main/resources/META-INF/solon/wx-java-cp-solon-plugin.properties b/solon-plugins/wx-java-cp-solon-plugin/src/main/resources/META-INF/solon/wx-java-cp-solon-plugin.properties
new file mode 100644
index 0000000000..c765affecb
--- /dev/null
+++ b/solon-plugins/wx-java-cp-solon-plugin/src/main/resources/META-INF/solon/wx-java-cp-solon-plugin.properties
@@ -0,0 +1,2 @@
+solon.plugin=com.binarywang.solon.wxjava.cp.integration.WxCpPluginImpl
+solon.plugin.priority=10
diff --git a/solon-plugins/wx-java-miniapp-solon-plugin/README.md b/solon-plugins/wx-java-miniapp-solon-plugin/README.md
new file mode 100644
index 0000000000..3d1d7517f7
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-solon-plugin/README.md
@@ -0,0 +1,35 @@
+# wx-java-miniapp-solon-plugin
+## 快速开始
+1. 引入依赖
+ ```xml
+
+ com.github.binarywang
+ wx-java-miniapp-solon-plugin
+ ${version}
+
+ ```
+2. 添加配置(app.properties)
+ ```properties
+ # 公众号配置(必填)
+ wx.miniapp.appid = appId
+ wx.miniapp.secret = @secret
+ wx.miniapp.token = @token
+ wx.miniapp.aesKey = @aesKey
+ wx.miniapp.msgDataFormat = @msgDataFormat # 消息格式,XML或者JSON.
+ # 存储配置redis(可选)
+ # 注意: 指定redis.host值后不会使用容器注入的redis连接(JedisPool)
+ wx.miniapp.config-storage.type = Jedis # 配置类型: Memory(默认), Jedis, RedisTemplate
+ wx.miniapp.config-storage.key-prefix = wa # 相关redis前缀配置: wa(默认)
+ wx.miniapp.config-storage.redis.host = 127.0.0.1
+ wx.miniapp.config-storage.redis.port = 6379
+ # http客户端配置
+ wx.miniapp.config-storage.http-client-type=HttpClient # http客户端类型: HttpClient(默认), OkHttp, JoddHttp
+ wx.miniapp.config-storage.http-proxy-host=
+ wx.miniapp.config-storage.http-proxy-port=
+ wx.miniapp.config-storage.http-proxy-username=
+ wx.miniapp.config-storage.http-proxy-password=
+ ```
+3. 自动注入的类型
+- `WxMaService`
+- `WxMaConfig`
+
diff --git a/solon-plugins/wx-java-miniapp-solon-plugin/pom.xml b/solon-plugins/wx-java-miniapp-solon-plugin/pom.xml
new file mode 100644
index 0000000000..b4d527d711
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-solon-plugin/pom.xml
@@ -0,0 +1,43 @@
+
+
+
+ wx-java-solon-plugins
+ com.github.binarywang
+ 4.6.4.B
+
+ 4.0.0
+
+ wx-java-miniapp-solon-plugin
+ WxJava - Solon Plugin for MiniApp
+ 微信小程序开发的 Solon Plugin
+
+
+
+ com.github.binarywang
+ weixin-java-miniapp
+ ${project.version}
+
+
+ redis.clients
+ jedis
+ provided
+
+
+ org.redisson
+ redisson
+ provided
+
+
+ org.jodd
+ jodd-http
+ provided
+
+
+ com.squareup.okhttp3
+ okhttp
+ provided
+
+
+
+
diff --git a/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/config/WxMaServiceAutoConfiguration.java b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/config/WxMaServiceAutoConfiguration.java
new file mode 100644
index 0000000000..5463ec08e9
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/config/WxMaServiceAutoConfiguration.java
@@ -0,0 +1,54 @@
+package com.binarywang.solon.wxjava.miniapp.config;
+
+import cn.binarywang.wx.miniapp.api.WxMaService;
+import cn.binarywang.wx.miniapp.api.impl.WxMaServiceHttpClientImpl;
+import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
+import cn.binarywang.wx.miniapp.api.impl.WxMaServiceJoddHttpImpl;
+import cn.binarywang.wx.miniapp.api.impl.WxMaServiceOkHttpImpl;
+import cn.binarywang.wx.miniapp.config.WxMaConfig;
+import com.binarywang.solon.wxjava.miniapp.enums.HttpClientType;
+import com.binarywang.solon.wxjava.miniapp.properties.WxMaProperties;
+import lombok.AllArgsConstructor;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+
+/**
+ * 微信小程序平台相关服务自动注册.
+ *
+ * @author someone TaoYu
+ */
+@Configuration
+@AllArgsConstructor
+public class WxMaServiceAutoConfiguration {
+
+ private final WxMaProperties wxMaProperties;
+
+ /**
+ * 小程序service.
+ *
+ * @return 小程序service
+ */
+ @Bean
+ @Condition(onMissingBean=WxMaService.class, onBean=WxMaConfig.class)
+ public WxMaService wxMaService(WxMaConfig wxMaConfig) {
+ HttpClientType httpClientType = wxMaProperties.getConfigStorage().getHttpClientType();
+ WxMaService wxMaService;
+ switch (httpClientType) {
+ case OkHttp:
+ wxMaService = new WxMaServiceOkHttpImpl();
+ break;
+ case JoddHttp:
+ wxMaService = new WxMaServiceJoddHttpImpl();
+ break;
+ case HttpClient:
+ wxMaService = new WxMaServiceHttpClientImpl();
+ break;
+ default:
+ wxMaService = new WxMaServiceImpl();
+ break;
+ }
+ wxMaService.setWxMaConfig(wxMaConfig);
+ return wxMaService;
+ }
+}
diff --git a/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/config/storage/AbstractWxMaConfigStorageConfiguration.java b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/config/storage/AbstractWxMaConfigStorageConfiguration.java
new file mode 100644
index 0000000000..9cc4fe161b
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/config/storage/AbstractWxMaConfigStorageConfiguration.java
@@ -0,0 +1,39 @@
+package com.binarywang.solon.wxjava.miniapp.config.storage;
+
+import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
+import com.binarywang.solon.wxjava.miniapp.properties.WxMaProperties;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * @author yl TaoYu
+ */
+public abstract class AbstractWxMaConfigStorageConfiguration {
+
+ protected WxMaDefaultConfigImpl config(WxMaDefaultConfigImpl config, WxMaProperties properties) {
+ config.setAppid(StringUtils.trimToNull(properties.getAppid()));
+ config.setSecret(StringUtils.trimToNull(properties.getSecret()));
+ config.setToken(StringUtils.trimToNull(properties.getToken()));
+ config.setAesKey(StringUtils.trimToNull(properties.getAesKey()));
+ config.setMsgDataFormat(StringUtils.trimToNull(properties.getMsgDataFormat()));
+
+ WxMaProperties.ConfigStorage configStorageProperties = properties.getConfigStorage();
+ config.setHttpProxyHost(configStorageProperties.getHttpProxyHost());
+ config.setHttpProxyUsername(configStorageProperties.getHttpProxyUsername());
+ config.setHttpProxyPassword(configStorageProperties.getHttpProxyPassword());
+ if (configStorageProperties.getHttpProxyPort() != null) {
+ config.setHttpProxyPort(configStorageProperties.getHttpProxyPort());
+ }
+
+ int maxRetryTimes = configStorageProperties.getMaxRetryTimes();
+ if (configStorageProperties.getMaxRetryTimes() < 0) {
+ maxRetryTimes = 0;
+ }
+ int retrySleepMillis = configStorageProperties.getRetrySleepMillis();
+ if (retrySleepMillis < 0) {
+ retrySleepMillis = 1000;
+ }
+ config.setRetrySleepMillis(retrySleepMillis);
+ config.setMaxRetryTimes(maxRetryTimes);
+ return config;
+ }
+}
diff --git a/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/config/storage/WxMaInJedisConfigStorageConfiguration.java b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/config/storage/WxMaInJedisConfigStorageConfiguration.java
new file mode 100644
index 0000000000..da8c4701ba
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/config/storage/WxMaInJedisConfigStorageConfiguration.java
@@ -0,0 +1,72 @@
+package com.binarywang.solon.wxjava.miniapp.config.storage;
+
+import cn.binarywang.wx.miniapp.config.WxMaConfig;
+import cn.binarywang.wx.miniapp.config.impl.WxMaRedisBetterConfigImpl;
+import com.binarywang.solon.wxjava.miniapp.properties.RedisProperties;
+import com.binarywang.solon.wxjava.miniapp.properties.WxMaProperties;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.common.redis.JedisWxRedisOps;
+import me.chanjar.weixin.common.redis.WxRedisOps;
+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;
+
+/**
+ * @author yl TaoYu
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxMaProperties.PREFIX + ".configStorage.type} = jedis",
+ onClass = JedisPool.class
+)
+@RequiredArgsConstructor
+public class WxMaInJedisConfigStorageConfiguration extends AbstractWxMaConfigStorageConfiguration {
+ private final WxMaProperties properties;
+ private final AppContext applicationContext;
+
+ @Bean
+ @Condition(onMissingBean=WxMaConfig.class)
+ public WxMaConfig wxMaConfig() {
+ WxMaRedisBetterConfigImpl config = getWxMaRedisBetterConfigImpl();
+ return this.config(config, properties);
+ }
+
+ private WxMaRedisBetterConfigImpl getWxMaRedisBetterConfigImpl() {
+ RedisProperties redisProperties = properties.getConfigStorage().getRedis();
+ JedisPool jedisPool;
+ if (redisProperties != null && StringUtils.isNotEmpty(redisProperties.getHost())) {
+ jedisPool = getJedisPool();
+ } else {
+ jedisPool = applicationContext.getBean(JedisPool.class);
+ }
+ WxRedisOps redisOps = new JedisWxRedisOps(jedisPool);
+ return new WxMaRedisBetterConfigImpl(redisOps, properties.getConfigStorage().getKeyPrefix());
+ }
+
+ private JedisPool getJedisPool() {
+ WxMaProperties.ConfigStorage storage = properties.getConfigStorage();
+ RedisProperties 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());
+ }
+}
diff --git a/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/config/storage/WxMaInMemoryConfigStorageConfiguration.java b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/config/storage/WxMaInMemoryConfigStorageConfiguration.java
new file mode 100644
index 0000000000..958742d2aa
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/config/storage/WxMaInMemoryConfigStorageConfiguration.java
@@ -0,0 +1,28 @@
+package com.binarywang.solon.wxjava.miniapp.config.storage;
+
+import cn.binarywang.wx.miniapp.config.WxMaConfig;
+import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
+import com.binarywang.solon.wxjava.miniapp.properties.WxMaProperties;
+import lombok.RequiredArgsConstructor;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+
+/**
+ * @author yl TaoYu
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxMaProperties.PREFIX + ".configStorage.type:memory} = memory"
+)
+@RequiredArgsConstructor
+public class WxMaInMemoryConfigStorageConfiguration extends AbstractWxMaConfigStorageConfiguration {
+ private final WxMaProperties properties;
+
+ @Bean
+ @Condition(onMissingBean=WxMaConfig.class)
+ public WxMaConfig wxMaConfig() {
+ WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
+ return this.config(config, properties);
+ }
+}
diff --git a/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/config/storage/WxMaInRedissonConfigStorageConfiguration.java b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/config/storage/WxMaInRedissonConfigStorageConfiguration.java
new file mode 100644
index 0000000000..af7c11448e
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/config/storage/WxMaInRedissonConfigStorageConfiguration.java
@@ -0,0 +1,61 @@
+package com.binarywang.solon.wxjava.miniapp.config.storage;
+
+import cn.binarywang.wx.miniapp.config.WxMaConfig;
+import cn.binarywang.wx.miniapp.config.impl.WxMaRedissonConfigImpl;
+import com.binarywang.solon.wxjava.miniapp.properties.RedisProperties;
+import com.binarywang.solon.wxjava.miniapp.properties.WxMaProperties;
+import lombok.RequiredArgsConstructor;
+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 org.redisson.Redisson;
+import org.redisson.api.RedissonClient;
+import org.redisson.config.Config;
+import org.redisson.config.TransportMode;
+
+/**
+ * @author yl TaoYu
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxMaProperties.PREFIX + ".configStorage.type} = redisson",
+ onClass = Redisson.class
+)
+@RequiredArgsConstructor
+public class WxMaInRedissonConfigStorageConfiguration extends AbstractWxMaConfigStorageConfiguration {
+ private final WxMaProperties properties;
+ private final AppContext applicationContext;
+
+ @Bean
+ @Condition(onMissingBean=WxMaConfig.class)
+ public WxMaConfig wxMaConfig() {
+ WxMaRedissonConfigImpl config = getWxMaInRedissonConfigStorage();
+ return this.config(config, properties);
+ }
+
+ private WxMaRedissonConfigImpl getWxMaInRedissonConfigStorage() {
+ RedisProperties redisProperties = properties.getConfigStorage().getRedis();
+ RedissonClient redissonClient;
+ if (redisProperties != null && StringUtils.isNotEmpty(redisProperties.getHost())) {
+ redissonClient = getRedissonClient();
+ } else {
+ redissonClient = applicationContext.getBean(RedissonClient.class);
+ }
+ return new WxMaRedissonConfigImpl(redissonClient, properties.getConfigStorage().getKeyPrefix());
+ }
+
+ private RedissonClient getRedissonClient() {
+ WxMaProperties.ConfigStorage storage = properties.getConfigStorage();
+ RedisProperties redis = storage.getRedis();
+
+ Config config = new Config();
+ config.useSingleServer()
+ .setAddress("redis://" + redis.getHost() + ":" + redis.getPort())
+ .setDatabase(redis.getDatabase())
+ .setPassword(redis.getPassword());
+ config.setTransportMode(TransportMode.NIO);
+ return Redisson.create(config);
+ }
+}
diff --git a/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/enums/HttpClientType.java b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/enums/HttpClientType.java
new file mode 100644
index 0000000000..a4475a02c7
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/enums/HttpClientType.java
@@ -0,0 +1,22 @@
+package com.binarywang.solon.wxjava.miniapp.enums;
+
+/**
+ * httpclient类型.
+ *
+ * @author Binary Wang
+ * created on 2020-05-25
+ */
+public enum HttpClientType {
+ /**
+ * HttpClient.
+ */
+ HttpClient,
+ /**
+ * OkHttp.
+ */
+ OkHttp,
+ /**
+ * JoddHttp.
+ */
+ JoddHttp,
+}
diff --git a/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/enums/StorageType.java b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/enums/StorageType.java
new file mode 100644
index 0000000000..b82261ba8a
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/enums/StorageType.java
@@ -0,0 +1,26 @@
+package com.binarywang.solon.wxjava.miniapp.enums;
+
+/**
+ * storage类型.
+ *
+ * @author Binary Wang
+ * created on 2020-05-25
+ */
+public enum StorageType {
+ /**
+ * 内存.
+ */
+ Memory,
+ /**
+ * redis(JedisClient).
+ */
+ Jedis,
+ /**
+ * redis(Redisson).
+ */
+ Redisson,
+ /**
+ * redis(RedisTemplate).
+ */
+ RedisTemplate
+}
diff --git a/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/integration/WxMiniappPluginImpl.java b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/integration/WxMiniappPluginImpl.java
new file mode 100644
index 0000000000..88d1c3023a
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/integration/WxMiniappPluginImpl.java
@@ -0,0 +1,25 @@
+package com.binarywang.solon.wxjava.miniapp.integration;
+
+import com.binarywang.solon.wxjava.miniapp.config.WxMaServiceAutoConfiguration;
+import com.binarywang.solon.wxjava.miniapp.config.storage.WxMaInJedisConfigStorageConfiguration;
+import com.binarywang.solon.wxjava.miniapp.config.storage.WxMaInMemoryConfigStorageConfiguration;
+import com.binarywang.solon.wxjava.miniapp.config.storage.WxMaInRedissonConfigStorageConfiguration;
+import com.binarywang.solon.wxjava.miniapp.properties.WxMaProperties;
+import org.noear.solon.core.AppContext;
+import org.noear.solon.core.Plugin;
+
+/**
+ * @author noear 2024/9/2 created
+ */
+public class WxMiniappPluginImpl implements Plugin {
+ @Override
+ public void start(AppContext context) throws Throwable {
+ context.beanMake(WxMaProperties.class);
+
+ context.beanMake(WxMaServiceAutoConfiguration.class);
+
+ context.beanMake(WxMaInMemoryConfigStorageConfiguration.class);
+ context.beanMake(WxMaInJedisConfigStorageConfiguration.class);
+ context.beanMake(WxMaInRedissonConfigStorageConfiguration.class);
+ }
+}
diff --git a/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/properties/RedisProperties.java b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/properties/RedisProperties.java
new file mode 100644
index 0000000000..021a4b1b6b
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/properties/RedisProperties.java
@@ -0,0 +1,43 @@
+package com.binarywang.solon.wxjava.miniapp.properties;
+
+import lombok.Data;
+
+/**
+ * redis 配置.
+ *
+ * @author Binary Wang
+ * created on 2020-08-30
+ */
+@Data
+public class RedisProperties {
+
+ /**
+ * 主机地址.不填则从solon容器内获取JedisPool
+ */
+ private String host;
+
+ /**
+ * 端口号.
+ */
+ private int port = 6379;
+
+ /**
+ * 密码.
+ */
+ private String password;
+
+ /**
+ * 超时.
+ */
+ private int timeout = 2000;
+
+ /**
+ * 数据库.
+ */
+ private int database = 0;
+
+ private Integer maxActive;
+ private Integer maxIdle;
+ private Integer maxWaitMillis;
+ private Integer minIdle;
+}
diff --git a/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/properties/WxMaProperties.java b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/properties/WxMaProperties.java
new file mode 100644
index 0000000000..5a993cf667
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/properties/WxMaProperties.java
@@ -0,0 +1,112 @@
+package com.binarywang.solon.wxjava.miniapp.properties;
+
+import com.binarywang.solon.wxjava.miniapp.enums.HttpClientType;
+import com.binarywang.solon.wxjava.miniapp.enums.StorageType;
+import lombok.Data;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.annotation.Inject;
+
+import static com.binarywang.solon.wxjava.miniapp.properties.WxMaProperties.PREFIX;
+
+/**
+ * 属性配置类.
+ *
+ * @author Binary Wang
+ * created on 2019-08-10
+ */
+@Data
+@Configuration
+@Inject("${" + PREFIX + "}")
+public class WxMaProperties {
+ public static final String PREFIX = "wx.miniapp";
+
+ /**
+ * 设置微信小程序的appid.
+ */
+ private String appid;
+
+ /**
+ * 设置微信小程序的Secret.
+ */
+ private String secret;
+
+ /**
+ * 设置微信小程序消息服务器配置的token.
+ */
+ private String token;
+
+ /**
+ * 设置微信小程序消息服务器配置的EncodingAESKey.
+ */
+ private String aesKey;
+
+ /**
+ * 消息格式,XML或者JSON.
+ */
+ private String msgDataFormat;
+
+ /**
+ * 存储策略
+ */
+ private final ConfigStorage configStorage = new ConfigStorage();
+
+ @Data
+ public static class ConfigStorage {
+
+ /**
+ * 存储类型.
+ */
+ private StorageType type = StorageType.Memory;
+
+ /**
+ * 指定key前缀.
+ */
+ private String keyPrefix = "wa";
+
+ /**
+ * redis连接配置.
+ */
+ private final RedisProperties redis = new RedisProperties();
+
+ /**
+ * http客户端类型.
+ */
+ private HttpClientType httpClientType = HttpClientType.HttpClient;
+
+ /**
+ * http代理主机.
+ */
+ private String httpProxyHost;
+
+ /**
+ * http代理端口.
+ */
+ private Integer httpProxyPort;
+
+ /**
+ * http代理用户名.
+ */
+ private String httpProxyUsername;
+
+ /**
+ * http代理密码.
+ */
+ private String httpProxyPassword;
+
+ /**
+ * http 请求重试间隔
+ *
+ * {@link cn.binarywang.wx.miniapp.api.impl.BaseWxMaServiceImpl#setRetrySleepMillis(int)}
+ *
+ */
+ private int retrySleepMillis = 1000;
+ /**
+ * http 请求最大重试次数
+ *
+ * {@link cn.binarywang.wx.miniapp.api.impl.BaseWxMaServiceImpl#setMaxRetryTimes(int)}
+ *
+ */
+ private int maxRetryTimes = 5;
+ }
+
+}
diff --git a/solon-plugins/wx-java-miniapp-solon-plugin/src/main/resources/META-INF/solon/wx-java-miniapp-solon-plugin.properties b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/resources/META-INF/solon/wx-java-miniapp-solon-plugin.properties
new file mode 100644
index 0000000000..ba1049647e
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/resources/META-INF/solon/wx-java-miniapp-solon-plugin.properties
@@ -0,0 +1,2 @@
+solon.plugin=com.binarywang.solon.wxjava.miniapp.integration.WxMiniappPluginImpl
+solon.plugin.priority=10
diff --git a/solon-plugins/wx-java-mp-multi-solon-plugin/README.md b/solon-plugins/wx-java-mp-multi-solon-plugin/README.md
new file mode 100644
index 0000000000..0d2b332d5a
--- /dev/null
+++ b/solon-plugins/wx-java-mp-multi-solon-plugin/README.md
@@ -0,0 +1,100 @@
+# wx-java-mp-multi-solon-plugin
+
+## 快速开始
+
+1. 引入依赖
+ ```xml
+
+ com.github.binarywang
+ wx-java-mp-multi-solon-plugin
+ ${version}
+
+ ```
+2. 添加配置(app.properties)
+ ```properties
+ # 公众号配置
+ ## 应用 1 配置(必填)
+ wx.mp.apps.tenantId1.app-id=appId
+ wx.mp.apps.tenantId1.app-secret=@secret
+ ## 选填
+ wx.mp.apps.tenantId1.token=@token
+ wx.mp.apps.tenantId1.aes-key=@aesKey
+ wx.mp.apps.tenantId1.use-stable-access-token=@useStableAccessToken
+ ## 应用 2 配置(必填)
+ wx.mp.apps.tenantId2.app-id=@appId
+ wx.mp.apps.tenantId2.app-secret =@secret
+ ## 选填
+ wx.mp.apps.tenantId2.token=@token
+ wx.mp.apps.tenantId2.aes-key=@aesKey
+ wx.mp.apps.tenantId2.use-stable-access-token=@useStableAccessToken
+
+ # ConfigStorage 配置(选填)
+ ## 配置类型: memory(默认), jedis, redisson, redis_template
+ wx.mp.config-storage.type=memory
+ ## 相关redis前缀配置: wx:mp:multi(默认)
+ wx.mp.config-storage.key-prefix=wx:mp:multi
+ wx.mp.config-storage.redis.host=127.0.0.1
+ wx.mp.config-storage.redis.port=6379
+ ## 单机和 sentinel 同时存在时,优先使用sentinel配置
+ # wx.mp.config-storage.redis.sentinel-ips=127.0.0.1:16379,127.0.0.1:26379
+ # wx.mp.config-storage.redis.sentinel-name=mymaster
+
+ # http 客户端配置(选填)
+ ## # http客户端类型: http_client(默认), ok_http, jodd_http
+ wx.mp.config-storage.http-client-type=http_client
+ wx.mp.config-storage.http-proxy-host=
+ wx.mp.config-storage.http-proxy-port=
+ wx.mp.config-storage.http-proxy-username=
+ wx.mp.config-storage.http-proxy-password=
+ ## 最大重试次数,默认:5 次,如果小于 0,则为 0
+ wx.mp.config-storage.max-retry-times=5
+ ## 重试时间间隔步进,默认:1000 毫秒,如果小于 0,则为 1000
+ wx.mp.config-storage.retry-sleep-millis=1000
+
+ # 公众号地址 host 配置
+ # wx.mp.hosts.api-host=http://proxy.com/
+ # wx.mp.hosts.open-host=http://proxy.com/
+ # wx.mp.hosts.mp-host=http://proxy.com/
+ ```
+3. 自动注入的类型:`WxMpMultiServices`
+
+4. 使用样例
+
+```java
+import com.binarywang.solon.wxjava.mp_multi.service.WxMpMultiServices;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.api.WxMpUserService;
+import org.noear.solon.annotation.Component;
+import org.noear.solon.annotation.Inject;
+
+@Component
+public class DemoService {
+ @Inject
+ private WxMpMultiServices wxMpMultiServices;
+
+ public void test() {
+ // 应用 1 的 WxMpService
+ WxMpService wxMpService1 = wxMpMultiServices.getWxMpService("tenantId1");
+ WxMpUserService userService1 = wxMpService1.getUserService();
+ userService1.userInfo("xxx");
+ // todo ...
+
+ // 应用 2 的 WxMpService
+ WxMpService wxMpService2 = wxMpMultiServices.getWxMpService("tenantId2");
+ WxMpUserService userService2 = wxMpService2.getUserService();
+ userService2.userInfo("xxx");
+ // todo ...
+
+ // 应用 3 的 WxMpService
+ WxMpService wxMpService3 = wxMpMultiServices.getWxMpService("tenantId3");
+ // 判断是否为空
+ if (wxMpService3 == null) {
+ // todo wxMpService3 为空,请先配置 tenantId3 微信公众号应用参数
+ return;
+ }
+ WxMpUserService userService3 = wxMpService3.getUserService();
+ userService3.userInfo("xxx");
+ // todo ...
+ }
+}
+```
diff --git a/solon-plugins/wx-java-mp-multi-solon-plugin/pom.xml b/solon-plugins/wx-java-mp-multi-solon-plugin/pom.xml
new file mode 100644
index 0000000000..197561e68b
--- /dev/null
+++ b/solon-plugins/wx-java-mp-multi-solon-plugin/pom.xml
@@ -0,0 +1,44 @@
+
+
+
+ wx-java-solon-plugins
+ com.github.binarywang
+ 4.6.4.B
+
+ 4.0.0
+
+ wx-java-mp-multi-solon-plugin
+ WxJava - Solon Plugin for MP::支持多账号配置
+ 微信公众号开发的 Solon Plugin::支持多账号配置
+
+
+
+ com.github.binarywang
+ weixin-java-mp
+ ${project.version}
+
+
+ redis.clients
+ jedis
+ provided
+
+
+ org.redisson
+ redisson
+ provided
+
+
+ org.jodd
+ jodd-http
+ provided
+
+
+ com.squareup.okhttp3
+ okhttp
+ provided
+
+
+
+
diff --git a/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/configuration/services/AbstractWxMpConfiguration.java b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/configuration/services/AbstractWxMpConfiguration.java
new file mode 100644
index 0000000000..d534b98746
--- /dev/null
+++ b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/configuration/services/AbstractWxMpConfiguration.java
@@ -0,0 +1,165 @@
+package com.binarywang.solon.wxjava.mp_multi.configuration.services;
+
+import com.binarywang.solon.wxjava.mp_multi.properties.WxMpMultiProperties;
+import com.binarywang.solon.wxjava.mp_multi.properties.WxMpSingleProperties;
+import com.binarywang.solon.wxjava.mp_multi.service.WxMpMultiServices;
+import com.binarywang.solon.wxjava.mp_multi.service.WxMpMultiServicesImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.api.impl.WxMpServiceHttpClientImpl;
+import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
+import me.chanjar.weixin.mp.api.impl.WxMpServiceJoddHttpImpl;
+import me.chanjar.weixin.mp.api.impl.WxMpServiceOkHttpImpl;
+import me.chanjar.weixin.mp.config.WxMpConfigStorage;
+import me.chanjar.weixin.mp.config.WxMpHostConfig;
+import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * WxMpConfigStorage 抽象配置类
+ *
+ * @author yl
+ * created on 2024/1/23
+ */
+@RequiredArgsConstructor
+@Slf4j
+public abstract class AbstractWxMpConfiguration {
+
+ protected WxMpMultiServices wxMpMultiServices(WxMpMultiProperties wxCpMultiProperties) {
+ Map appsMap = wxCpMultiProperties.getApps();
+ if (appsMap == null || appsMap.isEmpty()) {
+ log.warn("微信公众号应用参数未配置,通过 WxMpMultiServices#getWxMpService(\"tenantId\")获取实例将返回空");
+ return new WxMpMultiServicesImpl();
+ }
+ /**
+ * 校验 appId 是否唯一,避免使用 redis 缓存 token、ticket 时错乱。
+ *
+ * 查看 {@link me.chanjar.weixin.mp.config.impl.WxMpRedisConfigImpl#setAppId(String)}
+ */
+ Collection 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 的唯一性");
+ }
+ }
+ WxMpMultiServicesImpl services = new WxMpMultiServicesImpl();
+
+ Set> entries = appsMap.entrySet();
+ for (Map.Entry entry : entries) {
+ String tenantId = entry.getKey();
+ WxMpSingleProperties wxMpSingleProperties = entry.getValue();
+ WxMpDefaultConfigImpl storage = this.wxMpConfigStorage(wxCpMultiProperties);
+ this.configApp(storage, wxMpSingleProperties);
+ this.configHttp(storage, wxCpMultiProperties.getConfigStorage());
+ this.configHost(storage, wxCpMultiProperties.getHosts());
+ WxMpService wxCpService = this.wxMpService(storage, wxCpMultiProperties);
+ services.addWxMpService(tenantId, wxCpService);
+ }
+ return services;
+ }
+
+ /**
+ * 配置 WxMpDefaultConfigImpl
+ *
+ * @param wxMpMultiProperties 参数
+ * @return WxMpDefaultConfigImpl
+ */
+ protected abstract WxMpDefaultConfigImpl wxMpConfigStorage(WxMpMultiProperties wxMpMultiProperties);
+
+ public WxMpService wxMpService(WxMpConfigStorage configStorage, WxMpMultiProperties wxMpMultiProperties) {
+ WxMpMultiProperties.ConfigStorage storage = wxMpMultiProperties.getConfigStorage();
+ WxMpMultiProperties.HttpClientType httpClientType = storage.getHttpClientType();
+ WxMpService wxMpService;
+ switch (httpClientType) {
+ case OK_HTTP:
+ wxMpService = new WxMpServiceOkHttpImpl();
+ break;
+ case JODD_HTTP:
+ wxMpService = new WxMpServiceJoddHttpImpl();
+ break;
+ case HTTP_CLIENT:
+ wxMpService = new WxMpServiceHttpClientImpl();
+ break;
+ default:
+ wxMpService = new WxMpServiceImpl();
+ break;
+ }
+
+ wxMpService.setWxMpConfigStorage(configStorage);
+ int maxRetryTimes = storage.getMaxRetryTimes();
+ if (maxRetryTimes < 0) {
+ maxRetryTimes = 0;
+ }
+ int retrySleepMillis = storage.getRetrySleepMillis();
+ if (retrySleepMillis < 0) {
+ retrySleepMillis = 1000;
+ }
+ wxMpService.setRetrySleepMillis(retrySleepMillis);
+ wxMpService.setMaxRetryTimes(maxRetryTimes);
+ return wxMpService;
+ }
+
+ private void configApp(WxMpDefaultConfigImpl config, WxMpSingleProperties corpProperties) {
+ String appId = corpProperties.getAppId();
+ String appSecret = corpProperties.getAppSecret();
+ String token = corpProperties.getToken();
+ String aesKey = corpProperties.getAesKey();
+ boolean useStableAccessToken = corpProperties.isUseStableAccessToken();
+
+ config.setAppId(appId);
+ config.setSecret(appSecret);
+ if (StringUtils.isNotBlank(token)) {
+ config.setToken(token);
+ }
+ if (StringUtils.isNotBlank(aesKey)) {
+ config.setAesKey(aesKey);
+ }
+ config.setUseStableAccessToken(useStableAccessToken);
+ }
+
+ private void configHttp(WxMpDefaultConfigImpl config, WxMpMultiProperties.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);
+ }
+ }
+ }
+
+ /**
+ * wx host config
+ */
+ private void configHost(WxMpDefaultConfigImpl config, WxMpMultiProperties.HostConfig hostConfig) {
+ if (hostConfig != null) {
+ String apiHost = hostConfig.getApiHost();
+ String mpHost = hostConfig.getMpHost();
+ String openHost = hostConfig.getOpenHost();
+ WxMpHostConfig wxMpHostConfig = new WxMpHostConfig();
+ wxMpHostConfig.setApiHost(StringUtils.isNotBlank(apiHost) ? apiHost : null);
+ wxMpHostConfig.setMpHost(StringUtils.isNotBlank(mpHost) ? mpHost : null);
+ wxMpHostConfig.setOpenHost(StringUtils.isNotBlank(openHost) ? openHost : null);
+ config.setHostConfig(wxMpHostConfig);
+ }
+ }
+}
diff --git a/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/configuration/services/WxMpInJedisConfiguration.java b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/configuration/services/WxMpInJedisConfiguration.java
new file mode 100644
index 0000000000..c00898a82d
--- /dev/null
+++ b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/configuration/services/WxMpInJedisConfiguration.java
@@ -0,0 +1,78 @@
+package com.binarywang.solon.wxjava.mp_multi.configuration.services;
+
+import com.binarywang.solon.wxjava.mp_multi.properties.WxMpMultiProperties;
+import com.binarywang.solon.wxjava.mp_multi.properties.WxMpMultiRedisProperties;
+import com.binarywang.solon.wxjava.mp_multi.service.WxMpMultiServices;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.common.redis.JedisWxRedisOps;
+import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
+import me.chanjar.weixin.mp.config.impl.WxMpRedisConfigImpl;
+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 yl
+ * created on 2024/1/23
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxMpMultiProperties.PREFIX + ".configStorage.type} = jedis",
+ onClass = JedisPool.class
+)
+@RequiredArgsConstructor
+public class WxMpInJedisConfiguration extends AbstractWxMpConfiguration {
+ private final WxMpMultiProperties wxCpMultiProperties;
+ private final AppContext applicationContext;
+
+ @Bean
+ public WxMpMultiServices wxMpMultiServices() {
+ return this.wxMpMultiServices(wxCpMultiProperties);
+ }
+
+ @Override
+ protected WxMpDefaultConfigImpl wxMpConfigStorage(WxMpMultiProperties wxCpMultiProperties) {
+ return this.configRedis(wxCpMultiProperties);
+ }
+
+ private WxMpDefaultConfigImpl configRedis(WxMpMultiProperties wxCpMultiProperties) {
+ WxMpMultiRedisProperties wxCpMultiRedisProperties = wxCpMultiProperties.getConfigStorage().getRedis();
+ JedisPool jedisPool;
+ if (wxCpMultiRedisProperties != null && StringUtils.isNotEmpty(wxCpMultiRedisProperties.getHost())) {
+ jedisPool = getJedisPool(wxCpMultiProperties);
+ } else {
+ jedisPool = applicationContext.getBean(JedisPool.class);
+ }
+ return new WxMpRedisConfigImpl(new JedisWxRedisOps(jedisPool), wxCpMultiProperties.getConfigStorage().getKeyPrefix());
+ }
+
+ private JedisPool getJedisPool(WxMpMultiProperties wxCpMultiProperties) {
+ WxMpMultiProperties.ConfigStorage storage = wxCpMultiProperties.getConfigStorage();
+ WxMpMultiRedisProperties 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());
+ }
+}
diff --git a/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/configuration/services/WxMpInMemoryConfiguration.java b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/configuration/services/WxMpInMemoryConfiguration.java
new file mode 100644
index 0000000000..74bc13e03e
--- /dev/null
+++ b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/configuration/services/WxMpInMemoryConfiguration.java
@@ -0,0 +1,40 @@
+package com.binarywang.solon.wxjava.mp_multi.configuration.services;
+
+import com.binarywang.solon.wxjava.mp_multi.properties.WxMpMultiProperties;
+import com.binarywang.solon.wxjava.mp_multi.service.WxMpMultiServices;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
+import me.chanjar.weixin.mp.config.impl.WxMpMapConfigImpl;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+
+/**
+ * 自动装配基于内存策略配置
+ *
+ * @author yl
+ * created on 2024/1/23
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxMpMultiProperties.PREFIX + ".configStorage.type:memory} = memory"
+)
+@RequiredArgsConstructor
+public class WxMpInMemoryConfiguration extends AbstractWxMpConfiguration {
+ private final WxMpMultiProperties wxCpMultiProperties;
+
+ @Bean
+ public WxMpMultiServices wxCpMultiServices() {
+ return this.wxMpMultiServices(wxCpMultiProperties);
+ }
+
+ @Override
+ protected WxMpDefaultConfigImpl wxMpConfigStorage(WxMpMultiProperties wxCpMultiProperties) {
+ return this.configInMemory();
+ }
+
+ private WxMpDefaultConfigImpl configInMemory() {
+ return new WxMpMapConfigImpl();
+ // return new WxMpDefaultConfigImpl();
+ }
+}
diff --git a/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/configuration/services/WxMpInRedissonConfiguration.java b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/configuration/services/WxMpInRedissonConfiguration.java
new file mode 100644
index 0000000000..89ffdfd912
--- /dev/null
+++ b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/configuration/services/WxMpInRedissonConfiguration.java
@@ -0,0 +1,68 @@
+package com.binarywang.solon.wxjava.mp_multi.configuration.services;
+
+import com.binarywang.solon.wxjava.mp_multi.properties.WxMpMultiProperties;
+import com.binarywang.solon.wxjava.mp_multi.properties.WxMpMultiRedisProperties;
+import com.binarywang.solon.wxjava.mp_multi.service.WxMpMultiServices;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
+import me.chanjar.weixin.mp.config.impl.WxMpRedissonConfigImpl;
+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 org.redisson.Redisson;
+import org.redisson.api.RedissonClient;
+import org.redisson.config.Config;
+import org.redisson.config.TransportMode;
+
+/**
+ * 自动装配基于 redisson 策略配置
+ *
+ * @author yl
+ * created on 2024/1/23
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxMpMultiProperties.PREFIX + ".configStorage.type} = redisson",
+ onClass = Redisson.class
+)
+@RequiredArgsConstructor
+public class WxMpInRedissonConfiguration extends AbstractWxMpConfiguration {
+ private final WxMpMultiProperties wxCpMultiProperties;
+ private final AppContext applicationContext;
+
+ @Bean
+ public WxMpMultiServices wxMpMultiServices() {
+ return this.wxMpMultiServices(wxCpMultiProperties);
+ }
+
+ @Override
+ protected WxMpDefaultConfigImpl wxMpConfigStorage(WxMpMultiProperties wxCpMultiProperties) {
+ return this.configRedisson(wxCpMultiProperties);
+ }
+
+ private WxMpDefaultConfigImpl configRedisson(WxMpMultiProperties wxCpMultiProperties) {
+ WxMpMultiRedisProperties redisProperties = wxCpMultiProperties.getConfigStorage().getRedis();
+ RedissonClient redissonClient;
+ if (redisProperties != null && StringUtils.isNotEmpty(redisProperties.getHost())) {
+ redissonClient = getRedissonClient(wxCpMultiProperties);
+ } else {
+ redissonClient = applicationContext.getBean(RedissonClient.class);
+ }
+ return new WxMpRedissonConfigImpl(redissonClient, wxCpMultiProperties.getConfigStorage().getKeyPrefix());
+ }
+
+ private RedissonClient getRedissonClient(WxMpMultiProperties wxCpMultiProperties) {
+ WxMpMultiProperties.ConfigStorage storage = wxCpMultiProperties.getConfigStorage();
+ WxMpMultiRedisProperties redis = storage.getRedis();
+
+ Config config = new Config();
+ config.useSingleServer()
+ .setAddress("redis://" + redis.getHost() + ":" + redis.getPort())
+ .setDatabase(redis.getDatabase())
+ .setPassword(redis.getPassword());
+ config.setTransportMode(TransportMode.NIO);
+ return Redisson.create(config);
+ }
+}
diff --git a/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/integration/WxMpMultiPluginImpl.java b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/integration/WxMpMultiPluginImpl.java
new file mode 100644
index 0000000000..3629a8f78f
--- /dev/null
+++ b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/integration/WxMpMultiPluginImpl.java
@@ -0,0 +1,23 @@
+package com.binarywang.solon.wxjava.mp_multi.integration;
+
+import com.binarywang.solon.wxjava.mp_multi.configuration.services.WxMpInJedisConfiguration;
+import com.binarywang.solon.wxjava.mp_multi.configuration.services.WxMpInMemoryConfiguration;
+import com.binarywang.solon.wxjava.mp_multi.configuration.services.WxMpInRedissonConfiguration;
+import com.binarywang.solon.wxjava.mp_multi.properties.WxMpMultiProperties;
+import org.noear.solon.core.AppContext;
+import org.noear.solon.core.Plugin;
+
+/**
+ * @author noear 2024/9/2 created
+ */
+public class WxMpMultiPluginImpl implements Plugin {
+ @Override
+ public void start(AppContext context) throws Throwable {
+ context.beanMake(WxMpMultiProperties.class);
+
+ context.beanMake(WxMpInJedisConfiguration.class);
+ context.beanMake(WxMpInMemoryConfiguration.class);
+ context.beanMake(WxMpInRedissonConfiguration.class);
+
+ }
+}
diff --git a/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/properties/WxMpMultiProperties.java b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/properties/WxMpMultiProperties.java
new file mode 100644
index 0000000000..1929e92607
--- /dev/null
+++ b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/properties/WxMpMultiProperties.java
@@ -0,0 +1,154 @@
+package com.binarywang.solon.wxjava.mp_multi.properties;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.annotation.Inject;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author yl
+ * created on 2024/1/23
+ */
+@Data
+@NoArgsConstructor
+@Configuration
+@Inject("${"+WxMpMultiProperties.PREFIX+"}")
+public class WxMpMultiProperties implements Serializable {
+ private static final long serialVersionUID = -5358245184407791011L;
+ public static final String PREFIX = "wx.mp";
+
+ private Map apps = new HashMap<>();
+
+ /**
+ * 自定义host配置
+ */
+ private HostConfig hosts;
+
+ /**
+ * 存储策略
+ */
+ private final ConfigStorage configStorage = new ConfigStorage();
+
+ @Data
+ @NoArgsConstructor
+ public static class HostConfig implements Serializable {
+ private static final long serialVersionUID = -4172767630740346001L;
+
+ /**
+ * 对应于:https://api.weixin.qq.com
+ */
+ private String apiHost;
+
+ /**
+ * 对应于:https://open.weixin.qq.com
+ */
+ private String openHost;
+
+ /**
+ * 对应于:https://mp.weixin.qq.com
+ */
+ private String mpHost;
+ }
+
+ @Data
+ @NoArgsConstructor
+ public static class ConfigStorage implements Serializable {
+ private static final long serialVersionUID = 4815731027000065434L;
+
+ /**
+ * 存储类型.
+ */
+ private StorageType type = StorageType.MEMORY;
+
+ /**
+ * 指定key前缀.
+ */
+ private String keyPrefix = "wx:mp:multi";
+
+ /**
+ * redis连接配置.
+ */
+ private final WxMpMultiRedisProperties redis = new WxMpMultiRedisProperties();
+
+ /**
+ * http客户端类型.
+ */
+ private HttpClientType httpClientType = HttpClientType.HTTP_CLIENT;
+
+ /**
+ * http代理主机.
+ */
+ private String httpProxyHost;
+
+ /**
+ * http代理端口.
+ */
+ private Integer httpProxyPort;
+
+ /**
+ * http代理用户名.
+ */
+ private String httpProxyUsername;
+
+ /**
+ * http代理密码.
+ */
+ private String httpProxyPassword;
+
+ /**
+ * http 请求最大重试次数
+ *
+ * {@link me.chanjar.weixin.mp.api.WxMpService#setMaxRetryTimes(int)}
+ * {@link me.chanjar.weixin.mp.api.impl.BaseWxMpServiceImpl#setMaxRetryTimes(int)}
+ *
+ */
+ private int maxRetryTimes = 5;
+
+ /**
+ * http 请求重试间隔
+ *
+ * {@link me.chanjar.weixin.mp.api.WxMpService#setRetrySleepMillis(int)}
+ * {@link me.chanjar.weixin.mp.api.impl.BaseWxMpServiceImpl#setRetrySleepMillis(int)}
+ *
+ */
+ private int retrySleepMillis = 1000;
+ }
+
+ public enum StorageType {
+ /**
+ * 内存
+ */
+ MEMORY,
+ /**
+ * jedis
+ */
+ JEDIS,
+ /**
+ * redisson
+ */
+ REDISSON,
+ /**
+ * redisTemplate
+ */
+ REDIS_TEMPLATE
+ }
+
+ public enum HttpClientType {
+ /**
+ * HttpClient
+ */
+ HTTP_CLIENT,
+ /**
+ * OkHttp
+ */
+ OK_HTTP,
+ /**
+ * JoddHttp
+ */
+ JODD_HTTP
+ }
+}
diff --git a/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/properties/WxMpMultiRedisProperties.java b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/properties/WxMpMultiRedisProperties.java
new file mode 100644
index 0000000000..12646d4eaf
--- /dev/null
+++ b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/properties/WxMpMultiRedisProperties.java
@@ -0,0 +1,56 @@
+package com.binarywang.solon.wxjava.mp_multi.properties;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * @author yl
+ * created on 2024/1/23
+ */
+@Data
+@NoArgsConstructor
+public class WxMpMultiRedisProperties implements Serializable {
+ private static final long serialVersionUID = -5924815351660074401L;
+
+ /**
+ * 主机地址.
+ */
+ private String host = "127.0.0.1";
+
+ /**
+ * 端口号.
+ */
+ private int port = 6379;
+
+ /**
+ * 密码.
+ */
+ private String password;
+
+ /**
+ * 超时.
+ */
+ private int timeout = 2000;
+
+ /**
+ * 数据库.
+ */
+ private int database = 0;
+
+ /**
+ * sentinel ips
+ */
+ private String sentinelIps;
+
+ /**
+ * sentinel name
+ */
+ private String sentinelName;
+
+ private Integer maxActive;
+ private Integer maxIdle;
+ private Integer maxWaitMillis;
+ private Integer minIdle;
+}
diff --git a/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/properties/WxMpSingleProperties.java b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/properties/WxMpSingleProperties.java
new file mode 100644
index 0000000000..22938cb67c
--- /dev/null
+++ b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/properties/WxMpSingleProperties.java
@@ -0,0 +1,40 @@
+package com.binarywang.solon.wxjava.mp_multi.properties;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * @author yl
+ * created on 2024/1/23
+ */
+@Data
+@NoArgsConstructor
+public class WxMpSingleProperties implements Serializable {
+ private static final long serialVersionUID = 1980986361098922525L;
+ /**
+ * 设置微信公众号的 appid.
+ */
+ private String appId;
+
+ /**
+ * 设置微信公众号的 app secret.
+ */
+ private String appSecret;
+
+ /**
+ * 设置微信公众号的 token.
+ */
+ private String token;
+
+ /**
+ * 设置微信公众号的 EncodingAESKey.
+ */
+ private String aesKey;
+
+ /**
+ * 是否使用稳定版 Access Token
+ */
+ private boolean useStableAccessToken = false;
+}
diff --git a/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/service/WxMpMultiServices.java b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/service/WxMpMultiServices.java
new file mode 100644
index 0000000000..a59b5962ad
--- /dev/null
+++ b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/service/WxMpMultiServices.java
@@ -0,0 +1,27 @@
+package com.binarywang.solon.wxjava.mp_multi.service;
+
+
+import me.chanjar.weixin.mp.api.WxMpService;
+
+/**
+ * 企业微信 {@link WxMpService} 所有实例存放类.
+ *
+ * @author yl
+ * created on 2024/1/23
+ */
+public interface WxMpMultiServices {
+ /**
+ * 通过租户 Id 获取 WxMpService
+ *
+ * @param tenantId 租户 Id
+ * @return WxMpService
+ */
+ WxMpService getWxMpService(String tenantId);
+
+ /**
+ * 根据租户 Id,从列表中移除一个 WxMpService 实例
+ *
+ * @param tenantId 租户 Id
+ */
+ void removeWxMpService(String tenantId);
+}
diff --git a/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/service/WxMpMultiServicesImpl.java b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/service/WxMpMultiServicesImpl.java
new file mode 100644
index 0000000000..d87cd4e8df
--- /dev/null
+++ b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/service/WxMpMultiServicesImpl.java
@@ -0,0 +1,36 @@
+package com.binarywang.solon.wxjava.mp_multi.service;
+
+import me.chanjar.weixin.mp.api.WxMpService;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 企业微信 {@link WxMpMultiServices} 默认实现
+ *
+ * @author yl
+ * created on 2024/1/23
+ */
+public class WxMpMultiServicesImpl implements WxMpMultiServices {
+ private final Map services = new ConcurrentHashMap<>();
+
+ @Override
+ public WxMpService getWxMpService(String tenantId) {
+ return this.services.get(tenantId);
+ }
+
+ /**
+ * 根据租户 Id,添加一个 WxMpService 到列表
+ *
+ * @param tenantId 租户 Id
+ * @param wxMpService WxMpService 实例
+ */
+ public void addWxMpService(String tenantId, WxMpService wxMpService) {
+ this.services.put(tenantId, wxMpService);
+ }
+
+ @Override
+ public void removeWxMpService(String tenantId) {
+ this.services.remove(tenantId);
+ }
+}
diff --git a/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/resources/META-INF/solon/wx-java-mp-multi-solon-plugin.properties b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/resources/META-INF/solon/wx-java-mp-multi-solon-plugin.properties
new file mode 100644
index 0000000000..11c68ccc81
--- /dev/null
+++ b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/resources/META-INF/solon/wx-java-mp-multi-solon-plugin.properties
@@ -0,0 +1,2 @@
+solon.plugin=com.binarywang.solon.wxjava.mp_multi.integration.WxMpMultiPluginImpl
+solon.plugin.priority=10
diff --git a/solon-plugins/wx-java-mp-solon-plugin/README.md b/solon-plugins/wx-java-mp-solon-plugin/README.md
new file mode 100644
index 0000000000..e5d7d10e25
--- /dev/null
+++ b/solon-plugins/wx-java-mp-solon-plugin/README.md
@@ -0,0 +1,46 @@
+# wx-java-mp-solon-plugin
+
+## 快速开始
+
+1. 引入依赖
+ ```xml
+
+ com.github.binarywang
+ wx-java-mp-solon-plugin
+ ${version}
+
+ ```
+2. 添加配置(app.properties)
+ ```properties
+ # 公众号配置(必填)
+ wx.mp.app-id=appId
+ wx.mp.secret=@secret
+ wx.mp.token=@token
+ wx.mp.aes-key=@aesKey
+ wx.mp.use-stable-access-token=@useStableAccessToken
+ # 存储配置redis(可选)
+ wx.mp.config-storage.type= edis # 配置类型: Memory(默认), Jedis, RedisTemplate
+ wx.mp.config-storage.key-prefix=wx # 相关redis前缀配置: wx(默认)
+ wx.mp.config-storage.redis.host=127.0.0.1
+ wx.mp.config-storage.redis.port=6379
+ #单机和sentinel同时存在时,优先使用sentinel配置
+ #wx.mp.config-storage.redis.sentinel-ips=127.0.0.1:16379,127.0.0.1:26379
+ #wx.mp.config-storage.redis.sentinel-name=mymaster
+ # http客户端配置
+ wx.mp.config-storage.http-client-type=httpclient # http客户端类型: HttpClient(默认), OkHttp, JoddHttp
+ wx.mp.config-storage.http-proxy-host=
+ wx.mp.config-storage.http-proxy-port=
+ wx.mp.config-storage.http-proxy-username=
+ wx.mp.config-storage.http-proxy-password=
+ # 公众号地址host配置
+ #wx.mp.hosts.api-host=http://proxy.com/
+ #wx.mp.hosts.open-host=http://proxy.com/
+ #wx.mp.hosts.mp-host=http://proxy.com/
+ ```
+3. 自动注入的类型
+
+- `WxMpService`
+- `WxMpConfigStorage`
+
+4、参考demo:
+https://github.com/binarywang/wx-java-mp-demo
diff --git a/solon-plugins/wx-java-mp-solon-plugin/pom.xml b/solon-plugins/wx-java-mp-solon-plugin/pom.xml
new file mode 100644
index 0000000000..16aac18a57
--- /dev/null
+++ b/solon-plugins/wx-java-mp-solon-plugin/pom.xml
@@ -0,0 +1,39 @@
+
+
+
+ wx-java-solon-plugins
+ com.github.binarywang
+ 4.6.4.B
+
+ 4.0.0
+
+ wx-java-mp-solon-plugin
+ WxJava - Solon Plugin for MP
+ 微信公众号开发的 Solon Plugin
+
+
+
+ com.github.binarywang
+ weixin-java-mp
+ ${project.version}
+
+
+ redis.clients
+ jedis
+ compile
+
+
+ org.jodd
+ jodd-http
+ provided
+
+
+ com.squareup.okhttp3
+ okhttp
+ provided
+
+
+
+
diff --git a/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/config/WxMpServiceAutoConfiguration.java b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/config/WxMpServiceAutoConfiguration.java
new file mode 100644
index 0000000000..3e7a598494
--- /dev/null
+++ b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/config/WxMpServiceAutoConfiguration.java
@@ -0,0 +1,63 @@
+package com.binarywang.solon.wxjava.mp.config;
+
+import com.binarywang.solon.wxjava.mp.enums.HttpClientType;
+import com.binarywang.solon.wxjava.mp.properties.WxMpProperties;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.api.impl.WxMpServiceHttpClientImpl;
+import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
+import me.chanjar.weixin.mp.api.impl.WxMpServiceJoddHttpImpl;
+import me.chanjar.weixin.mp.api.impl.WxMpServiceOkHttpImpl;
+import me.chanjar.weixin.mp.config.WxMpConfigStorage;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+
+/**
+ * 微信公众号相关服务自动注册.
+ *
+ * @author someone
+ */
+@Configuration
+public class WxMpServiceAutoConfiguration {
+
+ @Bean
+ @Condition(onMissingBean = WxMpService.class)
+ public WxMpService wxMpService(WxMpConfigStorage configStorage, WxMpProperties wxMpProperties) {
+ HttpClientType httpClientType = wxMpProperties.getConfigStorage().getHttpClientType();
+ WxMpService wxMpService;
+ switch (httpClientType) {
+ case OkHttp:
+ wxMpService = newWxMpServiceOkHttpImpl();
+ break;
+ case JoddHttp:
+ wxMpService = newWxMpServiceJoddHttpImpl();
+ break;
+ case HttpClient:
+ wxMpService = newWxMpServiceHttpClientImpl();
+ break;
+ default:
+ wxMpService = newWxMpServiceImpl();
+ break;
+ }
+
+ wxMpService.setWxMpConfigStorage(configStorage);
+ return wxMpService;
+ }
+
+ private WxMpService newWxMpServiceImpl() {
+ return new WxMpServiceImpl();
+ }
+
+ private WxMpService newWxMpServiceHttpClientImpl() {
+ return new WxMpServiceHttpClientImpl();
+ }
+
+ private WxMpService newWxMpServiceOkHttpImpl() {
+ return new WxMpServiceOkHttpImpl();
+ }
+
+ private WxMpService newWxMpServiceJoddHttpImpl() {
+ return new WxMpServiceJoddHttpImpl();
+ }
+
+}
diff --git a/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/config/WxMpStorageAutoConfiguration.java b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/config/WxMpStorageAutoConfiguration.java
new file mode 100644
index 0000000000..ac995dd1ec
--- /dev/null
+++ b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/config/WxMpStorageAutoConfiguration.java
@@ -0,0 +1,128 @@
+package com.binarywang.solon.wxjava.mp.config;
+
+import com.binarywang.solon.wxjava.mp.enums.StorageType;
+import com.binarywang.solon.wxjava.mp.properties.RedisProperties;
+import com.binarywang.solon.wxjava.mp.properties.WxMpProperties;
+import com.google.common.collect.Sets;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.redis.JedisWxRedisOps;
+import me.chanjar.weixin.common.redis.WxRedisOps;
+import me.chanjar.weixin.mp.config.WxMpConfigStorage;
+import me.chanjar.weixin.mp.config.WxMpHostConfig;
+import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
+import me.chanjar.weixin.mp.config.impl.WxMpRedisConfigImpl;
+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.Jedis;
+import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.JedisPoolConfig;
+import redis.clients.jedis.JedisSentinelPool;
+import redis.clients.jedis.util.Pool;
+
+import java.util.Set;
+
+/**
+ * 微信公众号存储策略自动配置.
+ *
+ * @author Luo
+ */
+@Slf4j
+@Configuration
+@RequiredArgsConstructor
+public class WxMpStorageAutoConfiguration {
+ private final AppContext applicationContext;
+
+ private final WxMpProperties wxMpProperties;
+
+ @Bean
+ @Condition(onMissingBean=WxMpConfigStorage.class)
+ public WxMpConfigStorage wxMpConfigStorage() {
+ StorageType type = wxMpProperties.getConfigStorage().getType();
+ WxMpConfigStorage config;
+ switch (type) {
+ case Jedis:
+ config = jedisConfigStorage();
+ break;
+ default:
+ config = defaultConfigStorage();
+ break;
+ }
+ // wx host config
+ if (null != wxMpProperties.getHosts() && StringUtils.isNotEmpty(wxMpProperties.getHosts().getApiHost())) {
+ WxMpHostConfig hostConfig = new WxMpHostConfig();
+ hostConfig.setApiHost(wxMpProperties.getHosts().getApiHost());
+ hostConfig.setMpHost(wxMpProperties.getHosts().getMpHost());
+ hostConfig.setOpenHost(wxMpProperties.getHosts().getOpenHost());
+ config.setHostConfig(hostConfig);
+ }
+ return config;
+ }
+
+ private WxMpConfigStorage defaultConfigStorage() {
+ WxMpDefaultConfigImpl config = new WxMpDefaultConfigImpl();
+ setWxMpInfo(config);
+ return config;
+ }
+
+ private WxMpConfigStorage jedisConfigStorage() {
+ Pool jedisPool;
+ if (wxMpProperties.getConfigStorage() != null && wxMpProperties.getConfigStorage().getRedis() != null
+ && StringUtils.isNotEmpty(wxMpProperties.getConfigStorage().getRedis().getHost())) {
+ jedisPool = getJedisPool();
+ } else {
+ jedisPool = applicationContext.getBean(JedisPool.class);
+ }
+ WxRedisOps redisOps = new JedisWxRedisOps(jedisPool);
+ WxMpRedisConfigImpl wxMpRedisConfig = new WxMpRedisConfigImpl(redisOps,
+ wxMpProperties.getConfigStorage().getKeyPrefix());
+ setWxMpInfo(wxMpRedisConfig);
+ return wxMpRedisConfig;
+ }
+
+ private void setWxMpInfo(WxMpDefaultConfigImpl config) {
+ WxMpProperties properties = wxMpProperties;
+ WxMpProperties.ConfigStorage configStorageProperties = properties.getConfigStorage();
+ config.setAppId(properties.getAppId());
+ config.setSecret(properties.getSecret());
+ config.setToken(properties.getToken());
+ config.setAesKey(properties.getAesKey());
+ config.setUseStableAccessToken(wxMpProperties.isUseStableAccessToken());
+ config.setHttpProxyHost(configStorageProperties.getHttpProxyHost());
+ config.setHttpProxyUsername(configStorageProperties.getHttpProxyUsername());
+ config.setHttpProxyPassword(configStorageProperties.getHttpProxyPassword());
+ if (configStorageProperties.getHttpProxyPort() != null) {
+ config.setHttpProxyPort(configStorageProperties.getHttpProxyPort());
+ }
+ }
+
+ private Pool getJedisPool() {
+ RedisProperties redis = wxMpProperties.getConfigStorage().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);
+ if (StringUtils.isNotEmpty(redis.getSentinelIps())) {
+ Set sentinels = Sets.newHashSet(redis.getSentinelIps().split(","));
+ return new JedisSentinelPool(redis.getSentinelName(), sentinels);
+ }
+
+ return new JedisPool(config, redis.getHost(), redis.getPort(), redis.getTimeout(), redis.getPassword(),
+ redis.getDatabase());
+ }
+}
diff --git a/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/enums/HttpClientType.java b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/enums/HttpClientType.java
new file mode 100644
index 0000000000..9b1a8ccbf4
--- /dev/null
+++ b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/enums/HttpClientType.java
@@ -0,0 +1,22 @@
+package com.binarywang.solon.wxjava.mp.enums;
+
+/**
+ * httpclient类型.
+ *
+ * @author Binary Wang
+ * created on 2020-08-30
+ */
+public enum HttpClientType {
+ /**
+ * HttpClient.
+ */
+ HttpClient,
+ /**
+ * OkHttp.
+ */
+ OkHttp,
+ /**
+ * JoddHttp.
+ */
+ JoddHttp,
+}
diff --git a/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/enums/StorageType.java b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/enums/StorageType.java
new file mode 100644
index 0000000000..34433a8230
--- /dev/null
+++ b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/enums/StorageType.java
@@ -0,0 +1,26 @@
+package com.binarywang.solon.wxjava.mp.enums;
+
+/**
+ * storage类型.
+ *
+ * @author Binary Wang
+ * created on 2020-08-30
+ */
+public enum StorageType {
+ /**
+ * 内存.
+ */
+ Memory,
+ /**
+ * redis(JedisClient).
+ */
+ Jedis,
+ /**
+ * redis(Redisson).
+ */
+ Redisson,
+ /**
+ * redis(RedisTemplate).
+ */
+ RedisTemplate
+}
diff --git a/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/integration/WxMpPluginImpl.java b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/integration/WxMpPluginImpl.java
new file mode 100644
index 0000000000..3368d34269
--- /dev/null
+++ b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/integration/WxMpPluginImpl.java
@@ -0,0 +1,20 @@
+package com.binarywang.solon.wxjava.mp.integration;
+
+import com.binarywang.solon.wxjava.mp.config.WxMpServiceAutoConfiguration;
+import com.binarywang.solon.wxjava.mp.config.WxMpStorageAutoConfiguration;
+import com.binarywang.solon.wxjava.mp.properties.WxMpProperties;
+import org.noear.solon.core.AppContext;
+import org.noear.solon.core.Plugin;
+
+/**
+ * @author noear 2024/9/2 created
+ */
+public class WxMpPluginImpl implements Plugin {
+ @Override
+ public void start(AppContext context) throws Throwable {
+ context.beanMake(WxMpProperties.class);
+
+ context.beanMake(WxMpStorageAutoConfiguration.class);
+ context.beanMake(WxMpServiceAutoConfiguration.class);
+ }
+}
diff --git a/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/properties/HostConfig.java b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/properties/HostConfig.java
new file mode 100644
index 0000000000..8ccedf9294
--- /dev/null
+++ b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/properties/HostConfig.java
@@ -0,0 +1,27 @@
+package com.binarywang.solon.wxjava.mp.properties;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class HostConfig implements Serializable {
+
+ private static final long serialVersionUID = -4172767630740346001L;
+
+ /**
+ * 对应于:https://api.weixin.qq.com
+ */
+ private String apiHost;
+
+ /**
+ * 对应于:https://open.weixin.qq.com
+ */
+ private String openHost;
+
+ /**
+ * 对应于:https://mp.weixin.qq.com
+ */
+ private String mpHost;
+
+}
diff --git a/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/properties/RedisProperties.java b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/properties/RedisProperties.java
new file mode 100644
index 0000000000..0376f947a7
--- /dev/null
+++ b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/properties/RedisProperties.java
@@ -0,0 +1,56 @@
+package com.binarywang.solon.wxjava.mp.properties;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * redis 配置属性.
+ *
+ * @author Binary Wang
+ * created on 2020-08-30
+ */
+@Data
+public class RedisProperties implements Serializable {
+ private static final long serialVersionUID = -5924815351660074401L;
+
+ /**
+ * 主机地址.
+ */
+ private String host = "127.0.0.1";
+
+ /**
+ * 端口号.
+ */
+ private int port = 6379;
+
+ /**
+ * 密码.
+ */
+ private String password;
+
+ /**
+ * 超时.
+ */
+ private int timeout = 2000;
+
+ /**
+ * 数据库.
+ */
+ private int database = 0;
+
+ /**
+ * sentinel ips
+ */
+ private String sentinelIps;
+
+ /**
+ * sentinel name
+ */
+ private String sentinelName;
+
+ private Integer maxActive;
+ private Integer maxIdle;
+ private Integer maxWaitMillis;
+ private Integer minIdle;
+}
diff --git a/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/properties/WxMpProperties.java b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/properties/WxMpProperties.java
new file mode 100644
index 0000000000..cda0aa88e7
--- /dev/null
+++ b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/properties/WxMpProperties.java
@@ -0,0 +1,106 @@
+package com.binarywang.solon.wxjava.mp.properties;
+
+import com.binarywang.solon.wxjava.mp.enums.HttpClientType;
+import com.binarywang.solon.wxjava.mp.enums.StorageType;
+import lombok.Data;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.annotation.Inject;
+
+import java.io.Serializable;
+
+import static com.binarywang.solon.wxjava.mp.enums.StorageType.Memory;
+import static com.binarywang.solon.wxjava.mp.properties.WxMpProperties.PREFIX;
+
+/**
+ * 微信接入相关配置属性.
+ *
+ * @author someone
+ */
+@Data
+@Configuration
+@Inject("${" + PREFIX + "}")
+public class WxMpProperties {
+ public static final String PREFIX = "wx.mp";
+
+ /**
+ * 设置微信公众号的appid.
+ */
+ private String appId;
+
+ /**
+ * 设置微信公众号的app secret.
+ */
+ private String secret;
+
+ /**
+ * 设置微信公众号的token.
+ */
+ private String token;
+
+ /**
+ * 设置微信公众号的EncodingAESKey.
+ */
+ private String aesKey;
+
+ /**
+ * 是否使用稳定版 Access Token
+ */
+ private boolean useStableAccessToken = false;
+
+ /**
+ * 自定义host配置
+ */
+ private HostConfig hosts;
+
+ /**
+ * 存储策略
+ */
+ private final ConfigStorage configStorage = new ConfigStorage();
+
+ @Data
+ public static class ConfigStorage implements Serializable {
+ private static final long serialVersionUID = 4815731027000065434L;
+
+ /**
+ * 存储类型.
+ */
+ private StorageType type = Memory;
+
+ /**
+ * 指定key前缀.
+ */
+ private String keyPrefix = "wx";
+
+ /**
+ * redis连接配置.
+ */
+ private final RedisProperties redis = new RedisProperties();
+
+ /**
+ * http客户端类型.
+ */
+ private HttpClientType httpClientType = HttpClientType.HttpClient;
+
+ /**
+ * http代理主机.
+ */
+ private String httpProxyHost;
+
+ /**
+ * http代理端口.
+ */
+ private Integer httpProxyPort;
+
+ /**
+ * http代理用户名.
+ */
+ private String httpProxyUsername;
+
+ /**
+ * http代理密码.
+ */
+ private String httpProxyPassword;
+
+ }
+
+}
diff --git a/solon-plugins/wx-java-mp-solon-plugin/src/main/resources/META-INF/solon/wx-java-mp-solon-plugin.properties b/solon-plugins/wx-java-mp-solon-plugin/src/main/resources/META-INF/solon/wx-java-mp-solon-plugin.properties
new file mode 100644
index 0000000000..c80357184c
--- /dev/null
+++ b/solon-plugins/wx-java-mp-solon-plugin/src/main/resources/META-INF/solon/wx-java-mp-solon-plugin.properties
@@ -0,0 +1,2 @@
+solon.plugin=com.binarywang.solon.wxjava.mp.integration.WxMpPluginImpl
+solon.plugin.priority=10
diff --git a/solon-plugins/wx-java-open-solon-plugin/README.md b/solon-plugins/wx-java-open-solon-plugin/README.md
new file mode 100644
index 0000000000..619e28dbdd
--- /dev/null
+++ b/solon-plugins/wx-java-open-solon-plugin/README.md
@@ -0,0 +1,39 @@
+# wx-java-open-solon-plugin
+## 快速开始
+1. 引入依赖
+ ```xml
+
+ com.github.binarywang
+ wx-java-open-solon-plugin
+ ${version}
+
+ ```
+2. 添加配置(app.properties)
+ ```properties
+ # 公众号配置(必填)
+ wx.open.appId = appId
+ wx.open.secret = @secret
+ wx.open.token = @token
+ wx.open.aesKey = @aesKey
+ # 存储配置redis(可选)
+ # 优先注入容器的(JedisPool, RedissonClient), 当配置了wx.open.config-storage.redis.host, 不会使用容器注入redis连接配置
+ wx.open.config-storage.type = redis # 配置类型: memory(默认), redis(jedis), jedis, redisson, redistemplate
+ wx.open.config-storage.key-prefix = wx # 相关redis前缀配置: wx(默认)
+ wx.open.config-storage.redis.host = 127.0.0.1
+ wx.open.config-storage.redis.port = 6379
+ # http客户端配置
+ wx.open.config-storage.http-client-type=httpclient # http客户端类型: httpclient(默认)
+ wx.open.config-storage.http-proxy-host=
+ wx.open.config-storage.http-proxy-port=
+ wx.open.config-storage.http-proxy-username=
+ wx.open.config-storage.http-proxy-password=
+ # 最大重试次数,默认:5 次,如果小于 0,则为 0
+ wx.open.config-storage.max-retry-times=5
+ # 重试时间间隔步进,默认:1000 毫秒,如果小于 0,则为 1000
+ wx.open.config-storage.retry-sleep-millis=1000
+ ```
+3. 支持自动注入的类型: `WxOpenService, WxOpenMessageRouter, WxOpenComponentService`
+
+4. 覆盖自动配置: 自定义注入的bean会覆盖自动注入的
+ - WxOpenConfigStorage
+ - WxOpenService
diff --git a/solon-plugins/wx-java-open-solon-plugin/pom.xml b/solon-plugins/wx-java-open-solon-plugin/pom.xml
new file mode 100644
index 0000000000..00fce6281e
--- /dev/null
+++ b/solon-plugins/wx-java-open-solon-plugin/pom.xml
@@ -0,0 +1,32 @@
+
+
+
+ wx-java-solon-plugins
+ com.github.binarywang
+ 4.6.4.B
+
+ 4.0.0
+
+ wx-java-open-solon-plugin
+ WxJava - Solon Plugin for WxOpen
+ 微信开放平台开发的 Solon Plugin
+
+
+
+ com.github.binarywang
+ weixin-java-open
+ ${project.version}
+
+
+ redis.clients
+ jedis
+
+
+ org.redisson
+ redisson
+
+
+
+
diff --git a/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/config/WxOpenServiceAutoConfiguration.java b/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/config/WxOpenServiceAutoConfiguration.java
new file mode 100644
index 0000000000..7bda6816ed
--- /dev/null
+++ b/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/config/WxOpenServiceAutoConfiguration.java
@@ -0,0 +1,37 @@
+package com.binarywang.solon.wxjava.open.config;
+
+import me.chanjar.weixin.open.api.WxOpenComponentService;
+import me.chanjar.weixin.open.api.WxOpenConfigStorage;
+import me.chanjar.weixin.open.api.WxOpenService;
+import me.chanjar.weixin.open.api.impl.WxOpenMessageRouter;
+import me.chanjar.weixin.open.api.impl.WxOpenServiceImpl;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+
+/**
+ * 微信开放平台相关服务自动注册.
+ *
+ * @author someone
+ */
+@Configuration
+public class WxOpenServiceAutoConfiguration {
+
+ @Bean
+ @Condition(onMissingBean = WxOpenService.class, onBean = WxOpenConfigStorage.class)
+ public WxOpenService wxOpenService(WxOpenConfigStorage wxOpenConfigStorage) {
+ WxOpenService wxOpenService = new WxOpenServiceImpl();
+ wxOpenService.setWxOpenConfigStorage(wxOpenConfigStorage);
+ return wxOpenService;
+ }
+
+ @Bean
+ public WxOpenMessageRouter wxOpenMessageRouter(WxOpenService wxOpenService) {
+ return new WxOpenMessageRouter(wxOpenService);
+ }
+
+ @Bean
+ public WxOpenComponentService wxOpenComponentService(WxOpenService wxOpenService) {
+ return wxOpenService.getWxOpenComponentService();
+ }
+}
diff --git a/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/config/storage/AbstractWxOpenConfigStorageConfiguration.java b/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/config/storage/AbstractWxOpenConfigStorageConfiguration.java
new file mode 100644
index 0000000000..4a65b311d9
--- /dev/null
+++ b/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/config/storage/AbstractWxOpenConfigStorageConfiguration.java
@@ -0,0 +1,33 @@
+package com.binarywang.solon.wxjava.open.config.storage;
+
+import com.binarywang.solon.wxjava.open.properties.WxOpenProperties;
+import me.chanjar.weixin.open.api.impl.WxOpenInMemoryConfigStorage;
+
+/**
+ * @author yl
+ */
+public abstract class AbstractWxOpenConfigStorageConfiguration {
+
+ protected WxOpenInMemoryConfigStorage config(WxOpenInMemoryConfigStorage config, WxOpenProperties properties) {
+ WxOpenProperties.ConfigStorage storage = properties.getConfigStorage();
+ config.setWxOpenInfo(properties.getAppId(), properties.getSecret(), properties.getToken(), properties.getAesKey());
+ config.setHttpProxyHost(storage.getHttpProxyHost());
+ config.setHttpProxyUsername(storage.getHttpProxyUsername());
+ config.setHttpProxyPassword(storage.getHttpProxyPassword());
+ Integer httpProxyPort = storage.getHttpProxyPort();
+ if (httpProxyPort != null) {
+ config.setHttpProxyPort(httpProxyPort);
+ }
+ int maxRetryTimes = storage.getMaxRetryTimes();
+ if (maxRetryTimes < 0) {
+ maxRetryTimes = 0;
+ }
+ int retrySleepMillis = storage.getRetrySleepMillis();
+ if (retrySleepMillis < 0) {
+ retrySleepMillis = 1000;
+ }
+ config.setRetrySleepMillis(retrySleepMillis);
+ config.setMaxRetryTimes(maxRetryTimes);
+ return config;
+ }
+}
diff --git a/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/config/storage/WxOpenInJedisConfigStorageConfiguration.java b/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/config/storage/WxOpenInJedisConfigStorageConfiguration.java
new file mode 100644
index 0000000000..59e65ef48c
--- /dev/null
+++ b/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/config/storage/WxOpenInJedisConfigStorageConfiguration.java
@@ -0,0 +1,71 @@
+package com.binarywang.solon.wxjava.open.config.storage;
+
+import com.binarywang.solon.wxjava.open.properties.WxOpenProperties;
+import com.binarywang.solon.wxjava.open.properties.WxOpenRedisProperties;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.open.api.WxOpenConfigStorage;
+import me.chanjar.weixin.open.api.impl.WxOpenInMemoryConfigStorage;
+import me.chanjar.weixin.open.api.impl.WxOpenInRedisConfigStorage;
+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;
+
+/**
+ * @author yl
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxOpenProperties.PREFIX + ".configStorage.type} = jedis",
+ onClass = JedisPool.class
+)
+@RequiredArgsConstructor
+public class WxOpenInJedisConfigStorageConfiguration extends AbstractWxOpenConfigStorageConfiguration {
+ private final WxOpenProperties properties;
+ private final AppContext applicationContext;
+
+ @Bean
+ @Condition(onMissingBean=WxOpenConfigStorage.class)
+ public WxOpenConfigStorage wxOpenConfigStorage() {
+ WxOpenInMemoryConfigStorage config = getWxOpenInRedisConfigStorage();
+ return this.config(config, properties);
+ }
+
+ private WxOpenInRedisConfigStorage getWxOpenInRedisConfigStorage() {
+ WxOpenRedisProperties wxOpenRedisProperties = properties.getConfigStorage().getRedis();
+ JedisPool jedisPool;
+ if (wxOpenRedisProperties != null && StringUtils.isNotEmpty(wxOpenRedisProperties.getHost())) {
+ jedisPool = getJedisPool();
+ } else {
+ jedisPool = applicationContext.getBean(JedisPool.class);
+ }
+ return new WxOpenInRedisConfigStorage(jedisPool, properties.getConfigStorage().getKeyPrefix());
+ }
+
+ private JedisPool getJedisPool() {
+ WxOpenProperties.ConfigStorage storage = properties.getConfigStorage();
+ WxOpenRedisProperties 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());
+ }
+}
diff --git a/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/config/storage/WxOpenInMemoryConfigStorageConfiguration.java b/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/config/storage/WxOpenInMemoryConfigStorageConfiguration.java
new file mode 100644
index 0000000000..756b6525fc
--- /dev/null
+++ b/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/config/storage/WxOpenInMemoryConfigStorageConfiguration.java
@@ -0,0 +1,28 @@
+package com.binarywang.solon.wxjava.open.config.storage;
+
+import com.binarywang.solon.wxjava.open.properties.WxOpenProperties;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.open.api.WxOpenConfigStorage;
+import me.chanjar.weixin.open.api.impl.WxOpenInMemoryConfigStorage;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+
+/**
+ * @author yl
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxOpenProperties.PREFIX + ".configStorage.type:memory} = memory"
+)
+@RequiredArgsConstructor
+public class WxOpenInMemoryConfigStorageConfiguration extends AbstractWxOpenConfigStorageConfiguration {
+ private final WxOpenProperties properties;
+
+ @Bean
+ @Condition(onMissingBean=WxOpenConfigStorage.class)
+ public WxOpenConfigStorage wxOpenConfigStorage() {
+ WxOpenInMemoryConfigStorage config = new WxOpenInMemoryConfigStorage();
+ return this.config(config, properties);
+ }
+}
diff --git a/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/config/storage/WxOpenInRedissonConfigStorageConfiguration.java b/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/config/storage/WxOpenInRedissonConfigStorageConfiguration.java
new file mode 100644
index 0000000000..70844e2888
--- /dev/null
+++ b/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/config/storage/WxOpenInRedissonConfigStorageConfiguration.java
@@ -0,0 +1,62 @@
+package com.binarywang.solon.wxjava.open.config.storage;
+
+import com.binarywang.solon.wxjava.open.properties.WxOpenProperties;
+import com.binarywang.solon.wxjava.open.properties.WxOpenRedisProperties;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.open.api.WxOpenConfigStorage;
+import me.chanjar.weixin.open.api.impl.WxOpenInMemoryConfigStorage;
+import me.chanjar.weixin.open.api.impl.WxOpenInRedissonConfigStorage;
+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 org.redisson.Redisson;
+import org.redisson.api.RedissonClient;
+import org.redisson.config.Config;
+import org.redisson.config.TransportMode;
+
+/**
+ * @author yl
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxOpenProperties.PREFIX + ".configStorage.type} = redisson",
+ onClass = Redisson.class
+)
+@RequiredArgsConstructor
+public class WxOpenInRedissonConfigStorageConfiguration extends AbstractWxOpenConfigStorageConfiguration {
+ private final WxOpenProperties properties;
+ private final AppContext applicationContext;
+
+ @Bean
+ @Condition(onMissingBean=WxOpenConfigStorage.class)
+ public WxOpenConfigStorage wxOpenConfigStorage() {
+ WxOpenInMemoryConfigStorage config = getWxOpenInRedissonConfigStorage();
+ return this.config(config, properties);
+ }
+
+ private WxOpenInRedissonConfigStorage getWxOpenInRedissonConfigStorage() {
+ WxOpenRedisProperties wxOpenRedisProperties = properties.getConfigStorage().getRedis();
+ RedissonClient redissonClient;
+ if (wxOpenRedisProperties != null && StringUtils.isNotEmpty(wxOpenRedisProperties.getHost())) {
+ redissonClient = getRedissonClient();
+ } else {
+ redissonClient = applicationContext.getBean(RedissonClient.class);
+ }
+ return new WxOpenInRedissonConfigStorage(redissonClient, properties.getConfigStorage().getKeyPrefix());
+ }
+
+ private RedissonClient getRedissonClient() {
+ WxOpenProperties.ConfigStorage storage = properties.getConfigStorage();
+ WxOpenRedisProperties redis = storage.getRedis();
+
+ Config config = new Config();
+ config.useSingleServer()
+ .setAddress("redis://" + redis.getHost() + ":" + redis.getPort())
+ .setDatabase(redis.getDatabase())
+ .setPassword(redis.getPassword());
+ config.setTransportMode(TransportMode.NIO);
+ return Redisson.create(config);
+ }
+}
diff --git a/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/integration/WxOpenPluginImpl.java b/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/integration/WxOpenPluginImpl.java
new file mode 100644
index 0000000000..29352d81f0
--- /dev/null
+++ b/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/integration/WxOpenPluginImpl.java
@@ -0,0 +1,25 @@
+package com.binarywang.solon.wxjava.open.integration;
+
+import com.binarywang.solon.wxjava.open.config.WxOpenServiceAutoConfiguration;
+import com.binarywang.solon.wxjava.open.config.storage.WxOpenInJedisConfigStorageConfiguration;
+import com.binarywang.solon.wxjava.open.config.storage.WxOpenInMemoryConfigStorageConfiguration;
+import com.binarywang.solon.wxjava.open.config.storage.WxOpenInRedissonConfigStorageConfiguration;
+import com.binarywang.solon.wxjava.open.properties.WxOpenProperties;
+import org.noear.solon.core.AppContext;
+import org.noear.solon.core.Plugin;
+
+/**
+ * @author noear 2024/9/2 created
+ */
+public class WxOpenPluginImpl implements Plugin {
+ @Override
+ public void start(AppContext context) throws Throwable {
+ context.beanMake(WxOpenProperties.class);
+
+ context.beanMake(WxOpenServiceAutoConfiguration.class);
+
+ context.beanMake(WxOpenInMemoryConfigStorageConfiguration.class);
+ context.beanMake(WxOpenInJedisConfigStorageConfiguration.class);
+ context.beanMake(WxOpenInRedissonConfigStorageConfiguration.class);
+ }
+}
diff --git a/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/properties/WxOpenProperties.java b/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/properties/WxOpenProperties.java
new file mode 100644
index 0000000000..4ec34c02b8
--- /dev/null
+++ b/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/properties/WxOpenProperties.java
@@ -0,0 +1,138 @@
+package com.binarywang.solon.wxjava.open.properties;
+
+import lombok.Data;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.annotation.Inject;
+
+import java.io.Serializable;
+
+import static com.binarywang.solon.wxjava.open.properties.WxOpenProperties.PREFIX;
+import static com.binarywang.solon.wxjava.open.properties.WxOpenProperties.StorageType.memory;
+
+
+/**
+ * 微信接入相关配置属性.
+ *
+ * @author someone
+ */
+@Data
+@Configuration
+@Inject("${"+PREFIX+"}")
+public class WxOpenProperties {
+ public static final String PREFIX = "wx.open";
+
+ /**
+ * 设置微信开放平台的appid.
+ */
+ private String appId;
+
+ /**
+ * 设置微信开放平台的app secret.
+ */
+ private String secret;
+
+ /**
+ * 设置微信开放平台的token.
+ */
+ private String token;
+
+ /**
+ * 设置微信开放平台的EncodingAESKey.
+ */
+ private String aesKey;
+
+ /**
+ * 存储策略.
+ */
+ private ConfigStorage configStorage = new ConfigStorage();
+
+
+ @Data
+ public static class ConfigStorage implements Serializable {
+ private static final long serialVersionUID = 4815731027000065434L;
+
+ /**
+ * 存储类型.
+ */
+ private StorageType type = memory;
+
+ /**
+ * 指定key前缀.
+ */
+ private String keyPrefix = "wx:open";
+
+ /**
+ * redis连接配置.
+ */
+ private WxOpenRedisProperties redis = new WxOpenRedisProperties();
+
+ /**
+ * http客户端类型.
+ */
+ private HttpClientType httpClientType = HttpClientType.httpclient;
+
+ /**
+ * http代理主机.
+ */
+ private String httpProxyHost;
+
+ /**
+ * http代理端口.
+ */
+ private Integer httpProxyPort;
+
+ /**
+ * http代理用户名.
+ */
+ private String httpProxyUsername;
+
+ /**
+ * http代理密码.
+ */
+ private String httpProxyPassword;
+
+ /**
+ * http 请求重试间隔
+ *
+ * {@link me.chanjar.weixin.mp.api.impl.BaseWxMpServiceImpl#setRetrySleepMillis(int)}
+ * {@link cn.binarywang.wx.miniapp.api.impl.BaseWxMaServiceImpl#setRetrySleepMillis(int)}
+ *
+ */
+ private int retrySleepMillis = 1000;
+ /**
+ * http 请求最大重试次数
+ *
+ * {@link me.chanjar.weixin.mp.api.impl.BaseWxMpServiceImpl#setMaxRetryTimes(int)}
+ * {@link cn.binarywang.wx.miniapp.api.impl.BaseWxMaServiceImpl#setMaxRetryTimes(int)}
+ *
+ */
+ private int maxRetryTimes = 5;
+
+ }
+
+ public enum StorageType {
+ /**
+ * 内存.
+ */
+ memory,
+ /**
+ * jedis.
+ */
+ jedis,
+ /**
+ * redisson.
+ */
+ redisson,
+ /**
+ * redistemplate
+ */
+ redistemplate
+ }
+
+ public enum HttpClientType {
+ /**
+ * HttpClient.
+ */
+ httpclient
+ }
+}
diff --git a/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/properties/WxOpenRedisProperties.java b/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/properties/WxOpenRedisProperties.java
new file mode 100644
index 0000000000..6b7a2d8654
--- /dev/null
+++ b/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/properties/WxOpenRedisProperties.java
@@ -0,0 +1,45 @@
+package com.binarywang.solon.wxjava.open.properties;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * Redis配置.
+ *
+ * @author someone
+ */
+@Data
+public class WxOpenRedisProperties implements Serializable {
+ private static final long serialVersionUID = -5924815351660074401L;
+
+ /**
+ * 主机地址.
+ */
+ private String host;
+
+ /**
+ * 端口号.
+ */
+ private int port = 6379;
+
+ /**
+ * 密码.
+ */
+ private String password;
+
+ /**
+ * 超时.
+ */
+ private int timeout = 2000;
+
+ /**
+ * 数据库.
+ */
+ private int database = 0;
+
+ private Integer maxActive;
+ private Integer maxIdle;
+ private Integer maxWaitMillis;
+ private Integer minIdle;
+}
diff --git a/solon-plugins/wx-java-open-solon-plugin/src/main/resources/META-INF/solon/wx-java-open-solon-plugin.properties b/solon-plugins/wx-java-open-solon-plugin/src/main/resources/META-INF/solon/wx-java-open-solon-plugin.properties
new file mode 100644
index 0000000000..289aca5eeb
--- /dev/null
+++ b/solon-plugins/wx-java-open-solon-plugin/src/main/resources/META-INF/solon/wx-java-open-solon-plugin.properties
@@ -0,0 +1,2 @@
+solon.plugin=com.binarywang.solon.wxjava.open.integration.WxOpenPluginImpl
+solon.plugin.priority=10
diff --git a/solon-plugins/wx-java-pay-solon-plugin/README.md b/solon-plugins/wx-java-pay-solon-plugin/README.md
new file mode 100644
index 0000000000..b0e212593b
--- /dev/null
+++ b/solon-plugins/wx-java-pay-solon-plugin/README.md
@@ -0,0 +1,43 @@
+# 使用说明
+1. 在自己的Solon项目里,引入maven依赖
+```xml
+
+ com.github.binarywang
+ wx-java-pay-solon-plugin
+ ${version}
+
+ ```
+2. 添加配置(app.yml)
+###### 1)V2版本
+```yml
+wx:
+ pay:
+ appId:
+ mchId:
+ mchKey:
+ keyPath:
+```
+###### 2)V3版本
+```yml
+wx:
+ pay:
+ appId: xxxxxxxxxxx
+ mchId: 15xxxxxxxxx #商户id
+ apiV3Key: Dc1DBwSc094jACxxxxxxxxxxxxxxx #V3密钥
+ certSerialNo: 62C6CEAA360BCxxxxxxxxxxxxxxx
+ privateKeyPath: classpath:cert/apiclient_key.pem #apiclient_key.pem证书文件的绝对路径或者以classpath:开头的类路径
+ privateCertPath: classpath:cert/apiclient_cert.pem #apiclient_cert.pem证书文件的绝对路径或者以classpath:开头的类路径
+```
+###### 3)V3服务商版本
+```yml
+wx:
+ pay: #微信服务商支付
+ configs:
+ - appId: wxe97b2x9c2b3d #spAppId
+ mchId: 16486610 #服务商商户
+ subAppId: wx118cexxe3c07679 #子appId
+ subMchId: 16496705 #子商户
+ apiV3Key: Dc1DBwSc094jAKDGR5aqqb7PTHr #apiV3密钥
+ privateKeyPath: classpath:cert/apiclient_key.pem #服务商证书文件,apiclient_key.pem证书文件的绝对路径或者以classpath:开头的类路径(可以配置绝对路径)
+ privateCertPath: classpath:cert/apiclient_cert.pem #apiclient_cert.pem证书文件的绝对路径或者以classpath:开头的类路径
+```
diff --git a/solon-plugins/wx-java-pay-solon-plugin/pom.xml b/solon-plugins/wx-java-pay-solon-plugin/pom.xml
new file mode 100644
index 0000000000..9805e1d174
--- /dev/null
+++ b/solon-plugins/wx-java-pay-solon-plugin/pom.xml
@@ -0,0 +1,24 @@
+
+
+
+ wx-java-solon-plugins
+ com.github.binarywang
+ 4.6.4.B
+
+ 4.0.0
+
+ wx-java-pay-solon-plugin
+ WxJava - Solon Plugin for WxPay
+ 微信支付开发的 Solon Plugin
+
+
+
+ com.github.binarywang
+ weixin-java-pay
+ ${project.version}
+
+
+
+
diff --git a/solon-plugins/wx-java-pay-solon-plugin/src/main/java/com/binarywang/solon/wxjava/pay/config/WxPayAutoConfiguration.java b/solon-plugins/wx-java-pay-solon-plugin/src/main/java/com/binarywang/solon/wxjava/pay/config/WxPayAutoConfiguration.java
new file mode 100644
index 0000000000..1957e4157e
--- /dev/null
+++ b/solon-plugins/wx-java-pay-solon-plugin/src/main/java/com/binarywang/solon/wxjava/pay/config/WxPayAutoConfiguration.java
@@ -0,0 +1,61 @@
+package com.binarywang.solon.wxjava.pay.config;
+
+import com.binarywang.solon.wxjava.pay.properties.WxPayProperties;
+import com.github.binarywang.wxpay.config.WxPayConfig;
+import com.github.binarywang.wxpay.service.WxPayService;
+import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
+import org.apache.commons.lang3.StringUtils;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+
+/**
+ *
+ * 微信支付自动配置
+ * Created by BinaryWang on 2019/4/17.
+ *
+ *
+ * @author Binary Wang
+ */
+@Configuration
+@Condition(
+ onProperty = "${wx.pay.enabled:true} = true",
+ onClass=WxPayService.class
+)
+public class WxPayAutoConfiguration {
+ private WxPayProperties properties;
+
+ public WxPayAutoConfiguration(WxPayProperties properties) {
+ this.properties = properties;
+ }
+
+ /**
+ * 构造微信支付服务对象.
+ *
+ * @return 微信支付service
+ */
+ @Bean
+ @Condition(onMissingBean=WxPayService.class)
+ public WxPayService wxPayService() {
+ final WxPayServiceImpl wxPayService = new WxPayServiceImpl();
+ WxPayConfig payConfig = new WxPayConfig();
+ payConfig.setAppId(StringUtils.trimToNull(this.properties.getAppId()));
+ payConfig.setMchId(StringUtils.trimToNull(this.properties.getMchId()));
+ payConfig.setMchKey(StringUtils.trimToNull(this.properties.getMchKey()));
+ payConfig.setSubAppId(StringUtils.trimToNull(this.properties.getSubAppId()));
+ payConfig.setSubMchId(StringUtils.trimToNull(this.properties.getSubMchId()));
+ payConfig.setKeyPath(StringUtils.trimToNull(this.properties.getKeyPath()));
+ payConfig.setUseSandboxEnv(this.properties.isUseSandboxEnv());
+ //以下是apiv3以及支付分相关
+ payConfig.setServiceId(StringUtils.trimToNull(this.properties.getServiceId()));
+ payConfig.setPayScoreNotifyUrl(StringUtils.trimToNull(this.properties.getPayScoreNotifyUrl()));
+ payConfig.setPrivateKeyPath(StringUtils.trimToNull(this.properties.getPrivateKeyPath()));
+ payConfig.setPrivateCertPath(StringUtils.trimToNull(this.properties.getPrivateCertPath()));
+ payConfig.setCertSerialNo(StringUtils.trimToNull(this.properties.getCertSerialNo()));
+ payConfig.setApiV3Key(StringUtils.trimToNull(this.properties.getApiv3Key()));
+
+ wxPayService.setConfig(payConfig);
+ return wxPayService;
+ }
+
+}
diff --git a/solon-plugins/wx-java-pay-solon-plugin/src/main/java/com/binarywang/solon/wxjava/pay/integration/WxPayPluginImpl.java b/solon-plugins/wx-java-pay-solon-plugin/src/main/java/com/binarywang/solon/wxjava/pay/integration/WxPayPluginImpl.java
new file mode 100644
index 0000000000..e7ba275ca0
--- /dev/null
+++ b/solon-plugins/wx-java-pay-solon-plugin/src/main/java/com/binarywang/solon/wxjava/pay/integration/WxPayPluginImpl.java
@@ -0,0 +1,18 @@
+package com.binarywang.solon.wxjava.pay.integration;
+
+import com.binarywang.solon.wxjava.pay.config.WxPayAutoConfiguration;
+import com.binarywang.solon.wxjava.pay.properties.WxPayProperties;
+import org.noear.solon.core.AppContext;
+import org.noear.solon.core.Plugin;
+
+/**
+ * @author noear 2024/9/2 created
+ */
+public class WxPayPluginImpl implements Plugin {
+ @Override
+ public void start(AppContext context) throws Throwable {
+ context.beanMake(WxPayProperties.class);
+
+ context.beanMake(WxPayAutoConfiguration.class);
+ }
+}
diff --git a/solon-plugins/wx-java-pay-solon-plugin/src/main/java/com/binarywang/solon/wxjava/pay/properties/WxPayProperties.java b/solon-plugins/wx-java-pay-solon-plugin/src/main/java/com/binarywang/solon/wxjava/pay/properties/WxPayProperties.java
new file mode 100644
index 0000000000..a882f6ac31
--- /dev/null
+++ b/solon-plugins/wx-java-pay-solon-plugin/src/main/java/com/binarywang/solon/wxjava/pay/properties/WxPayProperties.java
@@ -0,0 +1,85 @@
+package com.binarywang.solon.wxjava.pay.properties;
+
+import lombok.Data;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.annotation.Inject;
+
+/**
+ *
+ * 微信支付属性配置类
+ * Created by Binary Wang on 2019/4/17.
+ *
+ *
+ * @author Binary Wang
+ */
+@Data
+@Configuration
+@Inject("${wx.pay}")
+public class WxPayProperties {
+ /**
+ * 设置微信公众号或者小程序等的appid.
+ */
+ private String appId;
+
+ /**
+ * 微信支付商户号.
+ */
+ private String mchId;
+
+ /**
+ * 微信支付商户密钥.
+ */
+ private String mchKey;
+
+ /**
+ * 服务商模式下的子商户公众账号ID,普通模式请不要配置,请在配置文件中将对应项删除.
+ */
+ private String subAppId;
+
+ /**
+ * 服务商模式下的子商户号,普通模式请不要配置,最好是请在配置文件中将对应项删除.
+ */
+ private String subMchId;
+
+ /**
+ * apiclient_cert.p12文件的绝对路径,或者如果放在项目中,请以classpath:开头指定.
+ */
+ private String keyPath;
+
+ /**
+ * 微信支付分serviceId
+ */
+ private String serviceId;
+
+ /**
+ * 证书序列号
+ */
+ private String certSerialNo;
+
+ /**
+ * apiV3秘钥
+ */
+ private String apiv3Key;
+
+ /**
+ * 微信支付分回调地址
+ */
+ private String payScoreNotifyUrl;
+
+ /**
+ * apiv3 商户apiclient_key.pem
+ */
+ private String privateKeyPath;
+
+ /**
+ * apiv3 商户apiclient_cert.pem
+ */
+ private String privateCertPath;
+
+ /**
+ * 微信支付是否使用仿真测试环境.
+ * 默认不使用
+ */
+ private boolean useSandboxEnv;
+
+}
diff --git a/solon-plugins/wx-java-pay-solon-plugin/src/main/resources/META-INF/solon/wx-java-pay-solon-plugin.properties b/solon-plugins/wx-java-pay-solon-plugin/src/main/resources/META-INF/solon/wx-java-pay-solon-plugin.properties
new file mode 100644
index 0000000000..98783176e2
--- /dev/null
+++ b/solon-plugins/wx-java-pay-solon-plugin/src/main/resources/META-INF/solon/wx-java-pay-solon-plugin.properties
@@ -0,0 +1,2 @@
+solon.plugin=com.binarywang.solon.wxjava.pay.integration.WxPayPluginImpl
+solon.plugin.priority=10
diff --git a/solon-plugins/wx-java-qidian-solon-plugin/README.md b/solon-plugins/wx-java-qidian-solon-plugin/README.md
new file mode 100644
index 0000000000..a409113c8c
--- /dev/null
+++ b/solon-plugins/wx-java-qidian-solon-plugin/README.md
@@ -0,0 +1,45 @@
+# wx-java-qidian-solon-plugin
+
+## 快速开始
+
+1. 引入依赖
+ ```xml
+
+ com.github.binarywang
+ wx-java-qidian-solon-plugin
+ ${version}
+
+ ```
+2. 添加配置(app.properties)
+ ```properties
+ # 公众号配置(必填)
+ wx.mp.appId = appId
+ wx.mp.secret = @secret
+ wx.mp.token = @token
+ wx.mp.aesKey = @aesKey
+ # 存储配置redis(可选)
+ wx.mp.config-storage.type = Jedis # 配置类型: Memory(默认), Jedis, RedisTemplate
+ wx.mp.config-storage.key-prefix = wx # 相关redis前缀配置: wx(默认)
+ wx.mp.config-storage.redis.host = 127.0.0.1
+ wx.mp.config-storage.redis.port = 6379
+ #单机和sentinel同时存在时,优先使用sentinel配置
+ #wx.mp.config-storage.redis.sentinel-ips=127.0.0.1:16379,127.0.0.1:26379
+ #wx.mp.config-storage.redis.sentinel-name=mymaster
+ # http客户端配置
+ wx.mp.config-storage.http-client-type=httpclient # http客户端类型: HttpClient(默认), OkHttp, JoddHttp
+ wx.mp.config-storage.http-proxy-host=
+ wx.mp.config-storage.http-proxy-port=
+ wx.mp.config-storage.http-proxy-username=
+ wx.mp.config-storage.http-proxy-password=
+ # 公众号地址host配置
+ #wx.mp.hosts.api-host=http://proxy.com/
+ #wx.mp.hosts.open-host=http://proxy.com/
+ #wx.mp.hosts.mp-host=http://proxy.com/
+ ```
+3. 自动注入的类型
+
+- `WxMpService`
+- `WxMpConfigStorage`
+
+4、参考 demo:
+https://github.com/binarywang/wx-java-mp-demo
diff --git a/solon-plugins/wx-java-qidian-solon-plugin/pom.xml b/solon-plugins/wx-java-qidian-solon-plugin/pom.xml
new file mode 100644
index 0000000000..c1e31e5839
--- /dev/null
+++ b/solon-plugins/wx-java-qidian-solon-plugin/pom.xml
@@ -0,0 +1,38 @@
+
+
+
+ wx-java-solon-plugins
+ com.github.binarywang
+ 4.6.4.B
+
+ 4.0.0
+
+ wx-java-qidian-solon-plugin
+ WxJava - Solon Plugin for QiDian
+ 腾讯企点的 Solon Plugin
+
+
+
+ com.github.binarywang
+ weixin-java-qidian
+ ${project.version}
+
+
+ redis.clients
+ jedis
+ 4.3.2
+ compile
+
+
+ org.jodd
+ jodd-http
+ provided
+
+
+ com.squareup.okhttp3
+ okhttp
+ provided
+
+
+
+
diff --git a/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/config/WxQidianServiceAutoConfiguration.java b/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/config/WxQidianServiceAutoConfiguration.java
new file mode 100644
index 0000000000..f3dce59a73
--- /dev/null
+++ b/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/config/WxQidianServiceAutoConfiguration.java
@@ -0,0 +1,63 @@
+package com.binarywang.solon.wxjava.qidian.config;
+
+import com.binarywang.solon.wxjava.qidian.enums.HttpClientType;
+import com.binarywang.solon.wxjava.qidian.properties.WxQidianProperties;
+import me.chanjar.weixin.qidian.api.WxQidianService;
+import me.chanjar.weixin.qidian.api.impl.WxQidianServiceHttpClientImpl;
+import me.chanjar.weixin.qidian.api.impl.WxQidianServiceImpl;
+import me.chanjar.weixin.qidian.api.impl.WxQidianServiceJoddHttpImpl;
+import me.chanjar.weixin.qidian.api.impl.WxQidianServiceOkHttpImpl;
+import me.chanjar.weixin.qidian.config.WxQidianConfigStorage;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+
+/**
+ * 腾讯企点相关服务自动注册.
+ *
+ * @author alegria
+ */
+@Configuration
+public class WxQidianServiceAutoConfiguration {
+
+ @Bean
+ @Condition(onMissingBean = WxQidianService.class)
+ public WxQidianService wxQidianService(WxQidianConfigStorage configStorage, WxQidianProperties wxQidianProperties) {
+ HttpClientType httpClientType = wxQidianProperties.getConfigStorage().getHttpClientType();
+ WxQidianService wxQidianService;
+ switch (httpClientType) {
+ case OkHttp:
+ wxQidianService = newWxQidianServiceOkHttpImpl();
+ break;
+ case JoddHttp:
+ wxQidianService = newWxQidianServiceJoddHttpImpl();
+ break;
+ case HttpClient:
+ wxQidianService = newWxQidianServiceHttpClientImpl();
+ break;
+ default:
+ wxQidianService = newWxQidianServiceImpl();
+ break;
+ }
+
+ wxQidianService.setWxMpConfigStorage(configStorage);
+ return wxQidianService;
+ }
+
+ private WxQidianService newWxQidianServiceImpl() {
+ return new WxQidianServiceImpl();
+ }
+
+ private WxQidianService newWxQidianServiceHttpClientImpl() {
+ return new WxQidianServiceHttpClientImpl();
+ }
+
+ private WxQidianService newWxQidianServiceOkHttpImpl() {
+ return new WxQidianServiceOkHttpImpl();
+ }
+
+ private WxQidianService newWxQidianServiceJoddHttpImpl() {
+ return new WxQidianServiceJoddHttpImpl();
+ }
+
+}
diff --git a/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/config/WxQidianStorageAutoConfiguration.java b/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/config/WxQidianStorageAutoConfiguration.java
new file mode 100644
index 0000000000..7f78864226
--- /dev/null
+++ b/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/config/WxQidianStorageAutoConfiguration.java
@@ -0,0 +1,135 @@
+package com.binarywang.solon.wxjava.qidian.config;
+
+import com.binarywang.solon.wxjava.qidian.enums.StorageType;
+import com.binarywang.solon.wxjava.qidian.properties.RedisProperties;
+import com.binarywang.solon.wxjava.qidian.properties.WxQidianProperties;
+import com.google.common.collect.Sets;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.redis.JedisWxRedisOps;
+import me.chanjar.weixin.common.redis.WxRedisOps;
+import me.chanjar.weixin.qidian.bean.WxQidianHostConfig;
+import me.chanjar.weixin.qidian.config.WxQidianConfigStorage;
+import me.chanjar.weixin.qidian.config.impl.WxQidianDefaultConfigImpl;
+import me.chanjar.weixin.qidian.config.impl.WxQidianRedisConfigImpl;
+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.annotation.Inject;
+import org.noear.solon.core.AppContext;
+import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.JedisPoolConfig;
+import redis.clients.jedis.JedisSentinelPool;
+import redis.clients.jedis.util.Pool;
+
+import java.util.Set;
+
+/**
+ * 腾讯企点存储策略自动配置.
+ *
+ * @author alegria
+ */
+@Slf4j
+@Configuration
+@RequiredArgsConstructor
+public class WxQidianStorageAutoConfiguration {
+ private final AppContext applicationContext;
+
+ private final WxQidianProperties wxQidianProperties;
+
+ @Inject("${wx.mp.config-storage.redis.host:")
+ private String redisHost;
+
+ @Inject("${wx.mp.configStorage.redis.host:")
+ private String redisHost2;
+
+ @Bean
+ @Condition(onMissingBean=WxQidianConfigStorage.class)
+ public WxQidianConfigStorage wxQidianConfigStorage() {
+ StorageType type = wxQidianProperties.getConfigStorage().getType();
+ WxQidianConfigStorage config;
+ switch (type) {
+ case Jedis:
+ config = jedisConfigStorage();
+ break;
+ default:
+ config = defaultConfigStorage();
+ break;
+ }
+ // wx host config
+ if (null != wxQidianProperties.getHosts() && StringUtils.isNotEmpty(wxQidianProperties.getHosts().getApiHost())) {
+ WxQidianHostConfig hostConfig = new WxQidianHostConfig();
+ hostConfig.setApiHost(wxQidianProperties.getHosts().getApiHost());
+ hostConfig.setQidianHost(wxQidianProperties.getHosts().getQidianHost());
+ hostConfig.setOpenHost(wxQidianProperties.getHosts().getOpenHost());
+ config.setHostConfig(hostConfig);
+ }
+ return config;
+ }
+
+ private WxQidianConfigStorage defaultConfigStorage() {
+ WxQidianDefaultConfigImpl config = new WxQidianDefaultConfigImpl();
+ setWxMpInfo(config);
+ return config;
+ }
+
+ private WxQidianConfigStorage jedisConfigStorage() {
+ Pool jedisPool;
+ if (StringUtils.isNotEmpty(redisHost) || StringUtils.isNotEmpty(redisHost2)) {
+ jedisPool = getJedisPool();
+ } else {
+ jedisPool = applicationContext.getBean(JedisPool.class);
+ }
+ WxRedisOps redisOps = new JedisWxRedisOps(jedisPool);
+ WxQidianRedisConfigImpl wxQidianRedisConfig = new WxQidianRedisConfigImpl(redisOps,
+ wxQidianProperties.getConfigStorage().getKeyPrefix());
+ setWxMpInfo(wxQidianRedisConfig);
+ return wxQidianRedisConfig;
+ }
+
+ private void setWxMpInfo(WxQidianDefaultConfigImpl config) {
+ WxQidianProperties properties = wxQidianProperties;
+ WxQidianProperties.ConfigStorage configStorageProperties = properties.getConfigStorage();
+ config.setAppId(properties.getAppId());
+ config.setSecret(properties.getSecret());
+ config.setToken(properties.getToken());
+ config.setAesKey(properties.getAesKey());
+
+ config.setHttpProxyHost(configStorageProperties.getHttpProxyHost());
+ config.setHttpProxyUsername(configStorageProperties.getHttpProxyUsername());
+ config.setHttpProxyPassword(configStorageProperties.getHttpProxyPassword());
+ if (configStorageProperties.getHttpProxyPort() != null) {
+ config.setHttpProxyPort(configStorageProperties.getHttpProxyPort());
+ }
+ }
+
+ private Pool getJedisPool() {
+ WxQidianProperties.ConfigStorage storage = wxQidianProperties.getConfigStorage();
+ RedisProperties 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);
+ if (StringUtils.isNotEmpty(redis.getSentinelIps())) {
+
+ Set sentinels = Sets.newHashSet(redis.getSentinelIps().split(","));
+ return new JedisSentinelPool(redis.getSentinelName(), sentinels,config);
+ }
+
+ return new JedisPool(config, redis.getHost(), redis.getPort(), redis.getTimeout(), redis.getPassword(),
+ redis.getDatabase());
+ }
+}
diff --git a/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/enums/HttpClientType.java b/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/enums/HttpClientType.java
new file mode 100644
index 0000000000..0b94821da7
--- /dev/null
+++ b/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/enums/HttpClientType.java
@@ -0,0 +1,22 @@
+package com.binarywang.solon.wxjava.qidian.enums;
+
+/**
+ * httpclient类型.
+ *
+ * @author Binary Wang
+ * created on 2020-08-30
+ */
+public enum HttpClientType {
+ /**
+ * HttpClient.
+ */
+ HttpClient,
+ /**
+ * OkHttp.
+ */
+ OkHttp,
+ /**
+ * JoddHttp.
+ */
+ JoddHttp,
+}
diff --git a/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/enums/StorageType.java b/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/enums/StorageType.java
new file mode 100644
index 0000000000..c4bd2f72cf
--- /dev/null
+++ b/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/enums/StorageType.java
@@ -0,0 +1,26 @@
+package com.binarywang.solon.wxjava.qidian.enums;
+
+/**
+ * storage类型.
+ *
+ * @author Binary Wang
+ * created on 2020-08-30
+ */
+public enum StorageType {
+ /**
+ * 内存.
+ */
+ Memory,
+ /**
+ * redis(JedisClient).
+ */
+ Jedis,
+ /**
+ * redis(Redisson).
+ */
+ Redisson,
+ /**
+ * redis(RedisTemplate).
+ */
+ RedisTemplate
+}
diff --git a/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/integration/WxQidianPluginImpl.java b/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/integration/WxQidianPluginImpl.java
new file mode 100644
index 0000000000..2a97b512fd
--- /dev/null
+++ b/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/integration/WxQidianPluginImpl.java
@@ -0,0 +1,20 @@
+package com.binarywang.solon.wxjava.qidian.integration;
+
+import com.binarywang.solon.wxjava.qidian.config.WxQidianServiceAutoConfiguration;
+import com.binarywang.solon.wxjava.qidian.config.WxQidianStorageAutoConfiguration;
+import com.binarywang.solon.wxjava.qidian.properties.WxQidianProperties;
+import org.noear.solon.core.AppContext;
+import org.noear.solon.core.Plugin;
+
+/**
+ * @author noear 2024/9/2 created
+ */
+public class WxQidianPluginImpl implements Plugin{
+ @Override
+ public void start(AppContext context) throws Throwable {
+ context.beanMake(WxQidianProperties.class);
+
+ context.beanMake(WxQidianStorageAutoConfiguration.class);
+ context.beanMake(WxQidianServiceAutoConfiguration.class);
+ }
+}
diff --git a/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/properties/HostConfig.java b/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/properties/HostConfig.java
new file mode 100644
index 0000000000..08546d8da6
--- /dev/null
+++ b/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/properties/HostConfig.java
@@ -0,0 +1,18 @@
+package com.binarywang.solon.wxjava.qidian.properties;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class HostConfig implements Serializable {
+
+ private static final long serialVersionUID = -4172767630740346001L;
+
+ private String apiHost;
+
+ private String openHost;
+
+ private String qidianHost;
+
+}
diff --git a/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/properties/RedisProperties.java b/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/properties/RedisProperties.java
new file mode 100644
index 0000000000..a6b10a9e0f
--- /dev/null
+++ b/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/properties/RedisProperties.java
@@ -0,0 +1,56 @@
+package com.binarywang.solon.wxjava.qidian.properties;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * redis 配置属性.
+ *
+ * @author Binary Wang
+ * created on 2020-08-30
+ */
+@Data
+public class RedisProperties implements Serializable {
+ private static final long serialVersionUID = -5924815351660074401L;
+
+ /**
+ * 主机地址.
+ */
+ private String host = "127.0.0.1";
+
+ /**
+ * 端口号.
+ */
+ private int port = 6379;
+
+ /**
+ * 密码.
+ */
+ private String password;
+
+ /**
+ * 超时.
+ */
+ private int timeout = 2000;
+
+ /**
+ * 数据库.
+ */
+ private int database = 0;
+
+ /**
+ * sentinel ips
+ */
+ private String sentinelIps;
+
+ /**
+ * sentinel name
+ */
+ private String sentinelName;
+
+ private Integer maxActive;
+ private Integer maxIdle;
+ private Integer maxWaitMillis;
+ private Integer minIdle;
+}
diff --git a/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/properties/WxQidianProperties.java b/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/properties/WxQidianProperties.java
new file mode 100644
index 0000000000..67c4dba543
--- /dev/null
+++ b/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/properties/WxQidianProperties.java
@@ -0,0 +1,101 @@
+package com.binarywang.solon.wxjava.qidian.properties;
+
+import com.binarywang.solon.wxjava.qidian.enums.HttpClientType;
+import com.binarywang.solon.wxjava.qidian.enums.StorageType;
+import lombok.Data;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.annotation.Inject;
+
+import java.io.Serializable;
+
+import static com.binarywang.solon.wxjava.qidian.enums.StorageType.Memory;
+import static com.binarywang.solon.wxjava.qidian.properties.WxQidianProperties.PREFIX;
+
+/**
+ * 企点接入相关配置属性.
+ *
+ * @author someone
+ */
+@Data
+@Configuration
+@Inject("${" + PREFIX + "}")
+public class WxQidianProperties {
+ public static final String PREFIX = "wx.qidian";
+
+ /**
+ * 设置腾讯企点的appid.
+ */
+ private String appId;
+
+ /**
+ * 设置腾讯企点的app secret.
+ */
+ private String secret;
+
+ /**
+ * 设置腾讯企点的token.
+ */
+ private String token;
+
+ /**
+ * 设置腾讯企点的EncodingAESKey.
+ */
+ private String aesKey;
+
+ /**
+ * 自定义host配置
+ */
+ private HostConfig hosts;
+
+ /**
+ * 存储策略
+ */
+ private ConfigStorage configStorage = new ConfigStorage();
+
+ @Data
+ public static class ConfigStorage implements Serializable {
+ private static final long serialVersionUID = 4815731027000065434L;
+
+ /**
+ * 存储类型.
+ */
+ private StorageType type = Memory;
+
+ /**
+ * 指定key前缀.
+ */
+ private String keyPrefix = "wx";
+
+ /**
+ * redis连接配置.
+ */
+ private RedisProperties redis = new RedisProperties();
+
+ /**
+ * http客户端类型.
+ */
+ private HttpClientType httpClientType = HttpClientType.HttpClient;
+
+ /**
+ * http代理主机.
+ */
+ private String httpProxyHost;
+
+ /**
+ * http代理端口.
+ */
+ private Integer httpProxyPort;
+
+ /**
+ * http代理用户名.
+ */
+ private String httpProxyUsername;
+
+ /**
+ * http代理密码.
+ */
+ private String httpProxyPassword;
+
+ }
+
+}
diff --git a/solon-plugins/wx-java-qidian-solon-plugin/src/main/resources/META-INF/solon/wx-java-qidian-solon-plugin.properties b/solon-plugins/wx-java-qidian-solon-plugin/src/main/resources/META-INF/solon/wx-java-qidian-solon-plugin.properties
new file mode 100644
index 0000000000..a60c450b06
--- /dev/null
+++ b/solon-plugins/wx-java-qidian-solon-plugin/src/main/resources/META-INF/solon/wx-java-qidian-solon-plugin.properties
@@ -0,0 +1,2 @@
+solon.plugin=com.binarywang.solon.wxjava.qidian.integration.WxQidianPluginImpl
+solon.plugin.priority=10