代码及接口文档自动生成

需求:

  1. 最近一段时间的工作中,发现代码自动生成模板比较单一,无法满足现有的代码规范。
  2. 好些代码模板中没有,需要手动编写代码
  3. 且生成的模板中没有办法设置校验规则,如:是否必填,字段长度判断等。
  4. 生成的代码,部分无法直接生成apifox,不需要手动编写接口文档。
  5. 生成apifox接口文档不规范,是否必填等内容还需要手动编写。

基于以上需要,整理了一套适合我们自己的代码模板,生成的模板能严格遵守我们的代码规范,且可以直接生成apifox。

安装插件EasyCode

代码自动生成插件

安装Apifox Helper插件

接口文档自动生成插件,我们接口都放在apifox中,如果是别的接口调试软件,请更换别的插件来生成代码。

Alibaba Java Coding Guidelines

阿里代码规约扫描软件

设置EasyCode模板

模板介绍

因为项目路径问题,不同的项目返回前段的公共包路径不一样,且有一些返回格式也不同,因此将不同的项目规范成为不同的代码生成模板。

以下我会以话务模板为例展示,其他模板,大家根据各自使用情况自行修改。

首先模板数量:

dto.java.vm

入参接受类,此类中会增加数据校验,且开发人员可以任意修改入参。

##引入宏定义
$!{define.vm}
##设置表后缀(宏定义)
#setTableSuffix("Dto")
##使用宏定义设置回调(保存位置与文件后缀)
#save("/controller/dto", "Dto.java")

##使用宏定义设置包后缀
#setPackageSuffix("controller.dto")
##使用全局变量实现默认包导入
$!{autoImport.vm}
import lombok.Data;
import javax.validation.constraints.NotNull;
import org.hibernate.validator.constraints.Length;
##使用宏定义实现类注释信息
#tableComment("实体类")
@Data
public class $!{tableName}{
#foreach($column in $tableInfo.fullColumn)
 #if(${column.comment})
      /**
        * ${column.comment}
        */
    #end
#if(${column.name} != "id"&&${column.obj.isNotNull()})
    @NotNull(message = "${column.comment}不能为空")
    #end
#if($!{tool.getClsNameByFullName($column.type)} == "String")
    @Length(max = $!{column.obj.dataType.length}, message = "${column.comment}不能超过$!{column.obj.dataType.length}个字符")
    #end
    private $!{tool.getClsNameByFullName($column.type)} $!{column.name};
#end

}

controller.java.vm

##定义初始变量
#set($tableName = $tool.append($tableInfo.name, "Controller"))
##设置回调
$!callback.setFileName($tool.append($tableName, ".java"))
$!callback.setSavePath($tool.append($tableInfo.savePath, "/controller"))
##拿到主键
#if(!$tableInfo.pkColumn.isEmpty())
    #set($pk = $tableInfo.pkColumn.get(0))
#end

#if($tableInfo.savePackageName)package $!{tableInfo.savePackageName}.#{end}controller;
import $!{tableInfo.savePackageName}.entity.$!{tableInfo.name};
import $!{tableInfo.savePackageName}.controller.dto.$!{tableInfo.name}Dto;
import $!{tableInfo.savePackageName}.service.$!{tableInfo.name}Service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.library.processing.controller.dto.CommonResult;
import org.springframework.web.bind.annotation.*;
import com.library.processing.controller.dto.IdDto;
import com.library.processing.controller.dto.SelectInputCommon;
import javax.annotation.Resource;
import org.springframework.beans.BeanUtils;
import org.springframework.validation.annotation.Validated;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

/**
 * $!{tableInfo.comment}($!{tableInfo.name})表控制层
 *
 * @author $!author
 * @since $!time.currTime()
 */
@RestController
@RequestMapping("$!tool.firstLowerCase($tableInfo.name)")
public class $!{tableName} {
    /**
     * 服务对象
     */
    @Resource
    private $!{tableInfo.name}Service $!tool.firstLowerCase($tableInfo.name)Service;

    /**
     * 分页查询
     *
     * @param input 筛选条件
     * @return 查询结果
     */
    @PostMapping("list")
    public CommonResult<Page<$!{tableInfo.name}>> queryByPage(@Validated @RequestBody SelectInputCommon<$!{tableInfo.name}Dto> input) {
            Page<$!{tableInfo.name}> page=new Page<>();
            BeanUtils.copyProperties(input.getPage(), page);
            $!{tableInfo.name} $!{tool.firstLowerCase($tableInfo.name)} = new $!{tableInfo.name}();
            BeanUtils.copyProperties(input.getToNullData(), $!{tool.firstLowerCase($tableInfo.name)});
        return CommonResult.ok(this.$!{tool.firstLowerCase($tableInfo.name)}Service.page(page, new QueryWrapper<>($!{tool.firstLowerCase($tableInfo.name)})));
    }

