当前位置: 首页 > 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…...

多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验

一、多模态商品数据接口的技术架构 &#xff08;一&#xff09;多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如&#xff0c;当用户上传一张“蓝色连衣裙”的图片时&#xff0c;接口可自动提取图像中的颜色&#xff08;RGB值&…...

Neo4j 集群管理:原理、技术与最佳实践深度解析

Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)

漏洞概览 漏洞名称&#xff1a;Apache Flink REST API 任意文件读取漏洞CVE编号&#xff1a;CVE-2020-17519CVSS评分&#xff1a;7.5影响版本&#xff1a;Apache Flink 1.11.0、1.11.1、1.11.2修复版本&#xff1a;≥ 1.11.3 或 ≥ 1.12.0漏洞类型&#xff1a;路径遍历&#x…...

嵌入式常见 CPU 架构

架构类型架构厂商芯片厂商典型芯片特点与应用场景PICRISC (8/16 位)MicrochipMicrochipPIC16F877A、PIC18F4550简化指令集&#xff0c;单周期执行&#xff1b;低功耗、CIP 独立外设&#xff1b;用于家电、小电机控制、安防面板等嵌入式场景8051CISC (8 位)Intel&#xff08;原始…...

go 里面的指针

指针 在 Go 中&#xff0c;指针&#xff08;pointer&#xff09;是一个变量的内存地址&#xff0c;就像 C 语言那样&#xff1a; a : 10 p : &a // p 是一个指向 a 的指针 fmt.Println(*p) // 输出 10&#xff0c;通过指针解引用• &a 表示获取变量 a 的地址 p 表示…...

从零开始了解数据采集(二十八)——制造业数字孪生

近年来&#xff0c;我国的工业领域正经历一场前所未有的数字化变革&#xff0c;从“双碳目标”到工业互联网平台的推广&#xff0c;国家政策和市场需求共同推动了制造业的升级。在这场变革中&#xff0c;数字孪生技术成为备受关注的关键工具&#xff0c;它不仅让企业“看见”设…...

LangChain【6】之输出解析器:结构化LLM响应的关键工具

文章目录 一 LangChain输出解析器概述1.1 什么是输出解析器&#xff1f;1.2 主要功能与工作原理1.3 常用解析器类型 二 主要输出解析器类型2.1 Pydantic/Json输出解析器2.2 结构化输出解析器2.3 列表解析器2.4 日期解析器2.5 Json输出解析器2.6 xml输出解析器 三 高级使用技巧3…...

【题解-洛谷】P10480 可达性统计

题目&#xff1a;P10480 可达性统计 题目描述 给定一张 N N N 个点 M M M 条边的有向无环图&#xff0c;分别统计从每个点出发能够到达的点的数量。 输入格式 第一行两个整数 N , M N,M N,M&#xff0c;接下来 M M M 行每行两个整数 x , y x,y x,y&#xff0c;表示从 …...

二叉树-144.二叉树的前序遍历-力扣(LeetCode)

一、题目解析 对于递归方法的前序遍历十分简单&#xff0c;但对于一位合格的程序猿而言&#xff0c;需要掌握将递归转化为非递归的能力&#xff0c;毕竟递归调用的时候会调用大量的栈帧&#xff0c;存在栈溢出风险。 二、算法原理 递归调用本质是系统建立栈帧&#xff0c;而非…...

第22节 Node.js JXcore 打包

Node.js是一个开放源代码、跨平台的、用于服务器端和网络应用的运行环境。 JXcore是一个支持多线程的 Node.js 发行版本&#xff0c;基本不需要对你现有的代码做任何改动就可以直接线程安全地以多线程运行。 本文主要介绍JXcore的打包功能。 JXcore 安装 下载JXcore安装包&a…...