主頁 > 後端開發 > 專案終于上了這個資料單位轉換工具類,金額轉換太優雅了!

專案終于上了這個資料單位轉換工具類,金額轉換太優雅了!

2023-06-26 07:58:23 後端開發

來源:blog.csdn.net/qq_35387940/article/details/129167329

前言

平時做一些統計資料,經常從資料庫或者是從介面獲取出來的資料,單位是跟業務需求不一致的,

  • 比如, 我們拿出來的 分, 實際上要是元

  • 又比如,我們拿到的資料需要 乘以100 回傳給前端做 百分比展示

  • 又比如, 千分比轉換

  • 又比如,拿出來的金額需要變成 萬為單位

  • 又比如,需要保留2位小數

  • ......

  • 等等等等

平時我們怎么搞?

很多時候拿到的是一個資料集合list,就需要去遍歷然后根據每個DTO的屬性去做相關單位轉換,

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

就像這樣:

所以,如果通過反射自動匹配出來一些操作轉換,是不是就看代碼看起來舒服一點,人也輕松一點,

推薦一個開源免費的 Spring Boot 實戰專案:

https://github.com/javastacks/spring-boot-best-practice

答案:是的

然后,我就搞了,

正文

本篇內容簡要:

  1. 初步的封裝,通過map去標記需要轉換的 類屬性欄位
  2. 進一步的封裝, 配合老朋友自定義注解搞事情

產品:

  • 支付總金額 換成萬 為單位, 方便運營統計 ;
  • 那個什么計數,要是百分比的 ;
  • 然后還有一個是千分比;
  • 另外,還有2個要保留2位小數;
  • 還有啊,那個,,,,,,

我:

別說了,喝口水吧,

拿到的資料都在這個DTO里面 :

開始封裝:

① 初步的封裝,通過map去標記需要轉換的 類屬性欄位

思路玩法:

  1. 通過反射拿出欄位
  2. 配合傳入的轉換標記Map 匹配哪些欄位需要操作
  3. 然后從map取出相關欄位的具體操作是什么,然后執行轉換操作
  4. 重新賦值

① 簡單弄個列舉,列出現在需求上的轉換操作型別

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:

拋磚引玉,傳遞‘玩’代碼思想,學編程,哎我就是玩,

近期熱文推薦:

1.1,000+ 道 Java面試題及答案整理(2022最新版)

2.勁爆!Java 協程要來了,,,

3.Spring Boot 2.x 教程,太全了!

4.別再寫滿屏的爆爆爆炸類了,試試裝飾器模式,這才是優雅的方式!!

5.《Java開發手冊(嵩山版)》最新發布,速速下載!

覺得不錯,別忘了隨手點贊+轉發哦!

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/555912.html

標籤:其他

上一篇:【python基礎】檔案-檔案路徑

下一篇:返回列表

