🎨 #1521 微信小程序 spring-boot-starter 增加一些新特性

This commit is contained in:
Mario Luo 2020-04-18 23:08:25 +08:00 committed by GitHub
parent 340f341947
commit 7feca8d962
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 477 additions and 63 deletions

View File

@ -253,6 +253,14 @@
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.12.0</version>
<optional>true</optional>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.8.23.RELEASE</version>
<optional>true</optional>
<scope>provided</scope>
</dependency>
<dependency>

View File

@ -19,6 +19,17 @@
<artifactId>weixin-java-miniapp</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>${spring.boot.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>

View File

@ -4,15 +4,23 @@ import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
import cn.binarywang.wx.miniapp.config.WxMaConfig;
import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
import cn.binarywang.wx.miniapp.config.impl.WxMaRedisBetterConfigImpl;
import com.binarywang.spring.starter.wxjava.miniapp.properties.WxMaProperties;
import lombok.AllArgsConstructor;
import me.chanjar.weixin.common.redis.JedisWxRedisOps;
import me.chanjar.weixin.common.redis.RedisTemplateWxRedisOps;
import me.chanjar.weixin.common.redis.WxRedisOps;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
/**
* 自动配置.
@ -26,7 +34,9 @@ import org.springframework.context.annotation.Configuration;
@EnableConfigurationProperties(WxMaProperties.class)
@ConditionalOnProperty(prefix = "wx.miniapp", value = "enabled", matchIfMissing = true)
public class WxMaAutoConfiguration {
private WxMaProperties properties;
private final WxMaProperties wxMaProperties;
private final ApplicationContext applicationContext;
/**
* 小程序service.
@ -35,16 +45,87 @@ public class WxMaAutoConfiguration {
*/
@Bean
@ConditionalOnMissingBean(WxMaService.class)
public WxMaService service() {
WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
config.setAppid(StringUtils.trimToNull(this.properties.getAppid()));
config.setSecret(StringUtils.trimToNull(this.properties.getSecret()));
config.setToken(StringUtils.trimToNull(this.properties.getToken()));
config.setAesKey(StringUtils.trimToNull(this.properties.getAesKey()));
config.setMsgDataFormat(StringUtils.trimToNull(this.properties.getMsgDataFormat()));
public WxMaService service(WxMaConfig wxMaConfig) {
final WxMaServiceImpl service = new WxMaServiceImpl();
service.setWxMaConfig(config);
service.setWxMaConfig(wxMaConfig);
return service;
}
@Bean
@ConditionalOnMissingBean(WxMaConfig.class)
public WxMaConfig wxMaConfig() {
WxMaProperties.StorageType type = wxMaProperties.getConfigStorage().getType();
WxMaDefaultConfigImpl config;
if (type == WxMaProperties.StorageType.jedis) {
config = wxMaInJedisConfigStorage();
} else if (type == WxMaProperties.StorageType.redistemplate) {
config = wxMaInRedisTemplateConfigStorage();
} else {
config = wxMaInMemoryConfigStorage();
}
config.setAppid(StringUtils.trimToNull(this.wxMaProperties.getAppid()));
config.setSecret(StringUtils.trimToNull(this.wxMaProperties.getSecret()));
config.setToken(StringUtils.trimToNull(this.wxMaProperties.getToken()));
config.setAesKey(StringUtils.trimToNull(this.wxMaProperties.getAesKey()));
config.setMsgDataFormat(StringUtils.trimToNull(this.wxMaProperties.getMsgDataFormat()));
WxMaProperties.ConfigStorage configStorageProperties = wxMaProperties.getConfigStorage();
config.setHttpProxyHost(configStorageProperties.getHttpProxyHost());
config.setHttpProxyUsername(configStorageProperties.getHttpProxyUsername());
config.setHttpProxyPassword(configStorageProperties.getHttpProxyPassword());
if (configStorageProperties.getHttpProxyPort() != null) {
config.setHttpProxyPort(configStorageProperties.getHttpProxyPort());
}
return config;
}
private WxMaDefaultConfigImpl wxMaInMemoryConfigStorage() {
WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
return config;
}
private WxMaDefaultConfigImpl wxMaInJedisConfigStorage() {
WxMaProperties.RedisProperties redisProperties = wxMaProperties.getConfigStorage().getRedis();
JedisPool jedisPool;
if (redisProperties != null && StringUtils.isNotEmpty(redisProperties.getHost())) {
jedisPool = getJedisPool();
} else {
jedisPool = applicationContext.getBean(JedisPool.class);
}
WxRedisOps redisOps = new JedisWxRedisOps(jedisPool);
WxMaRedisBetterConfigImpl wxMaRedisConfig = new WxMaRedisBetterConfigImpl(redisOps, wxMaProperties.getConfigStorage().getKeyPrefix());
return wxMaRedisConfig;
}
private WxMaDefaultConfigImpl wxMaInRedisTemplateConfigStorage() {
StringRedisTemplate redisTemplate = applicationContext.getBean(StringRedisTemplate.class);
WxRedisOps redisOps = new RedisTemplateWxRedisOps(redisTemplate);
WxMaRedisBetterConfigImpl wxMaRedisConfig = new WxMaRedisBetterConfigImpl(redisOps, wxMaProperties.getConfigStorage().getKeyPrefix());
return wxMaRedisConfig;
}
private JedisPool getJedisPool() {
WxMaProperties.ConfigStorage storage = wxMaProperties.getConfigStorage();
WxMaProperties.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());
}
}

