若依(微服務版)導入導出關聯動態字典值

来源:https://www.cnblogs.com/vip9527/archive/2023/07/28/17586947.html
-Advertisement-
Play Games

前言 需求背景: 業務系統需要做EXCEL的導入和導出功能,某些欄位是系統字典值,改造之前只能應用@Excel註解的combo屬性來做下拉限制,用readConverterExp屬性來做表達式實際值和顯示值的轉換。 每當運維人員在系統增加一個字典值的時候,都要來修改代碼,太麻煩了。 期望效果: 能夠 ...


前言

需求背景:
     業務系統需要做EXCEL的導入和導出功能,某些欄位是系統字典值,改造之前只能應用@Excel註解的combo屬性來做下拉限制,用readConverterExp屬性來做表達式實際值和顯示值的轉換。

每當運維人員在系統增加一個字典值的時候,都要來修改代碼,太麻煩了。

期望效果:

能夠根據系統字典值,動態限制下拉框內容,動態進行實際值和顯示值的轉換,無需每次都修改代碼。同時保留readConverterExp和combo屬性的效果

單體版若依

單體版若依網上有很多,這裡不在贅述,上連接

  • 文章1:

https://blog.csdn.net/wangmj518/article/details/128438841?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-128438841-blog-126104814.235%5Ev38%5Epc_relevant_sort_base3&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-128438841-blog-126104814.235%5Ev38%5Epc_relevant_sort_base3&utm_relevant_index=2

  • 文章2:

https://blog.csdn.net/wcnmbbbb/article/details/117952232

兩個文章內容基本一樣,下邊的微服務版,也是這種思路,不過需要做一點特殊處理

微服務版若依

在網上找了很多都是單體版的解決方式,微服務版目前,官方目前還沒有給出解決思路,這裡就套用一下單體版的修改做一點修改

整體思路

  1. 修改或者新建一個新的@Excel進行修改

  2. 轉移或者新建ExcelUtil.java,併進行修改

  3. 新建DictUtils.java

  4. 想辦法讓ExcelUtil.java中可以調用DictUtils.java(坑點)

  5. 修改RedisService.java,添加方法(坑點)

    image

    image

    image

套用單體版修改會有兩大坑:

坑一

這裡主要是針對上述中的第4點( 想辦法讓ExcelUtil.java中可以調用DictUtils.java )

若依(微服務版)把@Excel註解寫在了[ruoyi-common-core] 模塊,這是一個底層模塊,但是我們需要拿緩存,即需要操作redis,

把單體版的操作,轉移到[ruoyi-common-redis]模塊

所以我們有兩種思路解決

  • 思路一: 不如大膽的在別的模塊再搞一個@Excel

優點:不影響原來的註解,完全獨立,改動小

缺點: 以後使用註解的時候要註意導包不要導錯,使用ExcelUtil也要註意要使用自己新創建的

  • 思路二: 轉移 [ExcelUtil.java]文件位置到可以操作的轉移到[ruoyi-common-redis]模塊下

優點:直接在原註解上進行操作,不會出現兩個@Excel註解

缺點:需要把我們之前所有引用過ExcelUtil.java的包都改成自己的,改動文件很多

選什麼自己決定把,這裡就不做推薦了,最後的完整代碼是使用的思路一

坑二

這裡針對的上述的第5點(修改RedisService.java,添加方法(坑點))

當我們解決完坑一的時候,就遇到了坑二

image

其中DictUtils.java有一個方法

     /**
      * 獲取字典緩存
      *
      * @param key 參數鍵
      * @return dictDatas 字典數據列表
      */
     public static List<SysDictData> getDictCache(String key)
    {
         Object cacheObj = SpringUtils.getBean(RedisService.class).getCacheObject(getCacheKey(key));
         if (StringUtils.isNotNull(cacheObj))
        {
             // 這一步在轉類型 會報轉換異常 失敗原因主要是redis里有一個 @type 會規定必須轉換的包一致
             List<SysDictData> dictDatas = StringUtils.cast(cacheObj);
             return dictDatas;
        }
         return null;
    }

知道了原因,就好搞了,具體解決辦法網上有很多