    /**
     * 通过主键查询单条数据
     *
     * @param idDto 主键
     * @return 单条数据
     */
    @PostMapping("getById")
    public CommonResult<$!{tableInfo.name}> queryById(@Validated @RequestBody IdDto<$!pk.shortType> idDto) {
        return CommonResult.ok(this.$!{tool.firstLowerCase($tableInfo.name)}Service.getById(idDto.getId()));
    }

    /**
     * 新增数据
     *
     * @param $!{tool.firstLowerCase($tableInfo.name)}Dto 实体
     * @return 新增结果
     */
     @PostMapping("add")
    public CommonResult<Boolean> add(@Validated @RequestBody $!{tableInfo.name}Dto $!{tool.firstLowerCase($tableInfo.name)}Dto) {
          $!{tableInfo.name} $!{tool.firstLowerCase($tableInfo.name)} = new $!{tableInfo.name}();
            BeanUtils.copyProperties($!{tool.firstLowerCase($tableInfo.name)}Dto, $!{tool.firstLowerCase($tableInfo.name)});
    
        return CommonResult.ok(this.$!{tool.firstLowerCase($tableInfo.name)}Service.save($!{tool.firstLowerCase($tableInfo.name)}));
    }

    /**
     * 编辑数据
     *
     * @param $!{tool.firstLowerCase($tableInfo.name)}Dto 实体
     * @return 编辑结果
     */
     @PostMapping("edit")
    public CommonResult<Boolean> edit(@Validated @RequestBody $!{tableInfo.name}Dto $!{tool.firstLowerCase($tableInfo.name)}Dto) {
          $!{tableInfo.name} $!{tool.firstLowerCase($tableInfo.name)} = new $!{tableInfo.name}();
            BeanUtils.copyProperties($!{tool.firstLowerCase($tableInfo.name)}Dto, $!{tool.firstLowerCase($tableInfo.name)});
        return CommonResult.ok(this.$!{tool.firstLowerCase($tableInfo.name)}Service.saveOrUpdate($!{tool.firstLowerCase($tableInfo.name)}));
    }

    /**
     * 删除数据
     *
     * @param idDto 主键
     * @return 删除是否成功
     */
    @PostMapping("deleteById")
    public CommonResult<Boolean> deleteById(@Validated @RequestBody IdDto<$!pk.shortType> idDto) {
        return CommonResult.ok(this.$!{tool.firstLowerCase($tableInfo.name)}Service.removeById(idDto.getId()));
    }

}

entity.java.vm

##引入宏定义
$!{define.vm}

##使用宏定义设置回调(保存位置与文件后缀)
#save("/entity", ".java")

##使用宏定义设置包后缀
#setPackageSuffix("entity")

##使用全局变量实现默认包导入
$!{autoImport.vm}
import java.io.Serializable;
import lombok.Data;

##使用宏定义实现类注释信息
#tableComment("实体类")
@Data
public class $!{tableInfo.name} implements Serializable {
    private static final long serialVersionUID = $!tool.serial();
  
#foreach($column in $tableInfo.fullColumn)
    #if(${column.comment})/**
     * ${column.comment}
     */#end
     private $!{tool.getClsNameByFullName($column.type)} $!{column.name};
#end

}

service.java.vm

##导入宏定义
$!{define.vm}

##设置表后缀(宏定义)
#setTableSuffix("Service")

##保存文件(宏定义)
#save("/service", "Service.java")

##包路径(宏定义)
#setPackageSuffix("service")

import com.baomidou.mybatisplus.extension.service.IService;
import $!{tableInfo.savePackageName}.entity.$!tableInfo.name;

##表注释(宏定义)
#tableComment("表服务接口")
public interface $!{tableName} extends IService<$!tableInfo.name> {

}

serviceImpl.java.vm

##导入宏定义
$!{define.vm}

##设置表后缀(宏定义)
#setTableSuffix("ServiceImpl")

##保存文件(宏定义)
#save("/service/impl", "ServiceImpl.java")

##包路径(宏定义)
#setPackageSuffix("service.impl")

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import $!{tableInfo.savePackageName}.dao.$!{tableInfo.name}Dao;
import $!{tableInfo.savePackageName}.entity.$!{tableInfo.name};
import $!{tableInfo.savePackageName}.service.$!{tableInfo.name}Service;
import org.springframework.stereotype.Service;

