基于 RedisTemplate 自定义 Redis 操作类

7,264 阅读29分钟

该操作类基于自定义的 RedisTemplate,关于自定义 RedisTemplate,请参考Spring Boot 2.0 集成 redis

BaseRedisOperator

该类的设计思路是,与 Redis 命令语法及格式保持一致,以达到使用该类与直接使用 Redis 命令相似的体验。

异常处理: 吞掉异常,之后根据返回值类型进行返回。boolean 返回 false, Long 视情况返回 0 或 null,其他类型返回 null

由于 Redis 命令众多,并没有实现全部的命令,而是实现了常用的大部分命令。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Redis 操作类
 * 设计思路:与 Redis 命令语法及格式保持一致,以达到与直接使用 Redis 命令相似的体验
 * 异常处理: 吞掉异常,之后根据返回值类型进行返回。boolean 返回 false, Long 视情况返回 0 或 null,其他类型返回 null
 */
@Component
public class BaseRedisOperator {

    private static final Logger LOGGER = LoggerFactory.getLogger(BaseRedisOperator.class);

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private HashOperations<String, String, Object> hashOperations;


    /**
     * 元素插入位置
     */
    public enum Position {
        BEFORE, AFTER
    }

    /**
     * 修剪 list,删除为 null 的元素
     *
     * @param objects 原始对象集合
     * @return 实际对象集合
     */
    private List<Object> trimList(List<Object> objects) {
        if (objects == null) {
            return new ArrayList<>();
        }
        objects.removeAll(Collections.singleton(null));
        if (objects.size() == 0) {
            return new ArrayList<>();
        }
        return objects;
    }

    /**
     * 修剪 set,删除为 null 的元素
     *
     * @param objects 原始对象集合
     * @return 实际对象集合
     */
    private Set<Object> trimSet(Set<Object> objects) {
        if (objects == null) {
            return new HashSet<>();
        }
        objects.removeAll(Collections.singleton(null));
        if (objects.size() == 0) {
            return new HashSet<>();
        }
        return objects;
    }


    /**************************************************** key 相关操作 *************************************************/

