diff --git a/CHANGE.md b/CHANGE.md
index 6a96eda2..9f835539 100644
--- a/CHANGE.md
+++ b/CHANGE.md
@@ -701,4 +701,8 @@
+ weixin4j-base:修改Memcached-Java-Client的依赖
- + weixin4j-base:系统配置类抽象化
\ No newline at end of file
+ + weixin4j-base:系统配置类抽象化
+
+* 2016-05-28
+
+ + 重构Cache实现
\ No newline at end of file
diff --git a/weixin4j-base/pom.xml b/weixin4j-base/pom.xml
index 8f73da1b..76cfa5ad 100644
--- a/weixin4j-base/pom.xml
+++ b/weixin4j-base/pom.xml
@@ -42,7 +42,7 @@
redis.clients
jedis
- 2.6.0
+ 2.8.1
true
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/PayApi.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/PayApi.java
index b78b1840..b8a909d7 100644
--- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/PayApi.java
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/PayApi.java
@@ -632,7 +632,8 @@ public class PayApi extends MchApi {
String fileName = String.format("weixin4j_bill_%s_%s_%s.txt",
formatBillDate, billType.name().toLowerCase(),
weixinAccount.getId());
- File file = new File(String.format("%s/%s", billPath, fileName));
+ File file = new File(String.format("%s%s%s", billPath, File.separator,
+ fileName));
if (file.exists()) {
return file;
}
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/token/CacheCreator.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/CacheCreator.java
similarity index 76%
rename from weixin4j-base/src/main/java/com/foxinmy/weixin4j/token/CacheCreator.java
rename to weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/CacheCreator.java
index 5ec17574..cd524475 100644
--- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/token/CacheCreator.java
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/CacheCreator.java
@@ -1,4 +1,4 @@
-package com.foxinmy.weixin4j.token;
+package com.foxinmy.weixin4j.cache;
import com.foxinmy.weixin4j.exception.WeixinException;
@@ -11,7 +11,7 @@ import com.foxinmy.weixin4j.exception.WeixinException;
* @since JDK 1.6
* @see
*/
-public interface CacheCreator {
+public interface CacheCreator {
/**
* CacheKey
*
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/CacheManager.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/CacheManager.java
new file mode 100644
index 00000000..808bca27
--- /dev/null
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/CacheManager.java
@@ -0,0 +1,69 @@
+package com.foxinmy.weixin4j.cache;
+
+import com.foxinmy.weixin4j.exception.WeixinException;
+
+/**
+ * 缓存管理类
+ *
+ * @className CacheManager
+ * @author jinyu(foxinmy@gmail.com)
+ * @date 2016年5月27日
+ * @since JDK 1.7
+ * @see
+ */
+public class CacheManager {
+ protected final CacheCreator cacheCreator;
+ protected final CacheStorager cacheStorager;
+
+ public CacheManager(CacheCreator cacheCreator,
+ CacheStorager cacheStorager) {
+ this.cacheCreator = cacheCreator;
+ this.cacheStorager = cacheStorager;
+ }
+
+ /**
+ * 获取缓存对象
+ *
+ * @return 缓存对象
+ * @throws WeixinException
+ */
+ public T getCache() throws WeixinException {
+ String cacheKey = cacheCreator.key();
+ T cache = cacheStorager.lookup(cacheKey);
+ if (cache == null) {
+ cache = cacheCreator.create();
+ cacheStorager.caching(cacheKey, cache);
+ }
+ return cache;
+ }
+
+ /**
+ * 刷新缓存对象
+ *
+ * @return 缓存对象
+ * @throws WeixinException
+ */
+ public T refreshCache() throws WeixinException {
+ String cacheKey = cacheCreator.key();
+ T cache = cacheCreator.create();
+ cacheStorager.caching(cacheKey, cache);
+ return cache;
+ }
+
+ /**
+ * 移除缓存
+ *
+ * @return 被移除的缓存对象
+ */
+ public T evictCache() {
+ String cacheKey = cacheCreator.key();
+ return cacheStorager.evict(cacheKey);
+ }
+
+ /**
+ * 清除所有的缓存(请慎重)
+ */
+ public void clearCache() {
+ cacheStorager.clear();
+ }
+}
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/token/CacheStorager.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/CacheStorager.java
similarity index 65%
rename from weixin4j-base/src/main/java/com/foxinmy/weixin4j/token/CacheStorager.java
rename to weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/CacheStorager.java
index 6e295892..23878100 100644
--- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/token/CacheStorager.java
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/CacheStorager.java
@@ -1,4 +1,4 @@
-package com.foxinmy.weixin4j.token;
+package com.foxinmy.weixin4j.cache;
/**
* Cache的存储
@@ -9,7 +9,17 @@ package com.foxinmy.weixin4j.token;
* @since JDK 1.6
* @see
*/
-public interface CacheStorager {
+public interface CacheStorager {
+ /**
+ * 考虑到临界情况,实际缓存的有效时间减去该毫秒数(60秒)
+ */
+ long CUTMS = 60 * 1000l;
+
+ /**
+ * 所有的缓存KEY
+ */
+ String ALLKEY = "weixin4j_cache_keys";
+
/**
* 查找缓存中的对象
*
@@ -42,8 +52,6 @@ public interface CacheStorager {
/**
* 清除所有缓存对象(请慎重)
*
- * @param prefix
- * 缓存key的前缀
*/
- void clear(String prefix);
+ void clear();
}
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/Cacheable.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/Cacheable.java
new file mode 100644
index 00000000..28f1075b
--- /dev/null
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/Cacheable.java
@@ -0,0 +1,28 @@
+package com.foxinmy.weixin4j.cache;
+
+import java.io.Serializable;
+
+/**
+ * 可缓存的对象
+ *
+ * @className Cacheable
+ * @author jinyu(foxinmy@gmail.com)
+ * @date 2016年5月26日
+ * @since JDK 1.6
+ * @see
+ */
+public interface Cacheable extends Serializable {
+ /**
+ * 过期时间(单位:毫秒),值小于0时视为永不过期
+ *
+ * @return 缓存过期时间
+ */
+ public long getExpires();
+
+ /**
+ * 创建时间(单位:毫秒)
+ *
+ * @return 缓存对象创建时间
+ */
+ public long getCreateTime();
+}
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/FileCacheStorager.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/FileCacheStorager.java
new file mode 100644
index 00000000..998c5096
--- /dev/null
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/FileCacheStorager.java
@@ -0,0 +1,87 @@
+package com.foxinmy.weixin4j.cache;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import com.foxinmy.weixin4j.util.SerializationUtils;
+
+/**
+ * 用File保存缓存对象
+ *
+ * @className FileCacheStorager
+ * @author jinyu(foxinmy@gmail.com)
+ * @date 2016年5月27日
+ * @since JDK 1.6
+ * @see
+ */
+public class FileCacheStorager implements CacheStorager {
+
+ private final File tmpdir;
+ private final String SEPARATOR = File.separator;
+
+ public FileCacheStorager(String cachePath) {
+ this.tmpdir = new File(String.format("%s%sweixin4j_token_temp",
+ cachePath, SEPARATOR));
+ this.tmpdir.mkdirs();
+ }
+
+ @Override
+ public T lookup(String cacheKey) {
+ File cacheFile = new File(String.format("%s%s%s",
+ tmpdir.getAbsolutePath(), SEPARATOR, cacheKey));
+ try {
+ if (cacheFile.exists()) {
+ T cache = SerializationUtils.deserialize(new FileInputStream(
+ cacheFile));
+ if (cache.getCreateTime() < 0) {
+ return cache;
+ }
+ if ((cache.getCreateTime() + cache.getExpires() - CUTMS) > System
+ .currentTimeMillis()) {
+ return cache;
+ }
+ }
+ return null;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void caching(String cacheKey, T cache) {
+ try {
+ SerializationUtils.serialize(
+ cache,
+ new FileOutputStream(new File(String.format("%s%s%s",
+ tmpdir.getAbsolutePath(), SEPARATOR, cacheKey))));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public T evict(String cacheKey) {
+ T cache = null;
+ File cacheFile = new File(String.format("%s%s%s",
+ tmpdir.getAbsolutePath(), SEPARATOR, cacheKey));
+ try {
+ if (cacheFile.exists()) {
+ cache = SerializationUtils.deserialize(new FileInputStream(
+ cacheFile));
+ cacheFile.delete();
+ }
+ } catch (IOException e) {
+ ; // ingore
+ }
+ return cache;
+ }
+
+ @Override
+ public void clear() {
+ for (File cache : tmpdir.listFiles()) {
+ cache.delete();
+ }
+ }
+}
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/token/MemcacheTokenStorager.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/MemcacheCacheStorager.java
similarity index 75%
rename from weixin4j-base/src/main/java/com/foxinmy/weixin4j/token/MemcacheTokenStorager.java
rename to weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/MemcacheCacheStorager.java
index 1cff9439..42895c19 100644
--- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/token/MemcacheTokenStorager.java
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/MemcacheCacheStorager.java
@@ -1,61 +1,82 @@
-package com.foxinmy.weixin4j.token;
+package com.foxinmy.weixin4j.cache;
import java.util.ArrayList;
import java.util.Date;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
-import com.foxinmy.weixin4j.model.Token;
import com.whalin.MemCached.MemCachedClient;
import com.whalin.MemCached.SockIOPool;
/**
- * 用Memcache保存Token信息(推荐使用)
+ * 用Memcache保存缓存对象(推荐使用)
*
- * @className MemcacheTokenStorager
+ * @className MemcacheCacheStorager
* @author jinyu(foxinmy@gmail.com)
* @date 2016年5月11日
* @since JDK 1.6
* @see
*/
-public class MemcacheTokenStorager extends TokenStorager {
+public class MemcacheCacheStorager implements
+ CacheStorager {
private final MemCachedClient mc;
- public MemcacheTokenStorager(MemcachePoolConfig poolConfig) {
+ public MemcacheCacheStorager() {
+ this(new MemcachePoolConfig());
+ }
+
+ public MemcacheCacheStorager(MemcachePoolConfig poolConfig) {
mc = new MemCachedClient();
poolConfig.initSocketIO();
+ mc.set(ALLKEY, new HashSet());
}
+ @SuppressWarnings("unchecked")
@Override
- public Token lookup(String cacheKey) {
- return (Token) mc.get(cacheKey);
+ public T lookup(String cacheKey) {
+ return (T) mc.get(cacheKey);
}
+ @SuppressWarnings("unchecked")
@Override
- public void caching(String cacheKey, Token token) {
- if (token.getExpiresIn() > 0) {
- mc.set(cacheKey, token,
- new Date(token.getCreateTime() + token.getExpiresIn()
- * 1000 - ms()));
+ public void caching(String cacheKey, T cache) {
+ if (cache.getCreateTime() > 0l) {
+ mc.set(cacheKey,
+ cache,
+ new Date(cache.getCreateTime() + cache.getExpires() - CUTMS));
} else {
- mc.set(cacheKey, token);
+ mc.set(cacheKey, cache);
}
+ Set all = (Set) mc.get(ALLKEY);
+ all.add(cacheKey);
+ mc.set(ALLKEY, all);
}
+ @SuppressWarnings("unchecked")
@Override
- public Token evict(String cacheKey) {
- Token token = lookup(cacheKey);
+ public T evict(String cacheKey) {
+ T cache = lookup(cacheKey);
mc.delete(cacheKey);
- return token;
+ Set all = (Set) mc.get(ALLKEY);
+ all.remove(cacheKey);
+ mc.set(ALLKEY, all);
+ return cache;
}
+ @SuppressWarnings("unchecked")
@Override
- public void clear(String prefix) {
- throw new UnsupportedOperationException();
+ public void clear() {
+ Set all = (Set) mc.get(ALLKEY);
+ for (String key : all) {
+ mc.delete(key);
+ }
+ mc.delete(ALLKEY);
}
public static class MemcachePoolConfig {
- public final static String HOST = "localhost";
+ public final static String HOST = "127.0.0.1";
public final static int PORT = 11211;
public final static int WEIGHT = 1;
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/MemoryCacheStorager.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/MemoryCacheStorager.java
new file mode 100644
index 00000000..e0562013
--- /dev/null
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/MemoryCacheStorager.java
@@ -0,0 +1,50 @@
+package com.foxinmy.weixin4j.cache;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 用内存保存缓存对象(不推荐使用)
+ *
+ * @className MemoryCacheStorager
+ * @author jinyu(foxinmy@gmail.com)
+ * @date 2016年1月24日
+ * @since JDK 1.6
+ * @see
+ */
+public class MemoryCacheStorager implements
+ CacheStorager {
+
+ private final Map CONMAP;
+
+ public MemoryCacheStorager() {
+ this.CONMAP = new ConcurrentHashMap();
+ }
+
+ @Override
+ public T lookup(String cacheKey) {
+ T cache = this.CONMAP.get(cacheKey);
+ if (cache != null) {
+ if ((cache.getCreateTime() + cache.getExpires() - CUTMS) > System
+ .currentTimeMillis()) {
+ return cache;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void caching(String cacheKey, T cache) {
+ this.CONMAP.put(cacheKey, cache);
+ }
+
+ @Override
+ public T evict(String cacheKey) {
+ return this.CONMAP.remove(cacheKey);
+ }
+
+ @Override
+ public void clear() {
+ this.CONMAP.clear();
+ }
+}
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/README.md b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/README.md
new file mode 100644
index 00000000..545b17e0
--- /dev/null
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/README.md
@@ -0,0 +1,13 @@
+### CACHE的实现
+
+* CacheCreator 负责创建新的缓存对象
+
+* CacheStorager 负责查找已缓存的对象或者缓存新的对象
+
+* TokenManager 负责对缓存对象的管理(屏蔽细节)
+
+* FileCacheStorager 是系统默认的缓存存储策略实现
+
+* RedisCacheStorager(RedisClusterCacheStorager) 使用redis保存缓存对象(需要自行添加客户端包,[jedis](https://github.com/xetorthio/jedis))
+
+* MemcacheCacheStorager 使用memcache保存缓存对象(需要自行添加客户端包,[Memcached-Java-Client](https://github.com/gwhalin/Memcached-Java-Client))
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/RedisCacheStorager.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/RedisCacheStorager.java
new file mode 100644
index 00000000..d4524f96
--- /dev/null
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/RedisCacheStorager.java
@@ -0,0 +1,128 @@
+package com.foxinmy.weixin4j.cache;
+
+import java.util.Set;
+
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.JedisPoolConfig;
+
+import com.foxinmy.weixin4j.model.Consts;
+import com.foxinmy.weixin4j.util.SerializationUtils;
+
+/**
+ * 用Redis保存缓存对象(推荐使用)
+ *
+ * @className RedisCacheStorager
+ * @author jinyu(foxinmy@gmail.com)
+ * @date 2015年1月9日
+ * @since JDK 1.6
+ */
+public class RedisCacheStorager implements
+ CacheStorager {
+
+ private JedisPool jedisPool;
+
+ private final static String HOST = "127.0.0.1";
+ private final static int PORT = 6379;
+ private final static int TIMEOUT = 5000;
+ private final static int MAX_TOTAL = 50;
+ private final static int MAX_IDLE = 5;
+ private final static int MAX_WAIT_MILLIS = 5000;
+ private final static boolean TEST_ON_BORROW = false;
+ private final static boolean TEST_ON_RETURN = true;
+
+ public RedisCacheStorager() {
+ this(HOST, PORT, TIMEOUT);
+ }
+
+ public RedisCacheStorager(String host, int port, int timeout) {
+ JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
+ jedisPoolConfig.setMaxTotal(MAX_TOTAL);
+ jedisPoolConfig.setMaxIdle(MAX_IDLE);
+ jedisPoolConfig.setMaxWaitMillis(MAX_WAIT_MILLIS);
+ jedisPoolConfig.setTestOnBorrow(TEST_ON_BORROW);
+ jedisPoolConfig.setTestOnReturn(TEST_ON_RETURN);
+ this.jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout);
+ }
+
+ public RedisCacheStorager(JedisPoolConfig jedisPoolConfig) {
+ this(new JedisPool(jedisPoolConfig, HOST, PORT, TIMEOUT));
+ }
+
+ public RedisCacheStorager(String host, int port, int timeout,
+ JedisPoolConfig jedisPoolConfig) {
+ this(new JedisPool(jedisPoolConfig, host, port, timeout));
+ }
+
+ public RedisCacheStorager(JedisPool jedisPool) {
+ this.jedisPool = jedisPool;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public T lookup(String cacheKey) {
+ Jedis jedis = null;
+ try {
+ jedis = jedisPool.getResource();
+ byte[] value = jedis.get(cacheKey.getBytes(Consts.UTF_8));
+ return value != null ? (T) SerializationUtils.deserialize(value)
+ : null;
+ } finally {
+ if (jedis != null) {
+ jedis.close();
+ }
+ }
+ }
+
+ @Override
+ public void caching(String cacheKey, T cache) {
+ Jedis jedis = null;
+ try {
+ jedis = jedisPool.getResource();
+ byte[] key = cacheKey.getBytes(Consts.UTF_8);
+ byte[] value = SerializationUtils.serialize(cache);
+ jedis.set(key, value);
+ if (cache.getExpires() > 0) {
+ jedis.expire(key, (int) (cache.getExpires() - CUTMS) / 1000);
+ }
+ jedis.sadd(ALLKEY, cacheKey);
+ } finally {
+ if (jedis != null) {
+ jedis.close();
+ }
+ }
+ }
+
+ @Override
+ public T evict(String cacheKey) {
+ T cache = lookup(cacheKey);
+ Jedis jedis = null;
+ try {
+ jedis = jedisPool.getResource();
+ jedis.del(cacheKey);
+ jedis.srem(ALLKEY, cacheKey);
+ } finally {
+ if (jedis != null) {
+ jedis.close();
+ }
+ }
+ return cache;
+ }
+
+ @Override
+ public void clear() {
+ Jedis jedis = null;
+ try {
+ jedis = jedisPool.getResource();
+ Set cacheKeys = jedis.smembers(ALLKEY);
+ if (!cacheKeys.isEmpty()) {
+ cacheKeys.add(ALLKEY);
+ jedis.del(cacheKeys.toArray(new String[cacheKeys.size()]));
+ }
+ } finally {
+ if (jedis != null) {
+ jedis.close();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/RedisClusterCacheStorager.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/RedisClusterCacheStorager.java
new file mode 100644
index 00000000..5dc149e7
--- /dev/null
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/RedisClusterCacheStorager.java
@@ -0,0 +1,94 @@
+package com.foxinmy.weixin4j.cache;
+
+import java.util.Set;
+
+import redis.clients.jedis.HostAndPort;
+import redis.clients.jedis.JedisCluster;
+import redis.clients.jedis.JedisPoolConfig;
+
+import com.foxinmy.weixin4j.model.Consts;
+import com.foxinmy.weixin4j.util.SerializationUtils;
+
+/**
+ * 用Redis(集群)保存缓存对象(推荐使用)
+ *
+ * @className RedisCacheStorager
+ * @author jinyu(foxinmy@gmail.com)
+ * @date 2015年1月9日
+ * @since JDK 1.6
+ */
+public class RedisClusterCacheStorager implements
+ CacheStorager {
+ private final static int CONNECTION_TIMEOUT = 5000;
+ private final static int SO_TIMEOUT = 5000;
+ private final static int MAX_REDIRECTIONS = 5;
+ private final static int MAX_TOTAL = 50;
+ private final static int MAX_IDLE = 5;
+ private final static int MAX_WAIT_MILLIS = 5000;
+ private final static boolean TEST_ON_BORROW = false;
+ private final static boolean TEST_ON_RETURN = true;
+ private final JedisCluster jedisCluster;
+
+ public RedisClusterCacheStorager(Set nodes) {
+ JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
+ jedisPoolConfig.setMaxTotal(MAX_TOTAL);
+ jedisPoolConfig.setMaxIdle(MAX_IDLE);
+ jedisPoolConfig.setMaxWaitMillis(MAX_WAIT_MILLIS);
+ jedisPoolConfig.setTestOnBorrow(TEST_ON_BORROW);
+ jedisPoolConfig.setTestOnReturn(TEST_ON_RETURN);
+ this.jedisCluster = new JedisCluster(nodes, CONNECTION_TIMEOUT,
+ SO_TIMEOUT, MAX_REDIRECTIONS, jedisPoolConfig);
+ }
+
+ public RedisClusterCacheStorager(Set nodes,
+ JedisPoolConfig poolConfig) {
+ this(nodes, CONNECTION_TIMEOUT, SO_TIMEOUT, MAX_REDIRECTIONS,
+ poolConfig);
+ }
+
+ public RedisClusterCacheStorager(Set nodes,
+ int connectionTimeout, int soTimeout, int maxRedirections,
+ JedisPoolConfig poolConfig) {
+ this(new JedisCluster(nodes, connectionTimeout, soTimeout,
+ maxRedirections, poolConfig));
+ }
+
+ public RedisClusterCacheStorager(JedisCluster jedisCluster) {
+ this.jedisCluster = jedisCluster;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public T lookup(String cacheKey) {
+ byte[] value = jedisCluster.get(cacheKey.getBytes(Consts.UTF_8));
+ return value != null ? (T) SerializationUtils.deserialize(value) : null;
+ }
+
+ @Override
+ public void caching(String cacheKey, T cache) {
+ byte[] key = cacheKey.getBytes(Consts.UTF_8);
+ byte[] value = SerializationUtils.serialize(cache);
+ jedisCluster.set(key, value);
+ if (cache.getExpires() > 0) {
+ jedisCluster.expire(key, (int) (cache.getExpires() - CUTMS) / 1000);
+ }
+ jedisCluster.sadd(ALLKEY, cacheKey);
+ }
+
+ @Override
+ public T evict(String cacheKey) {
+ T cache = lookup(cacheKey);
+ jedisCluster.del(cacheKey);
+ jedisCluster.srem(ALLKEY, cacheKey);
+ return cache;
+ }
+
+ @Override
+ public void clear() {
+ Set cacheKeys = jedisCluster.smembers(ALLKEY);
+ if (!cacheKeys.isEmpty()) {
+ cacheKeys.add(ALLKEY);
+ jedisCluster.del(cacheKeys.toArray(new String[cacheKeys.size()]));
+ }
+ }
+}
\ No newline at end of file
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/error.xml b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/error.xml
index 410cac0f..3fa190b1 100644
--- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/error.xml
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/error.xml
@@ -1431,6 +1431,10 @@
81003
邀请额度已用完
+
+ 81004
+ 部门数量超过上限
+
82001
发送消息或者邀请的参数全部为空或者全部不合法
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/jssdk/JSSDKConfigurator.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/jssdk/JSSDKConfigurator.java
index 9c5cfd5d..dfd70c43 100644
--- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/jssdk/JSSDKConfigurator.java
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/jssdk/JSSDKConfigurator.java
@@ -7,7 +7,7 @@ import java.util.Set;
import com.alibaba.fastjson.JSONObject;
import com.foxinmy.weixin4j.exception.WeixinException;
-import com.foxinmy.weixin4j.token.TokenHolder;
+import com.foxinmy.weixin4j.token.TokenManager;
import com.foxinmy.weixin4j.util.DateUtil;
import com.foxinmy.weixin4j.util.DigestUtil;
import com.foxinmy.weixin4j.util.MapUtil;
@@ -17,7 +17,7 @@ import com.foxinmy.weixin4j.util.Weixin4jConfigUtil;
/**
* JSSDK配置类
- *
+ *
* @className JSSDKConfigurator
* @author jinyu(foxinmy@gmail.com)
* @date 2015年12月23日
@@ -25,17 +25,17 @@ import com.foxinmy.weixin4j.util.Weixin4jConfigUtil;
* @see
*/
public class JSSDKConfigurator {
- private final TokenHolder ticketTokenHolder;
+ private final TokenManager ticketTokenManager;
private JSONObject config;
private Set apis;
/**
- * ticket保存类 可调用WeixinProxy#getTicketHolder获取
- *
- * @param ticketTokenHolder
+ * ticket保存类 可调用WeixinProxy#getTicketManager获取
+ *
+ * @param ticketTokenManager
*/
- public JSSDKConfigurator(TokenHolder ticketTokenHolder) {
- this.ticketTokenHolder = ticketTokenHolder;
+ public JSSDKConfigurator(TokenManager ticketTokenManager) {
+ this.ticketTokenManager = ticketTokenManager;
this.config = new JSONObject();
this.apis = new HashSet();
}
@@ -43,7 +43,7 @@ public class JSSDKConfigurator {
/**
* 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,
* 仅在pc端时才会打印。
- *
+ *
* @return
*/
public JSSDKConfigurator debugMode() {
@@ -53,7 +53,7 @@ public class JSSDKConfigurator {
/**
* 公众号的唯一标识 不填则获取weixin4j.properties#account中的id
- *
+ *
* @param appId
* @return
*/
@@ -64,7 +64,7 @@ public class JSSDKConfigurator {
/**
* 需要使用的JS接口列表
- *
+ *
* @see JSSDKAPI
* @param apis
* @return
@@ -78,7 +78,7 @@ public class JSSDKConfigurator {
/**
* 需要使用的JS接口列表
- *
+ *
* @see JSSDKAPI
* @param apis
* @return
@@ -94,7 +94,7 @@ public class JSSDKConfigurator {
/**
* 生成config配置JSON串
- *
+ *
* @param url
* 当前网页的URL,不包含#及其后面部分
* @return jssdk配置JSON字符串
@@ -113,7 +113,7 @@ public class JSSDKConfigurator {
String noncestr = RandomUtil.generateString(24);
signMap.put("timestamp", timestamp);
signMap.put("noncestr", noncestr);
- signMap.put("jsapi_ticket", this.ticketTokenHolder.getAccessToken());
+ signMap.put("jsapi_ticket", this.ticketTokenManager.getAccessToken());
signMap.put("url", url);
String sign = DigestUtil.SHA1(MapUtil.toJoinString(signMap, false,
false));
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/Button.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/Button.java
index 1fcc3b75..441c1d16 100644
--- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/Button.java
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/Button.java
@@ -6,6 +6,7 @@ import java.util.Arrays;
import java.util.List;
import com.alibaba.fastjson.annotation.JSONField;
+import com.foxinmy.weixin4j.tuple.MpArticle;
import com.foxinmy.weixin4j.type.ButtonType;
/**
@@ -38,13 +39,20 @@ public class Button implements Serializable {
/**
* 菜单KEY值,根据type的类型而定
通过公众平台设置的自定义菜单: text:保存文字;
* img、voice:保存媒体ID; video:保存视频URL;
- * news:保存图文消息:List#com.foxinmy.weixin4j.tuple.MpArticle#; view:保存链接URL;
- * 使用API设置的自定义菜单:
+ * news:保存图文消息媒体ID view:保存链接URL;
+ *
+ * 使用API设置的自定义菜单:
+ *
* click、scancode_push、scancode_waitmsg、pic_sysphoto、pic_photo_or_album、
* pic_weixin、location_select:保存key; view:保存链接URL;
* media_id、view_limited:保存媒体ID
*/
- private Serializable content;
+ private String content;
+ /**
+ * 图文列表 只有在公众平台设置的菜单才有
+ */
+ @JSONField(serialize = false, deserialize = false)
+ private List articles;
/**
* 二级菜单数组,个数应为1~5个
*/
@@ -101,14 +109,27 @@ public class Button implements Serializable {
this.type = type;
}
- public Serializable getContent() {
+ public String getContent() {
return content;
}
- public void setContent(Serializable content) {
+ public void setContent(String content) {
this.content = content;
}
+ public List getArticles() {
+ return articles;
+ }
+
+ /**
+ * 创建菜单设置无效
+ *
+ * @param articles
+ */
+ public void setArticles(List articles) {
+ this.articles = articles;
+ }
+
public List