View File

@ -3,6 +3,8 @@ package com.binarywang.spring.starter.wxjava.miniapp.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.io.Serializable;
/**
* 属性配置类.
*
@ -36,4 +38,112 @@ public class WxMaProperties {
* 消息格式XML或者JSON.
*/
private String msgDataFormat;
/**
* 存储策略
*/
private ConfigStorage configStorage = new ConfigStorage();
@Data
public static class ConfigStorage implements Serializable {
private static final long serialVersionUID = 4815731027000065434L;
/**
* 存储类型.
*/
private StorageType type = StorageType.memory;
/**
* 指定key前缀.
*/
private String keyPrefix = "wa";
/**
* redis连接配置.
*/
private RedisProperties redis;
/**
* http客户端类型.
*/
private HttpClientType httpClientType = HttpClientType.httpclient;
/**
* http代理主机.
*/
private String httpProxyHost;
/**
* http代理端口.
*/
private Integer httpProxyPort;
/**
* http代理用户名.
*/
private String httpProxyUsername;
/**
* http代理密码.
*/
private String httpProxyPassword;
}
public enum StorageType {
/**
* 内存.
*/
memory,
/**
* redis(JedisClient).
*/
jedis,
/**
* redis(RedisTemplate).
*/
redistemplate
}
public enum HttpClientType {
/**
* HttpClient.
*/
httpclient
}
@Data
public static class RedisProperties 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;
}
}

View File

