supeng před 1 dnem
rodič
revize
1358804df9

+ 67 - 0
content-understanding-core/src/main/java/com/tzld/piaoquan/content/understanding/config/RedisTemplateConfig.java

@@ -0,0 +1,67 @@
+package com.tzld.piaoquan.content.understanding.config;
+
+import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
+import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
+import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
+import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+/**
+ * redis config
+ *
+ * @author supeng
+ * @date 2020/09/02
+ */
+@Configuration
+public class RedisTemplateConfig {
+
+    @Bean
+    @ConfigurationProperties(prefix = "spring.redis.lettuce.pool")
+    public GenericObjectPoolConfig<LettucePoolingClientConfiguration> redisPool() {
+        return new GenericObjectPoolConfig<>();
+    }
+
+    @Bean
+    @ConfigurationProperties(prefix = "spring.redis")
+    public RedisStandaloneConfiguration redisConfig() {
+        return new RedisStandaloneConfiguration();
+    }
+
+    @Bean("factory")
+    @Primary
+    public LettuceConnectionFactory factory(GenericObjectPoolConfig<LettucePoolingClientConfiguration> config, RedisStandaloneConfiguration redisConfig) {
+        LettuceClientConfiguration lettuceClientConfiguration = LettucePoolingClientConfiguration.builder().poolConfig(config).build();
+        return new LettuceConnectionFactory(redisConfig, lettuceClientConfiguration);
+    }
+
+    @Bean(name = "redisTemplate")
+    public RedisTemplate<String, String> getRedisTemplate(@Qualifier("factory") RedisConnectionFactory factory) {
+        return buildRedisTemplate(factory);
+    }
+
+    /**
+     * 构建redisTemplate 使用string序列化
+     *
+     * @param factory
+     * @return
+     */
+    public RedisTemplate<String, String> buildRedisTemplate(RedisConnectionFactory factory) {
+        RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
+        redisTemplate.setConnectionFactory(factory);
+        // key的序列化类型 保证可读性
+        redisTemplate.setKeySerializer(new StringRedisSerializer());
+        redisTemplate.setValueSerializer(new StringRedisSerializer());
+        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
+        redisTemplate.setHashValueSerializer(new StringRedisSerializer());
+        return redisTemplate;
+    }
+
+}

+ 25 - 1
content-understanding-core/src/main/java/com/tzld/piaoquan/content/understanding/service/impl/GeminiGenerateContentAction.java

@@ -7,10 +7,13 @@ import com.tzld.piaoquan.content.understanding.common.enums.ContentTypeEnum;
 import com.tzld.piaoquan.content.understanding.model.param.ActionParam;
 import com.tzld.piaoquan.content.understanding.model.param.GeminiParam;
 import com.tzld.piaoquan.content.understanding.service.Action;
+import com.tzld.piaoquan.content.understanding.service.ContentService;
 import com.tzld.piaoquan.content.understanding.util.HttpClientUtil;
 import com.tzld.piaoquan.content.understanding.util.HttpPoolClient;
+import com.tzld.piaoquan.content.understanding.util.RedisUtil;
 import com.tzld.piaoquan.content.understanding.util.RetryUtil;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
