修改器下载
选择压缩包,配置修改内容,开始执行。会输出一个文件
Swagger
Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务
它的主要作用有:
- 使得前后端分离开发更加方便,有利于团队协作
- 在线自动生成接口文档,降低后端开发人员编写接口文档的负担
- 功能测试
Spring已经将Swagger纳入自身的标准,建立了Spring-Swagger项目,现在叫Springfox。通过在项目中引入Springfox ,就可以非常简单快捷的使用Swagger啦。
<!-- 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>
核心配置类
/**
* Swagger2的接口配置
*/
@Configuration
public class SwaggerConfig {
/**
* 系统基础配置
*/
@Autowired
private RuoYiConfig ruoyiConfig;
/**
* 是否开启swagger
*/
@Value("${swagger.enabled}")
private boolean enabled;
/**
* 设置请求的统一前缀
*/
@Value("${swagger.pathMapping}")
private String pathMapping;
/**
* 创建API
*/
@Bean
public 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.kzzyl.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<>();
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("描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...")
// 作者信息
.contact(new Contact(ruoyiConfig.getName(), null, null))
// 版本
.version("版本号:" + ruoyiConfig.getVersion())
.build();
}
}
集成knife4j
如果不习惯使用swagger可以使用前端UI的增强解决方案knife4j,对比swagger相比有以下优势,友好界面,离线文档,接口排序,安全控制,在线调试,文档清晰,注解增强,容易上手。 目前,企业项目中一般都使用knife4j框架。
需要移除依赖,springfox-boot-starter 和 swagger-models
<!-- SpringBoot2 使用依赖 -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
前端修改 ry-ui\views\tool\swagger\index.vue 跳转地址,如下
const url = ref(import.meta.env.VITE_APP_BASE_API + "/doc.html")
注解参数
// 作用到类上描述,标题
@Api(value = "护理项目管理", tags = "护理项目管理")
// 作用到方法上,描述具体的接口
@ApiOperation("查询护理项目列表")
// 作用到方法的参数上,描述参数信息
@ApiParam("查询条件对象")
// 作用到实体类上
@ApiModel("Entity基类")
// 作用到实体类的成员变量上
@ApiModelProperty("搜索值")
Velocity模块引擎
Velocity是一个基于Java的模板引擎,它可以通过特定的语法获取java对象的数据 , 填充到模板中,从而实现界面和java代码的分离。
常见的应用场景:
- Web应用程序 : 作为应用程序的视图, 展示数据。
- 源代码生成 : Velocity可以基于模板生成Java源代码。
- 自动电子邮件 : 网站注册, 认证等的电子邮件模板。
- 网页静态化 : 基于velocity模板生成静态网页。
案例
//TODO: 1.初始化velocity
Properties p = new Properties();
// 加载classpath目录下的vm文件
p.setProperty("resource.loader.file.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
// 定义字符集
p.setProperty(Velocity.INPUT_ENCODING, Constants.UTF8);
// 初始化Velocity引擎,指定配置Properties
Velocity.init(p);
// TODO: 2.创建Velocity上下文对象
VelocityContext context = new VelocityContext();
context.put("message", "加油朋友");
// TODO: 3.获取模板
Template template = Velocity.getTemplate("vms/index.html.vm", Constants.UTF8);
// 准备输出流,将文件写出
FileWriter fileWriter = new FileWriter("E:\\黑马ai\\kzzyl\\kzzyl-generator\\src\\main\\resources\\vms\\index.html");
// TODO: 4.合并模板和数据模型
template.merge(context, fileWriter);
fileWriter.close();
模板如下:使用 ${message} 传递 message 变量
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>随便写点数据吧--${message}</h3>
</body>
</html>
Velocity语法解释
- ${variable}: 表示插入变量值。
- #foreach 和 #end: 循环结构,用于遍历列表。
- #if 和 #end: 条件判断结构。
- #set: 设置变量。
- #elseif: 条件分支。
集成 MybatisPlus
在 common 下引入
<!-- MyBatis-Plus 增强CRUD -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
删除 mybatis-config.xml 文件
# MyBatisPlus配置
mybatis-plus:
# 搜索指定包别名
typeAliasesPackage: com.kzzyl.**.domain
# 配置mapper的扫描,找到所有的mapper.xml映射文件
mapperLocations: classpath*:mapper/**/*Mapper.xml
global-config:
db-config:
id-type: auto
configuration:
# 开启驼峰命名规则
map-underscore-to-camel-case: true
删除 MyBatisConfig 文件,替换成 MyBatisPlusConfig 如下
/**
* Mybatis Plus 配置
*/
@EnableTransactionManagement(proxyTargetClass = true)
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 分页插件
interceptor.addInnerInterceptor(paginationInnerInterceptor());
// 乐观锁插件
interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor());
// 阻断插件
interceptor.addInnerInterceptor(blockAttackInnerInterceptor());
return interceptor;
}
/**
* 分页插件,自动识别数据库类型
*/
public PaginationInnerInterceptor paginationInnerInterceptor() {
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
// 设置数据库类型为mysql
paginationInnerInterceptor.setDbType(DbType.MYSQL);
// 设置最大单页限制数量,默认 500 条,-1 不受限制
paginationInnerInterceptor.setMaxLimit(-1L);
return paginationInnerInterceptor;
}
/**
* 乐观锁插件
*/
public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() {
return new OptimisticLockerInnerInterceptor();
}
/**
* 如果是对全表的删除或更新操作,就会终止该操作
*/
public BlockAttackInnerInterceptor blockAttackInnerInterceptor() {
return new BlockAttackInnerInterceptor();
}
}
注意事项:
- 需要在 BaseEntity 里
params和searchValue需要设置 @TableField(exist = false)
方便直接拷贝
package com.kzzyl.common.core.domain;
import java.io.Serial;
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;
import lombok.Data;
/**
* @author Kun
*/
@Data
@ApiModel("Entity基类")
public class BaseEntity implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@JsonIgnore
@ApiModelProperty("搜索值")
@TableField(exist = false)
private String searchValue;
@ApiModelProperty("创建者")
@TableField(fill = FieldFill.INSERT)
private String createBy;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField(fill = FieldFill.INSERT)
@ApiModelProperty("创建时间")
private Date createTime;
@ApiModelProperty("更新者")
@TableField(fill = FieldFill.UPDATE)
private String updateBy;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField(fill = FieldFill.UPDATE)
@ApiModelProperty("更新时间")
private Date updateTime;
/**
* 备注
*/
@ApiModelProperty("备注")
private String remark;
/**
* 请求参数
*/
@JsonInclude(JsonInclude.Include.NON_EMPTY)
@TableField(exist = false)
@ApiModelProperty("请求参数")
private Map<String, Object> params;
public Map<String, Object> getParams() {
if (params == null) {
params = new HashMap<>();
}
return params;
}
}
MybatisPlus 自动填充
package com.kzzyl.framework.interceptor;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.kzzyl.common.utils.DateUtils;
import com.kzzyl.common.utils.SecurityUtils;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class BaseEntityHandle implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createBy", String.class, SecurityUtils.getUserId().toString());
this.strictInsertFill(metaObject, "createTime", Date.class, DateUtils.getNowDate());
}
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("updateBy", SecurityUtils.getUserId().toString(), metaObject);
this.setFieldValByName("updateTime", DateUtils.getNowDate(), metaObject);
}
}
改造 controller 层
代码模板在下面,这里只说一下需要调整的内置代码
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
* 表格分页数据对象
*/
@Data
@NoArgsConstructor
public class TableDataInfo<T> implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 总记录数
*/
private long total;
/**
* 列表数据
*/
private List<T> rows;
/**
* 消息状态码
*/
private int code;
/**
* 消息内容
*/
private String msg;
/**
* 分页
*
* @param list 列表数据
* @param total 总记录数
*/
public TableDataInfo(List<T> list, long total) {
this.rows = list;
this.total = total;
}
}
还有 BaseController 中的下面方法
/**
* 响应请求分页数据
*/
protected <T> TableDataInfo<T> getDataTable(List<T> list) {
TableDataInfo<T> rspData = new TableDataInfo<>();
rspData.setCode(HttpStatus.SUCCESS);
rspData.setMsg("查询成功");
rspData.setRows(list);
rspData.setTotal(new PageInfo<>(list).getTotal());
return rspData;
}
修改时间类型为 LocalDate
前端在 views/tool/editTable 文件中如下图位置。新增上 LocalDateTime
Java
// src/main/java/com/kzzyl/common/constant/GenConstants.java
/** 时间类型 */
public static final String TYPE_LOCAL_DATE_TIME = "LocalDateTime";
// src/main/java/com/kzzyl/generator/util/VelocityUtils.java
else if (!column.isSuperColumn() && GenConstants.TYPE_LOCAL_DATE_TIME.equals(column.getJavaType())) {
importList.add("java.time.LocalDateTime");
importList.add("com.fasterxml.jackson.annotation.JsonFormat");
}
初始化字段对应关系修改
// 在 src/main/java/com/kzzyl/generator/util/GenUtils.java
else if (str != null && str.length == 1 && Integer.parseInt(str[0]) <= 10
|| GenConstants.MYSQL_TINYINT.equals(column.getColumnType()) ||
GenConstants.MYSQL_INT.equals(column.getColumnType())) {
// 修改为Integer
column.setJavaType(GenConstants.TYPE_INTEGER);
}
// ---------------
else if (arraysContains(GenConstants.COLUMNTYPE_TIME, dataType)) {
// 如果是时间类型 则生成LocalDateTime类型
column.setJavaType(GenConstants.TYPE_LOCAL_DATE_TIME);
column.setHtmlType(GenConstants.HTML_DATETIME);
}
// src/main/java/com/kzzyl/common/constant/GenConstants.java
/** MySql tinyint 类型 */
public static final String MYSQL_TINYINT = "tinyint";
/** MySql int 类型 */
public static final String MYSQL_INT = "int";
代码生成模板
controller.java.vm
package ${packageName}.controller;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import io.swagger.annotations.Api;
import com.kzzyl.common.core.domain.R;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.security.access.prepost.PreAuthorize;
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.kzzyl.common.annotation.Log;
import com.kzzyl.common.core.controller.BaseController;
import com.kzzyl.common.core.domain.AjaxResult;
import com.kzzyl.common.enums.BusinessType;
import ${packageName}.domain.${ClassName};
import ${packageName}.service.I${ClassName}Service;
import com.kzzyl.common.utils.poi.ExcelUtil;
#if($table.crud || $table.sub)
import com.kzzyl.common.core.page.TableDataInfo;
#elseif($table.tree)
#end
/**
* ${functionName}Controller
*
* @author ${author}
* date ${datetime}
*/
@RestController
@AllArgsConstructor
@RequestMapping("/${moduleName}/${businessName}")
@Api(value = "${functionName}管理", tags = "${functionName}管理")
public class ${ClassName}Controller extends BaseController {
private final I${ClassName}Service ${className}Service;
@ApiOperation("查询${functionName}集合")
@PreAuthorize("@ss.hasPermi('${permissionPrefix}:list')")
@GetMapping("/all")
public R<List<${ClassName}>> ${className}All (${ClassName} ${className}) {
return R.ok(${className}Service.list());
}
/**
* 查询${functionName}列表
*/
@ApiOperation("查询${functionName}列表")
@PreAuthorize("@ss.hasPermi('${permissionPrefix}:list')")
@GetMapping("/list")
#if($table.crud || $table.sub)
public TableDataInfo<${ClassName}> list(${ClassName} ${className}) {
startPage();
return getDataTable(${className}Service.select${ClassName}List(${className}));
}
#elseif($table.tree)
public AjaxResult list(${ClassName} ${className})
{
List<${ClassName}> list = ${className}Service.select${ClassName}List(${className});
return success(list);
}
#end
/**
* 导出${functionName}列表
*/
@ApiOperation("导出${functionName}列表")
@PreAuthorize("@ss.hasPermi('${permissionPrefix}:export')")
@Log(title = "${functionName}", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, ${ClassName} ${className}) {
List<${ClassName}> list = ${className}Service.select${ClassName}List(${className});
ExcelUtil<${ClassName}> util = new ExcelUtil<>(${ClassName}.class);
util.exportExcel(response, list, "${functionName}数据");
}
/**
* 获取${functionName}详细信息
*/
@ApiOperation("获取${functionName}列表")
@PreAuthorize("@ss.hasPermi('${permissionPrefix}:query')")
@GetMapping("/{${pkColumn.javaField}}")
public R<${ClassName}> getInfo(@PathVariable @ApiParam("唯一ID") ${pkColumn.javaType} ${pkColumn.javaField}) {
return R.ok(${className}Service.select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField}));
}
/**
* 新增${functionName}
*/
@ApiOperation("新增${functionName}列表")
@PreAuthorize("@ss.hasPermi('${permissionPrefix}:add')")
@Log(title = "${functionName}", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody ${ClassName} ${className}) {
return toAjax(${className}Service.insert${ClassName}(${className}));
}
/**
* 修改${functionName}
*/
@ApiOperation("修改${functionName}列表")
@PreAuthorize("@ss.hasPermi('${permissionPrefix}:edit')")
@Log(title = "${functionName}", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody ${ClassName} ${className}) {
return toAjax(${className}Service.update${ClassName}(${className}));
}
/**
* 删除${functionName}
*/
@ApiOperation("删除${functionName}列表")
@PreAuthorize("@ss.hasPermi('${permissionPrefix}:remove')")
@Log(title = "${functionName}", businessType = BusinessType.DELETE)
@DeleteMapping("/{${pkColumn.javaField}s}")
public AjaxResult remove(@PathVariable @ApiParam("唯一ID集合") ${pkColumn.javaType}[] ${pkColumn.javaField}s) {
return toAjax(${className}Service.delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaField}s));
}
}
domain.java.vm
package ${packageName}.domain;
import lombok.*;
import java.io.Serial;
#foreach ($import in $importList)
import ${import};
#end
import io.swagger.annotations.ApiModel;
import com.kzzyl.common.annotation.Excel;
import io.swagger.annotations.ApiModelProperty;
#if($table.crud || $table.sub)
import com.kzzyl.common.core.domain.BaseEntity;
#elseif($table.tree)
import com.kzzyl.common.core.domain.TreeEntity;
#end
/**
* @author ${author}
* date ${datetime}
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
#if($table.crud || $table.sub)
#set($Entity="BaseEntity")
#elseif($table.tree)
#set($Entity="TreeEntity")
#end
@EqualsAndHashCode(callSuper = true)
@ApiModel(description = "${functionName}")
public class ${ClassName} extends ${Entity} {
@Serial
private static final long serialVersionUID = 1L;
#foreach ($column in $columns)
#if(!$table.isSuperColumn($column.javaField))
#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' || $column.javaType == 'LocalDateTime')
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
#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
#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
}
mapper.java.vm
package ${packageName}.mapper;
import java.util.List;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import ${packageName}.domain.${ClassName};
#if($table.sub)
import ${packageName}.domain.${subClassName};
#end
/**
* ${functionName}Mapper接口
*
* @author ${author}
* date ${datetime}
*/
public interface ${ClassName}Mapper extends BaseMapper<${ClassName}> {
/**
* 查询${functionName}
*
* @param ${pkColumn.javaField} ${functionName}主键
*/
${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField});
/**
* 查询${functionName}列表
*
* @param ${className} ${functionName}
*/
List<${ClassName}> select${ClassName}List(${ClassName} ${className});
#if($table.sub)
/**
* 批量删除${subTable.functionName}
*
* @param ${pkColumn.javaField}s 需要删除的数据主键集合
*/
int delete${subClassName}By${subTableFkClassName}s(${pkColumn.javaType}[] ${pkColumn.javaField}s);
/**
* 批量新增${subTable.functionName}
*
* @param ${subclassName}List ${subTable.functionName}列表
*/
int batch${subClassName}(List<${subClassName}> ${subclassName}List);
/**
* 通过${functionName}主键删除${subTable.functionName}信息
*
* @param ${pkColumn.javaField} ${functionName}ID
*/
int delete${subClassName}By${subTableFkClassName}(${pkColumn.javaType} ${pkColumn.javaField});
#end
}
service.java.vm
package ${packageName}.service;
import java.util.List;
import com.baomidou.mybatisplus.extension.service.IService;
import ${packageName}.domain.${ClassName};
/**
* ${functionName}Service接口
*
* @author ${author}
* date ${datetime}
*/
public interface I${ClassName}Service extends IService<${ClassName}> {
/**
* 查询${functionName}
*
* @param ${pkColumn.javaField} ${functionName}主键
*/
${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField});
/**
* 查询${functionName}列表
*
* @param ${className} ${functionName}
*/
List<${ClassName}> select${ClassName}List(${ClassName} ${className});
/**
* 新增${functionName}
*
* @param ${className} ${functionName}
*/
boolean insert${ClassName}(${ClassName} ${className});
/**
* 修改${functionName}
*
* @param ${className} ${functionName}
*/
boolean update${ClassName}(${ClassName} ${className});
/**
* 批量删除${functionName}
*
* @param ${pkColumn.javaField}s 需要删除的${functionName}主键集合
*/
boolean delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s);
/**
* 删除${functionName}
*/
void delete${ClassName}(List<Long> ids);
/**
* 删除护理员老人关联
*/
void remove${ClassName}(${ClassName} ${className});
}
serviceImpl.java.vm
package ${packageName}.service.impl;
import java.util.Arrays;
import java.util.List;
import org.springframework.stereotype.Service;
#if($table.sub)
import java.util.ArrayList;
import com.kzzyl.common.utils.StringUtils;
import ${packageName}.domain.${subClassName};
#end
import ${packageName}.mapper.${ClassName}Mapper;
import ${packageName}.domain.${ClassName};
import ${packageName}.service.I${ClassName}Service;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.springframework.transaction.annotation.Transactional;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
/**
* ${functionName}Service业务层处理
*
* @author ${author}
* date ${datetime}
*/
@Service
public class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}>
implements I${ClassName}Service {
/**
* 查询${functionName}
*
* @param ${pkColumn.javaField} ${functionName}主键
*/
@Override
public ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}) {
return baseMapper.select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField});
}
/**
* 查询${functionName}列表
*
* @param ${className} ${functionName}
*/
@Override
public List<${ClassName}> select${ClassName}List(${ClassName} ${className}) {
return baseMapper.select${ClassName}List(${className});
}
/**
* 新增${functionName}
*
* @param ${className} ${functionName}
*/
#if($table.sub)
@Transactional
#end
@Override
public boolean insert${ClassName}(${ClassName} ${className}) {
#if($table.sub)
int rows = ${className}Mapper.insert${ClassName}(${className});
insert${subClassName}(${className});
return rows;
#else
return save(${className});
#end
}
/**
* 修改${functionName}
*
* @param ${className} ${functionName}
*/
#if($table.sub)
@Transactional
#end
@Override
public boolean update${ClassName}(${ClassName} ${className}) {
#if($table.sub)
${className}Mapper.delete${subClassName}By${subTableFkClassName}(${className}.get${pkColumn.capJavaField}());
insert${subClassName}(${className});
#end
return updateById(${className});
}
/**
* 批量删除${functionName}
*
* @param ${pkColumn.javaField}s 需要删除的${functionName}主键
*/
@Transactional
@Override
public boolean 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 removeBatchByIds(Arrays.asList(${pkColumn.javaField}s));
}
/**
* 删除${functionName}
*/
@Override
public void delete${ClassName}(List<Long> ids) {
boolean remove = remove(Wrappers.<${ClassName}>lambdaQuery());
if(!remove) {
throw new RuntimeException("${ClassName} 删除失败");
}
}
/**
* 删除护理员老人关联
*/
@Override
public void remove${ClassName}(${ClassName} ${className}) {
boolean remove = remove(Wrappers.<${ClassName}>lambdaQuery());
if (!remove) {
throw new RuntimeException("${ClassName} 删除失败");
}
}
#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
}
mapper.xml
<?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="${packageName}.mapper.${ClassName}Mapper">
<resultMap type="${ClassName}" id="${ClassName}Result">
#foreach ($column in $columns)
<result property="${column.javaField}" column="${column.columnName}" />
#end
</resultMap>
#if($table.sub)
<resultMap id="${ClassName}${subClassName}Result" type="${ClassName}" extends="${ClassName}Result">
<collection property="${subclassName}List" ofType="${subClassName}" column="${pkColumn.columnName}" select="select${subClassName}List" />
</resultMap>
<resultMap type="${subClassName}" id="${subClassName}Result">
#foreach ($column in $subTable.columns)
<result property="${column.javaField}" column="${column.columnName}" />
#end
</resultMap>
#end
<sql id="select${ClassName}Vo">
select#foreach($column in $columns) $column.columnName#if($foreach.count != $columns.size()),#end#end from ${tableName}
</sql>
<select id="select${ClassName}List" parameterType="${ClassName}" resultMap="${ClassName}Result">
<include refid="select${ClassName}Vo"/>
<where>
#foreach($column in $columns)
#set($queryType=$column.queryType)
#set($javaField=$column.javaField)
#set($javaType=$column.javaType)
#set($columnName=$column.columnName)
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
#if($column.query)
#if($column.queryType == "EQ")
<if test="$javaField != null #if($javaType == 'String' ) and $javaField.trim() != ''#end"> and $columnName = #{$javaField}</if>
#elseif($queryType == "NE")
<if test="$javaField != null #if($javaType == 'String' ) and $javaField.trim() != ''#end"> and $columnName != #{$javaField}</if>
#elseif($queryType == "GT")
<if test="$javaField != null #if($javaType == 'String' ) and $javaField.trim() != ''#end"> and $columnName > #{$javaField}</if>
#elseif($queryType == "GTE")
<if test="$javaField != null #if($javaType == 'String' ) and $javaField.trim() != ''#end"> and $columnName >= #{$javaField}</if>
#elseif($queryType == "LT")
<if test="$javaField != null #if($javaType == 'String' ) and $javaField.trim() != ''#end"> and $columnName < #{$javaField}</if>
#elseif($queryType == "LTE")
<if test="$javaField != null #if($javaType == 'String' ) and $javaField.trim() != ''#end"> and $columnName <= #{$javaField}</if>
#elseif($queryType == "LIKE")
<if test="$javaField != null #if($javaType == 'String' ) and $javaField.trim() != ''#end"> and $columnName like concat('%', #{$javaField}, '%')</if>
#elseif($queryType == "BETWEEN")
<if test="params.begin$AttrName != null and params.begin$AttrName != '' and params.end$AttrName != null and params.end$AttrName != ''"> and $columnName between #{params.begin$AttrName} and #{params.end$AttrName}</if>
#end
#end
#end
</where>
</select>
<select id="select${ClassName}By${pkColumn.capJavaField}" parameterType="${pkColumn.javaType}" resultMap="#if($table.sub)${ClassName}${subClassName}Result#else${ClassName}Result#end">
#if($table.crud || $table.tree)
<include refid="select${ClassName}Vo"/>
where ${pkColumn.columnName} = #{${pkColumn.javaField}}
#elseif($table.sub)
select#foreach($column in $columns) $column.columnName#if($foreach.count != $columns.size()),#end#end
from ${tableName}
where ${pkColumn.columnName} = #{${pkColumn.javaField}}
#end
</select>
#if($table.sub)
<select id="select${subClassName}List" resultMap="${subClassName}Result">
select#foreach ($column in $subTable.columns) $column.columnName#if($foreach.count != $subTable.columns.size()),#end#end
from ${subTableName}
where ${subTableFkName} = #{${subTableFkName}}
</select>
#end
#if($table.sub)
<delete id="delete${subClassName}By${subTableFkClassName}s" parameterType="String">
delete from ${subTableName} where ${subTableFkName} in
<foreach item="${subTableFkclassName}" collection="array" open="(" separator="," close=")">
#{${subTableFkclassName}}
</foreach>
</delete>
<delete id="delete${subClassName}By${subTableFkClassName}" parameterType="${pkColumn.javaType}">
delete from ${subTableName} where ${subTableFkName} = #{${subTableFkclassName}}
</delete>
<insert id="batch${subClassName}">
insert into ${subTableName}(#foreach($column in $subTable.columns) $column.columnName#if($foreach.count != $subTable.columns.size()),#end#end) values
<foreach item="item" index="index" collection="list" separator=",">
(#foreach($column in $subTable.columns) #{item.$column.javaField}#if($foreach.count != $subTable.columns.size()),#end#end)
</foreach>
</insert>
#end
</mapper>
前端模板
在 src/main/java/com/kzzyl/generator/util/VelocityUtils.java 文件下修改如下图
继续修改获取文件名方法
else if (template.contains("hooks.js.vm")) {
fileName = StringUtils.format("{}/hooks/{}/use{}.js", vuePath, moduleName, businessName);
}
hooks
import { ${businessName}All } from '@/api/${moduleName}/${ClassName}/index.js'
export function use${ClassName}(params) {
const loading = ref(false)
const ${className}List = ref([])
const get${ClassName}List = async () => {
loading.value = true
const {data} = await ${businessName}All(params)
${className}List.value = data.map(item => ({
label: item.name,
value: item.id
}))
loading.value = false
}
return {${className}List, get${ClassName}List}
}
API
import request from '@/utils/request'
const baseUrl = '/${moduleName}/${businessName}'
// 查询${functionName}
export function ${businessName}All(params) {
return request.get(`#[[$]]#{baseUrl}/all`, {params})
}
// 查询${functionName}列表
export function list${BusinessName}(params) {
return request.get(`#[[$]]#{baseUrl}/list`, {params})
}
// 查询${functionName}详细
export function get${BusinessName}(${pkColumn.javaField}) {
return request.get(`#[[$]]#{baseUrl}/#[[$]]#${${pkColumn.javaField}}`)
}
// 新增${functionName}
export function add${BusinessName}(data) {
return request.post(baseUrl, data)
}
// 修改${functionName}
export function update${BusinessName}(data) {
return request.put(baseUrl, data)
}
// 删除${functionName}
export function del${BusinessName}(${pkColumn.javaField}) {
return request.delete(`#[[$]]#{baseUrl}/#[[$]]#{${pkColumn.javaField}}`)
}
OSS集成
问题分析
在若依框架目前的实现中,是把图片存储到了服务器本地的目录,通过服务进行访问,这样做存储的是比较省事,但是缺点也有很多:
- 硬件与网络要求:服务器通常需要高性能的硬件和稳定的网络环境,以保证文件传输的效率和稳定性。这可能会增加硬件和网络资源的成本和维护难度。
- 管理难度:服务器目录需要管理员进行配置和管理,包括权限设置、备份策略等。如果管理不善或配置不当,可能会引发一些安全问题和性能问题。
- 性能瓶颈:如果服务器处理能力不足或网络带宽不够,可能会导致性能瓶颈,影响文件上传、下载和访问的速度。
- 单点故障风险:服务器故障可能导致所有存储在其上的文件无法访问,尽管可以通过备份和冗余措施来降低这种风险,但单点故障的风险仍然存在。 基于以上原因,企业中很多的文件都会存储到OSS中,OSS可以解决以上所有的问题,并且成本也不高,下面咱们就把阿里的OSS集成到若依项目中。
集成OSS,修改若依改成OSS。
配置文件
实际上传调用
@Component
@AllArgsConstructor
public class OSSClient {
private final OSS oss;
private final OSSProperties ossProperties;
public String upload(String bucketName, MultipartFile file) {
try {
String dir = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
String newFileName = UUID.randomUUID() + Objects.requireNonNull(file.getOriginalFilename())
.substring(file.getOriginalFilename().lastIndexOf("."));
String objectName = dir + "/" + newFileName;
oss.putObject(bucketName, objectName, file.getInputStream());
return "https://" + bucketName + "." + ossProperties.getEndpoint() + "/" + objectName;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
配置类
@Configuration
public class OSSConfig {
@Bean
public OSS endpoint(OSSProperties ossProperties) {
DefaultCredentialProvider defaultCredentialProvider =
new DefaultCredentialProvider(ossProperties.getAccessKeyId(), ossProperties.getSecretAccessKey());
ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
return OSSClientBuilder.create()
.endpoint("https://" + ossProperties.getEndpoint())
.credentialsProvider(defaultCredentialProvider)
.clientConfiguration(clientBuilderConfiguration)
.region(extractRegion(ossProperties.getEndpoint()))
.build();
}
/**
* 获取 region
*/
private String extractRegion(String endpoint) {
if (endpoint == null) return null;
int start = endpoint.indexOf("oss-");
int end = endpoint.indexOf(".aliyuncs.com");
if (start == -1 || end == -1 || start + 4 >= end)
throw new IllegalArgumentException("Invalid endpoint: " + endpoint);
return endpoint.substring(start + 4, end);
}
}
获取配置信息
@Data
@Component
@ConfigurationProperties(prefix = "aliyun.oss")
public class OSSProperties {
/**
* 存储桶
*/
private String endpoint;
/**
* 访问密钥
*/
private String accessKeyId;
/**
* 密钥
*/
private String secretAccessKey;
}
定时器
cron表达式
| 组成部分 | 含义 | 取值范围 |
|---|---|---|
| 第一部分 | Seconds (秒) | 0-59 |
| 第二部分 | Minutes(分) | 0-59 |
| 第三部分 | Hours(时) | 0-23 |
| 第四部分 | Day-of-Month(日) | 1月31日 |
| 第五部分 | Month(月) | 0-11或JAN-DEC |
| 第六部分 | Day-of-Week(星期) | 1-7(1表示星期日)或SUN-SAT |
| 第七部分 | Year(年) 可选 | 1970-2099 |
cron表达式还可以包含一些特殊符号来设置更加灵活的定时规则, 如下表所示:
| 符号 | 含义 |
|---|---|
| ? | 表示不确定的值。当两个子表达式其中一个被指定了值以后,为了避免冲突,需要将另外一个的值设为 "?" 。例如:想在每月20日触发调度,不管20号是星期几,只能用如下写法:0 0 0 20 * ?,其中最后以为只能用“?” |
| * | 代表所有可能的值 |
| , | 设置多个值,例如"26,29,33"表示在26分,29分和33分各自运行一次任务 |
| - | 设置取值范围,例如"5-20",表示从5分到20分钟每分钟运行一次任务 |
| / | 设置频率或间隔,如"1/15"表示从1分开始,每隔15分钟运行一次任务 |
| L | 用于每月,或每周,表示每月的最后一天,或每个月的最后星期几,例如"6L"表示"每月的最后一个星期五" |
| W | 表示离给定日期最近的工作日,例如"15W"放在每月(day-of-month)上表示"离本月15日最近的工作日" |
| # | 表示该月第几个周X。例如”6#3”表示该月第3个周五 |
| 为了让大家更熟悉cron表达式的用法, 接下来我们给大家列举了一些例子, 如下表所示:
| cron表达式 | 含义 |
|---|---|
| */5 * * * * ? | 每隔5秒运行一次任务 |
| 0 0 23 * * ? | 每天23点运行一次任务 |
| 0 0 1 1 * ? | 每月1号凌晨1点运行一次任务 |
| 0 0 23 L * ? | 每月最后一天23点运行一次任务 |
| 0 26,29,33 * * * ? | 在26分、29分、33分运行一次任务 |
| 0 0/30 9-17 * * ? | 朝九晚五工作时间内每半小时运行一次任务 |
| 0 15 10 ? * 6#3 | 每月的第三个星期五上午10:15运行一次任务 |
如何使用
- 创建执行方法
@Component
public class HelloTask {
public void hello(){
System.out.println("hello world");
}
}
- 在界面使用该方法
- 把该目录加入到白名单中
首先,com.kzzyl.common.constant.JOB_WHITELIST_STR 加入定时类的方法。
最后开启了就会自动执行