当前位置: 首页 > news >正文

项目一系列-第4章 在线接口文档 代码模板改造

第4章 在线接口文档 & 代码模板改造

4.1 在线接口文档

4.1.1 概述

什么是 Swagger ? 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。

**为什么用 Swagger **? 能实时生成的RESTful 风格的接口文档。

Swagger 官网:https://swagger.io/

**什么是 knife4j **? RuoYi的Swagger前端UI的增强解决方案。

knife4j 的作用?界面比Swagger更友好,还有离线文档,接口排序,安全控制,在线调试,文档清晰,注解增强,容易上手的优点。

knife4j 集成方式参考链接

https://doc.ruoyi.vip/ruoyi-vue/document/cjjc.html#%E9%9B%86%E6%88%90knife4j%E5%AE%9E%E7%8E%B0swagger%E6%96%87%E6%A1%A3%E5%A2%9E%E5%BC%BA

4.1.2 项目集成 Swagger 分几步?

一般用于测试,后续都会用 knife4j 增强UI。

1、添加依赖到ruoyi-admin\pom.xml

        <!-- swagger3--><dependency><groupId>io.springfox</groupId><artifactId>springfox-boot-starter</artifactId></dependency><!-- 防止进入swagger页面报类型转换错误,排除3.0.0中的引用,手动增加1.6.2版本 --><dependency><groupId>io.swagger</groupId><artifactId>swagger-models</artifactId><version>1.6.2</version></dependency><!-- ruoyi-springboot2 / swagger knife4j 配置 --><dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-boot-starter</artifactId><version>3.0.3</version></dependency>

2、核心配置类 com.zzyl.web.core.config.SwaggerConfig