https://blog.csdn.net/weixin_42169734/article/details/119609957

 
 // 修改getDictCache
     public static List<SysDictData> getDictCache(String key)
    {
         List<JSONArray> cacheListString = SpringUtils.getBean(RedisService.class).getCacheListString(getCacheKey(key));
 
         if (StringUtils.isNotNull(cacheListString)){
             JSONArray objects = cacheListString.get(0);
             List<SysDictData> sysDictData = JSONArray.parseArray(objects.toJSONString(), SysDictData.class);
             return sysDictData;
        }
         return null;
    }

// 在RedisService類中方法中添加getCacheListString方法

     public  List<JSONArray>   getCacheListString(final String key)
    {
         return  redisTemplate.opsForValue().multiGet(Arrays.asList(key));
    }

完整代碼

Excel註解

 /**
  * 自定義導出Excel數據註解
  *
  */
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.FIELD)
 public @interface Excel
 {
     /**
      * 導出時在excel中排序
      */
     public int sort() default Integer.MAX_VALUE;
 
     /**
      * 導出到Excel中的名字.
      */
     public String name() default "";
 
     /**
      * 日期格式, 如: yyyy-MM-dd
      */
     public String dateFormat() default "";
 
     /**
      * 讀取內容轉表達式 (如: 0=男,1=女,2=未知)
      */
     public String readConverterExp() default "";
     /**
      * 如果是字典類型,請設置字典的type值
      * >>>>>>>特別提醒,如若配置該欄位,則“combo”和“readConverterExp”不啟用
      */
     public String dictType() default "";
     /**
      * 分隔符,讀取字元串組內容
      */
     public String separator() default ",";
 
     /**
      * BigDecimal 精度 預設:-1(預設不開啟BigDecimal格式化)
      */
     public int scale() default -1;
 
     /**
      * BigDecimal 舍入規則 預設:BigDecimal.ROUND_HALF_EVEN
      */
     public int roundingMode() default BigDecimal.ROUND_HALF_EVEN;
 
     /**
      * 導出類型(0數字 1字元串)
      */
     public ColumnType cellType() default ColumnType.STRING;
 
     /**
      * 導出時在excel中每個列的高度 單位為字元
      */
     public double height() default 14;
 
     /**
      * 導出時在excel中每個列的寬 單位為字元
      */
     public double width() default 16;
 
     /**
      * 文字尾碼,如% 90 變成90%
      */
     public String suffix() default "";
 
     /**
      * 當值為空時,欄位的預設值
      */
     public String defaultValue() default "";
 
     /**
      * 提示信息
      */
     public String prompt() default "";
 
     /**
      * 設置只能選擇不能輸入的列內容.
      */
     public String[] combo() default {};
 
     /**
      * 是否導出數據,應對需求:有時我們需要導出一份模板,這是標題需要但內容需要用戶手工填寫.
      */
     public boolean isExport() default true;
 
     /**
      * 另一個類中的屬性名稱,支持多級獲取,以小數點隔開
      */
     public String targetAttr() default "";
 
     /**
      * 是否自動統計數據,在最後追加一行統計數據總和
      */
     public boolean isStatistics() default false;
 
     /**
      * 導出欄位對齊方式(0:預設;1:靠左;2:居中;3:靠右)
      */
     Align align() default Align.AUTO;
 
     public enum Align
    {
         AUTO(0), LEFT(1), CENTER(2), RIGHT(3);
         private final int value;
 
         Align(int value)
        {
             this.value = value;
        }
 
         public int value()
        {
             return this.value;
        }
    }
 
     /**
      * 欄位類型(0:導出導入;1:僅導出;2:僅導入)
      */
     Type type() default Type.ALL;
 
     public enum Type
    {
         ALL(0), EXPORT(1), IMPORT(2);
         private final int value;
 
         Type(int value)
        {
             this.value = value;
        }
 
         public int value()
        {
             return this.value;
        }
    }
 
     public enum ColumnType
    {
         NUMERIC(0), STRING(1), IMAGE(2);
         private final int value;
 
         ColumnType(int value)
        {
             this.value = value;
        }
 
         public int value()
        {
             return this.value;
        }
    }
 }

 

