|
|
@@ -0,0 +1,242 @@
|
|
|
+package org.dromara.business.utils;
|
|
|
+
|
|
|
+import com.fasterxml.jackson.core.JsonProcessingException;
|
|
|
+import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.data.redis.core.StringRedisTemplate;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
+
|
|
|
+@Component
|
|
|
+public class RedisUtil {
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private StringRedisTemplate redisTemplate;
|
|
|
+
|
|
|
+ private final ObjectMapper objectMapper = new ObjectMapper();
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置缓存值 + 过期时间(分钟)
|
|
|
+ */
|
|
|
+ public boolean set(String key, Object value, long time) {
|
|
|
+ try {
|
|
|
+ if (time > 0) {
|
|
|
+ if (value instanceof String) {
|
|
|
+ redisTemplate.opsForValue().set(key, (String) value, time, TimeUnit.SECONDS);
|
|
|
+ } else {
|
|
|
+ String jsonValue = objectMapper.writeValueAsString(value);
|
|
|
+ redisTemplate.opsForValue().set(key, jsonValue, time, TimeUnit.SECONDS);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ set(key, value);
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ } catch (JsonProcessingException e) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置缓存值(无过期时间)
|
|
|
+ */
|
|
|
+ public boolean set(String key, Object value) {
|
|
|
+ try {
|
|
|
+ String jsonValue = objectMapper.writeValueAsString(value);
|
|
|
+ redisTemplate.opsForValue().set(key, jsonValue);
|
|
|
+ return true;
|
|
|
+ } catch (JsonProcessingException e) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取缓存值(字符串或对象)
|
|
|
+ */
|
|
|
+ public <T> T get(String key, Class<T> clazz) {
|
|
|
+ String value = redisTemplate.opsForValue().get(key);
|
|
|
+ if (value == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ return objectMapper.readValue(value, clazz);
|
|
|
+ } catch (JsonProcessingException e) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取缓存值(字符串)
|
|
|
+ */
|
|
|
+ public String get(String key) {
|
|
|
+ String value = redisTemplate.opsForValue().get(key);
|
|
|
+ if (value != null && value.startsWith("\"") && value.endsWith("\"")) {
|
|
|
+ return value.substring(1, value.length() - 1);
|
|
|
+ }
|
|
|
+ return value;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 删除单个 key
|
|
|
+ */
|
|
|
+ public void delete(String key) {
|
|
|
+ redisTemplate.delete(key);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断 key 是否存在
|
|
|
+ */
|
|
|
+ public boolean hasKey(String key) {
|
|
|
+ return Boolean.TRUE.equals(redisTemplate.hasKey(key));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 指定缓存失效时间
|
|
|
+ */
|
|
|
+ public boolean expire(String key, long time) {
|
|
|
+ return redisTemplate.expire(key, time, TimeUnit.SECONDS);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 增加数值(原子操作)
|
|
|
+ *
|
|
|
+ * @param key 键
|
|
|
+ * @param delta 增加的数量
|
|
|
+ * @return 增加后的值(Long),失败返回 null
|
|
|
+ */
|
|
|
+ public Long increment(String key, long delta) {
|
|
|
+ return redisTemplate.opsForValue().increment(key, delta);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 减少数值(原子操作)
|
|
|
+ *
|
|
|
+ * @param key 键
|
|
|
+ * @param delta 减少的数量
|
|
|
+ * @return 减少后的值(Long),失败返回 null
|
|
|
+ */
|
|
|
+ public Long decrement(String key, long delta) {
|
|
|
+ return redisTemplate.opsForValue().decrement(key, delta);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 下面是针对不同类型的操作
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Hash 类型操作 - 存储对象
|
|
|
+ */
|
|
|
+ public <HK, HV> void hSetAll(String key, Map<HK, HV> map) throws JsonProcessingException {
|
|
|
+ for (Map.Entry<HK, HV> entry : map.entrySet()) {
|
|
|
+ redisTemplate.opsForHash().put(key, objectMapper.writeValueAsString(entry.getKey()), objectMapper.writeValueAsString(entry.getValue()));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Hash 类型操作 - 获取所有键值对
|
|
|
+ */
|
|
|
+ public <HK, HV> Map<HK, HV> hGetAll(String key, Class<HK> hkClass, Class<HV> hvClass) throws JsonProcessingException {
|
|
|
+ Map<Object, Object> entries = redisTemplate.opsForHash().entries(key);
|
|
|
+ Map<HK, HV> resultMap = new HashMap<>();
|
|
|
+ for (Map.Entry<Object, Object> entry : entries.entrySet()) {
|
|
|
+ HK hk = objectMapper.readValue((String) entry.getKey(), hkClass);
|
|
|
+ HV hv = objectMapper.readValue((String) entry.getValue(), hvClass);
|
|
|
+ resultMap.put(hk, hv);
|
|
|
+ }
|
|
|
+ return resultMap;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * List 类型操作 - 在右边添加元素
|
|
|
+ */
|
|
|
+ public void rPush(String key, Object... values) throws JsonProcessingException {
|
|
|
+ for (Object value : values) {
|
|
|
+ redisTemplate.opsForList().rightPush(key, objectMapper.writeValueAsString(value));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * List 类型操作 - 获取所有元素
|
|
|
+ */
|
|
|
+ public <T> List<T> lRange(String key, Class<T> clazz) throws JsonProcessingException {
|
|
|
+ List<String> range = redisTemplate.opsForList().range(key, 0, -1);
|
|
|
+ return range.stream()
|
|
|
+ .map(s -> {
|
|
|
+ try {
|
|
|
+ return objectMapper.readValue(s, clazz);
|
|
|
+ } catch (JsonProcessingException e) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .toList();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 安全获取 Long 类型的值,如果为空或不是 Long 类型则返回默认值
|
|
|
+ */
|
|
|
+ public Long getLongValue(String key, Long defaultValue) {
|
|
|
+ String value = get(key);
|
|
|
+ if (value != null && !value.isEmpty()) {
|
|
|
+ try {
|
|
|
+ return Long.parseLong(value);
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ return defaultValue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return defaultValue;
|
|
|
+ }
|
|
|
+
|
|
|
+ public Long getLongValue(String key) {
|
|
|
+ String value = get(key);
|
|
|
+ if (value != null && !value.isEmpty()) {
|
|
|
+ try {
|
|
|
+ return Long.parseLong(value);
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ public <T> T getObject(String key, Class<T> clazz) {
|
|
|
+ String json = redisTemplate.opsForValue().get(key);
|
|
|
+ if (json == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ return objectMapper.readValue(json, clazz);
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new RuntimeException("Redis 反序列化失败: " + key, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ public void setObject(String key, Object value, long expireSeconds) {
|
|
|
+ try {
|
|
|
+ String json = objectMapper.writeValueAsString(value);
|
|
|
+ redisTemplate.opsForValue().set(key, json, expireSeconds, TimeUnit.SECONDS);
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new RuntimeException("Redis 序列化失败", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 发布消息到指定频道(支持对象自动序列化为 JSON)
|
|
|
+ *
|
|
|
+ * @param channel 频道名
|
|
|
+ * @param message 消息内容(支持 POJO)
|
|
|
+ * @return 发送的消息数量(成功一般 >= 1)
|
|
|
+ */
|
|
|
+ public Long publish(String channel, Object message) {
|
|
|
+ try {
|
|
|
+ String msg = (message instanceof String) ? (String) message : objectMapper.writeValueAsString(message);
|
|
|
+ return redisTemplate.convertAndSend(channel, msg);
|
|
|
+ } catch (JsonProcessingException e) {
|
|
|
+ throw new RuntimeException("消息序列化失败: " + channel, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+}
|