package com.zzyl.web.core.config;import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.zzyl.common.config.RuoYiConfig;
import io.swagger.annotations.ApiOperation;
import io.swagger.models.auth.In;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.Contact;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.service.SecurityScheme;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;/*** Swagger2的接口配置* * @author peterpang*/
@Configuration
public class SwaggerConfig
{/** 系统基础配置 */@Autowiredprivate RuoYiConfig ruoyiConfig;/** 是否开启swagger */@Value("${swagger.enabled}")private boolean enabled;/** 设置请求的统一前缀 */@Value("${swagger.pathMapping}")private String pathMapping;/*** 创建API*/@Beanpublic Docket createRestApi(){return new Docket(DocumentationType.OAS_30)// 是否启用Swagger.enable(enabled)// 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息).apiInfo(apiInfo())// 设置哪些接口暴露给Swagger展示.select()// 扫描所有有注解的api,用这种方式更灵活.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))// 扫描指定包中的swagger注解// .apis(RequestHandlerSelectors.basePackage("com.zzyl.project.tool.swagger"))// 扫描所有 .apis(RequestHandlerSelectors.any()).paths(PathSelectors.any()).build()/* 设置安全模式,swagger可以设置访问token */.securitySchemes(securitySchemes()).securityContexts(securityContexts()).pathMapping(pathMapping);}/*** 安全模式,这里指定token通过Authorization头请求头传递*/private List<SecurityScheme> securitySchemes(){List<SecurityScheme> apiKeyList = new ArrayList<SecurityScheme>();apiKeyList.add(new ApiKey("Authorization", "Authorization", In.HEADER.toValue()));return apiKeyList;}/*** 安全上下文*/private List<SecurityContext> securityContexts(){List<SecurityContext> securityContexts = new ArrayList<>();securityContexts.add(SecurityContext.builder().securityReferences(defaultAuth()).operationSelector(o -> o.requestMappingPattern().matches("/.*")).build());return securityContexts;}/*** 默认的安全上引用*/private List<SecurityReference> defaultAuth(){AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];authorizationScopes[0] = authorizationScope;List<SecurityReference> securityReferences = new ArrayList<>();securityReferences.add(new SecurityReference("Authorization", authorizationScopes));return securityReferences;}/*** 添加摘要信息*/private ApiInfo apiInfo(){// 用ApiInfoBuilder进行定制return new ApiInfoBuilder()// 设置标题.title("标题:中州养老_接口文档")// 描述.description("描述:用于管理中州养老的后端接口")// 作者信息.contact(new Contact(ruoyiConfig.getName(), null, null))// 版本.version("版本号:" + ruoyiConfig.getVersion()).build();}
}

含摘要信息:接口文档标题、描述、作者信息、版本。

3、访问 Swagger UI

方式一:在【若依管理系统】的【系统工具】的【系统接口】中访问。

方式二:访问服务地址+/Swagger-ui/index.html — > http://localhost:8080/swagger-ui/index.html

4.1.3 项目集成 knife4j 分几步?

1、添加依赖到ruoyi-common\pom.xml

<!-- ruoyi-springboot2 / swagger knife4j 配置 --><dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-boot-starter</artifactId><version>3.0.3</version></dependency>

2、修改前端代码的ry-ui\views\tool\swagger\index.vue跳转地址:src: process.env.VUE_APP_BASE_API + "/doc.html"

<template><i-frame v-model:src="url"></i-frame>
</template><script setup>
import iFrame from '@/components/iFrame'const url = ref(import.meta.env.VITE_APP_BASE_API + "/doc.html")//修改位置
</script>

提示:引用knife4j-spring-boot-starter依赖,项目中的swagger依赖可以删除。

4.1.4 Swagger 注解

Swagger 注解能控制接口文档的生成内容。

常见注解如下:

注解说明
@Api用在控制层类上,描述Controller类的作用
@ApiOperation用在方法上,说明方法的用途、作用
@ApiParam(常用)用在参数上,描述单个形参的含义,适用于简单场景
@ApiImplicitParam(不常用)用在Controller类中的方法上方,描述单个形参的含义,适用于参数复杂或者需要详细描述参数的场景
@ApiModel用在实体类上,描述请求体或响应体的实体类的含义
@ApiModelProperty用在实体类的属性上,用来描述实体类中属性的含义

AI协助快速完成注解的编写:

Prompt

给我的代码添加上Swagger注解说明,要求每个参数都要添加说明,请使用下面几个注解:@Api、@ApiOperation、@ApiParam,每个注解只需要使用value属性描述作用即可,不要添加其他属性。

示例代码如下:

1、@Api、@ApiOperation、@ApiParam说明一类接口、接口方法、简单参数。

package com.zzyl.nursing.controller;import java.util.List;
import javax.servlet.http.HttpServletResponse;import com.zzyl.common.core.domain.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.zzyl.common.annotation.Log;
import com.zzyl.common.core.controller.BaseController;
import com.zzyl.common.core.domain.AjaxResult;
import com.zzyl.common.enums.BusinessType;
import com.zzyl.nursing.domain.NursingLevel;
import com.zzyl.nursing.service.INursingLevelService;
import com.zzyl.common.utils.poi.ExcelUtil;
import com.zzyl.common.core.page.TableDataInfo;/*** 护理等级Controller** @author peterpang* @date 2025-08-02*/
@RestController
@RequestMapping("/nursing/level")
@Api("护理等级管理")
public class NursingLevelController extends BaseController
{@Autowiredprivate INursingLevelService nursingLevelService;/*** 查询护理等级列表*/@PreAuthorize("@ss.hasPermi('nursing:level:list')")@GetMapping("/list")@ApiOperation("查询护理等级列表")public TableDataInfo<List<NursingLevel>> list(NursingLevel nursingLevel){startPage();List<NursingLevel> list = nursingLevelService.selectNursingLevelList(nursingLevel);return getDataTable(list);}/*** 导出护理等级列表*/@PreAuthorize("@ss.hasPermi('nursing:level:export')")@Log(title = "护理等级", businessType = BusinessType.EXPORT)@PostMapping("/export")@ApiOperation("导出护理等级列表")public void export(@ApiParam("Http响应对象") HttpServletResponse response,@ApiParam("查询条件对象") NursingLevel nursingLevel){List<NursingLevel> list = nursingLevelService.selectNursingLevelList(nursingLevel);ExcelUtil<NursingLevel> util = new ExcelUtil<NursingLevel>(NursingLevel.class);util.exportExcel(response, list, "护理等级数据");}/*** 获取护理等级详细信息*/@PreAuthorize("@ss.hasPermi('nursing:level:query')")@GetMapping(value = "/{id}")@ApiOperation("获取护理等级详细信息")public R<NursingLevel> getInfo(@ApiParam("要查询的护理等级ID") @PathVariable("id") Long id){
//        return success(nursingLevelService.selectNursingLevelById(id));return R.ok(nursingLevelService.selectNursingLevelById(id));}/*** 新增护理等级*/@PreAuthorize("@ss.hasPermi('nursing:level:add')")@Log(title = "护理等级", businessType = BusinessType.INSERT)@PostMapping@ApiOperation("新增护理等级")public AjaxResult add(@ApiParam("要新增的护理等级") @RequestBody NursingLevel nursingLevel){return toAjax(nursingLevelService.insertNursingLevel(nursingLevel));}/*** 修改护理等级*/@PreAuthorize("@ss.hasPermi('nursing:level:edit')")@Log(title = "护理等级", businessType = BusinessType.UPDATE)@PutMapping@ApiOperation("修改护理等级")public AjaxResult edit(@ApiParam("要修改的护理等级") @RequestBody NursingLevel nursingLevel){return toAjax(nursingLevelService.updateNursingLevel(nursingLevel));}/*** 删除护理等级*/@PreAuthorize("@ss.hasPermi('nursing:level:remove')")@Log(title = "护理等级", businessType = BusinessType.DELETE)@DeleteMapping("/{ids}")@ApiOperation("删除护理等级")public AjaxResult remove(@ApiParam("要删除的护理等级ID数组") @PathVariable Long[] ids){return toAjax(nursingLevelService.deleteNursingLevelByIds(ids));}
}

2、@ApiModel、@ApiModelProperty 说明实体类和实体类参数含义。

在实体类:

package com.zzyl.nursing.domain;import java.math.BigDecimal;import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.zzyl.common.annotation.Excel;
import com.zzyl.common.core.domain.BaseEntity;/*** 护理等级对象 nursing_level* * @author peterpang* @date 2025-08-02*/
@ApiModel("护理等级对象")
public class NursingLevel extends BaseEntity
{private static final long serialVersionUID = 1L;/** 主键ID */@ApiModelProperty("主键ID")private Long id;/** 等级名称 */@Excel(name = "等级名称")@ApiModelProperty("等级名称")private String name;/** 护理计划ID */@Excel(name = "护理计划ID")@ApiModelProperty("护理计划ID")private Long planId;/** 护理费用 */@Excel(name = "护理费用")@ApiModelProperty("护理费用")private BigDecimal fee;/** 状态(0:禁用,1:启用) */@Excel(name = "状态", readConverterExp = "0=:禁用,1:启用")@ApiModelProperty("状态(0:禁用,1:启用)")private Integer status;/** 等级说明 */@Excel(name = "等级说明")@ApiModelProperty("等级说明")private String description;public void setId(Long id) {this.id = id;}public Long getId() {return id;}public void setName(String name) {this.name = name;}public String getName() {return name;}public void setPlanId(Long planId) {this.planId = planId;}public Long getPlanId() {return planId;}public void setFee(BigDecimal fee) {this.fee = fee;}public BigDecimal getFee() {return fee;}public void setStatus(Integer status) {this.status = status;}public Integer getStatus() {return status;}public void setDescription(String description) {this.description = description;}public String getDescription() {return description;}@Overridepublic String toString() {return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE).append("id", getId()).append("name", getName()).append("planId", getPlanId()).append("fee", getFee()).append("status", getStatus()).append("description", getDescription()).append("remark", getRemark()).append("createTime", getCreateTime()).append("updateTime", getUpdateTime()).append("createBy", getCreateBy()).append("updateBy", getUpdateBy()).toString();}
}

在BaseEntity类下:

package com.zzyl.common.core.domain;import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;/*** Entity基类* * @author peterpang*/
@ApiModel("Entity基类")
public class BaseEntity implements Serializable
{private static final long serialVersionUID = 1L;/** 搜索值 */@JsonIgnoreprivate String searchValue;/** 创建者 */@ApiModelProperty("创建者")private String createBy;/** 创建时间 */@ApiModelProperty("创建时间")@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")private Date createTime;/** 更新者 */@ApiModelProperty("更新者")private String updateBy;/** 更新时间 */@ApiModelProperty("更新时间")@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")private Date updateTime;/** 备注 */@ApiModelProperty("备注")private String remark;/** 请求参数 */@JsonInclude(JsonInclude.Include.NON_EMPTY)@ApiModelProperty("请求参数")private Map<String, Object> params;public String getSearchValue(){return searchValue;}public void setSearchValue(String searchValue){this.searchValue = searchValue;}public String getCreateBy(){return createBy;}public void setCreateBy(String createBy){this.createBy = createBy;}public Date getCreateTime(){return createTime;}public void setCreateTime(Date createTime){this.createTime = createTime;}public String getUpdateBy(){return updateBy;}public void setUpdateBy(String updateBy){this.updateBy = updateBy;}public Date getUpdateTime(){return updateTime;}public void setUpdateTime(Date updateTime){this.updateTime = updateTime;}public String getRemark(){return remark;}public void setRemark(String remark){this.remark = remark;}public Map<String, Object> getParams(){if (params == null){params = new HashMap<>();}return params;}public void setParams(Map<String, Object> params){this.params = params;}
}

建议:不要将代码都放到若依框架的admin模块下,新建独立模块能增强项目的扩展性和维护性。

4.2 代码模板改造

4.2.1 若依自动代码生成原理

代码的自动生成基于什么

Velocity模块引擎:一个基于Java的模板引擎,它可以通过特定的语法获取java对象的数据 , 填充到模板中,从而实现界面和java代码的分离

下面是后端的模板代码示例:

// Velocity模板开始,定义包名
package ${packageName}.domain;// 循环导入所需的类库
#foreach ($import in $importList)
import ${import};
#end// 导入其他必要的类库
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.zzyl.common.annotation.Excel;
#if($table.crud || $table.sub)
import com.zzyl.common.core.domain.BaseEntity;
#elseif($table.tree)
import com.zzyl.common.core.domain.TreeEntity;
#end/*** ${functionName}对象 ${tableName}** @author ${author}* @date ${datetime}*/
#if($table.crud || $table.sub)
#set($Entity="BaseEntity")
#elseif($table.tree)
#set($Entity="TreeEntity")
#end// 定义Java类,继承自BaseEntity或TreeEntity
public class ${ClassName} extends ${Entity}
{// 序列化版本IDprivate static final long serialVersionUID = 1L;// 循环定义类中的属性#foreach ($column in $columns)#if(!$table.isSuperColumn($column.javaField))// 添加属性的注释/** $column.columnComment */// 根据属性是否为列表类型,决定是否添加Excel注解#if($column.list)#set($parentheseIndex=$column.columnComment.indexOf("("))#if($parentheseIndex != -1)#set($comment=$column.columnComment.substring(0, $parentheseIndex))#else#set($comment=$column.columnComment)#end#if($parentheseIndex != -1)// 如果属性名中有括号,则截取括号前的内容作为Excel注解的名字@Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()")#else// 如果是日期类型,添加JSON格式化注解#elseif($column.javaType == 'Date')@JsonFormat(pattern = "yyyy-MM-dd")@Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd")#else// 其他情况直接添加Excel注解@Excel(name = "${comment}")#end#end// 定义属性private $column.javaType $column.javaField;#end#end// 如果是子表,添加子表的集合属性#if($table.sub)/** $table.subTable.functionName信息 */private List<${subClassName}> ${subclassName}List;#end// 生成getter和setter方法#foreach ($column in $columns)#if(!$table.isSuperColumn($column.javaField))#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]"))#set($AttrName=$column.javaField)#else#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})#end// setter方法public void set${AttrName}($column.javaType $column.javaField){this.$column.javaField = $column.javaField;}// getter方法public $column.javaType get${AttrName}(){return $column.javaField;}#end#end// 如果是子表,生成子表集合的getter和setter方法#if($table.sub)public List<${subClassName}> get${subClassName}List(){return ${subclassName}List;}public void set${subClassName}List(List<${subClassName}> ${subclassName}List){this.${subclassName}List = ${subclassName}List;}#end// 重写toString方法@Overridepublic String toString() {return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)#foreach ($column in $columns)#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]"))#set($AttrName=$column.javaField)#else#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})#end.append("${column.javaField}", get${AttrName}())#end#if($table.sub).append("${subclassName}List", get${subClassName}List())#end.toString();}
}
  • Velocity语法解释
    • ${variable}: 表示插入变量值。
    • #foreach#end: 循环结构,用于遍历列表。
    • #if#end: 条件判断结构。
    • #set: 设置变量。
    • #elseif: 条件分支。
  • Java类结构说明
    • 包声明: 定义类所在的包。
    • 导入语句: 导入类所需要的类库。
    • 类注释: 描述类的功能和作者信息。
    • 类定义: 定义类并继承自BaseEntityTreeEntity
    • 属性定义: 根据模板参数定义类的属性。
    • 属性注释: 对属性进行描述。
    • Excel注解: 标记属性以方便导出Excel。
    • getter和setter方法: 自动生成属性的访问方法。
    • 子表集合: 如果是子表,则定义子表集合。
    • 重写toString方法: 提供类实例的字符串表示形式。

下面是前端的模板代码示例:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>velocity快速入门</title>
</head>
<body><h3>心怀梦想,坚持不懈,成功即在前方。${message}</h3></body>
</html>

下面是代码生成功能的核心代码

代码位置:com.zzyl.generator.service.GenTableServiceImpl.generatorCode()

/*** 生成代码* @param tableName 表名* @param zip       压缩输出流*/
private void generatorCode(String tableName, ZipOutputStream zip)
{// 查询表信息GenTable table = genTableMapper.selectGenTableByName(tableName);// 设置主子表信息setSubTable(table);// 设置主键列信息setPkColumn(table);// 初始化Velocity引擎VelocityInitializer.initVelocity();// 准备Velocity上下文   获取该表的详细数据,并设置模板所需要的数据模型VelocityContext context = VelocityUtils.prepareContext(table);// 获取模板列表   读取resources/vm目录中的定义的模板文件List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory(), table.getTplWebType());for (String template : templates){// 渲染模板StringWriter sw = new StringWriter();Template tpl = Velocity.getTemplate(template, Constants.UTF8);// 合并模板和数据模型tpl.merge(context, sw);try{// 将渲染结果添加到zip文件zip.putNextEntry(new ZipEntry(VelocityUtils.getFileName(template, table)));IOUtils.write(sw.toString(), zip, Constants.UTF8);IOUtils.closeQuietly(sw);zip.flush();zip.closeEntry();}catch (IOException e){// 记录日志log.error("渲染模板失败,表名:" + table.getTableName(), e);}}
}

当前自动生成的代码有哪些问题:

  • 为了生成接口文档,要手动添加 Swagger 相关注解。
  • 实体类中含大量的gettersettertoString方法。
  • 希望使用更方便的MyBatis-Plus,而不是MyBatis。
  • 日期时间从Date更换为更现代的LocalDateTime

改造目标:

  • 支持自动添加Swagger的注解
  • 支持Lombok
  • 支持MyBatis-Plus
  • 支持LocalDateTime

4.2.2 集成 Lombok

1、依赖导入

  • 在父工程管理lombok的依赖

    	<!-- 版本统一管理 --><properties><lombok.version>1.18.22</lombok.version></properties><dependencyManagement><dependencies><!-- 其他依赖省略... --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${lombok.version}</version></dependency></dependencies></dependencyManagement>
    
  • 在若依的common模块中引入lombok依赖

    <dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId>
    </dependency>
    

    若发现没有反射信息,要手动刷新一下依赖,确保是最新依赖信息。

2、修改模板代码domain.java.vm

主要修改为:

  • 添加lombok注解
  • 删除生成属性的 get / set 方法的代码
  • 删除 toString 方法及其引用工具类的代码
  • 删除主子表相关的 get / set 方法的代码

最终代码如下:

package ${packageName}.domain;#foreach ($import in $importList)
import ${import};
#end
import com.zzyl.common.annotation.DataScope;
import com.zzyl.common.annotation.Excel;
#if($table.crud || $table.sub)
import com.zzyl.common.core.domain.BaseEntity;
#elseif($table.tree)
import com.zzyl.common.core.domain.TreeEntity;
#end
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;/*** ${functionName}对象 ${tableName}** @author ${author}* @date ${datetime}*/
#if($table.crud || $table.sub)
#set($Entity="BaseEntity")
#elseif($table.tree)
#set($Entity="TreeEntity")
#end
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ${ClassName} extends ${Entity}
{private static final long serialVersionUID = 1L;#foreach ($column in $columns)
#if(!$table.isSuperColumn($column.javaField))/** $column.columnComment */
#if($column.list)
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
#if($parentheseIndex != -1)@Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()")
#elseif($column.javaType == 'Date')@JsonFormat(pattern = "yyyy-MM-dd")@Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd")
#else@Excel(name = "${comment}")
#end
#endprivate $column.javaType $column.javaField;#end
#end
#if($table.sub)/** $table.subTable.functionName信息 */private List<${subClassName}> ${subclassName}List;#end
}

