|
|
@@ -0,0 +1,501 @@
|
|
|
+package zs.payment.utils;
|
|
|
+
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
|
+import org.springframework.data.redis.core.ZSetOperations;
|
|
|
+import org.springframework.stereotype.Repository;
|
|
|
+import redis.clients.jedis.GeoCoordinate;
|
|
|
+import redis.clients.jedis.GeoUnit;
|
|
|
+import redis.clients.jedis.Pipeline;
|
|
|
+import redis.clients.jedis.Tuple;
|
|
|
+import redis.clients.jedis.params.SetParams;
|
|
|
+
|
|
|
+import java.util.*;
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 该redis主要包含支付、订单等数据相关缓存(与资讯池共用)
|
|
|
+ */
|
|
|
+@Repository
|
|
|
+@Slf4j
|
|
|
+public class RedisUtils {
|
|
|
+ @Value("${pay.redis.master}")
|
|
|
+ private String ecoCluster;
|
|
|
+
|
|
|
+ public void set(String key, String cont) {
|
|
|
+ set(key, cont, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void set(String key, String cont, int seconds) {
|
|
|
+ Redis.withCluster(ecoCluster, jedis -> {
|
|
|
+ jedis.set(key, cont);
|
|
|
+ if (seconds > 0) {
|
|
|
+ jedis.expire(key, seconds);
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ public String get(String key) {
|
|
|
+ return Redis.withCluster(ecoCluster, jedis -> jedis.get(key));
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getByKey(String key) {
|
|
|
+ return Redis.withCluster(ecoCluster, jedis -> jedis.get(key));
|
|
|
+ }
|
|
|
+
|
|
|
+ public List<String> lRange(String key, long startIndex, long endIndex) {
|
|
|
+ List<String> back = new ArrayList<>();
|
|
|
+ if (key == null || key.isEmpty()) {
|
|
|
+ return back;
|
|
|
+ }
|
|
|
+
|
|
|
+ Redis.withCluster(ecoCluster, jedis ->
|
|
|
+ back.addAll(jedis.lrange(key, startIndex, endIndex))
|
|
|
+ );
|
|
|
+ return back;
|
|
|
+ }
|
|
|
+
|
|
|
+ public List<String> mget(List<String> keys) {
|
|
|
+ List<String> back = new ArrayList<>();
|
|
|
+ if (keys == null || keys.isEmpty()) {
|
|
|
+ return back;
|
|
|
+ }
|
|
|
+
|
|
|
+ Redis.withCluster(ecoCluster, jedis ->
|
|
|
+ back.addAll(jedis.mget(keys.toArray(new String[0])))
|
|
|
+ );
|
|
|
+ return back;
|
|
|
+ }
|
|
|
+
|
|
|
+ public Long del(String key) {
|
|
|
+ return Redis.withCluster(ecoCluster, jedis -> jedis.del(key));
|
|
|
+ }
|
|
|
+
|
|
|
+ public void hset(String key, String field, String cont) {
|
|
|
+ hset(key, field, cont, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void hset(String key, String field, String cont, int seconds) {
|
|
|
+ Redis.withCluster(ecoCluster, jedis -> {
|
|
|
+ jedis.hset(key, field, cont);
|
|
|
+ if (seconds > 0) {
|
|
|
+ jedis.expire(key, seconds);
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ public void hmset(String key, Map<String, String> hash) {
|
|
|
+ hmset(key, hash, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void hmset(String key, Map<String, String> hash, int seconds) {
|
|
|
+ Redis.withCluster(ecoCluster, jedis -> {
|
|
|
+ jedis.hmset(key, hash);
|
|
|
+ if (seconds > 0) {
|
|
|
+ jedis.expire(key, seconds);
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ public Long hincrBy(String key, String field, long value) {
|
|
|
+ return Redis.withCluster(ecoCluster, jedis -> jedis.hincrBy(key, field, value));
|
|
|
+ }
|
|
|
+
|
|
|
+ public String hget(String key, String field) {
|
|
|
+ return Redis.withCluster(ecoCluster, jedis -> jedis.hget(key, field));
|
|
|
+ }
|
|
|
+
|
|
|
+ public Map<String, String> hgetAll(String key) {
|
|
|
+ return Redis.withCluster(ecoCluster, jedis -> jedis.hgetAll(key));
|
|
|
+ }
|
|
|
+
|
|
|
+ public void hdel(String key, String field) {
|
|
|
+ Redis.withCluster(ecoCluster, jedis -> {
|
|
|
+ jedis.hdel(key, field);
|
|
|
+ return null;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ public long sadd(String key, String... members) {
|
|
|
+ return Redis.withCluster(ecoCluster, jedis -> jedis.sadd(key, members));
|
|
|
+ }
|
|
|
+
|
|
|
+ public Set<String> smembers(String key) {
|
|
|
+ return Redis.withCluster(ecoCluster, jedis -> jedis.smembers(key));
|
|
|
+ }
|
|
|
+
|
|
|
+ public long zadd(String key, double score, String member) {
|
|
|
+ return Redis.withCluster(ecoCluster, jedis -> jedis.zadd(key, score, member));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 向有序集合批量添加元素(使用 Tuple 集合)
|
|
|
+ * @param key 键
|
|
|
+ * @param tuples 元组集合(包含分数和成员)
|
|
|
+ * @return 成功添加的数量
|
|
|
+ */
|
|
|
+ public Long zadd(String key, Set<ZSetOperations.TypedTuple<String>> tuples) {
|
|
|
+ if (tuples == null || tuples.isEmpty()) {
|
|
|
+ return 0L;
|
|
|
+ }
|
|
|
+
|
|
|
+ long result = 0L;
|
|
|
+
|
|
|
+ for (ZSetOperations.TypedTuple<String> tuple : tuples) {
|
|
|
+ result=result+ Redis.withCluster(ecoCluster, jedis -> jedis.zadd(key, tuple.getScore(),tuple.getValue()));
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+// /**
|
|
|
+// * 通过管道模式一次性添加多个值
|
|
|
+// *
|
|
|
+// * @param key
|
|
|
+// * @param list
|
|
|
+// */
|
|
|
+// public void zaddWithPipeline(String key, List<?> list) {
|
|
|
+// Redis.withCluster(ecoCluster, jedis -> {
|
|
|
+// Pipeline pipeline = jedis.pipelined();
|
|
|
+// for (Object item : list) {
|
|
|
+// double score = Double.parseDouble(((Map<?, ?>) item).get("userId").toString());
|
|
|
+// jedis.zadd(key, score, Strings.toJson(item));
|
|
|
+// }
|
|
|
+// pipeline.syncAndReturnAll();
|
|
|
+// });
|
|
|
+// }
|
|
|
+
|
|
|
+ public long llen(String key) {
|
|
|
+ return Redis.withCluster(ecoCluster, jedis -> jedis.llen(key));
|
|
|
+ }
|
|
|
+
|
|
|
+ public List<String> lrangeList(String key, int start, int end) {
|
|
|
+ List<String> result = Redis.withCluster(ecoCluster, jedis -> jedis.lrange(key, start, end));
|
|
|
+ return result != null ? result : new ArrayList<>();
|
|
|
+ }
|
|
|
+
|
|
|
+ public void lpush(String key, String... strings) {
|
|
|
+ Redis.withCluster(ecoCluster, jedis -> {
|
|
|
+ jedis.lpush(key, strings);
|
|
|
+ return null;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ public long rpush(String key, String value) {
|
|
|
+ return Redis.withCluster(ecoCluster, jedis -> jedis.rpush(key, value));
|
|
|
+ }
|
|
|
+
|
|
|
+ public String rpop(String key) {
|
|
|
+ return Redis.withCluster(ecoCluster, jedis -> jedis.rpop(key));
|
|
|
+ }
|
|
|
+
|
|
|
+ public String lpop(String key) {
|
|
|
+ return Redis.withCluster(ecoCluster, jedis -> jedis.lpop(key));
|
|
|
+ }
|
|
|
+
|
|
|
+ public long zrem(String key, String member) {
|
|
|
+ return Redis.withCluster(ecoCluster, jedis -> jedis.zrem(key, member));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ * 返回有序集的元素个数(对应 redisTemplate.opsForZSet().size())
|
|
|
+ * @param key
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public long zcard(String key) {
|
|
|
+ return Redis.withCluster(ecoCluster, jedis -> jedis.zcard(key));
|
|
|
+ }
|
|
|
+
|
|
|
+ public Double zscore(String key, String member) {
|
|
|
+ return Redis.withCluster(ecoCluster, jedis -> jedis.zscore(key, member));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 倒序(从大到小)获取指定区间内的数据
|
|
|
+ *
|
|
|
+ * @param key
|
|
|
+ * @param start
|
|
|
+ * @param end
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public Set<String> zrevrange(String key, long start, long end) {
|
|
|
+ return Redis.withCluster(ecoCluster, jedis -> jedis.zrevrange(key, start, end));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 正序(从小到大)获取指定区间内的数据
|
|
|
+ *
|
|
|
+ * @param key
|
|
|
+ * @param start
|
|
|
+ * @param end
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public Set<String> zrange(String key, long start, long end) {
|
|
|
+ return Redis.withCluster(ecoCluster, jedis -> jedis.zrange(key, start, end));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据score值,倒序(从大到小)获取指定区间内的数据
|
|
|
+ *
|
|
|
+ * @param key
|
|
|
+ * @param score
|
|
|
+ * @param psize
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public Set<String> zrevrangeByScore(String key, String score, Integer psize) {
|
|
|
+ return Redis.withCluster(ecoCluster, jedis -> jedis.zrevrangeByScore(key, "(" + score, "-inf", 0, psize));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取score值介于score1和score2之间的数据
|
|
|
+ *
|
|
|
+ * @param key
|
|
|
+ * @param score1
|
|
|
+ * @param score2
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public Set<String> zrangeByScore(String key, String score1, String score2) {
|
|
|
+ return Redis.withCluster(ecoCluster, jedis -> jedis.zrangeByScore(key, score1, score2));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置自增长
|
|
|
+ *
|
|
|
+ * @param key
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public synchronized long setIncyBy(String key) {
|
|
|
+ return setIncyBy(key, 1L, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置自增长
|
|
|
+ *
|
|
|
+ * @param key
|
|
|
+ * @param num
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public synchronized long setIncyBy(String key, long num) {
|
|
|
+ return setIncyBy(key, num, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置自增长
|
|
|
+ *
|
|
|
+ * @param key
|
|
|
+ * @param num
|
|
|
+ * @param seconds
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public synchronized long setIncyBy(String key, long num, int seconds) {
|
|
|
+ return Redis.withCluster(ecoCluster, jedis -> {
|
|
|
+ long result = jedis.incrBy(key, num);
|
|
|
+ if (seconds > 0) {
|
|
|
+ jedis.expire(key, seconds);
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ public long setIncy(String key) {
|
|
|
+ return Redis.withCluster(ecoCluster, jedis -> jedis.incr(key));
|
|
|
+ }
|
|
|
+
|
|
|
+ public long setDecy(String key) {
|
|
|
+ return Redis.withCluster(ecoCluster, jedis -> jedis.decr(key));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 按分数从小到大删除指定区域的数据 (对应redisTemplate.opsForZSet().removeRange(key,begin,end)))
|
|
|
+ *
|
|
|
+ * @param key
|
|
|
+ * @param start
|
|
|
+ * @param end
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public long zremrangeByRank(String key, long start, long end) {
|
|
|
+ return Redis.withCluster(ecoCluster, jedis -> jedis.zremrangeByRank(key, start, end));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * setnx实现简单的分布式锁
|
|
|
+ *
|
|
|
+ * @param key
|
|
|
+ * @param value
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public long setnx(String key, String value) {
|
|
|
+ return setnx(key, value, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * setnx实现简单的分布式锁
|
|
|
+ *
|
|
|
+ * @param key
|
|
|
+ * @param value
|
|
|
+ * @param seconds
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public long setnx(String key, String value, int seconds) {
|
|
|
+ return Redis.withCluster(ecoCluster, jedis -> {
|
|
|
+ long result = jedis.setnx(key, value);
|
|
|
+ if (result == 1 && seconds > 0) {
|
|
|
+ jedis.expire(key, seconds);
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * SET NX EX 原子命令:仅当Key不存在时设置值,并设置过期时间
|
|
|
+ * 用于分布式锁的获取
|
|
|
+ *
|
|
|
+ * @param key 锁的键
|
|
|
+ * @param value 锁的值(通常是实例ID或服务ID)
|
|
|
+ * @param seconds 过期时间(秒)
|
|
|
+ * @return true 表示成功获得锁,false 表示锁已被占用
|
|
|
+ */
|
|
|
+ public boolean setIfAbsent(String key, String value, int seconds) {
|
|
|
+ String result = Redis.withCluster(ecoCluster, jedis -> {
|
|
|
+ // 使用 SET key value NX EX seconds 原子命令
|
|
|
+ // NX: Only set the key if it does not already exist
|
|
|
+ // EX: Set the specified expire time, in seconds
|
|
|
+ SetParams params = new SetParams().nx().ex(seconds);
|
|
|
+ return jedis.set(key, value, params);
|
|
|
+ });
|
|
|
+ // 返回 "OK" 表示成功,null 表示键已存在
|
|
|
+ return "OK".equals(result);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据前缀获取所有的key
|
|
|
+ * 例如:pro_*
|
|
|
+ */
|
|
|
+ public Set<String> getListKey(String prefix) {
|
|
|
+ return Redis.withCluster(ecoCluster, jedis -> jedis.keys(prefix.concat("*")));
|
|
|
+ }
|
|
|
+
|
|
|
+ public long geoAdd(String key, double lng, double lat, String member) {
|
|
|
+ return Redis.withCluster(ecoCluster, jedis -> jedis.geoadd(key, lng, lat, member));
|
|
|
+ }
|
|
|
+
|
|
|
+ public double geoDist(String key, String member1, String member2) {
|
|
|
+ Double result = Redis.withCluster(ecoCluster, jedis -> jedis.geodist(key, member1, member2, GeoUnit.KM));
|
|
|
+ return result != null ? result : 0.0;
|
|
|
+ }
|
|
|
+
|
|
|
+ public List<Map<String, Object>> geoPos(String key, String... members) {
|
|
|
+ List<GeoCoordinate> coordinates = Redis.withCluster(ecoCluster, jedis -> jedis.geopos(key, members));
|
|
|
+
|
|
|
+ List<Map<String, Object>> coordinateList = new ArrayList<>();
|
|
|
+ if (coordinates != null && !coordinates.isEmpty()) {
|
|
|
+ for (GeoCoordinate coord : coordinates) {
|
|
|
+ Map<String, Object> location = new HashMap<>();
|
|
|
+ location.put("lng", coord.getLongitude());
|
|
|
+ location.put("lat", coord.getLatitude());
|
|
|
+ coordinateList.add(location);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return coordinateList;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置key的过期时间(对应 redisTemplate.expire())
|
|
|
+ * @param key 键
|
|
|
+ * @param timeout 过期时间
|
|
|
+ * @param unit 时间单位
|
|
|
+ * @return 是否设置成功
|
|
|
+ */
|
|
|
+ public Boolean expire(String key, long timeout, TimeUnit unit) {
|
|
|
+ return Redis.withCluster(ecoCluster, jedis -> {
|
|
|
+ // 转换为秒
|
|
|
+ long seconds = unit.toSeconds(timeout);
|
|
|
+ if (seconds > Integer.MAX_VALUE) {
|
|
|
+ log.warn("过期时间超过Integer最大值,将使用最大值: {}", Integer.MAX_VALUE);
|
|
|
+ seconds = Integer.MAX_VALUE;
|
|
|
+ }
|
|
|
+ jedis.expire(key, (int) seconds);
|
|
|
+ return true;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ public void set(String key, String cont, int timeout, TimeUnit unit) {
|
|
|
+ Redis.withCluster(ecoCluster, jedis -> {
|
|
|
+ // 转换为秒
|
|
|
+ long seconds = unit.toSeconds(timeout);
|
|
|
+ if (seconds > Integer.MAX_VALUE) {
|
|
|
+ seconds = Integer.MAX_VALUE;
|
|
|
+ }
|
|
|
+
|
|
|
+ jedis.set(key, cont);
|
|
|
+ if (seconds > 0) {
|
|
|
+ jedis.expire(key, seconds);
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取有序集合中指定排名范围内的成员及其分数(按分数从高到低排序)
|
|
|
+ * 对应 redisTemplate.opsForZSet().reverseRangeWithScores()
|
|
|
+ *
|
|
|
+ * @param key 键
|
|
|
+ * @param start 起始索引(从0开始,包含)
|
|
|
+ * @param end 结束索引(包含,-1表示最后一个)
|
|
|
+ * @return 包含成员和分数的元组集合
|
|
|
+ */
|
|
|
+ public Set<ZSetOperations.TypedTuple<String>> zReverseRangeWithScores(String key, long start, long end) {
|
|
|
+ Set<Tuple> tuples = Redis.withCluster(ecoCluster, jedis ->
|
|
|
+ jedis.zrevrangeWithScores(key, start, end));
|
|
|
+ return tuples.stream()
|
|
|
+ .map(tuple -> ZSetOperations.TypedTuple.of(
|
|
|
+ tuple.getElement(), // 获取元素
|
|
|
+ tuple.getScore() // 获取分数
|
|
|
+ ))
|
|
|
+ .collect(Collectors.toSet());
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 使用 Pipeline 批量设置字符串键值对(高性能,适合大量数据)
|
|
|
+ * @param map 包含多个键值对的Map
|
|
|
+ */
|
|
|
+ public void multiSetWithPipeline(Map<String, String> map,int timeout, TimeUnit unit) {
|
|
|
+ if (map == null || map.isEmpty()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ Redis.withCluster(ecoCluster, jedis -> {
|
|
|
+ // 创建管道
|
|
|
+ Pipeline pipeline = jedis.pipelined();
|
|
|
+
|
|
|
+ // 将所有set命令添加到管道
|
|
|
+ for (Map.Entry<String, String> entry : map.entrySet()) {
|
|
|
+ pipeline.set(entry.getKey(), entry.getValue());
|
|
|
+ pipeline.expire(entry.getKey(), unit.toSeconds(timeout));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 批量执行
|
|
|
+ pipeline.sync();
|
|
|
+ return null;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ public void hmset(String key, Map<String, String> hash, int timeout, TimeUnit unit) {
|
|
|
+ Redis.withCluster(ecoCluster, jedis -> {
|
|
|
+ jedis.hmset(key, hash);
|
|
|
+ if (timeout > 0) {
|
|
|
+ jedis.expire(key, unit.toSeconds(timeout));
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ });
|
|
|
+ }
|
|
|
+}
|