@@ -40,12 +43,33 @@ public class GeminiGenerateContentAction implements Action {
 
     private static final Integer RETRY_TIMES = 5;
 
+    @Autowired
+    private RedisUtil redisUtil;
+
+    private static final String GEMINI_API_KEY_INDEX = "gemini:apikey:list.index";
+
     @Override
     public String execute(ActionParam param) {
         Integer type = param.getType();
 //        ThreadLocalRandom random = ThreadLocalRandom.current();
 //        int index = random.nextInt(apiKeyList.size());
 //        String apiKey = apiKeyList.get(index);
+        int size = apiKeyList.size();
+        int index = 0;
+        Long value = redisUtil.incr(GEMINI_API_KEY_INDEX);
+        if (Objects.nonNull(value)) {
+            long indexValue = value - 1;
+            index = (int) (indexValue % size);
+            if (value > 0 && value % size == 0) {
+                try {
+                    redisUtil.deleteKey(GEMINI_API_KEY_INDEX);
+                } catch (Exception e) {
+                    // 重置失败不影响当前获取元素的操作,只记录错误
+                    log.error(e.getMessage(), e);
+                }
+            }
+        }
+        String apiKey = apiKeyList.get(index);
         GeminiParam geminiParam = new GeminiParam();
         geminiParam.setType(type);
         geminiParam.setPrompt(param.getPrompt());
@@ -54,7 +78,7 @@ public class GeminiGenerateContentAction implements Action {
                 || Objects.equals(ContentTypeEnum.VTT.getValue(), type)) {
             geminiParam.setMediaUrl(param.getInput());
         }
-//        geminiParam.setApiKey(apiKey);
+        geminiParam.setApiKey(apiKey);
         geminiParam.setModel(MODEL);
         geminiParam.setTemperature(TEMPERATURE);
         return RetryUtil.executeWithRetry(() -> {

+ 930 - 0
content-understanding-core/src/main/java/com/tzld/piaoquan/content/understanding/util/RedisUtil.java

@@ -0,0 +1,930 @@
+package com.tzld.piaoquan.content.understanding.util;
+
+import org.apache.ibatis.cache.CacheException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.data.redis.core.Cursor;
+import org.springframework.data.redis.core.RedisCallback;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.ScanOptions;
+import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
+import org.springframework.data.redis.core.script.DefaultRedisScript;
+import org.springframework.scripting.support.StaticScriptSource;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * redis 操作
+ *
+ * @author supeng
+ * @date 2020/11/10
+ */
+@Component
+public class RedisUtil {
+    /**
+     * 锁前缀
+     */
+    private static final String LOCK_PREFIX = "LOCK_";
+    /**
+     * 默认重试次数
+     */
+    private static final Integer DEFAULT_RETRIES = 1;
+    /**
+     * 默认 10毫秒
+     */
+    private static final Long DEFAULT_INTERVAL = 10L;
+
+    /**
+     * 加锁lua脚本
+     */
+    private static final String LOCK = "if (redis.call('exists', KEYS[1]) == 0) then " +
+            "redis.call('hset', KEYS[1], ARGV[2], 1); " +
+            "redis.call('pexpire', KEYS[1], ARGV[1]); " +
+            "return 1; " +
+            "end; " +
+            "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
+            "redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
+            "redis.call('pexpire', KEYS[1], ARGV[1]); " +
+            "return 1; " +
+            "end; " +
+            "return 0;";
+
+    /**
+     * 解锁 lua 脚本
+     */
+    public static final String UNLOCK = "if (redis.call('hexists', KEYS[1], ARGV[1]) == 0) then " +
+            "return nil; " +
+            "end; " +
+            "local counter = redis.call('hincrby', KEYS[1], ARGV[1], -1); " +
+            "if (counter > 0) then " +
+            "return 0; " +
+            "else " +
+            "redis.call('del', KEYS[1]); " +
+            "return 1; " +
+            "end; " +
+            "return nil;";
+
+    @Qualifier("redisTemplate")
+    @Autowired
+    RedisTemplate<String, String> redisTemplate;
+
+    /** ================Key相关操作================ */
+
+    /**
+     * 是否存在key
+     *
+     * @param key
+     * @return
+     */
+    public Boolean hasKey(String key) {
+        return redisTemplate.hasKey(key);
+    }
+
+    /**
+     * 删除 key
+     *
+     * @param key
+     * @return
+     */
+    public Boolean deleteKey(String key) {
+        return redisTemplate.delete(key);
+    }
+
+    /**
+     * 删除多个key
+     *
+     * @param keys
+     * @return
+     */
+    public Long deleteKeys(Collection<String> keys) {
+        return redisTemplate.delete(keys);
+    }
+
+    /**
+     * 设置key的过期时间
+     *
+     * @param key
+     * @param timeout
+     * @return
+     */
+    public Boolean expire(String key, long timeout) {
+        if (timeout < 0) {
+            return false;
+        }
+        return redisTemplate.expire(key, timeout, TimeUnit.SECONDS);
+    }
+
+    /**
+     * 获取key剩余的过期时间,秒
+     *
+     * @param key
+     * @return key不存在,返回-2,key存在并且没有设置过期时间(永久有效),返回 -1
+     */
+    public Long getExpire(String key) {
+        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
+    }
+
+    /** ================String相关操作================ */
+
+    /**
+     * 获取 值
+     *
+     * @param key
+     * @return
+     */
+    public String get(String key) {
+        return redisTemplate.opsForValue().get(key);
+    }
+
+    /**
+     * 获取多个值
+     *
+     * @param keys
+     * @return
+     */
+    public List<String> multiGet(Collection<String> keys) {
+        if (keys != null && keys.size() > 200) {
+            throw new CacheException("too many keys, max 200");
+        }
+        return redisTemplate.opsForValue().multiGet(keys);
+    }
+
+    /**
+     * set 值
+     *
+     * @param key
+     * @param value
+     */
+    public void set(String key, String value) {
+        redisTemplate.opsForValue().set(key, value);
+    }
+
+    /**
+     * set 值并指定过期时间
+     *
+     * @param key
+     * @param value
+     * @param timeout
+     */
+    public void set(String key, String value, long timeout) {
+        redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
+    }
+
+    /**
+     * 设置String缓存值,只有key不存在时才能设置成功
+     *
+     * @param key
+     * @param value
+     * @return
+     */
+    public Boolean setNx(String key, String value) {
+        return redisTemplate.opsForValue().setIfAbsent(key, value);
+    }
+
+    /**
+     * 设置String缓存值并指定过期时间,只有key不存在时才能设置成功
+     *
+     * @param key
+     * @param value
+     * @param timeout
+     * @return
+     */
+    public Boolean setNx(String key, String value, long timeout) {
+        return redisTemplate.opsForValue().setIfAbsent(key, value, timeout, TimeUnit.SECONDS);
+    }
+
+    /**
+     * getAndSet 值
+     *
+     * @param key
+     * @param value
+     * @return
+     */
+    public String getAndSet(String key, String value) {
+        return redisTemplate.opsForValue().getAndSet(key, value);
+    }
+
+    /**
+     * key的值 +1
+     *
+     * @param key
+     * @return
+     * @version 1.0
+     */
+    public Long incr(String key) {
+        return redisTemplate.opsForValue().increment(key);
+    }
+
+    /**
+     * key的值 +delta
+     *
+     * @param key
+     * @param delta
+     * @return
+     * @version 1.0
+     */
+    public Long incrBy(String key, long delta) {
+        return redisTemplate.opsForValue().increment(key, delta);
+    }
+
+    /**
+     * key的值 -1
+     *
+     * @param key
+     * @return
+     * @version 1.0
+     */
+    public Long decr(String key) {
+        return redisTemplate.opsForValue().decrement(key);
+    }
+
+    /**
+     * key的值 -delta
+     *
+     * @param key
+     * @param delta
+     * @return
+     * @version 1.0
+     */
+    public Long desrBy(String key, long delta) {
+        return redisTemplate.opsForValue().decrement(key, delta);
+    }
+
+    /** ================Hashes相关操作================ */
+
+    /**
+     * 是否存在 hashkey
+     *
+     * @param key
+     * @param hashKey
+     * @return
+     */
+    public boolean hasHashKey(String key, String hashKey) {
+        return redisTemplate.opsForHash().hasKey(key, hashKey);
+    }
+
+    /**
+     * 获取hash某个字段的值
+     *
+     * @param key
+     * @param hashKey
+     * @return
+     */
+    public Object hget(String key, String hashKey) {
+        return redisTemplate.opsForHash().get(key, hashKey);
+    }
+
+    /**
+     * 获取hash所有键值(hashkey超过200个时禁止使用)
+     *
+     * @param key
+     * @return
+     */
+    public Map<Object, Object> hgetAll(String key) {
+        return redisTemplate.opsForHash().entries(key);
+    }
+
+    /**
+     * 获取hash对应多个字段的值
+     *
+     * @param key
+     * @param hashKeys
+     * @return
+     */
+    public List<Object> hmget(String key, List<Object> hashKeys) {
+        return redisTemplate.opsForHash().multiGet(key, hashKeys);
+    }
+
+    /**
+     * @param key
+     * @param map
+     * @return
+     */
+    public boolean hmset(String key, Map<String, Object> map) {
+        try {
+            redisTemplate.opsForHash().putAll(key, map);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 不保证原子性
+     *
+     * @param key
+     * @param map
+     * @param expire
+     * @return
+     */
+    public boolean hmset(String key, Map<String, Object> map, long expire) {
+        try {
+            redisTemplate.opsForHash().putAll(key, map);
+            expire(key, expire);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 设置hash中某个字段的值,如果不存在将创建
+     *
+     * @param key
+     * @param hashKey
+     * @param value
+     */
+    public void hset(String key, String hashKey, String value) {
+        redisTemplate.opsForHash().put(key, hashKey, value);
+    }
+
+    /**
+     * 设置hash中某个字段的值并指定key的过期时间,如果不存在将创建
+     *
+     * @param key
+     * @param hashKey
+     * @param value
+     * @param timeout 单位:秒
+     */
+    public void hset(String key, String hashKey, String value, long timeout) {
+        redisTemplate.opsForHash().put(key, hashKey, value);
+        expire(key, timeout);
+    }
+
+    /**
+     * 设置hash中某个字段的值,只有当这个字段不存在时才能设置成功
+     *
+     * @param key
+     * @param hashKey
+     * @param value
+     * @return
+     */
+    public Boolean hsetNx(String key, String hashKey, String value) {
+        return redisTemplate.opsForHash().putIfAbsent(key, hashKey, value);
+    }
+
+    /**
+     * 删除hash中的多个字段
+     *
+     * @param key
+     * @param hashKey
+     */
+    public void hdel(String key, Object... hashKey) {
+        redisTemplate.opsForHash().delete(key, hashKey);
+    }
+
+    /**
+     * 将hash中的某个字段值增加delta
+     *
+     * @param key
+     * @param hashKey
+     * @param delta
+     * @return
+     */
+    public Long hincrBy(String key, String hashKey, long delta) {
+        return redisTemplate.opsForHash().increment(key, hashKey, delta);
+    }
+
+    /** ================Lists相关操作================ */
+
+    /**
+     * 返回list中指定范围的元素,第一个元素为0,最后一个元素为-1,倒数第二个为-2,以此类推。
+     *
+     * @param key
+     * @param start
+     * @param end
+     * @return
+     */
+    public List<String> lrange(String key, long start, long end) {
+        return redisTemplate.opsForList().range(key, start, end);
+    }
+
+    /**
+     * 获取list的长度
+     *
+     * @param key
+     * @return
+     */
+    public Long llen(String key) {
+        return redisTemplate.opsForList().size(key);
+    }
+
+    /**
+     * 获取list中index索引的元素
+     *
+     * @param key
+     * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
+     * @return
+     */
+    public String lindex(String key, long index) {
+        return redisTemplate.opsForList().index(key, index);
+    }
+
+    /**
+     * 向list的尾部插入指定的元素
+     *
+     * @param key
+     * @param value
+     * @return
+     */
+    public Long rpush(String key, String value) {
+        return redisTemplate.opsForList().rightPush(key, value);
+    }
+
+    /**
+     * 向list的尾部插入多个元素
+     *
+     * @param key
+     * @param values
+     * @return
+     */
+    public Long rpushAll(String key, Collection<String> values) {
+        return redisTemplate.opsForList().rightPushAll(key, values);
+    }
+
+    /**
+     * 移除并返回list尾部的元素
+     *
+     * @param key
+     * @return
+     */
+    public String rpop(String key) {
+        return redisTemplate.opsForList().rightPop(key);
+    }
+
+    /**
+     * 向list的头部插入指定的元素
+     *
+     * @param key
+     * @param value
+     * @return
+     */
+    public Long lpush(String key, String value) {
+        return redisTemplate.opsForList().leftPush(key, value);
+    }
+
+    /**
+     * 向list的头部插入多个元素
+     *
+     * @param key
+     * @param values
+     * @return
+     */
+    public Long lpushAll(String key, Collection<String> values) {
+        return redisTemplate.opsForList().leftPushAll(key, values);
+    }
+
+    /**
+     * 移除并返回list头部的元素
+     *
+     * @param key
+     * @return
+     */
+    public String lpop(String key) {
+        return redisTemplate.opsForList().leftPop(key);
+    }
+
+    /**
+     * 修改list中index索引对应元素的值
+     *
+     * @param key
+     * @param index
+     * @param value
+     */
+    public void lset(String key, long index, String value) {
+        redisTemplate.opsForList().set(key, index, value);
+    }
+
+    /**
+     * 从list中移除前count个出现值为value的元素
+     *
+     * @param key
+     * @param count count > 0: 从头往尾移除值为 value 的元素。</br>
+     *              count < 0: 从尾往头移除值为 value 的元素。</br>
+     *              count = 0: 移除所有值为 value 的元素。
+     * @param value
+     * @return
+     */
+    public Long lrem(String key, long count, String value) {
+        return redisTemplate.opsForList().remove(key, count, value);
+    }
+
+    /**
+     * lpop多个数据
+     *
+     * @param key
+     * @param count
+     * @return
+     */
+    public List<Object> lpopMutil(String key, long count) {
+        List<Object> result = redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
+            connection.lRange(key.getBytes(), 0, count - 1);
+            connection.lTrim(key.getBytes(), count, -1);
+            return null;
+        });
+        return result;
+    }
+
+    /**
+     * rpop多个数据
+     *
+     * @param key
+     * @param start 起始位置  end=-1
+     * @return
+     */
+    public List<Object> rpopMutil(String key, long start) {
+        List<Object> result = redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
+            connection.lRange(key.getBytes(), start, -1);
+            connection.lTrim(key.getBytes(), 0, start - 1);
+            return null;
+        });
+        return result;
+    }
+
+    /**
+     * 从右取多个值
+     *
+     * @param key
+     * @param count 个数
+     * @return
+     */
+    public List<Object> rpopMutil(String key, int count) {
+        List<Object> result = redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
+            connection.lRange(key.getBytes(), 0 - count, -1);
+            connection.lTrim(key.getBytes(), 0, -1 - count);
+            return null;
+        });
+        return result;
+    }
+
+    /** ================Sets相关操作================ */
+
+    /**
+     * set集合中是否存在值为value的元素
+     *
+     * @param key
+     * @param value
+     * @return
+     */
+    public boolean sIsMember(String key, String value) {
+        return redisTemplate.opsForSet().isMember(key, value);
+    }
+
+    /**
+     * 返回set集合所有元素,禁止直接使用members,通过scan获取
+     *
+     * @param key
+     * @return
+     */
+    public Set<String> sscan(String key) {
+        Set<String> result = new HashSet<>();
+        Cursor<String> cursor = redisTemplate.opsForSet().scan(key, ScanOptions.NONE);
+        while (cursor != null && cursor.hasNext()) {
+            result.add(cursor.next());
+        }
+        return result;
+    }
+
+    /**
+     * 向set集合添加多个元素
+     *
+     * @param key
+     * @param values
+     * @return
+     */
+    public Long sadd(String key, String... values) {
+        return redisTemplate.opsForSet().add(key, values);
+    }
+
+    /**
+     * 返回set集合的元素个数
+     *
+     * @param key
+     * @return
+     */
+    public Long scard(String key) {
+        return redisTemplate.opsForSet().size(key);
+    }
+
+    /**
+     * 从set中移除值为value的元素
+     *
+     * @param key
+     * @param values
+     * @return
+     */
+    public Long srem(String key, Object... values) {
+        return redisTemplate.opsForSet().remove(key, values);
+    }
+
+    /** ================Sorted Sets相关操作================ */
+
+    /**
+     * 向zset集合添加一个元素,或者更新已存在元素的分数
+     *
+     * @param key
+     * @param vaule
+     * @param score
+     */
+    public void zadd(String key, String vaule, double score) {
+        redisTemplate.opsForZSet().add(key, vaule, score);
+    }
+
+    /**
+     * 向zset集合添加一个元素,或者更新已存在元素的分数,并更新key的过期时间
+     *
+     * @param key
+     * @param vaule
+     * @param score
+     */
+    public void zadd(String key, String vaule, double score, long timeout) {
+        redisTemplate.opsForZSet().add(key, vaule, score);
+        expire(key, timeout);
+    }
+
+    /**
+     * 获取某个元素的分数
+     *
+     * @param key
+     * @param value
+     * @return
+     */
+    public Double zscore(String key, String value) {
+        return redisTemplate.opsForZSet().score(key, value);
+    }
+
+    /**
+     * 获取zset集合的元素数量
+     *
+     * @param key
+     * @return
+     */
+    public Long zcard(String key) {
+        return redisTemplate.opsForZSet().zCard(key);
+    }
+
+    /**
+     * zset 范围内元素个数
+     *
+     * @param key
+     * @param min
+     * @param max
+     * @return
+     */
+    public Long zcount(String key, double min, double max) {
+        return redisTemplate.opsForZSet().count(key, min, max);
+    }
+
+    /**
+     * 通过索引区间返回zset集合成指定区间内的元素,分数从小到大排序
+     *
+     * @param key
+     * @param start
+     * @param end
+     * @return
+     */
+    public Set<String> zrange(String key, long start, long end) {
+        return redisTemplate.opsForZSet().range(key, start, end);
+    }
+
+    /**
+     * 通过索引区间返回zset集合成指定区间内的元素,分数从大到小排序
+     *
+     * @param key
+     * @param start
+     * @param end
+     * @return
+     */
+    public Set<String> zreverseRange(String key, long start, long end) {
+        return redisTemplate.opsForZSet().reverseRange(key, start, end);
+    }
+
+    /**
+     * 通过索引区间返回zset集合成指定区间内的元素及分数
+     *
+     * @param key
+     * @param start
+     * @param end
+     * @return
+     */
+    public List<Map<String, Double>> zrangeWithScores(String key, long start, long end) {
+        List<Map<String, Double>> result = new ArrayList<>();
+        Set<TypedTuple<String>> cursor = redisTemplate.opsForZSet().rangeWithScores(key, start, end);
+        for (Iterator<TypedTuple<String>> it = cursor.iterator(); it.hasNext(); ) {
+            TypedTuple<String> item = it.next();
+            Map<String, Double> map = new HashMap<>();
+            map.put(item.getValue(), item.getScore());
+            result.add(map);
+        }
+        return result;
+    }
+
+    /**
+     * 通过索引区间返回zset集合成指定区间内的元素及分数
+     *
+     * @param key
+     * @param start
+     * @param end
+     * @return
+     */
+    public List<Map<String, Object>> zrangeWithScoresToList(String key, long start, long end) {
+        List<Map<String, Object>> result = new ArrayList<>();
+        Set<TypedTuple<String>> cursor = redisTemplate.opsForZSet().rangeWithScores(key, start, end);
+        for (Iterator<TypedTuple<String>> it = cursor.iterator(); it.hasNext(); ) {
+            TypedTuple<String> item = it.next();
+            Map<String, Object> map = new HashMap<>();
+            map.put("value", item.getValue());
+            map.put("score", item.getScore());
+            result.add(map);
+        }
+        return result;
+    }
+
+    /**
+     * 通过索引区间返回zset集合成指定区间内的元素及分数
+     *
+     * @param key
+     * @param start
+     * @param end
+     * @return map
+     */
+    public Map<String, Double> zrangeWithScoresToMap(String key, long start, long end) {
+        Map<String, Double> result = new HashMap<>();
+        Set<TypedTuple<String>> cursor = redisTemplate.opsForZSet().rangeWithScores(key, start, end);
+        for (Iterator<TypedTuple<String>> it = cursor.iterator(); it.hasNext(); ) {
+            TypedTuple<String> item = it.next();
+            result.put(item.getValue(), item.getScore());
+        }
+        return result;
+    }
+
+    /**
+     * 迭代zset集合中的元素(只包括元素成员)
+     *
+     * @param key
+     * @return
+     */
+    public Set<String> zscanValues(String key) {
+        Set<String> result = new HashSet<>();
+        Cursor<TypedTuple<String>> cursor = redisTemplate.opsForZSet().scan(key, ScanOptions.NONE);
+        while (cursor.hasNext()) {
+            TypedTuple<String> item = cursor.next();
+            result.add(item.getValue());
+        }
+        return result;
+    }
+
+    /**
+     * 迭代zset集合中的元素(包括元素和分数)
+     *
+     * @param key
+     * @return
+     */
+    public List<Map<String, Double>> zscan(String key) {
+        List<Map<String, Double>> result = new ArrayList<Map<String, Double>>();
+        Cursor<TypedTuple<String>> cursor = redisTemplate.opsForZSet().scan(key, ScanOptions.NONE);
+        while (cursor.hasNext()) {
+            TypedTuple<String> item = cursor.next();
+            Map<String, Double> map = new HashMap<String, Double>();
+            map.put(item.getValue(), item.getScore());
+            result.add(map);
+        }
+        return result;
+
+    }
+
+    /**
+     * 移除zset集合中的元素
+     *
+     * @param key
+     * @param values
+     */
+    public void zRemove(String key, Object... values) {
+        redisTemplate.opsForZSet().remove(key, values);
+    }
+
+    /**
+     * 移除zset集合指定范围数据
+     *
+     * @param key
+     * @param start
+     * @param end
+     */
+    public void zRemRange(String key, long start, long end) {
+        redisTemplate.opsForZSet().removeRange(key, start, end);
+    }
+
+
+    /**
+     * 删除
+     *
+     * @param key
+     * @param min
+     * @param max
+     */
+    public void zRemRangeByScore(String key, double min, double max) {
+        redisTemplate.opsForZSet().removeRangeByScore(key, min, max);
+    }
+
+    /**
+     * 加锁 不可重入
+     *
+     * @param key
+     * @param timeout 毫秒
+     * @return
+     */
+    public Boolean lock(String key, long timeout) {
+        return redisTemplate.opsForValue().setIfAbsent(LOCK_PREFIX + key, "1", timeout, TimeUnit.MILLISECONDS);
+    }
+
+    /**
+     * 解锁 不可重入
+     *
+     * @param key
+     * @return
+     */
+    public Boolean unlock(String key) {
+        return redisTemplate.delete(LOCK_PREFIX + key);
+    }
+
+    /**
+     * 加锁
+     *
+     * @param key         key
+     * @param reentrantId 重入Id
+     * @param timeout     超时时间 毫秒ms
+     * @return
+     */
+    public Boolean lock(String key, String reentrantId, long timeout) {
+        return lock(key, reentrantId, timeout, DEFAULT_RETRIES);
+    }
+
+    /**
+     * 加锁
+     *
+     * @param key         key
+     * @param reentrantId 重入Id
+     * @param timeout     超时时间 毫秒ms
+     * @param retries     重试次数
+     * @return
+     */
+    public Boolean lock(String key, String reentrantId, long timeout, int retries) {
+        return lock(key, reentrantId, timeout, retries, DEFAULT_INTERVAL);
+    }
+
+    /**
+     * 加锁
+     *
+     * @param key         key
+     * @param reentrantId 重入Id
+     * @param timeout     超时时间 毫秒ms
+     * @param retries     重试次数
+     * @param interval    每次重试间隔时间 毫秒
+     * @return
+     */
+    public Boolean lock(String key, String reentrantId, long timeout, int retries, long interval) {
+        String lockKey = LOCK_PREFIX + key;
+        DefaultRedisScript<Long> script = new DefaultRedisScript<>();
+        script.setResultType(Long.class);
+        script.setScriptSource(new StaticScriptSource(LOCK));
+        for (int i = 0; i < retries; i++) {
+            Object result = redisTemplate.execute(script, Arrays.asList(lockKey), String.valueOf(timeout), reentrantId);
+            if (Objects.nonNull(result) && Objects.equals(1L, Long.valueOf(result.toString()))) {
+                return true;
+            }
+            try {
+                TimeUnit.MILLISECONDS.sleep(interval);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+                return null;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 解锁
+     *
+     * @param key         key
+     * @param reentrantId 重入ID
+     */
+    public Boolean unlock(String key, String reentrantId) {
+        String lockKey = LOCK_PREFIX + key;
+        DefaultRedisScript<Long> script = new DefaultRedisScript<>();
+        script.setResultType(Long.class);
+        script.setScriptSource(new StaticScriptSource(UNLOCK));
+        Object result = redisTemplate.execute(script, Arrays.asList(lockKey), reentrantId);
+        if (Objects.isNull(result)) {
+            return null;
+        }
+        if (Objects.equals(1L, Long.valueOf(result.toString()))) {
+            return true;
+        }
+        return false;
+    }
+
+}

+ 11 - 1
content-understanding-server/src/main/resources/application-dev.yml

@@ -23,7 +23,17 @@ spring:
       minimum-idle: 10
       maximum-pool-size: 20
       connection-test-query: SELECT 1
-
+  redis:
+    hostName: r-bp1ps6my7lzg8rdhwx682.redis.rds.aliyuncs.com
+    port: 6379
+    password: Wqsd@2019
+    timeout: 1000
+    lettuce:
+      pool:
+        max-active: 8
+        max-wait: -1
+        max-idle: 8
+        min-idle: 0
   data:
     mongodb:
       uri: mongodb://content_user:Content123&@s-bp15728065fa22a4.mongodb.rds.aliyuncs.com:3717,s-bp12258b064c3ef4.mongodb.rds.aliyuncs.com:3717/content_test

+ 12 - 0
content-understanding-server/src/main/resources/application-pre.yml

@@ -23,6 +23,18 @@ spring:
       minimum-idle: 10
       maximum-pool-size: 20
       connection-test-query: SELECT 1
+
+  redis:
+    hostName: r-bp1oyhyx4mxgs6klyt561.redis.rds.aliyuncs.com
+    port: 6379
+    password: Wqsd@2019
+    timeout: 1000
+    lettuce:
+      pool:
+        max-active: 8
+        max-wait: -1
+        max-idle: 8
+        min-idle: 0
   data:
     mongodb:
       uri: mongodb://content_user:Content123&@s-bp15728065fa22a4.mongodb.rds.aliyuncs.com:3717,s-bp12258b064c3ef4.mongodb.rds.aliyuncs.com:3717/content

+ 12 - 0
content-understanding-server/src/main/resources/application-prod.yml

@@ -23,6 +23,18 @@ spring:
       minimum-idle: 10
       maximum-pool-size: 20
       connection-test-query: SELECT 1
+
+  redis:
+    hostName: r-bp1oyhyx4mxgs6klyt561.redis.rds.aliyuncs.com
+    port: 6379
+    password: Wqsd@2019
+    timeout: 1000
+    lettuce:
+      pool:
+        max-active: 8
+        max-wait: -1
+        max-idle: 8
+        min-idle: 0
   data:
     mongodb:
       uri: mongodb://content_user:Content123&@s-bp15728065fa22a4.mongodb.rds.aliyuncs.com:3717,s-bp12258b064c3ef4.mongodb.rds.aliyuncs.com:3717/content

+ 13 - 0
content-understanding-server/src/main/resources/application-stress.yml

@@ -23,6 +23,19 @@ spring:
       minimum-idle: 10
       maximum-pool-size: 20
       connection-test-query: SELECT 1
+
+  redis:
+    hostName: r-bp1ps6my7lzg8rdhwx682.redis.rds.aliyuncs.com
+    port: 6379
+    password: Wqsd@2019
+    timeout: 1000
+    lettuce:
+      pool:
+        max-active: 8
+        max-wait: -1
+        max-idle: 8
+        min-idle: 0
+
   data:
     mongodb:
       uri: mongodb://content_user:Content123&@s-bp15728065fa22a4.mongodb.rds.aliyuncs.com:3717,s-bp12258b064c3ef4.mongodb.rds.aliyuncs.com:3717/content_test

+ 12 - 0
content-understanding-server/src/main/resources/application-test.yml

@@ -23,6 +23,18 @@ spring:
       minimum-idle: 10
       maximum-pool-size: 20
       connection-test-query: SELECT 1
+  redis:
+    hostName: r-bp1ps6my7lzg8rdhwx682.redis.rds.aliyuncs.com
+    port: 6379
+    password: Wqsd@2019
+    timeout: 1000
+    lettuce:
+      pool:
+        max-active: 8
+        max-wait: -1
+        max-idle: 8
+        min-idle: 0
+
   data:
     mongodb:
       uri: mongodb://content_user:Content123&@s-bp15728065fa22a4.mongodb.rds.aliyuncs.com:3717,s-bp12258b064c3ef4.mongodb.rds.aliyuncs.com:3717/content_test