3、代码预览并拷贝到项目中测试

无论是代码预览还是代码拷贝,都可以在【若依管理系统】的【系统工具】的【代码生成】中点击相应项的【预览】按钮进去页面中操作。

4.2.3 集成 MyBatis-Plus

将MyBatis-Plus集成到项目中很多细节,颇为麻烦,可以参考链接项目进行MP的完整改造:https://gitee.com/tellsea/ruoyi-vue-plus

1、添加依赖

在父工程中的pom文件中添加MyBatis-Plus的依赖,如下:

<properties><mybatis-plus-spring-boot.version>3.5.3.1</mybatis-plus-spring-boot.version>
</properties><!-- 依赖声明 -->
<dependencyManagement><dependencies><!-- MyBatis-Plus 增强CRUD --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>${mybatis-plus-spring-boot.version}</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-annotation</artifactId><version>${mybatis-plus-spring-boot.version}</version></dependency></dependencies>
</dependencyManagement>

在若依的common模块中新增mybatis-plus的依赖

<!--MyBatisPlus-->
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-annotation</artifactId>
</dependency>

2、MP核心配置

appliation.yml文件中新增MyBatisPlus配置,同时删除Mybatis相关的配置

# MyBatisPlus配置
mybatis-plus:# 搜索指定包别名typeAliasesPackage: com.zzyl.**.domain# 配置mapper的扫描,找到所有的mapper.xml映射文件mapperLocations: classpath*:mapper/**/*Mapper.xml# 全局配置global-config:db-config:id-type: auto   #id生成策略为自增configuration: map-underscore-to-camel-case: true    #字段与属性,自动转换为驼峰命名

新增核心配置类,删除MyBatisConfig

package com.zzyl.framework.config;import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;/*** Mybatis Plus 配置** @author ruoyi*/
@EnableTransactionManagement(proxyTargetClass = true)
@Configuration
public class MybatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();// 分页插件interceptor.addInnerInterceptor(paginationInnerInterceptor());// 乐观锁插件interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor());// 阻断插件interceptor.addInnerInterceptor(blockAttackInnerInterceptor());return interceptor;}/*** 分页插件,自动识别数据库类型 https://baomidou.com/guide/interceptor-pagination.html*/public PaginationInnerInterceptor paginationInnerInterceptor() {PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();// 设置数据库类型为mysqlpaginationInnerInterceptor.setDbType(DbType.MYSQL);// 设置最大单页限制数量,默认 500 条,-1 不受限制paginationInnerInterceptor.setMaxLimit(-1L);return paginationInnerInterceptor;}/*** 乐观锁插件 https://baomidou.com/guide/interceptor-optimistic-locker.html*/public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() {return new OptimisticLockerInnerInterceptor();}/*** 如果是对全表的删除或更新操作,就会终止该操作 https://baomidou.com/guide/interceptor-block-attack.html*/public BlockAttackInnerInterceptor blockAttackInnerInterceptor() {return new BlockAttackInnerInterceptor();}}

3、修改生成的项目源码,为修改模板代码铺垫:

mapper层:

在这里插入图片描述

service层的接口和实现类:

service接口:

在这里插入图片描述

serviceImp实现类:

在这里插入图片描述

并且更改实现类中的CRUD接口为MP的接口方法:

package com.zzyl.nursing.service.impl;import java.util.Arrays;
import java.util.List;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zzyl.common.utils.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.zzyl.nursing.mapper.NursingProjectMapper;
import com.zzyl.nursing.domain.NursingProject;
import com.zzyl.nursing.service.INursingProjectService;/*** 护理项目Service业务层处理* * @author ruoyi* @date 2024-09-26*/
@Service
public class NursingProjectServiceImpl extends ServiceImpl<NursingProjectMapper, NursingProject> implements INursingProjectService
{@Autowiredprivate NursingProjectMapper nursingProjectMapper;/*** 查询护理项目* * @param id 护理项目主键* @return 护理项目*/@Overridepublic NursingProject selectNursingProjectById(Long id){
//        return nursingProjectMapper.selectNursingProjectById(id);return getById(id);}/*** 查询护理项目列表* * @param nursingProject 护理项目* @return 护理项目*/@Overridepublic List<NursingProject> selectNursingProjectList(NursingProject nursingProject){return nursingProjectMapper.selectNursingProjectList(nursingProject);}/*** 新增护理项目* * @param nursingProject 护理项目* @return 结果*/@Overridepublic int insertNursingProject(NursingProject nursingProject){nursingProject.setCreateTime(DateUtils.getNowDate());
//        return nursingProjectMapper.insertNursingProject(nursingProject);return save(nursingProject) ? 1 : 0;}/*** 修改护理项目* * @param nursingProject 护理项目* @return 结果*/@Overridepublic int updateNursingProject(NursingProject nursingProject){nursingProject.setUpdateTime(DateUtils.getNowDate());
//        return nursingProjectMapper.updateNursingProject(nursingProject);return updateById(nursingProject) ? 1 : 0;}/*** 批量删除护理项目* * @param ids 需要删除的护理项目主键* @return 结果*/@Overridepublic int deleteNursingProjectByIds(Long[] ids){
//        return nursingProjectMapper.deleteNursingProjectByIds(ids);return removeByIds(Arrays.asList(ids)) ? 1 : 0;}/*** 删除护理项目信息* * @param id 护理项目主键* @return 结果*/@Overridepublic int deleteNursingProjectById(Long id){
//        return nursingProjectMapper.deleteNursingProjectById(id);return removeById(id) ? 1 : 0;}
}

4、若依的common模块BaseEntity 类要添加如下注解,@TableField(exist = false)表示,表示该字段不存在于数据库表中:

/*** Entity基类* * @author ruoyi*/
public class BaseEntity implements Serializable
{private static final long serialVersionUID = 1L;/** 搜索值 */@JsonIgnore@TableField(exist = false)private String searchValue;/** 请求参数 */@JsonInclude(JsonInclude.Include.NON_EMPTY)@TableField(exist = false)private Map<String, Object> params;}

5、改造模板代码

在目前的模板文件中,咱们需要修改的模板共有3个,分别是:

