package com.tianShu.hr.utils;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import lombok.Data;
import lombok.experimental.Accessors;
import java.sql.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
/**
* MyBatis Plus 完整代码生成器 v9.0 - 达梦数据库版
* Entity、VO、DTO 字段完全一致
* 支持单主键和复合主键
* 直接运行 main 方法即可
*
* @author chenLiTao
*/
public class CodeGeneratorDm {
// ==================== 数据库配置 ====================
private static final String DB_URL = "jdbc:dm://127.0.0.1:5236/hr_center?" +
"useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&" +
"serverTimezone=Asia/Shanghai&useSSL=false";
private static final String DB_USERNAME = "SYSDBA";
private static final String DB_PASSWORD = "SYSDBA";
private static final String DB_NAME = "HR_CENTER"; // 达梦数据库名称为大写
private static final String DB_SCHEMA = "HR_CENTER"; // 达梦模式名
// ==================== 基础配置 ====================
private static final String OUTPUT_DIR = System.getProperty("user.dir") + "/generated-code";
private static final String AUTHOR = "chenLiTao";
private static final String BASE_PACKAGE = "com.tianShu.hr";
private static final String MODULE_NAME = "";
private static final String TABLE_NAMES = "";
private static final String[] TABLE_PREFIX = {"t_", "sys_", "tb_"};
// ==================== 包路径配置 ====================
private static final String ENTITY_PACKAGE = "entity";
private static final String VO_PACKAGE = "param.vo";
private static final String DTO_PACKAGE = "param.dto";
private static final String MAPPER_PACKAGE = "mapper";
private static final String SERVICE_PACKAGE = "service";
private static final String SERVICE_IMPL_PACKAGE = "service.impl";
private static final String CONTROLLER_PACKAGE = "controller";
private static final String COMMON_PACKAGE = "common";
private static final String MAPPER_XML_DIR = "mapper";
// ==================== 类名后缀配置 ====================
private static final String ENTITY_SUFFIX = "";
private static final String VO_SUFFIX = "VO";
private static final String DTO_SUFFIX = "DTO";
private static final String MAPPER_SUFFIX = "Mapper";
private static final String SERVICE_SUFFIX = "Service";
private static final String SERVICE_IMPL_SUFFIX = "ServiceImpl";
private static final String CONTROLLER_SUFFIX = "Controller";
// ==================== Controller 路径配置 ====================
private static final String CONTROLLER_BASE_PATH = "";
private static final boolean INCLUDE_MODULE_IN_PATH = true;
private static final boolean USE_TABLE_NAME_AS_PATH = false;
// ==================== 生成开关 ====================
private static final boolean GEN_ENTITY = true;
private static final boolean GEN_VO = true;
private static final boolean GEN_DTO = true;
private static final boolean GEN_MAPPER = true;
private static final boolean GEN_MAPPER_XML = true;
private static final boolean GEN_SERVICE = true;
private static final boolean GEN_SERVICE_IMPL = true;
private static final boolean GEN_CONTROLLER = true;
private static final boolean GEN_RESULT = true;
private static final boolean GEN_PAGE_DTO = true;
// ==================== Result配置 ====================
private static final String RESULT_CLASS = BASE_PACKAGE + "." + COMMON_PACKAGE + ".Result";
private static final String RESULT_CLASS_NAME = "Result";
// ==================== 逻辑删除/创建更新字段 ====================
private static final String[] LOGIC_DELETE_FIELDS = {"is_deleted", "deleted", "del_flag", "is_del"};
private static final String[] CREATE_UPDATE_FIELDS = {"create_time", "update_time", "created_at",
"updated_at", "create_date", "update_date", "gmt_create", "gmt_modified",
"create_user", "update_user", "creater", "updater"};
// ==================== 批量插入配置 ====================
private static final int BATCH_SIZE = 500;
// ==================== main ====================
public static void main(String[] args) {
System.out.println("\n============================================");
System.out.println(" MyBatis Plus 代码生成器 v9.0 - 达梦数据库版");
System.out.println(" 支持单主键/复合主键");
System.out.println("============================================\n");
System.out.println("数据库: " + DB_NAME);
System.out.println("输出目录: " + OUTPUT_DIR);
System.out.println("基础包名: " + BASE_PACKAGE);
System.out.println("模块名: " + MODULE_NAME);
System.out.println("Controller路径: " + getControllerPath("{table}"));
System.out.println("生成表: " + (StrUtil.isBlank(TABLE_NAMES) ? "全部表" : TABLE_NAMES));
System.out.println();
try {
testConnection();
List<TableInfo> tables = resolveTables();
if (tables.isEmpty()) {
System.out.println("没有找到任何有效的表!");
return;
}
FileUtil.del(OUTPUT_DIR);
System.out.println("已清理旧输出目录\n");
if (GEN_RESULT) {
generateResult();
}
if (GEN_PAGE_DTO) {
generatePageDTO();
}
int successCount = 0;
for (TableInfo table : tables) {
try {
generateAll(table);
successCount++;
} catch (Exception e) {
System.err.println("❌ 生成表 [" + table.getTableName() + "] 失败: " + e.getMessage());
e.printStackTrace();
}
}
System.out.println("\n============================================");
System.out.println(" ✅ 代码生成完成!成功: " + successCount + " 张表");
System.out.println(" 输出目录: " + OUTPUT_DIR);
System.out.println("============================================");
} catch (Exception e) {
System.err.println("\n❌ 代码生成失败: " + e.getMessage());
System.err.println("请检查: 1.达梦数据库是否启动 2.数据库'" + DB_NAME + "'是否存在 3.用户名密码是否正确");
e.printStackTrace();
}
}
// ==================== 数据库 ====================
private static void testConnection() throws SQLException {
System.out.print("测试数据库连接... ");
try (Connection conn = getConnection()) {
DatabaseMetaData metaData = conn.getMetaData();
System.out.println("✅ 成功 (达梦数据库 " + metaData.getDatabaseProductVersion() + ")");
}
}
private static Connection getConnection() throws SQLException {
try {
Class.forName("dm.jdbc.driver.DmDriver");
} catch (ClassNotFoundException e) {
throw new SQLException("达梦数据库驱动未找到,请确保已添加DmJdbcDriver依赖", e);
}
return DriverManager.getConnection(DB_URL, DB_USERNAME, DB_PASSWORD);
}
private static List<TableInfo> resolveTables() throws SQLException {
if (StrUtil.isBlank(TABLE_NAMES)) {
return getAllTables();
}
List<TableInfo> tables = new ArrayList<>();
List<TableInfo> allTables = getAllTables();
for (String name : TABLE_NAMES.split(",")) {
String trimmed = name.trim();
if (StrUtil.isBlank(trimmed)) {
continue;
}
TableInfo table = allTables.stream()
.filter(t -> t.getTableName().equalsIgnoreCase(trimmed))
.findFirst()
.orElse(null);
if (table != null) {
tables.add(table);
} else {
System.out.println("⚠ 表 [" + trimmed + "] 不存在");
}
}
return tables;
}
private static List<TableInfo> getAllTables() throws SQLException {
List<TableInfo> tables = new ArrayList<>();
// 达梦数据库查询表信息
String sql = "SELECT TABLE_NAME, COMMENTS AS TABLE_COMMENT " +
"FROM ALL_TAB_COMMENTS " +
"WHERE OWNER = ? AND TABLE_TYPE = 'TABLE'";
try (Connection conn = getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, DB_SCHEMA);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
TableInfo t = new TableInfo();
t.setTableName(rs.getString("TABLE_NAME"));
t.setTableComment(rs.getString("TABLE_COMMENT"));
t.setClassName(tableNameToClassName(t.getTableName()));
t.setVarName(tableNameToVarName(t.getTableName()));
t.setColumns(getTableColumns(t.getTableName()));
tables.add(t);
}
}
}
return tables;
}
private static List<ColumnInfo> getTableColumns(String tableName) throws SQLException {
List<ColumnInfo> columns = new ArrayList<>();
// 达梦数据库查询列信息
String sql = "SELECT " +
"C.COLUMN_NAME, " +
"C.DATA_TYPE, " +
"CM.COMMENTS AS COLUMN_COMMENT, " +
"C.NULLABLE, " +
"C.DATA_DEFAULT AS COLUMN_DEFAULT, " +
"C.DATA_LENGTH AS CHARACTER_MAXIMUM_LENGTH, " +
"CASE WHEN PK.COLUMN_NAME IS NOT NULL THEN 'PRI' ELSE '' END AS COLUMN_KEY " +
"FROM ALL_TAB_COLUMNS C " +
"LEFT JOIN ALL_COL_COMMENTS CM ON C.OWNER = CM.OWNER AND C.TABLE_NAME = CM.TABLE_NAME AND C.COLUMN_NAME = CM.COLUMN_NAME " +
"LEFT JOIN ( " +
" SELECT CC.OWNER, CC.TABLE_NAME, CC.COLUMN_NAME " +
" FROM ALL_CONS_COLUMNS CC " +
" JOIN ALL_CONSTRAINTS C ON CC.OWNER = C.OWNER AND CC.CONSTRAINT_NAME = C.CONSTRAINT_NAME " +
" WHERE C.CONSTRAINT_TYPE = 'P' " +
") PK ON C.OWNER = PK.OWNER AND C.TABLE_NAME = PK.TABLE_NAME AND C.COLUMN_NAME = PK.COLUMN_NAME " +
"WHERE C.OWNER = ? AND C.TABLE_NAME = ? " +
"ORDER BY C.COLUMN_ID";
try (Connection conn = getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, DB_SCHEMA);
ps.setString(2, tableName);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
ColumnInfo col = new ColumnInfo();
String colName = rs.getString("COLUMN_NAME");
col.setColumnName(colName);
col.setDataType(rs.getString("DATA_TYPE"));
col.setColumnComment(rs.getString("COLUMN_COMMENT"));
col.setNullable("Y".equals(rs.getString("NULLABLE")));
col.setColumnDefault(rs.getString("COLUMN_DEFAULT"));
col.setMaxLength(rs.getInt("CHARACTER_MAXIMUM_LENGTH"));
col.setPrimaryKey("PRI".equals(rs.getString("COLUMN_KEY")));
col.setJavaType(dmDbTypeToJavaType(col.getDataType(), col.getMaxLength()));
col.setJavaFieldName(StrUtil.toCamelCase(colName.toLowerCase()));
columns.add(col);
}
}
}
return columns;
}
// ==================== 代码生成入口 ====================
private static void generateAll(TableInfo table) {
System.out.println("\n▶ 生成表 [" + table.getTableName() + "] (" + getTableComment(table) + ")");
PrimaryKeyInfo pkInfo = getPrimaryKeyInfo(table);
if (pkInfo.isComposite()) {
System.out.println(" 复合主键: " + pkInfo.getColumnNames());
} else {
System.out.println(" 主键: " + pkInfo.getPrimaryKeyField());
}
if (table.getColumns() == null || table.getColumns().isEmpty()) {
System.out.println(" ⚠ 无列信息,跳过");
return;
}
if (GEN_ENTITY) {
generateEntity(table);
}
if (GEN_VO) {
generateVO(table);
}
if (GEN_DTO) {
generateDTO(table);
}
if (GEN_MAPPER) {
generateMapper(table);
}
if (GEN_MAPPER_XML) {
generateMapperXml(table);
}
if (GEN_SERVICE) {
generateService(table);
}
if (GEN_SERVICE_IMPL) {
generateServiceImpl(table);
}
if (GEN_CONTROLLER) {
generateController(table);
}
System.out.println(" ✔ 完成");
}
// ==================== Result ====================
private static void generateResult() {
StringBuilder sb = new StringBuilder();
sb.append("package ").append(getFullPackage(COMMON_PACKAGE)).append(";\n\n");
sb.append("import io.swagger.annotations.ApiModel;\n");
sb.append("import io.swagger.annotations.ApiModelProperty;\n");
sb.append("import lombok.AllArgsConstructor;\n");
sb.append("import lombok.Data;\n");
sb.append("import lombok.NoArgsConstructor;\n\n");
sb.append("@Data\n");
sb.append("@NoArgsConstructor\n");
sb.append("@AllArgsConstructor\n");
sb.append("@ApiModel(\"统一返回结果\")\n");
sb.append("public class Result<T> {\n\n");
sb.append(" @ApiModelProperty(\"状态码\")\n");
sb.append(" private Integer code;\n\n");
sb.append(" @ApiModelProperty(\"消息\")\n");
sb.append(" private String message;\n\n");
sb.append(" @ApiModelProperty(\"数据\")\n");
sb.append(" private T data;\n\n");
sb.append(" @ApiModelProperty(\"时间戳\")\n");
sb.append(" private Long timestamp;\n\n");
sb.append(" public static <T> Result<T> success() {\n");
sb.append(" return new Result<>(200, \"操作成功\", null, System.currentTimeMillis());\n");
sb.append(" }\n\n");
sb.append(" public static <T> Result<T> success(T data) {\n");
sb.append(" return new Result<>(200, \"操作成功\", data, System.currentTimeMillis());\n");
sb.append(" }\n\n");
sb.append(" public static <T> Result<T> success(String msg, T data) {\n");
sb.append(" return new Result<>(200, msg, data, System.currentTimeMillis());\n");
sb.append(" }\n\n");
sb.append(" public static <T> Result<T> error() {\n");
sb.append(" return new Result<>(500, \"操作失败\", null, System.currentTimeMillis());\n");
sb.append(" }\n\n");
sb.append(" public static <T> Result<T> error(String msg) {\n");
sb.append(" return new Result<>(500, msg, null, System.currentTimeMillis());\n");
sb.append(" }\n\n");
sb.append(" public static <T> Result<T> error(Integer code, String msg) {\n");
sb.append(" return new Result<>(code, msg, null, System.currentTimeMillis());\n");
sb.append(" }\n");
sb.append("}\n");
writeCommonFile(COMMON_PACKAGE, "Result.java", sb.toString());
}
// ==================== PageDTO ====================
private static void generatePageDTO() {
StringBuilder sb = new StringBuilder();
sb.append("package ").append(getFullPackage(DTO_PACKAGE)).append(";\n\n");
sb.append("import io.swagger.annotations.ApiModel;\n");
sb.append("import io.swagger.annotations.ApiModelProperty;\n");
sb.append("import lombok.Data;\n\n");
sb.append("@Data\n");
sb.append("@ApiModel(\"分页请求基类\")\n");
sb.append("public class PageDTO {\n\n");
sb.append(" @ApiModelProperty(value = \"当前页\", example = \"1\")\n");
sb.append(" private Long pageNum = 1L;\n\n");
sb.append(" @ApiModelProperty(value = \"每页大小\", example = \"10\")\n");
sb.append(" private Long pageSize = 10L;\n\n");
sb.append(" @ApiModelProperty(value = \"排序字段\", example = \"id\")\n");
sb.append(" private String orderBy;\n\n");
sb.append(" @ApiModelProperty(value = \"是否升序\", example = \"false\")\n");
sb.append(" private Boolean isAsc = false;\n");
sb.append("}\n");
writeCommonFile(DTO_PACKAGE, "PageDTO.java", sb.toString());
}
// ==================== Entity ====================
private static void generateEntity(TableInfo table) {
String className = table.getClassName() + ENTITY_SUFFIX;
StringBuilder sb = new StringBuilder();
sb.append("package ").append(getFullPackage(ENTITY_PACKAGE)).append(";\n\n");
sb.append("import com.baomidou.mybatisplus.annotation.*;\n");
sb.append("import io.swagger.annotations.ApiModel;\n");
sb.append("import io.swagger.annotations.ApiModelProperty;\n");
sb.append("import lombok.AllArgsConstructor;\n");
sb.append("import lombok.Builder;\n");
sb.append("import lombok.Data;\n");
sb.append("import lombok.NoArgsConstructor;\n");
sb.append("import lombok.EqualsAndHashCode;\n");
sb.append("import lombok.experimental.Accessors;\n");
boolean hasDateTime = false;
boolean hasBigDecimal = false;
for (ColumnInfo col : table.getColumns()) {
if ("LocalDateTime".equals(col.getJavaType())) {
hasDateTime = true;
}
if ("BigDecimal".equals(col.getJavaType())) {
hasBigDecimal = true;
}
}
if (hasDateTime) {
sb.append("import java.time.LocalDateTime;\n");
sb.append("import com.fasterxml.jackson.annotation.JsonFormat;\n");
}
if (hasBigDecimal) {
sb.append("import java.math.BigDecimal;\n");
}
sb.append("\n/**\n");
sb.append(" * ").append(getTableComment(table)).append("实体类\n");
sb.append(" *\n");
sb.append(" * @author ").append(AUTHOR).append("\n");
sb.append(" */\n");
sb.append("@Data\n");
sb.append("@Builder\n");
sb.append("@NoArgsConstructor\n");
sb.append("@AllArgsConstructor\n");
sb.append("@EqualsAndHashCode(callSuper = false)\n");
sb.append("@Accessors(chain = true)\n");
sb.append("@TableName(\"").append(table.getTableName()).append("\")\n");
sb.append("@ApiModel(value = \"").append(className).append("\", description = \"").append(getTableComment(table)).append("\")\n");
sb.append("public class ").append(className).append(" {\n\n");
PrimaryKeyInfo pkInfo = getPrimaryKeyInfo(table);
for (ColumnInfo col : table.getColumns()) {
String comment = StrUtil.isNotBlank(col.getColumnComment()) ? col.getColumnComment() : col.getJavaFieldName();
sb.append(" @ApiModelProperty(value = \"").append(comment).append("\"");
String example = getExampleValue(col);
if (example != null) {
sb.append(", example = \"").append(example).append("\"");
}
if (!col.isNullable() && !col.isPrimaryKey()) {
sb.append(", required = true");
}
sb.append(")\n");
if (col.isPrimaryKey()) {
if (!pkInfo.isComposite()) {
String pkType = col.getJavaType();
if ("String".equals(pkType)) {
sb.append(" @TableId(value = \"").append(col.getColumnName()).append("\", type = IdType.ASSIGN_ID)\n");
} else if ("Long".equals(pkType) || "Integer".equals(pkType)) {
sb.append(" @TableId(value = \"").append(col.getColumnName()).append("\", type = IdType.AUTO)\n");
} else {
sb.append(" @TableId(value = \"").append(col.getColumnName()).append("\", type = IdType.ASSIGN_ID)\n");
}
} else {
sb.append(" @TableId(value = \"").append(col.getColumnName()).append("\")\n");
}
} else if (isLogicDeleteField(col.getColumnName())) {
sb.append(" @TableLogic\n");
sb.append(" @TableField(\"").append(col.getColumnName()).append("\")\n");
} else {
sb.append(" @TableField(\"").append(col.getColumnName()).append("\")\n");
}
if ("LocalDateTime".equals(col.getJavaType())) {
sb.append(" @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\", timezone = \"GMT+8\")\n");
}
sb.append(" private ").append(col.getJavaType()).append(" ").append(col.getJavaFieldName()).append(";\n\n");
}
sb.append("}\n");
writeFile(ENTITY_PACKAGE, className + ".java", sb.toString());
}
// ==================== VO ====================
private static void generateVO(TableInfo table) {
String className = table.getClassName() + VO_SUFFIX;
StringBuilder sb = new StringBuilder();
sb.append("package ").append(getFullPackage(VO_PACKAGE)).append(";\n\n");
sb.append("import com.fasterxml.jackson.annotation.JsonFormat;\n");
sb.append("import io.swagger.annotations.ApiModel;\n");
sb.append("import io.swagger.annotations.ApiModelProperty;\n");
sb.append("import lombok.AllArgsConstructor;\n");
sb.append("import lombok.Builder;\n");
sb.append("import lombok.Data;\n");
sb.append("import lombok.NoArgsConstructor;\n\n");
boolean hasDateTime = false;
boolean hasBigDecimal = false;
for (ColumnInfo col : table.getColumns()) {
if ("LocalDateTime".equals(col.getJavaType())) {
hasDateTime = true;
}
if ("BigDecimal".equals(col.getJavaType())) {
hasBigDecimal = true;
}
}
if (hasDateTime) {
sb.append("import java.time.LocalDateTime;\n");
}
if (hasBigDecimal) {
sb.append("import java.math.BigDecimal;\n");
}
sb.append("\n/**\n");
sb.append(" * ").append(getTableComment(table)).append("视图对象\n");
sb.append(" *\n");
sb.append(" * @author ").append(AUTHOR).append("\n");
sb.append(" */\n");
sb.append("@Data\n");
sb.append("@Builder\n");
sb.append("@NoArgsConstructor\n");
sb.append("@AllArgsConstructor\n");
sb.append("@ApiModel(value = \"").append(className).append("\", description = \"").append(getTableComment(table)).append("视图对象\")\n");
sb.append("public class ").append(className).append(" {\n\n");
for (ColumnInfo col : table.getColumns()) {
String comment = StrUtil.isNotBlank(col.getColumnComment()) ? col.getColumnComment() : col.getJavaFieldName();
sb.append(" @ApiModelProperty(value = \"").append(comment).append("\"");
String example = getExampleValue(col);
if (example != null) {
sb.append(", example = \"").append(example).append("\"");
}
sb.append(")\n");
if ("LocalDateTime".equals(col.getJavaType())) {
sb.append(" @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\", timezone = \"GMT+8\")\n");
}
sb.append(" private ").append(col.getJavaType()).append(" ").append(col.getJavaFieldName()).append(";\n\n");
}
sb.append("}\n");
writeFile(VO_PACKAGE, className + ".java", sb.toString());
}
// ==================== DTO ====================
private static void generateDTO(TableInfo table) {
String className = table.getClassName() + DTO_SUFFIX;
StringBuilder sb = new StringBuilder();
sb.append("package ").append(getFullPackage(DTO_PACKAGE)).append(";\n\n");
sb.append("import com.fasterxml.jackson.annotation.JsonFormat;\n");
sb.append("import io.swagger.annotations.ApiModel;\n");
sb.append("import io.swagger.annotations.ApiModelProperty;\n");
sb.append("import lombok.Data;\n");
sb.append("import lombok.EqualsAndHashCode;\n\n");
boolean hasDateTime = false;
boolean hasBigDecimal = false;
for (ColumnInfo col : table.getColumns()) {
if ("LocalDateTime".equals(col.getJavaType())) {
hasDateTime = true;
}
if ("BigDecimal".equals(col.getJavaType())) {
hasBigDecimal = true;
}
}
if (hasDateTime) {
sb.append("import java.time.LocalDateTime;\n");
}
if (hasBigDecimal) {
sb.append("import java.math.BigDecimal;\n");
}
if (hasDateTime || hasBigDecimal) {
sb.append("\n");
}
sb.append("/**\n");
sb.append(" * ").append(getTableComment(table)).append("DTO\n");
sb.append(" * - 新增时主键为空(复合主键需要手动传入)\n");
sb.append(" * - 修改时主键必传\n");
sb.append(" * - 查询时传条件字段\n");
sb.append(" *\n");
sb.append(" * @author ").append(AUTHOR).append("\n");
sb.append(" */\n");
sb.append("@Data\n");
sb.append("@EqualsAndHashCode(callSuper = true)\n");
sb.append("@ApiModel(value = \"").append(className).append("\", description = \"").append(getTableComment(table)).append("请求DTO\")\n");
sb.append("public class ").append(className).append(" extends PageDTO {\n\n");
for (ColumnInfo col : table.getColumns()) {
String comment = StrUtil.isNotBlank(col.getColumnComment()) ? col.getColumnComment() : col.getJavaFieldName();
sb.append(" @ApiModelProperty(value = \"").append(comment).append("\"");
if (col.isPrimaryKey()) {
sb.append(", notes = \"修改时必填\"");
}
String example = getExampleValue(col);
if (example != null) {
sb.append(", example = \"").append(example).append("\"");
}
if (!col.isNullable() && !col.isPrimaryKey()) {
sb.append(", required = true");
}
sb.append(")\n");
if ("LocalDateTime".equals(col.getJavaType())) {
sb.append(" @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\", timezone = \"GMT+8\")\n");
}
sb.append(" private ").append(col.getJavaType()).append(" ").append(col.getJavaFieldName()).append(";\n\n");
}
sb.append("}\n");
writeFile(DTO_PACKAGE, className + ".java", sb.toString());
}
// ==================== Mapper ====================
private static void generateMapper(TableInfo table) {
String className = table.getClassName() + MAPPER_SUFFIX;
String entityClass = table.getClassName() + ENTITY_SUFFIX;
String voClass = table.getClassName() + VO_SUFFIX;
String dtoClass = table.getClassName() + DTO_SUFFIX;
StringBuilder sb = new StringBuilder();
sb.append("package ").append(getFullPackage(MAPPER_PACKAGE)).append(";\n\n");
sb.append("import com.baomidou.mybatisplus.core.mapper.BaseMapper;\n");
sb.append("import com.baomidou.mybatisplus.core.metadata.IPage;\n");
sb.append("import com.baomidou.mybatisplus.extension.plugins.pagination.Page;\n");
sb.append("import ").append(getFullClassName(ENTITY_PACKAGE, entityClass)).append(";\n");
sb.append("import ").append(getFullClassName(VO_PACKAGE, voClass)).append(";\n");
sb.append("import ").append(getFullClassName(DTO_PACKAGE, dtoClass)).append(";\n");
sb.append("import org.apache.ibatis.annotations.Mapper;\n");
sb.append("import org.apache.ibatis.annotations.Param;\n\n");
sb.append("import java.util.List;\n\n");
sb.append("@Mapper\n");
sb.append("public interface ").append(className).append(" extends BaseMapper<").append(entityClass).append("> {\n\n");
sb.append(" /**\n");
sb.append(" * 分页查询VO\n");
sb.append(" *\n");
sb.append(" * @param page 分页对象\n");
sb.append(" * @param query 查询条件\n");
sb.append(" * @return 分页结果\n");
sb.append(" */\n");
sb.append(" IPage<").append(voClass).append("> selectPageVO(Page<").append(voClass).append("> page, @Param(\"query\") ").append(dtoClass).append(" query);\n\n");
sb.append(" /**\n");
sb.append(" * 查询列表VO\n");
sb.append(" *\n");
sb.append(" * @param query 查询条件\n");
sb.append(" * @return 列表结果\n");
sb.append(" */\n");
sb.append(" List<").append(voClass).append("> selectListVO(@Param(\"query\") ").append(dtoClass).append(" query);\n\n");
sb.append(" /**\n");
sb.append(" * 批量插入\n");
sb.append(" *\n");
sb.append(" * @param list 实体列表\n");
sb.append(" * @return 影响行数\n");
sb.append(" */\n");
sb.append(" int insertBatch(@Param(\"list\") List<").append(entityClass).append("> list);\n\n");
sb.append("}\n");
writeFile(MAPPER_PACKAGE, className + ".java", sb.toString());
}
// ==================== Mapper XML ====================
private static void generateMapperXml(TableInfo table) {
String mapperClass = table.getClassName() + MAPPER_SUFFIX;
String voClass = table.getClassName() + VO_SUFFIX;
StringBuilder sb = new StringBuilder();
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
sb.append("<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n");
sb.append("<mapper namespace=\"").append(getFullPackage(MAPPER_PACKAGE)).append(".").append(mapperClass).append("\">\n\n");
// 基础字段列表
sb.append(" <sql id=\"Base_Column_List\">\n");
sb.append(" ");
List<String> cols = new ArrayList<>();
for (ColumnInfo col : table.getColumns()) {
// 达梦数据库列名默认大写,使用双引号包裹
cols.add("\"" + col.getColumnName() + "\"");
}
sb.append(String.join(", ", cols)).append("\n");
sb.append(" </sql>\n\n");
// 通用查询条件
sb.append(" <sql id=\"base\">\n");
sb.append(" <where>\n");
for (ColumnInfo col : table.getColumns()) {
if (isLogicDeleteField(col.getColumnName())) {
sb.append(" AND \"").append(col.getColumnName()).append("\" = 0\n");
break;
}
}
for (ColumnInfo col : table.getColumns()) {
if (isLogicDeleteField(col.getColumnName()) || isCreateUpdateField(col.getColumnName())) {
continue;
}
if ("String".equals(col.getJavaType())) {
sb.append(" <if test=\"query.").append(col.getJavaFieldName()).append(" != null and query.").append(col.getJavaFieldName()).append(" != ''\">\n");
// 达梦数据库使用 CONCAT 函数进行模糊查询
sb.append(" AND \"").append(col.getColumnName()).append("\" LIKE CONCAT('%', #{query.").append(col.getJavaFieldName()).append("}, '%')\n");
sb.append(" </if>\n");
} else {
sb.append(" <if test=\"query.").append(col.getJavaFieldName()).append(" != null\">\n");
sb.append(" AND \"").append(col.getColumnName()).append("\" = #{query.").append(col.getJavaFieldName()).append("}\n");
sb.append(" </if>\n");
}
}
sb.append(" <if test=\"query.orderBy != null and query.orderBy != ''\">\n");
sb.append(" ORDER BY \"${\"query.orderBy\"}\"\n");
sb.append(" <if test=\"query.isAsc != null and query.isAsc\">\n");
sb.append(" ASC\n");
sb.append(" </if>\n");
sb.append(" <if test=\"query.isAsc != null and !query.isAsc\">\n");
sb.append(" DESC\n");
sb.append(" </if>\n");
sb.append(" </if>\n");
sb.append(" </where>\n");
sb.append(" </sql>\n\n");
sb.append(" <resultMap id=\"VOMap\" type=\"").append(getFullClassName(VO_PACKAGE, voClass)).append("\">\n");
for (ColumnInfo col : table.getColumns()) {
if (col.isPrimaryKey()) {
sb.append(" <id column=\"").append(col.getColumnName()).append("\" property=\"").append(col.getJavaFieldName()).append("\"/>\n");
}
}
for (ColumnInfo col : table.getColumns()) {
if (!col.isPrimaryKey()) {
sb.append(" <result column=\"").append(col.getColumnName()).append("\" property=\"").append(col.getJavaFieldName()).append("\"/>\n");
}
}
sb.append(" </resultMap>\n\n");
// 分页查询
sb.append(" <select id=\"selectPageVO\" resultMap=\"VOMap\">\n");
sb.append(" SELECT <include refid=\"Base_Column_List\"/>\n");
sb.append(" FROM \"").append(DB_SCHEMA).append("\".\"").append(table.getTableName()).append("\"\n");
sb.append(" <include refid=\"base\"/>\n");
sb.append(" </select>\n\n");
// 列表查询
sb.append(" <select id=\"selectListVO\" resultMap=\"VOMap\">\n");
sb.append(" SELECT <include refid=\"Base_Column_List\"/>\n");
sb.append(" FROM \"").append(DB_SCHEMA).append("\".\"").append(table.getTableName()).append("\"\n");
sb.append(" <include refid=\"base\"/>\n");
sb.append(" </select>\n\n");
// 批量插入 - 达梦数据库批量插入语法
sb.append(" <insert id=\"insertBatch\">\n");
sb.append(" INSERT INTO \"").append(DB_SCHEMA).append("\".\"").append(table.getTableName()).append("\" (\n");
sb.append(" ");
List<String> insertCols = new ArrayList<>();
for (ColumnInfo col : table.getColumns()) {
insertCols.add("\"" + col.getColumnName() + "\"");
}
sb.append(String.join(", ", insertCols)).append("\n");
sb.append(" )\n");
sb.append(" <foreach collection=\"list\" item=\"item\" separator=\"UNION ALL\">\n");
sb.append(" SELECT\n");
sb.append(" ");
List<String> insertVals = new ArrayList<>();
for (ColumnInfo col : table.getColumns()) {
insertVals.add("#{item." + col.getJavaFieldName() + "} AS \"" + col.getColumnName() + "\"");
}
sb.append(String.join(", ", insertVals)).append("\n");
sb.append(" FROM DUAL\n");
sb.append(" </foreach>\n");
sb.append(" </insert>\n\n");
sb.append("</mapper>\n");
writeXmlFile(MAPPER_XML_DIR, mapperClass + ".xml", sb.toString());
}
// ==================== Service ====================
private static void generateService(TableInfo table) {
String className = table.getClassName() + SERVICE_SUFFIX;
String entityClass = table.getClassName() + ENTITY_SUFFIX;
String voClass = table.getClassName() + VO_SUFFIX;
String dtoClass = table.getClassName() + DTO_SUFFIX;
PrimaryKeyInfo pkInfo = getPrimaryKeyInfo(table);
String pkType = getPrimaryKeyType(table);
String pkField = getPrimaryKeyFieldName(table);
StringBuilder sb = new StringBuilder();
sb.append("package ").append(getFullPackage(SERVICE_PACKAGE)).append(";\n\n");
sb.append("import com.baomidou.mybatisplus.core.metadata.IPage;\n");
sb.append("import com.baomidou.mybatisplus.extension.service.IService;\n");
sb.append("import ").append(getFullClassName(ENTITY_PACKAGE, entityClass)).append(";\n");
sb.append("import ").append(getFullClassName(VO_PACKAGE, voClass)).append(";\n");
sb.append("import ").append(getFullClassName(DTO_PACKAGE, dtoClass)).append(";\n\n");
sb.append("import java.util.List;\n");
if (pkInfo.isComposite()) {
sb.append("import java.util.Map;\n");
}
sb.append("\n");
sb.append("/**\n");
sb.append(" * ").append(getTableComment(table)).append("服务接口\n");
sb.append(" *\n");
sb.append(" * @author ").append(AUTHOR).append("\n");
sb.append(" */\n");
sb.append("public interface ").append(className).append(" extends IService<").append(entityClass).append("> {\n\n");
sb.append(" /**\n");
sb.append(" * 分页查询VO\n");
sb.append(" *\n");
sb.append(" * @param dto 查询条件\n");
sb.append(" * @return 分页结果\n");
sb.append(" */\n");
sb.append(" IPage<").append(voClass).append("> pageVO(").append(dtoClass).append(" dto);\n\n");
sb.append(" /**\n");
sb.append(" * 查询列表VO\n");
sb.append(" *\n");
sb.append(" * @param dto 查询条件\n");
sb.append(" * @return 列表结果\n");
sb.append(" */\n");
sb.append(" List<").append(voClass).append("> listVO(").append(dtoClass).append(" dto);\n\n");
if (pkInfo.isComposite()) {
sb.append(" /**\n");
sb.append(" * 根据主键查询VO(复合主键)\n");
sb.append(" *\n");
sb.append(" * @param pkMap 主键Map,key为字段名,value为字段值\n");
sb.append(" * @return VO对象\n");
sb.append(" */\n");
sb.append(" ").append(voClass).append(" getVOById(Map<String, Object> pkMap);\n\n");
} else {
sb.append(" /**\n");
sb.append(" * 根据主键查询VO\n");
sb.append(" *\n");
sb.append(" * @param ").append(pkField).append(" 主键ID\n");
sb.append(" * @return VO对象\n");
sb.append(" */\n");
sb.append(" ").append(voClass).append(" getVOById(").append(pkType).append(" ").append(pkField).append(");\n\n");
}
sb.append(" /**\n");
sb.append(" * 新增\n");
sb.append(" *\n");
sb.append(" * @param dto DTO对象\n");
sb.append(" * @return 是否成功\n");
sb.append(" */\n");
sb.append(" Boolean add(").append(dtoClass).append(" dto);\n\n");
sb.append(" /**\n");
sb.append(" * 批量新增\n");
sb.append(" *\n");
sb.append(" * @param dtoList DTO列表\n");
sb.append(" * @return 是否成功\n");
sb.append(" */\n");
sb.append(" Boolean batchAdd(List<").append(dtoClass).append("> dtoList);\n\n");
sb.append(" /**\n");
sb.append(" * 修改\n");
sb.append(" *\n");
sb.append(" * @param dto DTO对象\n");
sb.append(" * @return 是否成功\n");
sb.append(" */\n");
sb.append(" Boolean update(").append(dtoClass).append(" dto);\n\n");
if (pkInfo.isComposite()) {
sb.append(" /**\n");
sb.append(" * 根据主键删除(复合主键)\n");
sb.append(" *\n");
sb.append(" * @param pkMap 主键Map,key为字段名,value为字段值\n");
sb.append(" * @return 是否成功\n");
sb.append(" */\n");
sb.append(" Boolean deleteById(Map<String, Object> pkMap);\n\n");
} else {
sb.append(" /**\n");
sb.append(" * 根据主键删除\n");
sb.append(" *\n");
sb.append(" * @param ").append(pkField).append(" 主键ID\n");
sb.append(" * @return 是否成功\n");
sb.append(" */\n");
sb.append(" Boolean deleteById(").append(pkType).append(" ").append(pkField).append(");\n\n");
}
sb.append("}\n");
writeFile(SERVICE_PACKAGE, className + ".java", sb.toString());
}
// ==================== ServiceImpl ====================
private static void generateServiceImpl(TableInfo table) {
String className = table.getClassName() + SERVICE_IMPL_SUFFIX;
String serviceClass = table.getClassName() + SERVICE_SUFFIX;
String mapperClass = table.getClassName() + MAPPER_SUFFIX;
String entityClass = table.getClassName() + ENTITY_SUFFIX;
String voClass = table.getClassName() + VO_SUFFIX;
String dtoClass = table.getClassName() + DTO_SUFFIX;
PrimaryKeyInfo pkInfo = getPrimaryKeyInfo(table);
String pkType = getPrimaryKeyType(table);
String pkField = getPrimaryKeyFieldName(table);
String logicDeleteField = null;
for (ColumnInfo col : table.getColumns()) {
if (isLogicDeleteField(col.getColumnName())) {
logicDeleteField = col.getJavaFieldName();
break;
}
}
StringBuilder sb = new StringBuilder();
sb.append("package ").append(getFullPackage(SERVICE_IMPL_PACKAGE)).append(";\n\n");
sb.append("import cn.hutool.core.bean.BeanUtil;\n");
sb.append("import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\n");
sb.append("import com.baomidou.mybatisplus.core.metadata.IPage;\n");
sb.append("import com.baomidou.mybatisplus.core.toolkit.IdWorker;\n");
sb.append("import com.baomidou.mybatisplus.extension.plugins.pagination.Page;\n");
sb.append("import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\n");
sb.append("import ").append(getFullClassName(ENTITY_PACKAGE, entityClass)).append(";\n");
sb.append("import ").append(getFullClassName(MAPPER_PACKAGE, mapperClass)).append(";\n");
sb.append("import ").append(getFullClassName(VO_PACKAGE, voClass)).append(";\n");
sb.append("import ").append(getFullClassName(DTO_PACKAGE, dtoClass)).append(";\n");
sb.append("import ").append(getFullClassName(SERVICE_PACKAGE, serviceClass)).append(";\n");
sb.append("import lombok.extern.slf4j.Slf4j;\n");
sb.append("import org.springframework.stereotype.Service;\n");
sb.append("import org.springframework.transaction.annotation.Transactional;\n\n");
sb.append("import java.util.List;\n");
if (pkInfo.isComposite()) {
sb.append("import java.util.Map;\n");
}
sb.append("import java.util.stream.Collectors;\n\n");
sb.append("/**\n");
sb.append(" * ").append(getTableComment(table)).append("服务实现类\n");
sb.append(" *\n");
sb.append(" * @author ").append(AUTHOR).append("\n");
sb.append(" */\n");
sb.append("@Slf4j\n");
sb.append("@Service\n");
sb.append("public class ").append(className).append(" extends ServiceImpl<").append(mapperClass).append(", ").append(entityClass).append("> implements ").append(serviceClass).append(" {\n\n");
sb.append(" private static final int BATCH_SIZE = ").append(BATCH_SIZE).append(";\n\n");
// pageVO
sb.append(" @Override\n");
sb.append(" public IPage<").append(voClass).append("> pageVO(").append(dtoClass).append(" dto) {\n");
sb.append(" Page<").append(voClass).append("> page = new Page<>(dto.getPageNum(), dto.getPageSize());\n");
sb.append(" return baseMapper.selectPageVO(page, dto);\n");
sb.append(" }\n\n");
// listVO
sb.append(" @Override\n");
sb.append(" public List<").append(voClass).append("> listVO(").append(dtoClass).append(" dto) {\n");
sb.append(" return baseMapper.selectListVO(dto);\n");
sb.append(" }\n\n");
// getVOById
if (pkInfo.isComposite()) {
sb.append(" @Override\n");
sb.append(" public ").append(voClass).append(" getVOById(Map<String, Object> pkMap) {\n");
sb.append(" QueryWrapper<").append(entityClass).append("> wrapper = new QueryWrapper<>();\n");
for (ColumnInfo col : pkInfo.getColumns()) {
sb.append(" wrapper.eq(\"").append(col.getColumnName()).append("\", pkMap.get(\"").append(col.getJavaFieldName()).append("\"));\n");
}
sb.append(" ").append(entityClass).append(" entity = getOne(wrapper);\n");
sb.append(" if (entity == null) {\n");
sb.append(" return null;\n");
sb.append(" }\n");
sb.append(" return BeanUtil.copyProperties(entity, ").append(voClass).append(".class);\n");
sb.append(" }\n\n");
} else {
sb.append(" @Override\n");
sb.append(" public ").append(voClass).append(" getVOById(").append(pkType).append(" ").append(pkField).append(") {\n");
sb.append(" ").append(entityClass).append(" entity = getById(").append(pkField).append(");\n");
sb.append(" if (entity == null) {\n");
sb.append(" return null;\n");
sb.append(" }\n");
sb.append(" return BeanUtil.copyProperties(entity, ").append(voClass).append(".class);\n");
sb.append(" }\n\n");
}
// add
sb.append(" @Override\n");
sb.append(" @Transactional(rollbackFor = Exception.class)\n");
sb.append(" public Boolean add(").append(dtoClass).append(" dto) {\n");
sb.append(" ").append(entityClass).append(" entity = BeanUtil.copyProperties(dto, ").append(entityClass).append(".class);\n");
if (!pkInfo.isComposite() && !pkInfo.getColumns().isEmpty()) {
ColumnInfo pkCol = pkInfo.getColumns().get(0);
String fieldName = pkCol.getJavaFieldName();
String pkTypeCol = pkCol.getJavaType();
if ("String".equals(pkTypeCol)) {
sb.append(" entity.set").append(StrUtil.upperFirst(fieldName)).append("(IdWorker.getIdStr());\n");
}
}
if (logicDeleteField != null) {
sb.append(" entity.set").append(StrUtil.upperFirst(logicDeleteField)).append("(0);\n");
}
sb.append(" return save(entity);\n");
sb.append(" }\n\n");
// batchAdd
sb.append(" @Override\n");
sb.append(" @Transactional(rollbackFor = Exception.class)\n");
sb.append(" public Boolean batchAdd(List<").append(dtoClass).append("> dtoList) {\n");
sb.append(" if (dtoList == null || dtoList.isEmpty()) {\n");
sb.append(" return true;\n");
sb.append(" }\n");
sb.append(" List<").append(entityClass).append("> list = dtoList.stream()\n");
sb.append(" .map(d -> {\n");
sb.append(" ").append(entityClass).append(" entity = BeanUtil.copyProperties(d, ").append(entityClass).append(".class);\n");
if (!pkInfo.isComposite() && !pkInfo.getColumns().isEmpty()) {
ColumnInfo pkCol = pkInfo.getColumns().get(0);
String fieldName = pkCol.getJavaFieldName();
String pkTypeCol = pkCol.getJavaType();
if ("String".equals(pkTypeCol)) {
sb.append(" entity.set").append(StrUtil.upperFirst(fieldName)).append("(IdWorker.getIdStr());\n");
}
}
if (logicDeleteField != null) {
sb.append(" entity.set").append(StrUtil.upperFirst(logicDeleteField)).append("(0);\n");
}
sb.append(" return entity;\n");
sb.append(" })\n");
sb.append(" .collect(Collectors.toList());\n");
sb.append(" return insertBatch(list);\n");
sb.append(" }\n\n");
// insertBatch
sb.append(" private Boolean insertBatch(List<").append(entityClass).append("> list) {\n");
sb.append(" int total = list.size();\n");
sb.append(" int inserted = 0;\n");
sb.append(" for (int i = 0; i < total; i += BATCH_SIZE) {\n");
sb.append(" int end = Math.min(i + BATCH_SIZE, total);\n");
sb.append(" List<").append(entityClass).append("> subList = list.subList(i, end);\n");
sb.append(" inserted += baseMapper.insertBatch(subList);\n");
sb.append(" }\n");
sb.append(" return inserted == total;\n");
sb.append(" }\n\n");
// update
sb.append(" @Override\n");
sb.append(" @Transactional(rollbackFor = Exception.class)\n");
sb.append(" public Boolean update(").append(dtoClass).append(" dto) {\n");
if (pkInfo.isComposite()) {
for (ColumnInfo col : pkInfo.getColumns()) {
String fieldName = col.getJavaFieldName();
sb.append(" if (dto.get").append(StrUtil.upperFirst(fieldName)).append("() == null) {\n");
sb.append(" throw new IllegalArgumentException(\"更新时主键字段 ").append(fieldName).append(" 不能为空\");\n");
sb.append(" }\n");
}
} else if (!pkInfo.getColumns().isEmpty()) {
String fieldName = pkInfo.getColumns().get(0).getJavaFieldName();
sb.append(" if (dto.get").append(StrUtil.upperFirst(fieldName)).append("() == null) {\n");
sb.append(" throw new IllegalArgumentException(\"更新时主键不能为空\");\n");
sb.append(" }\n");
}
sb.append(" return updateById(BeanUtil.copyProperties(dto, ").append(entityClass).append(".class));\n");
sb.append(" }\n\n");
// deleteById
if (pkInfo.isComposite()) {
sb.append(" @Override\n");
sb.append(" @Transactional(rollbackFor = Exception.class)\n");
sb.append(" public Boolean deleteById(Map<String, Object> pkMap) {\n");
sb.append(" QueryWrapper<").append(entityClass).append("> wrapper = new QueryWrapper<>();\n");
for (ColumnInfo col : pkInfo.getColumns()) {
sb.append(" wrapper.eq(\"").append(col.getColumnName()).append("\", pkMap.get(\"").append(col.getJavaFieldName()).append("\"));\n");
}
if (logicDeleteField != null) {
sb.append(" ").append(entityClass).append(" entity = new ").append(entityClass).append("();\n");
sb.append(" entity.set").append(StrUtil.upperFirst(logicDeleteField)).append("(1);\n");
sb.append(" return update(entity, wrapper);\n");
} else {
sb.append(" return remove(wrapper);\n");
}
sb.append(" }\n\n");
} else {
sb.append(" @Override\n");
sb.append(" @Transactional(rollbackFor = Exception.class)\n");
sb.append(" public Boolean deleteById(").append(pkType).append(" ").append(pkField).append(") {\n");
if (logicDeleteField != null) {
sb.append(" ").append(entityClass).append(" entity = new ").append(entityClass).append("();\n");
String fieldName = pkInfo.getColumns().isEmpty() ? "id" : pkInfo.getColumns().get(0).getJavaFieldName();
sb.append(" entity.set").append(StrUtil.upperFirst(fieldName)).append("(").append(pkField).append(");\n");
sb.append(" entity.set").append(StrUtil.upperFirst(logicDeleteField)).append("(1);\n");
sb.append(" return updateById(entity);\n");
} else {
sb.append(" return removeById(").append(pkField).append(");\n");
}
sb.append(" }\n\n");
}
sb.append("}\n");
writeFile(SERVICE_IMPL_PACKAGE, className + ".java", sb.toString());
}
// ==================== Controller ====================
private static void generateController(TableInfo table) {
String className = table.getClassName() + CONTROLLER_SUFFIX;
String serviceClass = table.getClassName() + SERVICE_SUFFIX;
String voClass = table.getClassName() + VO_SUFFIX;
String dtoClass = table.getClassName() + DTO_SUFFIX;
PrimaryKeyInfo pkInfo = getPrimaryKeyInfo(table);
String pkType = getPrimaryKeyType(table);
String pkField = getPrimaryKeyFieldName(table);
String path = getControllerPath(getTablePath(table));
StringBuilder sb = new StringBuilder();
sb.append("package ").append(getFullPackage(CONTROLLER_PACKAGE)).append(";\n\n");
sb.append("import io.swagger.annotations.Api;\n");
sb.append("import io.swagger.annotations.ApiOperation;\n");
sb.append("import io.swagger.annotations.ApiParam;\n");
sb.append("import ").append(RESULT_CLASS).append(";\n");
sb.append("import com.baomidou.mybatisplus.core.metadata.IPage;\n");
sb.append("import ").append(getFullClassName(VO_PACKAGE, voClass)).append(";\n");
sb.append("import ").append(getFullClassName(DTO_PACKAGE, dtoClass)).append(";\n");
sb.append("import ").append(getFullClassName(SERVICE_PACKAGE, serviceClass)).append(";\n");
sb.append("import lombok.RequiredArgsConstructor;\n");
sb.append("import lombok.extern.slf4j.Slf4j;\n");
sb.append("import org.springframework.web.bind.annotation.*;\n\n");
sb.append("import javax.validation.Valid;\n");
sb.append("import java.util.List;\n");
if (pkInfo.isComposite()) {
sb.append("import java.util.Map;\n");
}
sb.append("\n");
sb.append("/**\n");
sb.append(" * ").append(getTableComment(table)).append("控制器\n");
sb.append(" *\n");
sb.append(" * @author ").append(AUTHOR).append("\n");
sb.append(" */\n");
sb.append("@Slf4j\n");
sb.append("@RestController\n");
sb.append("@RequiredArgsConstructor\n");
sb.append("@RequestMapping(\"").append(path).append("\")\n");
sb.append("@Api(tags = \"").append(getTableComment(table)).append("管理\")\n");
sb.append("public class ").append(className).append(" {\n\n");
sb.append(" private final ").append(serviceClass).append(" ").append(table.getVarName()).append("Service;\n\n");
// page
sb.append(" @PostMapping(\"/page\")\n");
sb.append(" @ApiOperation(\"条件分页查询\")\n");
sb.append(" public Result<IPage<").append(voClass).append(">> page(@RequestBody(required = false) @Valid ").append(dtoClass).append(" dto) {\n");
sb.append(" dto = dto == null ? new ").append(dtoClass).append("() : dto;\n");
sb.append(" return Result.success(").append(table.getVarName()).append("Service.pageVO(dto));\n");
sb.append(" }\n\n");
// list
sb.append(" @PostMapping(\"/list\")\n");
sb.append(" @ApiOperation(\"条件查询所有\")\n");
sb.append(" public Result<List<").append(voClass).append(">> list(@RequestBody(required = false) ").append(dtoClass).append(" dto) {\n");
sb.append(" dto = dto == null ? new ").append(dtoClass).append("() : dto;\n");
sb.append(" return Result.success(").append(table.getVarName()).append("Service.listVO(dto));\n");
sb.append(" }\n\n");
// getById
if (pkInfo.isComposite()) {
sb.append(" @PostMapping(\"/detail\")\n");
sb.append(" @ApiOperation(\"根据主键查询(复合主键)\")\n");
sb.append(" public Result<").append(voClass).append("> getById(@RequestBody Map<String, Object> pkMap) {\n");
sb.append(" return Result.success(").append(table.getVarName()).append("Service.getVOById(pkMap));\n");
sb.append(" }\n\n");
} else {
sb.append(" @GetMapping(\"/{").append(pkField).append("}\")\n");
sb.append(" @ApiOperation(\"根据主键查询\")\n");
sb.append(" public Result<").append(voClass).append("> getById(@ApiParam(\"主键ID\") @PathVariable ").append(pkType).append(" ").append(pkField).append(") {\n");
sb.append(" return Result.success(").append(table.getVarName()).append("Service.getVOById(").append(pkField).append("));\n");
sb.append(" }\n\n");
}
// add
sb.append(" @PostMapping\n");
sb.append(" @ApiOperation(\"新增\")\n");
sb.append(" public Result<Boolean> add(@RequestBody @Valid ").append(dtoClass).append(" dto) {\n");
sb.append(" return Result.success(\"新增成功\", ").append(table.getVarName()).append("Service.add(dto));\n");
sb.append(" }\n\n");
// batchAdd
sb.append(" @PostMapping(\"/batch\")\n");
sb.append(" @ApiOperation(\"批量新增\")\n");
sb.append(" public Result<Boolean> batchAdd(@RequestBody @Valid List<").append(dtoClass).append("> dtoList) {\n");
sb.append(" return Result.success(\"批量新增成功\", ").append(table.getVarName()).append("Service.batchAdd(dtoList));\n");
sb.append(" }\n\n");
// update
sb.append(" @PutMapping\n");
sb.append(" @PostMapping(\"/update\")\n");
sb.append(" @ApiOperation(\"修改\")\n");
sb.append(" public Result<Boolean> update(@RequestBody @Valid ").append(dtoClass).append(" dto) {\n");
sb.append(" return Result.success(\"修改成功\", ").append(table.getVarName()).append("Service.update(dto));\n");
sb.append(" }\n\n");
// delete
if (pkInfo.isComposite()) {
sb.append(" @DeleteMapping\n");
sb.append(" @ApiOperation(\"根据主键删除(复合主键)\")\n");
sb.append(" public Result<Boolean> delete(@RequestBody Map<String, Object> pkMap) {\n");
sb.append(" return Result.success(\"删除成功\", ").append(table.getVarName()).append("Service.deleteById(pkMap));\n");
sb.append(" }\n\n");
} else {
sb.append(" @DeleteMapping(\"/{").append(pkField).append("}\")\n");
sb.append(" @ApiOperation(\"根据主键删除\")\n");
sb.append(" public Result<Boolean> delete(@ApiParam(\"主键ID\") @PathVariable ").append(pkType).append(" ").append(pkField).append(") {\n");
sb.append(" return Result.success(\"删除成功\", ").append(table.getVarName()).append("Service.deleteById(").append(pkField).append("));\n");
sb.append(" }\n\n");
}
sb.append("}\n");
writeFile(CONTROLLER_PACKAGE, className + ".java", sb.toString());
}
// ==================== 文件写入 ====================
private static void writeFile(String pkgDir, String fileName, String content) {
String pkgPath = pkgDir.replace(".", "/");
String path = String.format("%s/src/main/java/%s/%s/%s",
OUTPUT_DIR,
BASE_PACKAGE.replace(".", "/"),
pkgPath,
fileName);
FileUtil.writeUtf8String(content, path);
}
private static void writeXmlFile(String xmlDir, String fileName, String content) {
String path = String.format("%s/src/main/resources/%s/%s", OUTPUT_DIR, xmlDir, fileName);
FileUtil.writeUtf8String(content, path);
}
private static void writeCommonFile(String pkgDir, String fileName, String content) {
String pkgPath = pkgDir.replace(".", "/");
String path = String.format("%s/src/main/java/%s/%s/%s",
OUTPUT_DIR,
BASE_PACKAGE.replace(".", "/"),
pkgPath,
fileName);
FileUtil.writeUtf8String(content, path);
System.out.println(" ✔ " + fileName);
}
// ==================== 工具方法 ====================
private static String getControllerPath(String tablePath) {
StringBuilder sb = new StringBuilder(CONTROLLER_BASE_PATH);
if (INCLUDE_MODULE_IN_PATH && StrUtil.isNotBlank(MODULE_NAME)) {
sb.append("/").append(MODULE_NAME);
}
if (StrUtil.isNotBlank(tablePath)) {
sb.append("/").append(tablePath);
}
return sb.toString();
}
private static String getTablePath(TableInfo table) {
return USE_TABLE_NAME_AS_PATH ? table.getTableName().toLowerCase() : table.getVarName();
}
private static String getFullPackage(String pkg) {
return BASE_PACKAGE + "." + pkg;
}
private static String getFullClassName(String pkg, String className) {
return getFullPackage(pkg) + "." + className;
}
private static String tableNameToClassName(String tableName) {
String name = tableName.toLowerCase();
for (String p : TABLE_PREFIX) {
if (name.startsWith(p)) {
name = name.substring(p.length());
break;
}
}
return StrUtil.upperFirst(StrUtil.toCamelCase(name));
}
private static String tableNameToVarName(String tableName) {
String name = tableName.toLowerCase();
for (String p : TABLE_PREFIX) {
if (name.startsWith(p)) {
name = name.substring(p.length());
break;
}
}
return StrUtil.toCamelCase(name);
}
/**
* 达梦数据库类型映射到Java类型
*/
private static String dmDbTypeToJavaType(String dbType, Integer maxLength) {
if (dbType == null) {
return "String";
}
String upperType = dbType.toUpperCase().trim();
switch (upperType) {
case "INT":
case "INTEGER":
case "TINYINT":
case "SMALLINT":
case "MEDIUMINT":
case "PLS_INTEGER":
return "Integer";
case "BIGINT":
return "Long";
case "FLOAT":
case "REAL":
return "Float";
case "DOUBLE":
case "DOUBLE PRECISION":
case "BINARY_DOUBLE":
return "Double";
case "DECIMAL":
case "DEC":
case "NUMERIC":
case "NUMBER":
// 达梦的NUMBER类型根据精度判断
if (maxLength != null && maxLength > 0) {
return "BigDecimal";
}
return "Long";
case "DATE":
case "DATETIME":
case "TIMESTAMP":
case "TIME":
return "LocalDateTime";
case "BIT":
case "BOOLEAN":
return "Boolean";
case "CHAR":
case "CHARACTER":
case "VARCHAR":
case "VARCHAR2":
case "NVARCHAR":
case "NVARCHAR2":
case "LONGVARCHAR":
case "TEXT":
case "CLOB":
case "LONG":
return "String";
case "BLOB":
case "BINARY":
case "VARBINARY":
case "IMAGE":
case "BFILE":
case "RAW":
return "byte[]";
default:
return "String";
}
}
private static String getExampleValue(ColumnInfo col) {
String name = col.getJavaFieldName().toLowerCase();
switch (col.getJavaType()) {
case "String":
if (name.contains("mail") || name.contains("email")) {
return "example@email.com";
}
if (name.contains("phone") || name.contains("mobile")) {
return "13800138000";
}
return "示例" + col.getJavaFieldName();
case "Integer":
return name.contains("age") ? "18" : "1";
case "Long":
return "1";
case "Double":
case "Float":
return "1.0";
case "BigDecimal":
return "0.00";
case "Boolean":
return "true";
case "LocalDateTime":
return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
default:
return null;
}
}
private static boolean isLogicDeleteField(String columnName) {
for (String f : LOGIC_DELETE_FIELDS) {
if (f.equalsIgnoreCase(columnName)) {
return true;
}
}
return false;
}
private static boolean isCreateUpdateField(String columnName) {
for (String f : CREATE_UPDATE_FIELDS) {
if (f.equalsIgnoreCase(columnName)) {
return true;
}
}
return false;
}
private static String getTableComment(TableInfo table) {
return StrUtil.isNotBlank(table.getTableComment()) ? table.getTableComment() : table.getClassName();
}
// ==================== 主键信息类 ====================
private static class PrimaryKeyInfo {
private final List<ColumnInfo> columns = new ArrayList<>();
private boolean composite;
public List<ColumnInfo> getColumns() {
return columns;
}
public boolean isComposite() {
return composite;
}
public void setComposite(boolean composite) {
this.composite = composite;
}
public String getPrimaryKeyField() {
if (columns.isEmpty()) {
return "id";
}
return columns.get(0).getJavaFieldName();
}
public String getPrimaryKeyType() {
if (columns.isEmpty()) {
return "Long";
}
if (composite) {
return "String";
}
return columns.get(0).getJavaType();
}
public String getColumnNames() {
if (columns.isEmpty()) {
return "id";
}
List<String> names = new ArrayList<>();
for (ColumnInfo col : columns) {
names.add(col.getColumnName());
}
return String.join(", ", names);
}
}
// ==================== 主键辅助方法 ====================
private static PrimaryKeyInfo getPrimaryKeyInfo(TableInfo table) {
PrimaryKeyInfo pkInfo = new PrimaryKeyInfo();
if (table.getColumns() == null) {
return pkInfo;
}
for (ColumnInfo col : table.getColumns()) {
if (col.isPrimaryKey()) {
pkInfo.getColumns().add(col);
}
}
pkInfo.setComposite(pkInfo.getColumns().size() > 1);
return pkInfo;
}
private static String getPrimaryKeyType(TableInfo table) {
PrimaryKeyInfo pkInfo = getPrimaryKeyInfo(table);
if (pkInfo.getColumns().isEmpty()) {
return "Long";
}
if (pkInfo.isComposite()) {
return "String";
}
return pkInfo.getColumns().get(0).getJavaType();
}
private static String getPrimaryKeyFieldName(TableInfo table) {
PrimaryKeyInfo pkInfo = getPrimaryKeyInfo(table);
if (pkInfo.getColumns().isEmpty()) {
return "id";
}
if (pkInfo.isComposite()) {
return "id";
}
return pkInfo.getColumns().get(0).getJavaFieldName();
}
// ==================== 内部类 ====================
@Data
@Accessors(chain = true)
private static class TableInfo {
private String tableName;
private String tableComment;
private String className;
private String varName;
private List<ColumnInfo> columns;
}
@Data
@Accessors(chain = true)
private static class ColumnInfo {
private String columnName;
private String dataType;
private String columnComment;
private boolean nullable;
private String columnDefault;
private Integer maxLength;
private boolean primaryKey;
private String javaType;
private String javaFieldName;
}
}
package com.tianshu.hr.utills;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import lombok.Data;
import lombok.experimental.Accessors;
import java.sql.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
/**
* MyBatis Plus 完整代码生成器 v9.0
* Entity、VO、DTO 字段完全一致
* 支持单主键和复合主键
* 直接运行 main 方法即可
*
* @author chenLiTao
*/
public class CodeGenerator {
// ==================== 数据库配置 ====================
private static final String DB_URL = "jdbc:mysql://127.0.0.1:3306/hr_center?" +
"useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&" +
"serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true";
private static final String DB_USERNAME = "root";
private static final String DB_PASSWORD = "root";
private static final String DB_NAME = "hr_center";
// ==================== 基础配置 ====================
private static final String OUTPUT_DIR = System.getProperty("user.dir") + "/generated-code";
private static final String AUTHOR = "chenLiTao";
private static final String BASE_PACKAGE = "com.tianShu.hr";
private static final String MODULE_NAME = "";
private static final String TABLE_NAMES = "";
private static final String[] TABLE_PREFIX = {"t_", "sys_", "tb_"};
// ==================== 包路径配置 ====================
private static final String ENTITY_PACKAGE = "entity";
private static final String VO_PACKAGE = "param.vo";
private static final String DTO_PACKAGE = "param.dto";
private static final String MAPPER_PACKAGE = "mapper";
private static final String SERVICE_PACKAGE = "service";
private static final String SERVICE_IMPL_PACKAGE = "service.impl";
private static final String CONTROLLER_PACKAGE = "controller";
private static final String COMMON_PACKAGE = "common";
private static final String MAPPER_XML_DIR = "mapper";
// ==================== 类名后缀配置 ====================
private static final String ENTITY_SUFFIX = "";
private static final String VO_SUFFIX = "VO";
private static final String DTO_SUFFIX = "DTO";
private static final String MAPPER_SUFFIX = "Mapper";
private static final String SERVICE_SUFFIX = "Service";
private static final String SERVICE_IMPL_SUFFIX = "ServiceImpl";
private static final String CONTROLLER_SUFFIX = "Controller";
// ==================== Controller 路径配置 ====================
private static final String CONTROLLER_BASE_PATH = "";
private static final boolean INCLUDE_MODULE_IN_PATH = true;
private static final boolean USE_TABLE_NAME_AS_PATH = false;
// ==================== 生成开关 ====================
private static final boolean GEN_ENTITY = true;
private static final boolean GEN_VO = true;
private static final boolean GEN_DTO = true;
private static final boolean GEN_MAPPER = true;
private static final boolean GEN_MAPPER_XML = true;
private static final boolean GEN_SERVICE = true;
private static final boolean GEN_SERVICE_IMPL = true;
private static final boolean GEN_CONTROLLER = true;
private static final boolean GEN_RESULT = true;
private static final boolean GEN_PAGE_DTO = true;
// ==================== Result配置 ====================
private static final String RESULT_CLASS = BASE_PACKAGE + "." + COMMON_PACKAGE + ".Result";
private static final String RESULT_CLASS_NAME = "Result";
// ==================== 逻辑删除/创建更新字段 ====================
private static final String[] LOGIC_DELETE_FIELDS = {"is_deleted", "deleted", "del_flag", "is_del"};
private static final String[] CREATE_UPDATE_FIELDS = {"create_time", "update_time", "created_at",
"updated_at", "create_date", "update_date", "gmt_create", "gmt_modified",
"create_user", "update_user", "creater", "updater"};
// ==================== 批量插入配置 ====================
private static final int BATCH_SIZE = 500;
// ==================== main ====================
public static void main(String[] args) {
System.out.println("\n============================================");
System.out.println(" MyBatis Plus 代码生成器 v9.0");
System.out.println(" 支持单主键/复合主键");
System.out.println("============================================\n");
System.out.println("数据库: " + DB_NAME);
System.out.println("输出目录: " + OUTPUT_DIR);
System.out.println("基础包名: " + BASE_PACKAGE);
System.out.println("模块名: " + MODULE_NAME);
System.out.println("Controller路径: " + getControllerPath("{table}"));
System.out.println("生成表: " + (StrUtil.isBlank(TABLE_NAMES) ? "全部表" : TABLE_NAMES));
System.out.println();
try {
testConnection();
List<TableInfo> tables = resolveTables();
if (tables.isEmpty()) {
System.out.println("没有找到任何有效的表!");
return;
}
FileUtil.del(OUTPUT_DIR);
System.out.println("已清理旧输出目录\n");
if (GEN_RESULT) {
generateResult();
}
if (GEN_PAGE_DTO) {
generatePageDTO();
}
int successCount = 0;
for (TableInfo table : tables) {
try {
generateAll(table);
successCount++;
} catch (Exception e) {
System.err.println("❌ 生成表 [" + table.getTableName() + "] 失败: " + e.getMessage());
e.printStackTrace();
}
}
System.out.println("\n============================================");
System.out.println(" ✅ 代码生成完成!成功: " + successCount + " 张表");
System.out.println(" 输出目录: " + OUTPUT_DIR);
System.out.println("============================================");
} catch (Exception e) {
System.err.println("\n❌ 代码生成失败: " + e.getMessage());
System.err.println("请检查: 1.MySQL是否启动 2.数据库'" + DB_NAME + "'是否存在 3.用户名密码是否正确");
e.printStackTrace();
}
}
// ==================== 数据库 ====================
private static void testConnection() throws SQLException {
System.out.print("测试数据库连接... ");
try (Connection conn = getConnection()) {
System.out.println("✅ 成功 (MySQL " + conn.getMetaData().getDatabaseProductVersion() + ")");
}
}
private static Connection getConnection() throws SQLException {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
throw new SQLException("MySQL驱动未找到", e);
}
return DriverManager.getConnection(DB_URL, DB_USERNAME, DB_PASSWORD);
}
private static List<TableInfo> resolveTables() throws SQLException {
if (StrUtil.isBlank(TABLE_NAMES)) {
return getAllTables();
}
List<TableInfo> tables = new ArrayList<>();
List<TableInfo> allTables = getAllTables();
for (String name : TABLE_NAMES.split(",")) {
String trimmed = name.trim();
if (StrUtil.isBlank(trimmed)) {
continue;
}
TableInfo table = allTables.stream()
.filter(t -> t.getTableName().equalsIgnoreCase(trimmed))
.findFirst()
.orElse(null);
if (table != null) {
tables.add(table);
} else {
System.out.println("⚠ 表 [" + trimmed + "] 不存在");
}
}
return tables;
}
private static List<TableInfo> getAllTables() throws SQLException {
List<TableInfo> tables = new ArrayList<>();
String sql = "SELECT TABLE_NAME, TABLE_COMMENT FROM information_schema.TABLES " +
"WHERE TABLE_SCHEMA = ? AND TABLE_TYPE = 'BASE TABLE'";
try (Connection conn = getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, DB_NAME);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
TableInfo t = new TableInfo();
t.setTableName(rs.getString("TABLE_NAME"));
t.setTableComment(rs.getString("TABLE_COMMENT"));
t.setClassName(tableNameToClassName(t.getTableName()));
t.setVarName(tableNameToVarName(t.getTableName()));
t.setColumns(getTableColumns(t.getTableName()));
tables.add(t);
}
}
}
return tables;
}
private static List<ColumnInfo> getTableColumns(String tableName) throws SQLException {
List<ColumnInfo> columns = new ArrayList<>();
String sql = "SELECT COLUMN_NAME, DATA_TYPE, COLUMN_COMMENT, IS_NULLABLE, " +
"COLUMN_DEFAULT, CHARACTER_MAXIMUM_LENGTH, COLUMN_KEY " +
"FROM information_schema.COLUMNS " +
"WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? " +
"ORDER BY ORDINAL_POSITION";
try (Connection conn = getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, DB_NAME);
ps.setString(2, tableName);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
ColumnInfo col = new ColumnInfo();
String colName = rs.getString("COLUMN_NAME");
col.setColumnName(colName);
col.setDataType(rs.getString("DATA_TYPE"));
col.setColumnComment(rs.getString("COLUMN_COMMENT"));
col.setNullable("YES".equals(rs.getString("IS_NULLABLE")));
col.setColumnDefault(rs.getString("COLUMN_DEFAULT"));
col.setMaxLength(rs.getInt("CHARACTER_MAXIMUM_LENGTH"));
col.setPrimaryKey("PRI".equals(rs.getString("COLUMN_KEY")));
col.setJavaType(dbTypeToJavaType(col.getDataType()));
col.setJavaFieldName(StrUtil.toCamelCase(colName.toLowerCase()));
columns.add(col);
}
}
}
return columns;
}
// ==================== 代码生成入口 ====================
private static void generateAll(TableInfo table) {
System.out.println("\n▶ 生成表 [" + table.getTableName() + "] (" + getTableComment(table) + ")");
PrimaryKeyInfo pkInfo = getPrimaryKeyInfo(table);
if (pkInfo.isComposite()) {
System.out.println(" 复合主键: " + pkInfo.getColumnNames());
} else {
System.out.println(" 主键: " + pkInfo.getPrimaryKeyField());
}
if (table.getColumns() == null || table.getColumns().isEmpty()) {
System.out.println(" ⚠ 无列信息,跳过");
return;
}
if (GEN_ENTITY) {
generateEntity(table);
}
if (GEN_VO) {
generateVO(table);
}
if (GEN_DTO) {
generateDTO(table);
}
if (GEN_MAPPER) {
generateMapper(table);
}
if (GEN_MAPPER_XML) {
generateMapperXml(table);
}
if (GEN_SERVICE) {
generateService(table);
}
if (GEN_SERVICE_IMPL) {
generateServiceImpl(table);
}
if (GEN_CONTROLLER) {
generateController(table);
}
System.out.println(" ✔ 完成");
}
// ==================== Result ====================
private static void generateResult() {
StringBuilder sb = new StringBuilder();
sb.append("package ").append(getFullPackage(COMMON_PACKAGE)).append(";\n\n");
sb.append("import io.swagger.annotations.ApiModel;\n");
sb.append("import io.swagger.annotations.ApiModelProperty;\n");
sb.append("import lombok.AllArgsConstructor;\n");
sb.append("import lombok.Data;\n");
sb.append("import lombok.NoArgsConstructor;\n\n");
sb.append("@Data\n");
sb.append("@NoArgsConstructor\n");
sb.append("@AllArgsConstructor\n");
sb.append("@ApiModel(\"统一返回结果\")\n");
sb.append("public class Result<T> {\n\n");
sb.append(" @ApiModelProperty(\"状态码\")\n");
sb.append(" private Integer code;\n\n");
sb.append(" @ApiModelProperty(\"消息\")\n");
sb.append(" private String message;\n\n");
sb.append(" @ApiModelProperty(\"数据\")\n");
sb.append(" private T data;\n\n");
sb.append(" @ApiModelProperty(\"时间戳\")\n");
sb.append(" private Long timestamp;\n\n");
sb.append(" public static <T> Result<T> success() {\n");
sb.append(" return new Result<>(200, \"操作成功\", null, System.currentTimeMillis());\n");
sb.append(" }\n\n");
sb.append(" public static <T> Result<T> success(T data) {\n");
sb.append(" return new Result<>(200, \"操作成功\", data, System.currentTimeMillis());\n");
sb.append(" }\n\n");
sb.append(" public static <T> Result<T> success(String msg, T data) {\n");
sb.append(" return new Result<>(200, msg, data, System.currentTimeMillis());\n");
sb.append(" }\n\n");
sb.append(" public static <T> Result<T> error() {\n");
sb.append(" return new Result<>(500, \"操作失败\", null, System.currentTimeMillis());\n");
sb.append(" }\n\n");
sb.append(" public static <T> Result<T> error(String msg) {\n");
sb.append(" return new Result<>(500, msg, null, System.currentTimeMillis());\n");
sb.append(" }\n\n");
sb.append(" public static <T> Result<T> error(Integer code, String msg) {\n");
sb.append(" return new Result<>(code, msg, null, System.currentTimeMillis());\n");
sb.append(" }\n");
sb.append("}\n");
writeCommonFile(COMMON_PACKAGE, "Result.java", sb.toString());
}
// ==================== PageDTO ====================
private static void generatePageDTO() {
StringBuilder sb = new StringBuilder();
sb.append("package ").append(getFullPackage(DTO_PACKAGE)).append(";\n\n");
sb.append("import io.swagger.annotations.ApiModel;\n");
sb.append("import io.swagger.annotations.ApiModelProperty;\n");
sb.append("import lombok.Data;\n\n");
sb.append("@Data\n");
sb.append("@ApiModel(\"分页请求基类\")\n");
sb.append("public class PageDTO {\n\n");
sb.append(" @ApiModelProperty(value = \"当前页\", example = \"1\")\n");
sb.append(" private Long pageNum = 1L;\n\n");
sb.append(" @ApiModelProperty(value = \"每页大小\", example = \"10\")\n");
sb.append(" private Long pageSize = 10L;\n\n");
sb.append(" @ApiModelProperty(value = \"排序字段\", example = \"id\")\n");
sb.append(" private String orderBy;\n\n");
sb.append(" @ApiModelProperty(value = \"是否升序\", example = \"false\")\n");
sb.append(" private Boolean isAsc = false;\n");
sb.append("}\n");
writeCommonFile(DTO_PACKAGE, "PageDTO.java", sb.toString());
}
// ==================== Entity ====================
private static void generateEntity(TableInfo table) {
String className = table.getClassName() + ENTITY_SUFFIX;
StringBuilder sb = new StringBuilder();
sb.append("package ").append(getFullPackage(ENTITY_PACKAGE)).append(";\n\n");
sb.append("import com.baomidou.mybatisplus.annotation.*;\n");
sb.append("import io.swagger.annotations.ApiModel;\n");
sb.append("import io.swagger.annotations.ApiModelProperty;\n");
sb.append("import lombok.AllArgsConstructor;\n");
sb.append("import lombok.Builder;\n");
sb.append("import lombok.Data;\n");
sb.append("import lombok.NoArgsConstructor;\n");
sb.append("import lombok.EqualsAndHashCode;\n");
sb.append("import lombok.experimental.Accessors;\n");
boolean hasDateTime = false;
boolean hasBigDecimal = false;
for (ColumnInfo col : table.getColumns()) {
if ("LocalDateTime".equals(col.getJavaType())) {
hasDateTime = true;
}
if ("BigDecimal".equals(col.getJavaType())) {
hasBigDecimal = true;
}
}
if (hasDateTime) {
sb.append("import java.time.LocalDateTime;\n");
sb.append("import com.fasterxml.jackson.annotation.JsonFormat;\n");
}
if (hasBigDecimal) {
sb.append("import java.math.BigDecimal;\n");
}
sb.append("\n/**\n");
sb.append(" * ").append(getTableComment(table)).append("实体类\n");
sb.append(" *\n");
sb.append(" * @author ").append(AUTHOR).append("\n");
sb.append(" */\n");
sb.append("@Data\n");
sb.append("@Builder\n");
sb.append("@NoArgsConstructor\n");
sb.append("@AllArgsConstructor\n");
sb.append("@EqualsAndHashCode(callSuper = false)\n");
sb.append("@Accessors(chain = true)\n");
sb.append("@TableName(\"").append(table.getTableName()).append("\")\n");
sb.append("@ApiModel(value = \"").append(className).append("\", description = \"").append(getTableComment(table)).append("\")\n");
sb.append("public class ").append(className).append(" {\n\n");
PrimaryKeyInfo pkInfo = getPrimaryKeyInfo(table);
for (ColumnInfo col : table.getColumns()) {
String comment = StrUtil.isNotBlank(col.getColumnComment()) ? col.getColumnComment() : col.getJavaFieldName();
sb.append(" @ApiModelProperty(value = \"").append(comment).append("\"");
String example = getExampleValue(col);
if (example != null) {
sb.append(", example = \"").append(example).append("\"");
}
if (!col.isNullable() && !col.isPrimaryKey()) {
sb.append(", required = true");
}
sb.append(")\n");
if (col.isPrimaryKey()) {
// 修改原因:根据主键类型决定ID生成策略,而非仅判断字段名是否包含"id"
// 修改前:只判断字段名包含id才使用雪花算法
// 修改后:根据主键Java类型决定策略 - String用雪花算法,Long/Integer用自增
if (!pkInfo.isComposite()) {
String pkType = col.getJavaType();
if ("String".equals(pkType)) {
// String类型主键:使用雪花算法生成字符串ID
sb.append(" @TableId(value = \"").append(col.getColumnName()).append("\", type = IdType.ASSIGN_ID)\n");
} else if ("Long".equals(pkType) || "Integer".equals(pkType)) {
// Long/Integer类型主键:使用数据库自增
sb.append(" @TableId(value = \"").append(col.getColumnName()).append("\", type = IdType.AUTO)\n");
} else {
// 其他类型:使用ASSIGN_ID
sb.append(" @TableId(value = \"").append(col.getColumnName()).append("\", type = IdType.ASSIGN_ID)\n");
}
} else {
// 复合主键不自动生成
sb.append(" @TableId(value = \"").append(col.getColumnName()).append("\")\n");
}
} else if (isLogicDeleteField(col.getColumnName())) {
sb.append(" @TableLogic\n");
sb.append(" @TableField(\"").append(col.getColumnName()).append("\")\n");
} else {
sb.append(" @TableField(\"").append(col.getColumnName()).append("\")\n");
}
if ("LocalDateTime".equals(col.getJavaType())) {
sb.append(" @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\", timezone = \"GMT+8\")\n");
}
sb.append(" private ").append(col.getJavaType()).append(" ").append(col.getJavaFieldName()).append(";\n\n");
}
sb.append("}\n");
writeFile(ENTITY_PACKAGE, className + ".java", sb.toString());
}
// ==================== VO ====================
private static void generateVO(TableInfo table) {
String className = table.getClassName() + VO_SUFFIX;
StringBuilder sb = new StringBuilder();
sb.append("package ").append(getFullPackage(VO_PACKAGE)).append(";\n\n");
sb.append("import com.fasterxml.jackson.annotation.JsonFormat;\n");
sb.append("import io.swagger.annotations.ApiModel;\n");
sb.append("import io.swagger.annotations.ApiModelProperty;\n");
sb.append("import lombok.AllArgsConstructor;\n");
sb.append("import lombok.Builder;\n");
sb.append("import lombok.Data;\n");
sb.append("import lombok.NoArgsConstructor;\n\n");
boolean hasDateTime = false;
boolean hasBigDecimal = false;
for (ColumnInfo col : table.getColumns()) {
if ("LocalDateTime".equals(col.getJavaType())) {
hasDateTime = true;
}
if ("BigDecimal".equals(col.getJavaType())) {
hasBigDecimal = true;
}
}
if (hasDateTime) {
sb.append("import java.time.LocalDateTime;\n");
}
if (hasBigDecimal) {
sb.append("import java.math.BigDecimal;\n");
}
sb.append("\n/**\n");
sb.append(" * ").append(getTableComment(table)).append("视图对象\n");
sb.append(" *\n");
sb.append(" * @author ").append(AUTHOR).append("\n");
sb.append(" */\n");
sb.append("@Data\n");
sb.append("@Builder\n");
sb.append("@NoArgsConstructor\n");
sb.append("@AllArgsConstructor\n");
sb.append("@ApiModel(value = \"").append(className).append("\", description = \"").append(getTableComment(table)).append("视图对象\")\n");
sb.append("public class ").append(className).append(" {\n\n");
for (ColumnInfo col : table.getColumns()) {
String comment = StrUtil.isNotBlank(col.getColumnComment()) ? col.getColumnComment() : col.getJavaFieldName();
sb.append(" @ApiModelProperty(value = \"").append(comment).append("\"");
String example = getExampleValue(col);
if (example != null) {
sb.append(", example = \"").append(example).append("\"");
}
sb.append(")\n");
// 修改原因:VO中LocalDateTime字段需要添加@JsonFormat注解,确保返回给前端的日期格式统一
// 修改前:未添加@JsonFormat,返回的日期格式可能为数组或ISO格式
// 修改后:添加@JsonFormat指定格式和时区,统一返回 yyyy-MM-dd HH:mm:ss
if ("LocalDateTime".equals(col.getJavaType())) {
sb.append(" @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\", timezone = \"GMT+8\")\n");
}
sb.append(" private ").append(col.getJavaType()).append(" ").append(col.getJavaFieldName()).append(";\n\n");
}
sb.append("}\n");
writeFile(VO_PACKAGE, className + ".java", sb.toString());
}
// ==================== DTO ====================
private static void generateDTO(TableInfo table) {
String className = table.getClassName() + DTO_SUFFIX;
StringBuilder sb = new StringBuilder();
sb.append("package ").append(getFullPackage(DTO_PACKAGE)).append(";\n\n");
sb.append("import com.fasterxml.jackson.annotation.JsonFormat;\n");
sb.append("import io.swagger.annotations.ApiModel;\n");
sb.append("import io.swagger.annotations.ApiModelProperty;\n");
sb.append("import lombok.Data;\n");
sb.append("import lombok.EqualsAndHashCode;\n\n");
boolean hasDateTime = false;
boolean hasBigDecimal = false;
for (ColumnInfo col : table.getColumns()) {
if ("LocalDateTime".equals(col.getJavaType())) {
hasDateTime = true;
}
if ("BigDecimal".equals(col.getJavaType())) {
hasBigDecimal = true;
}
}
if (hasDateTime) {
sb.append("import java.time.LocalDateTime;\n");
}
if (hasBigDecimal) {
sb.append("import java.math.BigDecimal;\n");
}
if (hasDateTime || hasBigDecimal) {
sb.append("\n");
}
sb.append("/**\n");
sb.append(" * ").append(getTableComment(table)).append("DTO\n");
sb.append(" * - 新增时主键为空(复合主键需要手动传入)\n");
sb.append(" * - 修改时主键必传\n");
sb.append(" * - 查询时传条件字段\n");
sb.append(" *\n");
sb.append(" * @author ").append(AUTHOR).append("\n");
sb.append(" */\n");
sb.append("@Data\n");
sb.append("@EqualsAndHashCode(callSuper = true)\n");
sb.append("@ApiModel(value = \"").append(className).append("\", description = \"").append(getTableComment(table)).append("请求DTO\")\n");
sb.append("public class ").append(className).append(" extends PageDTO {\n\n");
for (ColumnInfo col : table.getColumns()) {
String comment = StrUtil.isNotBlank(col.getColumnComment()) ? col.getColumnComment() : col.getJavaFieldName();
sb.append(" @ApiModelProperty(value = \"").append(comment).append("\"");
if (col.isPrimaryKey()) {
sb.append(", notes = \"修改时必填\"");
}
String example = getExampleValue(col);
if (example != null) {
sb.append(", example = \"").append(example).append("\"");
}
if (!col.isNullable() && !col.isPrimaryKey()) {
sb.append(", required = true");
}
sb.append(")\n");
// 修改原因:DTO中LocalDateTime字段需要添加@JsonFormat注解,支持前端传入 yyyy-MM-dd HH:mm:ss 格式
// 修改前:未添加@JsonFormat,无法解析带空格的日期字符串
// 修改后:添加@JsonFormat指定格式和时区
if ("LocalDateTime".equals(col.getJavaType())) {
sb.append(" @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\", timezone = \"GMT+8\")\n");
}
sb.append(" private ").append(col.getJavaType()).append(" ").append(col.getJavaFieldName()).append(";\n\n");
}
sb.append("}\n");
writeFile(DTO_PACKAGE, className + ".java", sb.toString());
}
// ==================== Mapper ====================
private static void generateMapper(TableInfo table) {
String className = table.getClassName() + MAPPER_SUFFIX;
String entityClass = table.getClassName() + ENTITY_SUFFIX;
String voClass = table.getClassName() + VO_SUFFIX;
String dtoClass = table.getClassName() + DTO_SUFFIX;
StringBuilder sb = new StringBuilder();
sb.append("package ").append(getFullPackage(MAPPER_PACKAGE)).append(";\n\n");
sb.append("import com.baomidou.mybatisplus.core.mapper.BaseMapper;\n");
sb.append("import com.baomidou.mybatisplus.core.metadata.IPage;\n");
sb.append("import com.baomidou.mybatisplus.extension.plugins.pagination.Page;\n");
sb.append("import ").append(getFullClassName(ENTITY_PACKAGE, entityClass)).append(";\n");
sb.append("import ").append(getFullClassName(VO_PACKAGE, voClass)).append(";\n");
sb.append("import ").append(getFullClassName(DTO_PACKAGE, dtoClass)).append(";\n");
sb.append("import org.apache.ibatis.annotations.Mapper;\n");
sb.append("import org.apache.ibatis.annotations.Param;\n\n");
sb.append("import java.util.List;\n\n");
sb.append("@Mapper\n");
sb.append("public interface ").append(className).append(" extends BaseMapper<").append(entityClass).append("> {\n\n");
sb.append(" /**\n");
sb.append(" * 分页查询VO\n");
sb.append(" *\n");
sb.append(" * @param page 分页对象\n");
sb.append(" * @param query 查询条件\n");
sb.append(" * @return 分页结果\n");
sb.append(" */\n");
sb.append(" IPage<").append(voClass).append("> selectPageVO(Page<").append(voClass).append("> page, @Param(\"query\") ").append(dtoClass).append(" query);\n\n");
sb.append(" /**\n");
sb.append(" * 查询列表VO\n");
sb.append(" *\n");
sb.append(" * @param query 查询条件\n");
sb.append(" * @return 列表结果\n");
sb.append(" */\n");
sb.append(" List<").append(voClass).append("> selectListVO(@Param(\"query\") ").append(dtoClass).append(" query);\n\n");
sb.append(" /**\n");
sb.append(" * 批量插入\n");
sb.append(" *\n");
sb.append(" * @param list 实体列表\n");
sb.append(" * @return 影响行数\n");
sb.append(" */\n");
sb.append(" int insertBatch(@Param(\"list\") List<").append(entityClass).append("> list);\n\n");
sb.append("}\n");
writeFile(MAPPER_PACKAGE, className + ".java", sb.toString());
}
// ==================== Mapper XML ====================
private static void generateMapperXml(TableInfo table) {
String mapperClass = table.getClassName() + MAPPER_SUFFIX;
String voClass = table.getClassName() + VO_SUFFIX;
StringBuilder sb = new StringBuilder();
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
sb.append("<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n");
sb.append("<mapper namespace=\"").append(getFullPackage(MAPPER_PACKAGE)).append(".").append(mapperClass).append("\">\n\n");
// 基础字段列表
sb.append(" <sql id=\"Base_Column_List\">\n");
sb.append(" ");
List<String> cols = new ArrayList<>();
for (ColumnInfo col : table.getColumns()) {
cols.add(col.getColumnName());
}
sb.append(String.join(", ", cols)).append("\n");
sb.append(" </sql>\n\n");
// 通用查询条件
sb.append(" <sql id=\"base\">\n");
sb.append(" <where>\n");
// 逻辑删除过滤
for (ColumnInfo col : table.getColumns()) {
if (isLogicDeleteField(col.getColumnName())) {
sb.append(" AND ").append(col.getColumnName()).append(" = 0\n");
break;
}
}
// 动态查询条件
for (ColumnInfo col : table.getColumns()) {
if (isLogicDeleteField(col.getColumnName()) || isCreateUpdateField(col.getColumnName())) {
continue;
}
if ("String".equals(col.getJavaType())) {
sb.append(" <if test=\"query.").append(col.getJavaFieldName()).append(" != null and query.").append(col.getJavaFieldName()).append(" != ''\">\n");
sb.append(" AND ").append(col.getColumnName()).append(" LIKE CONCAT('%', #{query.").append(col.getJavaFieldName()).append("}, '%')\n");
sb.append(" </if>\n");
} else {
sb.append(" <if test=\"query.").append(col.getJavaFieldName()).append(" != null\">\n");
sb.append(" AND ").append(col.getColumnName()).append(" = #{query.").append(col.getJavaFieldName()).append("}\n");
sb.append(" </if>\n");
}
}
// 修改原因:将choose改为if else,更清晰地处理排序逻辑
// 修改前:使用choose,当isAsc为null时走otherwise默认DESC
// 修改后:使用if else,明确处理isAsc为true/false/null三种情况
sb.append(" <if test=\"query.orderBy != null and query.orderBy != ''\">\n");
sb.append(" ORDER BY ${query.orderBy}\n");
sb.append(" <if test=\"query.isAsc != null and query.isAsc\">\n");
sb.append(" ASC\n");
sb.append(" </if>\n");
sb.append(" <if test=\"query.isAsc != null and !query.isAsc\">\n");
sb.append(" DESC\n");
sb.append(" </if>\n");
sb.append(" <!-- 当isAsc为null时,默认不排序,由数据库默认顺序决定 -->\n");
sb.append(" </if>\n");
sb.append(" </where>\n");
sb.append(" </sql>\n\n");
// 修改原因:MyBatis DTD要求resultMap的子元素必须按 id* → result* 顺序
// 修改前:直接循环所有字段,未区分主键顺序,可能导致result在id前面
// 修改后:先输出id标签(主键),再输出result标签(非主键),符合DTD规范
sb.append(" <resultMap id=\"VOMap\" type=\"").append(getFullClassName(VO_PACKAGE, voClass)).append("\">\n");
// 先输出主键字段(id标签)
for (ColumnInfo col : table.getColumns()) {
if (col.isPrimaryKey()) {
sb.append(" <id column=\"").append(col.getColumnName()).append("\" property=\"").append(col.getJavaFieldName()).append("\"/>\n");
}
}
// 再输出非主键字段(result标签)
for (ColumnInfo col : table.getColumns()) {
if (!col.isPrimaryKey()) {
sb.append(" <result column=\"").append(col.getColumnName()).append("\" property=\"").append(col.getJavaFieldName()).append("\"/>\n");
}
}
sb.append(" </resultMap>\n\n");
// 分页查询
sb.append(" <select id=\"selectPageVO\" resultMap=\"VOMap\">\n");
sb.append(" SELECT <include refid=\"Base_Column_List\"/>\n");
sb.append(" FROM ").append(table.getTableName()).append("\n");
sb.append(" <include refid=\"base\"/>\n");
sb.append(" </select>\n\n");
// 列表查询
sb.append(" <select id=\"selectListVO\" resultMap=\"VOMap\">\n");
sb.append(" SELECT <include refid=\"Base_Column_List\"/>\n");
sb.append(" FROM ").append(table.getTableName()).append("\n");
sb.append(" <include refid=\"base\"/>\n");
sb.append(" </select>\n\n");
// 批量插入 - 包含所有字段(包括主键)
sb.append(" <insert id=\"insertBatch\">\n");
sb.append(" INSERT INTO ").append(table.getTableName()).append(" (\n");
sb.append(" ");
List<String> insertCols = new ArrayList<>();
for (ColumnInfo col : table.getColumns()) {
insertCols.add(col.getColumnName());
}
sb.append(String.join(", ", insertCols)).append("\n");
sb.append(" ) VALUES\n");
sb.append(" <foreach collection=\"list\" item=\"item\" separator=\",\">\n");
sb.append(" (\n");
sb.append(" ");
List<String> insertVals = new ArrayList<>();
for (ColumnInfo col : table.getColumns()) {
insertVals.add("#{item." + col.getJavaFieldName() + "}");
}
sb.append(String.join(", ", insertVals)).append("\n");
sb.append(" )\n");
sb.append(" </foreach>\n");
sb.append(" </insert>\n\n");
sb.append("</mapper>\n");
writeXmlFile(MAPPER_XML_DIR, mapperClass + ".xml", sb.toString());
}
// ==================== Service ====================
private static void generateService(TableInfo table) {
String className = table.getClassName() + SERVICE_SUFFIX;
String entityClass = table.getClassName() + ENTITY_SUFFIX;
String voClass = table.getClassName() + VO_SUFFIX;
String dtoClass = table.getClassName() + DTO_SUFFIX;
PrimaryKeyInfo pkInfo = getPrimaryKeyInfo(table);
String pkType = getPrimaryKeyType(table);
String pkField = getPrimaryKeyFieldName(table);
StringBuilder sb = new StringBuilder();
sb.append("package ").append(getFullPackage(SERVICE_PACKAGE)).append(";\n\n");
sb.append("import com.baomidou.mybatisplus.core.metadata.IPage;\n");
sb.append("import com.baomidou.mybatisplus.extension.service.IService;\n");
sb.append("import ").append(getFullClassName(ENTITY_PACKAGE, entityClass)).append(";\n");
sb.append("import ").append(getFullClassName(VO_PACKAGE, voClass)).append(";\n");
sb.append("import ").append(getFullClassName(DTO_PACKAGE, dtoClass)).append(";\n\n");
sb.append("import java.util.List;\n");
if (pkInfo.isComposite()) {
sb.append("import java.util.Map;\n");
}
sb.append("\n");
sb.append("/**\n");
sb.append(" * ").append(getTableComment(table)).append("服务接口\n");
sb.append(" *\n");
sb.append(" * @author ").append(AUTHOR).append("\n");
sb.append(" */\n");
sb.append("public interface ").append(className).append(" extends IService<").append(entityClass).append("> {\n\n");
sb.append(" /**\n");
sb.append(" * 分页查询VO\n");
sb.append(" *\n");
sb.append(" * @param dto 查询条件\n");
sb.append(" * @return 分页结果\n");
sb.append(" */\n");
sb.append(" IPage<").append(voClass).append("> pageVO(").append(dtoClass).append(" dto);\n\n");
sb.append(" /**\n");
sb.append(" * 查询列表VO\n");
sb.append(" *\n");
sb.append(" * @param dto 查询条件\n");
sb.append(" * @return 列表结果\n");
sb.append(" */\n");
sb.append(" List<").append(voClass).append("> listVO(").append(dtoClass).append(" dto);\n\n");
// 根据主键查询
if (pkInfo.isComposite()) {
sb.append(" /**\n");
sb.append(" * 根据主键查询VO(复合主键)\n");
sb.append(" *\n");
sb.append(" * @param pkMap 主键Map,key为字段名,value为字段值\n");
sb.append(" * @return VO对象\n");
sb.append(" */\n");
sb.append(" ").append(voClass).append(" getVOById(Map<String, Object> pkMap);\n\n");
} else {
sb.append(" /**\n");
sb.append(" * 根据主键查询VO\n");
sb.append(" *\n");
sb.append(" * @param ").append(pkField).append(" 主键ID\n");
sb.append(" * @return VO对象\n");
sb.append(" */\n");
sb.append(" ").append(voClass).append(" getVOById(").append(pkType).append(" ").append(pkField).append(");\n\n");
}
sb.append(" /**\n");
sb.append(" * 新增\n");
sb.append(" *\n");
sb.append(" * @param dto DTO对象\n");
sb.append(" * @return 是否成功\n");
sb.append(" */\n");
sb.append(" Boolean add(").append(dtoClass).append(" dto);\n\n");
sb.append(" /**\n");
sb.append(" * 批量新增\n");
sb.append(" *\n");
sb.append(" * @param dtoList DTO列表\n");
sb.append(" * @return 是否成功\n");
sb.append(" */\n");
sb.append(" Boolean batchAdd(List<").append(dtoClass).append("> dtoList);\n\n");
sb.append(" /**\n");
sb.append(" * 修改\n");
sb.append(" *\n");
sb.append(" * @param dto DTO对象\n");
sb.append(" * @return 是否成功\n");
sb.append(" */\n");
sb.append(" Boolean update(").append(dtoClass).append(" dto);\n\n");
// 根据主键删除
if (pkInfo.isComposite()) {
sb.append(" /**\n");
sb.append(" * 根据主键删除(复合主键)\n");
sb.append(" *\n");
sb.append(" * @param pkMap 主键Map,key为字段名,value为字段值\n");
sb.append(" * @return 是否成功\n");
sb.append(" */\n");
sb.append(" Boolean deleteById(Map<String, Object> pkMap);\n\n");
} else {
sb.append(" /**\n");
sb.append(" * 根据主键删除\n");
sb.append(" *\n");
sb.append(" * @param ").append(pkField).append(" 主键ID\n");
sb.append(" * @return 是否成功\n");
sb.append(" */\n");
sb.append(" Boolean deleteById(").append(pkType).append(" ").append(pkField).append(");\n\n");
}
sb.append("}\n");
writeFile(SERVICE_PACKAGE, className + ".java", sb.toString());
}
// ==================== ServiceImpl ====================
private static void generateServiceImpl(TableInfo table) {
String className = table.getClassName() + SERVICE_IMPL_SUFFIX;
String serviceClass = table.getClassName() + SERVICE_SUFFIX;
String mapperClass = table.getClassName() + MAPPER_SUFFIX;
String entityClass = table.getClassName() + ENTITY_SUFFIX;
String voClass = table.getClassName() + VO_SUFFIX;
String dtoClass = table.getClassName() + DTO_SUFFIX;
PrimaryKeyInfo pkInfo = getPrimaryKeyInfo(table);
String pkType = getPrimaryKeyType(table);
String pkField = getPrimaryKeyFieldName(table);
// 查找逻辑删除字段
String logicDeleteField = null;
for (ColumnInfo col : table.getColumns()) {
if (isLogicDeleteField(col.getColumnName())) {
logicDeleteField = col.getJavaFieldName();
break;
}
}
// 获取主键字段名列表
List<String> pkFieldNames = new ArrayList<>();
for (ColumnInfo col : pkInfo.getColumns()) {
pkFieldNames.add(col.getJavaFieldName());
}
StringBuilder sb = new StringBuilder();
sb.append("package ").append(getFullPackage(SERVICE_IMPL_PACKAGE)).append(";\n\n");
sb.append("import cn.hutool.core.bean.BeanUtil;\n");
sb.append("import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\n");
sb.append("import com.baomidou.mybatisplus.core.metadata.IPage;\n");
sb.append("import com.baomidou.mybatisplus.core.toolkit.IdWorker;\n");
sb.append("import com.baomidou.mybatisplus.extension.plugins.pagination.Page;\n");
sb.append("import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\n");
sb.append("import ").append(getFullClassName(ENTITY_PACKAGE, entityClass)).append(";\n");
sb.append("import ").append(getFullClassName(MAPPER_PACKAGE, mapperClass)).append(";\n");
sb.append("import ").append(getFullClassName(VO_PACKAGE, voClass)).append(";\n");
sb.append("import ").append(getFullClassName(DTO_PACKAGE, dtoClass)).append(";\n");
sb.append("import ").append(getFullClassName(SERVICE_PACKAGE, serviceClass)).append(";\n");
sb.append("import lombok.extern.slf4j.Slf4j;\n");
sb.append("import org.springframework.stereotype.Service;\n");
sb.append("import org.springframework.transaction.annotation.Transactional;\n\n");
sb.append("import java.util.List;\n");
if (pkInfo.isComposite()) {
sb.append("import java.util.Map;\n");
}
sb.append("import java.util.stream.Collectors;\n\n");
sb.append("/**\n");
sb.append(" * ").append(getTableComment(table)).append("服务实现类\n");
sb.append(" *\n");
sb.append(" * @author ").append(AUTHOR).append("\n");
sb.append(" */\n");
sb.append("@Slf4j\n");
sb.append("@Service\n");
sb.append("public class ").append(className).append(" extends ServiceImpl<").append(mapperClass).append(", ").append(entityClass).append("> implements ").append(serviceClass).append(" {\n\n");
sb.append(" /**\n");
sb.append(" * 批量插入分片大小\n");
sb.append(" */\n");
sb.append(" private static final int BATCH_SIZE = ").append(BATCH_SIZE).append(";\n\n");
// pageVO
sb.append(" @Override\n");
sb.append(" public IPage<").append(voClass).append("> pageVO(").append(dtoClass).append(" dto) {\n");
sb.append(" Page<").append(voClass).append("> page = new Page<>(dto.getPageNum(), dto.getPageSize());\n");
sb.append(" return baseMapper.selectPageVO(page, dto);\n");
sb.append(" }\n\n");
// listVO
sb.append(" @Override\n");
sb.append(" public List<").append(voClass).append("> listVO(").append(dtoClass).append(" dto) {\n");
sb.append(" return baseMapper.selectListVO(dto);\n");
sb.append(" }\n\n");
// getVOById
if (pkInfo.isComposite()) {
sb.append(" @Override\n");
sb.append(" public ").append(voClass).append(" getVOById(Map<String, Object> pkMap) {\n");
sb.append(" QueryWrapper<").append(entityClass).append("> wrapper = new QueryWrapper<>();\n");
for (ColumnInfo col : pkInfo.getColumns()) {
sb.append(" wrapper.eq(\"").append(col.getColumnName()).append("\", pkMap.get(\"").append(col.getJavaFieldName()).append("\"));\n");
}
sb.append(" ").append(entityClass).append(" entity = getOne(wrapper);\n");
sb.append(" if (entity == null) {\n");
sb.append(" return null;\n");
sb.append(" }\n");
sb.append(" return BeanUtil.copyProperties(entity, ").append(voClass).append(".class);\n");
sb.append(" }\n\n");
} else {
sb.append(" @Override\n");
sb.append(" public ").append(voClass).append(" getVOById(").append(pkType).append(" ").append(pkField).append(") {\n");
sb.append(" ").append(entityClass).append(" entity = getById(").append(pkField).append(");\n");
sb.append(" if (entity == null) {\n");
sb.append(" return null;\n");
sb.append(" }\n");
sb.append(" return BeanUtil.copyProperties(entity, ").append(voClass).append(".class);\n");
sb.append(" }\n\n");
}
// add
sb.append(" @Override\n");
sb.append(" @Transactional(rollbackFor = Exception.class)\n");
sb.append(" public Boolean add(").append(dtoClass).append(" dto) {\n");
sb.append(" ").append(entityClass).append(" entity = BeanUtil.copyProperties(dto, ").append(entityClass).append(".class);\n");
// 修改原因:根据主键类型决定是否自动生成ID,而非仅判断字段名是否包含"id"
// 修改前:只要字段名包含id就生成
// 修改后:String类型使用IdWorker.getIdStr(),Long/Integer不生成(使用自增),其他类型使用IdWorker.getId()
if (!pkInfo.isComposite() && !pkInfo.getColumns().isEmpty()) {
ColumnInfo pkCol = pkInfo.getColumns().get(0);
String fieldName = pkCol.getJavaFieldName();
String pkTypeCol = pkCol.getJavaType();
if ("String".equals(pkTypeCol)) {
// String类型主键:使用IdWorker生成字符串ID
sb.append(" entity.set").append(StrUtil.upperFirst(fieldName)).append("(IdWorker.getIdStr());\n");
} else if ("Long".equals(pkTypeCol) || "Integer".equals(pkTypeCol)) {
// Long/Integer类型主键:不生成,使用数据库自增
// 不做任何操作
} else {
// 其他类型:使用IdWorker生成数字ID
sb.append(" entity.set").append(StrUtil.upperFirst(fieldName)).append("(IdWorker.getId());\n");
}
}
if (logicDeleteField != null) {
sb.append(" entity.set").append(StrUtil.upperFirst(logicDeleteField)).append("(0);\n");
}
sb.append(" return save(entity);\n");
sb.append(" }\n\n");
// batchAdd
sb.append(" @Override\n");
sb.append(" @Transactional(rollbackFor = Exception.class)\n");
sb.append(" public Boolean batchAdd(List<").append(dtoClass).append("> dtoList) {\n");
sb.append(" if (dtoList == null || dtoList.isEmpty()) {\n");
sb.append(" return true;\n");
sb.append(" }\n");
sb.append(" List<").append(entityClass).append("> list = dtoList.stream()\n");
sb.append(" .map(d -> {\n");
sb.append(" ").append(entityClass).append(" entity = BeanUtil.copyProperties(d, ").append(entityClass).append(".class);\n");
// 修改原因:根据主键类型决定是否自动生成ID,与add方法保持一致
// 修改前:只要字段名包含id就生成
// 修改后:String类型使用IdWorker.getIdStr(),Long/Integer不生成(使用自增),其他类型使用IdWorker.getId()
if (!pkInfo.isComposite() && !pkInfo.getColumns().isEmpty()) {
ColumnInfo pkCol = pkInfo.getColumns().get(0);
String fieldName = pkCol.getJavaFieldName();
String pkTypeCol = pkCol.getJavaType();
if ("String".equals(pkTypeCol)) {
// String类型主键:使用IdWorker生成字符串ID
sb.append(" entity.set").append(StrUtil.upperFirst(fieldName)).append("(IdWorker.getIdStr());\n");
} else if ("Long".equals(pkTypeCol) || "Integer".equals(pkTypeCol)) {
// Long/Integer类型主键:不生成,使用数据库自增
// 不做任何操作
} else {
// 其他类型:使用IdWorker生成数字ID
sb.append(" entity.set").append(StrUtil.upperFirst(fieldName)).append("(IdWorker.getId());\n");
}
}
if (logicDeleteField != null) {
sb.append(" entity.set").append(StrUtil.upperFirst(logicDeleteField)).append("(0);\n");
}
sb.append(" return entity;\n");
sb.append(" })\n");
sb.append(" .collect(Collectors.toList());\n");
sb.append(" return insertBatch(list);\n");
sb.append(" }\n\n");
// insertBatch
sb.append(" /**\n");
sb.append(" * 批量插入(分片处理)\n");
sb.append(" *\n");
sb.append(" * @param list 实体列表\n");
sb.append(" * @return 是否全部插入成功\n");
sb.append(" */\n");
sb.append(" private Boolean insertBatch(List<").append(entityClass).append("> list) {\n");
sb.append(" int total = list.size();\n");
sb.append(" int inserted = 0;\n");
sb.append(" for (int i = 0; i < total; i += BATCH_SIZE) {\n");
sb.append(" int end = Math.min(i + BATCH_SIZE, total);\n");
sb.append(" List<").append(entityClass).append("> subList = list.subList(i, end);\n");
sb.append(" inserted += baseMapper.insertBatch(subList);\n");
sb.append(" }\n");
sb.append(" return inserted == total;\n");
sb.append(" }\n\n");
// update
sb.append(" @Override\n");
sb.append(" @Transactional(rollbackFor = Exception.class)\n");
sb.append(" public Boolean update(").append(dtoClass).append(" dto) {\n");
if (pkInfo.isComposite()) {
for (ColumnInfo col : pkInfo.getColumns()) {
String fieldName = col.getJavaFieldName();
sb.append(" if (dto.get").append(StrUtil.upperFirst(fieldName)).append("() == null) {\n");
sb.append(" throw new IllegalArgumentException(\"更新时主键字段 ").append(fieldName).append(" 不能为空\");\n");
sb.append(" }\n");
}
} else if (!pkInfo.getColumns().isEmpty()) {
String fieldName = pkInfo.getColumns().get(0).getJavaFieldName();
sb.append(" if (dto.get").append(StrUtil.upperFirst(fieldName)).append("() == null) {\n");
sb.append(" throw new IllegalArgumentException(\"更新时主键不能为空\");\n");
sb.append(" }\n");
}
sb.append(" return updateById(BeanUtil.copyProperties(dto, ").append(entityClass).append(".class));\n");
sb.append(" }\n\n");
// deleteById
if (pkInfo.isComposite()) {
sb.append(" @Override\n");
sb.append(" @Transactional(rollbackFor = Exception.class)\n");
sb.append(" public Boolean deleteById(Map<String, Object> pkMap) {\n");
sb.append(" QueryWrapper<").append(entityClass).append("> wrapper = new QueryWrapper<>();\n");
for (ColumnInfo col : pkInfo.getColumns()) {
sb.append(" wrapper.eq(\"").append(col.getColumnName()).append("\", pkMap.get(\"").append(col.getJavaFieldName()).append("\"));\n");
}
if (logicDeleteField != null) {
sb.append(" ").append(entityClass).append(" entity = new ").append(entityClass).append("();\n");
sb.append(" entity.set").append(StrUtil.upperFirst(logicDeleteField)).append("(1);\n");
sb.append(" return update(entity, wrapper);\n");
} else {
sb.append(" return remove(wrapper);\n");
}
sb.append(" }\n\n");
} else {
sb.append(" @Override\n");
sb.append(" @Transactional(rollbackFor = Exception.class)\n");
sb.append(" public Boolean deleteById(").append(pkType).append(" ").append(pkField).append(") {\n");
if (logicDeleteField != null) {
sb.append(" ").append(entityClass).append(" entity = new ").append(entityClass).append("();\n");
String fieldName = pkInfo.getColumns().isEmpty() ? "id" : pkInfo.getColumns().get(0).getJavaFieldName();
sb.append(" entity.set").append(StrUtil.upperFirst(fieldName)).append("(").append(pkField).append(");\n");
sb.append(" entity.set").append(StrUtil.upperFirst(logicDeleteField)).append("(1);\n");
sb.append(" return updateById(entity);\n");
} else {
sb.append(" return removeById(").append(pkField).append(");\n");
}
sb.append(" }\n\n");
}
sb.append("}\n");
writeFile(SERVICE_IMPL_PACKAGE, className + ".java", sb.toString());
}
// ==================== Controller ====================
private static void generateController(TableInfo table) {
String className = table.getClassName() + CONTROLLER_SUFFIX;
String serviceClass = table.getClassName() + SERVICE_SUFFIX;
String voClass = table.getClassName() + VO_SUFFIX;
String dtoClass = table.getClassName() + DTO_SUFFIX;
PrimaryKeyInfo pkInfo = getPrimaryKeyInfo(table);
String pkType = getPrimaryKeyType(table);
String pkField = getPrimaryKeyFieldName(table);
String path = getControllerPath(getTablePath(table));
StringBuilder sb = new StringBuilder();
sb.append("package ").append(getFullPackage(CONTROLLER_PACKAGE)).append(";\n\n");
sb.append("import io.swagger.annotations.Api;\n");
sb.append("import io.swagger.annotations.ApiOperation;\n");
sb.append("import io.swagger.annotations.ApiParam;\n");
sb.append("import ").append(RESULT_CLASS).append(";\n");
sb.append("import com.baomidou.mybatisplus.core.metadata.IPage;\n");
sb.append("import ").append(getFullClassName(VO_PACKAGE, voClass)).append(";\n");
sb.append("import ").append(getFullClassName(DTO_PACKAGE, dtoClass)).append(";\n");
sb.append("import ").append(getFullClassName(SERVICE_PACKAGE, serviceClass)).append(";\n");
sb.append("import lombok.RequiredArgsConstructor;\n");
sb.append("import lombok.extern.slf4j.Slf4j;\n");
sb.append("import org.springframework.web.bind.annotation.*;\n\n");
sb.append("import javax.validation.Valid;\n");
sb.append("import java.util.List;\n");
if (pkInfo.isComposite()) {
sb.append("import java.util.Map;\n");
}
sb.append("\n");
sb.append("/**\n");
sb.append(" * ").append(getTableComment(table)).append("控制器\n");
sb.append(" *\n");
sb.append(" * @author ").append(AUTHOR).append("\n");
sb.append(" */\n");
sb.append("@Slf4j\n");
sb.append("@RestController\n");
sb.append("@RequiredArgsConstructor\n");
sb.append("@RequestMapping(\"").append(path).append("\")\n");
sb.append("@Api(tags = \"").append(getTableComment(table)).append("管理\")\n");
sb.append("public class ").append(className).append(" {\n\n");
sb.append(" private final ").append(serviceClass).append(" ").append(table.getVarName()).append("Service;\n\n");
// page - 修改原因:支持不传参数也能分页查询,使用三元运算符更简洁
// 修改前:@RequestBody @Valid Dto dto(必须传递参数),使用if判断
// 修改后:@RequestBody(required = false) @Valid Dto dto(可选参数),使用三元运算符
sb.append(" @PostMapping(\"/page\")\n");
sb.append(" @ApiOperation(\"条件分页查询\")\n");
sb.append(" public Result<IPage<").append(voClass).append(">> page(@RequestBody(required = false) @Valid ").append(dtoClass).append(" dto) {\n");
sb.append(" dto = dto == null ? new ").append(dtoClass).append("() : dto;\n");
sb.append(" return Result.success(").append(table.getVarName()).append("Service.pageVO(dto));\n");
sb.append(" }\n\n");
// list - 修改原因:支持不传参数也能查询所有数据,使用三元运算符更简洁
// 修改前:@RequestBody Dto dto(必须传递参数),使用if判断
// 修改后:@RequestBody(required = false) Dto dto(可选参数),使用三元运算符
sb.append(" @PostMapping(\"/list\")\n");
sb.append(" @ApiOperation(\"条件查询所有\")\n");
sb.append(" public Result<List<").append(voClass).append(">> list(@RequestBody(required = false) ").append(dtoClass).append(" dto) {\n");
sb.append(" dto = dto == null ? new ").append(dtoClass).append("() : dto;\n");
sb.append(" return Result.success(").append(table.getVarName()).append("Service.listVO(dto));\n");
sb.append(" }\n\n");
// getById
if (pkInfo.isComposite()) {
sb.append(" @PostMapping(\"/detail\")\n");
sb.append(" @ApiOperation(\"根据主键查询(复合主键)\")\n");
sb.append(" public Result<").append(voClass).append("> getById(@RequestBody Map<String, Object> pkMap) {\n");
sb.append(" return Result.success(").append(table.getVarName()).append("Service.getVOById(pkMap));\n");
sb.append(" }\n\n");
} else {
sb.append(" @GetMapping(\"/{").append(pkField).append("}\")\n");
sb.append(" @ApiOperation(\"根据主键查询\")\n");
sb.append(" public Result<").append(voClass).append("> getById(@ApiParam(\"主键ID\") @PathVariable ").append(pkType).append(" ").append(pkField).append(") {\n");
sb.append(" return Result.success(").append(table.getVarName()).append("Service.getVOById(").append(pkField).append("));\n");
sb.append(" }\n\n");
}
// add
sb.append(" @PostMapping\n");
sb.append(" @ApiOperation(\"新增\")\n");
sb.append(" public Result<Boolean> add(@RequestBody @Valid ").append(dtoClass).append(" dto) {\n");
sb.append(" return Result.success(\"新增成功\", ").append(table.getVarName()).append("Service.add(dto));\n");
sb.append(" }\n\n");
// batchAdd
sb.append(" @PostMapping(\"/batch\")\n");
sb.append(" @ApiOperation(\"批量新增\")\n");
sb.append(" public Result<Boolean> batchAdd(@RequestBody @Valid List<").append(dtoClass).append("> dtoList) {\n");
sb.append(" return Result.success(\"批量新增成功\", ").append(table.getVarName()).append("Service.batchAdd(dtoList));\n");
sb.append(" }\n\n");
// update - 修改原因:同时支持PUT和POST两种请求方法
// 修改前:仅 @PutMapping
// 修改后:@PutMapping 和 @PostMapping("/update") 同时支持
sb.append(" @PutMapping\n");
sb.append(" @PostMapping(\"/update\")\n");
sb.append(" @ApiOperation(\"修改\")\n");
sb.append(" public Result<Boolean> update(@RequestBody @Valid ").append(dtoClass).append(" dto) {\n");
sb.append(" return Result.success(\"修改成功\", ").append(table.getVarName()).append("Service.update(dto));\n");
sb.append(" }\n\n");
// delete
if (pkInfo.isComposite()) {
sb.append(" @DeleteMapping\n");
sb.append(" @ApiOperation(\"根据主键删除(复合主键)\")\n");
sb.append(" public Result<Boolean> delete(@RequestBody Map<String, Object> pkMap) {\n");
sb.append(" return Result.success(\"删除成功\", ").append(table.getVarName()).append("Service.deleteById(pkMap));\n");
sb.append(" }\n\n");
} else {
sb.append(" @DeleteMapping(\"/{").append(pkField).append("}\")\n");
sb.append(" @ApiOperation(\"根据主键删除\")\n");
sb.append(" public Result<Boolean> delete(@ApiParam(\"主键ID\") @PathVariable ").append(pkType).append(" ").append(pkField).append(") {\n");
sb.append(" return Result.success(\"删除成功\", ").append(table.getVarName()).append("Service.deleteById(").append(pkField).append("));\n");
sb.append(" }\n\n");
}
sb.append("}\n");
writeFile(CONTROLLER_PACKAGE, className + ".java", sb.toString());
}
// ==================== 文件写入 ====================
private static void writeFile(String pkgDir, String fileName, String content) {
String pkgPath = pkgDir.replace(".", "/");
String path = String.format("%s/src/main/java/%s/%s/%s",
OUTPUT_DIR,
BASE_PACKAGE.replace(".", "/"),
pkgPath,
fileName);
FileUtil.writeUtf8String(content, path);
}
private static void writeXmlFile(String xmlDir, String fileName, String content) {
String path = String.format("%s/src/main/resources/%s/%s", OUTPUT_DIR, xmlDir, fileName);
FileUtil.writeUtf8String(content, path);
}
private static void writeCommonFile(String pkgDir, String fileName, String content) {
String pkgPath = pkgDir.replace(".", "/");
String path = String.format("%s/src/main/java/%s/%s/%s",
OUTPUT_DIR,
BASE_PACKAGE.replace(".", "/"),
pkgPath,
fileName);
FileUtil.writeUtf8String(content, path);
System.out.println(" ✔ " + fileName);
}
// ==================== 工具方法 ====================
private static String getControllerPath(String tablePath) {
StringBuilder sb = new StringBuilder(CONTROLLER_BASE_PATH);
if (INCLUDE_MODULE_IN_PATH && StrUtil.isNotBlank(MODULE_NAME)) {
sb.append("/").append(MODULE_NAME);
}
if (StrUtil.isNotBlank(tablePath)) {
sb.append("/").append(tablePath);
}
return sb.toString();
}
private static String getTablePath(TableInfo table) {
return USE_TABLE_NAME_AS_PATH ? table.getTableName().toLowerCase() : table.getVarName();
}
private static String getFullPackage(String pkg) {
return BASE_PACKAGE + "." + pkg;
}
private static String getFullClassName(String pkg, String className) {
return getFullPackage(pkg) + "." + className;
}
private static String tableNameToClassName(String tableName) {
String name = tableName.toLowerCase();
for (String p : TABLE_PREFIX) {
if (name.startsWith(p)) {
name = name.substring(p.length());
break;
}
}
return StrUtil.upperFirst(StrUtil.toCamelCase(name));
}
private static String tableNameToVarName(String tableName) {
String name = tableName.toLowerCase();
for (String p : TABLE_PREFIX) {
if (name.startsWith(p)) {
name = name.substring(p.length());
break;
}
}
return StrUtil.toCamelCase(name);
}
private static String dbTypeToJavaType(String dbType) {
if (dbType == null) {
return "String";
}
switch (dbType.toLowerCase()) {
case "int":
case "integer":
case "tinyint":
case "smallint":
case "mediumint":
return "Integer";
case "bigint":
return "Long";
case "float":
return "Float";
case "double":
return "Double";
case "decimal":
case "numeric":
return "BigDecimal";
case "date":
case "datetime":
case "timestamp":
return "LocalDateTime";
case "bit":
case "boolean":
return "Boolean";
default:
return "String";
}
}
private static String getExampleValue(ColumnInfo col) {
String name = col.getJavaFieldName().toLowerCase();
switch (col.getJavaType()) {
case "String":
if (name.contains("mail") || name.contains("email")) {
return "example@email.com";
}
if (name.contains("phone") || name.contains("mobile")) {
return "13800138000";
}
return "示例" + col.getJavaFieldName();
case "Integer":
return name.contains("age") ? "18" : "1";
case "Long":
return "1";
case "Double":
case "Float":
return "1.0";
case "BigDecimal":
return "0.00";
case "Boolean":
return "true";
case "LocalDateTime":
return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
default:
return null;
}
}
private static boolean isLogicDeleteField(String columnName) {
for (String f : LOGIC_DELETE_FIELDS) {
if (f.equalsIgnoreCase(columnName)) {
return true;
}
}
return false;
}
private static boolean isCreateUpdateField(String columnName) {
for (String f : CREATE_UPDATE_FIELDS) {
if (f.equalsIgnoreCase(columnName)) {
return true;
}
}
return false;
}
private static String getTableComment(TableInfo table) {
return StrUtil.isNotBlank(table.getTableComment()) ? table.getTableComment() : table.getClassName();
}
// ==================== 主键信息类 ====================
private static class PrimaryKeyInfo {
private final List<ColumnInfo> columns = new ArrayList<>();
private boolean composite;
public List<ColumnInfo> getColumns() {
return columns;
}
public boolean isComposite() {
return composite;
}
public void setComposite(boolean composite) {
this.composite = composite;
}
public String getPrimaryKeyField() {
if (columns.isEmpty()) {
return "id";
}
return columns.get(0).getJavaFieldName();
}
public String getPrimaryKeyType() {
if (columns.isEmpty()) {
return "Long";
}
if (composite) {
return "String";
}
return columns.get(0).getJavaType();
}
public String getColumnNames() {
if (columns.isEmpty()) {
return "id";
}
List<String> names = new ArrayList<>();
for (ColumnInfo col : columns) {
names.add(col.getColumnName());
}
return String.join(", ", names);
}
}
// ==================== 主键辅助方法 ====================
private static PrimaryKeyInfo getPrimaryKeyInfo(TableInfo table) {
PrimaryKeyInfo pkInfo = new PrimaryKeyInfo();
if (table.getColumns() == null) {
return pkInfo;
}
for (ColumnInfo col : table.getColumns()) {
if (col.isPrimaryKey()) {
pkInfo.getColumns().add(col);
}
}
pkInfo.setComposite(pkInfo.getColumns().size() > 1);
return pkInfo;
}
private static String getPrimaryKeyType(TableInfo table) {
PrimaryKeyInfo pkInfo = getPrimaryKeyInfo(table);
if (pkInfo.getColumns().isEmpty()) {
return "Long";
}
if (pkInfo.isComposite()) {
return "String";
}
return pkInfo.getColumns().get(0).getJavaType();
}
private static String getPrimaryKeyFieldName(TableInfo table) {
PrimaryKeyInfo pkInfo = getPrimaryKeyInfo(table);
if (pkInfo.getColumns().isEmpty()) {
return "id";
}
if (pkInfo.isComposite()) {
return "id";
}
return pkInfo.getColumns().get(0).getJavaFieldName();
}
// ==================== 内部类 ====================
@Data
@Accessors(chain = true)
private static class TableInfo {
private String tableName;
private String tableComment;
private String className;
private String varName;
private List<ColumnInfo> columns;
}
@Data
@Accessors(chain = true)
private static class ColumnInfo {
private String columnName;
private String dataType;
private String columnComment;
private boolean nullable;
private String columnDefault;
private Integer maxLength;
private boolean primaryKey;
private String javaType;
private String javaFieldName;
}
}