springboot 快速开发的定制补充

加强 SpringBoot 快速开发工具

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

它解决的问题:java

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

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

做者QQ: 2441719087web

做者邮箱: ningxiangsanri@163.comspring

9420 技术交流群: 645576465mongodb

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

项目功能

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

  • 固定了输入输出格式前端框架

    // 普通输出格式
    @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")