##表注释(宏定义)
#tableComment("表服务实现类")
@Service("$!tool.firstLowerCase($tableInfo.name)Service")
public class $!{tableName} extends ServiceImpl<$!{tableInfo.name}Dao, $!{tableInfo.name}> implements $!{tableInfo.name}Service {

}

dao.java.vm

##引入宏定义
$!{define.vm}
##设置表后缀(宏定义)
#setTableSuffix("Dto")
##使用宏定义设置回调(保存位置与文件后缀)
#save("/controller/dto", "Dto.java")

##使用宏定义设置包后缀
#setPackageSuffix("controller.dto")
##使用全局变量实现默认包导入
$!{autoImport.vm}
import lombok.Data;
import javax.validation.constraints.NotNull;
import org.hibernate.validator.constraints.Length;
##使用宏定义实现类注释信息
#tableComment("实体类")
@Data
public class $!{tableName}{
#foreach($column in $tableInfo.fullColumn)
 #if(${column.comment})
      /**
        * ${column.comment}
        */
    #end
#if(${column.name} != "id"&&${column.obj.isNotNull()})
    @NotNull(message = "${column.comment}不能为空")
    #end
#if($!{tool.getClsNameByFullName($column.type)} == "String")
    @Length(max = $!{column.obj.dataType.length}, message = "${column.comment}不能超过$!{column.obj.dataType.length}个字符")
    #end
    private $!{tool.getClsNameByFullName($column.type)} $!{column.name};
#end

}

mapper.xml.vm

##引入mybatis支持
$!{mybatisSupport.vm}

##设置保存名称与保存位置
$!callback.setFileName($tool.append($!{tableInfo.name}, "Dao.xml"))
$!callback.setSavePath($tool.append($modulePath, "/src/main/resources/mapper"))

##拿到主键
#if(!$tableInfo.pkColumn.isEmpty())
    #set($pk = $tableInfo.pkColumn.get(0))
#end

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="$!{tableInfo.savePackageName}.dao.$!{tableInfo.name}Dao">


</mapper>

代码生成过程

1.选择EasyCode工具

2. 选择要生成的代码地址(包名),选择要生成的文件,下面示例是我选择全部生成的文件,可以根据需要自行选择要生成的文件。

3.点击确定,生成成功。

生成代码如下

dto

package com.library.processing.callplatform.controller.dto;

import lombok.Data;
import org.hibernate.validator.constraints.Length;

import javax.validation.constraints.NotNull;
import java.util.Date;
/**
 * 技能组(SkillCopy)实体类
 *
 * @author 李亮
 * @since 2023-08-31 16:03:54
 */
@Data
public class SkillCopyDto {
    /**
     * 主键
     */
    @NotNull(message = "主键不能为空")
    private Integer id;
    /**
     * 技能组名称
     */
    @NotNull(message = "技能组名称不能为空")
    @Length(max = 50, message = "技能组名称不能超过50个字符")
    private String skillName;
    /**
     * 类型(ring_all 所有空闲坐席立即全部振铃(群振)。 longest_idle 最长空闲时间坐席振铃 round 顺振,每次从上次振铃坐席下一个开始振铃 top_down 顺振,每次都从第一个坐席开始振铃 least_talk_time 最少通话时间坐席振铃 fewest_calls 最少接通量坐席振铃 random 随机振铃)
     */
    @Length(max = 50, message = "类型(ring_all 所有空闲坐席立即全部振铃(群振)。 longest_idle 最长空闲时间坐席振铃 round 顺振,每次从上次振铃坐席下一个开始振铃 top_down 顺振,每次都从第一个坐席开始振铃 least_talk_time 最少通话时间坐席振铃 fewest_calls 最少接通量坐席振铃 random 随机振铃)不能超过50个字符")
    private String strategy;
    /**
     * 最大排队时间(秒)
     */
    private Integer maxWaitTime;
    /**
     * 排队等待音,wav音频文件的linux绝对路径
     */
    @Length(max = 100, message = "排队等待音,wav音频文件的linux绝对路径不能超过100个字符")
    private String nohSound;
    /**
     * 创建时间
     */
    @NotNull(message = "创建时间不能为空")
    private Date createTime;
    /**
     * 创建人名称
     */
    @Length(max = 50, message = "创建人名称不能超过50个字符")
    private String createUserName;
    /**
     * 创建人工号
     */
    @Length(max = 50, message = "创建人工号不能超过50个字符")
    private String createJobNumber;
    /**
     * 更新时间
     */
    private Date updateTime;
    /**
     * 更新人名称
     */
    @Length(max = 50, message = "更新人名称不能超过50个字符")
    private String updateUserName;
    /**
     * 更新人工号
     */
    @Length(max = 50, message = "更新人工号不能超过50个字符")
    private String updateJobNumber;
    /**
     * 技能组名称拼音
     */
    @NotNull(message = "技能组名称拼音不能为空")
    @Length(max = 255, message = "技能组名称拼音不能超过255个字符")
    private String skillNamePy;

}