    /**
     * 实现命令 : KEYS pattern
     * 查找所有符合 pattern 模式的 key
     * ?     匹配单个字符
     * *     匹配0到多个字符
     * [ac] 匹配a和c
     * [a-c]  匹配a到c
     * [^a]  匹配除了a以外的字符
     *
     * @param pattern redis pattern 表达式
     * @return 匹配到的 key
     */
    @Nullable
    public Set<String> keys(String pattern) {
        try {
            return redisTemplate.keys(pattern);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : DEL key1 [key2 ...]
     * 删除一个或多个key
     *
     * @param keys
     * @return 删除的数量
     */
    public Long del(String... keys) {
        try {
            Set<String> keySet = Stream.of(keys).collect(Collectors.toSet());
            return redisTemplate.delete(keySet);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return 0L;
        }
    }

    /**
     * 实现命令 : UNLINK key1 [key2 ...]
     * 删除一个或多个key
     *
     * @param keys
     * @return 已删除的 key 的数量
     */
    public Long unlink(String... keys) {
        try {
            Set<String> keySet = Stream.of(keys).collect(Collectors.toSet());
            return redisTemplate.unlink(keySet);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return 0L;
        }
    }


    /**
     * 实现命令 : EXISTS key1 [key2 ...]
     * 查看 key 是否存在,返回存在 key 的个数
     *
     * @param keys
     * @return key 的个数
     */
    @Nullable
    public Long exists(String... keys) {
        try {
            Set<String> keySet = Stream.of(keys).collect(Collectors.toSet());
            return redisTemplate.countExistingKeys(keySet);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : TYPE key
     * 查看 key 的 value 的类型
     *
     * @param key
     * @return none|string|list|set|zset|hash
     */
    public String type(String key) {
        try {
            DataType dataType = redisTemplate.type(key);
            return dataType == null ? DataType.NONE.code() : dataType.code();
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return DataType.NONE.code();
        }
    }


    /**
     * 实现命令 : PERSIST key
     * 取消 key 的超时时间,持久化 key
     *
     * @param key
     * @return true:成功
     */
    public boolean persist(String key) {
        try {
            Boolean result = redisTemplate.persist(key);
            return null != result && result;
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return false;
        }
    }


    /**
     * 实现命令 : TTL key
     * 返回给定 key 的剩余生存时间
     * 单位: 秒
     *
     * @param key
     * @return -2:key不存在 -1:永不过期
     */
    @Nullable
    public Long ttl(String key) {
        try {
            return redisTemplate.getExpire(key);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }

    /**
     * 实现命令 : PTTL key
     * 返回给定 key 的剩余生存时间,key不存在返回 null
     * 单位: 毫秒
     *
     * @param key
     * @return key 的剩余生存时间,单位: 毫秒
     */
    @Nullable
    public Long pTtl(String key) {
        try {
            return redisTemplate.getExpire(key, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }

    /**
     * 实现命令 : EXPIRE key 秒
     * 设置key 的生存时间
     * 单位 : 秒
     *
     * @param key
     * @param ttl 秒
     * @return true:成功
     */
    public boolean expire(String key, int ttl) {
        try {
            Boolean result = redisTemplate.expire(key, ttl, TimeUnit.SECONDS);
            return null != result && result;
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return false;
        }
    }

    /**
     * 实现命令 : PEXPIRE key 毫秒
     * 设置key 的生存时间
     * 单位 : 毫秒
     *
     * @param key
     * @return true:成功
     */
    public boolean pExpire(String key, Long ttl) {
        try {
            Boolean result = redisTemplate.expire(key, ttl, TimeUnit.MILLISECONDS);
            return null != result && result;
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return false;
        }
    }

    /**
     * 实现命令 : EXPIREAT key Unix时间戳(自1970年1月1日以来的秒数)
     * 设置key 的过期时间
     *
     * @param key
     * @param unixTimestamp 10位 Unix时间戳(自1970年1月1日以来的秒数)
     * @return true:成功
     */
    public boolean expireAt(String key, Long unixTimestamp) {
        try {
            Boolean result = redisTemplate.expireAt(key, new Date(unixTimestamp * 1000));
            return result != null && result;
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return false;
        }
    }


    /**
     * 实现命令 : RENAME key newkey
     * 重命名key,如果 newKey已经存在,则删掉 newKey 再重命名
     *
     * @param oldKey key 的原始名称
     * @param newKey key 的新名称
     */
    public void rename(String oldKey, String newKey) {
        try {
            redisTemplate.rename(oldKey, newKey);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
        }
    }

    /**
     * 实现命令 : RENAMENX key newkey
     * 安全重命名 key,newKey不存在时才重命名
     *
     * @param oldKey key 的原始名称
     * @param newKey key 的新名称
     * @return true:成功
     */
    public boolean renameNx(String oldKey, String newKey) {
        try {
            Boolean result = redisTemplate.renameIfAbsent(oldKey, newKey);
            return result != null && result;
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return false;
        }
    }

    /**************************************************** key 相关操作 *************************************************/


    /************************************************* String 相关操作 *************************************************/

    /**
     * 实现命令 : SET key value
     * 添加一个持久化的 String 类型的键值对
     *
     * @param key
     * @param value
     */
    public void set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
        }
    }

    /**
     * 实现命令 : SETEX key value 秒 / SET key value EX 秒
     * 添加一个 String 类型的键值对,并设置生存时间
     *
     * @param key
     * @param value
     * @param ttl   key 的生存时间,单位:秒
     */
    public void set(String key, Object value, int ttl) {
        try {
            redisTemplate.opsForValue().set(key, value, ttl, TimeUnit.SECONDS);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
        }
    }


    /**
     * 实现命令 :  PSETEX key value 毫秒 / SET key value PX 毫秒
     * 添加一个 String 类型的键值对,并设置生存时间
     *
     * @param key
     * @param value
     * @param ttl   key 的生存时间,单位:毫秒
     */
    public void set(String key, Object value, long ttl) {
        try {
            redisTemplate.opsForValue().set(key, value, ttl, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
        }
    }


    /**
     * 实现命令 : SET key value [EX 秒|PX 毫秒] [NX|XX]
     * 添加一个 String 类型的键值对,
     * ttl、timeUnit 不为 null 时设置生存时间
     * keyIfExist 不为 null 时,设置 NX 或 XX 模式
     *
     * @param key
     * @param value
     * @param ttl        生存时间
     * @param timeUnit   生存时间的单位,秒或毫秒
     * @param keyIfExist true 表示 xx,key 存在时才添加. false 表示 nx,key 不存在时才添加
     */
    public boolean set(String key, Object value, Long ttl, TimeUnit timeUnit, Boolean keyIfExist) {
        try {
            Boolean result = false;

            if ((ttl == null || timeUnit == null) && (keyIfExist == null)) {
                // SET key value
                redisTemplate.opsForValue().set(key, value);
                result = true;
            }

            if (ttl != null && timeUnit != null && keyIfExist == null) {
                // SET key value [EX 秒|PX 毫秒]
                redisTemplate.opsForValue().set(key, value, ttl, timeUnit);
                result = true;
            }

            if ((ttl == null || timeUnit == null) && (keyIfExist != null) && keyIfExist) {
                // SET key value XX
                result = redisTemplate.opsForValue().setIfPresent(key, value);
            }

            if (ttl != null && timeUnit != null && keyIfExist != null && keyIfExist) {
                // SET key value [EX 秒|PX 毫秒] XX
                result = redisTemplate.opsForValue().setIfPresent(key, value, ttl, timeUnit);
            }

            if ((ttl == null || timeUnit == null) && (keyIfExist != null) && (!keyIfExist)) {
                // SET key value NX
                result = redisTemplate.opsForValue().setIfAbsent(key, value);
            }

            if (ttl != null && timeUnit != null && keyIfExist != null && (!keyIfExist)) {
                // SET key value [EX 秒|PX 毫秒] NX
                result = redisTemplate.opsForValue().setIfAbsent(key, value, ttl, timeUnit);
            }

            return result != null && result;
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
        }
        return false;
    }


    /**
     * 实现命令 : MSET key1 value1 [key2 value2...]
     * 批量添加键值对,已存在的 key 会被覆盖
     *
     * @param keyValueMap 键值对集合
     */
    public void mSet(Map<String, Object> keyValueMap) {
        try {
            redisTemplate.opsForValue().multiSet(keyValueMap);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
        }
    }


    /**
     * 实现命令 : MSETNX key1 value1 [key2 value2...]
     * 安全批量添加键值对,只要有一个 key 已存在,所有的键值对都不会插入
     *
     * @param keyValueMap 键值对集合
     */
    public void mSetNx(Map<String, Object> keyValueMap) {
        try {
            redisTemplate.opsForValue().multiSetIfAbsent(keyValueMap);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
        }
    }


    /**
     * 实现命令 : SETRANGE key 下标 str
     * 覆盖 原始 value 的一部分,从指定的下标开始覆盖, 覆盖的长度为指定的字符串的长度。
     *
     * @param key
     * @param offset 开始覆盖的位置,下标从0开始,包括开始位置
     * @param str    字符串
     */
    public void setRange(String key, int offset, Object str) {
        try {
            redisTemplate.opsForValue().set(key, str, offset);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
        }
    }


    /**
     * 实现命令 : APPEND key value
     * 在原始 value 末尾追加字符串
     *
     * @param key
     * @param str 要追加的字符串
     */
    public void append(String key, String str) {
        try {
            redisTemplate.opsForValue().append(key, str);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
        }
    }


    /**
     * 实现命令 : GETSET key value
     * 设置 key 的 value 并返回旧 value
     *
     * @param key
     * @param value
     * @return 旧 value
     */
    @Nullable
    public Object getSet(String key, Object value) {
        try {
            return redisTemplate.opsForValue().getAndSet(key, value);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : GET key
     * 获取一个key的value
     *
     * @param key
     * @return value
     */
    @Nullable
    public Object get(String key) {
        try {
            return redisTemplate.opsForValue().get(key);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }

    /**
     * 实现命令 : MGET key1 [key2...]
     * 获取多个key的value
     *
     * @param keys
     * @return value 列表
     */
    @Nullable
    public List<Object> mGet(String... keys) {
        try {
            Set<String> keySet = Stream.of(keys).collect(Collectors.toSet());
            List<Object> objectList = redisTemplate.opsForValue().multiGet(keySet);
            return this.trimList(objectList);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : GETRANGE key 开始下标 结束下标
     * 获取指定key的value的子串,下标从0开始,包括开始下标,也包括结束下标。
     *
     * @param key
     * @param start 开始下标
     * @param end   结束下标
     * @return value的子串
     */
    @Nullable
    public String getRange(String key, int start, int end) {
        try {
            return redisTemplate.opsForValue().get(key, start, end);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : STRLEN key
     * 获取 key 对应 value 的字符串长度
     *
     * @param key
     * @return value 的字符串长度
     */
    @Nullable
    public Long strLen(String key) {
        try {
            return redisTemplate.opsForValue().size(key);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : INCR key
     * 给 value 加 1,value 必须是整数
     *
     * @param key
     * @return 加后的值
     */
    @Nullable
    public Long inCr(String key) {
        try {
            return redisTemplate.opsForValue().increment(key);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }

    /**
     * 实现命令 : INCRBY key 整数
     * 给 value 加上一个整数,value 必须是整数
     *
     * @param key
     * @return 加后的值
     */
    @Nullable
    public Long inCrBy(String key, Long number) {
        try {
            return redisTemplate.opsForValue().increment(key, number);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }

    /**
     * 实现命令 : INCRBYFLOAT key 数
     * 给 value 加上一个小数,value 必须是数
     *
     * @param key
     * @return 加后的值
     */
    @Nullable
    public Double inCrByFloat(String key, double number) {
        try {
            return redisTemplate.opsForValue().increment(key, number);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : DECR key
     * 给 value 减去 1,value 必须是整数
     *
     * @param key
     * @return 减后的值
     */
    @Nullable
    public Long deCr(String key) {
        try {
            return redisTemplate.opsForValue().decrement(key);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : DECRBY key 整数
     * 给 value 减去一个整数,value 必须是整数
     *
     * @param key
     * @return 减后的值
     */
    @Nullable
    public Long deCcrBy(String key, Long number) {
        try {
            return redisTemplate.opsForValue().decrement(key, number);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /************************************************* String 相关操作 *************************************************/


    /************************************************* Hash 相关操作 ***************************************************/

    /**
     * 实现命令 : HSET key field value
     * 添加 hash 类型的键值对,如果字段已经存在,则将其覆盖。
     *
     * @param key
     * @param field field
     * @param value
     */
    public void hSet(String key, String field, Object value) {
        try {
            hashOperations.put(key, field, value);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
        }
    }


    /**
     * 实现命令 : HSET key field1 value1 [field2 value2 ...]
     * 添加 hash 类型的键值对,如果字段已经存在,则将其覆盖。
     *
     * @param key
     * @param map 键值对集合
     */
    public void hSet(String key, Map<String, Object> map) {
        try {
            hashOperations.putAll(key, map);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
        }
    }


    /**
     * 实现命令 : HSETNX key field value
     * 添加 hash 类型的键值对,如果字段不存在,才添加
     *
     * @param key
     * @param field field
     * @param value
     * @return
     */
    public boolean hSetNx(String key, String field, Object value) {
        try {
            return hashOperations.putIfAbsent(key, field, value);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return false;
        }
    }


    /**
     * 实现命令 : HGET key field
     * 返回 field 对应的值,field 不存在返回 null
     *
     * @param key
     * @param field field
     * @return value
     */
    @Nullable
    public Object hGet(String key, String field) {
        try {
            return hashOperations.get(key, field);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : HMGET key field1 [field2 ...]
     * 返回 多个 field 对应的值,field 不存在返回 null
     *
     * @param key
     * @param fields field 数组
     * @return value 列表
     */
    @Nullable
    public List<Object> hGet(String key, String... fields) {
        try {
            Set<String> fieldSet = Stream.of(fields).collect(Collectors.toSet());
            List<Object> objectList = hashOperations.multiGet(key, fieldSet);
            return this.trimList(objectList);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : HGETALL key
     * 返回所有的键值对
     *
     * @param key
     * @return 所有的 hash 键值对
     */
    @Nullable
    public Map<String, Object> hGetAll(String key) {
        try {
            return hashOperations.entries(key);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : HKEYS key
     * 获取所有的 field
     *
     * @param key
     * @return 所有的 field
     */
    @Nullable
    public Set<String> hKeys(String key) {
        try {
            return hashOperations.keys(key);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : HVALS key
     * 获取所有的 value
     *
     * @param key
     * @return 所有的 value
     */
    @Nullable
    public List<Object> hValue(String key) {
        try {
            List<Object> objectList = hashOperations.values(key);
            return this.trimList(objectList);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : HDEL key field [field ...]
     * 删除哈希表 key 中的一个或多个指定域,不存在的域将被忽略。
     *
     * @param key
     * @param fields field 数组
     * @return 删除的个数
     */
    public Long hDel(String key, String... fields) {
        try {
            return hashOperations.delete(key, fields);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return 0L;
        }
    }


    /**
     * 实现命令 : HEXISTS key field
     * 判断 key 下指定的 field 是否存在
     *
     * @param key
     * @param field
     * @return true:存在
     */
    @Nullable
    public Boolean hExists(String key, String field) {
        try {
            return hashOperations.hasKey(key, field);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : HLEN key
     * 获取 hash 中 字段:值对 的数量
     *
     * @param key
     * @return 键值对的数量
     */
    @Nullable
    public Long hLen(String key) {
        try {
            return hashOperations.size(key);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : HSTRLEN key field
     * 获取字段对应值的长度
     *
     * @param key
     * @param field
     * @return value 的长度
     */
    @Nullable
    public Long hStrLen(String key, String field) {
        try {
            return hashOperations.lengthOfValue(key, field);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : HINCRBY key field 整数
     * 给字段的值加上一个整数
     *
     * @param key
     * @param field
     * @param number 要加的数
     * @return 运算后的值
     */
    @Nullable
    public Long hInCrBy(String key, String field, long number) {
        try {
            return hashOperations.increment(key, field, number);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : HINCRBYFLOAT key field 浮点数
     * 给字段的值加上一个浮点数
     *
     * @param key
     * @param field
     * @param number 要加的数
     * @return 运算后的值
     */
    @Nullable
    public Double hInCrByFloat(String key, String field, Double number) {
        try {
            return hashOperations.increment(key, field, number);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /************************************************* Hash 相关操作 ***************************************************/


    /************************************************* List 相关操作 ***************************************************/

    /**
     * 实现命令 : LPUSH key 元素1 [元素2 ...]
     * 在最左端依次推入元素
     *
     * @param key
     * @param values
     * @return 执行 LPUSH 命令后,列表的长度。
     */
    @Nullable
    public Long lPush(String key, Object... values) {
        try {
            return redisTemplate.opsForList().leftPushAll(key, values);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : RPUSH key 元素1 [元素2 ...]
     * 在最右端依次推入元素
     *
     * @param key
     * @param values
     * @return 执行 RPUSH 命令后,列表的长度。
     */
    @Nullable
    public Long rPush(String key, Object... values) {
        try {
            return redisTemplate.opsForList().rightPushAll(key, values);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : LPOP key
     * 弹出最左端的元素
     *
     * @param key
     * @return 弹出的元素
     */
    @Nullable
    public Object lPop(String key) {
        try {
            return redisTemplate.opsForList().leftPop(key);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }

    /**
     * 实现命令 : RPOP key
     * 弹出最右端的元素
     *
     * @param key
     * @return 弹出的元素
     */
    @Nullable
    public Object rPop(String key) {
        try {
            return redisTemplate.opsForList().rightPop(key);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : BLPOP key
     * (阻塞式)弹出最左端的元素,如果 key 中没有元素,将一直等待直到有元素或超时为止
     *
     * @param key
     * @param timeout 等待的时间,单位秒
     * @return 弹出的元素
     */
    @Nullable
    public Object bLPop(String key, int timeout) {
        try {
            return redisTemplate.opsForList().leftPop(key, timeout, TimeUnit.SECONDS);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }

    /**
     * 实现命令 : BRPOP key
     * (阻塞式)弹出最右端的元素,将一直等待直到有元素或超时为止
     *
     * @param key
     * @return 弹出的元素
     */
    @Nullable
    public Object bRPop(String key, int timeout) {
        try {
            return redisTemplate.opsForList().rightPop(key, timeout, TimeUnit.SECONDS);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : LINDEX key index
     * 返回指定下标处的元素,下标从0开始
     *
     * @param key
     * @param index 下标,从0开始
     * @return 指定下标处的元素
     */
    @Nullable
    public Object lIndex(String key, int index) {
        try {
            return redisTemplate.opsForList().index(key, index);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : LINSERT key BEFORE|AFTER 目标元素 value
     * 在目标元素前或后插入元素
     *
     * @param key
     * @param position BEFORE|AFTER
     * @param pivot    目标元素
     * @param value
     * @return 插入后列表的长度。如果没有找到目标元素,返回 -1,如果 key 不存在或为空,返回 0
     */
    @Nullable
    public Long lInsert(String key, Position position, Object pivot, Object value) {
        try {
            switch (position) {
                case AFTER:
                    return redisTemplate.opsForList().rightPush(key, pivot, value);
                case BEFORE:
                    return redisTemplate.opsForList().leftPush(key, pivot, value);
                default:
                    return null;
            }
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : LRANGE key 开始下标 结束下标
     * 获取指定范围的元素,下标从0开始,包括开始下标,也包括结束下标(待验证)
     *
     * @param key
     * @param startIndex 开始下标
     * @param endIndex   结束下标
     * @return 指定范围的元素
     */
    @Nullable
    public List<Object> lRange(String key, int startIndex, int endIndex) {
        try {
            List<Object> objectList = redisTemplate.opsForList().range(key, startIndex, endIndex);
            return this.trimList(objectList);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : LLEN key
     * 获取 list 的长度
     *
     * @param key
     * @return list 的长度
     */
    @Nullable
    public Long lLen(String key) {
        try {
            return redisTemplate.opsForList().size(key);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : LREM key count 元素
     * 删除 count 个指定元素,
     *
     * @param key
     * @param count count > 0: 从头往尾移除指定元素。count < 0: 从尾往头移除指定元素。count = 0: 移除列表所有的指定元素。(未验证)
     * @param value
     * @return 删除的数量
     */
    public Long lLen(String key, int count, Object value) {
        try {
            return redisTemplate.opsForList().remove(key, count, value);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return 0L;
        }
    }


    /**
     * 实现命令 : LSET key index 新值
     * 更新指定下标的值,下标从 0 开始,支持负下标,-1表示最右端的元素
     *
     * @param key
     * @param index 下标
     * @param value
     */
    public void lSet(String key, int index, Object value) {
        try {
            redisTemplate.opsForList().set(key, index, value);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
        }
    }


    /**
     * 实现命令 : LTRIM key 开始下标 结束下标
     * 裁剪 list,包含开始也包含结束。
     * [1,b,ss,23] 的 `LTRIM key 1 -2` 的结果为 [b,ss]
     *
     * @param key
     * @param startIndex 开始下标,支持负下标,-1表示最右端的元素
     * @param endIndex   结束下标,支持负下标,-1表示最右端的元素
     */
    public void lTrim(String key, int startIndex, int endIndex) {
        try {
            redisTemplate.opsForList().trim(key, startIndex, endIndex);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
        }
    }


    /**
     * 实现命令 : RPOPLPUSH 源list 目标list
     * 将 源list 的最右端元素弹出,推入到 目标list 的最左端,
     *
     * @param sourceKey 源list
     * @param targetKey 目标list
     * @return 弹出的元素
     */
    @Nullable
    public Object rPopLPush(String sourceKey, String targetKey) {
        try {
            return redisTemplate.opsForList().rightPopAndLeftPush(sourceKey, targetKey);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : BRPOPLPUSH 源list 目标list timeout
     * (阻塞式)将 源list 的最右端元素弹出,推入到 目标list 的最左端,如果 源list 没有元素,将一直等待直到有元素或超时为止
     *
     * @param sourceKey 源list
     * @param targetKey 目标list
     * @param timeout   超时时间,单位秒, 0表示无限阻塞
     * @return 弹出的元素
     */
    @Nullable
    public Object bRPopLPush(String sourceKey, String targetKey, int timeout) {
        try {
            return redisTemplate.opsForList().rightPopAndLeftPush(sourceKey, targetKey, timeout, TimeUnit.SECONDS);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /************************************************* List 相关操作 ***************************************************/


    /************************************************** SET 相关操作 ***************************************************/

    /**
     * 实现命令 : SADD key member1 [member2 ...]
     * 添加成员
     *
     * @param key
     * @param values
     * @return 添加成功的个数
     */
    public Long sAdd(String key, Object... values) {
        try {
            return redisTemplate.opsForSet().add(key, values);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return 0L;
        }
    }


    /**
     * 实现命令 : SREM key member1 [member2 ...]
     * 删除指定的成员
     *
     * @param key
     * @param values
     * @return 删除成功的个数
     */
    public Long sRem(String key, Object... values) {
        try {
            return redisTemplate.opsForSet().remove(key, values);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return 0L;
        }
    }


    /**
     * 实现命令 : SCARD key
     * 获取 set中的成员数量,key不存在返回 0
     *
     * @param key
     * @return set中的成员数量
     */
    @Nullable
    public Long sCard(String key) {
        try {
            return redisTemplate.opsForSet().size(key);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : SISMEMBER key member
     * 查看成员是否存在
     *
     * @param key
     * @param value
     * @return true:存在  false:不存在
     */
    @Nullable
    public Boolean sIsMember(String key, Object value) {
        try {
            Boolean result = redisTemplate.opsForSet().isMember(key, value);
            return result != null && result;
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : SMEMBERS key
     * 获取所有的成员
     *
     * @param key
     * @return 所有的成员
     */
    @Nullable
    public Set<Object> sMembers(String key) {
        try {
            Set<Object> objectSet = redisTemplate.opsForSet().members(key);
            return this.trimSet(objectSet);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : SMOVE 源key 目标key member
     * 移动成员到另一个集合
     *
     * @param sourceKey 源key
     * @param targetKey 目标key
     * @param value
     * @return true:成功
     */
    public boolean sMove(String sourceKey, String targetKey, Object value) {
        try {
            Boolean result = redisTemplate.opsForSet().move(sourceKey, value, targetKey);
            return result != null && result;
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return false;
        }

    }


    /**
     * 实现命令 : SDIFF key [otherKey ...]
     * 求 key 的差集(key中独有的元素),key 不存在视为空集
     *
     * @param key
     * @param otherKeys 相比较的 key
     * @return 差集
     */
    @Nullable
    public Set<Object> sDiff(String key, String... otherKeys) {
        try {
            List<String> otherKeyList = Stream.of(otherKeys).collect(Collectors.toList());
            Set<Object> objectSet = redisTemplate.opsForSet().difference(key, otherKeyList);
            return this.trimSet(objectSet);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : SDIFFSTORE 存储key key [otherKey ...]
     * 存储 key 的差集,存储key已存在元素,则覆盖
     *
     * @param storeKey  存储 key
     * @param key
     * @param otherKeys 相比较的 key
     * @return 差集中元素的数量
     */
    @Nullable
    public Long sDiffStore(String storeKey, String key, String... otherKeys) {
        try {
            List<String> otherKeyList = Stream.of(otherKeys).collect(Collectors.toList());
            return redisTemplate.opsForSet().differenceAndStore(key, otherKeyList, storeKey);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : SINTER key [otherKey ...]
     * 求 key 的交集
     *
     * @param key
     * @param otherKeys 相比较的 key
     * @return 交集
     */
    @Nullable
    public Set<Object> sInter(String key, String... otherKeys) {
        try {
            List<String> otherKeyList = Stream.of(otherKeys).collect(Collectors.toList());
            Set<Object> objectSet = redisTemplate.opsForSet().intersect(key, otherKeyList);
            return this.trimSet(objectSet);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : SINTERSTORE 存储key key [otherKey ...]
     * 存储 key 的交集
     *
     * @param storeKey  存储 key
     * @param key
     * @param otherKeys 相比较的 key
     * @return 交集中元素的数量
     */
    @Nullable
    public Long sInterStore(String storeKey, String key, String... otherKeys) {
        try {
            List<String> otherKeyList = Stream.of(otherKeys).collect(Collectors.toList());
            return redisTemplate.opsForSet().intersectAndStore(key, otherKeyList, storeKey);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : SUNION key [otherKey ...]
     * 求 key 的并集
     *
     * @param key
     * @param otherKeys 相比较的 key
     * @return 并集
     */
    @Nullable
    public Set<Object> sUnion(String key, String... otherKeys) {
        try {
            List<String> otherKeyList = Stream.of(otherKeys).collect(Collectors.toList());
            Set<Object> objectSet = redisTemplate.opsForSet().union(key, otherKeyList);
            return this.trimSet(objectSet);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : SUNIONSTORE 目标key key [otherKey ...]
     * 存储 key 的并集
     *
     * @param storeKey  存储 key
     * @param key
     * @param otherKeys 相比较的 key
     * @return 并集中元素的数量
     */
    @Nullable
    public Long sUnionStore(String storeKey, String key, String... otherKeys) {
        try {
            List<String> otherKeyList = Stream.of(otherKeys).collect(Collectors.toList());
            return redisTemplate.opsForSet().unionAndStore(key, otherKeyList, storeKey);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : SPOP key [count]
     * 随机删除(弹出)一个成员
     *
     * @param key
     * @return
     */
    @Nullable
    public Object sPop(String key) {
        try {
            return redisTemplate.opsForSet().pop(key);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }

    /**
     * 实现命令 : SPOP key [count]
     * 随机删除(弹出)指定个数的成员
     *
     * @param key
     * @param count 个数
     * @return 删除(弹出)的成员
     */
    public List<Object> sPop(String key, int count) {
        try {
            List<Object> objectList = redisTemplate.opsForSet().pop(key, count);
            return this.trimList(objectList);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return new ArrayList<>();
        }
    }


    /**
     * 实现命令 : SRANDMEMBER key [count]
     * 随机返回一个成员
     *
     * @param key
     * @return 返回的成员
     */
    @Nullable
    public Object sRandMember(String key) {
        try {
            return redisTemplate.opsForSet().randomMember(key);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : SRANDMEMBER key [count]
     * 随机返回指定个数的成员, 当 count 大于集合中元素的个数时,会返回重复的元素
     *
     * @param key
     * @param count 个数
     * @return 成员集合
     */
    @Nullable
    public List<Object> sRandMember(String key, int count) {
        try {
            List<Object> objectList = redisTemplate.opsForSet().randomMembers(key, count);
            return this.trimList(objectList);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /************************************************** SET 相关操作 ***************************************************/


    /*********************************************** Sorted SET 相关操作 ***********************************************/

    /**
     * 实现命令 : ZADD key score member
     * 添加一个 成员-分数 对
     *
     * @param key
     * @param value 成员
     * @param score 分数
     * @return
     */
    public boolean zAdd(String key, double score, Object value) {
        try {
            Boolean result = redisTemplate.opsForZSet().add(key, value, score);
            return result != null && result;
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return false;
        }
    }


    /**
     * 实现命令 : ZREM key member [member ...]
     * 删除成员
     *
     * @param key
     * @param values
     * @return 删除的个数
     */
    public Long zRem(String key, Object... values) {
        try {
            return redisTemplate.opsForZSet().remove(key, values);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return 0L;
        }
    }


    /**
     * 实现命令 : ZREMRANGEBYRANK key start stop
     * 删除 startIndex 和 endIndex 间的所有成员,包括开始下标也包括结束下标
     *
     * @param key
     * @param startIndex 开始下标,从0开始,支持负下标,-1表示最右端成员
     * @param endIndex   结束下标,从0开始,支持负下标,-1表示最右端成员
     * @return 删除的个数
     */
    public Long zRemRangeByRank(String key, int startIndex, int endIndex) {
        try {
            return redisTemplate.opsForZSet().removeRange(key, startIndex, endIndex);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return 0L;
        }
    }


    /**
     * 实现命令 : ZREMRANGEBYSCORE key start stop
     * 删除分数段内的所有成员
     * 包括min也包括max (未验证)
     *
     * @param key
     * @param min 小分数
     * @param max 大分数
     * @return 删除的个数
     */
    public Long zRemRangeByScore(String key, double min, double max) {
        try {
            return redisTemplate.opsForZSet().removeRangeByScore(key, min, max);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return 0L;
        }
    }


    /**
     * 实现命令 : ZSCORE key member
     * 获取成员的分数
     *
     * @param key
     * @param value
     * @return 分数
     */
    @Nullable
    public Double zScore(String key, Object value) {
        try {
            return redisTemplate.opsForZSet().score(key, value);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : ZINCRBY key 带符号的双精度浮点数 member
     * 增减成员的分数
     *
     * @param key
     * @param value
     * @param delta 带符号的双精度浮点数
     * @return 分数
     */
    @Nullable
    public Double zInCrBy(String key, Object value, double delta) {
        try {
            return redisTemplate.opsForZSet().incrementScore(key, value, delta);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : ZCARD key
     * 获取集合中成员的个数
     *
     * @param key
     * @return 集合中成员的个数
     */
    @Nullable
    public Long zCard(String key) {
        try {
            return redisTemplate.opsForZSet().size(key);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : ZCOUNT key min max
     * 获取某个分数范围内的成员个数,包括min也包括max (未验证)
     *
     * @param key
     * @param min 小分数
     * @param max 大分数
     * @return 范围内成员的个数
     */
    @Nullable
    public Long zCount(String key, double min, double max) {
        try {
            return redisTemplate.opsForZSet().count(key, min, max);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : ZRANK key member
     * 按分数从小到大获取成员在有序集合中的排名
     *
     * @param key
     * @param value
     * @return 排名
     */
    @Nullable
    public Long zRank(String key, Object value) {
        try {
            return redisTemplate.opsForZSet().rank(key, value);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : ZREVRANK key member
     * 按分数从大到小获取成员在有序集合中的排名
     *
     * @param key
     * @param value
     * @return 排名
     */
    @Nullable
    public Long zRevRank(String key, Object value) {
        try {
            return redisTemplate.opsForZSet().reverseRank(key, value);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : ZRANGE key start end
     * 获取 start下标到 end下标之间到成员,并按分数从小到大返回
     * 下标从0开始,支持负下标,-1表示最后一个成员,包括开始下标,也包括结束下标
     *
     * @param key
     * @param start 开始下标
     * @param end   结束下标
     * @return 成员集合
     */
    @Nullable
    public Set<Object> zRange(String key, int start, int end) {
        try {
            Set<Object> objectSet = redisTemplate.opsForZSet().range(key, start, end);
            return this.trimSet(objectSet);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : ZREVRANGE key start end
     * 获取 start下标到 end下标之间到成员,并按分数从大到小返回
     * 下标从0开始,支持负下标,-1表示最后一个成员,包括开始下标,也包括结束下标
     *
     * @param key
     * @param start 开始下标
     * @param end   结束下标
     * @return 成员集合
     */
    @Nullable
    public Set<Object> zRevRange(String key, int start, int end) {
        try {
            Set<Object> objectSet = redisTemplate.opsForZSet().reverseRange(key, start, end);
            return this.trimSet(objectSet);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : ZRANGEBYSCORE key min max
     * 获取分数范围内的成员并按从小到大返回,包括 min,也包括 max
     *
     * @param key
     * @param min 小分数
     * @param max 大分数
     * @return 成员集合
     */
    @Nullable
    public Set<Object> zRangeByScore(String key, double min, double max) {
        try {
            Set<Object> objectSet = redisTemplate.opsForZSet().rangeByScore(key, min, max);
            return this.trimSet(objectSet);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : ZRANGEBYSCORE key min max LIMIT offset count
     * 分页获取分数范围内的成员并按从小到大返回,包括 min,也包括 max
     *
     * @param key
     * @param min    小分数
     * @param max    大分数
     * @param offset 偏移量,从0开始
     * @param count  取多少条
     * @return 成员集合
     */
    @Nullable
    public Set<Object> zRangeByScore(String key, double min, double max, int offset, int count) {
        try {
            Set<Object> objectSet = redisTemplate.opsForZSet().rangeByScore(key, min, max, offset, count);
            return this.trimSet(objectSet);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : ZREVRANGEBYSCORE key min max
     * 获取分数范围内的成员并按从大到小返回
     *
     * @param key
     * @param min 小分数
     * @param max 大分数
     * @return 成员集合
     */
    @Nullable
    public Set<Object> zRevRangeByScore(String key, double min, double max) {
        try {
            Set<Object> objectSet = redisTemplate.opsForZSet().reverseRangeByScore(key, min, max);
            return this.trimSet(objectSet);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : ZREVRANGEBYSCORE key min max LIMIT offset count
     * 分页获取分数范围内的成员并按从大到小返回,包括min也包括max
     *
     * @param key
     * @param min    小分数
     * @param max    大分数
     * @param offset 偏移量,从0开始
     * @param count  取多少条
     * @return 成员集合
     */
    @Nullable
    public Set<Object> zRevRangeByScore(String key, double min, double max, int offset, int count) {
        try {
            Set<Object> objectSet = redisTemplate.opsForZSet().reverseRangeByScore(key, min, max, offset, count);
            return this.trimSet(objectSet);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }

    /*********************************************** Sorted SET 相关操作 ***********************************************/

}

RedisOperator

RedisOperator 类在 BaseRedisOperator 的基础上补充了一下无法按照 Redis 命令设计的类。

设计思路: 试图在保持 Redis 命令语法格式与使用方便上达到平衡


import com.cusc.its.operatemanage.utils.jackson.JacksonUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.TimeUnit;


/**
 * Redis 操作类,继承自 BaseRedisOperator, 添加了一些新接口
 * 设计思路: 试图在保持 Redis 命令语法格式与使用方便上达到平衡
 * 异常处理: 吞掉异常,之后根据返回值类型进行返回。boolean 返回 false, Long 视情况返回 0 或 null,其他类型返回 null
 */
@Component
public class RedisOperator extends BaseRedisOperator {

    private static final Logger LOGGER = LoggerFactory.getLogger(RedisOperator.class);

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private HashOperations<String, String, Object> hashOperations;


    /**
     * 元素插入位置
     */
    public enum Position {
        BEFORE, AFTER
    }


    /**
     * 将表面是 Object 实际是 map或具体对象 还原成具体对象
     *
     * @param objects       原始对象集合
     * @param typeReference 实际对象类型
     * @param <T>           实际对象类型
     * @return 实际对象集合
     */
    private <T> List<T> objectList2TList(Collection<Object> objects, TypeReference<List<T>> typeReference) {
        if (objects == null) {
            return new ArrayList<>();
        }
        // 修剪 list,删除为 null 的元素
        objects.removeAll(Collections.singleton(null));
        if (objects.size() == 0) {
            return new ArrayList<>();
        }
        return JacksonUtil.object2Type(objects, typeReference);
    }

    /**
     * 将表面是 Object 实际是 map或具体对象 还原成具体对象
     *
     * @param objects       原始对象集合
     * @param typeReference 实际对象类型
     * @param <T>           实际对象类型
     * @return 实际对象集合
     */
    private <T> Set<T> objectList2TSet(Collection<Object> objects, TypeReference<Set<T>> typeReference) {
        if (objects == null) {
            return new HashSet<>();
        }
        // 修剪 set,删除为 null 的元素
        objects.removeAll(Collections.singleton(null));
        if (objects.size() == 0) {
            return new HashSet<>();
        }
        return JacksonUtil.object2Type(objects, typeReference);
    }


    /**************************************************** key 相关操作 *************************************************/


    /**
     * 实现命令 : DEL key1 [key2 ...]
     * 删除一个或多个key
     *
     * @param keys
     * @return 已删除的 key 的数量
     */
    public Long del(Collection<String> keys) {
        try {
            Set<String> keySet = new HashSet<>(keys);
            return redisTemplate.delete(keySet);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return 0L;
        }
    }

    /**
     * 实现命令 : UNLINK key1 [key2 ...]
     * 删除一个或多个key
     *
     * @param keys
     * @return 已删除的 key 的数量
     */
    public Long unlink(Collection<String> keys) {
        try {
            Set<String> keySet = new HashSet<>(keys);
            return redisTemplate.unlink(keySet);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return 0L;
        }
    }


    /**
     * 实现命令 : EXISTS key1 [key2 ...]
     * 查看 key 是否存在,返回存在 key 的个数
     *
     * @param keys
     * @return key 的个数
     */
    @Nullable
    public Long exists(Collection<String> keys) {
        try {
            Set<String> keySet = new HashSet<>(keys);
            return redisTemplate.countExistingKeys(keySet);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }

    /**************************************************** key 相关操作 *************************************************/


    /************************************************* String 相关操作 *************************************************/


    /**
     * 实现命令 : GETSET key value
     * 设置 key 的 value 并返回旧 value
     *
     * @param key
     * @param value
     * @param typeReference 返回值类型
     * @param <T>           value 类型
     * @return 旧 value
     */
    @Nullable
    public <T> T getSet(String key, T value, TypeReference<T> typeReference) {
        try {
            Object obj = redisTemplate.opsForValue().getAndSet(key, value);
            return JacksonUtil.object2Type(obj, typeReference);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : GET key
     * 获取一个key的value
     *
     * @param key
     * @param typeReference 返回值类型
     * @param <T>           value 类型
     * @return value
     */
    @Nullable
    public <T> T get(String key, TypeReference<T> typeReference) {
        try {
            Object obj = redisTemplate.opsForValue().get(key);
            return JacksonUtil.object2Type(obj, typeReference);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }

    /**
     * 实现命令 : MGET key1 [key2...]
     * 获取多个key的value
     *
     * @param keys
     * @param typeReference 返回值类型
     * @param <T>           value 类型
     * @return value 列表
     */
    @Nullable
    public <T> List<T> mGet(Collection<String> keys, TypeReference<List<T>> typeReference) {
        try {
            Set<String> keySet = new HashSet<>(keys);
            List<Object> objectList = redisTemplate.opsForValue().multiGet(keySet);
            return this.objectList2TList(objectList, typeReference);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /************************************************* String 相关操作 *************************************************/


    /************************************************* Hash 相关操作 ***************************************************/


    /**
     * 实现命令 : HGET key field
     * 返回 field 对应的值,field 不存在返回 null
     *
     * @param key
     * @param field         field
     * @param typeReference 返回值类型
     * @param <T>           value 类型
     * @return value
     */
    @Nullable
    public <T> T hGet(String key, String field, TypeReference<T> typeReference) {
        try {
            Object obj = hashOperations.get(key, field);
            return JacksonUtil.object2Type(obj, typeReference);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : HMGET key field1 [field2 ...]
     * 返回 多个 field 对应的值,field 不存在返回 null
     *
     * @param key
     * @param fields        field 数组
     * @param typeReference 返回值类型
     * @param <T>           value 类型
     * @return value 列表
     */
    @Nullable
    public <T> List<T> hGet(String key, Collection<String> fields, TypeReference<List<T>> typeReference) {
        try {
            Set<String> fieldSet = new HashSet<>(fields);
            List<Object> objectList = hashOperations.multiGet(key, fieldSet);
            return this.objectList2TList(objectList, typeReference);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : HVALS key
     * 获取所有的 value
     *
     * @param key
     * @param typeReference 返回值类型
     * @param <T>           value 类型
     * @return 所有的 value
     */
    @Nullable
    public <T> List<T> hValue(String key, TypeReference<List<T>> typeReference) {
        try {
            List<Object> objectList = hashOperations.values(key);
            return this.objectList2TList(objectList, typeReference);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : HDEL key field [field ...]
     * 删除哈希表 key 中的一个或多个指定域,不存在的域将被忽略。
     *
     * @param key
     * @param fields field 数组
     * @return 删除的个数
     */
    public Long hDel(String key, Collection<String> fields) {
        try {
            Set<String> fieldSet = new HashSet<>(fields);
            return hashOperations.delete(key, fieldSet.toArray());
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return 0L;
        }
    }


    /************************************************* Hash 相关操作 ***************************************************/


    /************************************************* List 相关操作 ***************************************************/


    /**
     * 实现命令 : LPOP key
     * 弹出最左端的元素
     *
     * @param key
     * @param typeReference 返回值类型
     * @param <T>           value 类型
     * @return 弹出的元素
     */
    @Nullable
    public <T> T lPop(String key, TypeReference<T> typeReference) {
        try {
            Object obj = redisTemplate.opsForList().leftPop(key);
            return JacksonUtil.object2Type(obj, typeReference);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }

    /**
     * 实现命令 : RPOP key
     * 弹出最右端的元素
     *
     * @param key
     * @param typeReference 返回值类型
     * @param <T>           value 类型
     * @return 弹出的元素
     */
    @Nullable
    public <T> T rPop(String key, TypeReference<T> typeReference) {
        try {
            Object obj = redisTemplate.opsForList().rightPop(key);
            return JacksonUtil.object2Type(obj, typeReference);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : BLPOP key
     * (阻塞式)弹出最左端的元素,如果 key 中没有元素,将一直等待直到有元素或超时为止
     *
     * @param key
     * @param timeout       等待的时间,单位秒
     * @param typeReference 返回值类型
     * @param <T>           value 类型
     * @return 弹出的元素
     */
    @Nullable
    public <T> T bLPop(String key, int timeout, TypeReference<T> typeReference) {
        try {
            Object obj = redisTemplate.opsForList().leftPop(key, timeout, TimeUnit.SECONDS);
            return JacksonUtil.object2Type(obj, typeReference);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }

    /**
     * 实现命令 : BRPOP key
     * (阻塞式)弹出最右端的元素,将一直等待直到有元素或超时为止
     *
     * @param key
     * @param typeReference 返回值类型
     * @param <T>           value 类型
     * @return 弹出的元素
     */
    @Nullable
    public <T> T bRPop(String key, int timeout, TypeReference<T> typeReference) {
        try {
            Object obj = redisTemplate.opsForList().rightPop(key, timeout, TimeUnit.SECONDS);
            return JacksonUtil.object2Type(obj, typeReference);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : LINDEX key index
     * 返回指定下标处的元素,下标从0开始
     *
     * @param key
     * @param index         下标,从0开始,支持负下标,-1表示最后一个元素
     * @param typeReference 返回值类型
     * @param <T>           value 类型
     * @return 指定下标处的元素
     */
    @Nullable
    public <T> T lIndex(String key, int index, TypeReference<T> typeReference) {
        try {
            Object obj = redisTemplate.opsForList().index(key, index);
            return JacksonUtil.object2Type(obj, typeReference);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : LRANGE key 开始下标 结束下标
     * 获取指定范围的元素,下标从0开始,支持负下标,-1表示最后一个元素
     * 包括开始下标,也包括结束下标
     *
     * @param key
     * @param startIndex    开始下标
     * @param endIndex      结束下标
     * @param typeReference 返回值类型
     * @param <T>           value 类型
     * @return 指定范围的元素
     */
    @Nullable
    public <T> List<T> lRange(String key, int startIndex, int endIndex, TypeReference<List<T>> typeReference) {
        try {
            List<Object> objectList = redisTemplate.opsForList().range(key, startIndex, endIndex);
            return this.objectList2TList(objectList, typeReference);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : RPOPLPUSH 源list 目标list
     * 将 源list 的最右端元素弹出,推入到 目标list 的最左端,
     *
     * @param sourceKey     源list
     * @param targetKey     目标list
     * @param typeReference 返回值类型
     * @param <T>           value 类型
     * @return 弹出的元素
     */
    @Nullable
    public <T> T rPopLPush(String sourceKey, String targetKey, TypeReference<T> typeReference) {
        try {
            Object obj = redisTemplate.opsForList().rightPopAndLeftPush(sourceKey, targetKey);
            return JacksonUtil.object2Type(obj, typeReference);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : BRPOPLPUSH 源list 目标list timeout
     * (阻塞式)将 源list 的最右端元素弹出,推入到 目标list 的最左端,如果 源list 没有元素,将一直等待直到有元素或超时为止
     *
     * @param sourceKey     源list
     * @param targetKey     目标list
     * @param timeout       超时时间,单位秒, 0表示无限阻塞
     * @param typeReference 返回值类型
     * @param <T>           value 类型
     * @return 弹出的元素
     */
    @Nullable
    public <T> T bRPopLPush(String sourceKey, String targetKey, int timeout, TypeReference<T> typeReference) {
        try {
            Object obj = redisTemplate.opsForList().rightPopAndLeftPush(sourceKey, targetKey, timeout, TimeUnit.SECONDS);
            return JacksonUtil.object2Type(obj, typeReference);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /************************************************* List 相关操作 ***************************************************/


    /************************************************** SET 相关操作 ***************************************************/


    /**
     * 实现命令 : SMEMBERS key
     * 获取所有的成员
     *
     * @param key
     * @param typeReference 返回值类型
     * @param <T>           value 类型
     * @return 所有的成员
     */
    @Nullable
    public <T> Set<T> sMembers(String key, TypeReference<Set<T>> typeReference) {
        try {
            Set<Object> objectSet = redisTemplate.opsForSet().members(key);
            return this.objectList2TSet(objectSet, typeReference);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : SDIFF key [otherKey ...]
     * 求 key 的差集(key中独有的元素),key 不存在视为空集
     *
     * @param key
     * @param typeReference 返回值类型
     * @param otherKeys     相比较的 key
     * @param <T>           value 类型
     * @return 差集
     */
    @Nullable
    public <T> Set<T> sDiff(String key, TypeReference<Set<T>> typeReference, String... otherKeys) {
        try {
            Set<String> otherKeyList = new HashSet<>(Arrays.asList(otherKeys));
            Set<Object> objectSet = redisTemplate.opsForSet().difference(key, otherKeyList);
            return this.objectList2TSet(objectSet, typeReference);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : SDIFFSTORE 存储key key [otherKey ...]
     * 存储 key 的差集,存储key已存在元素,则覆盖
     *
     * @param storeKey  存储 key
     * @param key
     * @param otherKeys 相比较的 key
     * @return 差集中元素的数量
     */
    @Nullable
    public Long sDiffStore(String storeKey, String key, Collection<String> otherKeys) {
        try {
            Set<String> otherKeyList = new HashSet<>(otherKeys);
            return redisTemplate.opsForSet().differenceAndStore(key, otherKeyList, storeKey);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : SINTER key [otherKey ...]
     * 求 key 的交集
     *
     * @param key
     * @param typeReference 返回值类型
     * @param otherKeys     相比较的 key
     * @param <T>           value 类型
     * @return 交集
     */
    @Nullable
    public <T> Set<T> sInter(String key, TypeReference<Set<T>> typeReference, String... otherKeys) {
        try {
            Set<String> otherKeyList = new HashSet<>(Arrays.asList(otherKeys));
            Set<Object> objectSet = redisTemplate.opsForSet().intersect(key, otherKeyList);
            return this.objectList2TSet(objectSet, typeReference);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : SINTERSTORE 存储key key [otherKey ...]
     * 存储 key 的交集
     *
     * @param storeKey  存储 key
     * @param key
     * @param otherKeys 相比较的 key
     * @return 交集中元素的数量
     */
    @Nullable
    public Long sInterStore(String storeKey, String key, Collection<String> otherKeys) {
        try {
            Set<String> otherKeyList = new HashSet<>(otherKeys);
            return redisTemplate.opsForSet().intersectAndStore(key, otherKeyList, storeKey);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : SUNION key [otherKey ...]
     * 求 key 的并集
     *
     * @param key
     * @param typeReference 返回值类型
     * @param otherKeys     相比较的 key
     * @param <T>           value 类型
     * @return 并集
     */
    @Nullable
    public <T> Set<T> sUnion(String key, TypeReference<Set<T>> typeReference, String... otherKeys) {
        try {
            Set<String> otherKeyList = new HashSet<>(Arrays.asList(otherKeys));
            Set<Object> objectSet = redisTemplate.opsForSet().union(key, otherKeyList);
            return this.objectList2TSet(objectSet, typeReference);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : SUNIONSTORE 目标key key [otherKey ...]
     * 存储 key 的并集
     *
     * @param storeKey  存储 key
     * @param key
     * @param otherKeys 相比较的 key
     * @return 并集中元素的数量
     */
    @Nullable
    public Long sUnionStore(String storeKey, String key, Collection<String> otherKeys) {
        try {
            Set<String> otherKeyList = new HashSet<>(otherKeys);
            return redisTemplate.opsForSet().unionAndStore(key, otherKeyList, storeKey);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : SPOP key [count]
     * 随机删除(弹出)一个成员
     *
     * @param key
     * @param typeReference 返回值类型
     * @param <T>           value 类型
     * @return
     */
    @Nullable
    public <T> T sPop(String key, TypeReference<T> typeReference) {
        try {
            Object obj = redisTemplate.opsForSet().pop(key);
            return JacksonUtil.object2Type(obj, typeReference);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }

    /**
     * 实现命令 : SPOP key [count]
     * 随机删除(弹出)指定个数的成员
     *
     * @param key
     * @param count         个数
     * @param typeReference 返回值类型
     * @param <T>           value 类型
     * @return 删除(弹出)的成员
     */
    public <T> List<T> sPop(String key, int count, TypeReference<List<T>> typeReference) {
        try {
            List<Object> objectList = redisTemplate.opsForSet().pop(key, count);
            return this.objectList2TList(objectList, typeReference);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return new ArrayList<>();
        }
    }


    /**
     * 实现命令 : SRANDMEMBER key [count]
     * 随机返回一个成员
     *
     * @param key
     * @param typeReference 返回值类型
     * @param <T>           value 类型
     * @return 返回的成员
     */
    @Nullable
    public <T> T sRandMember(String key, TypeReference<T> typeReference) {
        try {
            Object obj = redisTemplate.opsForSet().randomMember(key);
            return JacksonUtil.object2Type(obj, typeReference);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : SRANDMEMBER key [count]
     * 随机返回指定个数的成员, 当 count 大于集合中元素的个数时,会返回重复的元素
     *
     * @param key
     * @param count         个数
     * @param typeReference 返回值类型
     * @param <T>           value 类型
     * @return 成员集合
     */
    @Nullable
    public <T> List<T> sRandMember(String key, int count, TypeReference<List<T>> typeReference) {
        try {
            List<Object> objectList = redisTemplate.opsForSet().randomMembers(key, count);
            return this.objectList2TList(objectList, typeReference);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /************************************************** SET 相关操作 ***************************************************/


    /*********************************************** Sorted SET 相关操作 ***********************************************/


    /**
     * 实现命令 : ZRANGE key start end
     * 获取 start下标到 end下标之间到成员,并按分数从小到大返回
     * 下标从0开始,支持负下标,-1表示最后一个成员,包括开始下标,也包括结束下标
     *
     * @param key
     * @param start         开始下标
     * @param end           结束下标
     * @param typeReference 返回值类型
     * @param <T>           value 类型
     * @return 成员集合
     */
    @Nullable
    public <T> Set<T> zRange(String key, int start, int end, TypeReference<Set<T>> typeReference) {
        try {
            Set<Object> objectSet = redisTemplate.opsForZSet().range(key, start, end);
            return this.objectList2TSet(objectSet, typeReference);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : ZREVRANGE key start end
     * 获取 start下标到 end下标之间到成员,并按分数从大到小返回
     * 下标从0开始,支持负下标,-1表示最后一个成员,包括开始下标,也包括结束下标
     *
     * @param key
     * @param start         开始下标
     * @param end           结束下标
     * @param typeReference 返回值类型
     * @param <T>           value 类型
     * @return 成员集合
     */
    @Nullable
    public <T> Set<T> zRevRange(String key, int start, int end, TypeReference<Set<T>> typeReference) {
        try {
            Set<Object> objectSet = redisTemplate.opsForZSet().reverseRange(key, start, end);
            return this.objectList2TSet(objectSet, typeReference);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : ZRANGEBYSCORE key min max
     * 获取分数范围内的成员并按从小到大返回,包括 min,也包括 max
     *
     * @param key
     * @param min           小分数
     * @param max           大分数
     * @param typeReference 返回值类型
     * @param <T>           value 类型
     * @return 成员集合
     */
    @Nullable
    public <T> Set<T> zRangeByScore(String key, double min, double max, TypeReference<Set<T>> typeReference) {
        try {
            Set<Object> objectSet = redisTemplate.opsForZSet().rangeByScore(key, min, max);
            return this.objectList2TSet(objectSet, typeReference);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : ZRANGEBYSCORE key min max LIMIT offset count
     * 分页获取分数范围内的成员并按从小到大返回,包括 min,也包括 max
     *
     * @param key
     * @param min           小分数
     * @param max           大分数
     * @param offset        偏移量,从0开始
     * @param count         取多少条
     * @param typeReference 返回值类型
     * @param <T>           value 类型
     * @return 成员集合
     */
    @Nullable
    public <T> Set<T> zRangeByScore(String key, double min, double max, int offset, int count, TypeReference<Set<T>> typeReference) {
        try {
            Set<Object> objectSet = redisTemplate.opsForZSet().rangeByScore(key, min, max, offset, count);
            return this.objectList2TSet(objectSet, typeReference);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : ZREVRANGEBYSCORE key min max
     * 获取分数范围内的成员并按从大到小返回
     *
     * @param key
     * @param min           小分数
     * @param max           大分数
     * @param typeReference 返回值类型
     * @param <T>           value 类型
     * @return 成员集合
     */
    @Nullable
    public <T> Set<T> zRevRangeByScore(String key, double min, double max, TypeReference<Set<T>> typeReference) {
        try {
            Set<Object> objectSet = redisTemplate.opsForZSet().reverseRangeByScore(key, min, max);
            return this.objectList2TSet(objectSet, typeReference);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }


    /**
     * 实现命令 : ZREVRANGEBYSCORE key min max LIMIT offset count
     * 分页获取分数范围内的成员并按从大到小返回,包括min也包括max
     *
     * @param key
     * @param min           小分数
     * @param max           大分数
     * @param offset        偏移量,从0开始
     * @param count         取多少条
     * @param typeReference 返回值类型
     * @param <T>           value 类型
     * @return 成员集合
     */
    @Nullable
    public <T> Set<T> zRevRangeByScore(String key, double min, double max, int offset, int count, TypeReference<Set<T>> typeReference) {
        try {
            Set<Object> objectSet = redisTemplate.opsForZSet().reverseRangeByScore(key, min, max, offset, count);
            return this.objectList2TSet(objectSet, typeReference);
        } catch (Exception e) {
            LOGGER.error("操作 Redis 出错", e);
            return null;
        }
    }

    /*********************************************** Sorted SET 相关操作 ***********************************************/

}

番外

虽然上面的自定义 Redis 操作类,都是对 RedisTemplate 的简单封装,但在简单的事情,只要你认真做也会有收获。

比如说,我在封装 RedisTemplate 的过程中,发现 RedisTemplate 的有些方法的表述与方法的作用不一致,也就是说官方的方法描述写错了。于是我 Fork 了一份 spring-data-redis ,修改了错误的描述后,Pull Requests,等待官方合并,出乎意料的是官方很快合并了该请求,前后不到2天。

也算是为 spring 项目做了一点微小的贡献吧!

通过这件事,我发现给开源项目做贡献,没有想象中的那么难,最困难的可能是如果踏出第一步。之后我会被整个过程写成详细的教程供大家参考。

如何给开源项目贡献代码,以spring为例