DictUtils.java

 /**
  * 字典工具類
  *
  */
 public class DictUtils
 {
     /**
      * 設置字典緩存
      *
      * @param key 參數鍵
      * @param dictDatas 字典數據列表
      */
     public static void setDictCache(String key, List<SysDictData> dictDatas)
    {
         SpringUtils.getBean(RedisService.class).setCacheObject(getCacheKey(key), dictDatas);
    }
 
     /**
      * 獲取字典緩存
      *
      * @param key 參數鍵
      * @return dictDatas 字典數據列表
      */
     public static List<SysDictData> getDictCache(String key)
    {
         List<JSONArray> cacheListString = SpringUtils.getBean(RedisService.class).getCacheListString(getCacheKey(key));
 
         if (StringUtils.isNotNull(cacheListString)){
             JSONArray objects = cacheListString.get(0);
             List<SysDictData> sysDictData = JSONArray.parseArray(objects.toJSONString(), SysDictData.class);
             return sysDictData;
        }
         return null;
    }
 
     /**
      * 清空字典緩存
      */
     public static void clearDictCache()
    {
         Collection<String> keys = SpringUtils.getBean(RedisService.class).keys(Constants.SYS_DICT_KEY + "*");
         SpringUtils.getBean(RedisService.class).deleteObject(keys);
    }
 
     /**
      * 設置cache key
      *
      * @param configKey 參數鍵
      * @return 緩存鍵key
      */
     public static String getCacheKey(String configKey)
    {
         return Constants.SYS_DICT_KEY + configKey;
    }
 
 
 
     /**
      * 根據指定的type ,value 獲取 label
      * @param value
      * @param type
      * @param defaultLabel 預設label
      * @return
      */
     public static String getDictLabelByTypeAndValue(String value, String type, String defaultLabel){
         if (StringUtils.isNotBlank(type) && StringUtils.isNotBlank(value)){
             for (SysDictData dict : getDictCache(type)){
                 if (type.equals(dict.getDictType()) && value.equals(dict.getDictValue())){
                     return dict.getDictLabel();
                }
            }
        }
         return defaultLabel;
    }
     /**
      * 根據type,label 獲取value
      *
      * @param label
      * @param type
      * @param
      * @return
      */
     public static String  getDictValueByTypeAndLabel(String label,String type,String defaultValue) {
         if (StringUtils.isNotBlank(type) && StringUtils.isNotBlank(label)){
             for (SysDictData dict : getDictCache(type)){
                 if(type.equals(dict.getDictType())  && label.equals(dict.getDictLabel())) {
                     return dict.getDictValue();
                }
            }
        }
         return defaultValue;
    }
 
     /**
      * 根據 type 將 lable組成 string數組
      * @param type
      * @return
      */
     public static String[] getLabelArr(String type) {
         String[] arr0 = new String[0];
         if (StringUtils.isNotBlank(type)) {
             List<SysDictData> dictList = getDictCache(type);
             String[] strArr = new String[dictList.size()];
             for (int i = 0 ; i < dictList.size() ; i ++) {
                 strArr[i] = dictList.get(i).getDictLabel();
            }
             return strArr;
        }
         return arr0;
    }
 
 }