@ -1,13 +1,13 @@
package com.binarywang.spring.starter.wxjava.mp.config;
import com.binarywang.spring.starter.wxjava.mp.extend.RedisTemplateWxMpRedisOps;
import com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties;
import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.common.redis.JedisWxRedisOps;
import me.chanjar.weixin.common.redis.RedisTemplateWxRedisOps;
import me.chanjar.weixin.common.redis.WxRedisOps;
import me.chanjar.weixin.mp.config.WxMpConfigStorage;
import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
import me.chanjar.weixin.mp.config.impl.WxMpRedisConfigImpl;
import me.chanjar.weixin.mp.config.redis.JedisWxMpRedisOps;
import me.chanjar.weixin.mp.config.redis.WxMpRedisOps;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@ -65,7 +65,7 @@ public class WxMpStorageAutoConfiguration {
} else {
jedisPool = applicationContext.getBean(JedisPool.class);
}
WxMpRedisOps redisOps = new JedisWxMpRedisOps(jedisPool);
WxRedisOps redisOps = new JedisWxRedisOps(jedisPool);
WxMpRedisConfigImpl wxMpRedisConfig = new WxMpRedisConfigImpl(redisOps, wxMpProperties.getConfigStorage().getKeyPrefix());
setWxMpInfo(wxMpRedisConfig);
return wxMpRedisConfig;
@ -73,7 +73,7 @@ public class WxMpStorageAutoConfiguration {
private WxMpConfigStorage wxMpInRedisTemplateConfigStorage() {
StringRedisTemplate redisTemplate = applicationContext.getBean(StringRedisTemplate.class);
WxMpRedisOps redisOps = new RedisTemplateWxMpRedisOps(redisTemplate);
WxRedisOps redisOps = new RedisTemplateWxRedisOps(redisTemplate);
WxMpRedisConfigImpl wxMpRedisConfig = new WxMpRedisConfigImpl(redisOps, wxMpProperties.getConfigStorage().getKeyPrefix());
setWxMpInfo(wxMpRedisConfig);
return wxMpRedisConfig;

View File

@ -122,11 +122,18 @@
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>com.github.jedis-lock</groupId>
<artifactId>jedis-lock</artifactId>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
</dependency>
</dependencies>
<build>

View File

@ -1,14 +1,14 @@
package me.chanjar.weixin.mp.config.redis;
package me.chanjar.weixin.common.redis;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
/**
* 微信公众号redis操作基本类
* 微信redis操作基本类
* <p>
* 非内置实现redis相关操作, 请实现该类
*/
public class BaseWxMpRedisOps implements WxMpRedisOps {
public abstract class BaseWxRedisOps implements WxRedisOps {
@Override
public String getValue(String key) {

View File

@ -1,6 +1,6 @@
package me.chanjar.weixin.mp.config.redis;
package me.chanjar.weixin.common.redis;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.common.util.locks.JedisDistributedLock;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
@ -8,13 +8,10 @@ import redis.clients.jedis.JedisPool;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
/**
* Jedis实现相关操作
*/
@AllArgsConstructor
public class JedisWxMpRedisOps implements WxMpRedisOps {
@RequiredArgsConstructor
public class JedisWxRedisOps implements WxRedisOps {
private JedisPool jedisPool;
private final JedisPool jedisPool;
@Override
public String getValue(String key) {

View File

@ -1,7 +1,6 @@
package com.binarywang.spring.starter.wxjava.mp.extend;
package me.chanjar.weixin.common.redis;
import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.mp.config.redis.BaseWxMpRedisOps;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.util.concurrent.TimeUnit;
@ -9,7 +8,7 @@ import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@RequiredArgsConstructor
public class RedisTemplateWxMpRedisOps extends BaseWxMpRedisOps {
public class RedisTemplateWxRedisOps implements WxRedisOps {
private final StringRedisTemplate redisTemplate;

View File

@ -0,0 +1,39 @@
package me.chanjar.weixin.common.redis;
import lombok.RequiredArgsConstructor;
import org.redisson.api.RedissonClient;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
@RequiredArgsConstructor
public class RedissonWxRedisOps implements WxRedisOps {
private final RedissonClient redissonClient;
@Override
public String getValue(String key) {
Object value = redissonClient.getBucket(key).get();
return value == null ? null : value.toString();
}
@Override
public void setValue(String key, String value, int expire, TimeUnit timeUnit) {
redissonClient.getBucket(key).set(value, expire, timeUnit);
}
@Override
public Long getExpire(String key) {
return redissonClient.getBucket(key).remainTimeToLive();
}
@Override
public void expire(String key, int expire, TimeUnit timeUnit) {
redissonClient.getBucket(key).expire(expire, timeUnit);
}
@Override
public Lock getLock(String key) {
return redissonClient.getLock(key);
}
}

View File

@ -0,0 +1,27 @@
package me.chanjar.weixin.common.redis;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
/**
* 微信Redis相关操作
* <p>
* 该接口不承诺稳定, 外部实现请继承{@link BaseWxRedisOps}
*
* @see BaseWxRedisOps 实现需要继承该类
* @see JedisWxRedisOps jedis实现
* @see RedissonWxRedisOps redisson实现
* @see RedisTemplateWxRedisOps redisTemplate实现
*/
public interface WxRedisOps {
String getValue(String key);
void setValue(String key, String value, int expire, TimeUnit timeUnit);
Long getExpire(String key);
void expire(String key, int expire, TimeUnit timeUnit);
Lock getLock(String key);
}

View File

@ -46,8 +46,8 @@ public class WxMaDefaultConfigImpl implements WxMaConfig {
*/
private volatile String cardApiTicket;
private volatile long cardApiTicketExpiresTime;
private Lock jsapiTicketLock = new ReentrantLock();
private Lock cardApiTicketLock = new ReentrantLock();
protected volatile Lock jsapiTicketLock = new ReentrantLock();
protected volatile Lock cardApiTicketLock = new ReentrantLock();
private volatile ApacheHttpClientBuilder apacheHttpClientBuilder;
/**

View File

@ -0,0 +1,130 @@
package cn.binarywang.wx.miniapp.config.impl;
import cn.binarywang.wx.miniapp.constant.TicketType;
import me.chanjar.weixin.common.redis.JedisWxRedisOps;
import me.chanjar.weixin.common.redis.WxRedisOps;
import redis.clients.jedis.JedisPool;
import java.util.concurrent.TimeUnit;
/**
* 基于redis存储的微信小程序配置类
*/
public class WxMaRedisBetterConfigImpl extends WxMaDefaultConfigImpl {
private static final String ACCESS_TOKEN_KEY_TPL = "%s:access_token:%s";
private static final String TICKET_KEY_TPL = "%s:ticket:key:%s:%s";
private static final String LOCK_KEY_TPL = "%s:lock:%s:";
private final WxRedisOps redisOps;
private final String keyPrefix;
private volatile String accessTokenKey;
private volatile String lockKey;
public WxMaRedisBetterConfigImpl(JedisPool jedisPool) {
this(new JedisWxRedisOps(jedisPool), "wa");
}
public WxMaRedisBetterConfigImpl(WxRedisOps redisOps, String keyPrefix) {
this.redisOps = redisOps;
this.keyPrefix = keyPrefix;
}
@Override
public void setAppid(String appId) {
super.setAppid(appId);
this.accessTokenKey = String.format(ACCESS_TOKEN_KEY_TPL, this.keyPrefix, appId);
this.lockKey = String.format(LOCK_KEY_TPL, this.keyPrefix, appId);
super.accessTokenLock = this.redisOps.getLock(lockKey.concat("accessTokenLock"));
super.jsapiTicketLock = this.redisOps.getLock(lockKey.concat("jsapiTicketLock"));
super.cardApiTicketLock = this.redisOps.getLock(lockKey.concat("cardApiTicketLock"));
}
//------------------------------------------------------------------------
// token相关
//------------------------------------------------------------------------
@Override
public String getAccessToken() {
return redisOps.getValue(this.accessTokenKey);
}
@Override
public boolean isAccessTokenExpired() {
Long expire = redisOps.getExpire(this.accessTokenKey);
return expire == null || expire < 2;
}
@Override
public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) {
redisOps.setValue(this.accessTokenKey, accessToken, expiresInSeconds - 200, TimeUnit.SECONDS);
}
@Override
public void expireAccessToken() {
redisOps.expire(this.accessTokenKey, 0, TimeUnit.SECONDS);
}
//------------------------------------------------------------------------
// ticket相关
//------------------------------------------------------------------------
@Override
public String getJsapiTicket() {
return doGetTicket(TicketType.JSAPI);
}
@Override
public boolean isJsapiTicketExpired() {
return doIsTicketExpired(TicketType.JSAPI);
}
@Override
public void expireJsapiTicket() {
doExpireTicket(TicketType.JSAPI);
}
@Override
public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) {
doUpdateTicket(TicketType.JSAPI, jsapiTicket, expiresInSeconds);
}
@Override
public String getCardApiTicket() {
return doGetTicket(TicketType.WX_CARD);
}
@Override
public boolean isCardApiTicketExpired() {
return doIsTicketExpired(TicketType.WX_CARD);
}
@Override
public void expireCardApiTicket() {
doExpireTicket(TicketType.WX_CARD);
}
@Override
public synchronized void updateCardApiTicket(String cardApiTicket, int expiresInSeconds) {
doUpdateTicket(TicketType.WX_CARD, cardApiTicket, expiresInSeconds);
}
private String getTicketRedisKey(TicketType type) {
return String.format(TICKET_KEY_TPL, this.keyPrefix, this.appid, type.getCode());
}
private String doGetTicket(TicketType type) {
return redisOps.getValue(this.getTicketRedisKey(type));
}
private boolean doIsTicketExpired(TicketType type) {
return redisOps.getExpire(this.getTicketRedisKey(type)) < 2;
}
private void doUpdateTicket(TicketType type, String ticket, int expiresInSeconds) {
redisOps.setValue(this.getTicketRedisKey(type), ticket, expiresInSeconds - 200, TimeUnit.SECONDS);
}
private void doExpireTicket(TicketType type) {
redisOps.expire(this.getTicketRedisKey(type), 0, TimeUnit.SECONDS);
}
}

View File

@ -0,0 +1,30 @@
package cn.binarywang.wx.miniapp.constant;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* <pre>
* ticket类型枚举
* Created by Binary Wang on 2018/11/18.
* </pre>
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@Getter
@RequiredArgsConstructor
public enum TicketType {
/**
* jsapi
*/
JSAPI("jsapi"),
/**
* 微信卡券
*/
WX_CARD("wx_card");
/**
* type代码
*/
private final String code;
}

View File

@ -1,8 +1,8 @@
package me.chanjar.weixin.mp.config.impl;
import lombok.Data;
import me.chanjar.weixin.mp.config.redis.JedisWxMpRedisOps;
import me.chanjar.weixin.mp.config.redis.WxMpRedisOps;
import me.chanjar.weixin.common.redis.JedisWxRedisOps;
import me.chanjar.weixin.common.redis.WxRedisOps;
import me.chanjar.weixin.mp.enums.TicketType;
import redis.clients.jedis.JedisPool;
@ -22,22 +22,22 @@ import java.util.concurrent.TimeUnit;
@SuppressWarnings("hiding")
public class WxMpRedisConfigImpl extends WxMpDefaultConfigImpl {
private static final long serialVersionUID = -988502871997239733L;
private static final String ACCESS_TOKEN_KEY_TPL = "%s:access_token:%s";
private static final String TICKET_KEY_TPL = "%s:ticket:key:%s:%s";
private static final String LOCK_KEY_TPL = "%s:lock:%s:";
private final WxMpRedisOps redisOps;
private final WxRedisOps redisOps;
private final String keyPrefix;
private String accessTokenKey;
private String lockKey;
public WxMpRedisConfigImpl(JedisPool jedisPool) {
this(new JedisWxMpRedisOps(jedisPool), "wx");
this(new JedisWxRedisOps(jedisPool), "wx");
}
public WxMpRedisConfigImpl(WxMpRedisOps redisOps, String keyPrefix) {
public WxMpRedisConfigImpl(WxRedisOps redisOps, String keyPrefix) {
this.redisOps = redisOps;
this.keyPrefix = keyPrefix;
}

View File

@ -1,25 +0,0 @@
package me.chanjar.weixin.mp.config.redis;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
/**
* 微信公众号Redis相关操作
* <p>
* 该接口不承诺稳定, 外部实现请继承{@link BaseWxMpRedisOps}
*
* @see BaseWxMpRedisOps 实现需要继承该类
* @see JedisWxMpRedisOps jedis实现
*/
public interface WxMpRedisOps {
String getValue(String key);
void setValue(String key, String value, int expire, TimeUnit timeUnit);
Long getExpire(String key);
void expire(String key, int expire, TimeUnit timeUnit);
Lock getLock(String key);
}