地区级联参数传递注解式解决方案

需求

  地区数据往往是存在强上下级关系的一种数据结构,在电商系统中是比较常应用到的,比如北京的下级地区只有海淀区、通州区……,而不会是太原市,而且在开发人员传递地区值的时候往往要传递很多的值,比如省、市、区、镇、省Id,市id、区id、镇id,这样影响了代码的美观性及校验强上下级关系代码的复杂性。基于这样的问题要求我们必须要实现一种可以简洁传递参数并且可以实现地区格式化的一种格式化器。这样衍生出了我们基于Spring Formatter地区自动化格式的一种格式化器。

预期效果

常规做法的地区参数传递:

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

  

在controller接收参数时:

@PostMappingpublic 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() 

}  

  

原文地址:https://www.cnblogs.com/javashop-docs/p/12299556.html

时间: 2024-10-13 17:28:48

地区级联参数传递注解式解决方案的相关文章

spring(7)--注解式控制器的数据验证、类型转换及格式化

7.1.简介 在编写可视化界面项目时,我们通常需要对数据进行类型转换.验证及格式化. 一.在Spring3之前,我们使用如下架构进行类型转换.验证及格式化: 流程: ①:类型转换:首先调用PropertyEditor的setAsText(String),内部根据需要调用setValue(Object)方法进行设置转换后的值: ②:数据验证:需要显示调用Spring的Validator接口实现进行数据验证: ③:格式化显示:需要调用PropertyEditor的getText进行格式化显示. 使用

注解式控制器

注解式控制器 Spring2.5之前,我们都是通过实现Controller接口或其实现来定义我们的处理器类.已经@Deprecated,建议不再使用了 Spring2.5引入注解式处理器支持,通过@Controller 和 @RequestMapping注解定义我们的处理器类.并且提供了一组强大的注解: @Controller:用于标识是处理器类: @RequestMapping:请求到处理器功能方法的映射规则: @RequestParam:请求参数到处理器功能处理方法的方法参数上的绑定: @M

spring(6)--注解式控制器

6.1.注解式控制器简介 一.Spring2.5之前,我们都是通过实现Controller接口或其实现来定义我们的处理器类.已经@Deprecated.   二.Spring2.5引入注解式处理器支持,通过@Controller 和 @RequestMapping注解定义我们的处理器类. 并且提供了一组强大的注解: 需要通过处理器映射DefaultAnnotationHandlerMapping和处理器适配器AnnotationMethodHandlerAdapter来开启支持@Controll

spring注解式参数校验

很痛苦遇到大量的参数进行校验,在业务中还要抛出异常或者返回异常时的校验信息,在代码中相当冗长,今天我们就来学习spring注解式参数校验. 其实就是:hibernate的validator. 开始啦...... 1.controller的bean加上@Validated就像这样 1 @ApiOperation(value = "用户登录接口", notes = "用户登录") 2 @PostMapping("/userLogin") 3 publ

AOP注解式拦截

1. 自己定义的拦截注解 package com.spring.aop; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(R

springmvc注解式控制器简介

一.Spring2.5之前,我们都是通过实现Controller接口或其实现来定义我们的处理器类.已经@Deprecated. 二.Spring2.5引入注解式处理器支持,通过@Controller 和 @RequestMapping注解定义我们的处理器类.并且提供了一组强大的注解: 需要通过处理器映射DefaultAnnotationHandlerMapping和处理器适配器AnnotationMethodHandlerAdapter来开启支持@Controller 和@RequestMapp

SpringMVC学习系列(9) 之 实现注解式权限验证

对大部分系统来说都需要权限管理来决定不同用户可以看到哪些内容,那么如何在Spring MVC中实现权限验证呢?当然我们可以继续使用servlet中的过滤器Filter来实现.但借助于Spring MVC中的action拦截器我们可以实现注解式的权限验证. 一.首先介绍一下action拦截器: HandlerInterceptor是Spring MVC为我们提供的拦截器接口,来让我们实现自己的处理逻辑,HandlerInterceptor 的内容如下: public interface Handl

13.MyBatis注解式开发

mybatis 的注解,主要是用于替换映射文件.而映射文件中无非存放着增.删.改.查 的 SQL 映射标签.所以,mybatis 注解,就是要替换映射文件中的 SQL 标签. mybatis 官方文档中指出,若要真正想发挥 mybatis 功能,还是要用映射文件.即 mybatis官方并不建议通过注解方式来使用 mybatis. 注解的基础知识: 注解的基础语法 A.注解后是没有分号的. B.注解首字母是大写的,因为注解与类.接口是同一级别的.一个注解,后台对应着 一个@interface 类.

springmvc 实现注解式权限验证

对大部分系统来说都需要权限管理来决定不同用户可以看到哪些内容,那么如何在Spring MVC中实现权限验证呢?当然我们可以继续使用servlet中的过滤器Filter来实现.但借助于Spring MVC中的action拦截器我们可以实现注解式的权限验证. 一.首先介绍一下action拦截器: HandlerInterceptor是Spring MVC为我们提供的拦截器接口,来让我们实现自己的处理逻辑,HandlerInterceptor 的内容如下: public interface Handl