地區級聯參數傳遞註解式解決方案

来源:https://www.cnblogs.com/javashop-docs/archive/2020/02/12/12299556.html
-Advertisement-
Play Games

需求 地區數據往往是存在強上下級關係的一種數據結構,在電商系統中是比較常應用到的,比如北京的下級地區只有海澱區、通州區……,而不會是太原市,而且在開發人員傳遞地區值的時候往往要傳遞很多的值,比如省、市、區、鎮、省Id,市id、區id、鎮id,這樣影響了代碼的美觀性及校驗強上下級關係代碼的複雜性。基於 ...


需求

  地區數據往往是存在強上下級關係的一種數據結構,在電商系統中是比較常應用到的,比如北京的下級地區只有海澱區、通州區……,而不會是太原市,而且在開發人員傳遞地區值的時候往往要傳遞很多的值,比如省、市、區、鎮、省Id,市id、區id、鎮id,這樣影響了代碼的美觀性及校驗強上下級關係代碼的複雜性。基於這樣的問題要求我們必須要實現一種可以簡潔傳遞參數並且可以實現地區格式化的一種格式化器。這樣衍生出了我們基於Spring Formatter地區自動化格式的一種格式化器

預期效果

常規做法的地區參數傳遞:

?province_id=1&county_id=2&city_id=3&town_id=4

  

在controller接收參數時:

@PostMapping
public Result someMethod(Integer provinceId,Integer countyId,Integer cityId,Integer townId){ //街道id不為空找到級聯的父 if(townId!=null){ //找到區 countyId=findCounty(townId) //找到市 cityId=findCity(countyId); //找到省 provinceId=findProvince(cityId); } //如果是只保存到區的需求,也要依次尋找級聯父 if(countyId!=null){ //找到市 cityId=findCity(countyId); //找到省 provinceId=findCity(cityId); } //如果是保存到市也是同樣,不在贅述 }

 

使用註解後效果:

參數傳遞:

xxx?region=3

 

在controller接收參數時

 

public result someMethod(@RegionFormat  Region region) {

//這裡的region對象中的省市區街道已經按相應的級別對應好,直接使用即可
if(region.getProvinceProId!=null){ // 保存省id}
if(region.getCityId!=null){ // 保存市id}
if(region.getCountyId=!=null{//保存區id}
if(region.getTwonId=!=null{//保存街道id}
}  

  

根據上述需求,如下是基於javashop電商系統中,地區參數接收註解的架構和實現:

類圖

  

 

 

1、 RegionFormatter地區格式化器實現RegionFomat註解介面,實現地區轉換器方法pase方法,傳入地區id和地區語言,最後將轉換後的數據輸出。如下代碼

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
public @interface RegionFormat {

}

  


public class RegionFormatter implements Formatter<Region> {

    private RegionsClient regionsClient;

    public RegionFormatter(RegionsClient regionsClient) {
        this.regionsClient = regionsClient;
    }

    public RegionFormatter() {

    }

    /**
     * 地區轉換器
     *
     * @param regionId 地區id(只允許第三級或者第四級地區id)
     * @param locale   國際化US
     * @return 地區對象
     * @throws ParseException
     */
    @Override
    public Region parse(String regionId, Locale locale) throws ParseException {
        Regions regions = regionsClient.getModel(Integer.valueOf(regionId));
        if (regions == null || regions.getRegionGrade() < 3) {
            throw new IllegalArgumentException("地區不合法,請聯繫管理員");
        }
        //根據底層地區id反推出上級id
        String regionPath = regions.getRegionPath();
        regionPath = regionPath.substring(1, regionPath.length());
        String[] regionPathArray = regionPath.split(",");
        //給地區賦值
        List rList = new ArrayList();
        for (String path : regionPathArray) {
            Regions region = regionsClient.getModel(Integer.valueOf(path));
            if (regions == null) {
                throw new IllegalArgumentException("地區不合法,請聯繫管理員");
            }
            rList.add(region);
        }
        return this.createRegion(rList);
    }

    /**
     * 組織地區數據
     *
     * @param list 地區集合
     * @return 地區
     */
    private Region createRegion(List<Regions> list) {
        //將地區數據組織好存入Region對象
        Region region = new Region();
        region.setProvinceId(list.get(0).getId());
        region.setProvince(list.get(0).getLocalName());
        region.setCityId(list.get(1).getId());
        region.setCity(list.get(1).getLocalName());
        region.setCountyId(list.get(2).getId());
        region.setCounty(list.get(2).getLocalName());
        //如果地區數據為四級,為第四級地區賦值
        if (list.size() == 4) {
            region.setTown(list.get(3).getLocalName());
            region.setTownId(list.get(3).getId());
        } else {
            region.setTown("");
            region.setTownId(0);
        }
        return region;
    }

    /**
     * 將格式化的地區toString輸出
     *
     * @param object 地區對象
     * @param locale 國際化US
     * @return
     */
    @Override
    public String print(Region object, Locale locale) {
        return object.toString();
    }


}

2、 RegionFormatAnnotationFormatterFactory地區格式化工廠實現AnnotationFormatterFactory自定義格式工廠介面,將要實現的自動註解RegionFomat傳入。如下代碼:


public class RegionFormatAnnotationFormatterFactory implements AnnotationFormatterFactory<RegionFormat> {

    /**
     * 獲取被註解對象的類型
     *
     * @return
     */
    @Override
    public Set<Class<?>> getFieldTypes() {
        return new HashSet<Class<?>>(asList(Region.class));
    }

    /**
     * 獲取輸出對象
     *
     * @param annotation 註解實例
     * @param fieldType  被註解欄位的類型
     * @return 地區格式化後輸出的對象
     */
    @Override
    public Printer<?> getPrinter(RegionFormat annotation, Class<?> fieldType) {
        return new RegionFormatter();
    }

    /**
     * 獲取解析器
     *
     * @param annotation 註解實例
     * @param fieldType  被註解欄位的類型
     * @return 地區格式化後對象
     */
    @Override
    public Parser<?> getParser(RegionFormat annotation, Class<?> fieldType) {
        return new RegionFormatter();
    }
}

3、 WebConfig實現WebMvcConfigurer,註冊自定義的地區格式化器。如下代碼:


public class RegionFormatAnnotationFormatterFactory implements AnnotationFormatterFactory<RegionFormat> {

    /**
     * 獲取被註解對象的類型
     *
     * @return
     */
    @Override
    public Set<Class<?>> getFieldTypes() {
        return new HashSet<Class<?>>(asList(Region.class));
    }

    /**
     * 獲取輸出對象
     *
     * @param annotation 註解實例
     * @param fieldType  被註解欄位的類型
     * @return 地區格式化後輸出的對象
     */
    @Override
    public Printer<?> getPrinter(RegionFormat annotation, Class<?> fieldType) {
        return new RegionFormatter();
    }

    /**
     * 獲取解析器
     *
     * @param annotation 註解實例
     * @param fieldType  被註解欄位的類型
     * @return 地區格式化後對象
     */
    @Override
    public Parser<?> getParser(RegionFormat annotation, Class<?> fieldType) {
        return new RegionFormatter();
    }
}

使用

1、支持對象屬性註解

@RegionFormat
@ApiModelProperty(name = "region", value = "地區")
private Region region;

2、值Controller入參註解

    @ApiOperation(value = "平臺添加店鋪", response = ShopVO.class)
    @PostMapping()
    public ShopVO save(@Valid ShopVO shop,
                       @RegionFormat @RequestParam("license_region") Region licenseRegion,
                       @RegionFormat @RequestParam("bank_region") Region bankRegion,
                       @RegionFormat @RequestParam("shop_region") Region shopRegion) {


  }  

效果

使用上述地區格式化器方法,假設如下請求

xxx?region=3

則會將id為1的地區及父的信息讀取並且形成Region對象。
例如region=3的地區為長安街,父級為東城區->北京,形成的Region對象如下。

{     
	"cityId": 2,     
	"townId": 0,     
	"countyId": 3,     
	"provinceId": 1,    
	"province": "長安街",     
	"county": "東城區",     
	"city": "北京市",     
	"town": "" 
}

如果傳遞的是townId,則會自動填充county、city及province。
如果傳遞的是countyId,則會自動填充city、province,此時townId為0。

 

同時支持對象屬性級別註解:

對象:

public Person{

   //所屬區域
    @RegionFormat
    private Region region;

    //geter and seter
}

controller: 

@PostMapping()
public Result save(Person person) {

     //這裡的地區已經按上述規則賦值
     person.getRegion() 

     
}  

  


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 表單是什麼?表單是前端和伺服器做交互的一種機制,表單收集用戶輸入信息,之後發送或者提交給伺服器。用戶在輸入的信息稱之為內容,內容的文本分為普通和密碼型,用戶通過單選框、覆選框、下拉框(也就是下拉菜單)完成內容信息輸入,最後通過提交按鈕發送給伺服器!這裡要講到瀏覽器怎麼發送給伺服器?涉及到http協議... ...
  • 要用面向對象的方式去編程,而不要用面向過程的方式去編程 對象是各種類型的數據的集合,可以是數字、字元串、數組、函數、對象…… 對象中的內容以鍵值對方式進行存儲 對象要賦值給一個變數 var cat={ "name":"喵1", "age":4, "family":["喵爸","喵媽"], "spea ...
  • SPA單頁面應用容器 開源地址: https://github.com/yuzd/Spa 功能介紹 前端應用開發完後打包後自助上傳部署發佈 配合服務端腳本(javascript)實現服務端業務邏輯編寫渲染SSR功能 可以快速回滾到上一個版本 可以設置環境變數供SSR功能使用 服務端腳本提供執行日誌 ...
  • 最近自學到了JS運動部分,自己整理了一些js模板,望採納。 1.支持鏈式運動的模板: 先解釋一下函數中的幾個參數含義: 1)obj: 要操作的對象 2)target: 屬性要到達的目標值 3)attr: 要操作的屬性值 4)callback: 回調函數 註:調用函數時,回調函數可不寫 2.支持完美運 ...
  • 在vue、react等框架大量應用之前,我們需要使用jQuery或者原生js來操作dom寫代碼,在用原生js進行事件綁定時,我們可以應用DOM2級綁定事件的方法,即:元素.addEventListener(),因為相容性,還有: 元素.attachEvent()。所以我們需要封裝成一個方法: fun ...
  • 原文:https://zhuanlan.zhihu.com/p/23987456?refer=study-fe 大部分講 new 的文章會從面向對象的思路講起,但是我始終認為,在解釋一個事物的時候,不應該引入另一個更複雜的事物。 今天我從「省代碼」的角度來講 new。 想象我們在製作一個策略類戰爭游 ...
  • 1.HTML基礎標簽圖片常見代碼形式<img src="圖片路徑地址" alt="屬性名" title="占位符">常見的圖片格式為以下三種:.jpg(圖片有損壓縮,影響畫質)、.png(圖片無損壓縮、容積大、具有透明通道)、.gif(動圖)。圖片路徑地址分為本地圖片和網路圖片,本地圖片中分為絕對路 ...
  • 1、工廠模式介紹: 2、簡單工廠模式 3、工廠方法模式 4、小結 5、抽象工廠模式 ...
一周排行
    -Advertisement-
    Play Games
  • Timer是什麼 Timer 是一種用於創建定期粒度行為的機制。 與標準的 .NET System.Threading.Timer 類相似,Orleans 的 Timer 允許在一段時間後執行特定的操作,或者在特定的時間間隔內重覆執行操作。 它在分散式系統中具有重要作用,特別是在處理需要周期性執行的 ...
  • 前言 相信很多做WPF開發的小伙伴都遇到過表格類的需求,雖然現有的Grid控制項也能實現,但是使用起來的體驗感並不好,比如要實現一個Excel中的表格效果,估計你能想到的第一個方法就是套Border控制項,用這種方法你需要控制每個Border的邊框,並且在一堆Bordr中找到Grid.Row,Grid. ...
  • .NET C#程式啟動閃退,目錄導致的問題 這是第2次踩這個坑了,很小的編程細節,容易忽略,所以寫個博客,分享給大家。 1.第一次坑:是windows 系統把程式運行成服務,找不到配置文件,原因是以服務運行它的工作目錄是在C:\Windows\System32 2.本次坑:WPF桌面程式通過註冊表設 ...
  • 在分散式系統中,數據的持久化是至關重要的一環。 Orleans 7 引入了強大的持久化功能,使得在分散式環境下管理數據變得更加輕鬆和可靠。 本文將介紹什麼是 Orleans 7 的持久化,如何設置它以及相應的代碼示例。 什麼是 Orleans 7 的持久化? Orleans 7 的持久化是指將 Or ...
  • 前言 .NET Feature Management 是一個用於管理應用程式功能的庫,它可以幫助開發人員在應用程式中輕鬆地添加、移除和管理功能。使用 Feature Management,開發人員可以根據不同用戶、環境或其他條件來動態地控制應用程式中的功能。這使得開發人員可以更靈活地管理應用程式的功 ...
  • 在 WPF 應用程式中,拖放操作是實現用戶交互的重要組成部分。通過拖放操作,用戶可以輕鬆地將數據從一個位置移動到另一個位置,或者將控制項從一個容器移動到另一個容器。然而,WPF 中預設的拖放操作可能並不是那麼好用。為瞭解決這個問題,我們可以自定義一個 Panel 來實現更簡單的拖拽操作。 自定義 Pa ...
  • 在實際使用中,由於涉及到不同編程語言之間互相調用,導致C++ 中的OpenCV與C#中的OpenCvSharp 圖像數據在不同編程語言之間難以有效傳遞。在本文中我們將結合OpenCvSharp源碼實現原理,探究兩種數據之間的通信方式。 ...
  • 一、前言 這是一篇搭建許可權管理系統的系列文章。 隨著網路的發展,信息安全對應任何企業來說都越發的重要,而本系列文章將和大家一起一步一步搭建一個全新的許可權管理系統。 說明:由於搭建一個全新的項目過於繁瑣,所有作者將挑選核心代碼和核心思路進行分享。 二、技術選擇 三、開始設計 1、自主搭建vue前端和. ...
  • Csharper中的表達式樹 這節課來瞭解一下表示式樹是什麼? 在C#中,表達式樹是一種數據結構,它可以表示一些代碼塊,如Lambda表達式或查詢表達式。表達式樹使你能夠查看和操作數據,就像你可以查看和操作代碼一樣。它們通常用於創建動態查詢和解析表達式。 一、認識表達式樹 為什麼要這樣說?它和委托有 ...
  • 在使用Django等框架來操作MySQL時,實際上底層還是通過Python來操作的,首先需要安裝一個驅動程式,在Python3中,驅動程式有多種選擇,比如有pymysql以及mysqlclient等。使用pip命令安裝mysqlclient失敗應如何解決? 安裝的python版本說明 機器同時安裝了 ...