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 为了偷懒,我封装了一个自适配的数据单位转换工具类
前言 平时做一些统计数据,经常从数据库或者是从接口获取出来的数据,单位是跟业务需求不一致的。 比如, 我们拿出来的 分, 实际上要是元 又比如,我们拿到的数据需要 乘以100 返回给前端做 百分比展示 又比如ÿ…...
正则表达式
当我们需要对字符串进行判断的时候,使用正则表达式能大大提高编程效率。比如,当我们需要找出所有“像邮箱”的字符串(包含"" "." ".com",且顺序一致),我们需要一个某种模式的…...
java进阶Map 集合
通过之前的学习我们知道Map是一个双列集合,就是以键值对的形式进行数据存储 java进阶—集合 Map 下面有 三个子接口,HashMap , HashTable 以及 TreeMap 提醒一点:Map不是Collection下的集合,Collection是单列集合&am…...
Java 方法超详细整理,适合新手入门
目录 一、什么是方法呢? 二、方法的优点 三、带返回值方法定义 语法: 示例: 四、带返回值方法调用 语法: 示例: 五、结果示例 一、什么是方法呢? Java方法是语句的集合,它们在一起执行…...
软考学习笔记(题目知识记录)
答案为 概要设计阶段 本题涉及软件工程的概念 软件工程的任务是基于需求分析的结果建立各种设计模型,给出问题的解决方案 软件设计可以分为两个阶段: 概要设计阶段和详细设计阶段 结构化设计方法中,概要设计阶段进行软件体系结构的设计&…...
2021.3.3idea创建Maven项目
首先new - project - 找到Maven 然后按下图操作:先勾选使用骨架,再找到Maven-archetype-webapp,选中,然后next填写自己想要创建的项目名,然后选择自己的工作空间①、选择自己下载的Maven插件②、选择选择Maven里的sett…...
ASP.NET MVC | 创建应用程序
目录 首先 NO.1 No.2 App_Data 文件夹 Content 文件夹 Controllers 文件夹 Models 文件夹 Views 文件夹 Scripts 文件夹 最后 首先 一步一步的来,电脑上需要安装vs2019软件,版本高低无所谓,就是功能多少而已。 长这样的࿰…...
思科设备命令讲解(超基础)
♥️作者:小刘在C站 ♥️个人主页:小刘主页 ♥️每天分享云计算网络运维课堂笔记,努力不一定有收获,但一定会有收获加油!一起努力,共赴美好人生! ♥️夕阳下,是最美的绽放࿰…...
Qt-FFmpeg开发-保存视频流裸流(11)
Qt-FFmpeg开发-保存视频流裸流📀 文章目录Qt-FFmpeg开发-保存视频流裸流📀1、概述📸2、实现效果💽3、FFmpeg保存裸流代码流程💡4、主要代码🔍5、完整源代码📑更多精彩内容👉个人内容…...
Zebec官方辟谣“我们与Protradex没有任何关系”
近日,流支付协议Zebec Protocol在其官方推特上,发表了一个辟谣澄清声明。该条推特推文表示,“Zebec 与 Protradex 没有任何关系或产生关联。他们( Protradex )声称Zebec 生态正在支持他们,但这是错误的。随…...
BMS电池管理系统中的各种算法介绍
BMS电池管理系统 是一种用于电池组中的单个电池管理的系统,以确保其安全性、寿命和性能。BMS系统通过采集电池信息并对其进行分析,以确保电池组的正常运行。在BMS电池管理系统中,涉及到了许多算法,包括最大功率点追踪算法、SOC计算…...
stack Overflow 的使用
文章目录优雅的搜索1.1要在特定标签内搜索1.2搜索特定的短语1.3 限定检索位置1.4选择性屏蔽优雅的筛选搜索结果1. 返回的搜索筛选2. 特定时间段的帖子3. 精准的BOOL判断4. 其他的例子优雅的搜索 其实,在Stack OverFlow上的搜索方式,与国内的百度没什么大…...
Vue 在for循环中动态添加类名及style样式集合
介绍 在vue的 for 循环中,经常会使用到动态添加类名或者样式的情况,实现给当前的选中的 div 添加不同的样式。 动态添加类名 提示: 所有动态添加的类名,放在表达式里都需要添加引号,进行包裹。 通过 对象 的形式&a…...
Maven的优势
作用一:个人理解maven主要是用来解决导入java类依赖的jar,编译java项目主要问题。(最早手动导入jar,使用Ant之类的编译java项目)以pom.xml文件中dependency属性管理依赖的jar包,而jar包包含class文件和一些必要的资源文件。当然它可以构建项目…...
uboot,内核,根文件系统的作用
复习了下uboot,内核,根文件系统,简单概括下三者的主要内容。 1 uboot uboot的目的:启动内核。 uboot的功能可以分为两个阶段任务。 1.2.1 uboot第一阶段 uboot第一阶段主要负责硬件相关的初始化,主要在cpu/arm920…...
Vue3通透教程【四】Vue3组合API初体验
文章目录🌟 写在前面🌟 组合式 API 是什么?🌟 直观组合式API🌟 写在最后🌟 写在前面 专栏介绍: 凉哥作为 Vue 的忠实 粉丝输出过大量的 Vue 文章,应粉丝要求开始更新 Vue3 的相关技…...
coco数据集训练nanodet详细流程
github地址 首先要配置环境 conda create -n nanodet python3.8 -y conda activate nanodet确认一下cuda版本 nvcc -V确认是11.3之后,要安装11.3对应的pytorch版本。 本机装pytorch1.12.1后面运行的时候会报错(torch没有经过cuda编译)&…...
关于Kubernetes不兼容Docker
本博客地址:https://security.blog.csdn.net/article/details/129153459 参考文献:https://www.cnblogs.com/1234roro/p/16892031.html 一、总结 总结起来就是一句话: k8s只是弃用了dockershim,并不是弃用了整个Docker…...
MySql_基础篇_从下载mysq到数据库操作
本笔记基于b站up主黑马程序员的视频整理 用于记录与回顾 数据库相关概念 数据库 DS 存储数据的仓库 数据库管理系统 DSMS 操纵和管理数据库的大型软件 sql 操作关系型数据库的编程语言是一套标准 mysql 默认端口号是3306 下载数据库 社区版下载 https://dev.mysql.com/…...
当我尝试问了chatGPT几个问题之后,我感到了危机......
问题1 : vue3 框架的bug有哪些? 组件内部的状态更新不及时:由于Vue3使用Proxy来实现响应式,在某些情况下,组件内部的状态更新不及时,导致组件的渲染出现问题。 组件内部的数据更新不及时:由于V…...
使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
python/java环境配置
环境变量放一起 python: 1.首先下载Python Python下载地址:Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个,然后自定义,全选 可以把前4个选上 3.环境配置 1)搜高级系统设置 2…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...
用递归算法解锁「子集」问题 —— LeetCode 78题解析
文章目录 一、题目介绍二、递归思路详解:从决策树开始理解三、解法一:二叉决策树 DFS四、解法二:组合式回溯写法(推荐)五、解法对比 递归算法是编程中一种非常强大且常见的思想,它能够优雅地解决很多复杂的…...
C# winform教程(二)----checkbox
一、作用 提供一个用户选择或者不选的状态,这是一个可以多选的控件。 二、属性 其实功能大差不差,除了特殊的几个外,与button基本相同,所有说几个独有的 checkbox属性 名称内容含义appearance控件外观可以变成按钮形状checkali…...
统计学(第8版)——统计抽样学习笔记(考试用)
一、统计抽样的核心内容与问题 研究内容 从总体中科学抽取样本的方法利用样本数据推断总体特征(均值、比率、总量)控制抽样误差与非抽样误差 解决的核心问题 在成本约束下,用少量样本准确推断总体特征量化估计结果的可靠性(置…...
Linux 内存管理调试分析:ftrace、perf、crash 的系统化使用
Linux 内存管理调试分析:ftrace、perf、crash 的系统化使用 Linux 内核内存管理是构成整个内核性能和系统稳定性的基础,但这一子系统结构复杂,常常有设置失败、性能展示不良、OOM 杀进程等问题。要分析这些问题,需要一套工具化、…...
【汇编逆向系列】六、函数调用包含多个参数之多个整型-参数压栈顺序,rcx,rdx,r8,r9寄存器
从本章节开始,进入到函数有多个参数的情况,前面几个章节中介绍了整型和浮点型使用了不同的寄存器在进行函数传参,ECX是整型的第一个参数的寄存器,那么多个参数的情况下函数如何传参,下面展开介绍参数为整型时候的几种情…...