controller

package com.library.processing.callplatform.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.library.processing.callplatform.controller.dto.SkillCopyDto;
import com.library.processing.callplatform.entity.SkillCopy;
import com.library.processing.callplatform.service.SkillCopyService;
import com.library.processing.controller.dto.CommonResult;
import com.library.processing.controller.dto.IdDto;
import com.library.processing.controller.dto.SelectInputCommon;
import org.springframework.beans.BeanUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

/**
 * 技能组(SkillCopy)表控制层
 *
 * @author 李亮
 * @since 2023-08-31 16:07:40
 */
@RestController
@RequestMapping("skillCopy")
public class SkillCopyController {
    /**
     * 服务对象
     */
    @Resource
    private SkillCopyService skillCopyService;

    /**
     * 分页查询
     *
     * @param input 筛选条件
     * @return 查询结果
     */
    @PostMapping("list")
    public CommonResult<Page<SkillCopy>> queryByPage(@Validated @RequestBody SelectInputCommon<SkillCopyDto> input) {
        Page<SkillCopy> page = new Page<>();
        BeanUtils.copyProperties(input.getPage(), page);
        SkillCopy skillCopy = new SkillCopy();
        BeanUtils.copyProperties(input.getToNullData(), skillCopy);
        return CommonResult.ok(this.skillCopyService.page(page, new QueryWrapper<>(skillCopy)));
    }

    /**
     * 通过主键查询单条数据
     *
     * @param idDto 主键
     * @return 单条数据
     */
    @PostMapping("getById")
    public CommonResult<SkillCopy> queryById(@Validated @RequestBody IdDto<Integer> idDto) {
        return CommonResult.ok(this.skillCopyService.getById(idDto.getId()));
    }

    /**
     * 新增数据
     *
     * @param skillCopyDto 实体
     * @return 新增结果
     */
    @PostMapping("add")
    public CommonResult<Boolean> add(@Validated @RequestBody SkillCopyDto skillCopyDto) {
        SkillCopy skillCopy = new SkillCopy();
        BeanUtils.copyProperties(skillCopyDto, skillCopy);

        return CommonResult.ok(this.skillCopyService.save(skillCopy));
    }

    /**
     * 编辑数据
     *
     * @param skillCopyDto 实体
     * @return 编辑结果
     */
    @PostMapping("edit")
    public CommonResult<Boolean> edit(@Validated @RequestBody SkillCopyDto skillCopyDto) {
        SkillCopy skillCopy = new SkillCopy();
        BeanUtils.copyProperties(skillCopyDto, skillCopy);
        return CommonResult.ok(this.skillCopyService.saveOrUpdate(skillCopy));
    }

    /**
     * 删除数据
     *
     * @param idDto 主键
     * @return 删除是否成功
     */
    @PostMapping("deleteById")
    public CommonResult<Boolean> deleteById(@Validated @RequestBody IdDto<Integer> idDto) {
        return CommonResult.ok(this.skillCopyService.removeById(idDto.getId()));
    }

}

这里主要展示上述两个文件,其他文件不在过多赘述。其他文件和通用的内容没有什么区别。

Controller公共包补充:

CommonResult

package com.library.processing.controller.dto;

import java.io.Serializable;
import java.util.List;