  • mapper.java.vm 继承BaseMapper
  • service.java.vm 继承IService<T>
  • serviceImpl.java.vm 继承ServiceImpl<XxxMapper, T> 常见方法的使用(单表的增删改查)

mapper.java.vm

在这里插入图片描述

service.java.vm

在这里插入图片描述

serviceImpl.java.vm

在这里插入图片描述

并且在代码中使用MP的getByIdsaveupdateByIdremoveByIdsremoveById

package ${packageName}.service.impl;import java.util.List;
#foreach ($column in $columns)
#if($column.javaField == 'createTime' || $column.javaField == 'updateTime')
import com.zzyl.common.utils.DateUtils;
#break
#end
#end
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#if($table.sub)
import java.util.ArrayList;
import com.zzyl.common.utils.StringUtils;
import org.springframework.transaction.annotation.Transactional;
import ${packageName}.domain.${subClassName};
#end
import ${packageName}.mapper.${ClassName}Mapper;
import ${packageName}.domain.${ClassName};
import ${packageName}.service.I${ClassName}Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import java.util.Arrays;/*** ${functionName}Service业务层处理* * @author ${author}* @date ${datetime}*/
@Service
public class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}> implements I${ClassName}Service
{@Autowiredprivate ${ClassName}Mapper ${className}Mapper;/*** 查询${functionName}* * @param ${pkColumn.javaField} ${functionName}主键* @return ${functionName}*/@Overridepublic ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}){##return ${className}Mapper.select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField});return getById(${pkColumn.javaField});}/*** 查询${functionName}列表* * @param ${className} ${functionName}* @return ${functionName}*/@Overridepublic List<${ClassName}> select${ClassName}List(${ClassName} ${className}){return ${className}Mapper.select${ClassName}List(${className});}/*** 新增${functionName}* * @param ${className} ${functionName}* @return 结果*/
#if($table.sub)@Transactional
#end@Overridepublic int insert${ClassName}(${ClassName} ${className}){
#foreach ($column in $columns)
#if($column.javaField == 'createTime')${className}.setCreateTime(DateUtils.getNowDate());
#end
#end
#if($table.sub)int rows = ${className}Mapper.insert${ClassName}(${className});insert${subClassName}(${className});return rows;
#else##return ${className}Mapper.insert${ClassName}(${className});return save(${className}) ? 1 : 0;
#end}/*** 修改${functionName}* * @param ${className} ${functionName}* @return 结果*/
#if($table.sub)@Transactional
#end@Overridepublic int update${ClassName}(${ClassName} ${className}){
#foreach ($column in $columns)
#if($column.javaField == 'updateTime')${className}.setUpdateTime(DateUtils.getNowDate());
#end
#end
#if($table.sub)${className}Mapper.delete${subClassName}By${subTableFkClassName}(${className}.get${pkColumn.capJavaField}());insert${subClassName}(${className});
#end
##        return ${className}Mapper.update${ClassName}(${className});return updateById(${className}) ? 1 : 0;}/*** 批量删除${functionName}* * @param ${pkColumn.javaField}s 需要删除的${functionName}主键* @return 结果*/
#if($table.sub)@Transactional
#end@Overridepublic int delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s){
#if($table.sub)${className}Mapper.delete${subClassName}By${subTableFkClassName}s(${pkColumn.javaField}s);
#end
##        return ${className}Mapper.delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaField}s);return removeByIds(Arrays.asList(${pkColumn.javaField}s)) ? 1 : 0;}/*** 删除${functionName}信息* * @param ${pkColumn.javaField} ${functionName}主键* @return 结果*/
#if($table.sub)@Transactional
#end@Overridepublic int delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}){
#if($table.sub)${className}Mapper.delete${subClassName}By${subTableFkClassName}(${pkColumn.javaField});
#end
##        return ${className}Mapper.delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField});return removeById(${pkColumn.javaField}) ? 1 : 0;}
#if($table.sub)/*** 新增${subTable.functionName}信息* * @param ${className} ${functionName}对象*/public void insert${subClassName}(${ClassName} ${className}){List<${subClassName}> ${subclassName}List = ${className}.get${subClassName}List();${pkColumn.javaType} ${pkColumn.javaField} = ${className}.get${pkColumn.capJavaField}();if (StringUtils.isNotNull(${subclassName}List)){List<${subClassName}> list = new ArrayList<${subClassName}>();for (${subClassName} ${subclassName} : ${subclassName}List){${subclassName}.set${subTableFkClassName}(${pkColumn.javaField});list.add(${subclassName});}if (list.size() > 0){${className}Mapper.batch${subClassName}(list);}}}
#end
}

注意:要检查BaseEntity类中的字段有没有加上@TableField(exist = false)表示,表示该字段不存在于数据库表中

6、代码预览并测试代码

改造完成后,重新查看若依生成的代码:

在这里插入图片描述

在这里插入图片描述

接下来,先用代码生成功能重新生成一次三层架构的代码,替换到项目中,测试通过后再接着往下操作。

7、MP字段自动填充

官网链接:https://baomidou.com/guides/auto-fill-field/

在MybatisPlus中通过两步可以实现这个功能:

在实体类中,使用@TableField注解,来标明哪些字段是需要自动填充的,并且需要指定填充策略

package com.zzyl.common.core.domain;import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.Swagger.annotations.ApiModel;
import io.Swagger.annotations.ApiModelProperty;/*** Entity基类* * @author ruoyi*/
@ApiModel("Entity基类")
public class BaseEntity implements Serializable
{private static final long serialVersionUID = 1L;/** 搜索值 */@JsonIgnore@TableField(exist = false)private String searchValue;/** 创建者 */@ApiModelProperty(value = "创建者")@TableField(fill = FieldFill.INSERT)private String createBy;/** 创建时间 */@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")@ApiModelProperty(value = "创建时间")@TableField(fill = FieldFill.INSERT)private Date createTime;/** 更新者 */@ApiModelProperty(value = "更新者")@TableField(fill = FieldFill.UPDATE)private String updateBy;/** 更新时间 */@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")@ApiModelProperty(value = "更新时间")@TableField(fill = FieldFill.UPDATE)private Date updateTime;/** 备注 */@ApiModelProperty(value = "备注")private String remark;/** 请求参数 */@JsonInclude(JsonInclude.Include.NON_EMPTY)@ApiModelProperty(value = "请求参数")@TableField(exist = false)private Map<String, Object> params;}

在若依的framework模块中新增MyMetaObjectHandler 来处理字段自动填充

在这里插入图片描述

详细的代码如下:

package com.zzyl.framework.interceptor;import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.zzyl.common.core.domain.model.LoginUser;
import com.zzyl.common.utils.SecurityUtils;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;import java.util.Date;@Component
public class MyMetaObjectHandler implements MetaObjectHandler {@Overridepublic void insertFill(MetaObject metaObject) {this.strictInsertFill(metaObject, "createTime", Date.class, DateUtils.getNowDate());this.strictInsertFill(metaObject, "createBy", String.class, String.valueOf(getLoginUser()));}@Overridepublic void updateFill(MetaObject metaObject) {this.strictUpdateFill(metaObject, "updateTime", Date.class, DateUtils.getNowDate());this.strictUpdateFill(metaObject, "updateBy", String.class, String.valueOf(getLoginUser()));}/*** 获取当前登录人的ID** @return 登录人ID*/public Long getLoginUser() {LoginUser loginUser = SecurityUtils.getLoginUser();if (loginUser != null) {return loginUser.getUserId();}return 1L;}
}

