• 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")
    
  • 相关阅读:
    LeetCode 121. Best Time to Buy and Sell Stock
    LeetCode 221. Maximal Square
    LeetCode 152. Maximum Product Subarray
    LeetCode 53. Maximum Subarray
    LeetCode 91. Decode Ways
    LeetCode 64. Minimum Path Sum
    LeetCode 264. Ugly Number II
    LeetCode 263. Ugly Number
    LeetCode 50. Pow(x, n)
    LeetCode 279. Perfect Squares
  • 原文地址:https://www.cnblogs.com/sanri1993/p/11723163.html
Copyright © 2020-2023  润新知