zoukankan      html  css  js  c++  java
  • springboot 快速开发的定制补充

    增强 SpringBoot 快速开发工具

    项目地址:https://gitee.com/sanri/web-ui
    优点:这是一个 web 通用配置的组件,即插即用,可用于新项目或私活。是对 SpringBoot 快速开发的一种补充,它内置了大量的配置来简化开发,遵循约定高于配置原则。

    它解决的问题:

    • 固定了输入输出格式
    • 不用像公司的返回结构一样,需要自己包装类型,直接返回原始类型,如果需要可以返回 void
    • 支持树结构数据返回,可以一个注解就转换结构为树形结构
    • 如果项目中出现业务操作不符合或调用第三方出错,可使用异常或断言抛出,我们将拦截成统一格式返回
    • 自带参数空格过滤功能,还可以定义特殊字符和谐
    • 支持校验器,添加了大量常用验证器
    • 支持大文件分片上传,已内置 Controller
    • 支持方法日志参数记录

    发现BUG可以提Issue,可以给我发邮件,可以加我QQ,可以进9420技术群讨论.

    作者QQ: 2441719087

    作者邮箱: ningxiangsanri@163.com

    9420 技术交流群: 645576465

    作者微信:sanri1993-
    在这里插入图片描述

    项目功能

    我新开的一个项目,总结了多年的开发经验所得,它具有的功能有

    • 固定了输入输出格式

      // 普通输出格式
      @Data
      public class ResponseDto<T> implements Serializable {
          // 0 字符串表示成功,否则失败
          private String code = "0";
          private String message;
          private T data;
      }
      // 分页输出格式,是包裹在普通输出格式中的,PageResponseDto 做为 data 属性
      @Data
      public class PageResponseDto<T> {
          private List<T> rows;
          private Integer total;
      }
      
      // 分页输入格式 
      @Setter
      public class PageParam {
      	private String pageNo;
      	private String pageSize;
      }
      
    • 对于 Controller 中的返回不用关心包装类型,返回你所需要的类型就可以了,对于 insert 单表操作可以直接返回 void

      示例一:

      @PostMapping("/insertUser")
      public void insertUser(User user){
          xxxService.insert(user);
      }
      

      它将会返回这样的数据结构

      {
          "code":"0",
          "message":"ok",
          "data":null
      }
      

      示例二:

      @GetMapping("/queryUserById")
      public User queryUserById(Integer userId){
          xxxService.queryUserById(userId);
      }
      

      它将会返回这样的数据结构

      {
          "code":"0",
          "message":"ok",
          "data":{
              "userId":1,
              "username":"9420"
          }
      }
      

      示例三:

      对于分页数据的处理

      @GetMapping("/queryUserPage")
      public PageResponseDto<User> pageQuery(PageParam pageParam,Map<String,String> queryParams){
          PageHelper.startPage(pageParam.getPageNo(),pageParam.getPageSize());
          Page page = (Page) xxxService.pageQuery(queryParams);
          List result = page.getResult();
          long total = page.getTotal();
          return new PageResponseDto(result,total);
      }
      

      它将会返回这样的数据结构

      {
          "code":"0",
          "message":"ok",
          "data":{
              "total":100,
              "rows":[{...},{...}]
          }
      }
      

      示例四: 树结构返回

      对于树型结构数据,你可以用简单数据返回,即原来的 List<Dto> 也可以添加一个注解,使其成为树状结构

      //rootId 指定为根结点 id 
      @GetMapping("/treeShowMenu")
      @TreeResponse(type = MenuDto.class,rootId = "1")
      public List<Menu> treeShowMenu(){
          List<Menu> menus = new ArrayList<>();
          menus.add(new Menu(1,"全国",-1));
          menus.add(new Menu(2,"湖南",1));
          menus.add(new Menu(3,"长沙",2));
          menus.add(new Menu(4,"深圳",1));
          return menus;
      }
      
      // 树状结构消息类
      public class MenuDto extends RootTreeResponseDto<Menu> {
      	public MenuDto(Menu origin) {
      		super(origin);
      	}
          
      	@Override
      	public String getId() {return origin.getId()+"";}
      
      	@Override
      	public String getParentId() {return origin.getPid()+"";}
      
      	@Override
      	public String getLabel() {return origin.getText();}
      
      	@Override
      	public Menu getOrigin() {return origin;}
      }
      

      它将返回如下数据结构

      {
      	"code": "0",
      	"message": "ok",
      	"data": [{
      		"origin": {
      			"id": 1,
      			"text": "全国",
      			"pid": -1
      		},
      		"childrens": [{
      			"origin": {
      				"id": 2,
      				"text": "湖南",
      				"pid": 1
      			},
      			"childrens": [{
      				"origin": {
      					"id": 3,
      					"text": "长沙",
      					"pid": 2
      				},
      				"childrens": [],
      				"id": "3",
      				"label": "长沙",
      				"parentId": "2"
      			}],
      			"id": "2",
      			"label": "湖南",
      			"parentId": "1"
      		}, {
      			"origin": {
      				"id": 4,
      				"text": "深圳",
      				"pid": 1
      			},
      			"childrens": [],
      			"id": "4",
      			"label": "深圳",
      			"parentId": "1"
      		}],
      		"id": "1",
      		"label": "全国",
      		"parentId": "-1"
      	}]
      }
      
    • 如果项目中出现业务操作不符合或调用第三方出错,可使用异常抛出,我们将拦截成统一格式返回

      示例一:

      if(业务条件不满足){
          throw BusinessException.create("业务提示信息");
      }
      

      它将会返回这样的数据结构,code 是随机生成的

      {
          "code":"234234",
          "message":"业务提示信息",
          "data":null
      }
      

      示例二:

      自定义 code 示例方法一

      if(业务条件不满足){
          throw BusinessException.create("E007","业务提示信息");
      }
      

      它将会返回这样的数据结构

      {
          "code":"E007",
          "message":"业务提示信息",
          "data":null
      }
      

      示例三:

      自定义 code 示例方法二

      // 配置异常代码 
      public enum  SystemMessage implements ExceptionCause<BusinessException> {
          SIGN_ERROR(4005,"签名错误,你的签名串为 [%s]"),;
          ResponseDto responseDto = new ResponseDto();
      
          private SystemMessage(int returnCode,String message){
              responseDto.setCode(returnCode+"");
              responseDto.setMessage(message);
          }
      
          public BusinessException exception(Object...args) {
              return BusinessException.create(this,args);
          }
      }
      

      使用异常

      if(业务条件不满足){
          throw SystemMessage.SIGN_ERROR.exception("签名串");
      }
      

      它将会返回这样的数据结构

      {
          "code":"4005",
          "message":"签名错误,你的签名串为 [签名串]",
          "data":null
      }
      
    • 你以为它就这么点能耐吗,它还自带参数空格过滤功能,还可以定义特殊字符和谐

      你只需要注入一个处理器,它就能工作,注入方式如下

      @Bean("paramHandler")
      public Function paramHandler(){
          return param -> param.replace("<","《");
      }
      
    • 自带了日期转化(输入)功能,可以支持的日期格式有

      final String[] parsePatterns = new String[]{"yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm:ss.S"};
      

      现在是固定这三种格式 ,后面会放开让使用者自己配置

    • 支持校验器,已经帮你设置好了两个 group ,直接使用即可

      public interface Insert {
      }
      public interface Update {
      }
      
    • 支持大文件上传,文件秒传,文件验证;你只需要配置几个选项即可使用

       # 文件上传的位置
       sanri.webui.upload.basePath=d:/test/
       # 临时文件路径 
       spring.servlet.multipart.location=d:/tmp
      

      或者你想上传到别的地方,那就需要自己实现 com.sanri.web.bigfile.BigFileStorage然后注入 IOC 到容器

      @Bean
      public BigFileStorage bigFileStorage(){
          return new LocalBigFileStorage();
      }
      

    大文件上传的几个接口说明

    已经帮你添加了一个 controller ,用于大文件上传,下面是接口说明

    • GET /upload/file/fileMetaData?originFileName=原始文件名&fileSize=文件大小&md5=文件 md5
      返回 FileMetaData 数据,重要的数据是那个相对路径 relativePath 因为它是接下来所有接口的入参

    • GET /upload/file/filePosition?relativePath=相对路径
      返回 文件当前上传的大小,用于断点续传

    • POST /upload/file/uploadPart?relativePath=相对路径
      body 中添加 form-data 参数,file=文件
      返回上传文件位置

    • GET /upload/file/validateFile?relativePath=相对路径&fileSize=文件大小&md5=文件 md5 值
      返回文件是否正常上传,无损坏

    使用说明

    引入包或下载 jar 包文件

    <dependency>
    	<groupId>com.sanri.web</groupId>
        <artifactId>web-ui</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    

    开启快速开发

    @EnableWebUI 
    

    如果想开启大文件上传

    @EnableBigFileUpload
    

    更新列表

    update 2019/10/24 增加日志组件

    可以为方法标记记录日志功能,这是很常见的一个功能,感谢网友 东莞-队长(qq: 1178130627) 的朋友提出

    因为日志每个系统有各自的做法,有的可能还需要把日志存储到 mongodb 中去,所以不可能全部统一起来,注解也是不支持继承的;所以我的解决办法是,我可以帮你尽可能的解析出一些参数来,但具体的实现逻辑还需要你自己来弄,框架默认会给你注入一个把日志打印到控制台的功能。

    使用方法为使用注解标记当前方法,它将默认使用 com.sanri.web.logmark.Slf4jLogInfoHandler 来记录日志

    @GetMapping("/testParamTrim")
    @SysLogMark
    public void testParamTrim(TestParam testParam){}
    

    兼容性说明 :

    1. 兼容 application/x-www-form-urlencoded 类型参数
    2. 兼容 application/form-data 类型参数
    3. 兼容 application/json 类型参数

    当然,我会排除文件类型的参数,它太庞大了,不可能打印在日志里面

    实现自己的日志记录方法:注入一个 LogInfoHandler 的 Bean 到 IOC 容器中

    新增配置

    # 日志参数打印,可支持的项有 base,param,header,body
    sanri.webui.logmark.showInfos=base,param,header,body
    

    update 2019/10/25 增加常用验证器和部分工具

    一些常用校验器和通用预览和下载功能,比较常用,这里给全部集成了

    参数校验器使用方法

    // 必须为强密码
    @NotNull
    @Password(strength = Password.Strength.STRONG)
    private String password;
    

    其它常用验证器

    • @UserName 验证参数是否为用户名

    • @Password 验证参数是否为密码

    • @IdCard18 验证参数是否为 18 位身份证

      可以在 resources 目录下放置一个区域代码,做更强的验证,文件名为 areaCodes,文件内容以逗号分隔所有的区域代码

    • @EnumIntValue@EnumStringValue 验证参数是否为枚举值

    增加一些文件下载,预览方法,和 request 请求信息的获取

    @Autowired
    RequestInfoHelper requestInfoHelper;
    @Autowired
    StreamHelper streamHelper;
    

    修改处理器注入方式 ,使用自己的接口 ParamHandler,不使用 Function

    @Bean("paramHandler")
    public ParamHandler paramHandler(){
        return param -> param.replace("<","《");
    }
    

    update 2019/10/27 增加树形数据返回

    为解决前端 mm 需要后端人员返回树形结构数据问题,其实大部分框架已经支持简单树形数据,像 ztree ,但也有的前端框架是需要后端帮忙转化一下数据结构的,所以特加此功能

    这个树形结构的转换使用了一个快速转换的机制,充分利用了对象在内存中地址的原理,实测在万条数据转换为 10ms 左右,使用方法是先实现一个 TreeResponseDto 的类,然后在 Controller 中添加一个注解

    @TreeResponse(type = MenuDto.class,rootId = "1")
    
  • 相关阅读:
    .NET中string和StringBuilder的区别
    Hashtable 类
    C#中null和""的区别
    Dictionary 泛型类
    宿主进程含义
    事件和委托
    关于SQLServer中索引使用及维护简介
    什么是序列化
    “锁定”语句(C# 参考)
    栈和托管堆/值类型和引用类型/强制类型转换/装箱和拆箱[C#]
  • 原文地址:https://www.cnblogs.com/sanri1993/p/11723163.html
Copyright © 2011-2022 走看看