当前位置: 首页 > news >正文

Springboot 为了偷懒,我封装了一个自适配的数据单位转换工具类

前言


平时做一些统计数据,经常从数据库或者是从接口获取出来的数据,单位是跟业务需求不一致的。


比如, 我们拿出来的, 实际上要是

又比如,我们拿到的数据需要 乘以100 返回给前端做 百分比展示

又比如, 千分比转换

又比如,拿出来的金额需要变成 万为单位

又比如,需要保留2位小数


......

等等等等。

平时我们怎么搞?
很多时候拿到的是一个数据集合list,就需要去遍历然后根据每个DTO的属性去做相关单位转换。


一直get 完 set ,get 完 set ,get 完 set ,get 完 set ,get 完 set ,人都麻了。

就像这样:


所以,如果通过反射自动匹配出来一些操作转换,是不是就看代码看起来舒服一点,人也轻松一点。

答案: 是的


然后,我就搞了。

正文


本篇内容简要:

① 初步的封装,通过map去标记需要转换的 类属性字段

② 进一步的封装, 配合老朋友自定义注解搞事情

产品:

支付总金额 换成万 为单位, 方便运营统计 ;

那个什么计数,要是百分比的 ;

然后还有一个是千分比;

另外,还有2个要保留2位小数;

还有啊,那个。。。。。。

我:

别说了,喝口水吧。

拿到的数据都在这个DTO里面 :

 

开始封装:

 

① 初步的封装,通过map去标记需要转换的 类属性字段


思路玩法: 

a.通过反射拿出字段
b.配合传入的转换标记Map 匹配哪些字段需要操作
c.然后从map取出相关字段的具体操作是什么,然后执行转换操作
d.重新赋值 

① 简单弄个枚举,列出现在需求上的转换操作类型

UnitConvertType.java

/*** @Author : JCccc* @CreateTime : 2023/01/14* @Description :**/
public enum UnitConvertType {/*** 精度*/R,/*** 万元*/B,/*** 百分*/PERCENTAGE,/*** 千分*/PERMIL
}


② 核心封装的转换函数 

UnitConvertUtil.java


