需求:
根据用户的配置的小数位来规范金额、数量的小数位显示。
问题:
由于在业务逻辑中涉及到大量的金额和数量的计算,所以在程序中对于这些字段都采用了BigDecimal来保证计算精度的准确性,但在回传给前端的数据中,由于Json序列化以后会将1.00序列化为1(默认转为数值类型),那就达不到产品所说的小数位显示的要求。
解决方案
- 前端一个一个获取配置属性然后再补充小数位
- 后端将BigDecimal字段全部转为String
解决方式
采用了第二种的方式进行转换,但此时项目开发已经到后期,如果一个个进行修改为String类型,在业务中在进行转换为BigDecimal在进行计算,这样的改动量太大,显然不太可能。所以顺着序列化这一方向,进行思考:如果我在序列化的时候 让他转为String,那不就可以了吗?这样不就可以保证小数位的不丢失了吗?
在我思来想去的时候看到了这个,时间格式化Date转String,String转Date。嗯哼哼,好像可以哟,于是点开这个注解看
竟然真可以转为String类型,于是进行了尝试,发现真的可以。那这样也就能解决序列化为String类型的问题了,如果想按照规定的配置小数位,也可以使用pattern来进行规定。
另外一种方法:
自定义一个序列化器和使用@JsonSerialize的混合双打
public class PriceToStrSerializer extends JsonSerializer<BigDecimal> {
@Override
public void serialize(BigDecimal value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString(scaledValue.toPlainString());
}
}
到这里,基本都能满足转为String的要求了。但对于我的需求而言,好像还不行,要根据配置好的小数位进行格式化。
哐哐哐,那是不是我在自定义序列化器上加上对应的注解,在注入对应的类,最后就可以操作数据库拿配置了丫。
好像有点异想天开了
emmm,这个想法不对,得换,通过静态工厂注入?好像可行啊,于是哐哐哐,我又写了代码
public class PriceToStrSerializer extends JsonSerializer<BigDecimal> {
private static RemoteMdOrgService remoteMdOrgService;
// 无参构造方法
public PriceToStrSerializer() {
}
// 静态工厂注入对应的Bean
public static void setRemoteMdOrgService(RemoteMdOrgService service) {
remoteMdOrgService = service;
}
@Override
public void serialize(BigDecimal value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
Long orgId = GetSysDefaultUtil.getOrgID();
MdOrgInfoVO data = remoteMdOrgService.getMdOrgInfoByOrgId(orgId).getData();
BigDecimal scaledValue = value.setScale(scale, RoundingMode.HALF_UP);
jsonGenerator.writeString(scaledValue.toPlainString());
}
}
看到这,那不得加一个将这个值注入进来的类。
@Configuration
public class SerializerConfig {
private final RemoteMdOrgService remoteMdOrgService;
public SerializerConfig(RemoteMdOrgService remoteMdOrgService) {
this.remoteMdOrgService = remoteMdOrgService;
// 注入属性
PriceToStrSerializer.setRemoteMdOrgService(remoteMdOrgService);
}
//全局配置时起作用,需要注册到ObjectMapper
// @Bean
// public SimpleModule bigDecimalModule() {
// SimpleModule module = new SimpleModule();
// // 注册自定义序列化器
// module.addSerializer(BigDecimal.class, new BigDecimalToStrSerializer());
// return module;
// }
}
好吧 一套通过构造器注入属性 又再通过静态工厂模式 将值注入,总算拿到对应的配置值了,到这里我也总算实现了需求了。
诶,怎么加载多一点价钱数据这加载时间怎么有点长啊?
由于每次序列化,我都要对对应字段进行查库,那这样 不慢也才奇怪,于是我把这个配置值搞到了redis中,去缓存中去,最终也是加快了加载速度了
public class PriceToStrSerializer extends JsonSerializer<BigDecimal> {
private static RemoteMdOrgService remoteMdOrgService;
private static RedisService redisService;
private final static String CACHE_KEY ="MdOrgInfo:";
private final static String PRICE_KEY = "drugPriceXsw";
// 无参构造方法
public PriceToStrSerializer() {
}
// 静态工厂注入对应的Bean
public static void setRemoteMdOrgService(RemoteMdOrgService service) {
remoteMdOrgService = service;
}
public static void setRedisService(RedisService redis){redisService = redis; }
@Override
public void serialize(BigDecimal value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
Long orgId = GetSysDefaultUtil.getOrgID();
int scale = 2;
Map<String, Object> cacheMap = redisService.getCacheMap(CACHE_KEY+orgId);
try {
//获取缓存不在,再去查数据库
if(cacheMap.isEmpty()||!cacheMap.containsKey(PRICE_KEY)) {
MdOrgInfoVO data = remoteMdOrgService.getMdOrgInfoByOrgId(orgId).getData();
if (data != null) {
scale = data.getDrugPriceXsw();
}
}else{
scale = (Integer) cacheMap.get(PRICE_KEY);
}
} catch (Exception e) {
e.printStackTrace(); // Handle or log the exception as needed
}
BigDecimal scaledValue = value.setScale(scale, RoundingMode.HALF_UP);
jsonGenerator.writeString(scaledValue.toPlainString());
}