mirror of
https://gitee.com/hhyykk/ipms-sjy.git
synced 2025-09-09 22:51:59 +08:00
将 tool 合并到 infra 模块
This commit is contained in:
@@ -13,8 +13,9 @@
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
<description>
|
||||
infra 模块,我们放基础设施的运维与管理,支撑上层的通用与核心业务。
|
||||
例如说:定时任务的管理、服务器的信息等等
|
||||
infra 模块,主要提供两块能力:
|
||||
1. 我们放基础设施的运维与管理,支撑上层的通用与核心业务。 例如说:定时任务的管理、服务器的信息等等
|
||||
2. 研发工具,提升研发效率与质量。 例如说:代码生成器、接口文档等等
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
@@ -88,6 +89,11 @@
|
||||
<artifactId>yudao-spring-boot-starter-excel</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.velocity</groupId>
|
||||
<artifactId>velocity-engine-core</artifactId> <!-- 实现代码生成 -->
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.smallbun.screw</groupId>
|
||||
<artifactId>screw-core</artifactId> <!-- 实现数据库文档 -->
|
||||
|
@@ -0,0 +1,168 @@
|
||||
package cn.iocoder.yudao.module.infra.controller.admin.codegen;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.ZipUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenDetailRespVO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenPreviewRespVO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTablePageReqVO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTableRespVO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.SchemaTableRespVO;
|
||||
import cn.iocoder.yudao.module.infra.convert.codegen.CodegenConvert;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.SchemaTableDO;
|
||||
import cn.iocoder.yudao.module.infra.service.codegen.CodegenService;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiImplicitParams;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.validation.Valid;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Api(tags = "管理后台 - 代码生成器")
|
||||
@RestController
|
||||
@RequestMapping("/infra/codegen")
|
||||
@Validated
|
||||
public class CodegenController {
|
||||
|
||||
@Resource
|
||||
private CodegenService codegenService;
|
||||
|
||||
@GetMapping("/db/table/list")
|
||||
@ApiOperation(value = "获得数据库自带的表定义列表", notes = "会过滤掉已经导入 Codegen 的表")
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(name = "tableName", value = "表名,模糊匹配", required = true, example = "yudao", dataTypeClass = String.class),
|
||||
@ApiImplicitParam(name = "tableComment", value = "描述,模糊匹配", required = true, example = "芋道", dataTypeClass = String.class)
|
||||
})
|
||||
@PreAuthorize("@ss.hasPermission('infra:codegen:query')")
|
||||
public CommonResult<List<SchemaTableRespVO>> getSchemaTableList(
|
||||
@RequestParam(value = "tableName", required = false) String tableName,
|
||||
@RequestParam(value = "tableComment", required = false) String tableComment) {
|
||||
// 获得数据库自带的表定义列表
|
||||
List<SchemaTableDO> schemaTables = codegenService.getSchemaTableList(tableName, tableComment);
|
||||
// 移除在 Codegen 中,已经存在的
|
||||
Set<String> existsTables = CollectionUtils.convertSet(codegenService.getCodeGenTableList(), CodegenTableDO::getTableName);
|
||||
schemaTables.removeIf(table -> existsTables.contains(table.getTableName()));
|
||||
return success(CodegenConvert.INSTANCE.convertList04(schemaTables));
|
||||
}
|
||||
|
||||
@GetMapping("/table/page")
|
||||
@ApiOperation("获得表定义分页")
|
||||
@PreAuthorize("@ss.hasPermission('infra:codegen:query')")
|
||||
public CommonResult<PageResult<CodegenTableRespVO>> getCodeGenTablePage(@Valid CodegenTablePageReqVO pageReqVO) {
|
||||
PageResult<CodegenTableDO> pageResult = codegenService.getCodegenTablePage(pageReqVO);
|
||||
return success(CodegenConvert.INSTANCE.convertPage(pageResult));
|
||||
}
|
||||
|
||||
@GetMapping("/detail")
|
||||
@ApiOperation("获得表和字段的明细")
|
||||
@ApiImplicitParam(name = "tableId", value = "表编号", required = true, example = "1024", dataTypeClass = Long.class)
|
||||
@PreAuthorize("@ss.hasPermission('infra:codegen:query')")
|
||||
public CommonResult<CodegenDetailRespVO> getCodegenDetail(@RequestParam("tableId") Long tableId) {
|
||||
CodegenTableDO table = codegenService.getCodegenTablePage(tableId);
|
||||
List<CodegenColumnDO> columns = codegenService.getCodegenColumnListByTableId(tableId);
|
||||
// 拼装返回
|
||||
return success(CodegenConvert.INSTANCE.convert(table, columns));
|
||||
}
|
||||
|
||||
@ApiOperation("基于数据库的表结构,创建代码生成器的表和字段定义")
|
||||
@ApiImplicitParam(name = "tableNames", value = "表名数组", required = true, example = "sys_user", dataTypeClass = List.class)
|
||||
@PostMapping("/create-list-from-db")
|
||||
@PreAuthorize("@ss.hasPermission('infra:codegen:create')")
|
||||
public CommonResult<List<Long>> createCodegenListFromDB(@RequestParam("tableNames") List<String> tableNames) {
|
||||
return success(codegenService.createCodegenListFromDB(getLoginUserId(), tableNames));
|
||||
}
|
||||
|
||||
@ApiOperation("基于 SQL 建表语句,创建代码生成器的表和字段定义")
|
||||
@ApiImplicitParam(name = "sql", value = "SQL 建表语句", required = true, example = "sql", dataTypeClass = String.class)
|
||||
@PostMapping("/create-list-from-sql")
|
||||
@PreAuthorize("@ss.hasPermission('infra:codegen:create')")
|
||||
public CommonResult<Long> createCodegenListFromSQL(@RequestParam("sql") String sql) {
|
||||
return success(codegenService.createCodegenListFromSQL(getLoginUserId(), sql));
|
||||
}
|
||||
|
||||
@ApiOperation("更新数据库的表和字段定义")
|
||||
@PutMapping("/update")
|
||||
@PreAuthorize("@ss.hasPermission('infra:codegen:update')")
|
||||
public CommonResult<Boolean> updateCodegen(@Valid @RequestBody CodegenUpdateReqVO updateReqVO) {
|
||||
codegenService.updateCodegen(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@ApiOperation("基于数据库的表结构,同步数据库的表和字段定义")
|
||||
@PutMapping("/sync-from-db")
|
||||
@ApiImplicitParam(name = "tableId", value = "表编号", required = true, example = "1024", dataTypeClass = Long.class)
|
||||
@PreAuthorize("@ss.hasPermission('infra:codegen:update')")
|
||||
public CommonResult<Boolean> syncCodegenFromDB(@RequestParam("tableId") Long tableId) {
|
||||
codegenService.syncCodegenFromDB(tableId);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@ApiOperation("基于 SQL 建表语句,同步数据库的表和字段定义")
|
||||
@PutMapping("/sync-from-sql")
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(name = "tableId", value = "表编号", required = true, example = "1024", dataTypeClass = Long.class),
|
||||
@ApiImplicitParam(name = "sql", value = "SQL 建表语句", required = true, example = "sql", dataTypeClass = String.class)
|
||||
})
|
||||
@PreAuthorize("@ss.hasPermission('infra:codegen:update')")
|
||||
public CommonResult<Boolean> syncCodegenFromSQL(@RequestParam("tableId") Long tableId,
|
||||
@RequestParam("sql") String sql) {
|
||||
codegenService.syncCodegenFromSQL(tableId, sql);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@ApiOperation("删除数据库的表和字段定义")
|
||||
@DeleteMapping("/delete")
|
||||
@ApiImplicitParam(name = "tableId", value = "表编号", required = true, example = "1024", dataTypeClass = Long.class)
|
||||
@PreAuthorize("@ss.hasPermission('infra:codegen:delete')")
|
||||
public CommonResult<Boolean> deleteCodegen(@RequestParam("tableId") Long tableId) {
|
||||
codegenService.deleteCodegen(tableId);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@ApiOperation("预览生成代码")
|
||||
@GetMapping("/preview")
|
||||
@ApiImplicitParam(name = "tableId", value = "表编号", required = true, example = "1024", dataTypeClass = Long.class)
|
||||
@PreAuthorize("@ss.hasPermission('infra:codegen:preview')")
|
||||
public CommonResult<List<CodegenPreviewRespVO>> previewCodegen(@RequestParam("tableId") Long tableId) {
|
||||
Map<String, String> codes = codegenService.generationCodes(tableId);
|
||||
return success(CodegenConvert.INSTANCE.convert(codes));
|
||||
}
|
||||
|
||||
@ApiOperation("下载生成代码")
|
||||
@GetMapping("/download")
|
||||
@ApiImplicitParam(name = "tableId", value = "表编号", required = true, example = "1024", dataTypeClass = Long.class)
|
||||
@PreAuthorize("@ss.hasPermission('infra:codegen:download')")
|
||||
public void downloadCodegen(@RequestParam("tableId") Long tableId,
|
||||
HttpServletResponse response) throws IOException {
|
||||
// 生成代码
|
||||
Map<String, String> codes = codegenService.generationCodes(tableId);
|
||||
// 构建 zip 包
|
||||
String[] paths = codes.keySet().toArray(new String[0]);
|
||||
ByteArrayInputStream[] ins = codes.values().stream().map(IoUtil::toUtf8Stream).toArray(ByteArrayInputStream[]::new);
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
ZipUtil.zip(outputStream, paths, ins);
|
||||
// 输出
|
||||
ServletUtils.writeAttachment(response, "codegen.zip", outputStream.toByteArray());
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
package cn.iocoder.yudao.module.infra.controller.admin.codegen.vo;
|
||||
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.column.CodegenColumnRespVO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTableRespVO;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ApiModel("管理后台 - 代码生成表和字段的明细 Response VO")
|
||||
@Data
|
||||
public class CodegenDetailRespVO {
|
||||
|
||||
@ApiModelProperty("表定义")
|
||||
private CodegenTableRespVO table;
|
||||
|
||||
@ApiModelProperty("字段定义")
|
||||
private List<CodegenColumnRespVO> columns;
|
||||
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
package cn.iocoder.yudao.module.infra.controller.admin.codegen.vo;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
@ApiModel(value = "管理后台 - 代码生成预览 Response VO", description ="注意,每个文件都是一个该对象")
|
||||
@Data
|
||||
public class CodegenPreviewRespVO {
|
||||
|
||||
@ApiModelProperty(value = "文件路径", required = true, example = "java/cn/iocoder/yudao/adminserver/modules/system/controller/test/SysTestDemoController.java")
|
||||
private String filePath;
|
||||
|
||||
@ApiModelProperty(value = "代码", required = true, example = "Hello World")
|
||||
private String code;
|
||||
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
package cn.iocoder.yudao.module.infra.controller.admin.codegen.vo;
|
||||
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.column.CodegenColumnBaseVO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTableBaseVO;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
|
||||
@ApiModel("管理后台 - 代码生成表和字段的修改 Request VO")
|
||||
@Data
|
||||
public class CodegenUpdateReqVO {
|
||||
|
||||
@Valid // 校验内嵌的字段
|
||||
@NotNull(message = "表定义不能为空")
|
||||
private Table table;
|
||||
|
||||
@Valid // 校验内嵌的字段
|
||||
@NotNull(message = "字段定义不能为空")
|
||||
private List<Column> columns;
|
||||
|
||||
@ApiModel("更新表定义")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public static class Table extends CodegenTableBaseVO {
|
||||
|
||||
@ApiModelProperty(value = "编号", required = true, example = "1")
|
||||
private Long id;
|
||||
|
||||
}
|
||||
|
||||
@ApiModel("更新表定义")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public static class Column extends CodegenColumnBaseVO {
|
||||
|
||||
@ApiModelProperty(value = "编号", required = true, example = "1")
|
||||
private Long id;
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,85 @@
|
||||
package cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.column;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 代码生成字段定义 Base VO,提供给添加、修改、详细的子 VO 使用
|
||||
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
|
||||
*/
|
||||
@Data
|
||||
public class CodegenColumnBaseVO {
|
||||
|
||||
@ApiModelProperty(value = "表编号", required = true, example = "1")
|
||||
@NotNull(message = "表编号不能为空")
|
||||
private Long tableId;
|
||||
|
||||
@ApiModelProperty(value = "字段名", required = true, example = "user_age")
|
||||
@NotNull(message = "字段名不能为空")
|
||||
private String columnName;
|
||||
|
||||
@ApiModelProperty(value = "字段类型", required = true, example = "int(11)")
|
||||
@NotNull(message = "字段类型不能为空")
|
||||
private String columnType;
|
||||
|
||||
@ApiModelProperty(value = "字段描述", required = true, example = "年龄")
|
||||
@NotNull(message = "字段描述不能为空")
|
||||
private String columnComment;
|
||||
|
||||
@ApiModelProperty(value = "是否允许为空", required = true, example = "true")
|
||||
@NotNull(message = "是否允许为空不能为空")
|
||||
private Boolean nullable;
|
||||
|
||||
@ApiModelProperty(value = "是否主键", required = true, example = "false")
|
||||
@NotNull(message = "是否主键不能为空")
|
||||
private Boolean primaryKey;
|
||||
|
||||
@ApiModelProperty(value = "是否自增", required = true, example = "true")
|
||||
@NotNull(message = "是否自增不能为空")
|
||||
private String autoIncrement;
|
||||
|
||||
@ApiModelProperty(value = "排序", required = true, example = "10")
|
||||
@NotNull(message = "排序不能为空")
|
||||
private Integer ordinalPosition;
|
||||
|
||||
@ApiModelProperty(value = "Java 属性类型", required = true, example = "userAge")
|
||||
@NotNull(message = "Java 属性类型不能为空")
|
||||
private String javaType;
|
||||
|
||||
@ApiModelProperty(value = "Java 属性名", required = true, example = "Integer")
|
||||
@NotNull(message = "Java 属性名不能为空")
|
||||
private String javaField;
|
||||
|
||||
@ApiModelProperty(value = "字典类型", example = "sys_gender")
|
||||
private String dictType;
|
||||
|
||||
@ApiModelProperty(value = "数据示例", example = "1024")
|
||||
private String example;
|
||||
|
||||
@ApiModelProperty(value = "是否为 Create 创建操作的字段", required = true, example = "true")
|
||||
@NotNull(message = "是否为 Create 创建操作的字段不能为空")
|
||||
private Boolean createOperation;
|
||||
|
||||
@ApiModelProperty(value = "是否为 Update 更新操作的字段", required = true, example = "false")
|
||||
@NotNull(message = "是否为 Update 更新操作的字段不能为空")
|
||||
private Boolean updateOperation;
|
||||
|
||||
@ApiModelProperty(value = "是否为 List 查询操作的字段", required = true, example = "true")
|
||||
@NotNull(message = "是否为 List 查询操作的字段不能为空")
|
||||
private Boolean listOperation;
|
||||
|
||||
@ApiModelProperty(value = "List 查询操作的条件类型", required = true, example = "LIKE", notes = "参见 CodegenColumnListConditionEnum 枚举")
|
||||
@NotNull(message = "List 查询操作的条件类型不能为空")
|
||||
private String listOperationCondition;
|
||||
|
||||
@ApiModelProperty(value = "是否为 List 查询操作的返回字段", required = true, example = "true")
|
||||
@NotNull(message = "是否为 List 查询操作的返回字段不能为空")
|
||||
private Boolean listOperationResult;
|
||||
|
||||
@ApiModelProperty(value = "显示类型", required = true, example = "input")
|
||||
@NotNull(message = "显示类型不能为空")
|
||||
private String htmlType;
|
||||
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
package cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.column;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@ApiModel("管理后台 - 代码生成字段定义 Response VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class CodegenColumnRespVO extends CodegenColumnBaseVO {
|
||||
|
||||
@ApiModelProperty(value = "编号", required = true, example = "1")
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty(value = "创建时间", required = true)
|
||||
private Date createTime;
|
||||
|
||||
}
|
@@ -0,0 +1,61 @@
|
||||
package cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 代码生成 Base VO,提供给添加、修改、详细的子 VO 使用
|
||||
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
|
||||
*/
|
||||
@Data
|
||||
public class CodegenTableBaseVO {
|
||||
|
||||
@ApiModelProperty(value = "导入类型", required = true, example = "1", notes = "参见 CodegenImportTypeEnum 枚举")
|
||||
@NotNull(message = "导入类型不能为空")
|
||||
private Integer importType;
|
||||
|
||||
@ApiModelProperty(value = "生成场景", required = true, example = "1", notes = "参见 CodegenSceneEnum 枚举")
|
||||
@NotNull(message = "导入类型不能为空")
|
||||
private Integer scene;
|
||||
|
||||
@ApiModelProperty(value = "表名称", required = true, example = "yudao")
|
||||
@NotNull(message = "表名称不能为空")
|
||||
private String tableName;
|
||||
|
||||
@ApiModelProperty(value = "表描述", required = true, example = "芋道")
|
||||
@NotNull(message = "表描述不能为空")
|
||||
private String tableComment;
|
||||
|
||||
@ApiModelProperty(value = "备注", example = "我是备注")
|
||||
private String remark;
|
||||
|
||||
@ApiModelProperty(value = "模块名", required = true, example = "system")
|
||||
@NotNull(message = "模块名不能为空")
|
||||
private String moduleName;
|
||||
|
||||
@ApiModelProperty(value = "业务名", required = true, example = "codegen")
|
||||
@NotNull(message = "业务名不能为空")
|
||||
private String businessName;
|
||||
|
||||
@ApiModelProperty(value = "类名称", required = true, example = "CodegenTable")
|
||||
@NotNull(message = "类名称不能为空")
|
||||
private String className;
|
||||
|
||||
@ApiModelProperty(value = "类描述", required = true, example = "代码生成器的表定义")
|
||||
@NotNull(message = "类描述不能为空")
|
||||
private String classComment;
|
||||
|
||||
@ApiModelProperty(value = "作者", required = true, example = "芋道源码")
|
||||
@NotNull(message = "作者不能为空")
|
||||
private String author;
|
||||
|
||||
@ApiModelProperty(value = "模板类型", required = true, example = "1", notes = "参见 CodegenTemplateTypeEnum 枚举")
|
||||
@NotNull(message = "模板类型不能为空")
|
||||
private Integer templateType;
|
||||
|
||||
@ApiModelProperty(value = "父菜单编号", example = "1024")
|
||||
private Long parentMenuId;
|
||||
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
package cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@ApiModel("管理后台 - 表定义分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class CodegenTablePageReqVO extends PageParam {
|
||||
|
||||
@ApiModelProperty(value = "表名称", example = "yudao", notes = "模糊匹配")
|
||||
private String tableName;
|
||||
|
||||
@ApiModelProperty(value = "表描述", example = "芋道", notes = "模糊匹配")
|
||||
private String tableComment;
|
||||
|
||||
@ApiModelProperty(value = "开始创建时间", example = "2020-10-24 00:00:00")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private Date beginCreateTime;
|
||||
|
||||
@ApiModelProperty(value = "结束创建时间", example = "2020-10-24 23:59:59")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private Date endCreateTime;
|
||||
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
package cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@ApiModel("管理后台 - 代码生成表定义 Response VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class CodegenTableRespVO extends CodegenTableBaseVO {
|
||||
|
||||
@ApiModelProperty(value = "编号", required = true, example = "1")
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty(value = "创建时间", required = true)
|
||||
private Date createTime;
|
||||
|
||||
@ApiModelProperty(value = "更新时间", required = true)
|
||||
private Date updateTime;
|
||||
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
package cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@ApiModel("管理后台 - 数据字典的表定义 Response VO")
|
||||
@Data
|
||||
public class SchemaTableRespVO {
|
||||
|
||||
@ApiModelProperty(value = "数据库", required = true, example = "yudao")
|
||||
private String tableSchema;
|
||||
|
||||
@ApiModelProperty(value = "表名称", required = true, example = "yuanma")
|
||||
private String tableName;
|
||||
|
||||
@ApiModelProperty(value = "表描述", required = true, example = "芋道源码")
|
||||
private String tableComment;
|
||||
|
||||
@ApiModelProperty(value = "创建时间", required = true)
|
||||
private Date createTime;
|
||||
|
||||
}
|
@@ -0,0 +1,157 @@
|
||||
package cn.iocoder.yudao.module.infra.controller.admin.doc;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
|
||||
import cn.smallbun.screw.core.Configuration;
|
||||
import cn.smallbun.screw.core.engine.EngineConfig;
|
||||
import cn.smallbun.screw.core.engine.EngineFileType;
|
||||
import cn.smallbun.screw.core.engine.EngineTemplateType;
|
||||
import cn.smallbun.screw.core.execute.DocumentationExecute;
|
||||
import cn.smallbun.screw.core.process.ProcessConfig;
|
||||
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
|
||||
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
@Api(tags = "管理后台 - 数据库文档")
|
||||
@RestController
|
||||
@RequestMapping("/infra/db-doc")
|
||||
public class DbDocController {
|
||||
|
||||
@Resource
|
||||
private DynamicDataSourceProperties dynamicDataSourceProperties;
|
||||
|
||||
private static final String FILE_OUTPUT_DIR = System.getProperty("java.io.tmpdir") + File.separator
|
||||
+ "db-doc";
|
||||
private static final String DOC_FILE_NAME = "数据库文档";
|
||||
private static final String DOC_VERSION = "1.0.0";
|
||||
private static final String DOC_DESCRIPTION = "文档描述";
|
||||
|
||||
@GetMapping("/export-html")
|
||||
@ApiOperation("导出 html 格式的数据文档")
|
||||
@ApiImplicitParam(name = "deleteFile", value = "是否删除在服务器本地生成的数据库文档", example = "true",
|
||||
dataTypeClass = Boolean.class)
|
||||
public void exportHtml(@RequestParam(defaultValue = "true") Boolean deleteFile,
|
||||
HttpServletResponse response) throws IOException {
|
||||
doExportFile(EngineFileType.HTML, deleteFile, response);
|
||||
}
|
||||
|
||||
@GetMapping("/export-word")
|
||||
@ApiOperation("导出 word 格式的数据文档")
|
||||
@ApiImplicitParam(name = "deleteFile", value = "是否删除在服务器本地生成的数据库文档", example = "true",
|
||||
dataTypeClass = Boolean.class)
|
||||
public void exportWord(@RequestParam(defaultValue = "true") Boolean deleteFile,
|
||||
HttpServletResponse response) throws IOException {
|
||||
doExportFile(EngineFileType.WORD, deleteFile, response);
|
||||
}
|
||||
|
||||
@GetMapping("/export-markdown")
|
||||
@ApiOperation("导出 markdown 格式的数据文档")
|
||||
@ApiImplicitParam(name = "deleteFile", value = "是否删除在服务器本地生成的数据库文档", example = "true",
|
||||
dataTypeClass = Boolean.class)
|
||||
public void exportMarkdown(@RequestParam(defaultValue = "true") Boolean deleteFile,
|
||||
HttpServletResponse response) throws IOException {
|
||||
doExportFile(EngineFileType.MD, deleteFile, response);
|
||||
}
|
||||
|
||||
private void doExportFile(EngineFileType fileOutputType, Boolean deleteFile,
|
||||
HttpServletResponse response) throws IOException {
|
||||
String docFileName = DOC_FILE_NAME + "_" + IdUtil.fastSimpleUUID();
|
||||
String filePath = doExportFile(fileOutputType, docFileName);
|
||||
String downloadFileName = DOC_FILE_NAME + fileOutputType.getFileSuffix(); //下载后的文件名
|
||||
try {
|
||||
// 读取,返回
|
||||
ServletUtils.writeAttachment(response, downloadFileName, FileUtil.readBytes(filePath));
|
||||
} finally {
|
||||
handleDeleteFile(deleteFile, filePath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出文件,返回文件路径
|
||||
*
|
||||
* @param fileOutputType 文件类型
|
||||
* @param fileName 文件名, 无需 ".docx" 等文件后缀
|
||||
* @return 生成的文件所在路径
|
||||
*/
|
||||
private String doExportFile(EngineFileType fileOutputType, String fileName) {
|
||||
try (HikariDataSource dataSource = buildDataSource()) {
|
||||
// 创建 screw 的配置
|
||||
Configuration config = Configuration.builder()
|
||||
.version(DOC_VERSION) // 版本
|
||||
.description(DOC_DESCRIPTION) // 描述
|
||||
.dataSource(dataSource) // 数据源
|
||||
.engineConfig(buildEngineConfig(fileOutputType, fileName)) // 引擎配置
|
||||
.produceConfig(buildProcessConfig()) // 处理配置
|
||||
.build();
|
||||
|
||||
// 执行 screw,生成数据库文档
|
||||
new DocumentationExecute(config).execute();
|
||||
|
||||
return FILE_OUTPUT_DIR + File.separator + fileName + fileOutputType.getFileSuffix();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleDeleteFile(Boolean deleteFile, String filePath) {
|
||||
if (!deleteFile) {
|
||||
return;
|
||||
}
|
||||
FileUtil.del(filePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建数据源
|
||||
*/
|
||||
// TODO 芋艿:screw 暂时不支持 druid,尴尬
|
||||
private HikariDataSource buildDataSource() {
|
||||
// 获得 DataSource 数据源,目前只支持首个
|
||||
String primary = dynamicDataSourceProperties.getPrimary();
|
||||
DataSourceProperty dataSourceProperty = dynamicDataSourceProperties.getDatasource().get(primary);
|
||||
// 创建 HikariConfig 配置类
|
||||
HikariConfig hikariConfig = new HikariConfig();
|
||||
hikariConfig.setJdbcUrl(dataSourceProperty.getUrl());
|
||||
hikariConfig.setUsername(dataSourceProperty.getUsername());
|
||||
hikariConfig.setPassword(dataSourceProperty.getPassword());
|
||||
hikariConfig.addDataSourceProperty("useInformationSchema", "true"); // 设置可以获取 tables remarks 信息
|
||||
// 创建数据源
|
||||
return new HikariDataSource(hikariConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 screw 的引擎配置
|
||||
*/
|
||||
private static EngineConfig buildEngineConfig(EngineFileType fileOutputType, String docFileName) {
|
||||
return EngineConfig.builder()
|
||||
.fileOutputDir(FILE_OUTPUT_DIR) // 生成文件路径
|
||||
.openOutputDir(false) // 打开目录
|
||||
.fileType(fileOutputType) // 文件类型
|
||||
.produceType(EngineTemplateType.velocity) // 文件类型
|
||||
.fileName(docFileName) // 自定义文件名称
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 screw 的处理配置,一般可忽略
|
||||
* 指定生成逻辑、当存在指定表、指定表前缀、指定表后缀时,将生成指定表,其余表不生成、并跳过忽略表配置
|
||||
*/
|
||||
private static ProcessConfig buildProcessConfig() {
|
||||
return ProcessConfig.builder()
|
||||
.ignoreTablePrefix(Arrays.asList("QRTZ_", "ACT_")) // 忽略表前缀
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,96 @@
|
||||
package cn.iocoder.yudao.module.infra.controller.admin.test;
|
||||
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.test.vo.*;
|
||||
import cn.iocoder.yudao.module.infra.convert.test.TestDemoConvert;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.test.TestDemoDO;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import javax.annotation.Resource;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import io.swagger.annotations.*;
|
||||
|
||||
import javax.validation.*;
|
||||
import javax.servlet.http.*;
|
||||
import java.util.*;
|
||||
import java.io.IOException;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
||||
|
||||
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
|
||||
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;
|
||||
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.test.vo.*;
|
||||
import cn.iocoder.yudao.module.infra.service.test.TestDemoService;
|
||||
|
||||
@Api(tags = "管理后台 - 字典类型")
|
||||
@RestController
|
||||
@RequestMapping("/infra/test-demo")
|
||||
@Validated
|
||||
public class TestDemoController {
|
||||
|
||||
@Resource
|
||||
private TestDemoService testDemoService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@ApiOperation("创建字典类型")
|
||||
@PreAuthorize("@ss.hasPermission('infra:test-demo:create')") public CommonResult<Long> createTestDemo(@Valid @RequestBody TestDemoCreateReqVO createReqVO) {
|
||||
return success(testDemoService.createTestDemo(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@ApiOperation("更新字典类型")
|
||||
@PreAuthorize("@ss.hasPermission('infra:test-demo:update')") public CommonResult<Boolean> updateTestDemo(@Valid @RequestBody TestDemoUpdateReqVO updateReqVO) {
|
||||
testDemoService.updateTestDemo(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@ApiOperation("删除字典类型")
|
||||
@ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class)
|
||||
@PreAuthorize("@ss.hasPermission('infra:test-demo:delete')")
|
||||
public CommonResult<Boolean> deleteTestDemo(@RequestParam("id") Long id) {
|
||||
testDemoService.deleteTestDemo(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@ApiOperation("获得字典类型")
|
||||
@ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
|
||||
@PreAuthorize("@ss.hasPermission('infra:test-demo:query')")
|
||||
public CommonResult<TestDemoRespVO> getTestDemo(@RequestParam("id") Long id) {
|
||||
TestDemoDO testDemo = testDemoService.getTestDemo(id);
|
||||
return success(TestDemoConvert.INSTANCE.convert(testDemo));
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
@ApiOperation("获得字典类型列表")
|
||||
@ApiImplicitParam(name = "ids", value = "编号列表", required = true, example = "1024,2048", dataTypeClass = List.class)
|
||||
@PreAuthorize("@ss.hasPermission('infra:test-demo:query')")
|
||||
public CommonResult<List<TestDemoRespVO>> getTestDemoList(@RequestParam("ids") Collection<Long> ids) {
|
||||
List<TestDemoDO> list = testDemoService.getTestDemoList(ids);
|
||||
return success(TestDemoConvert.INSTANCE.convertList(list));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@ApiOperation("获得字典类型分页")
|
||||
@PreAuthorize("@ss.hasPermission('infra:test-demo:query')") public CommonResult<PageResult<TestDemoRespVO>> getTestDemoPage(@Valid TestDemoPageReqVO pageVO) {
|
||||
PageResult<TestDemoDO> pageResult = testDemoService.getTestDemoPage(pageVO);
|
||||
return success(TestDemoConvert.INSTANCE.convertPage(pageResult));
|
||||
}
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@ApiOperation("导出字典类型 Excel")
|
||||
@PreAuthorize("@ss.hasPermission('infra:test-demo:export')") @OperateLog(type = EXPORT)
|
||||
public void exportTestDemoExcel(@Valid TestDemoExportReqVO exportReqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
List<TestDemoDO> list = testDemoService.getTestDemoList(exportReqVO);
|
||||
// 导出 Excel
|
||||
List<TestDemoExcelVO> datas = TestDemoConvert.INSTANCE.convertList02(list);
|
||||
ExcelUtils.write(response, "字典类型.xls", "数据", TestDemoExcelVO.class, datas);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
package cn.iocoder.yudao.module.infra.controller.admin.test.vo;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import io.swagger.annotations.*;
|
||||
import javax.validation.constraints.*;
|
||||
|
||||
/**
|
||||
* 字典类型 Base VO,提供给添加、修改、详细的子 VO 使用
|
||||
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
|
||||
*/
|
||||
@Data
|
||||
public class TestDemoBaseVO {
|
||||
|
||||
@ApiModelProperty(value = "名字", required = true)
|
||||
@NotNull(message = "名字不能为空")
|
||||
private String name;
|
||||
|
||||
@ApiModelProperty(value = "状态", required = true)
|
||||
@NotNull(message = "状态不能为空")
|
||||
private Integer status;
|
||||
|
||||
@ApiModelProperty(value = "类型", required = true)
|
||||
@NotNull(message = "类型不能为空")
|
||||
private Integer type;
|
||||
|
||||
@ApiModelProperty(value = "分类", required = true)
|
||||
@NotNull(message = "分类不能为空")
|
||||
private Integer category;
|
||||
|
||||
@ApiModelProperty(value = "备注")
|
||||
private String remark;
|
||||
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
package cn.iocoder.yudao.module.infra.controller.admin.test.vo;
|
||||
|
||||
import lombok.*;
|
||||
import io.swagger.annotations.*;
|
||||
|
||||
@ApiModel("管理后台 - 字典类型创建 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class TestDemoCreateReqVO extends TestDemoBaseVO {
|
||||
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
package cn.iocoder.yudao.module.infra.controller.admin.test.vo;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import io.swagger.annotations.*;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
|
||||
/**
|
||||
* 字典类型 Excel VO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
public class TestDemoExcelVO {
|
||||
|
||||
@ExcelProperty("编号")
|
||||
private Long id;
|
||||
|
||||
@ExcelProperty("名字")
|
||||
private String name;
|
||||
|
||||
@ExcelProperty("状态")
|
||||
private Integer status;
|
||||
|
||||
@ExcelProperty("类型")
|
||||
private Integer type;
|
||||
|
||||
@ExcelProperty("分类")
|
||||
private Integer category;
|
||||
|
||||
@ExcelProperty("备注")
|
||||
private String remark;
|
||||
|
||||
@ExcelProperty("创建时间")
|
||||
private Date createTime;
|
||||
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
package cn.iocoder.yudao.module.infra.controller.admin.test.vo;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import io.swagger.annotations.*;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@ApiModel(value = "管理后台 - 字典类型 Excel 导出 Request VO", description = "参数和 TestDemoPageReqVO 是一致的")
|
||||
@Data
|
||||
public class TestDemoExportReqVO {
|
||||
|
||||
@ApiModelProperty(value = "名字")
|
||||
private String name;
|
||||
|
||||
@ApiModelProperty(value = "状态")
|
||||
private Integer status;
|
||||
|
||||
@ApiModelProperty(value = "类型")
|
||||
private Integer type;
|
||||
|
||||
@ApiModelProperty(value = "分类")
|
||||
private Integer category;
|
||||
|
||||
@ApiModelProperty(value = "备注")
|
||||
private String remark;
|
||||
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
@ApiModelProperty(value = "开始创建时间")
|
||||
private Date beginCreateTime;
|
||||
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
@ApiModelProperty(value = "结束创建时间")
|
||||
private Date endCreateTime;
|
||||
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
package cn.iocoder.yudao.module.infra.controller.admin.test.vo;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import io.swagger.annotations.*;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@ApiModel("管理后台 - 字典类型分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class TestDemoPageReqVO extends PageParam {
|
||||
|
||||
@ApiModelProperty(value = "名字")
|
||||
private String name;
|
||||
|
||||
@ApiModelProperty(value = "状态")
|
||||
private Integer status;
|
||||
|
||||
@ApiModelProperty(value = "类型")
|
||||
private Integer type;
|
||||
|
||||
@ApiModelProperty(value = "分类")
|
||||
private Integer category;
|
||||
|
||||
@ApiModelProperty(value = "备注")
|
||||
private String remark;
|
||||
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
@ApiModelProperty(value = "开始创建时间")
|
||||
private Date beginCreateTime;
|
||||
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
@ApiModelProperty(value = "结束创建时间")
|
||||
private Date endCreateTime;
|
||||
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
package cn.iocoder.yudao.module.infra.controller.admin.test.vo;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import io.swagger.annotations.*;
|
||||
|
||||
@ApiModel("管理后台 - 字典类型 Response VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class TestDemoRespVO extends TestDemoBaseVO {
|
||||
|
||||
@ApiModelProperty(value = "编号", required = true)
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty(value = "创建时间", required = true)
|
||||
private Date createTime;
|
||||
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
package cn.iocoder.yudao.module.infra.controller.admin.test.vo;
|
||||
|
||||
import lombok.*;
|
||||
import io.swagger.annotations.*;
|
||||
import javax.validation.constraints.*;
|
||||
|
||||
@ApiModel("管理后台 - 字典类型更新 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class TestDemoUpdateReqVO extends TestDemoBaseVO {
|
||||
|
||||
@ApiModelProperty(value = "编号", required = true)
|
||||
@NotNull(message = "编号不能为空")
|
||||
private Long id;
|
||||
|
||||
}
|
@@ -0,0 +1,70 @@
|
||||
package cn.iocoder.yudao.module.infra.convert.codegen;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenDetailRespVO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenPreviewRespVO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.column.CodegenColumnRespVO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTableRespVO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.SchemaTableRespVO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.SchemaColumnDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.SchemaTableDO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Mapper
|
||||
public interface CodegenConvert {
|
||||
|
||||
CodegenConvert INSTANCE = Mappers.getMapper(CodegenConvert.class);
|
||||
|
||||
// ========== InformationSchemaTableDO 和 InformationSchemaColumnDO 相关 ==========
|
||||
|
||||
CodegenTableDO convert(SchemaTableDO bean);
|
||||
|
||||
List<CodegenColumnDO> convertList(List<SchemaColumnDO> list);
|
||||
|
||||
CodegenTableRespVO convert(SchemaColumnDO bean);
|
||||
|
||||
// ========== CodegenTableDO 相关 ==========
|
||||
|
||||
// List<CodegenTableRespVO> convertList02(List<CodegenTableDO> list);
|
||||
|
||||
CodegenTableRespVO convert(CodegenTableDO bean);
|
||||
|
||||
PageResult<CodegenTableRespVO> convertPage(PageResult<CodegenTableDO> page);
|
||||
|
||||
// ========== CodegenTableDO 相关 ==========
|
||||
|
||||
List<CodegenColumnRespVO> convertList02(List<CodegenColumnDO> list);
|
||||
|
||||
CodegenTableDO convert(CodegenUpdateReqVO.Table bean);
|
||||
|
||||
List<CodegenColumnDO> convertList03(List<CodegenUpdateReqVO.Column> columns);
|
||||
|
||||
List<SchemaTableRespVO> convertList04(List<SchemaTableDO> list);
|
||||
|
||||
// ========== 其它 ==========
|
||||
|
||||
default CodegenDetailRespVO convert(CodegenTableDO table, List<CodegenColumnDO> columns) {
|
||||
CodegenDetailRespVO respVO = new CodegenDetailRespVO();
|
||||
respVO.setTable(convert(table));
|
||||
respVO.setColumns(convertList02(columns));
|
||||
return respVO;
|
||||
}
|
||||
|
||||
default List<CodegenPreviewRespVO> convert(Map<String, String> codes) {
|
||||
return codes.entrySet().stream().map(entry -> {
|
||||
CodegenPreviewRespVO respVO = new CodegenPreviewRespVO();
|
||||
respVO.setFilePath(entry.getKey());
|
||||
respVO.setCode(entry.getValue());
|
||||
return respVO;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
package cn.iocoder.yudao.module.infra.convert.test;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.test.vo.TestDemoCreateReqVO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.test.vo.TestDemoExcelVO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.test.vo.TestDemoRespVO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.test.vo.TestDemoUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.test.TestDemoDO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 字典类型 Convert
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Mapper
|
||||
public interface TestDemoConvert {
|
||||
|
||||
TestDemoConvert INSTANCE = Mappers.getMapper(TestDemoConvert.class);
|
||||
|
||||
TestDemoDO convert(TestDemoCreateReqVO bean);
|
||||
|
||||
TestDemoDO convert(TestDemoUpdateReqVO bean);
|
||||
|
||||
TestDemoRespVO convert(TestDemoDO bean);
|
||||
|
||||
List<TestDemoRespVO> convertList(List<TestDemoDO> list);
|
||||
|
||||
PageResult<TestDemoRespVO> convertPage(PageResult<TestDemoDO> page);
|
||||
|
||||
List<TestDemoExcelVO> convertList02(List<TestDemoDO> list);
|
||||
|
||||
}
|
@@ -0,0 +1,124 @@
|
||||
package cn.iocoder.yudao.module.infra.dal.dataobject.codegen;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenColumnHtmlTypeEnum;
|
||||
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenColumnListConditionEnum;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 代码生成 column 字段定义
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName(value = "infra_codegen_column", autoResultMap = true)
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CodegenColumnDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* ID 编号
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 表编号
|
||||
*
|
||||
* 关联 {@link CodegenTableDO#getId()}
|
||||
*/
|
||||
private Long tableId;
|
||||
|
||||
// ========== 表相关字段 ==========
|
||||
|
||||
/**
|
||||
* 字段名
|
||||
*/
|
||||
private String columnName;
|
||||
/**
|
||||
* 字段类型
|
||||
*/
|
||||
private String columnType;
|
||||
/**
|
||||
* 字段描述
|
||||
*/
|
||||
private String columnComment;
|
||||
/**
|
||||
* 是否允许为空
|
||||
*/
|
||||
private Boolean nullable;
|
||||
/**
|
||||
* 是否主键
|
||||
*/
|
||||
private Boolean primaryKey;
|
||||
/**
|
||||
* 是否自增
|
||||
*/
|
||||
private Boolean autoIncrement;
|
||||
/**
|
||||
* 排序
|
||||
*/
|
||||
private Integer ordinalPosition;
|
||||
|
||||
// ========== Java 相关字段 ==========
|
||||
|
||||
/**
|
||||
* Java 属性类型
|
||||
*
|
||||
* 例如说 String、Boolean 等等
|
||||
*/
|
||||
private String javaType;
|
||||
/**
|
||||
* Java 属性名
|
||||
*/
|
||||
// @NotBlank(message = "Java属性不能为空")
|
||||
private String javaField;
|
||||
/**
|
||||
* 字典类型
|
||||
*
|
||||
* 关联 DictTypeDO 的 type 属性
|
||||
*/
|
||||
private String dictType;
|
||||
/**
|
||||
* 数据示例,主要用于生成 Swagger 注解的 example 字段
|
||||
*/
|
||||
private String example;
|
||||
|
||||
// ========== CRUD 相关字段 ==========
|
||||
|
||||
/**
|
||||
* 是否为 Create 创建操作的字段
|
||||
*/
|
||||
private Boolean createOperation;
|
||||
/**
|
||||
* 是否为 Update 更新操作的字段
|
||||
*/
|
||||
private Boolean updateOperation;
|
||||
/**
|
||||
* 是否为 List 查询操作的字段
|
||||
*/
|
||||
private Boolean listOperation;
|
||||
/**
|
||||
* List 查询操作的条件类型
|
||||
*
|
||||
* 枚举 {@link CodegenColumnListConditionEnum}
|
||||
*/
|
||||
private String listOperationCondition;
|
||||
/**
|
||||
* 是否为 List 查询操作的返回字段
|
||||
*/
|
||||
private Boolean listOperationResult;
|
||||
|
||||
// ========== UI 相关字段 ==========
|
||||
|
||||
/**
|
||||
* 显示类型
|
||||
*
|
||||
* 枚举 {@link CodegenColumnHtmlTypeEnum}
|
||||
*/
|
||||
private String htmlType;
|
||||
|
||||
}
|
@@ -0,0 +1,102 @@
|
||||
package cn.iocoder.yudao.module.infra.dal.dataobject.codegen;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenSceneEnum;
|
||||
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 代码生成 table 表定义
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName(value = "infra_codegen_table", autoResultMap = true)
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CodegenTableDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* ID 编号
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 导入类型
|
||||
*
|
||||
* 枚举 {@link CodegenTemplateTypeEnum}
|
||||
*/
|
||||
private Integer importType;
|
||||
/**
|
||||
* 生成场景
|
||||
*
|
||||
* 枚举 {@link CodegenSceneEnum}
|
||||
*/
|
||||
private Integer scene;
|
||||
|
||||
// ========== 表相关字段 ==========
|
||||
|
||||
/**
|
||||
* 表名称
|
||||
*/
|
||||
private String tableName;
|
||||
/**
|
||||
* 表描述
|
||||
*/
|
||||
private String tableComment;
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
// ========== 类相关字段 ==========
|
||||
|
||||
/**
|
||||
* 模块名,即一级目录
|
||||
*
|
||||
* 例如说,system、infra、tool 等等
|
||||
*/
|
||||
private String moduleName;
|
||||
/**
|
||||
* 业务名,即二级目录
|
||||
*
|
||||
* 例如说,user、permission、dict 等等
|
||||
*/
|
||||
private String businessName;
|
||||
/**
|
||||
* 类名称(首字母大写)
|
||||
*
|
||||
* 例如说,SysUser、SysMenu、SysDictData 等等
|
||||
*/
|
||||
private String className;
|
||||
/**
|
||||
* 类描述
|
||||
*/
|
||||
private String classComment;
|
||||
/**
|
||||
* 作者
|
||||
*/
|
||||
private String author;
|
||||
|
||||
// ========== 生成相关字段 ==========
|
||||
|
||||
/**
|
||||
* 模板类型
|
||||
*
|
||||
* 枚举 {@link CodegenTemplateTypeEnum}
|
||||
*/
|
||||
private Integer templateType;
|
||||
|
||||
// ========== 菜单相关字段 ==========
|
||||
|
||||
/**
|
||||
* 父菜单编号
|
||||
*
|
||||
* 关联 MenuDO 的 id 属性
|
||||
*/
|
||||
private Long parentMenuId;
|
||||
|
||||
}
|
@@ -0,0 +1,54 @@
|
||||
package cn.iocoder.yudao.module.infra.dal.dataobject.codegen;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* MySQL 数据库中的 column 字段定义
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName(value = "information_schema.columns", autoResultMap = true)
|
||||
@Data
|
||||
@Builder
|
||||
public class SchemaColumnDO {
|
||||
|
||||
/**
|
||||
* 表名称
|
||||
*/
|
||||
private String tableName;
|
||||
/**
|
||||
* 字段名
|
||||
*/
|
||||
private String columnName;
|
||||
/**
|
||||
* 字段类型
|
||||
*/
|
||||
private String columnType;
|
||||
/**
|
||||
* 字段描述
|
||||
*/
|
||||
private String columnComment;
|
||||
/**
|
||||
* 是否允许为空
|
||||
*/
|
||||
@TableField("case when is_nullable = 'yes' then '1' else '0' end")
|
||||
private Boolean nullable;
|
||||
/**
|
||||
* 是否主键
|
||||
*/
|
||||
@TableField("case when column_key = 'PRI' then '1' else '0' end")
|
||||
private Boolean primaryKey;
|
||||
/**
|
||||
* 是否自增
|
||||
*/
|
||||
@TableField("case when extra = 'auto_increment' then '1' else '0' end")
|
||||
private Boolean autoIncrement;
|
||||
/**
|
||||
* 排序字段
|
||||
*/
|
||||
private Integer ordinalPosition;
|
||||
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
package cn.iocoder.yudao.module.infra.dal.dataobject.codegen;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* MySQL 数据库中的 table 表定义
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName(value = "information_schema.tables", autoResultMap = true)
|
||||
@Data
|
||||
@Builder
|
||||
public class SchemaTableDO {
|
||||
|
||||
/**
|
||||
* 数据库
|
||||
*/
|
||||
private String tableSchema;
|
||||
/**
|
||||
* 表名称
|
||||
*/
|
||||
private String tableName;
|
||||
/**
|
||||
* 表描述
|
||||
*/
|
||||
private String tableComment;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createTime;
|
||||
|
||||
}
|
@@ -0,0 +1,48 @@
|
||||
package cn.iocoder.yudao.module.infra.dal.dataobject.test;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* 字典类型 DO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName("infra_test_demo")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class TestDemoDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 名字
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
private Integer status;
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
private Integer type;
|
||||
/**
|
||||
* 分类
|
||||
*/
|
||||
private Integer category;
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
package cn.iocoder.yudao.module.infra.dal.mysql.codegen;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface CodegenColumnMapper extends BaseMapperX<CodegenColumnDO> {
|
||||
|
||||
default List<CodegenColumnDO> selectListByTableId(Long tableId) {
|
||||
return selectList(new QueryWrapper<CodegenColumnDO>().eq("table_id", tableId)
|
||||
.orderByAsc("ordinal_position"));
|
||||
}
|
||||
|
||||
default void deleteListByTableId(Long tableId) {
|
||||
delete(new QueryWrapper<CodegenColumnDO>().eq("table_id", tableId));
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
package cn.iocoder.yudao.module.infra.dal.mysql.codegen;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTablePageReqVO;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface CodegenTableMapper extends BaseMapperX<CodegenTableDO> {
|
||||
|
||||
default CodegenTableDO selectByTableName(String tableName) {
|
||||
return selectOne(new QueryWrapper<CodegenTableDO>().eq("table_name", tableName));
|
||||
}
|
||||
|
||||
default PageResult<CodegenTableDO> selectPage(CodegenTablePageReqVO pageReqVO) {
|
||||
return selectPage(pageReqVO, new QueryWrapperX<CodegenTableDO>()
|
||||
.likeIfPresent("table_name", pageReqVO.getTableName())
|
||||
.likeIfPresent("table_comment", pageReqVO.getTableComment())
|
||||
.betweenIfPresent("create_time", pageReqVO.getBeginCreateTime(), pageReqVO.getEndCreateTime()));
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
package cn.iocoder.yudao.module.infra.dal.mysql.codegen;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.SchemaColumnDO;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface SchemaColumnMapper extends BaseMapperX<SchemaColumnDO> {
|
||||
|
||||
default List<SchemaColumnDO> selectListByTableName(String tableSchema, String tableName) {
|
||||
return selectList(new QueryWrapper<SchemaColumnDO>().eq("table_name", tableName)
|
||||
.eq("table_schema", tableSchema)
|
||||
.orderByAsc("ordinal_position"));
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
package cn.iocoder.yudao.module.infra.dal.mysql.codegen;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.SchemaTableDO;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface SchemaTableMapper extends BaseMapperX<SchemaTableDO> {
|
||||
|
||||
default List<SchemaTableDO> selectList(Collection<String> tableSchemas, String tableName, String tableComment) {
|
||||
return selectList(new QueryWrapperX<SchemaTableDO>().in("table_schema", tableSchemas)
|
||||
.likeIfPresent("table_name", tableName)
|
||||
.likeIfPresent("table_comment", tableComment));
|
||||
}
|
||||
|
||||
default SchemaTableDO selectByTableSchemaAndTableName(String tableSchema, String tableName) {
|
||||
return selectOne(new QueryWrapper<SchemaTableDO>().eq("table_schema",tableSchema)
|
||||
.eq("table_name", tableName));
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
package cn.iocoder.yudao.module.infra.dal.mysql.test;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.test.vo.TestDemoExportReqVO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.test.vo.TestDemoPageReqVO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.test.TestDemoDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 字典类型 Mapper
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Mapper
|
||||
public interface TestDemoMapper extends BaseMapperX<TestDemoDO> {
|
||||
|
||||
default PageResult<TestDemoDO> selectPage(TestDemoPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<TestDemoDO>()
|
||||
.likeIfPresent(TestDemoDO::getName, reqVO.getName())
|
||||
.eqIfPresent(TestDemoDO::getStatus, reqVO.getStatus())
|
||||
.eqIfPresent(TestDemoDO::getType, reqVO.getType())
|
||||
.eqIfPresent(TestDemoDO::getCategory, reqVO.getCategory())
|
||||
.eqIfPresent(TestDemoDO::getRemark, reqVO.getRemark())
|
||||
.betweenIfPresent(TestDemoDO::getCreateTime, reqVO.getBeginCreateTime(), reqVO.getEndCreateTime())
|
||||
.orderByDesc(TestDemoDO::getId));
|
||||
}
|
||||
|
||||
default List<TestDemoDO> selectList(TestDemoExportReqVO reqVO) {
|
||||
return selectList(new LambdaQueryWrapperX<TestDemoDO>()
|
||||
.likeIfPresent(TestDemoDO::getName, reqVO.getName())
|
||||
.eqIfPresent(TestDemoDO::getStatus, reqVO.getStatus())
|
||||
.eqIfPresent(TestDemoDO::getType, reqVO.getType())
|
||||
.eqIfPresent(TestDemoDO::getCategory, reqVO.getCategory())
|
||||
.eqIfPresent(TestDemoDO::getRemark, reqVO.getRemark())
|
||||
.betweenIfPresent(TestDemoDO::getCreateTime, reqVO.getBeginCreateTime(), reqVO.getEndCreateTime())
|
||||
.orderByDesc(TestDemoDO::getId));
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
package cn.iocoder.yudao.module.infra.enums.codegen;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 代码生成器的字段 HTML 展示枚举
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum CodegenColumnHtmlTypeEnum {
|
||||
|
||||
INPUT("input"), // 文本框
|
||||
TEXTAREA("textarea"), // 文本域
|
||||
SELECT("select"), // 下拉框
|
||||
RADIO("radio"), // 单选框
|
||||
CHECKBOX("checkbox"), // 复选框
|
||||
DATETIME("datetime"), // 日期控件
|
||||
UPLOAD_IMAGE("upload_image"), // 上传图片
|
||||
UPLOAD_FILE("upload_file"), // 上传文件
|
||||
EDITOR("editor"), // 富文本控件
|
||||
;
|
||||
|
||||
/**
|
||||
* 条件
|
||||
*/
|
||||
private final String type;
|
||||
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
package cn.iocoder.yudao.module.infra.enums.codegen;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 代码生成器的字段过滤条件枚举
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum CodegenColumnListConditionEnum {
|
||||
|
||||
EQ("="),
|
||||
NE("!="),
|
||||
GT(">"),
|
||||
GTE(">="),
|
||||
LT("<"),
|
||||
LTE("<="),
|
||||
LIKE("LIKE"),
|
||||
BETWEEN("BETWEEN");
|
||||
|
||||
/**
|
||||
* 条件
|
||||
*/
|
||||
private final String condition;
|
||||
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
package cn.iocoder.yudao.module.infra.enums.codegen;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 代码生成的导入类型
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum CodegenImportTypeEnum {
|
||||
|
||||
DB(1), // 从 information_schema 的 table 和 columns 表导入
|
||||
SQL(2); // 基于建表 SQL 语句导入
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
private final Integer type;
|
||||
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
package cn.iocoder.yudao.module.infra.enums.codegen;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import static cn.hutool.core.util.ArrayUtil.*;
|
||||
|
||||
/**
|
||||
* 代码生成的场景枚举
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum CodegenSceneEnum {
|
||||
|
||||
ADMIN(1, "管理后台", "admin", ""),
|
||||
APP(2, "用户 APP", "app", "App");
|
||||
|
||||
/**
|
||||
* 场景
|
||||
*/
|
||||
private final Integer scene;
|
||||
/**
|
||||
* 场景名
|
||||
*/
|
||||
private final String name;
|
||||
/**
|
||||
* 基础包名
|
||||
*/
|
||||
private final String basePackage;
|
||||
/**
|
||||
* Controller 和 VO 类的前缀
|
||||
*/
|
||||
private final String prefixClass;
|
||||
|
||||
public static CodegenSceneEnum valueOf(Integer scene) {
|
||||
return firstMatch(sceneEnum -> sceneEnum.getScene().equals(scene), values());
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
package cn.iocoder.yudao.module.infra.enums.codegen;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 代码生成模板类型
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum CodegenTemplateTypeEnum {
|
||||
|
||||
CRUD(1), // 单表(增删改查)
|
||||
TREE(2), // 树表(增删改查)
|
||||
;
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
private final Integer type;
|
||||
|
||||
}
|
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 占位
|
||||
*/
|
||||
package cn.iocoder.yudao.module.infra.enums;
|
@@ -0,0 +1,9 @@
|
||||
package cn.iocoder.yudao.module.infra.framework.codegen.config;
|
||||
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(CodegenProperties.class)
|
||||
public class CodegenConfiguration {
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
package cn.iocoder.yudao.module.infra.framework.codegen.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Collection;
|
||||
|
||||
@ConfigurationProperties(prefix = "yudao.codegen")
|
||||
@Validated
|
||||
@Data
|
||||
public class CodegenProperties {
|
||||
|
||||
/**
|
||||
* 生成的 Java 代码的基础包
|
||||
*/
|
||||
@NotNull(message = "Java 代码的基础包不能为空")
|
||||
private String basePackage;
|
||||
|
||||
/**
|
||||
* 数据库名数组
|
||||
*/
|
||||
@NotEmpty(message = "数据库不能为空")
|
||||
private Collection<String> dbSchemas;
|
||||
|
||||
}
|
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 代码生成器
|
||||
*/
|
||||
package cn.iocoder.yudao.module.infra.framework.codegen;
|
@@ -1,6 +1,7 @@
|
||||
/**
|
||||
* infra 包下,我们放基础设施的运维与管理,支撑上层的通用与核心业务。
|
||||
* 例如说:定时任务的管理、服务器的信息等等
|
||||
* infra 模块,主要提供两块能力:
|
||||
* 1. 我们放基础设施的运维与管理,支撑上层的通用与核心业务。 例如说:定时任务的管理、服务器的信息等等
|
||||
* 2. 研发工具,提升研发效率与质量。 例如说:代码生成器、接口文档等等
|
||||
*
|
||||
* 1. Controller URL:以 /infra/ 开头,避免和其它 Module 冲突
|
||||
* 2. DataObject 表名:以 infra_ 开头,方便在数据库中区分
|
||||
|
@@ -0,0 +1,124 @@
|
||||
package cn.iocoder.yudao.module.infra.service.codegen;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTablePageReqVO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.SchemaTableDO;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 代码生成 Service 接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface CodegenService {
|
||||
|
||||
/**
|
||||
* 基于 SQL 建表语句,创建代码生成器的表定义
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param sql SQL 建表语句
|
||||
* @return 创建的表定义的编号
|
||||
*/
|
||||
Long createCodegenListFromSQL(Long userId, String sql);
|
||||
|
||||
/**
|
||||
* 基于数据库的表结构,创建代码生成器的表定义
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param tableName 表名称
|
||||
* @return 创建的表定义的编号
|
||||
*/
|
||||
Long createCodegen(Long userId, String tableName);
|
||||
|
||||
/**
|
||||
* 基于 {@link #createCodegen(Long, String)} 的批量创建
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param tableNames 表名称数组
|
||||
* @return 创建的表定义的编号数组
|
||||
*/
|
||||
List<Long> createCodegenListFromDB(Long userId, List<String> tableNames);
|
||||
|
||||
/**
|
||||
* 更新数据库的表和字段定义
|
||||
*
|
||||
* @param updateReqVO 更新信息
|
||||
*/
|
||||
void updateCodegen(CodegenUpdateReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 基于数据库的表结构,同步数据库的表和字段定义
|
||||
*
|
||||
* @param tableId 表编号
|
||||
*/
|
||||
void syncCodegenFromDB(Long tableId);
|
||||
|
||||
/**
|
||||
* 基于 SQL 建表语句,同步数据库的表和字段定义
|
||||
*
|
||||
* @param tableId 表编号
|
||||
* @param sql SQL 建表语句
|
||||
*/
|
||||
void syncCodegenFromSQL(Long tableId, String sql);
|
||||
|
||||
/**
|
||||
* 删除数据库的表和字段定义
|
||||
*
|
||||
* @param tableId 数据编号
|
||||
*/
|
||||
void deleteCodegen(Long tableId);
|
||||
|
||||
/**
|
||||
* 获得表定义分页
|
||||
*
|
||||
* @param pageReqVO 分页条件
|
||||
* @return 表定义分页
|
||||
*/
|
||||
PageResult<CodegenTableDO> getCodegenTablePage(CodegenTablePageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 获得表定义
|
||||
*
|
||||
* @param id 表编号
|
||||
* @return 表定义
|
||||
*/
|
||||
CodegenTableDO getCodegenTablePage(Long id);
|
||||
|
||||
/**
|
||||
* 获得全部表定义
|
||||
*
|
||||
* @return 表定义数组
|
||||
*/
|
||||
List<CodegenTableDO> getCodeGenTableList();
|
||||
|
||||
/**
|
||||
* 获得指定表的字段定义数组
|
||||
*
|
||||
* @param tableId 表编号
|
||||
* @return 字段定义数组
|
||||
*/
|
||||
List<CodegenColumnDO> getCodegenColumnListByTableId(Long tableId);
|
||||
|
||||
/**
|
||||
* 执行指定表的代码生成
|
||||
*
|
||||
* @param tableId 表编号
|
||||
* @return 生成结果。key 为文件路径,value 为对应的代码内容
|
||||
*/
|
||||
Map<String, String> generationCodes(Long tableId);
|
||||
|
||||
/**
|
||||
* 获得数据库自带的表定义列表
|
||||
*
|
||||
* @param tableName 表名称
|
||||
* @param tableComment 表描述
|
||||
* @return 表定义列表
|
||||
*/
|
||||
List<SchemaTableDO> getSchemaTableList(String tableName, String tableComment);
|
||||
|
||||
}
|
@@ -0,0 +1,292 @@
|
||||
package cn.iocoder.yudao.module.infra.service.codegen;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTablePageReqVO;
|
||||
import cn.iocoder.yudao.module.infra.convert.codegen.CodegenConvert;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.SchemaColumnDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.SchemaTableDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.mysql.codegen.CodegenColumnMapper;
|
||||
import cn.iocoder.yudao.module.infra.dal.mysql.codegen.CodegenTableMapper;
|
||||
import cn.iocoder.yudao.module.infra.dal.mysql.codegen.SchemaColumnMapper;
|
||||
import cn.iocoder.yudao.module.infra.dal.mysql.codegen.SchemaTableMapper;
|
||||
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenImportTypeEnum;
|
||||
import cn.iocoder.yudao.module.infra.framework.codegen.config.CodegenProperties;
|
||||
import cn.iocoder.yudao.module.infra.service.codegen.inner.CodegenBuilder;
|
||||
import cn.iocoder.yudao.module.infra.service.codegen.inner.CodegenEngine;
|
||||
import cn.iocoder.yudao.module.infra.service.codegen.inner.CodegenSQLParser;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import org.apache.commons.collections4.KeyValue;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
|
||||
|
||||
/**
|
||||
* 代码生成 Service 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
public class CodegenServiceImpl implements CodegenService {
|
||||
|
||||
@Resource
|
||||
private SchemaTableMapper schemaTableMapper;
|
||||
@Resource
|
||||
private SchemaColumnMapper schemaColumnMapper;
|
||||
@Resource
|
||||
private CodegenTableMapper codegenTableMapper;
|
||||
@Resource
|
||||
private CodegenColumnMapper codegenColumnMapper;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi userApi;
|
||||
|
||||
@Resource
|
||||
private CodegenBuilder codegenBuilder;
|
||||
@Resource
|
||||
private CodegenEngine codegenEngine;
|
||||
|
||||
@Resource
|
||||
private CodegenProperties codegenProperties;
|
||||
|
||||
private Long createCodegen0(Long userId, CodegenImportTypeEnum importType,
|
||||
SchemaTableDO schemaTable, List<SchemaColumnDO> schemaColumns) {
|
||||
// 校验导入的表和字段非空
|
||||
if (schemaTable == null) {
|
||||
throw exception(CODEGEN_IMPORT_TABLE_NULL);
|
||||
}
|
||||
if (CollUtil.isEmpty(schemaColumns)) {
|
||||
throw exception(CODEGEN_IMPORT_COLUMNS_NULL);
|
||||
}
|
||||
// 校验是否已经存在
|
||||
if (codegenTableMapper.selectByTableName(schemaTable.getTableName()) != null) {
|
||||
throw exception(CODEGEN_TABLE_EXISTS);
|
||||
}
|
||||
|
||||
// 构建 CodegenTableDO 对象,插入到 DB 中
|
||||
CodegenTableDO table = codegenBuilder.buildTable(schemaTable);
|
||||
table.setImportType(importType.getType());
|
||||
table.setAuthor(userApi.getUser(userId).getNickname());
|
||||
codegenTableMapper.insert(table);
|
||||
// 构建 CodegenColumnDO 数组,插入到 DB 中
|
||||
List<CodegenColumnDO> columns = codegenBuilder.buildColumns(schemaColumns);
|
||||
columns.forEach(column -> {
|
||||
column.setTableId(table.getId());
|
||||
codegenColumnMapper.insert(column); // TODO 批量插入
|
||||
});
|
||||
return table.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long createCodegenListFromSQL(Long userId, String sql) {
|
||||
// 从 SQL 中,获得数据库表结构
|
||||
SchemaTableDO schemaTable;
|
||||
List<SchemaColumnDO> schemaColumns;
|
||||
try {
|
||||
KeyValue<SchemaTableDO, List<SchemaColumnDO>> result = CodegenSQLParser.parse(sql);
|
||||
schemaTable = result.getKey();
|
||||
schemaColumns = result.getValue();
|
||||
} catch (Exception ex) {
|
||||
throw exception(CODEGEN_PARSE_SQL_ERROR);
|
||||
}
|
||||
// 导入
|
||||
return this.createCodegen0(userId, CodegenImportTypeEnum.SQL, schemaTable, schemaColumns);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long createCodegen(Long userId, String tableName) {
|
||||
// 获取当前schema
|
||||
String tableSchema = codegenProperties.getDbSchemas().iterator().next();
|
||||
// 从数据库中,获得数据库表结构
|
||||
SchemaTableDO schemaTable = schemaTableMapper.selectByTableSchemaAndTableName(tableSchema, tableName);
|
||||
List<SchemaColumnDO> schemaColumns = schemaColumnMapper.selectListByTableName(tableSchema, tableName);
|
||||
// 导入
|
||||
return this.createCodegen0(userId, CodegenImportTypeEnum.DB, schemaTable, schemaColumns);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public List<Long> createCodegenListFromDB(Long userId, List<String> tableNames) {
|
||||
List<Long> ids = new ArrayList<>(tableNames.size());
|
||||
// 遍历添加。虽然效率会低一点,但是没必要做成完全批量,因为不会这么大量
|
||||
tableNames.forEach(tableName -> ids.add(createCodegen(userId, tableName)));
|
||||
return ids;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateCodegen(CodegenUpdateReqVO updateReqVO) {
|
||||
// 校验是否已经存在
|
||||
if (codegenTableMapper.selectById(updateReqVO.getTable().getId()) == null) {
|
||||
throw exception(CODEGEN_TABLE_NOT_EXISTS);
|
||||
}
|
||||
|
||||
// 更新 table 表定义
|
||||
CodegenTableDO updateTableObj = CodegenConvert.INSTANCE.convert(updateReqVO.getTable());
|
||||
codegenTableMapper.updateById(updateTableObj);
|
||||
// 更新 column 字段定义
|
||||
List<CodegenColumnDO> updateColumnObjs = CodegenConvert.INSTANCE.convertList03(updateReqVO.getColumns());
|
||||
updateColumnObjs.forEach(updateColumnObj -> codegenColumnMapper.updateById(updateColumnObj));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void syncCodegenFromDB(Long tableId) {
|
||||
// 校验是否已经存在
|
||||
CodegenTableDO table = codegenTableMapper.selectById(tableId);
|
||||
if (table == null) {
|
||||
throw exception(CODEGEN_TABLE_NOT_EXISTS);
|
||||
}
|
||||
String tableSchema = codegenProperties.getDbSchemas().iterator().next();
|
||||
// 从数据库中,获得数据库表结构
|
||||
List<SchemaColumnDO> schemaColumns = schemaColumnMapper.selectListByTableName(tableSchema, table.getTableName());
|
||||
|
||||
// 执行同步
|
||||
this.syncCodegen0(tableId, schemaColumns);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void syncCodegenFromSQL(Long tableId, String sql) {
|
||||
// 校验是否已经存在
|
||||
CodegenTableDO table = codegenTableMapper.selectById(tableId);
|
||||
if (table == null) {
|
||||
throw exception(CODEGEN_TABLE_NOT_EXISTS);
|
||||
}
|
||||
// 从 SQL 中,获得数据库表结构
|
||||
List<SchemaColumnDO> schemaColumns;
|
||||
try {
|
||||
KeyValue<SchemaTableDO, List<SchemaColumnDO>> result = CodegenSQLParser.parse(sql);
|
||||
schemaColumns = result.getValue();
|
||||
} catch (Exception ex) {
|
||||
throw exception(CODEGEN_PARSE_SQL_ERROR);
|
||||
}
|
||||
|
||||
// 执行同步
|
||||
this.syncCodegen0(tableId, schemaColumns);
|
||||
}
|
||||
|
||||
private void syncCodegen0(Long tableId, List<SchemaColumnDO> schemaColumns) {
|
||||
// 校验导入的字段不为空
|
||||
if (CollUtil.isEmpty(schemaColumns)) {
|
||||
throw exception(CODEGEN_SYNC_COLUMNS_NULL);
|
||||
}
|
||||
Set<String> schemaColumnNames = CollectionUtils.convertSet(schemaColumns, SchemaColumnDO::getColumnName);
|
||||
|
||||
// 构建 CodegenColumnDO 数组,只同步新增的字段
|
||||
List<CodegenColumnDO> codegenColumns = codegenColumnMapper.selectListByTableId(tableId);
|
||||
Set<String> codegenColumnNames = CollectionUtils.convertSet(codegenColumns, CodegenColumnDO::getColumnName);
|
||||
// 移除已经存在的字段
|
||||
schemaColumns.removeIf(column -> codegenColumnNames.contains(column.getColumnName()));
|
||||
// 计算需要删除的字段
|
||||
Set<Long> deleteColumnIds = codegenColumns.stream().filter(column -> !schemaColumnNames.contains(column.getColumnName()))
|
||||
.map(CodegenColumnDO::getId).collect(Collectors.toSet());
|
||||
if (CollUtil.isEmpty(schemaColumns) && CollUtil.isEmpty(deleteColumnIds)) {
|
||||
throw exception(CODEGEN_SYNC_NONE_CHANGE);
|
||||
}
|
||||
|
||||
// 插入新增的字段
|
||||
List<CodegenColumnDO> columns = codegenBuilder.buildColumns(schemaColumns);
|
||||
columns.forEach(column -> {
|
||||
column.setTableId(tableId);
|
||||
codegenColumnMapper.insert(column); // TODO 批量插入
|
||||
});
|
||||
// 删除不存在的字段
|
||||
if (CollUtil.isNotEmpty(deleteColumnIds)) {
|
||||
codegenColumnMapper.deleteBatchIds(deleteColumnIds);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deleteCodegen(Long tableId) {
|
||||
// 校验是否已经存在
|
||||
if (codegenTableMapper.selectById(tableId) == null) {
|
||||
throw exception(CODEGEN_TABLE_NOT_EXISTS);
|
||||
}
|
||||
|
||||
// 删除 table 表定义
|
||||
codegenTableMapper.deleteById(tableId);
|
||||
// 删除 column 字段定义
|
||||
codegenColumnMapper.deleteListByTableId(tableId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<CodegenTableDO> getCodegenTablePage(CodegenTablePageReqVO pageReqVO) {
|
||||
return codegenTableMapper.selectPage(pageReqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodegenTableDO getCodegenTablePage(Long id) {
|
||||
return codegenTableMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CodegenTableDO> getCodeGenTableList() {
|
||||
return codegenTableMapper.selectList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CodegenColumnDO> getCodegenColumnListByTableId(Long tableId) {
|
||||
return codegenColumnMapper.selectListByTableId(tableId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> generationCodes(Long tableId) {
|
||||
// 校验是否已经存在
|
||||
CodegenTableDO table = codegenTableMapper.selectById(tableId);
|
||||
if (codegenTableMapper.selectById(tableId) == null) {
|
||||
throw exception(CODEGEN_TABLE_NOT_EXISTS);
|
||||
}
|
||||
List<CodegenColumnDO> columns = codegenColumnMapper.selectListByTableId(tableId);
|
||||
if (CollUtil.isEmpty(columns)) {
|
||||
throw exception(CODEGEN_COLUMN_NOT_EXISTS);
|
||||
}
|
||||
|
||||
// 执行生成
|
||||
return codegenEngine.execute(table, columns);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SchemaTableDO> getSchemaTableList(String tableName, String tableComment) {
|
||||
List<SchemaTableDO> tables = schemaTableMapper.selectList(codegenProperties.getDbSchemas(), tableName, tableComment);
|
||||
// TODO 强制移除 Quartz 的表,未来做成可配置
|
||||
tables.removeIf(table -> table.getTableName().startsWith("QRTZ_"));
|
||||
tables.removeIf(table -> table.getTableName().startsWith("ACT_"));
|
||||
return tables;
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 修改保存参数校验
|
||||
// *
|
||||
// * @param genTable 业务信息
|
||||
// */
|
||||
// @Override
|
||||
// public void validateEdit(GenTable genTable) {
|
||||
// if (GenConstants.TPL_TREE.equals(genTable.getTplCategory())) {
|
||||
// String options = JSON.toJSONString(genTable.getParams());
|
||||
// JSONObject paramsObj = JSONObject.parseObject(options);
|
||||
// if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_CODE))) {
|
||||
// throw new CustomException("树编码字段不能为空");
|
||||
// } else if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_PARENT_CODE))) {
|
||||
// throw new CustomException("树父编码字段不能为空");
|
||||
// } else if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_NAME))) {
|
||||
// throw new CustomException("树名称字段不能为空");
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
@@ -0,0 +1,207 @@
|
||||
package cn.iocoder.yudao.module.infra.service.codegen.inner;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.module.infra.convert.codegen.CodegenConvert;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.SchemaColumnDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.SchemaTableDO;
|
||||
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenColumnHtmlTypeEnum;
|
||||
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenColumnListConditionEnum;
|
||||
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
|
||||
import static cn.hutool.core.text.CharSequenceUtil.*;
|
||||
|
||||
/**
|
||||
* 代码生成器的 Builder,负责:
|
||||
* 1. 将数据库的表 {@link SchemaTableDO} 定义,构建成 {@link CodegenTableDO}
|
||||
* 2. 将数据库的列 {@link SchemaColumnDO} 构定义,建成 {@link CodegenColumnDO}
|
||||
*/
|
||||
@Component
|
||||
public class CodegenBuilder {
|
||||
|
||||
/**
|
||||
* 字段名与 {@link CodegenColumnListConditionEnum} 的默认映射
|
||||
* 注意,字段的匹配以后缀的方式
|
||||
*/
|
||||
private static final Map<String, CodegenColumnListConditionEnum> columnListOperationConditionMappings =
|
||||
MapUtil.<String, CodegenColumnListConditionEnum>builder()
|
||||
.put("name", CodegenColumnListConditionEnum.LIKE)
|
||||
.put("time", CodegenColumnListConditionEnum.BETWEEN)
|
||||
.put("date", CodegenColumnListConditionEnum.BETWEEN)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* 字段名与 {@link CodegenColumnHtmlTypeEnum} 的默认映射
|
||||
* 注意,字段的匹配以后缀的方式
|
||||
*/
|
||||
private static final Map<String, CodegenColumnHtmlTypeEnum> columnHtmlTypeMappings =
|
||||
MapUtil.<String, CodegenColumnHtmlTypeEnum>builder()
|
||||
.put("status", CodegenColumnHtmlTypeEnum.RADIO)
|
||||
.put("sex", CodegenColumnHtmlTypeEnum.RADIO)
|
||||
.put("type", CodegenColumnHtmlTypeEnum.SELECT)
|
||||
.put("image", CodegenColumnHtmlTypeEnum.UPLOAD_IMAGE)
|
||||
.put("file", CodegenColumnHtmlTypeEnum.UPLOAD_FILE)
|
||||
.put("content", CodegenColumnHtmlTypeEnum.EDITOR)
|
||||
.put("time", CodegenColumnHtmlTypeEnum.DATETIME)
|
||||
.put("date", CodegenColumnHtmlTypeEnum.DATETIME)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* 多租户编号的字段名
|
||||
*/
|
||||
public static final String TENANT_ID_FIELD = "tenant_id";
|
||||
/**
|
||||
* {@link cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO} 的字段
|
||||
*/
|
||||
public static final Set<String> BASE_DO_FIELDS = new HashSet<>();
|
||||
/**
|
||||
* 新增操作,不需要传递的字段
|
||||
*/
|
||||
private static final Set<String> CREATE_OPERATION_EXCLUDE_COLUMN = Sets.newHashSet("id");
|
||||
/**
|
||||
* 修改操作,不需要传递的字段
|
||||
*/
|
||||
private static final Set<String> UPDATE_OPERATION_EXCLUDE_COLUMN = Sets.newHashSet();
|
||||
/**
|
||||
* 列表操作的条件,不需要传递的字段
|
||||
*/
|
||||
private static final Set<String> LIST_OPERATION_EXCLUDE_COLUMN = Sets.newHashSet("id");
|
||||
/**
|
||||
* 列表操作的结果,不需要返回的字段
|
||||
*/
|
||||
private static final Set<String> LIST_OPERATION_RESULT_EXCLUDE_COLUMN = Sets.newHashSet();
|
||||
|
||||
/**
|
||||
* Java 类型与 MySQL 类型的映射关系
|
||||
*/
|
||||
private static final Map<String, Set<String>> javaTypeMappings = MapUtil.<String, Set<String>>builder()
|
||||
.put(Boolean.class.getSimpleName(), Sets.newHashSet("bit"))
|
||||
.put(Integer.class.getSimpleName(), Sets.newHashSet("tinyint", "smallint", "mediumint", "int"))
|
||||
.put(Long.class.getSimpleName(), Collections.singleton("bigint"))
|
||||
.put(Double.class.getSimpleName(), Sets.newHashSet("float", "double"))
|
||||
.put(BigDecimal.class.getSimpleName(), Sets.newHashSet("decimal", "numeric"))
|
||||
.put(String.class.getSimpleName(), Sets.newHashSet("tinytext", "text", "mediumtext", "longtext", // 长文本
|
||||
"char", "varchar", "nvarchar", "varchar2")) // 短文本
|
||||
.put(Date.class.getSimpleName(), Sets.newHashSet("datetime", "time", "date", "timestamp"))
|
||||
.put("byte[]", Sets.newHashSet("blob"))
|
||||
.build();
|
||||
|
||||
static {
|
||||
Arrays.stream(ReflectUtil.getFields(BaseDO.class)).forEach(field -> BASE_DO_FIELDS.add(field.getName()));
|
||||
BASE_DO_FIELDS.add(TENANT_ID_FIELD);
|
||||
// 处理 OPERATION 相关的字段
|
||||
CREATE_OPERATION_EXCLUDE_COLUMN.addAll(BASE_DO_FIELDS);
|
||||
UPDATE_OPERATION_EXCLUDE_COLUMN.addAll(BASE_DO_FIELDS);
|
||||
LIST_OPERATION_EXCLUDE_COLUMN.addAll(BASE_DO_FIELDS);
|
||||
LIST_OPERATION_EXCLUDE_COLUMN.remove("createTime"); // 创建时间,还是可能需要传递的
|
||||
LIST_OPERATION_RESULT_EXCLUDE_COLUMN.addAll(BASE_DO_FIELDS);
|
||||
LIST_OPERATION_RESULT_EXCLUDE_COLUMN.remove("createTime"); // 创建时间,还是需要返回的
|
||||
}
|
||||
|
||||
public CodegenTableDO buildTable(SchemaTableDO schemaTable) {
|
||||
CodegenTableDO table = CodegenConvert.INSTANCE.convert(schemaTable);
|
||||
initTableDefault(table);
|
||||
return table;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化 Table 表的默认字段
|
||||
*
|
||||
* @param table 表定义
|
||||
*/
|
||||
private void initTableDefault(CodegenTableDO table) {
|
||||
// 以 system_dept 举例子。moduleName 为 system、businessName 为 dept、className 为 SystemDept
|
||||
// 如果不希望 System 前缀,则可以手动在【代码生成 - 修改生成配置 - 基本信息】,将实体类名称改为 Dept 即可
|
||||
table.setModuleName(StrUtil.subBefore(table.getTableName(),
|
||||
'_', false)); // 第一个 _ 前缀的前面,作为 module 名字
|
||||
table.setBusinessName(toCamelCase(subAfter(table.getTableName(),
|
||||
'_', false))); // 第一步,第一个 _ 前缀的后面,作为 module 名字; 第二步,可能存在多个 _ 的情况,转换成驼峰
|
||||
table.setClassName(upperFirst(toCamelCase(table.getTableName()))); // 驼峰 + 首字母大写
|
||||
table.setClassComment(subBefore(table.getTableComment(), // 去除结尾的表,作为类描述
|
||||
'表', true));
|
||||
table.setAuthor("芋艿"); // TODO 稍后改成创建人
|
||||
table.setTemplateType(CodegenTemplateTypeEnum.CRUD.getType());
|
||||
}
|
||||
|
||||
public List<CodegenColumnDO> buildColumns(List<SchemaColumnDO> schemaColumns) {
|
||||
List<CodegenColumnDO> columns = CodegenConvert.INSTANCE.convertList(schemaColumns);
|
||||
columns.forEach(this::initColumnDefault);
|
||||
return columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化 Column 列的默认字段
|
||||
*
|
||||
* @param column 列定义
|
||||
*/
|
||||
private void initColumnDefault(CodegenColumnDO column) {
|
||||
// 处理 Java 相关的字段的默认值
|
||||
processColumnJava(column);
|
||||
// 处理 CRUD 相关的字段的默认值
|
||||
processColumnOperation(column);
|
||||
// 处理 UI 相关的字段的默认值
|
||||
processColumnUI(column);
|
||||
}
|
||||
|
||||
private void processColumnJava(CodegenColumnDO column) {
|
||||
// 处理 javaField 字段
|
||||
column.setJavaField(toCamelCase(column.getColumnName()));
|
||||
// 处理 dictType 字段,暂无
|
||||
// 处理 javaType 字段
|
||||
String dbType = StrUtil.subBefore(column.getColumnType(), '(', false);
|
||||
javaTypeMappings.entrySet().stream()
|
||||
.filter(entry -> entry.getValue().contains(dbType))
|
||||
.findFirst().ifPresent(entry -> column.setJavaType(entry.getKey()));
|
||||
if (column.getJavaType() == null) {
|
||||
throw new IllegalStateException(String.format("column(%s) 的数据库类型(%s) 找不到匹配的 Java 类型",
|
||||
column.getColumnName(), column.getColumnType()));
|
||||
}
|
||||
}
|
||||
|
||||
private void processColumnOperation(CodegenColumnDO column) {
|
||||
// 处理 createOperation 字段
|
||||
column.setCreateOperation(!CREATE_OPERATION_EXCLUDE_COLUMN.contains(column.getJavaField())
|
||||
&& !column.getPrimaryKey()); // 对于主键,创建时无需传递
|
||||
// 处理 updateOperation 字段
|
||||
column.setUpdateOperation(!UPDATE_OPERATION_EXCLUDE_COLUMN.contains(column.getJavaField())
|
||||
|| column.getPrimaryKey()); // 对于主键,更新时需要传递
|
||||
// 处理 listOperation 字段
|
||||
column.setListOperation(!LIST_OPERATION_EXCLUDE_COLUMN.contains(column.getJavaField())
|
||||
&& !column.getPrimaryKey()); // 对于主键,列表过滤不需要传递
|
||||
// 处理 listOperationCondition 字段
|
||||
columnListOperationConditionMappings.entrySet().stream()
|
||||
.filter(entry -> StrUtil.endWithIgnoreCase(column.getJavaField(), entry.getKey()))
|
||||
.findFirst().ifPresent(entry -> column.setListOperationCondition(entry.getValue().getCondition()));
|
||||
if (column.getListOperationCondition() == null) {
|
||||
column.setListOperationCondition(CodegenColumnListConditionEnum.EQ.getCondition());
|
||||
}
|
||||
// 处理 listOperationResult 字段
|
||||
column.setListOperationResult(!LIST_OPERATION_RESULT_EXCLUDE_COLUMN.contains(column.getJavaField()));
|
||||
}
|
||||
|
||||
private void processColumnUI(CodegenColumnDO column) {
|
||||
// 基于后缀进行匹配
|
||||
columnHtmlTypeMappings.entrySet().stream()
|
||||
.filter(entry -> StrUtil.endWithIgnoreCase(column.getJavaField(), entry.getKey()))
|
||||
.findFirst().ifPresent(entry -> column.setHtmlType(entry.getValue().getType()));
|
||||
// 如果是 Boolean 类型时,设置为 radio 类型.
|
||||
// 其它类型,因为字段名可以相对保障,所以不进行处理。例如说 date 对应 datetime 类型.
|
||||
if (Boolean.class.getSimpleName().equals(column.getJavaType())) {
|
||||
column.setHtmlType(CodegenColumnHtmlTypeEnum.RADIO.getType());
|
||||
}
|
||||
// 兜底,设置默认为 input 类型
|
||||
if (column.getHtmlType() == null) {
|
||||
column.setHtmlType(CodegenColumnHtmlTypeEnum.INPUT.getType());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,225 @@
|
||||
package cn.iocoder.yudao.module.infra.service.codegen.inner;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.template.TemplateConfig;
|
||||
import cn.hutool.extra.template.TemplateEngine;
|
||||
import cn.hutool.extra.template.engine.velocity.VelocityEngine;
|
||||
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
|
||||
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
|
||||
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
|
||||
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
|
||||
import cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
|
||||
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenSceneEnum;
|
||||
import cn.iocoder.yudao.module.infra.framework.codegen.config.CodegenProperties;
|
||||
import com.google.common.collect.Maps;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.hutool.core.map.MapUtil.getStr;
|
||||
import static cn.hutool.core.text.CharSequenceUtil.*;
|
||||
|
||||
/**
|
||||
* 代码生成的引擎,用于具体生成代码
|
||||
* 目前基于 {@link org.apache.velocity.app.Velocity} 模板引擎实现
|
||||
*
|
||||
* 考虑到 Java 模板引擎的框架非常多,Freemarker、Velocity、Thymeleaf 等等,所以我们采用 hutool 封装的 {@link cn.hutool.extra.template.Template} 抽象
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Component
|
||||
public class CodegenEngine {
|
||||
|
||||
/**
|
||||
* 模板配置
|
||||
* key:模板在 resources 的地址
|
||||
* value:生成的路径
|
||||
*/
|
||||
private static final Map<String, String> TEMPLATES = MapUtil.<String, String>builder(new LinkedHashMap<>()) // 有序
|
||||
// Java module-impl Main
|
||||
.put(javaTemplatePath("controller/vo/baseVO"), javaModuleImplVOFilePath("BaseVO"))
|
||||
.put(javaTemplatePath("controller/vo/createReqVO"), javaModuleImplVOFilePath("CreateReqVO"))
|
||||
.put(javaTemplatePath("controller/vo/pageReqVO"), javaModuleImplVOFilePath("PageReqVO"))
|
||||
.put(javaTemplatePath("controller/vo/respVO"), javaModuleImplVOFilePath("RespVO"))
|
||||
.put(javaTemplatePath("controller/vo/updateReqVO"), javaModuleImplVOFilePath("UpdateReqVO"))
|
||||
.put(javaTemplatePath("controller/vo/exportReqVO"), javaModuleImplVOFilePath("ExportReqVO"))
|
||||
.put(javaTemplatePath("controller/vo/excelVO"), javaModuleImplVOFilePath("ExcelVO"))
|
||||
.put(javaTemplatePath("controller/controller"), javaModuleImplControllerFilePath())
|
||||
.put(javaTemplatePath("convert/convert"),
|
||||
javaModuleImplMainFilePath("convert/${table.businessName}/${table.className}Convert"))
|
||||
.put(javaTemplatePath("dal/do"),
|
||||
javaModuleImplMainFilePath("dal/dataobject/${table.businessName}/${table.className}DO"))
|
||||
.put(javaTemplatePath("dal/mapper"),
|
||||
javaModuleImplMainFilePath("dal/mysql/${table.businessName}/${table.className}Mapper"))
|
||||
.put(javaTemplatePath("service/serviceImpl"),
|
||||
javaModuleImplMainFilePath("service/${table.businessName}/${table.className}ServiceImpl"))
|
||||
.put(javaTemplatePath("service/service"),
|
||||
javaModuleImplMainFilePath("service/${table.businessName}/${table.className}Service"))
|
||||
// Java module-impl Test
|
||||
.put(javaTemplatePath("test/serviceTest"),
|
||||
javaModuleImplTestFilePath("service/${table.businessName}/${table.className}ServiceImplTest"))
|
||||
// Java module-api Main
|
||||
.put(javaTemplatePath("enums/errorcode"), javaModuleApiMainFilePath("enums/ErrorCodeConstants_手动操作"))
|
||||
// Vue
|
||||
.put(vueTemplatePath("views/index.vue"),
|
||||
vueFilePath("views/${table.moduleName}/${classNameVar}/index.vue"))
|
||||
.put(vueTemplatePath("api/api.js"),
|
||||
vueFilePath("api/${table.moduleName}/${classNameVar}.js"))
|
||||
// SQL
|
||||
.put("codegen/sql/sql.vm", "sql/sql.sql")
|
||||
.put("codegen/sql/h2.vm", "sql/h2.sql")
|
||||
.build();
|
||||
|
||||
@Resource
|
||||
private CodegenProperties codegenProperties;
|
||||
|
||||
/**
|
||||
* 模板引擎,由 hutool 实现
|
||||
*/
|
||||
private final TemplateEngine templateEngine;
|
||||
/**
|
||||
* 全局通用变量映射
|
||||
*/
|
||||
private final Map<String, Object> globalBindingMap = new HashMap<>();
|
||||
|
||||
public CodegenEngine() {
|
||||
// 初始化 TemplateEngine 属性
|
||||
TemplateConfig config = new TemplateConfig();
|
||||
config.setResourceMode(TemplateConfig.ResourceMode.CLASSPATH);
|
||||
this.templateEngine = new VelocityEngine(config);
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
private void initGlobalBindingMap() {
|
||||
// 全局配置
|
||||
globalBindingMap.put("basePackage", codegenProperties.getBasePackage());
|
||||
globalBindingMap.put("baseFrameworkPackage", codegenProperties.getBasePackage()
|
||||
+ '.' + "framework"); // 用于后续获取测试类的 package 地址
|
||||
// 全局 Java Bean
|
||||
globalBindingMap.put("CommonResultClassName", CommonResult.class.getName());
|
||||
globalBindingMap.put("PageResultClassName", PageResult.class.getName());
|
||||
// VO 类,独有字段
|
||||
globalBindingMap.put("PageParamClassName", PageParam.class.getName());
|
||||
globalBindingMap.put("DictFormatClassName", DictFormat.class.getName());
|
||||
// DO 类,独有字段
|
||||
globalBindingMap.put("BaseDOClassName", BaseDO.class.getName());
|
||||
globalBindingMap.put("baseDOFields", CodegenBuilder.BASE_DO_FIELDS);
|
||||
globalBindingMap.put("QueryWrapperClassName", LambdaQueryWrapperX.class.getName());
|
||||
globalBindingMap.put("BaseMapperClassName", BaseMapperX.class.getName());
|
||||
// Util 工具类
|
||||
globalBindingMap.put("ServiceExceptionUtilClassName", ServiceExceptionUtil.class.getName());
|
||||
globalBindingMap.put("DateUtilsClassName", DateUtils.class.getName());
|
||||
globalBindingMap.put("ExcelUtilsClassName", ExcelUtils.class.getName());
|
||||
globalBindingMap.put("ObjectUtilsClassName", ObjectUtils.class.getName());
|
||||
globalBindingMap.put("DictConvertClassName", DictConvert.class.getName());
|
||||
globalBindingMap.put("OperateLogClassName", OperateLog.class.getName());
|
||||
globalBindingMap.put("OperateTypeEnumClassName", OperateTypeEnum.class.getName());
|
||||
}
|
||||
|
||||
public Map<String, String> execute(CodegenTableDO table, List<CodegenColumnDO> columns) {
|
||||
// 创建 bindingMap
|
||||
Map<String, Object> bindingMap = new HashMap<>(globalBindingMap);
|
||||
bindingMap.put("table", table);
|
||||
bindingMap.put("columns", columns);
|
||||
bindingMap.put("primaryColumn", CollectionUtils.findFirst(columns, CodegenColumnDO::getPrimaryKey)); // 主键字段
|
||||
bindingMap.put("sceneEnum", CodegenSceneEnum.valueOf(table.getScene()));
|
||||
|
||||
// className 相关
|
||||
// 去掉指定前缀,将 TestDictType 转换成 DictType. 因为在 create 等方法后,不需要带上 Test 前缀
|
||||
String simpleClassName = removePrefix(table.getClassName(), upperFirst(table.getModuleName()));
|
||||
bindingMap.put("simpleClassName", simpleClassName);
|
||||
bindingMap.put("simpleClassName_underlineCase", toUnderlineCase(simpleClassName)); // 将 DictType 转换成 dict_type
|
||||
bindingMap.put("classNameVar", lowerFirst(simpleClassName)); // 将 DictType 转换成 dictType,用于变量
|
||||
// 将 DictType 转换成 dict-type
|
||||
String simpleClassNameStrikeCase = toSymbolCase(simpleClassName, '-');
|
||||
bindingMap.put("simpleClassName_strikeCase", simpleClassNameStrikeCase);
|
||||
// permission 前缀
|
||||
bindingMap.put("permissionPrefix", table.getModuleName() + ":" + simpleClassNameStrikeCase);
|
||||
|
||||
// 执行生成
|
||||
final Map<String, String> result = Maps.newLinkedHashMapWithExpectedSize(TEMPLATES.size()); // 有序
|
||||
TEMPLATES.forEach((vmPath, filePath) -> {
|
||||
filePath = formatFilePath(filePath, bindingMap);
|
||||
String content = templateEngine.getTemplate(vmPath).render(bindingMap);
|
||||
result.put(filePath, content);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
private String formatFilePath(String filePath, Map<String, Object> bindingMap) {
|
||||
filePath = StrUtil.replace(filePath, "${basePackage}",
|
||||
getStr(bindingMap, "basePackage").replaceAll("\\.", "/"));
|
||||
filePath = StrUtil.replace(filePath, "${classNameVar}",
|
||||
getStr(bindingMap, "classNameVar"));
|
||||
// sceneEnum 包含的字段
|
||||
CodegenSceneEnum sceneEnum = (CodegenSceneEnum) bindingMap.get("sceneEnum");
|
||||
filePath = StrUtil.replace(filePath, "${sceneEnum.prefixClass}", sceneEnum.getPrefixClass());
|
||||
filePath = StrUtil.replace(filePath, "${sceneEnum.basePackage}", sceneEnum.getBasePackage());
|
||||
// table 包含的字段
|
||||
CodegenTableDO table = (CodegenTableDO) bindingMap.get("table");
|
||||
filePath = StrUtil.replace(filePath, "${table.moduleName}", table.getModuleName());
|
||||
filePath = StrUtil.replace(filePath, "${table.businessName}", table.getBusinessName());
|
||||
filePath = StrUtil.replace(filePath, "${table.className}", table.getClassName());
|
||||
return filePath;
|
||||
}
|
||||
|
||||
private static String javaTemplatePath(String path) {
|
||||
return "codegen/java/" + path + ".vm";
|
||||
}
|
||||
|
||||
private static String javaModuleImplVOFilePath(String path) {
|
||||
return javaModuleFilePath("controller/${sceneEnum.basePackage}/${table.businessName}/" +
|
||||
"vo/${sceneEnum.prefixClass}${table.className}" + path, "impl", "main");
|
||||
}
|
||||
|
||||
private static String javaModuleImplControllerFilePath() {
|
||||
return javaModuleFilePath("controller/${sceneEnum.basePackage}/${table.businessName}/" +
|
||||
"${sceneEnum.prefixClass}${table.className}Controller", "impl", "main");
|
||||
}
|
||||
|
||||
private static String javaModuleImplMainFilePath(String path) {
|
||||
return javaModuleFilePath(path, "impl", "main");
|
||||
}
|
||||
|
||||
private static String javaModuleApiMainFilePath(String path) {
|
||||
return javaModuleFilePath(path, "api", "main");
|
||||
}
|
||||
|
||||
private static String javaModuleImplTestFilePath(String path) {
|
||||
return javaModuleFilePath(path, "impl", "test");
|
||||
}
|
||||
|
||||
private static String javaModuleFilePath(String path, String module, String src) {
|
||||
return "yudao-module-${table.moduleName}/" + // 顶级模块
|
||||
"yudao-module-${table.moduleName}-" + module + "/" + // 子模块
|
||||
"src/" + src + "/java/${basePackage}/module/${table.moduleName}/" + path + ".java";
|
||||
}
|
||||
|
||||
private static String vueTemplatePath(String path) {
|
||||
return "codegen/vue/" + path + ".vm";
|
||||
}
|
||||
|
||||
private static String vueFilePath(String path) {
|
||||
return "yudao-ui-${sceneEnum.basePackage}/" + // 顶级目录
|
||||
"src/" + path;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,117 @@
|
||||
package cn.iocoder.yudao.module.infra.service.codegen.inner;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.SchemaColumnDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.SchemaTableDO;
|
||||
import com.alibaba.druid.DbType;
|
||||
import com.alibaba.druid.sql.ast.expr.SQLCharExpr;
|
||||
import com.alibaba.druid.sql.ast.statement.SQLColumnDefinition;
|
||||
import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement;
|
||||
import com.alibaba.druid.sql.ast.statement.SQLPrimaryKey;
|
||||
import com.alibaba.druid.sql.ast.statement.SQLTableElement;
|
||||
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement;
|
||||
import com.alibaba.druid.sql.repository.SchemaRepository;
|
||||
import org.apache.commons.collections4.KeyValue;
|
||||
import org.apache.commons.collections4.keyvalue.DefaultKeyValue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static com.alibaba.druid.sql.SQLUtils.normalize;
|
||||
|
||||
/**
|
||||
* SQL 解析器,将创建表的 SQL,解析成 {@link SchemaTableDO} 和 {@link SchemaColumnDO} 对象,
|
||||
* 后续可以基于它们,生成代码~
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class CodegenSQLParser {
|
||||
|
||||
/**
|
||||
* 解析建表 SQL 语句,返回 {@link SchemaTableDO} 和 {@link SchemaColumnDO} 对象
|
||||
*
|
||||
* @param sql 建表 SQL 语句
|
||||
* @return 解析结果
|
||||
*/
|
||||
public static KeyValue<SchemaTableDO, List<SchemaColumnDO>> parse(String sql) {
|
||||
// 解析 SQL 成 Statement
|
||||
SQLCreateTableStatement statement = parseCreateSQL(sql);
|
||||
// 解析 Table 表
|
||||
SchemaTableDO table = parseTable(statement);
|
||||
// 解析 Column 字段
|
||||
List<SchemaColumnDO> columns = parseColumns(statement);
|
||||
columns.forEach(column -> column.setTableName(table.getTableName()));
|
||||
// 返回
|
||||
return new DefaultKeyValue<>(table, columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用 Druid 工具,建表 SQL 语句
|
||||
*
|
||||
* @param sql 建表 SQL 语句
|
||||
* @return 创建 Statement
|
||||
*/
|
||||
private static SQLCreateTableStatement parseCreateSQL(String sql) {
|
||||
// 解析 SQL
|
||||
SchemaRepository repository = new SchemaRepository(DbType.mysql);
|
||||
repository.console(sql);
|
||||
// 获得该表对应的 MySqlCreateTableStatement 对象
|
||||
String tableName = CollUtil.getFirst(repository.getDefaultSchema().getObjects()).getName();
|
||||
return (MySqlCreateTableStatement) repository.findTable(tableName).getStatement();
|
||||
}
|
||||
|
||||
private static SchemaTableDO parseTable(SQLCreateTableStatement statement) {
|
||||
return SchemaTableDO.builder()
|
||||
.tableName(statement.getTableSource().getTableName(true))
|
||||
.tableComment(getCommentText(statement))
|
||||
.build();
|
||||
}
|
||||
|
||||
private static String getCommentText(SQLCreateTableStatement statement) {
|
||||
if (statement == null || statement.getComment() == null) {
|
||||
return "";
|
||||
}
|
||||
return ((SQLCharExpr) statement.getComment()).getText();
|
||||
}
|
||||
|
||||
private static List<SchemaColumnDO> parseColumns(SQLCreateTableStatement statement) {
|
||||
List<SchemaColumnDO> columns = new ArrayList<>();
|
||||
statement.getTableElementList().forEach(element -> parseColumn(columns, element));
|
||||
return columns;
|
||||
}
|
||||
|
||||
private static void parseColumn(List<SchemaColumnDO> columns, SQLTableElement element) {
|
||||
// 处理主键
|
||||
if (element instanceof SQLPrimaryKey) {
|
||||
parsePrimaryKey(columns, (SQLPrimaryKey) element);
|
||||
return;
|
||||
}
|
||||
// 处理字段定义
|
||||
if (element instanceof SQLColumnDefinition) {
|
||||
parseColumnDefinition(columns, (SQLColumnDefinition) element);
|
||||
}
|
||||
}
|
||||
|
||||
private static void parsePrimaryKey(List<SchemaColumnDO> columns, SQLPrimaryKey primaryKey) {
|
||||
String columnName = normalize(primaryKey.getColumns().get(0).toString()); // 暂时不考虑联合主键
|
||||
// 匹配 columns 主键字段,设置为 primary
|
||||
columns.stream().filter(column -> column.getColumnName().equals(columnName))
|
||||
.forEach(column -> column.setPrimaryKey(true));
|
||||
}
|
||||
|
||||
private static void parseColumnDefinition(List<SchemaColumnDO> columns, SQLColumnDefinition definition) {
|
||||
String text = definition.toString().toUpperCase();
|
||||
columns.add(SchemaColumnDO.builder()
|
||||
.columnName(normalize(definition.getColumnName()))
|
||||
.columnType(definition.getDataType().toString())
|
||||
.columnComment(Objects.isNull(definition.getComment()) ? ""
|
||||
: normalize(definition.getComment().toString()))
|
||||
.nullable(!text.contains(" NOT NULL"))
|
||||
.primaryKey(false)
|
||||
.autoIncrement(text.contains("AUTO_INCREMENT"))
|
||||
.ordinalPosition(columns.size() + 1)
|
||||
.build());
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,75 @@
|
||||
package cn.iocoder.yudao.module.infra.service.test;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.test.vo.TestDemoCreateReqVO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.test.vo.TestDemoExportReqVO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.test.vo.TestDemoPageReqVO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.test.vo.TestDemoUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.test.TestDemoDO;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 字典类型 Service 接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface TestDemoService {
|
||||
|
||||
/**
|
||||
* 创建字典类型
|
||||
*
|
||||
* @param createReqVO 创建信息
|
||||
* @return 编号
|
||||
*/
|
||||
Long createTestDemo(@Valid TestDemoCreateReqVO createReqVO);
|
||||
|
||||
/**
|
||||
* 更新字典类型
|
||||
*
|
||||
* @param updateReqVO 更新信息
|
||||
*/
|
||||
void updateTestDemo(@Valid TestDemoUpdateReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 删除字典类型
|
||||
*
|
||||
* @param id 编号
|
||||
*/
|
||||
void deleteTestDemo(Long id);
|
||||
|
||||
/**
|
||||
* 获得字典类型
|
||||
*
|
||||
* @param id 编号
|
||||
* @return 字典类型
|
||||
*/
|
||||
TestDemoDO getTestDemo(Long id);
|
||||
|
||||
/**
|
||||
* 获得字典类型列表
|
||||
*
|
||||
* @param ids 编号
|
||||
* @return 字典类型列表
|
||||
*/
|
||||
List<TestDemoDO> getTestDemoList(Collection<Long> ids);
|
||||
|
||||
/**
|
||||
* 获得字典类型分页
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @return 字典类型分页
|
||||
*/
|
||||
PageResult<TestDemoDO> getTestDemoPage(TestDemoPageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 获得字典类型列表, 用于 Excel 导出
|
||||
*
|
||||
* @param exportReqVO 查询条件
|
||||
* @return 字典类型列表
|
||||
*/
|
||||
List<TestDemoDO> getTestDemoList(TestDemoExportReqVO exportReqVO);
|
||||
|
||||
}
|
@@ -0,0 +1,85 @@
|
||||
package cn.iocoder.yudao.module.infra.service.test;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.test.vo.TestDemoCreateReqVO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.test.vo.TestDemoExportReqVO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.test.vo.TestDemoPageReqVO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.test.vo.TestDemoUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.infra.convert.test.TestDemoConvert;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.test.TestDemoDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.mysql.test.TestDemoMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.TEST_DEMO_NOT_EXISTS;
|
||||
|
||||
/**
|
||||
* 字典类型 Service 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class TestDemoServiceImpl implements TestDemoService {
|
||||
|
||||
@Resource
|
||||
private TestDemoMapper testDemoMapper;
|
||||
|
||||
@Override
|
||||
public Long createTestDemo(TestDemoCreateReqVO createReqVO) {
|
||||
// 插入
|
||||
TestDemoDO testDemo = TestDemoConvert.INSTANCE.convert(createReqVO);
|
||||
testDemoMapper.insert(testDemo);
|
||||
// 返回
|
||||
return testDemo.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTestDemo(TestDemoUpdateReqVO updateReqVO) {
|
||||
// 校验存在
|
||||
this.validateTestDemoExists(updateReqVO.getId());
|
||||
// 更新
|
||||
TestDemoDO updateObj = TestDemoConvert.INSTANCE.convert(updateReqVO);
|
||||
testDemoMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteTestDemo(Long id) {
|
||||
// 校验存在
|
||||
this.validateTestDemoExists(id);
|
||||
// 删除
|
||||
testDemoMapper.deleteById(id);
|
||||
}
|
||||
|
||||
private void validateTestDemoExists(Long id) {
|
||||
if (testDemoMapper.selectById(id) == null) {
|
||||
throw exception(TEST_DEMO_NOT_EXISTS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TestDemoDO getTestDemo(Long id) {
|
||||
return testDemoMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TestDemoDO> getTestDemoList(Collection<Long> ids) {
|
||||
return testDemoMapper.selectBatchIds(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<TestDemoDO> getTestDemoPage(TestDemoPageReqVO pageReqVO) {
|
||||
return testDemoMapper.selectPage(pageReqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TestDemoDO> getTestDemoList(TestDemoExportReqVO exportReqVO) {
|
||||
return testDemoMapper.selectList(exportReqVO);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,109 @@
|
||||
package ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName};
|
||||
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import javax.annotation.Resource;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
#if ($sceneEnum.scene == 1)import org.springframework.security.access.prepost.PreAuthorize;#end
|
||||
|
||||
import io.swagger.annotations.*;
|
||||
|
||||
import javax.validation.constraints.*;
|
||||
import javax.validation.*;
|
||||
import javax.servlet.http.*;
|
||||
import java.util.*;
|
||||
import java.io.IOException;
|
||||
|
||||
import ${PageResultClassName};
|
||||
import ${CommonResultClassName};
|
||||
import static ${CommonResultClassName}.success;
|
||||
|
||||
import ${ExcelUtilsClassName};
|
||||
|
||||
import ${OperateLogClassName};
|
||||
import static ${OperateTypeEnumClassName}.*;
|
||||
|
||||
import ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo.*;
|
||||
import ${basePackage}.module.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO;
|
||||
import ${basePackage}.module.${table.moduleName}.convert.${table.businessName}.${table.className}Convert;
|
||||
import ${basePackage}.module.${table.moduleName}.service.${table.businessName}.${table.className}Service;
|
||||
|
||||
@Api(tags = "${sceneEnum.name} - ${table.classComment}")
|
||||
@RestController
|
||||
##二级的 businessName 暂时不算在 HTTP 路径上,可以根据需要写
|
||||
@RequestMapping("/${table.moduleName}/${simpleClassName_strikeCase}")
|
||||
@Validated
|
||||
public class ${sceneEnum.prefixClass}${table.className}Controller {
|
||||
|
||||
@Resource
|
||||
private ${table.className}Service ${classNameVar}Service;
|
||||
|
||||
@PostMapping("/create")
|
||||
@ApiOperation("创建${table.classComment}")
|
||||
#if ($sceneEnum.scene == 1) @PreAuthorize("@ss.hasPermission('${permissionPrefix}:create')")#end
|
||||
|
||||
public CommonResult<${primaryColumn.javaType}> create${simpleClassName}(@Valid @RequestBody ${sceneEnum.prefixClass}${table.className}CreateReqVO createReqVO) {
|
||||
return success(${classNameVar}Service.create${simpleClassName}(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@ApiOperation("更新${table.classComment}")
|
||||
#if ($sceneEnum.scene == 1) @PreAuthorize("@ss.hasPermission('${permissionPrefix}:update')")#end
|
||||
|
||||
public CommonResult<Boolean> update${simpleClassName}(@Valid @RequestBody ${sceneEnum.prefixClass}${table.className}UpdateReqVO updateReqVO) {
|
||||
${classNameVar}Service.update${simpleClassName}(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@ApiOperation("删除${table.classComment}")
|
||||
@ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = ${primaryColumn.javaType}.class)
|
||||
#if ($sceneEnum.scene == 1) @PreAuthorize("@ss.hasPermission('${permissionPrefix}:delete')")#end
|
||||
|
||||
public CommonResult<Boolean> delete${simpleClassName}(@RequestParam("id") ${primaryColumn.javaType} id) {
|
||||
${classNameVar}Service.delete${simpleClassName}(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@ApiOperation("获得${table.classComment}")
|
||||
@ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = ${primaryColumn.javaType}.class)
|
||||
#if ($sceneEnum.scene == 1) @PreAuthorize("@ss.hasPermission('${permissionPrefix}:query')")#end
|
||||
|
||||
public CommonResult<${sceneEnum.prefixClass}${table.className}RespVO> get${simpleClassName}(@RequestParam("id") ${primaryColumn.javaType} id) {
|
||||
${table.className}DO ${classNameVar} = ${classNameVar}Service.get${simpleClassName}(id);
|
||||
return success(${table.className}Convert.INSTANCE.convert(${classNameVar}));
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
@ApiOperation("获得${table.classComment}列表")
|
||||
@ApiImplicitParam(name = "ids", value = "编号列表", required = true, example = "1024,2048", dataTypeClass = List.class)
|
||||
#if ($sceneEnum.scene == 1) @PreAuthorize("@ss.hasPermission('${permissionPrefix}:query')")#end
|
||||
|
||||
public CommonResult<List<${sceneEnum.prefixClass}${table.className}RespVO>> get${simpleClassName}List(@RequestParam("ids") Collection<${primaryColumn.javaType}> ids) {
|
||||
List<${table.className}DO> list = ${classNameVar}Service.get${simpleClassName}List(ids);
|
||||
return success(${table.className}Convert.INSTANCE.convertList(list));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@ApiOperation("获得${table.classComment}分页")
|
||||
#if ($sceneEnum.scene == 1) @PreAuthorize("@ss.hasPermission('${permissionPrefix}:query')")#end
|
||||
|
||||
public CommonResult<PageResult<${sceneEnum.prefixClass}${table.className}RespVO>> get${simpleClassName}Page(@Valid ${sceneEnum.prefixClass}${table.className}PageReqVO pageVO) {
|
||||
PageResult<${table.className}DO> pageResult = ${classNameVar}Service.get${simpleClassName}Page(pageVO);
|
||||
return success(${table.className}Convert.INSTANCE.convertPage(pageResult));
|
||||
}
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@ApiOperation("导出${table.classComment} Excel")
|
||||
#if ($sceneEnum.scene == 1) @PreAuthorize("@ss.hasPermission('${permissionPrefix}:export')")#end
|
||||
|
||||
@OperateLog(type = EXPORT)
|
||||
public void export${simpleClassName}Excel(@Valid ${sceneEnum.prefixClass}${table.className}ExportReqVO exportReqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
List<${table.className}DO> list = ${classNameVar}Service.get${simpleClassName}List(exportReqVO);
|
||||
// 导出 Excel
|
||||
List<${sceneEnum.prefixClass}${table.className}ExcelVO> datas = ${table.className}Convert.INSTANCE.convertList02(list);
|
||||
ExcelUtils.write(response, "${table.classComment}.xls", "数据", ${sceneEnum.prefixClass}${table.className}ExcelVO.class, datas);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
## 提供给 baseVO、createVO、updateVO 生成字段
|
||||
@ApiModelProperty(value = "${column.columnComment}"#if (!${column.nullable}), required = true#end#if ("$!column.example" != ""), example = "${column.example}"#end)
|
||||
#if (!${column.nullable})## 判断 @NotEmpty 和 @NotNull 注解
|
||||
#if (${field.fieldType} == 'String')
|
||||
@NotEmpty(message = "${column.columnComment}不能为空")
|
||||
#else
|
||||
@NotNull(message = "${column.columnComment}不能为空")
|
||||
#end
|
||||
#end
|
||||
#if (${column.javaType} == "Date")## 时间类型
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
#end
|
||||
private ${column.javaType} ${column.javaField};
|
@@ -0,0 +1,31 @@
|
||||
package ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import io.swagger.annotations.*;
|
||||
import javax.validation.constraints.*;
|
||||
## 处理 Date 字段的引入
|
||||
#foreach ($column in $columns)
|
||||
#if (${column.createOperation} && ${column.updateOperation} && ${column.listOperationResult}
|
||||
&& ${column.javaType} == "Date")## 时间类型
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import static ${DateUtilsClassName}.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
#break
|
||||
#end
|
||||
#end
|
||||
|
||||
/**
|
||||
* ${table.classComment} Base VO,提供给添加、修改、详细的子 VO 使用
|
||||
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
|
||||
*/
|
||||
@Data
|
||||
public class ${sceneEnum.prefixClass}${table.className}BaseVO {
|
||||
|
||||
#foreach ($column in $columns)
|
||||
#if (${column.createOperation} && ${column.updateOperation} && ${column.listOperationResult})##通用操作
|
||||
#parse("codegen/java/controller/vo/_column.vm")
|
||||
|
||||
#end
|
||||
#end
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
package ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import io.swagger.annotations.*;
|
||||
import javax.validation.constraints.*;
|
||||
## 处理 Date 字段的引入
|
||||
#foreach ($column in $columns)
|
||||
#if (${column.createOperation} && (!${column.updateOperation} || !${column.listOperationResult})
|
||||
&& ${column.javaType} == "Date")## 时间类型
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import static ${DateUtilsClassName}.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
#break
|
||||
#end
|
||||
#end
|
||||
|
||||
@ApiModel("${sceneEnum.name} - ${table.classComment}创建 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class ${sceneEnum.prefixClass}${table.className}CreateReqVO extends ${sceneEnum.prefixClass}${table.className}BaseVO {
|
||||
|
||||
#foreach ($column in $columns)
|
||||
#if (${column.createOperation} && (!${column.updateOperation} || !${column.listOperationResult}))##不是通用字段
|
||||
#parse("codegen/java/controller/vo/_column.vm")
|
||||
|
||||
#end
|
||||
#end
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
package ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import io.swagger.annotations.*;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
#foreach ($column in $columns)
|
||||
#if ("$!column.dictType" != "")## 有设置数据字典
|
||||
import ${DictFormatClassName};
|
||||
import ${DictConvertClassName};
|
||||
|
||||
#break
|
||||
#end
|
||||
#end
|
||||
|
||||
/**
|
||||
* ${table.classComment} Excel VO
|
||||
*
|
||||
* @author ${table.author}
|
||||
*/
|
||||
@Data
|
||||
public class ${sceneEnum.prefixClass}${table.className}ExcelVO {
|
||||
|
||||
#foreach ($column in $columns)
|
||||
#if (${column.listOperationResult})##返回字段
|
||||
#if ("$!column.dictType" != "")##处理枚举值
|
||||
@ExcelProperty(value = "${column.columnComment}", converter = DictConvert.class)
|
||||
@DictFormat("${column.dictType}") // TODO 代码优化:建议设置到对应的 XXXDictTypeConstants 枚举类中
|
||||
#else
|
||||
@ExcelProperty("${column.columnComment}")
|
||||
#end
|
||||
private ${column.javaType} ${column.javaField};
|
||||
|
||||
#end
|
||||
#end
|
||||
}
|
@@ -0,0 +1,42 @@
|
||||
package ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import io.swagger.annotations.*;
|
||||
import ${PageParamClassName};
|
||||
## 处理 Date 字段的引入
|
||||
#foreach ($column in $columns)
|
||||
#if (${column.listOperation} && ${column.javaType} == "Date")## 时间类型
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import static ${DateUtilsClassName}.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
#break
|
||||
#end
|
||||
#end
|
||||
## 字段模板
|
||||
#macro(columnTpl $prefix $prefixStr)
|
||||
#if (${column.javaType} == "Date")## 时间类型
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
#end
|
||||
@ApiModelProperty(value = "${prefixStr}${column.columnComment}"#if ("$!column.example" != ""), example = "${column.example}"#end)
|
||||
private ${column.javaType}#if ("$!prefix" != "") ${prefix}${JavaField}#else ${column.javaField}#end;
|
||||
#end
|
||||
|
||||
@ApiModel(value = "${sceneEnum.name} - ${table.classComment} Excel 导出 Request VO", description = "参数和 ${table.className}PageReqVO 是一致的")
|
||||
@Data
|
||||
public class ${sceneEnum.prefixClass}${table.className}ExportReqVO {
|
||||
|
||||
#foreach ($column in $columns)
|
||||
#set ($JavaField = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})##首字母大写
|
||||
#if (${column.listOperation})##查询操作
|
||||
#if (${column.listOperationCondition} == "BETWEEN")## 情况一,Between 的时候
|
||||
#columnTpl('begin', '开始')
|
||||
|
||||
#columnTpl('end', '结束')
|
||||
#else##情况二,非 Between 的时间
|
||||
#columnTpl('', '')
|
||||
#end
|
||||
|
||||
#end
|
||||
#end
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
package ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import io.swagger.annotations.*;
|
||||
import ${PageParamClassName};
|
||||
## 处理 Date 字段的引入
|
||||
#foreach ($column in $columns)
|
||||
#if (${column.listOperation} && ${column.javaType} == "Date")## 时间类型
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import static ${DateUtilsClassName}.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
#break
|
||||
#end
|
||||
#end
|
||||
## 字段模板
|
||||
#macro(columnTpl $prefix $prefixStr)
|
||||
#if (${column.javaType} == "Date")## 时间类型
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
#end
|
||||
@ApiModelProperty(value = "${prefixStr}${column.columnComment}"#if ("$!column.example" != ""), example = "${column.example}"#end)
|
||||
private ${column.javaType}#if ("$!prefix" != "") ${prefix}${JavaField}#else ${column.javaField}#end;
|
||||
#end
|
||||
|
||||
@ApiModel("${sceneEnum.name} - ${table.classComment}分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class ${sceneEnum.prefixClass}${table.className}PageReqVO extends PageParam {
|
||||
|
||||
#foreach ($column in $columns)
|
||||
#set ($JavaField = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})##首字母大写
|
||||
#if (${column.listOperation})##查询操作
|
||||
#if (${column.listOperationCondition} == "BETWEEN")## 情况一,Between 的时候
|
||||
#columnTpl('begin', '开始')
|
||||
|
||||
#columnTpl('end', '结束')
|
||||
#else##情况二,非 Between 的时间
|
||||
#columnTpl('', '')
|
||||
#end
|
||||
|
||||
#end
|
||||
#end
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
package ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import io.swagger.annotations.*;
|
||||
|
||||
@ApiModel("${sceneEnum.name} - ${table.classComment} Response VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class ${sceneEnum.prefixClass}${table.className}RespVO extends ${sceneEnum.prefixClass}${table.className}BaseVO {
|
||||
|
||||
#foreach ($column in $columns)
|
||||
#if (${column.listOperationResult} && (!${column.createOperation} || !${column.updateOperation}))##不是通用字段
|
||||
@ApiModelProperty(value = "${column.columnComment}"#if (!${column.nullable}), required = true#end#if ("$!column.example" != ""), example = "${column.example}"#end)
|
||||
private ${column.javaType} ${column.javaField};
|
||||
|
||||
#end
|
||||
#end
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
package ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import io.swagger.annotations.*;
|
||||
import javax.validation.constraints.*;
|
||||
## 处理 Date 字段的引入
|
||||
#foreach ($column in $columns)
|
||||
#if (${column.updateOperation} && (!${column.createOperation} || !${column.listOperationResult})
|
||||
&& ${column.javaType} == "Date"))## 时间类型
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import static ${DateUtilsClassName}.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
#break
|
||||
#end
|
||||
#end
|
||||
|
||||
@ApiModel("${sceneEnum.name} - ${table.classComment}更新 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class ${sceneEnum.prefixClass}${table.className}UpdateReqVO extends ${sceneEnum.prefixClass}${table.className}BaseVO {
|
||||
|
||||
#foreach ($column in $columns)
|
||||
#if (${column.updateOperation} && (!${column.createOperation} || !${column.listOperationResult}))##不是通用字段
|
||||
#parse("codegen/java/controller/vo/_column.vm")
|
||||
|
||||
#end
|
||||
#end
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
package ${basePackage}.module.${table.moduleName}.convert.${table.businessName};
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import ${PageResultClassName};
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
import ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo.*;
|
||||
import ${basePackage}.module.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO;
|
||||
|
||||
/**
|
||||
* ${table.classComment} Convert
|
||||
*
|
||||
* @author ${table.author}
|
||||
*/
|
||||
@Mapper
|
||||
public interface ${table.className}Convert {
|
||||
|
||||
${table.className}Convert INSTANCE = Mappers.getMapper(${table.className}Convert.class);
|
||||
|
||||
${table.className}DO convert(${sceneEnum.prefixClass}${table.className}CreateReqVO bean);
|
||||
|
||||
${table.className}DO convert(${sceneEnum.prefixClass}${table.className}UpdateReqVO bean);
|
||||
|
||||
${sceneEnum.prefixClass}${table.className}RespVO convert(${table.className}DO bean);
|
||||
|
||||
List<${sceneEnum.prefixClass}${table.className}RespVO> convertList(List<${table.className}DO> list);
|
||||
|
||||
PageResult<${sceneEnum.prefixClass}${table.className}RespVO> convertPage(PageResult<${table.className}DO> page);
|
||||
|
||||
List<${sceneEnum.prefixClass}${table.className}ExcelVO> convertList02(List<${table.className}DO> list);
|
||||
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
package ${basePackage}.module.${table.moduleName}.dal.dataobject.${table.businessName};
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import ${BaseDOClassName};
|
||||
|
||||
/**
|
||||
* ${table.classComment} DO
|
||||
*
|
||||
* @author ${table.author}
|
||||
*/
|
||||
@TableName("${table.tableName}")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ${table.className}DO extends BaseDO {
|
||||
|
||||
#foreach ($column in $columns)
|
||||
#if (!${baseDOFields.contains(${column.javaField})})##排除 BaseDO 的字段
|
||||
/**
|
||||
* ${column.columnComment}
|
||||
#if ("$!column.dictType" != "")##处理枚举值
|
||||
*
|
||||
* 枚举 {@link TODO ${column.dictType} 对应的类}
|
||||
#end
|
||||
*/
|
||||
#if (${column.primaryKey})##处理主键
|
||||
@TableId#if (${column.javaType} == 'String')type = IdType.INPUT)#end
|
||||
#end
|
||||
private ${column.javaType} ${column.javaField};
|
||||
#end
|
||||
#end
|
||||
|
||||
}
|
@@ -0,0 +1,66 @@
|
||||
package ${basePackage}.module.${table.moduleName}.dal.mysql.${table.businessName};
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import ${PageResultClassName};
|
||||
import ${QueryWrapperClassName};
|
||||
import ${BaseMapperClassName};
|
||||
import ${basePackage}.module.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo.*;
|
||||
|
||||
## 字段模板
|
||||
#macro(listCondition)
|
||||
#foreach ($column in $columns)
|
||||
#if (${column.listOperation})
|
||||
#set ($JavaField = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})##首字母大写
|
||||
#if (${column.listOperationCondition} == "=")##情况一,= 的时候
|
||||
.eqIfPresent(${table.className}DO::get${JavaField}, reqVO.get${JavaField}())
|
||||
#end
|
||||
#if (${column.listOperationCondition} == "!=")##情况二,!= 的时候
|
||||
.neIfPresent(${table.className}DO::get${JavaField}, reqVO.get${JavaField}())
|
||||
#end
|
||||
#if (${column.listOperationCondition} == ">")##情况三,> 的时候
|
||||
.gtIfPresent(${table.className}DO::get${JavaField}, reqVO.get${JavaField}())
|
||||
#end
|
||||
#if (${column.listOperationCondition} == ">=")##情况四,>= 的时候
|
||||
.geIfPresent(${table.className}DO::get${JavaField}, reqVO.get${JavaField}())
|
||||
#end
|
||||
#if (${column.listOperationCondition} == "<")##情况五,< 的时候
|
||||
.ltIfPresent(${table.className}DO::get${JavaField}, reqVO.get${JavaField}())
|
||||
#end
|
||||
#if (${column.listOperationCondition} == "<=")##情况五,<= 的时候
|
||||
.leIfPresent(${table.className}DO::get${JavaField}, reqVO.get${JavaField}())
|
||||
#end
|
||||
#if (${column.listOperationCondition} == "LIKE")##情况七,Like 的时候
|
||||
.likeIfPresent(${table.className}DO::get${JavaField}, reqVO.get${JavaField}())
|
||||
#end
|
||||
#if (${column.listOperationCondition} == "BETWEEN")##情况八,Between 的时候
|
||||
.betweenIfPresent(${table.className}DO::get${JavaField}, reqVO.getBegin${JavaField}(), reqVO.getEnd${JavaField}())
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
/**
|
||||
* ${table.classComment} Mapper
|
||||
*
|
||||
* @author ${table.author}
|
||||
*/
|
||||
@Mapper
|
||||
public interface ${table.className}Mapper extends BaseMapperX<${table.className}DO> {
|
||||
|
||||
default PageResult<${table.className}DO> selectPage(${sceneEnum.prefixClass}${table.className}PageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<${table.className}DO>()
|
||||
#listCondition()
|
||||
.orderByDesc(${table.className}DO::getId));## 大多数情况下,id 倒序
|
||||
|
||||
}
|
||||
|
||||
default List<${table.className}DO> selectList(${sceneEnum.prefixClass}${table.className}ExportReqVO reqVO) {
|
||||
return selectList(new LambdaQueryWrapperX<${table.className}DO>()
|
||||
#listCondition()
|
||||
.orderByDesc(${table.className}DO::getId));## 大多数情况下,id 倒序
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
// TODO 待办:请将下面的错误码复制到 yudao-module-${table.moduleName}-api 模块的 ErrorCodeConstants 类中。注意,请给“TODO 补充编号”设置一个错误码编号!!!
|
||||
// ========== ${table.classComment} TODO 补充编号 ==========
|
||||
ErrorCode ${simpleClassName_underlineCase.toUpperCase()}_NOT_EXISTS = new ErrorCode(TODO 补充编号, "${table.classComment}不存在");
|
@@ -0,0 +1,70 @@
|
||||
package ${basePackage}.module.${table.moduleName}.service.${table.businessName};
|
||||
|
||||
import java.util.*;
|
||||
import javax.validation.*;
|
||||
import ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo.*;
|
||||
import ${basePackage}.module.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO;
|
||||
import ${PageResultClassName};
|
||||
|
||||
/**
|
||||
* ${table.classComment} Service 接口
|
||||
*
|
||||
* @author ${table.author}
|
||||
*/
|
||||
public interface ${table.className}Service {
|
||||
|
||||
/**
|
||||
* 创建${table.classComment}
|
||||
*
|
||||
* @param createReqVO 创建信息
|
||||
* @return 编号
|
||||
*/
|
||||
${primaryColumn.javaType} create${simpleClassName}(@Valid ${sceneEnum.prefixClass}${table.className}CreateReqVO createReqVO);
|
||||
|
||||
/**
|
||||
* 更新${table.classComment}
|
||||
*
|
||||
* @param updateReqVO 更新信息
|
||||
*/
|
||||
void update${simpleClassName}(@Valid ${sceneEnum.prefixClass}${table.className}UpdateReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 删除${table.classComment}
|
||||
*
|
||||
* @param id 编号
|
||||
*/
|
||||
void delete${simpleClassName}(${primaryColumn.javaType} id);
|
||||
|
||||
/**
|
||||
* 获得${table.classComment}
|
||||
*
|
||||
* @param id 编号
|
||||
* @return ${table.classComment}
|
||||
*/
|
||||
${table.className}DO get${simpleClassName}(${primaryColumn.javaType} id);
|
||||
|
||||
/**
|
||||
* 获得${table.classComment}列表
|
||||
*
|
||||
* @param ids 编号
|
||||
* @return ${table.classComment}列表
|
||||
*/
|
||||
List<${table.className}DO> get${simpleClassName}List(Collection<${primaryColumn.javaType}> ids);
|
||||
|
||||
/**
|
||||
* 获得${table.classComment}分页
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @return ${table.classComment}分页
|
||||
*/
|
||||
PageResult<${table.className}DO> get${simpleClassName}Page(${sceneEnum.prefixClass}${table.className}PageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 获得${table.classComment}列表, 用于 Excel 导出
|
||||
*
|
||||
* @param exportReqVO 查询条件
|
||||
* @return ${table.classComment}列表
|
||||
*/
|
||||
List<${table.className}DO> get${simpleClassName}List(${sceneEnum.prefixClass}${table.className}ExportReqVO exportReqVO);
|
||||
|
||||
}
|
@@ -0,0 +1,82 @@
|
||||
package ${basePackage}.module.${table.moduleName}.service.${table.businessName};
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import javax.annotation.Resource;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.util.*;
|
||||
import ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo.*;
|
||||
import ${basePackage}.module.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO;
|
||||
import ${PageResultClassName};
|
||||
|
||||
import ${basePackage}.module.${table.moduleName}.convert.${table.businessName}.${table.className}Convert;
|
||||
import ${basePackage}.module.${table.moduleName}.dal.mysql.${table.businessName}.${table.className}Mapper;
|
||||
|
||||
import static ${ServiceExceptionUtilClassName}.exception;
|
||||
import static ${basePackage}.module.${table.moduleName}.enums.ErrorCodeConstants.*;
|
||||
|
||||
/**
|
||||
* ${table.classComment} Service 实现类
|
||||
*
|
||||
* @author ${table.author}
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class ${table.className}ServiceImpl implements ${table.className}Service {
|
||||
|
||||
@Resource
|
||||
private ${table.className}Mapper ${classNameVar}Mapper;
|
||||
|
||||
@Override
|
||||
public ${primaryColumn.javaType} create${simpleClassName}(${sceneEnum.prefixClass}${table.className}CreateReqVO createReqVO) {
|
||||
// 插入
|
||||
${table.className}DO ${classNameVar} = ${table.className}Convert.INSTANCE.convert(createReqVO);
|
||||
${classNameVar}Mapper.insert(${classNameVar});
|
||||
// 返回
|
||||
return ${classNameVar}.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update${simpleClassName}(${sceneEnum.prefixClass}${table.className}UpdateReqVO updateReqVO) {
|
||||
// 校验存在
|
||||
this.validate${simpleClassName}Exists(updateReqVO.getId());
|
||||
// 更新
|
||||
${table.className}DO updateObj = ${table.className}Convert.INSTANCE.convert(updateReqVO);
|
||||
${classNameVar}Mapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete${simpleClassName}(${primaryColumn.javaType} id) {
|
||||
// 校验存在
|
||||
this.validate${simpleClassName}Exists(id);
|
||||
// 删除
|
||||
${classNameVar}Mapper.deleteById(id);
|
||||
}
|
||||
|
||||
private void validate${simpleClassName}Exists(${primaryColumn.javaType} id) {
|
||||
if (${classNameVar}Mapper.selectById(id) == null) {
|
||||
throw exception(${simpleClassName_underlineCase.toUpperCase()}_NOT_EXISTS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ${table.className}DO get${simpleClassName}(${primaryColumn.javaType} id) {
|
||||
return ${classNameVar}Mapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<${table.className}DO> get${simpleClassName}List(Collection<${primaryColumn.javaType}> ids) {
|
||||
return ${classNameVar}Mapper.selectBatchIds(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<${table.className}DO> get${simpleClassName}Page(${sceneEnum.prefixClass}${table.className}PageReqVO pageReqVO) {
|
||||
return ${classNameVar}Mapper.selectPage(pageReqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<${table.className}DO> get${simpleClassName}List(${sceneEnum.prefixClass}${table.className}ExportReqVO exportReqVO) {
|
||||
return ${classNameVar}Mapper.selectList(exportReqVO);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,161 @@
|
||||
package ${basePackage}.module.${table.moduleName}.service.${table.businessName};
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import ${basePackage}.module.${table.moduleName}.test.BaseDbUnitTest;## 每个项目,默认有一个基础 DB Test 基类
|
||||
|
||||
import ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo.*;
|
||||
import ${basePackage}.module.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO;
|
||||
import ${basePackage}.module.${table.moduleName}.dal.mysql.${table.businessName}.${table.className}Mapper;
|
||||
import ${PageResultClassName};
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import java.util.*;
|
||||
|
||||
import static cn.hutool.core.util.RandomUtil.*;
|
||||
import static ${basePackage}.module.${table.moduleName}.enums.ErrorCodeConstants.*;
|
||||
import static ${baseFrameworkPackage}.test.core.util.AssertUtils.*;
|
||||
import static ${baseFrameworkPackage}.test.core.util.RandomUtils.*;
|
||||
import static ${ObjectUtilsClassName}.*;
|
||||
import static ${DateUtilsClassName}.*;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
## 字段模板
|
||||
#macro(getPageCondition $VO)
|
||||
// mock 数据
|
||||
${table.className}DO db${simpleClassName} = randomPojo(${table.className}DO.class, o -> { // 等会查询到
|
||||
#foreach ($column in $columns)
|
||||
#if (${column.listOperation})
|
||||
#set ($JavaField = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})##首字母大写
|
||||
o.set$JavaField(null);
|
||||
#end
|
||||
#end
|
||||
});
|
||||
${classNameVar}Mapper.insert(db${simpleClassName});
|
||||
#foreach ($column in $columns)
|
||||
#if (${column.listOperation})
|
||||
#set ($JavaField = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})##首字母大写
|
||||
// 测试 ${column.javaField} 不匹配
|
||||
${classNameVar}Mapper.insert(cloneIgnoreId(db${simpleClassName}, o -> o.set$JavaField(null)));
|
||||
#end
|
||||
#end
|
||||
// 准备参数
|
||||
${sceneEnum.prefixClass}${table.className}${VO} reqVO = new ${sceneEnum.prefixClass}${table.className}${VO}();
|
||||
#foreach ($column in $columns)
|
||||
#if (${column.listOperation})
|
||||
#set ($JavaField = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})##首字母大写
|
||||
#if (${column.listOperationCondition} == "BETWEEN")## BETWEEN 的情况
|
||||
reqVO.setBegin${JavaField}(null);
|
||||
reqVO.setEnd${JavaField}(null);
|
||||
#else
|
||||
reqVO.set$JavaField(null);
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
/**
|
||||
* {@link ${table.className}ServiceImpl} 的单元测试类
|
||||
*
|
||||
* @author ${table.author}
|
||||
*/
|
||||
@Import(${table.className}ServiceImpl.class)
|
||||
public class ${table.className}ServiceImplTest extends BaseDbUnitTest {
|
||||
|
||||
@Resource
|
||||
private ${table.className}ServiceImpl ${classNameVar}Service;
|
||||
|
||||
@Resource
|
||||
private ${table.className}Mapper ${classNameVar}Mapper;
|
||||
|
||||
@Test
|
||||
public void testCreate${simpleClassName}_success() {
|
||||
// 准备参数
|
||||
${sceneEnum.prefixClass}${table.className}CreateReqVO reqVO = randomPojo(${sceneEnum.prefixClass}${table.className}CreateReqVO.class);
|
||||
|
||||
// 调用
|
||||
${primaryColumn.javaType} ${classNameVar}Id = ${classNameVar}Service.create${simpleClassName}(reqVO);
|
||||
// 断言
|
||||
assertNotNull(${classNameVar}Id);
|
||||
// 校验记录的属性是否正确
|
||||
${table.className}DO ${classNameVar} = ${classNameVar}Mapper.selectById(${classNameVar}Id);
|
||||
assertPojoEquals(reqVO, ${classNameVar});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdate${simpleClassName}_success() {
|
||||
// mock 数据
|
||||
${table.className}DO db${simpleClassName} = randomPojo(${table.className}DO.class);
|
||||
${classNameVar}Mapper.insert(db${simpleClassName});// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
${sceneEnum.prefixClass}${table.className}UpdateReqVO reqVO = randomPojo(${sceneEnum.prefixClass}${table.className}UpdateReqVO.class, o -> {
|
||||
o.setId(db${simpleClassName}.getId()); // 设置更新的 ID
|
||||
});
|
||||
|
||||
// 调用
|
||||
${classNameVar}Service.update${simpleClassName}(reqVO);
|
||||
// 校验是否更新正确
|
||||
${table.className}DO ${classNameVar} = ${classNameVar}Mapper.selectById(reqVO.getId()); // 获取最新的
|
||||
assertPojoEquals(reqVO, ${classNameVar});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdate${simpleClassName}_notExists() {
|
||||
// 准备参数
|
||||
${sceneEnum.prefixClass}${table.className}UpdateReqVO reqVO = randomPojo(${sceneEnum.prefixClass}${table.className}UpdateReqVO.class);
|
||||
|
||||
// 调用, 并断言异常
|
||||
assertServiceException(() -> ${classNameVar}Service.update${simpleClassName}(reqVO), ${simpleClassName_underlineCase.toUpperCase()}_NOT_EXISTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDelete${simpleClassName}_success() {
|
||||
// mock 数据
|
||||
${table.className}DO db${simpleClassName} = randomPojo(${table.className}DO.class);
|
||||
${classNameVar}Mapper.insert(db${simpleClassName});// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
${primaryColumn.javaType} id = db${simpleClassName}.getId();
|
||||
|
||||
// 调用
|
||||
${classNameVar}Service.delete${simpleClassName}(id);
|
||||
// 校验数据不存在了
|
||||
assertNull(${classNameVar}Mapper.selectById(id));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDelete${simpleClassName}_notExists() {
|
||||
// 准备参数
|
||||
${primaryColumn.javaType} id = random${primaryColumn.javaType}Id();
|
||||
|
||||
// 调用, 并断言异常
|
||||
assertServiceException(() -> ${classNameVar}Service.delete${simpleClassName}(id), ${simpleClassName_underlineCase.toUpperCase()}_NOT_EXISTS);
|
||||
}
|
||||
|
||||
@Test // TODO 请修改 null 为需要的值
|
||||
public void testGet${simpleClassName}Page() {
|
||||
#getPageCondition("PageReqVO")
|
||||
|
||||
// 调用
|
||||
PageResult<${table.className}DO> pageResult = ${classNameVar}Service.get${simpleClassName}Page(reqVO);
|
||||
// 断言
|
||||
assertEquals(1, pageResult.getTotal());
|
||||
assertEquals(1, pageResult.getList().size());
|
||||
assertPojoEquals(db${simpleClassName}, pageResult.getList().get(0));
|
||||
}
|
||||
|
||||
@Test // TODO 请修改 null 为需要的值
|
||||
public void testGet${simpleClassName}List() {
|
||||
#getPageCondition("ExportReqVO")
|
||||
|
||||
// 调用
|
||||
List<${table.className}DO> list = ${classNameVar}Service.get${simpleClassName}List(reqVO);
|
||||
// 断言
|
||||
assertEquals(1, list.size());
|
||||
assertPojoEquals(db${simpleClassName}, list.get(0));
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
-- 将该建表 SQL 语句,添加到 yudao-module-${table.moduleName}-impl 模块的 test/resources/sql/create_tables.sql 文件里
|
||||
CREATE TABLE IF NOT EXISTS "${table.tableName}" (
|
||||
#foreach ($column in $columns)
|
||||
#if (${column.primaryKey})##处理主键
|
||||
"${column.javaField}"#if (${column.javaType} == 'String') ${column.columnType} NOT NULL#else ${column.columnType} NOT NULL GENERATED BY DEFAULT AS IDENTITY#end,
|
||||
#else
|
||||
#if (${column.columnName} == 'create_time')
|
||||
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
#elseif (${column.columnName} == 'update_time')
|
||||
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
#elseif (${column.columnName} == 'creator' || ${column.columnName} == 'updater')
|
||||
"${column.columnName}" ${column.columnType} DEFAULT '',
|
||||
#elseif (${column.columnName} == 'deleted')
|
||||
"deleted" bit NOT NULL DEFAULT FALSE,
|
||||
#else
|
||||
"${column.columnName}" ${column.columnType}#if (${column.nullable} == false) NOT NULL#end,
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
PRIMARY KEY ("${primaryColumn.columnName}")
|
||||
) COMMENT '${table.tableComment}';
|
||||
|
||||
-- 将该删表 SQL 语句,添加到 yudao-module-${table.moduleName}-impl 模块的 test/resources/sql/clean.sql 文件里
|
||||
DELETE FROM "${table.tableName}";
|
@@ -0,0 +1,27 @@
|
||||
-- 菜单 SQL
|
||||
INSERT INTO `system_menu`(
|
||||
`name`, `permission`, `menu_type`, `sort`, `parent_id`,
|
||||
`path`, `icon`, `component`, `status`
|
||||
)
|
||||
VALUES (
|
||||
'${table.classComment}管理', '', 2, 0, ${table.parentMenuId},
|
||||
'${simpleClassName_strikeCase}', '', '${table.moduleName}/${classNameVar}/index', 0
|
||||
);
|
||||
|
||||
-- 按钮父菜单ID
|
||||
SELECT @parentId := LAST_INSERT_ID();
|
||||
|
||||
-- 按钮 SQL
|
||||
#set ($functionNames = ['查询', '创建', '更新', '删除', '导出'])
|
||||
#set ($functionOps = ['query', 'create', 'update', 'delete', 'export'])
|
||||
#foreach ($functionName in $functionNames)
|
||||
#set ($index = $foreach.count - 1)
|
||||
INSERT INTO `system_menu`(
|
||||
`name`, `permission`, `menu_type`, `sort`, `parent_id`,
|
||||
`path`, `icon`, `component`, `status`
|
||||
)
|
||||
VALUES (
|
||||
'${table.classComment}${functionName}', '${permissionPrefix}:${functionOps.get($index)}', 3, $foreach.count, @parentId,
|
||||
'', '', '', 0
|
||||
);
|
||||
#end
|
@@ -0,0 +1,55 @@
|
||||
import request from '@/utils/request'
|
||||
#set ($baseURL = "/${table.moduleName}/${simpleClassName_strikeCase}")
|
||||
|
||||
// 创建${table.classComment}
|
||||
export function create${simpleClassName}(data) {
|
||||
return request({
|
||||
url: '${baseURL}/create',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 更新${table.classComment}
|
||||
export function update${simpleClassName}(data) {
|
||||
return request({
|
||||
url: '${baseURL}/update',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除${table.classComment}
|
||||
export function delete${simpleClassName}(id) {
|
||||
return request({
|
||||
url: '${baseURL}/delete?id=' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 获得${table.classComment}
|
||||
export function get${simpleClassName}(id) {
|
||||
return request({
|
||||
url: '${baseURL}/get?id=' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 获得${table.classComment}分页
|
||||
export function get${simpleClassName}Page(query) {
|
||||
return request({
|
||||
url: '${baseURL}/page',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 导出${table.classComment} Excel
|
||||
export function export${simpleClassName}Excel(query) {
|
||||
return request({
|
||||
url: '${baseURL}/export-excel',
|
||||
method: 'get',
|
||||
params: query,
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
@@ -0,0 +1,399 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
|
||||
#foreach($column in $columns)
|
||||
#if ($column.listOperation)
|
||||
#set ($dictType=$column.dictType)
|
||||
#set ($javaField = $column.javaField)
|
||||
#set ($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
|
||||
#set ($comment=$column.columnComment)
|
||||
#if ($column.htmlType == "input")
|
||||
<el-form-item label="${comment}" prop="${javaField}">
|
||||
<el-input v-model="queryParams.${javaField}" placeholder="请输入${comment}" clearable size="small" @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
#elseif ($column.htmlType == "select" || $column.htmlType == "radio")
|
||||
<el-form-item label="${comment}" prop="${javaField}">
|
||||
<el-select v-model="queryParams.${javaField}" placeholder="请选择${comment}" clearable size="small">
|
||||
#if ("" != $dictType)## 设置了 dictType 数据字典的情况
|
||||
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())"
|
||||
:key="dict.value" :label="dict.label" :value="dict.value"/>
|
||||
#else## 未设置 dictType 数据字典的情况
|
||||
<el-option label="请选择字典生成" value="" />
|
||||
#end
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
#elseif($column.htmlType == "datetime")
|
||||
#if ($column.listOperationCondition != "BETWEEN")## 非范围
|
||||
<el-form-item label="${comment}" prop="${javaField}">
|
||||
<el-date-picker clearable size="small" v-model="queryParams.${javaField}" type="date" value-format="yyyy-MM-dd" placeholder="选择${comment}" />
|
||||
</el-form-item>
|
||||
#else## 范围
|
||||
<el-form-item label="${comment}">
|
||||
<el-date-picker v-model="dateRange${AttrName}" size="small" style="width: 240px" value-format="yyyy-MM-dd"
|
||||
type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" />
|
||||
</el-form-item>
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 操作工具栏 -->
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
|
||||
v-hasPermi="['${permissionPrefix}:create']">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" :loading="exportLoading"
|
||||
v-hasPermi="['${permissionPrefix}:export']">导出</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<!-- 列表 -->
|
||||
<el-table v-loading="loading" :data="list">
|
||||
#foreach($column in $columns)
|
||||
#if ($column.listOperationResult)
|
||||
#set ($dictType=$column.dictType)
|
||||
#set ($javaField = $column.javaField)
|
||||
#set ($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
|
||||
#set ($comment=$column.columnComment)
|
||||
#if ($column.javaType == "Date")## 时间类型
|
||||
<el-table-column label="${comment}" align="center" prop="${javaField}" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.${javaField}) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
#elseif("" != $column.dictType)## 数据字典
|
||||
<el-table-column label="${comment}" align="center" prop="${javaField}">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.${column.javaField}" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
#else
|
||||
<el-table-column label="${comment}" align="center" prop="${javaField}" />
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['${permissionPrefix}:update']">修改</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
|
||||
v-hasPermi="['${permissionPrefix}:delete']">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页组件 -->
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"/>
|
||||
|
||||
<!-- 对话框(添加 / 修改) -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
||||
#foreach($column in $columns)
|
||||
#if ($column.createOperation || $column.updateOperation)
|
||||
#set ($dictType = $column.dictType)
|
||||
#set ($javaField = $column.javaField)
|
||||
#set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
|
||||
#set ($comment = $column.columnComment)
|
||||
#if ($column.htmlType == "input")
|
||||
#if (!$column.primaryKey)## 忽略主键,不用在表单里
|
||||
<el-form-item label="${comment}" prop="${javaField}">
|
||||
<el-input v-model="form.${javaField}" placeholder="请输入${comment}" />
|
||||
</el-form-item>
|
||||
#end
|
||||
#elseif($column.htmlType == "imageUpload")## 图片上传
|
||||
#set ($hasImageUploadColumn = true)
|
||||
<el-form-item label="${comment}">
|
||||
<imageUpload v-model="form.${javaField}"/>
|
||||
</el-form-item>
|
||||
#elseif($column.htmlType == "fileUpload")## 文件上传
|
||||
#set ($hasFileUploadColumn = true)
|
||||
<el-form-item label="${comment}">
|
||||
<fileUpload v-model="form.${javaField}"/>
|
||||
</el-form-item>
|
||||
#elseif($column.htmlType == "editor")## 文本编辑器
|
||||
#set ($hasEditorColumn = true)
|
||||
<el-form-item label="${comment}">
|
||||
<editor v-model="form.${javaField}" :min-height="192"/>
|
||||
</el-form-item>
|
||||
#elseif($column.htmlType == "select")## 下拉框
|
||||
<el-form-item label="${comment}" prop="${javaField}">
|
||||
<el-select v-model="form.${javaField}" placeholder="请选择${comment}">
|
||||
#if ("" != $dictType)## 有数据字典
|
||||
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())"
|
||||
:key="dict.value" :label="dict.label" #if ($column.javaType == "Integer" || $column.javaType == "Long"):value="parseInt(dict.value)"#else:value="dict.value"#end />
|
||||
#else##没数据字典
|
||||
<el-option label="请选择字典生成" value="" />
|
||||
#end
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
#elseif($column.htmlType == "checkbox")## 多选框
|
||||
<el-form-item label="${comment}" prop="${javaField}">
|
||||
<el-checkbox-group v-model="form.${javaField}">
|
||||
#if ("" != $dictType)## 有数据字典
|
||||
<el-checkbox v-for="dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())"
|
||||
:key="dict.value" #if($column.javaType == "Integer" || $column.javaType == "Long"):label="parseInt(dict.value)"#else:label="dict.value"#end>{{dict.label}}</el-checkbox>
|
||||
#else##没数据字典
|
||||
<el-checkbox>请选择字典生成</el-checkbox>
|
||||
#end
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
#elseif($column.htmlType == "radio")## 单选框
|
||||
<el-form-item label="${comment}" prop="${javaField}">
|
||||
<el-radio-group v-model="form.${javaField}">
|
||||
#if ("" != $dictType)## 有数据字典
|
||||
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())"
|
||||
:key="dict.value" #if($column.javaType == "Integer" || $column.javaType == "Long"):label="parseInt(dict.value)"#else:label="dict.value"#end>{{dict.label}}</el-radio>
|
||||
#else##没数据字典
|
||||
<el-radio label="1">请选择字典生成</el-radio>
|
||||
#end
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
#elseif($column.htmlType == "datetime")## 时间框
|
||||
<el-form-item label="${comment}" prop="${javaField}">
|
||||
<el-date-picker clearable size="small" v-model="form.${javaField}" type="date" value-format="yyyy-MM-dd" placeholder="选择${comment}" />
|
||||
</el-form-item>
|
||||
#elseif($column.htmlType == "textarea")## 文本框
|
||||
<el-form-item label="${comment}" prop="${javaField}">
|
||||
<el-input v-model="form.${javaField}" type="textarea" placeholder="请输入内容" />
|
||||
</el-form-item>
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { create${simpleClassName}, update${simpleClassName}, delete${simpleClassName}, get${simpleClassName}, get${simpleClassName}Page, export${simpleClassName}Excel } from "@/api/${table.moduleName}/${classNameVar}";
|
||||
#if ($hasImageUploadColumn)
|
||||
import ImageUpload from '@/components/ImageUpload';
|
||||
#end
|
||||
#if ($hasFileUploadColumn)
|
||||
import FileUpload from '@/components/FileUpload';
|
||||
#end
|
||||
#if ($hasEditorColumn)
|
||||
import Editor from '@/components/Editor';
|
||||
#end
|
||||
|
||||
export default {
|
||||
name: "${simpleClassName}",
|
||||
components: {
|
||||
#if ($hasImageUploadColumn)
|
||||
ImageUpload,
|
||||
#end
|
||||
#if ($hasFileUploadColumn)
|
||||
FileUpload,
|
||||
#end
|
||||
#if ($hasEditorColumn)
|
||||
Editor,
|
||||
#end
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 导出遮罩层
|
||||
exportLoading: false,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// ${table.classComment}列表
|
||||
list: [],
|
||||
// 弹出层标题
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
#foreach ($column in $columns)## 时间范围
|
||||
#if ($column.listOperation)
|
||||
#if ($column.htmlType == "datetime" && $column.listOperationCondition == "BETWEEN")
|
||||
#set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
|
||||
dateRange${AttrName}: [],
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
#foreach ($column in $columns)
|
||||
#if ($column.listOperation && $column.listOperationCondition != 'BETWEEN')
|
||||
$column.javaField: null,
|
||||
#end
|
||||
#end
|
||||
},
|
||||
// 表单参数
|
||||
form: {},
|
||||
// 表单校验
|
||||
rules: {
|
||||
#foreach ($column in $columns)
|
||||
#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键
|
||||
#set($comment=$column.columnComment)
|
||||
$column.javaField: [{ required: true, message: "${comment}不能为空", trigger: #if($column.htmlType == "select")"change"#else"blur"#end }],
|
||||
#end
|
||||
#end
|
||||
}
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
/** 查询列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
// 处理查询参数
|
||||
let params = {...this.queryParams};
|
||||
#foreach ($column in $columns)
|
||||
#if ($column.listOperation)
|
||||
#if ($column.htmlType == "datetime" && $column.listOperationCondition == "BETWEEN")
|
||||
#set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
|
||||
this.addBeginAndEndTime(params, this.dateRange${AttrName}, '${column.javaField}');
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
// 执行查询
|
||||
get${simpleClassName}Page(params).then(response => {
|
||||
this.list = response.data.list;
|
||||
this.total = response.data.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 取消按钮 */
|
||||
cancel() {
|
||||
this.open = false;
|
||||
this.reset();
|
||||
},
|
||||
/** 表单重置 */
|
||||
reset() {
|
||||
this.form = {
|
||||
#foreach ($column in $columns)
|
||||
#if ($column.createOperation || $column.updateOperation)
|
||||
#if ($column.htmlType == "checkbox")
|
||||
$column.javaField: [],
|
||||
#else
|
||||
$column.javaField: undefined,
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
};
|
||||
this.resetForm("form");
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNo = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
#foreach ($column in $columns)
|
||||
#if ($column.listOperation)
|
||||
#if ($column.htmlType == "datetime" && $column.listOperationCondition == "BETWEEN")
|
||||
#set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
|
||||
this.dateRange${AttrName} = [];
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.reset();
|
||||
this.open = true;
|
||||
this.title = "添加${table.classComment}";
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate(row) {
|
||||
this.reset();
|
||||
const ${primaryColumn.javaField} = row.${primaryColumn.javaField};
|
||||
get${simpleClassName}(${primaryColumn.javaField}).then(response => {
|
||||
this.form = response.data;
|
||||
#foreach ($column in $columns)
|
||||
#if($column.htmlType == "checkbox")## checkbox 特殊处理
|
||||
this.form.$column.javaField = this.form.${column.javaField}.split(",");
|
||||
#end
|
||||
#end
|
||||
this.open = true;
|
||||
this.title = "修改${table.classComment}";
|
||||
});
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm() {
|
||||
this.#[[$]]#refs["form"].validate(valid => {
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
#foreach ($column in $columns)
|
||||
#if($column.htmlType == "checkbox")
|
||||
this.form.$column.javaField = this.form.${column.javaField}.join(",");
|
||||
#end
|
||||
#end
|
||||
// 修改的提交
|
||||
if (this.form.${primaryColumn.javaField} != null) {
|
||||
update${simpleClassName}(this.form).then(response => {
|
||||
this.#[[$modal]]#.msgSuccess("修改成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
});
|
||||
return;
|
||||
}
|
||||
// 添加的提交
|
||||
create${simpleClassName}(this.form).then(response => {
|
||||
this.#[[$modal]]#.msgSuccess("新增成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
});
|
||||
});
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
const ${primaryColumn.javaField} = row.${primaryColumn.javaField};
|
||||
this.#[[$modal]]#.confirm('是否确认删除${table.classComment}编号为"' + ${primaryColumn.javaField} + '"的数据项?').then(function() {
|
||||
return delete${simpleClassName}(${primaryColumn.javaField});
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.#[[$modal]]#.msgSuccess("删除成功");
|
||||
}).catch(() => {});
|
||||
},
|
||||
/** 导出按钮操作 */
|
||||
handleExport() {
|
||||
// 处理查询参数
|
||||
let params = {...this.queryParams};
|
||||
params.pageNo = undefined;
|
||||
params.pageSize = undefined;
|
||||
#foreach ($column in $columns)
|
||||
#if ($column.listOperation)
|
||||
#if ($column.htmlType == "datetime" && $column.listOperationCondition == "BETWEEN")
|
||||
#set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
|
||||
this.addBeginAndEndTime(params, this.dateRange${AttrName}, '${column.javaField}');
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
// 执行导出
|
||||
this.#[[$modal]]#.confirm('是否确认导出所有${table.classComment}数据项?').then(() => {
|
||||
this.exportLoading = true;
|
||||
return export${simpleClassName}Excel(params);
|
||||
}).then(response => {
|
||||
this.$download.excel(response, '${table.classComment}.xls');
|
||||
this.exportLoading = false;
|
||||
}).catch(() => {});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
@@ -0,0 +1,23 @@
|
||||
package cn.iocoder.yudao.module.infra.dal.mysql.codegen;
|
||||
|
||||
import cn.iocoder.yudao.module.tool.dal.dataobject.codegen.SchemaColumnDO;
|
||||
import cn.iocoder.yudao.module.tool.test.BaseDbUnitTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class SchemaColumnMapperTest extends BaseDbUnitTest {
|
||||
|
||||
@Resource
|
||||
private SchemaColumnMapper schemaColumnMapper;
|
||||
|
||||
@Test
|
||||
public void testSelectListByTableName() {
|
||||
List<SchemaColumnDO> columns = schemaColumnMapper.selectListByTableName("", "inf_config");
|
||||
assertTrue(columns.size() > 0);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 占位
|
||||
*/
|
||||
package cn.iocoder.yudao.module.infra.dal.mysql;
|
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 占位
|
||||
*/
|
||||
package cn.iocoder.yudao.module.infra.dal;
|
@@ -0,0 +1,34 @@
|
||||
package cn.iocoder.yudao.module.infra.service.codegen;
|
||||
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.mysql.codegen.CodegenColumnMapper;
|
||||
import cn.iocoder.yudao.module.infra.dal.mysql.codegen.CodegenTableMapper;
|
||||
import cn.iocoder.yudao.module.infra.service.codegen.inner.CodegenEngine;
|
||||
import cn.iocoder.yudao.module.infra.test.BaseDbUnitTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class CodegenEngineTest extends BaseDbUnitTest {
|
||||
|
||||
@Resource
|
||||
private CodegenTableMapper codegenTableMapper;
|
||||
@Resource
|
||||
private CodegenColumnMapper codegenColumnMapper;
|
||||
|
||||
@Resource
|
||||
private CodegenEngine codegenEngine;
|
||||
|
||||
@Test
|
||||
public void testExecute() {
|
||||
CodegenTableDO table = codegenTableMapper.selectById(20);
|
||||
List<CodegenColumnDO> columns = codegenColumnMapper.selectListByTableId(table.getId());
|
||||
Map<String, String> result = codegenEngine.execute(table, columns);
|
||||
result.forEach((s, s2) -> System.out.println(s2));
|
||||
// System.out.println(result.get("vue/views/system/test/index.vue"));
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
package cn.iocoder.yudao.module.infra.service.codegen;
|
||||
|
||||
import cn.iocoder.yudao.module.infra.service.codegen.inner.CodegenSQLParser;
|
||||
import cn.iocoder.yudao.module.infra.test.BaseDbUnitTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class CodegenSQLParserTest extends BaseDbUnitTest {
|
||||
|
||||
@Test
|
||||
public void testParse() {
|
||||
String sql = "CREATE TABLE `infra_test_demo` (\n" +
|
||||
" `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '编号',\n" +
|
||||
" `name` varchar(100) NOT NULL DEFAULT '' COMMENT '名字',\n" +
|
||||
" `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '状态',\n" +
|
||||
" `type` tinyint(4) NOT NULL COMMENT '类型',\n" +
|
||||
" `category` tinyint(4) NOT NULL COMMENT '分类',\n" +
|
||||
" `remark` varchar(500) DEFAULT NULL COMMENT '备注',\n" +
|
||||
" `create_by` varchar(64) DEFAULT '' COMMENT '创建者',\n" +
|
||||
" `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n" +
|
||||
" `update_by` varchar(64) DEFAULT '' COMMENT '更新者',\n" +
|
||||
" `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n" +
|
||||
" `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n" +
|
||||
" PRIMARY KEY (`id`) USING BTREE\n" +
|
||||
") ENGINE=InnoDB AUTO_INCREMENT=108 DEFAULT CHARSET=utf8mb4 COMMENT='字典类型表';";
|
||||
CodegenSQLParser.parse(sql);
|
||||
// TODO 芋艿:后续完善断言
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
package cn.iocoder.yudao.module.infra.service.codegen;
|
||||
|
||||
import cn.iocoder.yudao.module.infra.test.BaseDbUnitTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
class CodegenServiceImplTest extends BaseDbUnitTest {
|
||||
|
||||
@Resource
|
||||
private CodegenServiceImpl codegenService;
|
||||
|
||||
@Test
|
||||
public void tetCreateCodegenTable() {
|
||||
codegenService.createCodegen(0L, "infra_test_demo");
|
||||
// infraCodegenService.createCodegenTable("infra_codegen_table");
|
||||
// infraCodegenService.createCodegen("infra_codegen_column");
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 占位
|
||||
*/
|
||||
package cn.iocoder.yudao.module.infra.service;
|
@@ -0,0 +1,38 @@
|
||||
package cn.iocoder.yudao.module.infra.test;
|
||||
|
||||
import cn.iocoder.yudao.framework.datasource.config.YudaoDataSourceAutoConfiguration;
|
||||
import cn.iocoder.yudao.framework.mybatis.config.YudaoMybatisAutoConfiguration;
|
||||
import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration;
|
||||
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration;
|
||||
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
|
||||
import org.redisson.spring.starter.RedissonAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseDbAndRedisIntegrationTest.Application.class)
|
||||
@ActiveProfiles("integration-test") // 设置使用 application-integration-test 配置文件
|
||||
public class BaseDbAndRedisIntegrationTest {
|
||||
|
||||
@Import({
|
||||
// DB 配置类
|
||||
DynamicDataSourceAutoConfiguration.class, // Dynamic Datasource 配置类
|
||||
YudaoDataSourceAutoConfiguration.class, // 自己的 DB 配置类
|
||||
DataSourceAutoConfiguration.class, // Spring DB 自动配置类
|
||||
DataSourceTransactionManagerAutoConfiguration.class, // Spring 事务自动配置类
|
||||
// MyBatis 配置类
|
||||
YudaoMybatisAutoConfiguration.class, // 自己的 MyBatis 配置类
|
||||
MybatisPlusAutoConfiguration.class, // MyBatis 的自动配置类
|
||||
|
||||
// Redis 配置类
|
||||
RedisAutoConfiguration.class, // Spring Redis 自动配置类
|
||||
YudaoRedisAutoConfiguration.class, // 自己的 Redis 配置类
|
||||
RedissonAutoConfiguration.class, // Redisson 自动高配置类
|
||||
})
|
||||
public static class Application {
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
package cn.iocoder.yudao.module.infra.test;
|
||||
|
||||
import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration;
|
||||
import org.redisson.spring.starter.RedissonAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseRedisIntegrationTest.Application.class)
|
||||
@ActiveProfiles("integration-test") // 设置使用 application-integration-test 配置文件
|
||||
public class BaseRedisIntegrationTest {
|
||||
|
||||
@Import({
|
||||
// Redis 配置类
|
||||
RedisAutoConfiguration.class, // Spring Redis 自动配置类
|
||||
YudaoRedisAutoConfiguration.class, // 自己的 Redis 配置类
|
||||
RedissonAutoConfiguration.class, // Redisson 自动高配置类
|
||||
})
|
||||
public static class Application {
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,187 @@
|
||||
package cn.iocoder.yudao.module.infra.service.test;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.test.vo.TestDemoExportReqVO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.test.TestDemoDO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.test.vo.TestDemoCreateReqVO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.test.vo.TestDemoPageReqVO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.test.vo.TestDemoUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.infra.dal.mysql.test.TestDemoMapper;
|
||||
import cn.iocoder.yudao.module.infra.test.BaseDbUnitTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.TEST_DEMO_NOT_EXISTS;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* {@link TestDemoServiceImpl} 的单元测试类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Import(TestDemoServiceImpl.class)
|
||||
public class TestDemoServiceImplTest extends BaseDbUnitTest {
|
||||
|
||||
@Resource
|
||||
private TestDemoServiceImpl testDemoService;
|
||||
|
||||
@Resource
|
||||
private TestDemoMapper testDemoMapper;
|
||||
|
||||
@Test
|
||||
public void testCreateTestDemo_success() {
|
||||
// 准备参数
|
||||
TestDemoCreateReqVO reqVO = randomPojo(TestDemoCreateReqVO.class);
|
||||
|
||||
// 调用
|
||||
Long testDemoId = testDemoService.createTestDemo(reqVO);
|
||||
// 断言
|
||||
assertNotNull(testDemoId);
|
||||
// 校验记录的属性是否正确
|
||||
TestDemoDO testDemo = testDemoMapper.selectById(testDemoId);
|
||||
assertPojoEquals(reqVO, testDemo);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateTestDemo_success() {
|
||||
// mock 数据
|
||||
TestDemoDO dbTestDemo = randomPojo(TestDemoDO.class);
|
||||
testDemoMapper.insert(dbTestDemo);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
TestDemoUpdateReqVO reqVO = randomPojo(TestDemoUpdateReqVO.class, o -> {
|
||||
o.setId(dbTestDemo.getId()); // 设置更新的 ID
|
||||
});
|
||||
|
||||
// 调用
|
||||
testDemoService.updateTestDemo(reqVO);
|
||||
// 校验是否更新正确
|
||||
TestDemoDO testDemo = testDemoMapper.selectById(reqVO.getId()); // 获取最新的
|
||||
assertPojoEquals(reqVO, testDemo);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateTestDemo_notExists() {
|
||||
// 准备参数
|
||||
TestDemoUpdateReqVO reqVO = randomPojo(TestDemoUpdateReqVO.class);
|
||||
|
||||
// 调用, 并断言异常
|
||||
assertServiceException(() -> testDemoService.updateTestDemo(reqVO), TEST_DEMO_NOT_EXISTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteTestDemo_success() {
|
||||
// mock 数据
|
||||
TestDemoDO dbTestDemo = randomPojo(TestDemoDO.class);
|
||||
testDemoMapper.insert(dbTestDemo);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
Long id = dbTestDemo.getId();
|
||||
|
||||
// 调用
|
||||
testDemoService.deleteTestDemo(id);
|
||||
// 校验数据不存在了
|
||||
assertNull(testDemoMapper.selectById(id));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteTestDemo_notExists() {
|
||||
// 准备参数
|
||||
Long id = randomLongId();
|
||||
|
||||
// 调用, 并断言异常
|
||||
assertServiceException(() -> testDemoService.deleteTestDemo(id), TEST_DEMO_NOT_EXISTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTestDemoPage() {
|
||||
// mock 数据
|
||||
TestDemoDO dbTestDemo = randomPojo(TestDemoDO.class, o -> { // 等会查询到
|
||||
o.setName("芋道源码");
|
||||
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
o.setType(1);
|
||||
o.setCategory(2);
|
||||
o.setRemark("哈哈哈");
|
||||
o.setCreateTime(DateUtils.buildTime(2021, 11, 11));
|
||||
});
|
||||
testDemoMapper.insert(dbTestDemo);
|
||||
// 测试 name 不匹配
|
||||
testDemoMapper.insert(cloneIgnoreId(dbTestDemo, o -> o.setName("不匹配")));
|
||||
// 测试 status 不匹配
|
||||
testDemoMapper.insert(cloneIgnoreId(dbTestDemo, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
|
||||
// 测试 type 不匹配
|
||||
testDemoMapper.insert(cloneIgnoreId(dbTestDemo, o -> o.setType(2)));
|
||||
// 测试 category 不匹配
|
||||
testDemoMapper.insert(cloneIgnoreId(dbTestDemo, o -> o.setCategory(1)));
|
||||
// 测试 remark 不匹配
|
||||
testDemoMapper.insert(cloneIgnoreId(dbTestDemo, o -> o.setRemark("呵呵呵")));
|
||||
// 测试 createTime 不匹配
|
||||
testDemoMapper.insert(cloneIgnoreId(dbTestDemo, o -> o.setCreateTime(DateUtils.buildTime(2021, 12, 12))));
|
||||
// 准备参数
|
||||
TestDemoPageReqVO reqVO = new TestDemoPageReqVO();
|
||||
reqVO.setName("芋道");
|
||||
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
reqVO.setType(1);
|
||||
reqVO.setCategory(2);
|
||||
reqVO.setRemark("哈哈哈");
|
||||
reqVO.setBeginCreateTime(DateUtils.buildTime(2021, 11, 10));
|
||||
reqVO.setEndCreateTime(DateUtils.buildTime(2021, 11, 12));
|
||||
|
||||
// 调用
|
||||
PageResult<TestDemoDO> pageResult = testDemoService.getTestDemoPage(reqVO);
|
||||
// 断言
|
||||
assertEquals(1, pageResult.getTotal());
|
||||
assertEquals(1, pageResult.getList().size());
|
||||
assertPojoEquals(dbTestDemo, pageResult.getList().get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTestDemoList() {
|
||||
// mock 数据
|
||||
TestDemoDO dbTestDemo = randomPojo(TestDemoDO.class, o -> { // 等会查询到
|
||||
o.setName("芋道源码");
|
||||
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
o.setType(1);
|
||||
o.setCategory(2);
|
||||
o.setRemark("哈哈哈");
|
||||
o.setCreateTime(DateUtils.buildTime(2021, 11, 11));
|
||||
});
|
||||
testDemoMapper.insert(dbTestDemo);
|
||||
// 测试 name 不匹配
|
||||
testDemoMapper.insert(cloneIgnoreId(dbTestDemo, o -> o.setName("不匹配")));
|
||||
// 测试 status 不匹配
|
||||
testDemoMapper.insert(cloneIgnoreId(dbTestDemo, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
|
||||
// 测试 type 不匹配
|
||||
testDemoMapper.insert(cloneIgnoreId(dbTestDemo, o -> o.setType(2)));
|
||||
// 测试 category 不匹配
|
||||
testDemoMapper.insert(cloneIgnoreId(dbTestDemo, o -> o.setCategory(1)));
|
||||
// 测试 remark 不匹配
|
||||
testDemoMapper.insert(cloneIgnoreId(dbTestDemo, o -> o.setRemark("呵呵呵")));
|
||||
// 测试 createTime 不匹配
|
||||
testDemoMapper.insert(cloneIgnoreId(dbTestDemo, o -> o.setCreateTime(DateUtils.buildTime(2021, 12, 12))));
|
||||
// 准备参数
|
||||
TestDemoExportReqVO reqVO = new TestDemoExportReqVO();
|
||||
reqVO.setName("芋道");
|
||||
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
reqVO.setType(1);
|
||||
reqVO.setCategory(2);
|
||||
reqVO.setRemark("哈哈哈");
|
||||
reqVO.setBeginCreateTime(DateUtils.buildTime(2021, 11, 10));
|
||||
reqVO.setEndCreateTime(DateUtils.buildTime(2021, 11, 12));
|
||||
|
||||
// 调用
|
||||
List<TestDemoDO> list = testDemoService.getTestDemoList(reqVO);
|
||||
// 断言
|
||||
assertEquals(1, list.size());
|
||||
assertPojoEquals(dbTestDemo, list.get(0));
|
||||
}
|
||||
|
||||
}
|
@@ -7,3 +7,4 @@ DELETE FROM "infra_api_error_log";
|
||||
DELETE FROM "infra_api_access_log";
|
||||
DELETE FROM "infra_file";
|
||||
DELETE FROM "infra_api_error_log";
|
||||
DELETE FROM "infra_test_demo";
|
||||
|
@@ -135,3 +135,18 @@ CREATE TABLE IF NOT EXISTS "infra_api_error_log" (
|
||||
"tenant_id" bigint not null default '0',
|
||||
primary key ("id")
|
||||
) COMMENT '系统异常日志';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "infra_test_demo" (
|
||||
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
"name" varchar(100) NOT NULL,
|
||||
"status" tinyint NOT NULL,
|
||||
"type" tinyint NOT NULL,
|
||||
"category" tinyint NOT NULL,
|
||||
"remark" varchar(500),
|
||||
"creator" varchar(64) DEFAULT '''',
|
||||
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updater" varchar(64) DEFAULT '''',
|
||||
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
"deleted" bit NOT NULL DEFAULT FALSE,
|
||||
PRIMARY KEY ("id")
|
||||
) COMMENT '字典类型表';
|
||||
|
Reference in New Issue
Block a user