import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** @Author : JCccc* @CreateTime : 2023/01/14* @Description :**/
@Slf4j
public class UnitConvertUtil {public static <T> void unitMapConvert(List<T> list, Map<String, UnitConvertType> propertyMap) {for (T t : list) {Field[] declaredFields = t.getClass().getDeclaredFields();for (Field declaredField : declaredFields) {if (propertyMap.keySet().stream().anyMatch(x -> x.equals(declaredField.getName()))) {try {declaredField.setAccessible(true);Object o = declaredField.get(t);UnitConvertType unitConvertType = propertyMap.get(declaredField.getName());if (o != null) {if (unitConvertType.equals(UnitConvertType.PERCENTAGE)) {BigDecimal bigDecimal = ((BigDecimal) o).multiply(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP);declaredField.set(t, bigDecimal);}if (unitConvertType.equals(UnitConvertType.PERMIL)) {BigDecimal bigDecimal = ((BigDecimal) o).multiply(new BigDecimal(1000)).setScale(2, BigDecimal.ROUND_HALF_UP);declaredField.set(t, bigDecimal);}if (unitConvertType.equals(UnitConvertType.B)) {BigDecimal bigDecimal = ((BigDecimal) o).divide(new BigDecimal(10000)).setScale(2, BigDecimal.ROUND_HALF_UP);declaredField.set(t, bigDecimal);}if (unitConvertType.equals(UnitConvertType.R)) {BigDecimal bigDecimal = ((BigDecimal) o).setScale(2, BigDecimal.ROUND_HALF_UP);declaredField.set(t, bigDecimal);}}} catch (Exception ex) {log.error("处理失败");continue;}}}}}public static void main(String[] args) {//获取模拟数据List<MySumReportDTO> list = getMySumReportList();Map<String, UnitConvertType> map =new HashMap<>();map.put("payTotalAmount", UnitConvertType.B);map.put("jcAmountPercentage", UnitConvertType.PERCENTAGE);map.put("jcCountPermillage", UnitConvertType.PERMIL);map.put("length", UnitConvertType.R);map.put("width", UnitConvertType.R);unitMapConvert(list,map);System.out.println("通过map标识的自动转换玩法:"+list.toString());}private static List<MySumReportDTO> getMySumReportList() {MySumReportDTO mySumReportDTO = new MySumReportDTO();mySumReportDTO.setPayTotalAmount(new BigDecimal(1100000));mySumReportDTO.setJcAmountPercentage(BigDecimal.valueOf(0.695));mySumReportDTO.setJcCountPermillage(BigDecimal.valueOf(0.7894));mySumReportDTO.setLength(BigDecimal.valueOf(1300.65112));mySumReportDTO.setWidth(BigDecimal.valueOf(6522.12344));MySumReportDTO mySumReportDTO1 = new MySumReportDTO();mySumReportDTO1.setPayTotalAmount(new BigDecimal(2390000));mySumReportDTO1.setJcAmountPercentage(BigDecimal.valueOf(0.885));mySumReportDTO1.setJcCountPermillage(BigDecimal.valueOf(0.2394));mySumReportDTO1.setLength(BigDecimal.valueOf(1700.64003));mySumReportDTO1.setWidth(BigDecimal.valueOf(7522.12344));List<MySumReportDTO> list = new ArrayList<>();list.add(mySumReportDTO);list.add(mySumReportDTO1);return list;}}

代码简析: 

看看怎么调用的:

public static void main(String[] args) {//获取模拟数据List<MySumReportDTO> list = getMySumReportList();System.out.println("转换前:"+list.toString());Map<String, UnitConvertType> map =new HashMap<>();map.put("payTotalAmount", UnitConvertType.B);map.put("jcAmountPercentage", UnitConvertType.PERCENTAGE);map.put("jcCountPermillage", UnitConvertType.PERMIL);map.put("length", UnitConvertType.R);map.put("width", UnitConvertType.R);unitMapConvert(list,map);System.out.println("通过map标识的自动转换玩法:"+list.toString());}

 

代码简析: 

 

效果:

整个集合list的 对应字段都自动转换成功(转换逻辑想怎么样就自己在对应if里面调整、拓展): 

 

 

 ② 进一步的封装, 配合老朋友自定义注解搞事情

 

实说实话,第一步的封装程度已经够用了,就是传map标识出来哪些需要转换,对应转换枚举类型是什么。

其实我感觉是够用的。


但是么,为了用起来更加方便,或者说 更加地可拓展, 那么配合自定义注解是更nice的。

开搞。

创建一个自定义注解 ,JcBigDecConvert.java

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** @Author : JCccc* @CreateTime : 2023/01/14* @Description :**/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface JcBigDecConvert {UnitConvertType name();
}

 怎么用? 就是在我们的报表DTO里面,去标记字段。

示例:

MyYearSumReportDTO.java

ps: 可以看到我们在字段上面使用了自定义注解

import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;/*** @Author : JCccc* @CreateTime : 2023/2/03* @Description :**/@Data
public class MyYearSumReportDTO implements Serializable {private static final long serialVersionUID = 5285987517581372888L;//支付总金额@JcBigDecConvert(name=UnitConvertType.B)private BigDecimal payTotalAmount;//jc金额百分比@JcBigDecConvert(name=UnitConvertType.PERCENTAGE)private BigDecimal jcAmountPercentage;//jc计数千分比@JcBigDecConvert(name=UnitConvertType.PERMIL)private BigDecimal jcCountPermillage;//保留2位@JcBigDecConvert(name=UnitConvertType.R)private BigDecimal length;//保留2位@JcBigDecConvert(name=UnitConvertType.R)private BigDecimal width;}

然后针对配合我们的自定义,封一个转换函数,反射获取属性字段,然后解析注解,然后做对应转换操作。

 代码:

    public static <T> void unitAnnotateConvert(List<T> list) {for (T t : list) {Field[] declaredFields = t.getClass().getDeclaredFields();for (Field declaredField : declaredFields) {try {if (declaredField.getName().equals("serialVersionUID")){continue;}JcBigDecConvert myFieldAnn = declaredField.getAnnotation(JcBigDecConvert.class);if(Objects.isNull(myFieldAnn)){continue;}UnitConvertType unitConvertType = myFieldAnn.name();declaredField.setAccessible(true);Object o = declaredField.get(t);if (Objects.nonNull(o)) {if (unitConvertType.equals(UnitConvertType.PERCENTAGE)) {BigDecimal bigDecimal = ((BigDecimal) o).multiply(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP);declaredField.set(t, bigDecimal);}if (unitConvertType.equals(UnitConvertType.PERMIL)) {BigDecimal bigDecimal = ((BigDecimal) o).multiply(new BigDecimal(1000)).setScale(2, BigDecimal.ROUND_HALF_UP);declaredField.set(t, bigDecimal);}if (unitConvertType.equals(UnitConvertType.B)) {BigDecimal bigDecimal = ((BigDecimal) o).divide(new BigDecimal(10000)).setScale(2, BigDecimal.ROUND_HALF_UP);declaredField.set(t, bigDecimal);}if (unitConvertType.equals(UnitConvertType.R)) {BigDecimal bigDecimal = ((BigDecimal) o).setScale(2, BigDecimal.ROUND_HALF_UP);declaredField.set(t, bigDecimal);}}} catch (Exception ex) {log.error("处理失败");}}}}

写个调用示例看看效果:
 

    public static void main(String[] args) {List<MyYearSumReportDTO> yearsList = getMyYearSumReportList();unitAnnotateConvert(yearsList);System.out.println("通过注解标识的自动转换玩法:"+yearsList.toString());}private static List<MyYearSumReportDTO> getMyYearSumReportList() {MyYearSumReportDTO mySumReportDTO = new MyYearSumReportDTO();mySumReportDTO.setPayTotalAmount(new BigDecimal(1100000));mySumReportDTO.setJcAmountPercentage(BigDecimal.valueOf(0.695));mySumReportDTO.setJcCountPermillage(BigDecimal.valueOf(0.7894));mySumReportDTO.setLength(BigDecimal.valueOf(1300.65112));mySumReportDTO.setWidth(BigDecimal.valueOf(6522.12344));MyYearSumReportDTO mySumReportDTO1 = new MyYearSumReportDTO();mySumReportDTO1.setPayTotalAmount(new BigDecimal(2390000));mySumReportDTO1.setJcAmountPercentage(BigDecimal.valueOf(0.885));mySumReportDTO1.setJcCountPermillage(BigDecimal.valueOf(0.2394));mySumReportDTO1.setLength(BigDecimal.valueOf(1700.64003));mySumReportDTO1.setWidth(BigDecimal.valueOf(7522.12344));List<MyYearSumReportDTO> list = new ArrayList<>();list.add(mySumReportDTO);list.add(mySumReportDTO1);return list;}

效果也是很OK:

 

抛砖引玉,传递‘玩’代码思想,学编程,哎我就是玩。


好了,该篇就到这。 

相关文章:

Springboot 为了偷懒,我封装了一个自适配的数据单位转换工具类

前言 平时做一些统计数据&#xff0c;经常从数据库或者是从接口获取出来的数据&#xff0c;单位是跟业务需求不一致的。 比如&#xff0c; 我们拿出来的 分&#xff0c; 实际上要是元 又比如&#xff0c;我们拿到的数据需要 乘以100 返回给前端做 百分比展示 又比如&#xff…...

正则表达式

当我们需要对字符串进行判断的时候&#xff0c;使用正则表达式能大大提高编程效率。比如&#xff0c;当我们需要找出所有“像邮箱”的字符串&#xff08;包含"" "." ".com"&#xff0c;且顺序一致&#xff09;&#xff0c;我们需要一个某种模式的…...

java进阶Map 集合

通过之前的学习我们知道Map是一个双列集合&#xff0c;就是以键值对的形式进行数据存储 java进阶—集合 Map 下面有 三个子接口&#xff0c;HashMap &#xff0c; HashTable 以及 TreeMap 提醒一点&#xff1a;Map不是Collection下的集合&#xff0c;Collection是单列集合&am…...

Java 方法超详细整理,适合新手入门

目录 一、什么是方法呢&#xff1f; 二、方法的优点 三、带返回值方法定义 语法&#xff1a; 示例&#xff1a; 四、带返回值方法调用 语法&#xff1a; 示例&#xff1a; 五、结果示例 一、什么是方法呢&#xff1f; Java方法是语句的集合&#xff0c;它们在一起执行…...

软考学习笔记(题目知识记录)

答案为 概要设计阶段 本题涉及软件工程的概念 软件工程的任务是基于需求分析的结果建立各种设计模型&#xff0c;给出问题的解决方案 软件设计可以分为两个阶段&#xff1a; 概要设计阶段和详细设计阶段 结构化设计方法中&#xff0c;概要设计阶段进行软件体系结构的设计&…...

2021.3.3idea创建Maven项目

首先new - project - 找到Maven 然后按下图操作&#xff1a;先勾选使用骨架&#xff0c;再找到Maven-archetype-webapp&#xff0c;选中&#xff0c;然后next填写自己想要创建的项目名&#xff0c;然后选择自己的工作空间①、选择自己下载的Maven插件②、选择选择Maven里的sett…...

ASP.NET MVC | 创建应用程序

目录 首先 NO.1 No.2 App_Data 文件夹 Content 文件夹 Controllers 文件夹 Models 文件夹 Views 文件夹 Scripts 文件夹 最后 首先 一步一步的来&#xff0c;电脑上需要安装vs2019软件&#xff0c;版本高低无所谓&#xff0c;就是功能多少而已。 长这样的&#xff0…...

思科设备命令讲解(超基础)

♥️作者&#xff1a;小刘在C站 ♥️个人主页&#xff1a;小刘主页 ♥️每天分享云计算网络运维课堂笔记&#xff0c;努力不一定有收获&#xff0c;但一定会有收获加油&#xff01;一起努力&#xff0c;共赴美好人生&#xff01; ♥️夕阳下&#xff0c;是最美的绽放&#xff0…...

Qt-FFmpeg开发-保存视频流裸流(11)

Qt-FFmpeg开发-保存视频流裸流&#x1f4c0; 文章目录Qt-FFmpeg开发-保存视频流裸流&#x1f4c0;1、概述&#x1f4f8;2、实现效果&#x1f4bd;3、FFmpeg保存裸流代码流程&#x1f4a1;4、主要代码&#x1f50d;5、完整源代码&#x1f4d1;更多精彩内容&#x1f449;个人内容…...

Zebec官方辟谣“我们与Protradex没有任何关系”

近日&#xff0c;流支付协议Zebec Protocol在其官方推特上&#xff0c;发表了一个辟谣澄清声明。该条推特推文表示&#xff0c;“Zebec 与 Protradex 没有任何关系或产生关联。他们&#xff08; Protradex &#xff09;声称Zebec 生态正在支持他们&#xff0c;但这是错误的。随…...

BMS电池管理系统中的各种算法介绍

BMS电池管理系统 是一种用于电池组中的单个电池管理的系统&#xff0c;以确保其安全性、寿命和性能。BMS系统通过采集电池信息并对其进行分析&#xff0c;以确保电池组的正常运行。在BMS电池管理系统中&#xff0c;涉及到了许多算法&#xff0c;包括最大功率点追踪算法、SOC计算…...

stack Overflow 的使用

文章目录优雅的搜索1.1要在特定标签内搜索1.2搜索特定的短语1.3 限定检索位置1.4选择性屏蔽优雅的筛选搜索结果1. 返回的搜索筛选2. 特定时间段的帖子3. 精准的BOOL判断4. 其他的例子优雅的搜索 其实&#xff0c;在Stack OverFlow上的搜索方式&#xff0c;与国内的百度没什么大…...

Vue 在for循环中动态添加类名及style样式集合

介绍 在vue的 for 循环中&#xff0c;经常会使用到动态添加类名或者样式的情况&#xff0c;实现给当前的选中的 div 添加不同的样式。 动态添加类名 提示&#xff1a; 所有动态添加的类名&#xff0c;放在表达式里都需要添加引号&#xff0c;进行包裹。 通过 对象 的形式&a…...

Maven的优势

作用一&#xff1a;个人理解maven主要是用来解决导入java类依赖的jar,编译java项目主要问题。(最早手动导入jar&#xff0c;使用Ant之类的编译java项目)以pom.xml文件中dependency属性管理依赖的jar包&#xff0c;而jar包包含class文件和一些必要的资源文件。当然它可以构建项目…...

uboot,内核,根文件系统的作用

复习了下uboot&#xff0c;内核&#xff0c;根文件系统&#xff0c;简单概括下三者的主要内容。 1 uboot uboot的目的&#xff1a;启动内核。 uboot的功能可以分为两个阶段任务。 1.2.1 uboot第一阶段 uboot第一阶段主要负责硬件相关的初始化&#xff0c;主要在cpu/arm920…...

Vue3通透教程【四】Vue3组合API初体验

文章目录&#x1f31f; 写在前面&#x1f31f; 组合式 API 是什么&#xff1f;&#x1f31f; 直观组合式API&#x1f31f; 写在最后&#x1f31f; 写在前面 专栏介绍&#xff1a; 凉哥作为 Vue 的忠实 粉丝输出过大量的 Vue 文章&#xff0c;应粉丝要求开始更新 Vue3 的相关技…...

coco数据集训练nanodet详细流程

github地址 首先要配置环境 conda create -n nanodet python3.8 -y conda activate nanodet确认一下cuda版本 nvcc -V确认是11.3之后&#xff0c;要安装11.3对应的pytorch版本。 本机装pytorch1.12.1后面运行的时候会报错&#xff08;torch没有经过cuda编译&#xff09;&…...

关于Kubernetes不兼容Docker

本博客地址&#xff1a;https://security.blog.csdn.net/article/details/129153459 参考文献&#xff1a;https://www.cnblogs.com/1234roro/p/16892031.html 一、总结 总结起来就是一句话&#xff1a; k8s只是弃用了dockershim&#xff0c;并不是弃用了整个Docker&#xf…...

MySql_基础篇_从下载mysq到数据库操作

本笔记基于b站up主黑马程序员的视频整理 用于记录与回顾 数据库相关概念 数据库 DS 存储数据的仓库 数据库管理系统 DSMS 操纵和管理数据库的大型软件 sql 操作关系型数据库的编程语言是一套标准 mysql 默认端口号是3306 下载数据库 社区版下载 https://dev.mysql.com/…...

当我尝试问了chatGPT几个问题之后,我感到了危机......

问题1 &#xff1a; vue3 框架的bug有哪些&#xff1f; 组件内部的状态更新不及时&#xff1a;由于Vue3使用Proxy来实现响应式&#xff0c;在某些情况下&#xff0c;组件内部的状态更新不及时&#xff0c;导致组件的渲染出现问题。 组件内部的数据更新不及时&#xff1a;由于V…...

大数据学习栈记——Neo4j的安装与使用

本文介绍图数据库Neofj的安装与使用&#xff0c;操作系统&#xff1a;Ubuntu24.04&#xff0c;Neofj版本&#xff1a;2025.04.0。 Apt安装 Neofj可以进行官网安装&#xff1a;Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...

51c自动驾驶~合集58

我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留&#xff0c;CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制&#xff08;CCA-Attention&#xff09;&#xff0c;…...

day52 ResNet18 CBAM

在深度学习的旅程中&#xff0c;我们不断探索如何提升模型的性能。今天&#xff0c;我将分享我在 ResNet18 模型中插入 CBAM&#xff08;Convolutional Block Attention Module&#xff09;模块&#xff0c;并采用分阶段微调策略的实践过程。通过这个过程&#xff0c;我不仅提升…...

质量体系的重要

质量体系是为确保产品、服务或过程质量满足规定要求&#xff0c;由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面&#xff1a; &#x1f3db;️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限&#xff0c;形成层级清晰的管理网络&#xf…...

佰力博科技与您探讨热释电测量的几种方法

热释电的测量主要涉及热释电系数的测定&#xff0c;这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中&#xff0c;积分电荷法最为常用&#xff0c;其原理是通过测量在电容器上积累的热释电电荷&#xff0c;从而确定热释电系数…...

AI病理诊断七剑下天山,医疗未来触手可及

一、病理诊断困局&#xff1a;刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断"&#xff0c;医生需通过显微镜观察组织切片&#xff0c;在细胞迷宫中捕捉癌变信号。某省病理质控报告显示&#xff0c;基层医院误诊率达12%-15%&#xff0c;专家会诊…...

算法:模拟

1.替换所有的问号 1576. 替换所有的问号 - 力扣&#xff08;LeetCode&#xff09; ​遍历字符串​&#xff1a;通过外层循环逐一检查每个字符。​遇到 ? 时处理​&#xff1a; 内层循环遍历小写字母&#xff08;a 到 z&#xff09;。对每个字母检查是否满足&#xff1a; ​与…...

【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题

【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要&#xff1a; 近期&#xff0c;在使用较新版本的OpenSSH客户端连接老旧SSH服务器时&#xff0c;会遇到 "no matching key exchange method found"​, "n…...

从面试角度回答Android中ContentProvider启动原理

Android中ContentProvider原理的面试角度解析&#xff0c;分为​​已启动​​和​​未启动​​两种场景&#xff1a; 一、ContentProvider已启动的情况 1. ​​核心流程​​ ​​触发条件​​&#xff1a;当其他组件&#xff08;如Activity、Service&#xff09;通过ContentR…...

spring Security对RBAC及其ABAC的支持使用

RBAC (基于角色的访问控制) RBAC (Role-Based Access Control) 是 Spring Security 中最常用的权限模型&#xff0c;它将权限分配给角色&#xff0c;再将角色分配给用户。 RBAC 核心实现 1. 数据库设计 users roles permissions ------- ------…...