package com.imyeyu.spring.util;
import com.imyeyu.java.TimiJava;
import com.imyeyu.java.bean.timi.TimiException;
import com.imyeyu.spring.config.AbstractRedisConfig;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.serializer.RedisSerializer;
import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
/**
* RedisTemplate 功能封装,简化 Redis 操作
*
serializer 为该 RedisTemplate 的键的序列化操作,序列化解析器由 {@link AbstractRedisConfig} 提供
*
* @param 键类型
* @param 值类型
* @author 夜雨
* @version 2021-11-21 09:58
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Redis {
private RedisTemplate redis;
private RedisSerializer serializer;
/**
* 加锁
*
* @param key 键
* @param value 值
* @param timeoutMS 超时时间毫秒
* @return true 为加锁成功
*/
public boolean lock(K key, V value, long timeoutMS) {
Boolean lock = redis.opsForValue().setIfAbsent(key, value, timeoutMS, TimeUnit.MILLISECONDS);
return lock != null && lock;
}
/**
* 释放锁
*
* @param key 键
*/
public void releaseLock(K key) {
destroy(key);
}
/**
* 设置存活时间
*
* @param key 键
* @param ms 毫秒 TTL
*/
public void setExpire(K key, long ms) {
redis.expire(key, Duration.ofMillis(ms));
}
/**
* 获取该数据 TTL
*
* @param key 键
* @return 毫秒 TTL
*/
public long getExpire(K key) {
return Objects.requireNonNullElse(redis.getExpire(key, TimeUnit.MILLISECONDS), -1L);
}
/**
* 设置数据并保持 TTL
*
* @param key 键
* @param value 值
*/
public void setAndKeepTTL(K key, V value) {
long expire = redis.getExpire(key, TimeUnit.MILLISECONDS);
if (expire <= 0) {
// 判死
destroy(key);
} else {
redis.opsForValue().set(key, value, Duration.ofMillis(expire));
}
}
/**
* 设置数据
*
* @param key 键
* @param value 值
* @param ms 毫秒 TTL
*/
public void set(K key, V value, long ms) {
redis.opsForValue().set(key, value, Duration.ofMillis(ms));
}
/**
* 获取值
*
* @param key 键
* @return 值
*/
public V get(K key) {
return redis.opsForValue().get(key);
}
/**
* 获取值,强转为 String
*
* @param key 键
* @return 值
*/
public String getString(K key) {
return get(key).toString();
}
/**
* 获取值,强转为 Boolean
*
* @param key 键
* @return 值
*/
public Boolean is(K key) {
return Boolean.parseBoolean(getString(key));
}
/**
* 获取值,强转为 Boolean 并取反
*
* @param key 键
* @return 值
*/
public Boolean not(K key) {
return !is(key);
}
/**
* 是否存在
*
* @param key 键
* @return true 为存在
*/
public boolean has(K key) {
return get(key) != null;
}
/**
* 对列表添加值
*
* @param key 键
* @param value 值
*/
public void add(K key, V value) {
redis.opsForList().leftPush(key, value);
}
/**
* 对列表批量添加值
*
* @param key 键
* @param values 值
*/
@SafeVarargs
public final void addAll(K key, V... values) {
redis.opsForList().leftPushAll(key, values);
}
/**
* 获取为列表
*
* @param key 键
* @return 列表
*/
public List getList(K key) {
return redis.opsForList().range(key, 0, -1);
}
/**
* 获取所有数据列表
*
* @return 所有数据列表
*/
public Map> getAllList() {
Map> r = new HashMap<>();
List ks = keys("*");
for (K k : ks) {
r.put(k, getList(k));
}
return r;
}
/**
* 值为列表时查找是否存在某值
*
* @param key 键
* @param value 值
* @return true 为存在
*/
public boolean contains(K key, V value) {
return getList(key).contains(value);
}
/**
* 获取所有值
*
* @return 所有值
* @throws TimiException 异常
*/
public List values() {
List r = new ArrayList<>();
List ks = keys("*");
for (K k : ks) {
r.add(get(k));
}
return r;
}
/**
* 获取所有数据(包括键)
*
* @return 所有数据(包括键)
*/
public Map map() {
Map r = new HashMap<>();
List ks = keys("*");
for (K k : ks) {
r.put(k, get(k));
}
return r;
}
/**
* 获取符合条件的 key
*
* @param pattern 表达式
* @return keys
*/
public List keys(String pattern) {
List keys = new ArrayList<>();
scan(pattern, item -> {
if (item != null) {
keys.add(serializer.deserialize(item));
}
});
return keys;
}
/**
* 销毁对象
*
* @param key 键
* @return true 为成功
*/
public boolean destroy(K key) {
if (TimiJava.isNotEmpty(key) && has(key)) {
return redis.delete(key);
}
return false;
}
/** 删库 */
public void flushAll() {
Objects.requireNonNull(redis.getConnectionFactory()).getConnection().serverCommands().flushAll();
}
/**
* scan 实现
*
* @param pattern 表达式
* @param consumer 对迭代到的 key 进行操作
*/
private void scan(String pattern, Consumer consumer) {
redis.execute((RedisConnection connection) -> {
try (Cursor cursor = connection.keyCommands().scan(ScanOptions.scanOptions().count(Long.MAX_VALUE).match(pattern).build())) {
cursor.forEachRemaining(consumer);
return null;
}
});
}
}