目录
2.自定义RedisTemplate,自定义序列化和反序列化方式
运费模板微服务
接收前端发送的模板实体类
这里对实体类的每一个成员类变量使用了javax.validation.constraints注解来限制成员变量的值
java
package com.sl.ms.carriage.domain.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
import java.util.List;
/**
* 运费模板对象
*/
@Data
public class CarriageDTO {
/**
* 运费模板id
*/
@ApiModelProperty(value = "运费模板id")
private Long id;
/**
* 模板类型:1-同城寄,2-省内寄,3-经济区互寄,4-跨省
*/
@ApiModelProperty(value = "模板类型:1-同城寄,2-省内寄,3-经济区互寄,4-跨省", required = true)
@Max(value = 4, message = "类型值必须是1、2、3、4")
@Min(value = 1, message = "类型值必须是1、2、3、4")
@NotNull(message = "模板类型不能为空")
private Integer templateType;
/**
* 运送类型:1-普快,2-特快
*/
@ApiModelProperty(value = "运送类型:1-普快,2-特快", required = true)
@Max(value = 2, message = "类型值必须是1、2")
@Min(value = 1, message = "类型值必须是1、2")
@NotNull(message = "运送类型不能为空")
private Integer transportType;
/**
* 关联城市:1-全国,2-京津冀,3-江浙沪,4-川渝,5-黑吉辽
*/
@ApiModelProperty(value = "关联城市:1-全国,2-京津冀,3-江浙沪,4-川渝,5-黑吉辽", required = true)
@NotNull(message = "关联城市不能为空")
private List<String> associatedCityList;
/**
* 首重价格
*/
@ApiModelProperty(value = "首重价格", required = true)
@DecimalMin(value = "0.1", message = "首重价格必须大于0")
@NotNull(message = "首重价格不能为空")
private Double firstWeight;
/**
* 续重价格
*/
@ApiModelProperty(value = "续重价格", required = true)
@DecimalMin(value = "0.1", message = "续重价格必须大于0")
@NotNull(message = "续重价格不能为空")
private Double continuousWeight;
/**
* 轻抛系数
*/
@ApiModelProperty(value = "轻抛系数", required = true)
@Min(value = 1, message = "轻抛系数必须大于0")
@NotNull(message = "轻抛系数不能为空")
private Integer lightThrowingCoefficient;
/**
* 创建时间
*/
@ApiModelProperty(value = "创建时间")
private LocalDateTime created;
/**
* 更新时间
*/
@ApiModelProperty(value = "更新时间")
private LocalDateTime updated;
@ApiModelProperty(value = "运费")
private Double expense;
@ApiModelProperty(value = "计费重量,单位:kg")
private Double computeWeight;
}
插入数据时使用的entity类对象
java
@Data
@EqualsAndHashCode(callSuper = false)
@AllArgsConstructor
@NoArgsConstructor
@TableName("sl_carriage")
public class CarriageEntity extends BaseEntity {
private static final long serialVersionUID = -5358753714676282742L;
/**
* 模板类型:1-同城寄,2-省内寄,3-经济区互寄,4-跨省
*/
private Integer templateType;
/**
* 运送类型:1-普快,2-特快
*/
private Integer transportType;
/**
* 关联城市:1-全国,2-京津冀,3-江浙沪,4-川渝,5-黑吉辽
*/
private String associatedCity;
/**
* 首重价格
*/
private Double firstWeight;
/**
* 续重价格
*/
private Double continuousWeight;
/**
* 轻抛系数
*/
private Integer lightThrowingCoefficient;
}
BaseEntity类
java
@Data
public abstract class BaseEntity implements Serializable {
@TableId
private Long id; //主键id
@TableField(fill = FieldFill.INSERT) //MP自动填充
private LocalDateTime created;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updated;
}
查询运费模板服务
/** * 获取全部运费模板,按照创建时间倒序排序 * * @return 运费模板对象列表 */
这里是通过工具类批量把entity类对象转换成dto对象,然后返回给前端
java
@Override
public List<CarriageDTO> findAll() {
// 构造查询条件,按创建时间倒序
LambdaQueryWrapper<CarriageEntity> wrapper = Wrappers.<CarriageEntity>lambdaQuery().orderByDesc(CarriageEntity::getCreated);
// 查询数据库
List<CarriageEntity> carriageEntities = baseMapper.selectList(wrapper);
// 将结果转换为DTO类型 使用CarriageUtils工具类
return carriageEntities.stream().map(CarriageUtils::toDTO).collect(Collectors.toList());
}
新增和修改运费模块
整体流程
流程说明:
- 根据传入的CarriageDTO对象参数进行查询模板,判断模板是否存在,如果不存在直接落库
- 如果存在,需要进一步的判断是否为经济区互寄,如果不是,说明模板重复,不能落库
- 如果是经济区互寄,再进一步的判断是否有重复的城市,如果是,模板重复,不能落库
- 如果不重复,落库,响应返回
模板为什么不能重复?
因为运费的计算是通过模板进行的,如果存在多个模板,该基于哪个模板计算呢?所以模板是不能重复的。
代码实现
// 校验运费模板是否存在,如果不存在直接插入 (查询条件: 模板类型 运输类型 如果是修改排除当前id)
LambdaQueryWrapper<CarriageEntity> wrapper = Wrappers.<CarriageEntity>lambdaQuery()
.eq(CarriageEntity::getTemplateType, carriageDto.getTemplateType())
.eq(CarriageEntity::getTransportType, carriageDto.getTransportType())
//如果有id字段值,就说明是更新操作,那么就需要把与自己id值一样的模板排除掉,不然会与自己模板类型一致冲突
.ne(ObjectUtil.isNotEmpty(carriageDto.getId()), CarriageEntity::getId, carriageDto.getId());
这段代码构建的条件查询对象,特别注意ne()方法,如果dto对象的id成员变量不为null,说明就是更新操作,就说明在数据库中,有与这个对象一模一样的模板,所以需要排除掉这个存在与数据库中的模板(根据id主键值)
// 如果是经济区互寄类型,需进一步判断关联城市是否重复,通过集合取交集判断是否重复List<String> assCities = carriageEntityList.stream().map(CarriageEntity::getAssociatedCity)//获得每一个数据关联的城市字符串
.map(s -> {
return s.split(",");//将每一个字符串变成一个字符串数组
})
.flatMap(Arrays::stream)//将每一个字符串数组重新变成流对象
//TODO:不然直接收集的是List<String[]>类型
.collect(Collectors.toList());
Collection<String> intersection = CollUtil.intersection(assCities, carriageDto.getAssociatedCityList());
在数据库中,关联城市字段是 2,3,4 使用,间隔的字符串类型,2是城市id。
所以需要把这个字符串分割成字符数组。
.flatMap(Arrays::stream)
**将每一个字符串数组重新变成流对象,**不然直接收集的是List<String[]>类型,
assCities是数据表中所有的关联城市集合,最后判断与dto传入的关联城市集合是否有重复
java
/**
* 流程说明:
* ● 根据传入的CarriageDTO对象参数进行查询模板,判断模板是否存在,如果不存在直接落库
* ● 如果存在,需要进一步的判断是否为经济区互寄,如果不是,说明模板重复,不能落库
* ● 如果是经济区互寄,再进一步的判断是否有重复的城市,如果是,模板重复,不能落库
* ● 如果不重复,落库,响应返回
* 模板为什么不能重复?
* 因为运费的计算是通过模板进行的,如果存在多个模板,该基于哪个模板计算呢?所以模板是不能重复的。
* @param carriageDto 新增/修改运费对象
* @return
*
* 模板类型常量 {@link com.sl.ms.carriage.domain.constant.CarriageConstant}
*
*/
@Override
public CarriageDTO saveOrUpdate(CarriageDTO carriageDto) {
// 校验运费模板是否存在,如果不存在直接插入 (查询条件: 模板类型 运输类型 如果是修改排除当前id)
LambdaQueryWrapper<CarriageEntity> wrapper = Wrappers.<CarriageEntity>lambdaQuery()
.eq(CarriageEntity::getTemplateType, carriageDto.getTemplateType())
.eq(CarriageEntity::getTransportType, carriageDto.getTransportType())
//如果有id字段值,就说明是更新操作,那么就需要把与自己id值一样的模板排除掉,不然会与自己模板类型一致冲突
.ne(ObjectUtil.isNotEmpty(carriageDto.getId()), CarriageEntity::getId, carriageDto.getId());
List<CarriageEntity> carriageEntityList = super.list(wrapper);
// 如果没有重复的模板,可以直接插入或更新操作 (DTO 转 entity 保存成功 entity 转 DTO)
if (CollUtil.isEmpty(carriageEntityList)){
return saveOrUpdateCarriage(carriageDto);
}
// 如果存在重复模板,需要判断此次插入的是否为经济区互寄,非经济区互寄是不可以重复的
if(ObjectUtil.notEqual(carriageDto.getTemplateType(),CarriageConstant.ECONOMIC_ZONE)){
throw new SLException(CarriageExceptionEnum.NOT_ECONOMIC_ZONE_REPEAT);
}
// 如果是经济区互寄类型,需进一步判断关联城市是否重复,通过集合取交集判断是否重复
List<String> assCities = carriageEntityList.stream().map(CarriageEntity::getAssociatedCity)//获得每一个数据关联的城市字符串
.map(s -> {
return s.split(",");//将每一个字符串变成一个字符串数组
})
.flatMap(Arrays::stream)//将每一个字符串数组重新变成流对象
//TODO:不然直接收集的是List<String[]>类型
.collect(Collectors.toList());
Collection<String> intersection = CollUtil.intersection(assCities, carriageDto.getAssociatedCityList());
// 如果没有重复,可以新增或更新 (DTO 转 entity 保存成功 entity 转 DTO)
if(CollUtil.isEmpty(intersection)){
return saveOrUpdateCarriage(carriageDto);
}else{
throw new SLException(CarriageExceptionEnum.ECONOMIC_ZONE_CITY_REPEAT);
}
}
public CarriageDTO saveOrUpdateCarriage(CarriageDTO carriageDTO){
CarriageEntity entity = CarriageUtils.toEntity(carriageDTO);
super.saveOrUpdate(entity);
return CarriageUtils.toDTO(entity);
}
运费计算
整体流程
说明:
- 运费模板优先级:同城>省内>经济区互寄>跨省
- 将体积转化成重量,与重量比较,取大值
总的代码
java
/**
* ● 运费模板优先级:同城>省内>经济区互寄>跨省
* ● 将体积转化成重量,与重量比较,取大值
* @param waybillDTO 运费计算对象
* @return
*
*
*/
@Override
public CarriageDTO compute(WaybillDTO waybillDTO) {
// 根据参数查找运费模板 调用findCarriage方法
CarriageEntity carriage = findCarriage(waybillDTO);
// 计算重量,最小重量为1kg 调用getComputeWeight方法
double weight = getComputeWeight(waybillDTO, carriage);
// 计算运费 运费 = 首重价格 + (实际重量 - 1) * 续重架构
double money=carriage.getFirstWeight()+(weight-1)*carriage.getContinuousWeight();
// 结果四舍五入保留一位小数
money=NumberUtil.round(money,1).doubleValue();
// 封装运费和计算重量到 CarriageDTO,并返回
CarriageDTO carriageDTO = CarriageUtils.toDTO(carriage);
carriageDTO.setComputeWeight(weight);
carriageDTO.setExpense(money);
return carriageDTO;
}
查找运费模板方法
java
/**
* 根据参数查找运费模板
* 运费模板优先级:同城>省内>经济区互寄>跨省
* @param waybillDTO 参数
* @return 运费模板
*/
@Resource
private AreaFeign areaFeign;
private CarriageEntity findCarriage(WaybillDTO waybillDTO) {
// 如果 发件的城市id 和 收件的城市id相同, 查询同城模板 调用findByTemplateType方法
if(ObjectUtil.equal(waybillDTO.getSenderCityId(),waybillDTO.getReceiverCityId())){
CarriageEntity carriageEntity = findByTemplateType(CarriageConstant.SAME_CITY);
if(ObjectUtil.isNotEmpty(carriageEntity)){
return carriageEntity;
}
}
// 如果没查到或不是同城,则获取收寄件地址省份id 使用AreaFeign结构查询
//根据areaFeign接口获取这个市所属的省id
Long receiveProvinceId = areaFeign.get(waybillDTO.getReceiverCityId()).getParentId();
Long senderProvinceId = areaFeign.get(waybillDTO.getSenderCityId()).getParentId();
// 如果 收发件的省份id相同,查询同省的模板 调用findByTemplateType方法
if(ObjectUtil.equal(receiveProvinceId,senderProvinceId)){
CarriageEntity carriageEntity = findByTemplateType(CarriageConstant.SAME_PROVINCE);
if(ObjectUtil.isNotEmpty(carriageEntity)){
return carriageEntity;
}
}
// 如果没查到或不是同省,则查询是否为经济区互寄 调用findEconomicCarriage方法查询
CarriageEntity carriageEntity = findEconomicCarriage(receiveProvinceId,senderProvinceId);
if(ObjectUtil.isNotEmpty(carriageEntity)){
return carriageEntity;
}
// 如果没查到或不是经济区互寄,直接查跨省运费模板
carriageEntity = findByTemplateType(CarriageConstant.TRANS_PROVINCE);
if(ObjectUtil.isNotEmpty(carriageEntity)){
return carriageEntity;
}
// 如果最后没查到,直接抛自定义异常,提示模板未找到
if(ObjectUtil.isEmpty(carriageEntity)){
throw new SLException(CarriageExceptionEnum.NOT_FOUND);
}
return null;
}
/**
* @param receiverProvinceId 收件省份id
* @param senderProvinceId 发件省份id
* @return
*/
private CarriageEntity findEconomicCarriage(Long receiverProvinceId, Long senderProvinceId) {
//通过工具类EnumUtil 获取经济区城市配置枚举
LinkedHashMap<String, EconomicRegionEnum> enumMap = EnumUtil.getEnumMap(EconomicRegionEnum.class);
// 遍历所有经济区枚举值
EconomicRegionEnum targetEconomicEnum=null;
for (EconomicRegionEnum regionEnum : enumMap.values()) {
// 通过ArrayUtil工具类 判断发件网点 和 收件网点是否在同一经济区
if(ArrayUtil.containsAll(regionEnum.getValue(),receiverProvinceId,senderProvinceId)){
// 如果在得到对应经济区枚举
targetEconomicEnum=regionEnum;
break;
}
}
// 循环遍历未发现所属经济区,方法直接返回null
if(ObjectUtil.isEmpty(targetEconomicEnum)){
return null;
}
// 如果有经济区 根据 模板类型=经济区, 运输类型=普快 关联城市=枚举的code值 查询
LambdaQueryWrapper<CarriageEntity> wrapper = Wrappers.<CarriageEntity>lambdaQuery()
.eq(CarriageEntity::getTemplateType, CarriageConstant.ECONOMIC_ZONE)
.eq(CarriageEntity::getTransportType, CarriageConstant.REGULAR_FAST)
.like(CarriageEntity::getAssociatedCity, targetEconomicEnum.getCode());
return super.getOne(wrapper);
}
/**
* 根据模板类型查询模板
* @param templateType 模板类型:1-同城寄,2-省内寄,3-经济区互寄,4-跨省
* @return 运费模板
*/
private CarriageEntity findByTemplateType(Integer templateType) {
// 根据模板类型,及运输类型 = CarriageConstant.REGULAR_FAST 查询模板
LambdaQueryWrapper<CarriageEntity> wrapper = Wrappers.<CarriageEntity>lambdaQuery()
.eq(CarriageEntity::getTemplateType, templateType)
.eq(CarriageEntity::getTransportType,CarriageConstant.REGULAR_FAST);
return super.getOne(wrapper);
}
经济特区枚举类
java
package com.sl.ms.carriage.domain.enums;
/**
* 经济区枚举
*/
public enum EconomicRegionEnum {
BTH("2", new Long[] {1L, 7362L, 13267L}),
JZS("3", new Long[] {167904L, 191019L, 161792L}),
SC("4", new Long[] {545532L, 533328L}),
HJL("5", new Long[] {145665L, 133208L, 115224L});
/**
* 类型编码
*/
private final String code;
/**
* 类型值
*/
private final Long[] value;
EconomicRegionEnum(String code, Long[] value) {
this.code = code;
this.value = value;
}
public String getCode() {
return code;
}
public Long[] getValue() {
return value;
}
public static EconomicRegionEnum codeOf(String code) {
switch (code) {
case "2": {
return BTH;
}
case "3": {
return JZS;
}
case "4": {
return SC;
}
case "5": {
return HJL;
}
default: {
return null;
}
}
}
}
计算重量方法
java
/**
* 根据体积参数与实际重量计算计费重量
*
* @param waybillDTO 运费计算对象
* @param carriage 运费模板
* @return 计费重量
*/
private double getComputeWeight(WaybillDTO waybillDTO, CarriageEntity carriage) {
// 计算体积,如果传入体积则不需要计算
Integer volume = waybillDTO.getVolume();
if(ObjectUtil.isEmpty(volume)){
try {
volume=waybillDTO.getMeasureHigh()*waybillDTO.getMeasureLong()*waybillDTO.getMeasureWidth();
} catch (Exception e) {//防止有null值
throw new RuntimeException(e);
}
}
// 计算体积重量 = 体积 / 轻抛系数 tips: 使用NumberUtil工具类计算 保留一位小数
BigDecimal weightByV = NumberUtil.div(volume, carriage.getLightThrowingCoefficient(), 1);
// 重量取大值 = 体积重量 和 实际重量 tips: 使用NumberUtil工具类计算 保留一位小数
double weight = NumberUtil.max(weightByV.doubleValue(), NumberUtil.round(waybillDTO.getWeight(), 1).doubleValue());
// 计算续重,规则:不满1kg,按1kg计费;
if(weight<=1){
return 1;
}
// 10kg以下续重以0.1kg计量保留1位小数;
if(weight<=10){
return weight;
}
// 100kg以上四舍五入取整 举例:108.4kg按照108kg收费 108.5kg按照109kg收费
// tips: 使用NumberUtil工具类计算
if(weight>=100){
return NumberUtil.round(weight,0).doubleValue();
}
// 10-100kg续重以0.5kg计量保留1位小数;
// 0.5为一个计算单位,举例:18.8kg按照19收费, 18.4kg按照18.5kg收费 18.1kg按照18.5kg收费
int i=NumberUtil.round(weight, 0, RoundingMode.DOWN).intValue();//向下取整
if(NumberUtil.sub(weight,i)==0){
return weight;
}else if(NumberUtil.sub(weight,i)<=0.5){
return NumberUtil.add(i,0.5);
}else{
return NumberUtil.add(i,1);
}
}
Redis存入热点数据
思考:根据模板类型查询模板时需要频繁的查询数据库,性能较低,而且模板数据不会频繁变化,查询次数多,属于热点数据
所以可以使用redis存储模板entity类数据,
使用hash结构。大key->template 小key->发件城市id_收件城市
1.从nacos导入共享redis配置
java
spring:
cloud:
nacos:
username: nacos
password: nacos
server-addr: 192.168.150.101:8848
discovery:
namespace: ecae68ba-7b43-4473-a980-4ddeb6157bdc
ip: 192.168.150.1
config:
namespace: ecae68ba-7b43-4473-a980-4ddeb6157bdc
shared-configs: #共享配置
- data-id: shared-spring-seata.yml
group: SHARED_GROUP
refresh: false
- data-id: shared-spring-mysql.yml
group: SHARED_GROUP
refresh: false
- data-id: shared-spring-mybatis-plus.yml
group: SHARED_GROUP
refresh: false
# 导入redis共享配置
- data-id: shared-spring-redis.yml
group: SHARED_GROUP
refresh: false
这是共享redis配置文件的内容,可以发现里面的值并没有进行定义,那在哪定义了呢
其实共享配置文件是定义了文件的结构,具体的内容由微服务的配置文件自己配置
具体的配置在sl-express-ms-carriage.properties运费微服务自己的配置文件当中定义
2.自定义RedisTemplate,自定义序列化和反序列化方式
存入value值时使用json序列
java
@Configuration//告诉Spring这是一个配置类,并让Spring加载这个类
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public RedisTemplate<Object,Object> redisTemplate(RedisConnectionFactory factory){
RedisTemplate<Object,Object> redisTemplate = new RedisTemplate<>();
//默认的key序列化器为:JdkSerializationRedisSerializer
//我们改成StringRedisSerializer
//键序列化
redisTemplate.setKeySerializer(new StringRedisSerializer());
//值序列化
// 创建一个json的序列化方式
GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
// 设置value用jackjson进行处理
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
//key hashMap序列化
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
//value hashMap序列化
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
//连接工厂
redisTemplate.setConnectionFactory(factory);
return redisTemplate;
}
}
3.修改代码
java
@Resource
private RedisTemplate redisTemplate;
@Override
public CarriageDTO compute(WaybillDTO waybillDTO) {
//TODO day02 计算运费
HashOperations operations = redisTemplate.opsForHash();
// 根据参数查找运费模板 调用findCarriage方法
//CarriageEntity carriage = findCarriage(waybillDTO);
/*
* 根据模板类型查询模板时需要频繁的查询数据库,性能较低,而且模板数据不会频繁变化,查询次数多,属于热点数据
* 可以存于redis中,使用hash结构。大key->template 小key->发件城市id_收件城市
*/
//组装小key
String sKey = String.join("_", waybillDTO.getSenderCityId() + "", waybillDTO.getReceiverCityId() + "");
//先从redis中查数据
CarriageEntity carriage= (CarriageEntity) operations.get(RedisConstant.templateCode, sKey);
if(ObjectUtil.isEmpty(carriage)){
//redis没有改数据,就从数据库中查询
carriage=findCarriage(waybillDTO);
if(ObjectUtil.isEmpty(carriage)){
return null;
}
//将数据存入redis
operations.put(RedisConstant.templateCode,sKey,carriage);
}
// 计算重量,最小重量为1kg 调用getComputeWeight方法
double weight = getComputeWeight(waybillDTO, carriage);
// 计算运费 运费 = 首重价格 + (实际重量 - 1) * 续重架构
double money=carriage.getFirstWeight()+(weight-1)*carriage.getContinuousWeight();
// 结果四舍五入保留一位小数
money=NumberUtil.round(money,1).doubleValue();
// 封装运费和计算重量到 CarriageDTO,并返回
CarriageDTO carriageDTO = CarriageUtils.toDTO(carriage);
carriageDTO.setComputeWeight(weight);
carriageDTO.setExpense(money);
return carriageDTO;
}
4.存入数据时报错
com.fasterxml.jackson.databind.exc.InvalidDefinitionException:
Java 8 date/time type `java.time.LocalDateTime` not supported by default: add Module
"com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling (through
reference chain: java.util.ArrayList[0]->com.zym.entity.Banner["gmtCreate"])
解决:
导入依赖
html
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.12.3</version>
</dependency>
给LocalTime数据类型的成员变量规定序列化方式和反序列化方式
java
@Data
public abstract class BaseEntity implements Serializable {
@TableId
private Long id; //主键id
@TableField(fill = FieldFill.INSERT) //MP自动填充
@JsonDeserialize(using = LocalDateTimeDeserializer.class) // 反序列化
@JsonSerialize(using = LocalDateTimeSerializer.class) // 序列化
private LocalDateTime created;
@TableField(fill = FieldFill.INSERT_UPDATE)
@JsonDeserialize(using = LocalDateTimeDeserializer.class) // 反序列化
@JsonSerialize(using = LocalDateTimeSerializer.class) // 序列化
private LocalDateTime updated;
}
第二种代码方式
1.直接注入StringRedisTemplate模板类
这样一来就不用写配置类,重新定义RedisTemplate,它会把value序列化成String类型存入redis中
java
@Resource
private StringRedisTemplate stringRedisTemplate;
2.存取数据
java
//获取hashKey
String sKey = StrUtil.format("{}_{}", waybillDTO.getSenderCityId(), waybillDTO.getReceiverCityId());
Object cacheData = stringRedisTemplate.opsForHash().get(RedisConstant.templateCode, sKey);
CarriageEntity carriage=null;
if(ObjectUtil.isNotEmpty(cacheData)){
//将json数据反序列化成实体类对象,先将cacheData变成String类型,再反序列化
carriage= JSONUtil.toBean(StrUtil.toString(cacheData),CarriageEntity.class);
}else{
//从数据库中查
carriage = this.findCarriage(waybillDTO);
//将实体类转成json字符串存入redis中
stringRedisTemplate.opsForHash().put(RedisConstant.templateCode,sKey,JSONUtil.toJsonStr(carriage));
}
结果