ExcelUtil.java

 /**
  * Excel相關處理
  *
  */
 public class ExcelUtil<T>
 {
     private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class);
 
     /**
      * Excel sheet最大行數,預設65536
      */
     public static final int sheetSize = 65536;
 
     /**
      * 工作表名稱
      */
     private String sheetName;
 
     /**
      * 導出類型(EXPORT:導出數據;IMPORT:導入模板)
      */
     private Excel.Type type;
 
     /**
      * 工作薄對象
      */
     private Workbook wb;
 
     /**
      * 工作表對象
      */
     private Sheet sheet;
 
     /**
      * 樣式列表
      */
     private Map<String, CellStyle> styles;
 
     /**
      * 導入導出數據列表
      */
     private List<T> list;
 
     /**
      * 註解列表
      */
     private List<Object[]> fields;
 
     /**
      * 最大高度
      */
     private short maxHeight;
 
     /**
      * 統計列表
      */
     private Map<Integer, Double> statistics = new HashMap<Integer, Double>();
 
     /**
      * 數字格式
      */
     private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00");
 
     /**
      * 實體對象
      */
     public Class<T> clazz;
 
     public ExcelUtil(Class<T> clazz)
    {
         this.clazz = clazz;
    }
 
     public void init(List<T> list, String sheetName, Excel.Type type)
    {
         if (list == null)
        {
             list = new ArrayList<T>();
        }
         this.list = list;
         this.sheetName = sheetName;
         this.type = type;
         createExcelField();
         createWorkbook();
    }
 
     /**
      * 對excel表單預設第一個索引名轉換成list
      *
      * @param is 輸入流
      * @return 轉換後集合
      */
     public List<T> importExcel(InputStream is) throws Exception
    {
         return importExcel(StringUtils.EMPTY, is);
    }
 
     /**
      * 對excel表單指定表格索引名轉換成list
      *
      * @param sheetName 表格索引名
      * @param is 輸入流
      * @return 轉換後集合
      */
     public List<T> importExcel(String sheetName, InputStream is) throws Exception
    {
         this.type = Excel.Type.IMPORT;
         this.wb = WorkbookFactory.create(is);
         List<T> list = new ArrayList<T>();
         Sheet sheet = null;
         if (StringUtils.isNotEmpty(sheetName))
        {
             // 如果指定sheet名,則取指定sheet中的內容.
             sheet = wb.getSheet(sheetName);
        }
         else
        {
             // 如果傳入的sheet名不存在則預設指向第1個sheet.
             sheet = wb.getSheetAt(0);
        }
 
         if (sheet == null)
        {
             throw new IOException("文件sheet不存在");
        }
 
         int rows = sheet.getPhysicalNumberOfRows();
 
         if (rows > 0)
        {
             // 定義一個map用於存放excel列的序號和field.
             Map<String, Integer> cellMap = new HashMap<String, Integer>();
             // 獲取表頭
             Row heard = sheet.getRow(0);
             for (int i = 0; i < heard.getPhysicalNumberOfCells(); i++)
            {
                 Cell cell = heard.getCell(i);
                 if (StringUtils.isNotNull(cell))
                {
                     String value = this.getCellValue(heard, i).toString();
                     cellMap.put(value, i);
                }
                 else
                {
                     cellMap.put(null, i);
                }
            }
             // 有數據時才處理 得到類的所有field.
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 理解二叉樹深度定義,思路剖析後可以使用深度優先搜索,這麼高大上的名字背後使用的是遞歸函數,遞歸三要素還可以回憶到嗎?還是已經拋到九霄雲外了! ...
  • **原文鏈接:** [Go 語言 select 都能做什麼?](https://mp.weixin.qq.com/s/YyyMzYxMi8I4HEaxzy4c7g) 在 Go 語言中,`select` 是一個關鍵字,用於監聽和 `channel` 有關的 IO 操作。 通過 `select` 語句, ...
  • [TOC] # 前言 做嵌入式的上位機開發需要要用到Qt的,Qt是一個開源、跨平臺的程式和UI開發框架。我們使用Qt可以用Python或者C++進行開發,這裡我使用的全部都是C++,不涉及到Python。 # 一、Qt安裝 要學習Qt前先得學習一下如何安裝Qt,這裡安裝的是QtIDE,是Qt的集成開 ...
  • `json.load()`和`json.loads()`都是Python標準庫`json`模塊中用於處理JSON數據的方法,二者的作用都是將JSON數據轉換為Python數據類型,它們之間的區別如下: ### 1. `json.load()`是從文件中讀取JSON數據 `json.load()`用於 ...
  • # 批處理 - **基本介紹:** 1. 當需要成批插入或者更新記錄時。可以採用Java的批量更新機制,這一機制允許多條語句一次性提交給資料庫批量處理。通常情況下比單獨提交處理更有效率。 2. JDBC的批量處理語句包括下麵方法: - addBatch():添加需要批量處理的SQL語句或參數; - ...
  • 公眾號服務號每個月只能群發推送四次文章,我們可以使用模板消息為公眾號粉絲推送信息 下麵是使用golang實現的模板消息發送類庫封裝,輕鬆實現模板消息發送 wechat.go package lib import ( "github.com/silenceper/wechat/v2" "github. ...
  • ## 教程簡介 Drupal是使用PHP語言編寫的開源內容管理框架(CMF),它由內容管理系統(CMS)和PHP開發框架(Framework)共同構成,在GPL2.0及更新協議下發佈。連續多年榮獲全球最佳CMS大獎,是基於PHP語言最著名的WEB應用程式。截止2011年底,共有13,802位WEB專 ...
  • CompletableFuture對象是JDK1.8版本新引入的類,這個類實現了兩個介面,一個是Future介面,一個是CompletionStage介面。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...