標籤雲
其他(161561) Python(38248) JavaScript(25513) Java(18259) C(15238) 區塊鏈(8272) C#(7972) AI(7469) 爪哇(7425) MySQL(7266) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5875) 数组(5741) R(5409) Linux(5347) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4606) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2437) ASP.NET(2404) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) .NET技术(1984) HtmlCss(1971) 功能(1967) Web開發(1951) C++(1942) python-3.x(1918) 弹簧靴(1913) xml(1889) PostgreSQL(1881) .NETCore(1863) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • 專案終于上了這個資料單位轉換工具類,金額轉換太優雅了!

    來源:blog.csdn.net/qq_35387940/article/details/129167329 ## **前言** 平時做一些統計資料,經常從資料庫或者是從介面獲取出來的資料,單位是跟業務需求不一致的。 - 比如, 我們拿出來的 分, 實際上要是元 - 又比如,我們拿到的資料需要 乘以 ......

    uj5u.com 2023-06-26 07:58:23 more
  • 【python基礎】檔案-檔案路徑

    # 1.檔案路徑 我們發現不管是寫入還是寫出操作,我們提供的都是檔案名,其實這里準確說應該是檔案路徑。當我們簡單把檔案名傳遞給open函式時,Python將在當前執行程式的檔案所在的目錄中查找檔案名所代表的檔案。 根據組織檔案的方式,可能需要打開不在當前執行程式檔案所屬目錄中的檔案。如果此時我們把該 ......

    uj5u.com 2023-06-26 07:57:58 more
  • 前端學習C語言 - 初級指標

    ## 初級指標 本篇主要介紹:`指標和變數的關系`、指標型別、`指標的運算子`、空指標和野指標、`指標和陣列`、`指標和字串`、const 和指標、以及`gdb 除錯段錯誤`。 ### 基礎概念 > 指標是一種特殊的變數。存放地址的變數就是指標。 `int num = 1;` 會申請4個位元組的記憶體 ......

    uj5u.com 2023-06-26 07:57:45 more
  • PGO前瞻

    原文在[這里](https://go.dev/blog/pgo-preview)。 > 原文發布于2023年2月8日 在構建Go二進制檔案時,Go編譯器會進行優化,以盡可能生成性能最佳的二進制檔案。例如,常量傳播可以在編譯時對常量運算式進行求值,避免了運行時的計算開銷;逃逸分析可以避免對區域作用域對 ......

    uj5u.com 2023-06-26 07:57:39 more
  • 記錄一個在寫專案中遇到的Maven依賴無法匯入的問題

    # 記錄一個在寫專案中遇到的Maven依賴無法匯入的問題 專案是一個父專案做依賴管理,三個子專案,今天遇到一個問題: 子專案中匯入的依賴,怎么都匯入不進去,maven倉庫中已經有了,idea提示也沒有問題,如圖: ![](https://img2023.cnblogs.com/blog/295824 ......

    uj5u.com 2023-06-26 07:52:22 more
  • CouchDB教程_編程入門自學教程_菜鳥教程-免費教程分享

    ## 教程簡介 CouchDB 是一個開源的面向檔案的資料庫管理系統,可以通過 RESTful JavaScript Object Notation (JSON) API 訪問。術語 “Couch” 是 “Cluster Of Unreliable Commodity Hardware” 的首字母縮 ......

    uj5u.com 2023-06-26 07:52:12 more
  • B+ tree implemented in Java

    ## B+樹相關介紹 > B+樹是一棵**多叉排序樹**,即每個非葉子節點可以包含多個子節點,其整體結構呈扁平化,所以其非常適配于資料庫和作業系統的檔案系統中。且B+樹能夠保持資料的穩定有序,插入和洗掉都擁有較穩定的**對數時間復雜度**。 **B+樹的特性**:以 m 階為例,m 表示內部節點即非 ......

    uj5u.com 2023-06-26 07:52:08 more
  • 深入Python網路編程:從基礎到實踐

    **Python,作為一種被廣泛使用的高級編程語言,擁有許多優勢,其中之一就是它的網路編程能力。Python的強大網路庫如socket, requests, urllib, asyncio,等等,讓它在網路編程中表現優秀。本文將深入探討Python在網路編程中的應用,包括了基礎的socket編程,到 ......

    uj5u.com 2023-06-26 07:50:53 more
  • celery筆記七之周期/定時任務及crontab定義

    > 本文首發于公眾號:Hunter后端 > 原文鏈接:[celery筆記七之周期/定時任務及crontab定義](https://mp.weixin.qq.com/s/sNShaRbuM2gm2qn_codaTg) periodic task,即為周期,或者定時任務,比如說每天晚上零點零分需要運行一 ......

    uj5u.com 2023-06-26 07:45:03 more
  • 【python基礎】檔案-檔案路徑

    # 1.檔案路徑 我們發現不管是寫入還是寫出操作,我們提供的都是檔案名,其實這里準確說應該是檔案路徑。當我們簡單把檔案名傳遞給open函式時,Python將在當前執行程式的檔案所在的目錄中查找檔案名所代表的檔案。 根據組織檔案的方式,可能需要打開不在當前執行程式檔案所屬目錄中的檔案。如果此時我們把該 ......

    uj5u.com 2023-06-26 07:42:46 more