Java 電商系統地區級聯參數傳遞註解式源碼分享

需求

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

預期效果

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

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

在controller接收參數時:


@PostMapping<br>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()
 
      
}  

易族智匯(javashop)原創文章

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章