检查字段填充是否有效,在数据库中看保存的创建时间和更新时间是否是当前时区的时间。

如果不对,换一种更新方式this.setFieldValByName()

package com.zzyl.framework.interceptor;import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.zzyl.common.core.domain.model.LoginUser;
import com.zzyl.common.utils.SecurityUtils;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;import java.util.Date;@Component
public class MyMetaObjectHandler implements MetaObjectHandler {@Overridepublic void insertFill(MetaObject metaObject) {this.strictInsertFill(metaObject, "createTime", Date.class, new Date());// 自动填充创建人this.strictInsertFill(metaObject, "createBy", String.class, String.valueOf(getLoginUser()));}@Overridepublic void updateFill(MetaObject metaObject) {this.setFieldValByName("updateTime", new Date(), metaObject);this.setFieldValByName("updateBy", String.valueOf(getLoginUser()), metaObject);
//        this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
//        this.strictUpdateFill(metaObject, "updateBy", String.class, String.valueOf(getLoginUser()));}public Long getLoginUser() {LoginUser loginUser = SecurityUtils.getLoginUser();if (loginUser != null) {return loginUser.getUserId();}return 1L;}
}

8.更改模板代码在 serviceImp 中,去掉update和inseet的时间填充。

下面是 serviceImpl.java.vm 模板代码:

package ${packageName}.service.impl;import java.util.List;
#foreach ($column in $columns)
#if($column.javaField == 'createTime' || $column.javaField == 'updateTime')
import com.zzyl.common.utils.DateUtils;
#break
#end
#end
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#if($table.sub)
import java.util.ArrayList;
import com.zzyl.common.utils.StringUtils;
import org.springframework.transaction.annotation.Transactional;
import ${packageName}.domain.${subClassName};
#end
import ${packageName}.mapper.${ClassName}Mapper;
import ${packageName}.domain.${ClassName};
import ${packageName}.service.I${ClassName}Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import java.util.Arrays;/*** ${functionName}Service业务层处理* * @author ${author}* @date ${datetime}*/
@Service
public class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}> implements I${ClassName}Service
{@Autowiredprivate ${ClassName}Mapper ${className}Mapper;/*** 查询${functionName}* * @param ${pkColumn.javaField} ${functionName}主键* @return ${functionName}*/@Overridepublic ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}){
##        return ${className}Mapper.select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField});return getById(${pkColumn.javaField});}/*** 查询${functionName}列表* * @param ${className} ${functionName}* @return ${functionName}*/@Overridepublic List<${ClassName}> select${ClassName}List(${ClassName} ${className}){return ${className}Mapper.select${ClassName}List(${className});}/*** 新增${functionName}* * @param ${className} ${functionName}* @return 结果*/
#if($table.sub)@Transactional
#end@Overridepublic int insert${ClassName}(${ClassName} ${className}){
###foreach ($column in $columns)
###if($column.javaField == 'createTime')
##        ${className}.setCreateTime(DateUtils.getNowDate());
###end
###end
#if($table.sub)int rows = ${className}Mapper.insert${ClassName}(${className});insert${subClassName}(${className});return rows;
#else
##        return ${className}Mapper.insert${ClassName}(${className});return save(${className}) ? 1 : 0;
#end}/*** 修改${functionName}* * @param ${className} ${functionName}* @return 结果*/
#if($table.sub)@Transactional
#end@Overridepublic int update${ClassName}(${ClassName} ${className}){
###foreach ($column in $columns)
###if($column.javaField == 'updateTime')
##        ${className}.setUpdateTime(DateUtils.getNowDate());
###end
###end
#if($table.sub)${className}Mapper.delete${subClassName}By${subTableFkClassName}(${className}.get${pkColumn.capJavaField}());insert${subClassName}(${className});
#end
##        return ${className}Mapper.update${ClassName}(${className});return updateById(${className}) ? 1 : 0;}/*** 批量删除${functionName}* * @param ${pkColumn.javaField}s 需要删除的${functionName}主键* @return 结果*/
#if($table.sub)@Transactional
#end@Overridepublic int delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s){
#if($table.sub)${className}Mapper.delete${subClassName}By${subTableFkClassName}s(${pkColumn.javaField}s);
#end
##        return ${className}Mapper.delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaField}s);return removeByIds(Arrays.asList(${pkColumn.javaField}s)) ? 1 : 0;}/*** 删除${functionName}信息* * @param ${pkColumn.javaField} ${functionName}主键* @return 结果*/
#if($table.sub)@Transactional
#end@Overridepublic int delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}){
#if($table.sub)${className}Mapper.delete${subClassName}By${subTableFkClassName}(${pkColumn.javaField});
#end
##        return ${className}Mapper.delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField});return removeById(${pkColumn.javaField}) ? 1 : 0;}
#if($table.sub)/*** 新增${subTable.functionName}信息* * @param ${className} ${functionName}对象*/public void insert${subClassName}(${ClassName} ${className}){List<${subClassName}> ${subclassName}List = ${className}.get${subClassName}List();${pkColumn.javaType} ${pkColumn.javaField} = ${className}.get${pkColumn.capJavaField}();if (StringUtils.isNotNull(${subclassName}List)){List<${subClassName}> list = new ArrayList<${subClassName}>();for (${subClassName} ${subclassName} : ${subclassName}List){${subclassName}.set${subTableFkClassName}(${pkColumn.javaField});list.add(${subclassName});}if (list.size() > 0){${className}Mapper.batch${subClassName}(list);}}}
#end
}

下面是要去掉的代码:

###foreach ($column in $columns)
###if($column.javaField == 'createTime')
##        ${className}.setCreateTime(DateUtils.getNowDate());
###end
###end###foreach ($column in $columns)
###if($column.javaField == 'updateTime')
##        ${className}.setUpdateTime(DateUtils.getNowDate());
###end
###end

4.2.4 集成 Swagger

需要修改2个模板文件:controller.java.vmdomain.java.vm,先来修改controller.java.vm文件,具体的添加的内容如下:

在controller.java.vm中,在头添加如下代码

import com.zzyl.common.core.domain.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;

其他的主要修改部位是添加 @Api@ApiOperation@ApiParam以及修改 successR.okTableDataInfo 改为泛型 TableDataInfo<List<${ClassName}>>,下面是示例代码:

package ${packageName}.controller;import com.zzyl.common.core.domain.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.zzyl.common.annotation.Log;
import com.zzyl.common.core.controller.BaseController;
import com.zzyl.common.core.domain.AjaxResult;
import com.zzyl.common.enums.BusinessType;
import ${packageName}.domain.${ClassName};
import ${packageName}.service.I${ClassName}Service;
import com.zzyl.common.utils.poi.ExcelUtil;
#if($table.crud || $table.sub)
import com.zzyl.common.core.page.TableDataInfo;
#elseif($table.tree)
#end/*** ${functionName}Controller* * @author ${author}* @date ${datetime}*/
@RestController
@RequestMapping("/${moduleName}/${businessName}")
@Api(tags = "${functionName}相关接口")
public class ${ClassName}Controller extends BaseController
{@Autowiredprivate I${ClassName}Service ${className}Service;/*** 查询${functionName}列表*/@PreAuthorize("@ss.hasPermi('${permissionPrefix}:list')")@GetMapping("/list")@ApiOperation("查询${functionName}列表")
#if($table.crud || $table.sub)public TableDataInfo<List<${ClassName}>> list(@ApiParam(value = "${functionName}查询条件") ${ClassName} ${className}){startPage();List<${ClassName}> list = ${className}Service.select${ClassName}List(${className});return getDataTable(list);}
#elseif($table.tree)public AjaxResult list(${ClassName} ${className}){List<${ClassName}> list = ${className}Service.select${ClassName}List(${className});return success(list);}
#end/*** 导出${functionName}列表*/@PreAuthorize("@ss.hasPermi('${permissionPrefix}:export')")@Log(title = "${functionName}", businessType = BusinessType.EXPORT)@PostMapping("/export")@ApiOperation("导出${functionName}列表")public void export(@ApiParam(value = "HTTP响应对象,用于输出导出文件") HttpServletResponse response,@ApiParam(value = "${functionName}查询条件") ${ClassName} ${className}){List<${ClassName}> list = ${className}Service.select${ClassName}List(${className});ExcelUtil<${ClassName}> util = new ExcelUtil<${ClassName}>(${ClassName}.class);util.exportExcel(response, list, "${functionName}数据");}/*** 获取${functionName}详细信息*/@PreAuthorize("@ss.hasPermi('${permissionPrefix}:query')")@GetMapping(value = "/{${pkColumn.javaField}}")@ApiOperation("获取${functionName}详细信息")public R<${ClassName}> getInfo(@ApiParam(value = "${functionName}ID") @PathVariable("${pkColumn.javaField}") ${pkColumn.javaType} ${pkColumn.javaField}){return R.ok(${className}Service.select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField}));}/*** 新增${functionName}*/@PreAuthorize("@ss.hasPermi('${permissionPrefix}:add')")@Log(title = "${functionName}", businessType = BusinessType.INSERT)@PostMapping@ApiOperation("新增${functionName}")public AjaxResult add(@ApiParam(value = "${functionName}信息", required = true) @RequestBody ${ClassName} ${className}){return toAjax(${className}Service.insert${ClassName}(${className}));}/*** 修改${functionName}*/@PreAuthorize("@ss.hasPermi('${permissionPrefix}:edit')")@Log(title = "${functionName}", businessType = BusinessType.UPDATE)@PutMapping@ApiOperation("修改${functionName}")public AjaxResult edit(@ApiParam(value = "${functionName}信息", required = true) @RequestBody ${ClassName} ${className}){return toAjax(${className}Service.update${ClassName}(${className}));}/*** 删除${functionName}*/@PreAuthorize("@ss.hasPermi('${permissionPrefix}:remove')")@Log(title = "${functionName}", businessType = BusinessType.DELETE)@DeleteMapping("/{${pkColumn.javaField}s}")@ApiOperation("删除${functionName}")public AjaxResult remove(@ApiParam(value = "${functionName}ID数组", required = true) @PathVariable ${pkColumn.javaType}[] ${pkColumn.javaField}s){return toAjax(${className}Service.delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaField}s));}
}

下面是TableDataInfo改造为TableDataInfo<T>代码:

package com.zzyl.common.core.page;import java.io.Serializable;
import java.util.List;/*** 表格分页数据对象* * @author ruoyi*/
public class TableDataInfo<T> implements Serializable
{private static final long serialVersionUID = 1L;/** 总记录数 */private long total;/** 列表数据 */private List<T> rows;/** 消息状态码 */private int code;/** 消息内容 */private String msg;/*** 表格数据对象*/public TableDataInfo(){}/*** 分页* * @param list 列表数据* @param total 总记录数*/public TableDataInfo(List<T> list, long total){this.rows = list;this.total = total;}public long getTotal(){return total;}public void setTotal(long total){this.total = total;}public List<T> getRows(){return rows;}public void setRows(List<T> rows){this.rows = rows;}public int getCode(){return code;}public void setCode(int code){this.code = code;}public String getMsg(){return msg;}public void setMsg(String msg){this.msg = msg;}
}

再来修改 domain.java.vm 文件,先在头加上如下代码:

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;

再添加 @ApiModel@ApiModelProperty ,下面是修改后的示例代码:

package ${packageName}.domain;#foreach ($import in $importList)
import ${import};
#end
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import com.zzyl.common.annotation.DataScope;
import com.zzyl.common.annotation.Excel;
#if($table.crud || $table.sub)
import com.zzyl.common.core.domain.BaseEntity;
#elseif($table.tree)
import com.zzyl.common.core.domain.TreeEntity;
#end
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;/*** ${functionName}对象 ${tableName}** @author ${author}* @date ${datetime}*/
#if($table.crud || $table.sub)
#set($Entity="BaseEntity")
#elseif($table.tree)
#set($Entity="TreeEntity")
#end
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel("${functionName}信息")
public class ${ClassName} extends ${Entity}
{private static final long serialVersionUID = 1L;#foreach ($column in $columns)
#if(!$table.isSuperColumn($column.javaField))/** $column.columnComment */
#if($column.list)
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
#if($parentheseIndex != -1)@Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()")
#elseif($column.javaType == 'Date')@JsonFormat(pattern = "yyyy-MM-dd")@Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd")
#else@Excel(name = "${comment}")
#end
#end@ApiModelProperty("${column.columnComment}")private $column.javaType $column.javaField;#end
#end
#if($table.sub)/** $table.subTable.functionName信息 */private List<${subClassName}> ${subclassName}List;#end
}

我们来重启一下服务,看一下代码预览:

在这里插入图片描述

4.2.5 支持LocalDateTime类型

1、前端改为支持LocalDateTime的Java类型

打开前端项目,找到src/views/tool/gen/editTable.vue文件,在这个文件添加中一行代码,如下:

在这里插入图片描述

<el-option label="LocalDateTime" value="LocalDateTime" />

2、后端改为支持LocalDateTime的类型

打开VelocityUtils类,这里面有处理导入包的逻辑,找到getImportList方法,在其中添加对应的包导入如下代码:

// 如果字段类型在前端选择的是LocalDateTime,则导入对应的包if (!column.isSuperColumn() && GenConstants.TYPE_LOCAL_DATE_TYPE.equals(column.getJavaType())){importList.add("java.time.LocalDateTime");// 导入这个是为了格式化日期importList.add("com.fasterxml.jackson.annotation.JsonFormat");}

这里是导入后的示例代码:

/*** 根据列类型获取导入包* * @param genTable 业务表对象* @return 返回需要导入的包列表*/
public static HashSet<String> getImportList(GenTable genTable)
{List<GenTableColumn> columns = genTable.getColumns();GenTable subGenTable = genTable.getSubTable();HashSet<String> importList = new HashSet<String>();if (StringUtils.isNotNull(subGenTable)){importList.add("java.util.List");}for (GenTableColumn column : columns){if (!column.isSuperColumn() && GenConstants.TYPE_DATE.equals(column.getJavaType())){importList.add("java.util.Date");importList.add("com.fasterxml.jackson.annotation.JsonFormat");}// 如果字段类型在前端选择的是LocalDateTime,则导入对应的包if (!column.isSuperColumn() && GenConstants.TYPE_LOCAL_DATE_TYPE.equals(column.getJavaType())){importList.add("java.time.LocalDateTime");// 导入这个是为了格式化日期importList.add("com.fasterxml.jackson.annotation.JsonFormat");}else if (!column.isSuperColumn() && GenConstants.TYPE_BIGDECIMAL.equals(column.getJavaType())){importList.add("java.math.BigDecimal");}}return importList;
}

并且新增代码中的 TYPE_LOCAL_DATE_TYPE需要定义在 GenConstants 常量类中。

/** JDK8时间类型 */
public static final String TYPE_LOCAL_DATE_TYPE= "LocalDateTime";

3、修改模板代码

修改模块文件domain.java.vm文件,修改的内容如下:

在这里插入图片描述

#elseif($column.javaType == 'Date' || $column.javaType == 'LocalDateTime')@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")

4、生成代码进行预览并测试,这是重要的一步。

4.3 自动生成代码的默认设置

自动生成代码有哪些默认设置:

  • 默认作者
  • 默认生成包路径
  • 默认物理类型
  • 默认Java类型

4.3.1 修改作者和生成包路径

若依默认生成的包路径和模块名都是system,效果如下:

在这里插入图片描述

而咱们项目中用到的包路径和模块名都是nursing,如果要将这里的默认值改为nursing,只需要修改配置文件即可:

修改zzyl-generator模块下的generator.yml配置文件,修改后的内容如下:

# 代码生成
gen:# 作者author: peterpeng# 默认生成包路径 system 需改成自己的模块名称 如 system monitor toolpackageName: com.zzyl.nursing# 自动去除表前缀,默认是falseautoRemovePre: false# 表前缀(生成类名不会包含表前缀,多个用逗号分隔)tablePrefix: sys_# 是否允许生成文件覆盖到本地(自定义路径),默认不允许allowOverwrite: false

4.3.2 修改物理类型对应的Java类型

若依默认生成的字段信息中很多数据库中的int类型和tinyint类型对应的Java类型都是Long,如下图所示:

在这里插入图片描述

可以通过修改zzyl-generator模块下的GenUtils工具类中的代码来将其统一改为INTEGER类型,具体的修改位置为:

/*** 初始化列属性字段*/
public static void initColumnField(GenTableColumn column, GenTable table)
{String dataType = getDbType(column.getColumnType());String columnName = column.getColumnName();column.setTableId(table.getTableId());column.setCreateBy(table.getCreateBy());// 设置java字段名column.setJavaField(StringUtils.toCamelCase(columnName));// 设置默认类型column.setJavaType(GenConstants.TYPE_STRING);column.setQueryType(GenConstants.QUERY_EQ);if (arraysContains(GenConstants.COLUMNTYPE_STR, dataType) || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType)){// 字符串长度超过500设置为文本域Integer columnLength = getColumnLength(column.getColumnType());String htmlType = columnLength >= 500 || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType) ? GenConstants.HTML_TEXTAREA : GenConstants.HTML_INPUT;column.setHtmlType(htmlType);}else if (arraysContains(GenConstants.COLUMNTYPE_TIME, dataType)){// 将日期时间类型改为LocalDateTime(重点)column.setJavaType(GenConstants.TYPE_LOCAL_DATE_TIME);column.setHtmlType(GenConstants.HTML_DATETIME);}else if (arraysContains(GenConstants.COLUMNTYPE_NUMBER, dataType)){column.setHtmlType(GenConstants.HTML_INPUT);// 如果是浮点型 统一用BigDecimalString[] str = StringUtils.split(StringUtils.substringBetween(column.getColumnType(), "(", ")"), ",");if (str != null && str.length == 2 && Integer.parseInt(str[1]) > 0){column.setJavaType(GenConstants.TYPE_BIGDECIMAL);}// 如果是整形else if (str != null && str.length == 1 && Integer.parseInt(str[0]) <= 10|| GenConstants.MYSQL_TINYINT.equals(column.getColumnType()) || GenConstants.MYSQL_INT.equals(column.getColumnType()))//(重点){column.setJavaType(GenConstants.TYPE_INTEGER);}// 长整形else{column.setJavaType(GenConstants.TYPE_LONG);}}......省略了后续代码
}

新增代码中的 LocalDateTime、 tinyint 和 int 类型需要定义在 GenConstants 常量类中。

/** LocalDateTime时间类型 */
public static final String TYPE_LOCAL_DATE_TIME = "LocalDateTime";/** MySql tinyint 类型 */
public static final String MYSQL_TINYINT = "tinyint";/** MySql int 类型 */
public static final String MYSQL_INT = "int";

这样,在系统中导入表结构后,数据库中的tinyint类型和int类型对应的默认Java类型就都是Integer类型啦。

http://www.lryc.cn/news/614800.html

相关文章:

  • AJAX与axios框架
  • Netty-Rest搭建笔记
  • 系统集成项目管理工程师【第十一章 规划过程组】规划成本管理、成本估算、制定预算和规划质量管理篇
  • 轻松实现浏览器自动化——AI浏览器自动化框架Stagehand
  • 【华为机试】63. 不同路径 II
  • C++简单项目跟练【通讯录管理系统000】
  • 数据集: TSPLIB旅行商问题-对称TSP数据集
  • 宁商平台税务升级之路:合规为纲,服务为本
  • 五、SpringBoot工程打包与运行
  • 解决 MinIO 上传文件时报 S3 API Requests must be made to API port错误
  • Sklearn 机器学习 数据降维PCA 使用PCA算法
  • Java 之 设计模式
  • Python day38
  • SVM算法实战应用
  • 【感知机】感知机(perceptron)学习算法例题及详解
  • 政治社会时间线
  • 为什么输入 URL 后会显示页面?HTTP 协议的 “幕后操作”
  • JDK、eclipse的安装,配置JDK、Tomcat并使用eclipse创建项目
  • Cursor CLI 来了,准备 Build anything
  • latex基础
  • Vue 路由跳转
  • Redis数据组织方式
  • 第39周——训练自己的数据集
  • Vue 组件化开发
  • 零基础小白如何使用QGIS制作研究区地形区位图教程
  • SQL聚合函数:SUM与COUNT的区别
  • 算法训练之字符串
  • 04--模板初阶(了解)
  • 常见数据结构介绍(顺序表,单链表,双链表,单向循环链表,双向循环链表、内核链表、栈、队列、二叉树)
  • VMware使用NAT模式,使本机与虚拟机在不同的网络,并且虚拟机可以上网