public class CommonResult<T> extends BaseResult implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 数据
     */
    private T data;

    private void setData(T data) {
        this.data = data;
    }

    public CommonResult<T> setResult(T data) {
        this.setData(data);
        return this;
    }

    private CommonResult() {

    }

    private CommonResult(int code, String desc) {
        this.setCode(code);
        this.setDesc(desc);
    }

    private CommonResult(int code) {
        this.setCode(code);
    }

    private CommonResult(int code, String desc, T data) {
        this.setCode(code);
        this.setDesc(desc);
        this.setData(data);
    }

    public static CommonResult ok() {
        return ok("");
    }

    public static CommonResult ok(String desc) {
        return baseCreate(BaseResult.CODE_SUCCESS, desc);
    }

    public static <T> CommonResult<T> ok(T data) {
        return baseCreate(BaseResult.CODE_SUCCESS, "", data);
    }

    public static <T> CommonResult<T> ok(String desc, T data) {
        return baseCreate(BaseResult.CODE_SUCCESS, desc, data);
    }

    public static CommonResult fail(int code, String desc) {
        return baseCreate(code, desc);
    }

    private static <T> CommonResult<T> baseCreate(int code, String desc, T data) {
        CommonResult<T> result = new CommonResult<T>();
        result.setCode(code);
        result.setDesc(desc);
        result.setData(data);
        return result;
    }

    private static <T> CommonResult<T> baseCreate(int code, String desc, T data, String eventType) {
        CommonResult<T> result = new CommonResult<T>();
        result.setCode(code);
        result.setDesc(desc);
        result.setData(data);
        result.setEventType(eventType);
        return result;
    }

    private static CommonResult baseCreate(int code, String desc) {
        CommonResult result = new CommonResult();
        result.setCode(code);
        result.setDesc(desc);
        return result;
    }


    public static CommonResult getNoAuthorityUserInstance() {
        return fail(CODE_NO_AUTHORITY, "没有该用户修改权限");
    }


    public static CommonResult paramErrorResult(List<ParamError> errors) {
        CommonResult result = fail(CODE_PARAM_VALIDATED_ERROR, "输入参数错误");
        result.setParamErr(errors);
        return result;
    }

    public static CommonResult paramErrorResult(ParamError error) {
        CommonResult result = fail(CODE_PARAM_VALIDATED_ERROR, "输入参数错误");
        result.addParamErr(error);
        return result;
    }

    public T getData() {
        return data;
    }

    public static CommonResult fail(ResultCode resultCode) {
        return baseCreate(resultCode.getCode(), resultCode.getDesc());
    }

    public static <T> CommonResult<T> ok(ResultCode resultCode, T data) {
        return baseCreate(resultCode.getCode(), resultCode.getDesc(), data);
    }

    public static <T> CommonResult<T> ok(ResultCode resultCode, T data, String eventType) {
        return baseCreate(resultCode.getCode(), resultCode.getDesc(), data, eventType);
    }
}

IdDto

package com.library.processing.controller.dto;

import lombok.Data;

import javax.validation.constraints.NotNull;

/**
 * * id
 * *
 * * @author ll
 * * @since 2023-05-08 16:18:03
 */
@Data
public class IdDto<T> {
    @NotNull
    T id;

    public IdDto() {
        super();
    }
}

SelectInputCommon

package com.library.processing.controller.dto;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * 公共输入参数
 *
 * @author ll
 * @since 2023.5.8
 */
@Slf4j
@Data
public class SelectInputCommon<T> {
    /**
     * 查询筛选条件
     */
    private T input;
    /**
     * 页码
     */
    private int current = 1;
    /**
     * 每页size
     */
    private int size = 10;

    public Page<T> getPage() {

        Page<T> page = new Page<T>();
        page.setCurrent(current);
        page.setSize(size);
        return page;
    }

    /**
     * 主要解决查询时前端传参为空值 ("")
     * BeanUtils.copyProperties会把空值带入目标对象中
     * 使用目标对象作为查询对象到mybatisPlus进行查询会导致没有匹配数据;
     * 使用该方法将 空值 转换为 null,避免copy时带入到查询对象
     */
    public T getToNullData() {
        if (this.input == null) {
            return null;
        }
        Class<?> clazz = this.input.getClass();
        // 获取实体类的所有属性,返回Field数组
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            // 可访问私有变量
            field.setAccessible(true);
            // 获取属性类型
            String type = field.getGenericType().toString();
            // 如果type是类类型,则前面包含"class ",后面跟类名
            if ("class java.lang.String".equals(type)) {
                // 将属性的首字母大写
                String methodName = field.getName().replaceFirst(field.getName().substring(0, 1),
                        field.getName().substring(0, 1).toUpperCase());

                try {
                    Method methodGet = clazz.getMethod("get" + methodName);
                    // 调用getter方法获取属性值
                    String str = (String) methodGet.invoke(this.input);
                    //属性为null结束循环
                    if (str == null) {
                        continue;
                    }
                    if ("".equals(str)) {
                        // 为空 设置为null
                        field.set(this.input, null);
                    }
                } catch (Exception e) {
                    log.error("emptyToNull 方法属性转换异常: {}", e.getMessage());
                }
            }
        }
        return this.input;
    }

}

接口文档生成

1.点击生成api接口文档

2.接口文档生成成功

3.接口文档内容如下所示

分页查询接口(接口文档中可以查看详细的入参和出参的含义)

添加修改接口(可以查看参数是否必填等参数)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值