Loki
3 years ago
18 changed files with 481 additions and 586 deletions
@ -1,136 +1,33 @@
|
||||
package cn.iocoder.yudao.framework.redis.config; |
||||
|
||||
import cn.hutool.core.lang.Assert; |
||||
import cn.hutool.core.util.StrUtil; |
||||
import cn.hutool.crypto.SecureUtil; |
||||
import cn.hutool.json.JSONUtil; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.springframework.cache.Cache; |
||||
import org.springframework.cache.annotation.CachingConfigurerSupport; |
||||
import org.springframework.cache.interceptor.CacheErrorHandler; |
||||
import org.springframework.cache.interceptor.KeyGenerator; |
||||
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.RedisTemplate; |
||||
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; |
||||
import org.springframework.data.redis.serializer.RedisSerializer; |
||||
import org.springframework.data.redis.serializer.StringRedisSerializer; |
||||
import org.springframework.util.DigestUtils; |
||||
|
||||
import java.nio.charset.Charset; |
||||
import java.nio.charset.StandardCharsets; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* Redis 配置类 |
||||
*/ |
||||
@Slf4j |
||||
public class YudaoRedisAutoConfiguration extends CachingConfigurerSupport { |
||||
@Configuration |
||||
public class YudaoRedisAutoConfiguration { |
||||
|
||||
/** |
||||
* 创建 RedisTemplate Bean,使用 JSON 序列化方式 |
||||
*/ |
||||
@Bean |
||||
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { |
||||
// 创建 RedisTemplate 对象
|
||||
RedisTemplate<String, Object> template = new RedisTemplate<>(); |
||||
//序列化
|
||||
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class); |
||||
// value值的序列化采用fastJsonRedisSerializer
|
||||
template.setValueSerializer(jackson2JsonRedisSerializer); |
||||
template.setHashValueSerializer(jackson2JsonRedisSerializer); |
||||
// key的序列化采用StringRedisSerializer
|
||||
template.setKeySerializer(new StringRedisSerializer()); |
||||
template.setHashKeySerializer(new StringRedisSerializer()); |
||||
// 设置 RedisConnection 工厂。😈 它就是实现多种 Java Redis 客户端接入的秘密工厂。感兴趣的胖友,可以自己去撸下。
|
||||
template.setConnectionFactory(factory); |
||||
return template; |
||||
} |
||||
|
||||
/** |
||||
* 自定义缓存key生成策略,默认将使用该策略 |
||||
*/ |
||||
@Bean |
||||
@Override |
||||
public KeyGenerator keyGenerator() { |
||||
return (target, method, params) -> { |
||||
Map<String,Object> container = new HashMap<>(3); |
||||
Class<?> targetClassClass = target.getClass(); |
||||
// 类地址
|
||||
container.put("class",targetClassClass.toGenericString()); |
||||
// 方法名称
|
||||
container.put("methodName",method.getName()); |
||||
// 包名称
|
||||
container.put("package",targetClassClass.getPackage()); |
||||
// 参数列表
|
||||
for (int i = 0; i < params.length; i++) { |
||||
container.put(String.valueOf(i),params[i]); |
||||
} |
||||
// 转为JSON字符串
|
||||
String jsonString = JSONUtil.toJsonStr(container); |
||||
// 做SHA256 Hash计算,得到一个SHA256摘要作为Key
|
||||
return SecureUtil.sha256(jsonString); |
||||
// return DigestUtils.sha256Hex(jsonString);
|
||||
}; |
||||
} |
||||
|
||||
@Bean |
||||
@Override |
||||
public CacheErrorHandler errorHandler() { |
||||
// 异常处理,当Redis发生异常时,打印日志,但是程序正常走
|
||||
log.info("初始化 -> [{}]", "Redis CacheErrorHandler"); |
||||
return new CacheErrorHandler() { |
||||
@Override |
||||
public void handleCacheGetError(RuntimeException e, Cache cache, Object key) { |
||||
log.error("Redis occur handleCacheGetError:key -> [{}]", key, e); |
||||
} |
||||
|
||||
@Override |
||||
public void handleCachePutError(RuntimeException e, Cache cache, Object key, Object value) { |
||||
log.error("Redis occur handleCachePutError:key -> [{}];value -> [{}]", key, value, e); |
||||
} |
||||
|
||||
@Override |
||||
public void handleCacheEvictError(RuntimeException e, Cache cache, Object key) { |
||||
log.error("Redis occur handleCacheEvictError:key -> [{}]", key, e); |
||||
} |
||||
|
||||
@Override |
||||
public void handleCacheClearError(RuntimeException e, Cache cache) { |
||||
log.error("Redis occur handleCacheClearError:", e); |
||||
} |
||||
}; |
||||
} |
||||
|
||||
|
||||
class StringRedisSerializer implements RedisSerializer<Object> { |
||||
|
||||
private final Charset charset; |
||||
|
||||
StringRedisSerializer() { |
||||
this(StandardCharsets.UTF_8); |
||||
} |
||||
// @Bean
|
||||
// public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
|
||||
// // 创建 RedisTemplate 对象
|
||||
// RedisTemplate<String, Object> template = new RedisTemplate<>();
|
||||
// // 设置 RedisConnection 工厂。😈 它就是实现多种 Java Redis 客户端接入的秘密工厂。感兴趣的胖友,可以自己去撸下。
|
||||
// template.setConnectionFactory(factory);
|
||||
// // 使用 String 序列化方式,序列化 KEY 。
|
||||
// template.setKeySerializer(RedisSerializer.string());
|
||||
// template.setHashKeySerializer(RedisSerializer.string());
|
||||
// // 使用 JSON 序列化方式(库是 Jackson ),序列化 VALUE 。
|
||||
// template.setValueSerializer(RedisSerializer.json());
|
||||
// template.setHashValueSerializer(RedisSerializer.json());
|
||||
// return template;
|
||||
// }
|
||||
|
||||
private StringRedisSerializer(Charset charset) { |
||||
Assert.notNull(charset, "Charset must not be null!"); |
||||
this.charset = charset; |
||||
} |
||||
|
||||
@Override |
||||
public String deserialize(byte[] bytes) { |
||||
return (bytes == null ? null : new String(bytes, charset)); |
||||
} |
||||
|
||||
@Override |
||||
public byte[] serialize(Object object) { |
||||
String string = JSONUtil.toJsonStr(object); |
||||
if (StrUtil.isBlank(string)) { |
||||
return null; |
||||
} |
||||
string = string.replace("\"", ""); |
||||
return string.getBytes(charset); |
||||
} |
||||
} |
||||
} |
||||
|
@ -1,137 +1,137 @@
|
||||
package cn.iocoder.yudao.module.member.service.user; |
||||
|
||||
import cn.hutool.core.util.RandomUtil; |
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
||||
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; |
||||
import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration; |
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseDbAndRedisUnitTest; |
||||
import cn.iocoder.yudao.module.infra.api.file.FileApi; |
||||
import cn.iocoder.yudao.module.member.controller.app.user.vo.AppUserUpdateMobileReqVO; |
||||
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; |
||||
import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper; |
||||
import cn.iocoder.yudao.module.member.service.auth.MemberAuthServiceImpl; |
||||
import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; |
||||
import org.junit.jupiter.api.Test; |
||||
import org.springframework.boot.test.mock.mockito.MockBean; |
||||
import org.springframework.context.annotation.Import; |
||||
import org.springframework.data.redis.core.StringRedisTemplate; |
||||
import org.springframework.security.crypto.password.PasswordEncoder; |
||||
|
||||
import javax.annotation.Resource; |
||||
import java.io.ByteArrayInputStream; |
||||
import java.util.function.Consumer; |
||||
|
||||
import static cn.hutool.core.util.RandomUtil.*; |
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; |
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; |
||||
import static org.junit.jupiter.api.Assertions.assertEquals; |
||||
import static org.mockito.Mockito.eq; |
||||
import static org.mockito.Mockito.when; |
||||
|
||||
// TODO @芋艿:单测的 review,等逻辑都达成一致后
|
||||
/** |
||||
* {@link MemberUserServiceImpl} 的单元测试类 |
||||
* |
||||
* @author 宋天 |
||||
*/ |
||||
@Import({MemberUserServiceImpl.class, YudaoRedisAutoConfiguration.class}) |
||||
public class MemberUserServiceImplTest extends BaseDbAndRedisUnitTest { |
||||
|
||||
@Resource |
||||
private MemberUserServiceImpl memberUserService; |
||||
|
||||
@Resource |
||||
private StringRedisTemplate stringRedisTemplate; |
||||
|
||||
@Resource |
||||
private MemberUserMapper userMapper; |
||||
|
||||
@MockBean |
||||
private MemberAuthServiceImpl authService; |
||||
|
||||
@MockBean |
||||
private PasswordEncoder passwordEncoder; |
||||
|
||||
@MockBean |
||||
private SmsCodeApi smsCodeApi; |
||||
@MockBean |
||||
private FileApi fileApi; |
||||
|
||||
@Test |
||||
public void testUpdateNickName_success(){ |
||||
// mock 数据
|
||||
MemberUserDO userDO = randomUserDO(); |
||||
userMapper.insert(userDO); |
||||
|
||||
// 随机昵称
|
||||
String newNickName = randomString(); |
||||
|
||||
// 调用接口修改昵称
|
||||
memberUserService.updateUserNickname(userDO.getId(),newNickName); |
||||
// 查询新修改后的昵称
|
||||
String nickname = memberUserService.getUser(userDO.getId()).getNickname(); |
||||
// 断言
|
||||
assertEquals(newNickName,nickname); |
||||
} |
||||
|
||||
@Test |
||||
public void testUpdateAvatar_success() throws Exception { |
||||
// mock 数据
|
||||
MemberUserDO dbUser = randomUserDO(); |
||||
userMapper.insert(dbUser); |
||||
|
||||
// 准备参数
|
||||
Long userId = dbUser.getId(); |
||||
byte[] avatarFileBytes = randomBytes(10); |
||||
ByteArrayInputStream avatarFile = new ByteArrayInputStream(avatarFileBytes); |
||||
// mock 方法
|
||||
String avatar = randomString(); |
||||
when(fileApi.createFile(eq(avatarFileBytes))).thenReturn(avatar); |
||||
// 调用
|
||||
String str = memberUserService.updateUserAvatar(userId, avatarFile); |
||||
// 断言
|
||||
assertEquals(avatar, str); |
||||
} |
||||
|
||||
@Test |
||||
public void updateMobile_success(){ |
||||
// mock数据
|
||||
String oldMobile = randomNumbers(11); |
||||
MemberUserDO userDO = randomUserDO(); |
||||
userDO.setMobile(oldMobile); |
||||
userMapper.insert(userDO); |
||||
|
||||
// TODO 芋艿:需要修复该单元测试,重构多模块带来的
|
||||
// 旧手机和旧验证码
|
||||
// SmsCodeDO codeDO = new SmsCodeDO();
|
||||
String oldCode = RandomUtil.randomString(4); |
||||
// codeDO.setMobile(userDO.getMobile());
|
||||
// codeDO.setCode(oldCode);
|
||||
// codeDO.setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene());
|
||||
// codeDO.setUsed(Boolean.FALSE);
|
||||
// when(smsCodeService.checkCodeIsExpired(codeDO.getMobile(),codeDO.getCode(),codeDO.getScene())).thenReturn(codeDO);
|
||||
|
||||
// 更新手机号
|
||||
String newMobile = randomNumbers(11); |
||||
String newCode = randomNumbers(4); |
||||
AppUserUpdateMobileReqVO reqVO = new AppUserUpdateMobileReqVO(); |
||||
reqVO.setMobile(newMobile); |
||||
reqVO.setCode(newCode); |
||||
reqVO.setOldMobile(oldMobile); |
||||
reqVO.setOldCode(oldCode); |
||||
memberUserService.updateUserMobile(userDO.getId(),reqVO); |
||||
|
||||
assertEquals(memberUserService.getUser(userDO.getId()).getMobile(),newMobile); |
||||
} |
||||
|
||||
// ========== 随机对象 ==========
|
||||
|
||||
@SafeVarargs |
||||
private static MemberUserDO randomUserDO(Consumer<MemberUserDO>... consumers) { |
||||
Consumer<MemberUserDO> consumer = (o) -> { |
||||
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
|
||||
}; |
||||
return randomPojo(MemberUserDO.class, ArrayUtils.append(consumer, consumers)); |
||||
} |
||||
|
||||
} |
||||
//package cn.iocoder.yudao.module.member.service.user;
|
||||
//
|
||||
//import cn.hutool.core.util.RandomUtil;
|
||||
//import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
//import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
|
||||
//import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration;
|
||||
//import cn.iocoder.yudao.framework.test.core.ut.BaseDbAndRedisUnitTest;
|
||||
//import cn.iocoder.yudao.module.infra.api.file.FileApi;
|
||||
//import cn.iocoder.yudao.module.member.controller.app.user.vo.AppUserUpdateMobileReqVO;
|
||||
//import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
|
||||
//import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper;
|
||||
//import cn.iocoder.yudao.module.member.service.auth.MemberAuthServiceImpl;
|
||||
//import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
|
||||
//import org.junit.jupiter.api.Test;
|
||||
//import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
//import org.springframework.context.annotation.Import;
|
||||
//import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
//import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
//
|
||||
//import javax.annotation.Resource;
|
||||
//import java.io.ByteArrayInputStream;
|
||||
//import java.util.function.Consumer;
|
||||
//
|
||||
//import static cn.hutool.core.util.RandomUtil.*;
|
||||
//import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||
//import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
|
||||
//import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
//import static org.mockito.Mockito.eq;
|
||||
//import static org.mockito.Mockito.when;
|
||||
//
|
||||
//// TODO @芋艿:单测的 review,等逻辑都达成一致后
|
||||
///**
|
||||
// * {@link MemberUserServiceImpl} 的单元测试类
|
||||
// *
|
||||
// * @author 宋天
|
||||
// */
|
||||
//@Import({MemberUserServiceImpl.class, YudaoRedisAutoConfiguration.class})
|
||||
//public class MemberUserServiceImplTest extends BaseDbAndRedisUnitTest {
|
||||
//
|
||||
// @Resource
|
||||
// private MemberUserServiceImpl memberUserService;
|
||||
//
|
||||
// @Resource
|
||||
// private StringRedisTemplate stringRedisTemplate;
|
||||
//
|
||||
// @Resource
|
||||
// private MemberUserMapper userMapper;
|
||||
//
|
||||
// @MockBean
|
||||
// private MemberAuthServiceImpl authService;
|
||||
//
|
||||
// @MockBean
|
||||
// private PasswordEncoder passwordEncoder;
|
||||
//
|
||||
// @MockBean
|
||||
// private SmsCodeApi smsCodeApi;
|
||||
// @MockBean
|
||||
// private FileApi fileApi;
|
||||
//
|
||||
// @Test
|
||||
// public void testUpdateNickName_success(){
|
||||
// // mock 数据
|
||||
// MemberUserDO userDO = randomUserDO();
|
||||
// userMapper.insert(userDO);
|
||||
//
|
||||
// // 随机昵称
|
||||
// String newNickName = randomString();
|
||||
//
|
||||
// // 调用接口修改昵称
|
||||
// memberUserService.updateUserNickname(userDO.getId(),newNickName);
|
||||
// // 查询新修改后的昵称
|
||||
// String nickname = memberUserService.getUser(userDO.getId()).getNickname();
|
||||
// // 断言
|
||||
// assertEquals(newNickName,nickname);
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testUpdateAvatar_success() throws Exception {
|
||||
// // mock 数据
|
||||
// MemberUserDO dbUser = randomUserDO();
|
||||
// userMapper.insert(dbUser);
|
||||
//
|
||||
// // 准备参数
|
||||
// Long userId = dbUser.getId();
|
||||
// byte[] avatarFileBytes = randomBytes(10);
|
||||
// ByteArrayInputStream avatarFile = new ByteArrayInputStream(avatarFileBytes);
|
||||
// // mock 方法
|
||||
// String avatar = randomString();
|
||||
// when(fileApi.createFile(eq(avatarFileBytes))).thenReturn(avatar);
|
||||
// // 调用
|
||||
// String str = memberUserService.updateUserAvatar(userId, avatarFile);
|
||||
// // 断言
|
||||
// assertEquals(avatar, str);
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void updateMobile_success(){
|
||||
// // mock数据
|
||||
// String oldMobile = randomNumbers(11);
|
||||
// MemberUserDO userDO = randomUserDO();
|
||||
// userDO.setMobile(oldMobile);
|
||||
// userMapper.insert(userDO);
|
||||
//
|
||||
// // TODO 芋艿:需要修复该单元测试,重构多模块带来的
|
||||
// // 旧手机和旧验证码
|
||||
//// SmsCodeDO codeDO = new SmsCodeDO();
|
||||
// String oldCode = RandomUtil.randomString(4);
|
||||
//// codeDO.setMobile(userDO.getMobile());
|
||||
//// codeDO.setCode(oldCode);
|
||||
//// codeDO.setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene());
|
||||
//// codeDO.setUsed(Boolean.FALSE);
|
||||
//// when(smsCodeService.checkCodeIsExpired(codeDO.getMobile(),codeDO.getCode(),codeDO.getScene())).thenReturn(codeDO);
|
||||
//
|
||||
// // 更新手机号
|
||||
// String newMobile = randomNumbers(11);
|
||||
// String newCode = randomNumbers(4);
|
||||
// AppUserUpdateMobileReqVO reqVO = new AppUserUpdateMobileReqVO();
|
||||
// reqVO.setMobile(newMobile);
|
||||
// reqVO.setCode(newCode);
|
||||
// reqVO.setOldMobile(oldMobile);
|
||||
// reqVO.setOldCode(oldCode);
|
||||
// memberUserService.updateUserMobile(userDO.getId(),reqVO);
|
||||
//
|
||||
// assertEquals(memberUserService.getUser(userDO.getId()).getMobile(),newMobile);
|
||||
// }
|
||||
//
|
||||
// // ========== 随机对象 ==========
|
||||
//
|
||||
// @SafeVarargs
|
||||
// private static MemberUserDO randomUserDO(Consumer<MemberUserDO>... consumers) {
|
||||
// Consumer<MemberUserDO> consumer = (o) -> {
|
||||
// o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
|
||||
// };
|
||||
// return randomPojo(MemberUserDO.class, ArrayUtils.append(consumer, consumers));
|
||||
// }
|
||||
//
|
||||
//}
|
||||
|
@ -0,0 +1,73 @@
|
||||
package co.yixiang.config; |
||||
|
||||
import com.alibaba.fastjson.JSON; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.apache.commons.codec.digest.DigestUtils; |
||||
import org.springframework.cache.Cache; |
||||
import org.springframework.cache.annotation.CachingConfigurerSupport; |
||||
import org.springframework.cache.interceptor.CacheErrorHandler; |
||||
import org.springframework.cache.interceptor.KeyGenerator; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
|
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
@Slf4j |
||||
@Configuration |
||||
public class CachingConfigurer extends CachingConfigurerSupport { |
||||
|
||||
/** |
||||
* 自定义缓存key生成策略,默认将使用该策略 |
||||
*/ |
||||
@Bean |
||||
@Override |
||||
public KeyGenerator keyGenerator() { |
||||
return (target, method, params) -> { |
||||
Map<String,Object> container = new HashMap<>(3); |
||||
Class<?> targetClassClass = target.getClass(); |
||||
// 类地址
|
||||
container.put("class",targetClassClass.toGenericString()); |
||||
// 方法名称
|
||||
container.put("methodName",method.getName()); |
||||
// 包名称
|
||||
container.put("package",targetClassClass.getPackage()); |
||||
// 参数列表
|
||||
for (int i = 0; i < params.length; i++) { |
||||
container.put(String.valueOf(i),params[i]); |
||||
} |
||||
// 转为JSON字符串
|
||||
String jsonString = JSON.toJSONString(container); |
||||
// 做SHA256 Hash计算,得到一个SHA256摘要作为Key
|
||||
return DigestUtils.sha256Hex(jsonString); |
||||
}; |
||||
} |
||||
|
||||
@Bean |
||||
@Override |
||||
public CacheErrorHandler errorHandler() { |
||||
// 异常处理,当Redis发生异常时,打印日志,但是程序正常走
|
||||
log.info("初始化 -> [{}]", "Redis CacheErrorHandler"); |
||||
return new CacheErrorHandler() { |
||||
@Override |
||||
public void handleCacheGetError(RuntimeException e, Cache cache, Object key) { |
||||
log.error("Redis occur handleCacheGetError:key -> [{}]", key, e); |
||||
} |
||||
|
||||
@Override |
||||
public void handleCachePutError(RuntimeException e, Cache cache, Object key, Object value) { |
||||
log.error("Redis occur handleCachePutError:key -> [{}];value -> [{}]", key, value, e); |
||||
} |
||||
|
||||
@Override |
||||
public void handleCacheEvictError(RuntimeException e, Cache cache, Object key) { |
||||
log.error("Redis occur handleCacheEvictError:key -> [{}]", key, e); |
||||
} |
||||
|
||||
@Override |
||||
public void handleCacheClearError(RuntimeException e, Cache cache) { |
||||
log.error("Redis occur handleCacheClearError:", e); |
||||
} |
||||
}; |
||||
} |
||||
} |
@ -1,205 +1,118 @@
|
||||
///**
|
||||
// * Copyright (C) 2018-2022
|
||||
// * All rights reserved, Designed By www.yixiang.co
|
||||
//
|
||||
// */
|
||||
//package co.yixiang.config;
|
||||
//
|
||||
//import cn.hutool.core.lang.Assert;
|
||||
//import co.yixiang.utils.StringUtils;
|
||||
//import com.alibaba.fastjson.JSON;
|
||||
//import com.alibaba.fastjson.parser.ParserConfig;
|
||||
//import com.alibaba.fastjson.serializer.SerializerFeature;
|
||||
//import lombok.extern.slf4j.Slf4j;
|
||||
//import org.apache.commons.codec.digest.DigestUtils;
|
||||
//import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
//import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
//import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
|
||||
//import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
//import org.springframework.cache.Cache;
|
||||
//import org.springframework.cache.annotation.CachingConfigurerSupport;
|
||||
//import org.springframework.cache.annotation.EnableCaching;
|
||||
//import org.springframework.cache.interceptor.CacheErrorHandler;
|
||||
//import org.springframework.cache.interceptor.KeyGenerator;
|
||||
//import org.springframework.context.annotation.Bean;
|
||||
//import org.springframework.context.annotation.Configuration;
|
||||
//import org.springframework.data.redis.cache.RedisCacheConfiguration;
|
||||
//import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
//import org.springframework.data.redis.core.RedisOperations;
|
||||
//import org.springframework.data.redis.core.RedisTemplate;
|
||||
//import org.springframework.data.redis.serializer.RedisSerializationContext;
|
||||
//import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
//
|
||||
//import java.nio.charset.Charset;
|
||||
//import java.nio.charset.StandardCharsets;
|
||||
//import java.time.Duration;
|
||||
//import java.util.HashMap;
|
||||
//import java.util.Map;
|
||||
//
|
||||
///**
|
||||
// * @author Zheng Jie
|
||||
// * @date 2018-11-24
|
||||
// */
|
||||
//@Slf4j
|
||||
//@Configuration(proxyBeanMethods = false)
|
||||
//@EnableCaching
|
||||
//@ConditionalOnClass(RedisOperations.class)
|
||||
/** |
||||
* Copyright (C) 2018-2022 |
||||
* All rights reserved, Designed By www.yixiang.co |
||||
|
||||
*/ |
||||
package co.yixiang.config; |
||||
|
||||
import cn.hutool.core.lang.Assert; |
||||
import co.yixiang.utils.StringUtils; |
||||
import com.alibaba.fastjson.JSON; |
||||
import com.alibaba.fastjson.parser.ParserConfig; |
||||
import com.alibaba.fastjson.serializer.SerializerFeature; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.apache.commons.codec.digest.DigestUtils; |
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; |
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; |
||||
import org.springframework.boot.autoconfigure.data.redis.RedisProperties; |
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties; |
||||
import org.springframework.cache.Cache; |
||||
import org.springframework.cache.annotation.CachingConfigurerSupport; |
||||
import org.springframework.cache.annotation.EnableCaching; |
||||
import org.springframework.cache.interceptor.CacheErrorHandler; |
||||
import org.springframework.cache.interceptor.KeyGenerator; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.context.annotation.Primary; |
||||
import org.springframework.data.redis.cache.RedisCacheConfiguration; |
||||
import org.springframework.data.redis.connection.RedisConnectionFactory; |
||||
import org.springframework.data.redis.core.RedisOperations; |
||||
import org.springframework.data.redis.core.RedisTemplate; |
||||
import org.springframework.data.redis.serializer.RedisSerializationContext; |
||||
import org.springframework.data.redis.serializer.RedisSerializer; |
||||
|
||||
import java.nio.charset.Charset; |
||||
import java.nio.charset.StandardCharsets; |
||||
import java.time.Duration; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* @author Zheng Jie |
||||
* @date 2018-11-24 |
||||
*/ |
||||
@Slf4j |
||||
@Configuration |
||||
//@EnableConfigurationProperties(RedisProperties.class)
|
||||
//public class RedisConfig extends CachingConfigurerSupport {
|
||||
//
|
||||
// /**
|
||||
// * 设置 redis 数据默认过期时间,默认2小时
|
||||
// * 设置@cacheable 序列化方式
|
||||
// */
|
||||
// @Bean
|
||||
// public RedisCacheConfiguration redisCacheConfiguration(){
|
||||
// FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
|
||||
// RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig();
|
||||
// configuration = configuration.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer)).entryTtl(Duration.ofHours(2));
|
||||
// return configuration;
|
||||
// }
|
||||
//
|
||||
// @SuppressWarnings("all")
|
||||
// @Bean(name = "redisTemplate")
|
||||
public class RedisConfig { |
||||
|
||||
/** |
||||
* 设置 redis 数据默认过期时间,默认2小时 |
||||
* 设置@cacheable 序列化方式 |
||||
*/ |
||||
@Bean |
||||
public RedisCacheConfiguration redisCacheConfiguration(){ |
||||
FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class); |
||||
RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig(); |
||||
configuration = configuration.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer)).entryTtl(Duration.ofHours(2)); |
||||
return configuration; |
||||
} |
||||
|
||||
@Bean(name = "redisTemplate") |
||||
// @ConditionalOnMissingBean(name = "redisTemplate")
|
||||
// public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
|
||||
// RedisTemplate<Object, Object> template = new RedisTemplate<>();
|
||||
// //序列化
|
||||
// FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
|
||||
// // value值的序列化采用fastJsonRedisSerializer
|
||||
// template.setValueSerializer(fastJsonRedisSerializer);
|
||||
// template.setHashValueSerializer(fastJsonRedisSerializer);
|
||||
// // 全局开启AutoType,这里方便开发,使用全局的方式
|
||||
// ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
|
||||
// // 建议使用这种方式,小范围指定白名单
|
||||
// // ParserConfig.getGlobalInstance().addAccept("me.zhengjie.domain");
|
||||
// // key的序列化采用StringRedisSerializer
|
||||
// template.setKeySerializer(new StringRedisSerializer());
|
||||
// template.setHashKeySerializer(new StringRedisSerializer());
|
||||
// template.setConnectionFactory(redisConnectionFactory);
|
||||
// return template;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 自定义缓存key生成策略,默认将使用该策略
|
||||
// */
|
||||
// @Bean
|
||||
// @Override
|
||||
// public KeyGenerator keyGenerator() {
|
||||
// return (target, method, params) -> {
|
||||
// Map<String,Object> container = new HashMap<>(3);
|
||||
// Class<?> targetClassClass = target.getClass();
|
||||
// // 类地址
|
||||
// container.put("class",targetClassClass.toGenericString());
|
||||
// // 方法名称
|
||||
// container.put("methodName",method.getName());
|
||||
// // 包名称
|
||||
// container.put("package",targetClassClass.getPackage());
|
||||
// // 参数列表
|
||||
// for (int i = 0; i < params.length; i++) {
|
||||
// container.put(String.valueOf(i),params[i]);
|
||||
// }
|
||||
// // 转为JSON字符串
|
||||
// String jsonString = JSON.toJSONString(container);
|
||||
// // 做SHA256 Hash计算,得到一个SHA256摘要作为Key
|
||||
// return DigestUtils.sha256Hex(jsonString);
|
||||
// };
|
||||
// }
|
||||
//
|
||||
// @Bean
|
||||
// @Override
|
||||
// public CacheErrorHandler errorHandler() {
|
||||
// // 异常处理,当Redis发生异常时,打印日志,但是程序正常走
|
||||
// log.info("初始化 -> [{}]", "Redis CacheErrorHandler");
|
||||
// return new CacheErrorHandler() {
|
||||
// @Override
|
||||
// public void handleCacheGetError(RuntimeException e, Cache cache, Object key) {
|
||||
// log.error("Redis occur handleCacheGetError:key -> [{}]", key, e);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void handleCachePutError(RuntimeException e, Cache cache, Object key, Object value) {
|
||||
// log.error("Redis occur handleCachePutError:key -> [{}];value -> [{}]", key, value, e);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void handleCacheEvictError(RuntimeException e, Cache cache, Object key) {
|
||||
// log.error("Redis occur handleCacheEvictError:key -> [{}]", key, e);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void handleCacheClearError(RuntimeException e, Cache cache) {
|
||||
// log.error("Redis occur handleCacheClearError:", e);
|
||||
// }
|
||||
// };
|
||||
// }
|
||||
//
|
||||
//}
|
||||
//
|
||||
///**
|
||||
// * Value 序列化
|
||||
// *
|
||||
// * @author /
|
||||
// * @param <T>
|
||||
// */
|
||||
// class FastJsonRedisSerializer<T> implements RedisSerializer<T> {
|
||||
//
|
||||
// private final Class<T> clazz;
|
||||
//
|
||||
// FastJsonRedisSerializer(Class<T> clazz) {
|
||||
// super();
|
||||
// this.clazz = clazz;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public byte[] serialize(T t) {
|
||||
// if (t == null) {
|
||||
// return new byte[0];
|
||||
// }
|
||||
// return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(StandardCharsets.UTF_8);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public T deserialize(byte[] bytes) {
|
||||
// if (bytes == null || bytes.length <= 0) {
|
||||
// return null;
|
||||
// }
|
||||
// String str = new String(bytes, StandardCharsets.UTF_8);
|
||||
// return JSON.parseObject(str, clazz);
|
||||
// }
|
||||
//
|
||||
//}
|
||||
//
|
||||
///**
|
||||
// * 重写序列化器
|
||||
// *
|
||||
// * @author /
|
||||
// */
|
||||
//class StringRedisSerializer implements RedisSerializer<Object> {
|
||||
//
|
||||
// private final Charset charset;
|
||||
//
|
||||
// StringRedisSerializer() {
|
||||
// this(StandardCharsets.UTF_8);
|
||||
// }
|
||||
//
|
||||
// private StringRedisSerializer(Charset charset) {
|
||||
// Assert.notNull(charset, "Charset must not be null!");
|
||||
// this.charset = charset;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public String deserialize(byte[] bytes) {
|
||||
// return (bytes == null ? null : new String(bytes, charset));
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public byte[] serialize(Object object) {
|
||||
// String string = JSON.toJSONString(object);
|
||||
// if (StringUtils.isBlank(string)) {
|
||||
// return null;
|
||||
// }
|
||||
// string = string.replace("\"", "");
|
||||
// return string.getBytes(charset);
|
||||
// }
|
||||
//}
|
||||
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { |
||||
RedisTemplate<String, Object> template = new RedisTemplate<>(); |
||||
//序列化
|
||||
FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class); |
||||
// value值的序列化采用fastJsonRedisSerializer
|
||||
template.setValueSerializer(fastJsonRedisSerializer); |
||||
template.setHashValueSerializer(fastJsonRedisSerializer); |
||||
// 全局开启AutoType,这里方便开发,使用全局的方式
|
||||
ParserConfig.getGlobalInstance().setAutoTypeSupport(true); |
||||
// 建议使用这种方式,小范围指定白名单
|
||||
// ParserConfig.getGlobalInstance().addAccept("me.zhengjie.domain");
|
||||
// key的序列化采用StringRedisSerializer
|
||||
template.setKeySerializer(new StringKeyRedisSerializer()); |
||||
template.setHashKeySerializer(new StringKeyRedisSerializer()); |
||||
template.setConnectionFactory(redisConnectionFactory); |
||||
return template; |
||||
} |
||||
|
||||
|
||||
|
||||
} |
||||
|
||||
/** |
||||
* Value 序列化 |
||||
* |
||||
* @author / |
||||
* @param <T> |
||||
*/ |
||||
class FastJsonRedisSerializer<T> implements RedisSerializer<T> { |
||||
|
||||
private final Class<T> clazz; |
||||
|
||||
FastJsonRedisSerializer(Class<T> clazz) { |
||||
super(); |
||||
this.clazz = clazz; |
||||
} |
||||
|
||||
@Override |
||||
public byte[] serialize(T t) { |
||||
if (t == null) { |
||||
return new byte[0]; |
||||
} |
||||
return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(StandardCharsets.UTF_8); |
||||
} |
||||
|
||||
@Override |
||||
public T deserialize(byte[] bytes) { |
||||
if (bytes == null || bytes.length <= 0) { |
||||
return null; |
||||
} |
||||
String str = new String(bytes, StandardCharsets.UTF_8); |
||||
return JSON.parseObject(str, clazz); |
||||
} |
||||
|
||||
} |
||||
|
||||
|
@ -0,0 +1,50 @@
|
||||
package co.yixiang.config; |
||||
|
||||
import cn.hutool.core.lang.Assert; |
||||
import cn.hutool.core.util.ObjectUtil; |
||||
import cn.hutool.core.util.StrUtil; |
||||
import cn.hutool.json.JSONUtil; |
||||
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.springframework.data.redis.serializer.RedisSerializer; |
||||
import org.springframework.data.redis.serializer.SerializationException; |
||||
|
||||
import java.nio.charset.Charset; |
||||
import java.nio.charset.StandardCharsets; |
||||
|
||||
@Slf4j |
||||
public class StringKeyRedisSerializer implements RedisSerializer<Object> { |
||||
|
||||
private final Charset charset; |
||||
|
||||
public StringKeyRedisSerializer() { |
||||
this(StandardCharsets.UTF_8); |
||||
} |
||||
|
||||
private StringKeyRedisSerializer(Charset charset) { |
||||
Assert.notNull(charset, "Charset must not be null!"); |
||||
this.charset = charset; |
||||
} |
||||
|
||||
@Override |
||||
public byte[] serialize(Object object) throws SerializationException { |
||||
String string = JSONUtil.toJsonStr(object); |
||||
if (StrUtil.isBlank(string)) { |
||||
return null; |
||||
} |
||||
|
||||
log.info("redis serialize:{}",string); |
||||
string = string.replace("\"", ""); |
||||
if (ObjectUtil.isNotEmpty(TenantContextHolder.getTenantId())){ |
||||
string = StrUtil.format("{}_{}",TenantContextHolder.getTenantId().toString(),string); |
||||
} |
||||
return string.getBytes(charset); |
||||
} |
||||
|
||||
@Override |
||||
public Object deserialize(byte[] bytes) throws SerializationException { |
||||
String rs = (bytes == null ? null : new String(bytes)); |
||||
log.info("redis deserialize:{}",rs); |
||||
return rs; |
||||
} |
||||
} |
Loading…
Reference in new issue