feat(REQ-1465): 消息模板分页查询接口实现

背景:
  https://jira.axzo.cn/browse/REQ-1465?goToView=1

修改:
  1、消息模板分页查询接口实现

影响:
  无
This commit is contained in:
luofu 2023-10-17 12:35:01 +08:00
parent 23014760bf
commit 5a8fe60c20
5 changed files with 106 additions and 55 deletions

View File

@ -47,8 +47,7 @@ public class MessageTemplateController implements MessageTemplateClient {
@Override @Override
public CommonResponse<Page<MessageTemplatePageResponse>> page(MessageTemplatePageRequest request) { public CommonResponse<Page<MessageTemplatePageResponse>> page(MessageTemplatePageRequest request) {
// TODO:[cold_blade] [P0] page query template return CommonResponse.success(messageTemplateNewService.page(request));
return null;
} }
@Override @Override

View File

@ -2,7 +2,7 @@ package cn.axzo.msg.center.message.service.impl;
import cn.axzo.basics.common.util.TreeUtil; import cn.axzo.basics.common.util.TreeUtil;
import cn.axzo.msg.center.common.enums.TableIsDeleteEnum; import cn.axzo.msg.center.common.enums.TableIsDeleteEnum;
import cn.axzo.msg.center.common.utils.RedisUtil; import cn.axzo.msg.center.common.redis.RedisUtil;
import cn.axzo.msg.center.dal.MessageGroupNodeDao; import cn.axzo.msg.center.dal.MessageGroupNodeDao;
import cn.axzo.msg.center.domain.entity.MessageGroupNode; import cn.axzo.msg.center.domain.entity.MessageGroupNode;
import cn.axzo.msg.center.message.domain.dto.GroupTreeNodePathDTO; import cn.axzo.msg.center.message.domain.dto.GroupTreeNodePathDTO;
@ -37,6 +37,7 @@ public class MessageGroupTreeNodeCacheServiceImpl implements MessageGroupTreeNod
private static final String CACHE_VALUE = "msg-center:message:group:cache-val"; private static final String CACHE_VALUE = "msg-center:message:group:cache-val";
private static final long CACHE_KEY_TIMEOUT_DAYS = 1; private static final long CACHE_KEY_TIMEOUT_DAYS = 1;
private final RedisUtil redisUtil;
private final MessageGroupNodeDao messageGroupNodeDao; private final MessageGroupNodeDao messageGroupNodeDao;
private List<GroupTreeNodeDTO> allGroupTreeRootNodesCache = Collections.emptyList(); private List<GroupTreeNodeDTO> allGroupTreeRootNodesCache = Collections.emptyList();
@ -84,7 +85,7 @@ public class MessageGroupTreeNodeCacheServiceImpl implements MessageGroupTreeNod
@Override @Override
public void refreshCache() { public void refreshCache() {
// 清除redis中的缓存标识 // 清除redis中的缓存标识
RedisUtil.KeyOps.delete(CACHE_KEY); redisUtil.getKeyOps().delete(CACHE_KEY);
// 清除本地缓存 // 清除本地缓存
allGroupTreeRootNodesCache = Collections.emptyList(); allGroupTreeRootNodesCache = Collections.emptyList();
leafTreeNodePathsCache = Collections.emptyList(); leafTreeNodePathsCache = Collections.emptyList();
@ -107,7 +108,7 @@ public class MessageGroupTreeNodeCacheServiceImpl implements MessageGroupTreeNod
} }
private List<GroupTreeNodeDTO> getAllGroupTreeRootNodesCache() { private List<GroupTreeNodeDTO> getAllGroupTreeRootNodesCache() {
if (RedisUtil.KeyOps.hasKey(CACHE_KEY)) { if (redisUtil.getKeyOps().hasKey(CACHE_KEY)) {
// 其它结点进行了本地缓存但是当前服务进程还未进行缓存 // 其它结点进行了本地缓存但是当前服务进程还未进行缓存
if (CollectionUtils.isEmpty(allGroupTreeRootNodesCache)) { if (CollectionUtils.isEmpty(allGroupTreeRootNodesCache)) {
// 本地缓存初始化不更新redis中的缓存标识 // 本地缓存初始化不更新redis中的缓存标识
@ -121,7 +122,7 @@ public class MessageGroupTreeNodeCacheServiceImpl implements MessageGroupTreeNod
} }
private List<GroupTreeNodePathDTO> getLeafTreeNodePathsCache() { private List<GroupTreeNodePathDTO> getLeafTreeNodePathsCache() {
if (RedisUtil.KeyOps.hasKey(CACHE_KEY)) { if (redisUtil.getKeyOps().hasKey(CACHE_KEY)) {
// 其它结点进行了本地缓存但是当前服务进程还未进行缓存 // 其它结点进行了本地缓存但是当前服务进程还未进行缓存
if (leafTreeNodePathsCache.isEmpty()) { if (leafTreeNodePathsCache.isEmpty()) {
// 本地缓存初始化不更新redis中的缓存标识 // 本地缓存初始化不更新redis中的缓存标识
@ -141,7 +142,7 @@ public class MessageGroupTreeNodeCacheServiceImpl implements MessageGroupTreeNod
.flatMap(e -> parseRootNode(e).stream()) .flatMap(e -> parseRootNode(e).stream())
.collect(Collectors.toList()); .collect(Collectors.toList());
if (refreshCache) { if (refreshCache) {
RedisUtil.StringOps.setIfAbsent(CACHE_KEY, CACHE_VALUE, CACHE_KEY_TIMEOUT_DAYS, TimeUnit.DAYS); redisUtil.getStringOps().setIfAbsent(CACHE_KEY, CACHE_VALUE, CACHE_KEY_TIMEOUT_DAYS, TimeUnit.DAYS);
} }
} }

View File

@ -2,8 +2,8 @@ package cn.axzo.msg.center.message.service.impl;
import cn.axzo.basics.common.util.AssertUtil; import cn.axzo.basics.common.util.AssertUtil;
import cn.axzo.msg.center.common.enums.ServiceErrorCodeEnum; import cn.axzo.msg.center.common.enums.ServiceErrorCodeEnum;
import cn.axzo.msg.center.common.redis.RedisUtil;
import cn.axzo.msg.center.common.utils.PageHelperUtil; import cn.axzo.msg.center.common.utils.PageHelperUtil;
import cn.axzo.msg.center.common.utils.RedisUtil;
import cn.axzo.msg.center.dal.MessageBaseTemplateDao; import cn.axzo.msg.center.dal.MessageBaseTemplateDao;
import cn.axzo.msg.center.domain.entity.MessageBaseTemplate; import cn.axzo.msg.center.domain.entity.MessageBaseTemplate;
import cn.axzo.msg.center.message.domain.dto.MessageTemplateDTO; import cn.axzo.msg.center.message.domain.dto.MessageTemplateDTO;
@ -51,6 +51,7 @@ public class MessageTemplateNewServiceImpl implements MessageTemplateNewService
private static final long TRY_LOCK_TIMEOUT_MILLS = 1000; private static final long TRY_LOCK_TIMEOUT_MILLS = 1000;
private static final int RETRY_CNT_MAX = 3; private static final int RETRY_CNT_MAX = 3;
private final RedisUtil redisUtil;
private final MessageBaseTemplateDao messageBaseTemplateDao; private final MessageBaseTemplateDao messageBaseTemplateDao;
private final MessageGroupNodeService messageGroupNodeService; private final MessageGroupNodeService messageGroupNodeService;
private final MessageTemplateGroupService messageTemplateGroupService; private final MessageTemplateGroupService messageTemplateGroupService;
@ -167,7 +168,7 @@ public class MessageTemplateNewServiceImpl implements MessageTemplateNewService
String requestId = UUIDUtil.uuidRawString(); String requestId = UUIDUtil.uuidRawString();
try { try {
String opKey = String.format(SAVE_TEMPLATE_MUTEX_KEY, templateCode); String opKey = String.format(SAVE_TEMPLATE_MUTEX_KEY, templateCode);
boolean lockResult = RedisUtil.LockOps.getLockUntilTimeout(opKey, requestId, TRY_LOCK_TIMEOUT_MILLS); boolean lockResult = redisUtil.getLockOps().getLockUntilTimeout(opKey, requestId, TRY_LOCK_TIMEOUT_MILLS);
AssertUtil.isTrue(lockResult, ServiceErrorCodeEnum.SYSTEM_BUSY.getDesc()); AssertUtil.isTrue(lockResult, ServiceErrorCodeEnum.SYSTEM_BUSY.getDesc());
if (isTemplateExist(templateCode)) { if (isTemplateExist(templateCode)) {
return false; return false;
@ -176,7 +177,7 @@ public class MessageTemplateNewServiceImpl implements MessageTemplateNewService
messageBaseTemplateDao.save(template); messageBaseTemplateDao.save(template);
return true; return true;
} finally { } finally {
RedisUtil.LockOps.releaseLock(SAVE_TEMPLATE_MUTEX_KEY, requestId); redisUtil.getLockOps().releaseLock(SAVE_TEMPLATE_MUTEX_KEY, requestId);
} }
} }

View File

@ -1,15 +1,16 @@
package cn.axzo.msg.center.common.utils; package cn.axzo.msg.center.common.redis;
import cn.hutool.extra.spring.SpringUtil;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisStringCommands; import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.connection.ReturnType; import org.springframework.data.redis.connection.ReturnType;
import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.types.Expiration; import org.springframework.data.redis.core.types.Expiration;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.time.Instant; import java.time.Instant;
@ -22,23 +23,25 @@ import java.util.concurrent.TimeUnit;
* @version 1.0 * @version 1.0
*/ */
@Slf4j @Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE) @Getter
public final class RedisUtil implements InitializingBean { @Component
@RequiredArgsConstructor
public class RedisUtil {
/** /**
* 使用StringRedisTemplate(其是RedisTemplate的定制化升级) * 使用StringRedisTemplate(其是RedisTemplate的定制化升级)
*/ */
private static StringRedisTemplate redisTemplate; private final StringRedisTemplate redisTemplate;
@Override private final KeyOps keyOps = new KeyOps();
public void afterPropertiesSet() throws Exception { private final StringOps stringOps = new StringOps();
RedisUtil.redisTemplate = SpringUtil.getBean(StringRedisTemplate.class); private final LockOps lockOps = new LockOps();
}
/** /**
* key相关操作 * key相关操作
*/ */
public static class KeyOps { @NoArgsConstructor(access = AccessLevel.PRIVATE)
public class KeyOps {
/** /**
* 根据key, 删除redis中的对应key-value * 根据key, 删除redis中的对应key-value
@ -50,7 +53,7 @@ public final class RedisUtil implements InitializingBean {
* @param key 要删除的key * @param key 要删除的key
* @return 删除是否成功 * @return 删除是否成功
*/ */
public static Boolean delete(String key) { public Boolean delete(String key) {
log.info("delete(...) => key= {}", key); log.info("delete(...) => key= {}", key);
// 返回值只可能为true/false, 不可能为null // 返回值只可能为true/false, 不可能为null
Boolean result = redisTemplate.delete(key); Boolean result = redisTemplate.delete(key);
@ -72,7 +75,7 @@ public final class RedisUtil implements InitializingBean {
* 要删除的key集合 * 要删除的key集合
* @return 删除了的key-value个数 * @return 删除了的key-value个数
*/ */
public static Long delete(Collection<String> keys) { public Long delete(Collection<String> keys) {
log.info("delete(...) => keys= {}", keys); log.info("delete(...) => keys= {}", keys);
Long count = redisTemplate.delete(keys); Long count = redisTemplate.delete(keys);
log.info("delete(...) => count= {}", count); log.info("delete(...) => count= {}", count);
@ -88,7 +91,7 @@ public final class RedisUtil implements InitializingBean {
* @param key 指定的key * @param key 指定的key
* @return 是否存在对应的key-value * @return 是否存在对应的key-value
*/ */
public static Boolean hasKey(String key) { public Boolean hasKey(String key) {
log.info("hasKey(...) => key= {}", key); log.info("hasKey(...) => key= {}", key);
Boolean result = redisTemplate.hasKey(key); Boolean result = redisTemplate.hasKey(key);
log.info("hasKey(...) => result= {}", result); log.info("hasKey(...) => result= {}", result);
@ -109,7 +112,7 @@ public final class RedisUtil implements InitializingBean {
* @param unit timeout的单位 * @param unit timeout的单位
* @return 操作是否成功 * @return 操作是否成功
*/ */
public static Boolean expire(String key, Long timeout, TimeUnit unit) { public Boolean expire(String key, Long timeout, TimeUnit unit) {
log.info("expire(...) => key= {}, timeout= {}, unit= {}", key, timeout, unit); log.info("expire(...) => key= {}, timeout= {}, unit= {}", key, timeout, unit);
Boolean result = redisTemplate.expire(key, timeout, unit); Boolean result = redisTemplate.expire(key, timeout, unit);
log.info("expire(...) => result is= {}", result); log.info("expire(...) => result is= {}", result);
@ -126,7 +129,8 @@ public final class RedisUtil implements InitializingBean {
* 提示: redis中String的数据结构可参考resources/data-structure/String(字符串)的数据结构(示例一).png * 提示: redis中String的数据结构可参考resources/data-structure/String(字符串)的数据结构(示例一).png
* redis中String的数据结构可参考resources/data-structure/String(字符串)的数据结构(示例二).png * redis中String的数据结构可参考resources/data-structure/String(字符串)的数据结构(示例二).png
*/ */
public static class StringOps { @NoArgsConstructor(access = AccessLevel.PRIVATE)
public class StringOps {
/** /**
* 设置key-value * 设置key-value
@ -135,7 +139,7 @@ public final class RedisUtil implements InitializingBean {
* @param key key * @param key key
* @param value key对应的value * @param value key对应的value
*/ */
public static void set(String key, String value) { public void set(String key, String value) {
log.info("set(...) => key= {}, value= {}", key, value); log.info("set(...) => key= {}, value= {}", key, value);
redisTemplate.opsForValue().set(key, value); redisTemplate.opsForValue().set(key, value);
} }
@ -149,7 +153,7 @@ public final class RedisUtil implements InitializingBean {
* @param timeout 过时时长 * @param timeout 过时时长
* @param unit timeout的单位 * @param unit timeout的单位
*/ */
public static void setEx(String key, String value, Long timeout, TimeUnit unit) { public void setEx(String key, String value, Long timeout, TimeUnit unit) {
log.info("setEx(...) => key= {}, value= {}, timeout= {}, unit= {}", key, value, timeout, unit); log.info("setEx(...) => key= {}, value= {}, timeout= {}, unit= {}", key, value, timeout, unit);
redisTemplate.opsForValue().set(key, value, timeout, unit); redisTemplate.opsForValue().set(key, value, timeout, unit);
} }
@ -163,7 +167,7 @@ public final class RedisUtil implements InitializingBean {
* *
* @return set是否成功 * @return set是否成功
*/ */
public static Boolean setIfAbsent(String key, String value) { public Boolean setIfAbsent(String key, String value) {
log.info("setIfAbsent(...) => key= {}, value= {}", key, value); log.info("setIfAbsent(...) => key= {}, value= {}", key, value);
Boolean result = redisTemplate.opsForValue().setIfAbsent(key, value); Boolean result = redisTemplate.opsForValue().setIfAbsent(key, value);
log.info("setIfAbsent(...) => result= {}", result); log.info("setIfAbsent(...) => result= {}", result);
@ -184,7 +188,7 @@ public final class RedisUtil implements InitializingBean {
* *
* @return set是否成功 * @return set是否成功
*/ */
public static Boolean setIfAbsent(String key, String value, Long timeout, TimeUnit unit) { public Boolean setIfAbsent(String key, String value, Long timeout, TimeUnit unit) {
log.info("setIfAbsent(...) => key= {}, value= {}, key= {}, value= {}", key, value, timeout, unit); log.info("setIfAbsent(...) => key= {}, value= {}, key= {}, value= {}", key, value, timeout, unit);
Boolean result = redisTemplate.opsForValue().setIfAbsent(key, value, timeout, unit); Boolean result = redisTemplate.opsForValue().setIfAbsent(key, value, timeout, unit);
log.info("setIfAbsent(...) => result= {}", result); log.info("setIfAbsent(...) => result= {}", result);
@ -201,7 +205,7 @@ public final class RedisUtil implements InitializingBean {
* @param key key-value对应的key * @param key key-value对应的key
* @return 该key对应的值 * @return 该key对应的值
*/ */
public static String get(String key) { public String get(String key) {
log.info("get(...) => key= {}", key); log.info("get(...) => key= {}", key);
String result = redisTemplate.opsForValue().get(key); String result = redisTemplate.opsForValue().get(key);
log.info("get(...) => result= {} ", result); log.info("get(...) => result= {} ", result);
@ -233,26 +237,22 @@ public final class RedisUtil implements InitializingBean {
* } * }
* } * }
*/ */
public static class LockOps { @NoArgsConstructor(access = AccessLevel.PRIVATE)
public class LockOps {
/** lua脚本, 保证 释放锁脚本 的原子性(以避免, 并发场景下, 释放了别人的锁) */ /** lua脚本, 保证 释放锁脚本 的原子性(以避免, 并发场景下, 释放了别人的锁) */
private static final String RELEASE_LOCK_LUA; private final String RELEASE_LOCK_LUA = "if redis.call('get',KEYS[1]) == ARGV[1] "
+ "then "
+ " return redis.call('del',KEYS[1]) "
+ "else "
+ " return 0 "
+ "end ";
/** 分布式锁默认(最大)存活时长 */ /** 分布式锁默认(最大)存活时长 */
public static final long DEFAULT_LOCK_TIMEOUT = 3; public final long DEFAULT_LOCK_TIMEOUT = 3;
/** DEFAULT_LOCK_TIMEOUT的单位 */ /** DEFAULT_LOCK_TIMEOUT的单位 */
public static final TimeUnit DEFAULT_TIMEOUT_UNIT = TimeUnit.SECONDS; public final TimeUnit DEFAULT_TIMEOUT_UNIT = TimeUnit.SECONDS;
static {
// 不论lua中0是否代表失败; 对于java的Boolean而言, 返回0, 则会被解析为false
RELEASE_LOCK_LUA = "if redis.call('get',KEYS[1]) == ARGV[1] "
+ "then "
+ " return redis.call('del',KEYS[1]) "
+ "else "
+ " return 0 "
+ "end ";
}
/** /**
* 获取(分布式). * 获取(分布式).
@ -260,7 +260,7 @@ public final class RedisUtil implements InitializingBean {
* *
* @see LockOps#getLock(String, String, Long, TimeUnit) * @see LockOps#getLock(String, String, Long, TimeUnit)
*/ */
public static Boolean getLock(final String key, final String value) { public Boolean getLock(final String key, final String value) {
return getLock(key, value, DEFAULT_LOCK_TIMEOUT, DEFAULT_TIMEOUT_UNIT); return getLock(key, value, DEFAULT_LOCK_TIMEOUT, DEFAULT_TIMEOUT_UNIT);
} }
@ -276,7 +276,7 @@ public final class RedisUtil implements InitializingBean {
* *
* @return 是否成功 * @return 是否成功
*/ */
public static Boolean getLockUntilTimeout(final String key, final String value, final Long retryTimeoutLimit) { public Boolean getLockUntilTimeout(final String key, final String value, final Long retryTimeoutLimit) {
return getLockUntilTimeout(key, value, DEFAULT_LOCK_TIMEOUT, DEFAULT_TIMEOUT_UNIT, return getLockUntilTimeout(key, value, DEFAULT_LOCK_TIMEOUT, DEFAULT_TIMEOUT_UNIT,
retryTimeoutLimit); retryTimeoutLimit);
} }
@ -293,9 +293,9 @@ public final class RedisUtil implements InitializingBean {
* *
* @return 是否成功 * @return 是否成功
*/ */
public static Boolean getLockUntilTimeout(final String key, final String value, public Boolean getLockUntilTimeout(final String key, final String value,
final Long timeout, final TimeUnit unit, final Long timeout, final TimeUnit unit,
final Long retryTimeoutLimit) { final Long retryTimeoutLimit) {
log.info("getLockUntilTimeout(...) => key= {}, value= {}, timeout= {}, unit= {}, " log.info("getLockUntilTimeout(...) => key= {}, value= {}, timeout= {}, unit= {}, "
+ "retryTimeoutLimit= {}ms", key, value, timeout, unit, retryTimeoutLimit); + "retryTimeoutLimit= {}ms", key, value, timeout, unit, retryTimeoutLimit);
long startTime = Instant.now().toEpochMilli(); long startTime = Instant.now().toEpochMilli();
@ -322,7 +322,7 @@ public final class RedisUtil implements InitializingBean {
* *
* @see LockOps#getLock(String, String, Long, TimeUnit, Boolean) * @see LockOps#getLock(String, String, Long, TimeUnit, Boolean)
*/ */
public static Boolean getLock(final String key, final String value, final Long timeout, final TimeUnit unit) { public Boolean getLock(final String key, final String value, final Long timeout, final TimeUnit unit) {
return getLock(key, value, timeout, unit, true); return getLock(key, value, timeout, unit, true);
} }
@ -347,9 +347,9 @@ public final class RedisUtil implements InitializingBean {
* *
* @return 是否成功 * @return 是否成功
*/ */
public static Boolean getLock(final String key, final String value, public Boolean getLock(final String key, final String value,
final Long timeout, final TimeUnit unit, final Long timeout, final TimeUnit unit,
Boolean recordLog) { Boolean recordLog) {
if (recordLog) { if (recordLog) {
log.info("getLock(...) => key= {}, value= {}, timeout= {}, unit= {}, recordLog= {}", log.info("getLock(...) => key= {}, value= {}, timeout= {}, unit= {}, recordLog= {}",
key, value, timeout, unit, recordLog); key, value, timeout, unit, recordLog);
@ -379,7 +379,7 @@ public final class RedisUtil implements InitializingBean {
* *
* @return 释放锁是否成功 * @return 释放锁是否成功
*/ */
public static Boolean releaseLock(final String key, final String value) { public Boolean releaseLock(final String key, final String value) {
log.info("releaseLock(...) => key= {}, lockValue= {}", key, value); log.info("releaseLock(...) => key= {}, lockValue= {}", key, value);
Boolean result = redisTemplate.execute((RedisConnection connection) -> Boolean result = redisTemplate.execute((RedisConnection connection) ->
connection.eval(RELEASE_LOCK_LUA.getBytes(), connection.eval(RELEASE_LOCK_LUA.getBytes(),

View File

@ -0,0 +1,50 @@
package cn.axzo.msg.center;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* @author cold_blade
* @date 2023/10/17
* @version 1.0
*/
@Configuration
public class RedisConfiguration {
@Bean
public RedisSerializer<String> redisKeySerializer() {
return new StringRedisSerializer();
}
@Bean
public RedisSerializer<String> redisValueSerializer() {
return new StringRedisSerializer();
}
/**
* RedisTemplate配置
*/
@Bean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory,
RedisSerializer<String> redisKeySerializer,
RedisSerializer<String> redisValueSerializer) {
StringRedisTemplate redisTemplate = new StringRedisTemplate();
redisTemplate.setConnectionFactory(factory);
//设置Key的序列化采用StringRedisSerializer
redisTemplate.setKeySerializer(redisKeySerializer);
redisTemplate.setHashKeySerializer(redisKeySerializer);
//设置值的序列化
redisTemplate.setValueSerializer(redisValueSerializer);
redisTemplate.setHashValueSerializer(redisValueSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}