Browse Source

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

Mario Luo 5 years ago
parent
commit
7feca8d962
16 changed files with 477 additions and 63 deletions
  1. 8 0
      pom.xml
  2. 11 0
      spring-boot-starters/wx-java-miniapp-spring-boot-starter/pom.xml
  3. 91 10
      spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/config/WxMaAutoConfiguration.java
  4. 110 0
      spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/properties/WxMaProperties.java
  5. 5 5
      spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/WxMpStorageAutoConfiguration.java
  6. 8 1
      weixin-java-common/pom.xml
  7. 3 3
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/config/redis/BaseWxMpRedisOps.java
  8. 5 8
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/config/redis/JedisWxMpRedisOps.java
  9. 2 3
      spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/extend/RedisTemplateWxMpRedisOps.java
  10. 39 0
      weixin-java-common/src/main/java/me/chanjar/weixin/common/redis/RedissonWxRedisOps.java
  11. 27 0
      weixin-java-common/src/main/java/me/chanjar/weixin/common/redis/WxRedisOps.java
  12. 2 2
      weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaDefaultConfigImpl.java
  13. 130 0
      weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedisBetterConfigImpl.java
  14. 30 0
      weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/TicketType.java
  15. 6 6
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/config/impl/WxMpRedisConfigImpl.java
  16. 0 25
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/config/redis/WxMpRedisOps.java

+ 8 - 0
pom.xml

@@ -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>

+ 11 - 0
spring-boot-starters/wx-java-miniapp-spring-boot-starter/pom.xml

@@ -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>

+ 91 - 10
spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/config/WxMaAutoConfiguration.java

@@ -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());
+  }
 }

+ 110 - 0
spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/properties/WxMaProperties.java

@@ -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;
+  }
 }

+ 5 - 5
spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/WxMpStorageAutoConfiguration.java

@@ -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;

+ 8 - 1
weixin-java-common/pom.xml

@@ -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>

+ 3 - 3
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/config/redis/BaseWxMpRedisOps.java

@@ -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) {

+ 5 - 8
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/config/redis/JedisWxMpRedisOps.java

@@ -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) {

+ 2 - 3
spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/extend/RedisTemplateWxMpRedisOps.java

@@ -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;
 

+ 39 - 0
weixin-java-common/src/main/java/me/chanjar/weixin/common/redis/RedissonWxRedisOps.java

@@ -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);
+  }
+}

+ 27 - 0
weixin-java-common/src/main/java/me/chanjar/weixin/common/redis/WxRedisOps.java

@@ -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);
+}

+ 2 - 2
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaDefaultConfigImpl.java

@@ -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;
 
   /**

+ 130 - 0
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedisBetterConfigImpl.java

@@ -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);
+  }
+
+}

+ 30 - 0
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/TicketType.java

@@ -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;
+
+}

+ 6 - 6
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/config/impl/WxMpRedisConfigImpl.java

@@ -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;
   }

+ 0 - 25
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/config/redis/WxMpRedisOps.java

@@ -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);
-}