From 9baf682f63569fce77ed5edfca53c560e0791fad Mon Sep 17 00:00:00 2001 From: Bunny <1319900154@qq.com> Date: Wed, 2 Jul 2025 11:23:32 +0800 Subject: [PATCH] =?UTF-8?q?:recycle:=20=E9=87=8D=E6=9E=84=E5=90=8E?= =?UTF-8?q?=E7=AB=AF=E4=BB=A3=E7=A0=81;=E6=B7=BB=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E9=87=8Al;=E4=BC=98=E5=8C=96=E5=8F=AF=E8=AF=BB=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../provider/DatabaseMetadataProvider.java | 180 +++++++++++++----- .../core/provider/SqlMetadataProvider.java | 7 +- .../template/AbstractTemplateGenerator.java | 60 +++--- .../template/VmsTBaseTemplateGenerator.java | 12 +- .../core/vms/VmsCodeGeneratorService.java | 79 ++++---- .../java/cn/bunny/core/vms/VmsZipService.java | 14 +- .../domain/entity/DatabaseInfoMetaData.java | 18 +- .../exception/GeneratorCodeException.java | 6 + .../cn/bunny/service/impl/VmsServiceImpl.java | 69 ++++--- .../cn/bunny/utils/MysqlTypeConvertUtil.java | 13 +- .../src/main/java/cn/bunny/utils/VmsUtil.java | 104 +++++----- 11 files changed, 338 insertions(+), 224 deletions(-) diff --git a/generator-code-server/src/main/java/cn/bunny/core/provider/DatabaseMetadataProvider.java b/generator-code-server/src/main/java/cn/bunny/core/provider/DatabaseMetadataProvider.java index afa3fd7..50cfee9 100644 --- a/generator-code-server/src/main/java/cn/bunny/core/provider/DatabaseMetadataProvider.java +++ b/generator-code-server/src/main/java/cn/bunny/core/provider/DatabaseMetadataProvider.java @@ -7,12 +7,12 @@ import cn.bunny.exception.GeneratorCodeException; import cn.bunny.exception.MetadataNotFoundException; import cn.bunny.exception.MetadataProviderException; import cn.bunny.utils.MysqlTypeConvertUtil; +import com.zaxxer.hikari.HikariDataSource; import lombok.RequiredArgsConstructor; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; -import javax.sql.DataSource; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.ResultSet; @@ -23,7 +23,7 @@ import java.util.*; @RequiredArgsConstructor public class DatabaseMetadataProvider implements IMetadataProvider { - private final DataSource dataSource; + private final HikariDataSource dataSource; @Value("${bunny.master.database}") private String currentDatabase; @@ -57,155 +57,235 @@ public class DatabaseMetadataProvider implements IMetadataProvider { } /** - * 数据库所有的信息 + * 获取数据库的元数据信息 * - * @return 当前连接的数据库信息属性 + * @return DatabaseInfoMetaData 包含数据库基本信息的对象,包括: + * - 数据库产品名称和版本 + * - JDBC驱动名称和版本 + * - 连接URL + * - 用户名 + * - 当前数据库名称 + * @throws GeneratorCodeException 如果获取数据库信息时发生SQL异常 */ public DatabaseInfoMetaData databaseInfoMetaData() { + // 使用try-with-resources确保Connection自动关闭 try (Connection connection = dataSource.getConnection()) { + // 获取数据库的元数据对象 DatabaseMetaData metaData = connection.getMetaData(); + // 使用Builder模式构建数据库信息对象 return DatabaseInfoMetaData.builder() + // 数据库产品名称(如MySQL, Oracle等) .databaseProductName(metaData.getDatabaseProductName()) + // 数据库产品版本号 .databaseProductVersion(metaData.getDatabaseProductVersion()) + // JDBC驱动名称 .driverName(metaData.getDriverName()) + // JDBC驱动版本 .driverVersion(metaData.getDriverVersion()) + // 数据库连接URL .url(metaData.getURL()) + // 连接使用的用户名 .username(metaData.getUserName()) + // 当前使用的数据库名称(由类成员变量提供) .currentDatabase(currentDatabase) .build(); } catch (SQLException e) { + // 将SQL异常转换为自定义的业务异常 throw new GeneratorCodeException("Get database info error:" + e.getMessage()); } } /** - * 解析 sql 表信息 + * 根据表名标识符获取单个表的元数据信息 * - * @param identifier 表名称或sql - * @return 表西悉尼 + * @param identifier 要查询的表名(大小写敏感度取决于数据库实现) + * @return TableMetaData 包含表元数据的对象,包括: + * - 表名 + * - 表注释/备注 + * - 表所属目录(通常是数据库名) + * - 表类型(通常为"TABLE") + * @throws MetadataNotFoundException 当指定的表不存在时抛出 + * @throws GeneratorCodeException 当查询过程中发生其他异常时抛出 */ - @Override public TableMetaData getTableMetadata(String identifier) { + // 声明返回对象 TableMetaData tableMetaData; + // 使用try-with-resources自动管理数据库连接 try (Connection connection = dataSource.getConnection()) { + // 获取数据库元数据对象 DatabaseMetaData metaData = connection.getMetaData(); + + /* + 查询指定表名的元数据信息 + 参数说明: + 1. null - 不限制数据库目录(catalog) + 2. null - 不限制模式(schema) + 3. identifier - 要查询的精确表名 + 4. new String[]{"TABLE"} - 只查询基本表类型(排除视图等) + */ ResultSet tables = metaData.getTables(null, null, identifier, new String[]{"TABLE"}); - // 获取表的注释信息 + // 检查是否找到匹配的表 if (tables.next()) { - // 备注信息 + // 获取表的注释/备注(可能为null) String remarks = tables.getString("REMARKS"); - // 数组名称 + // 获取表所属的目录(通常对应数据库名) String tableCat = tables.getString("TABLE_CAT"); - // 通常是"TABLE" + // 获取表类型(正常表应为"TABLE") String tableType = tables.getString("TABLE_TYPE"); + // 使用Builder模式构建表元数据对象 tableMetaData = TableMetaData.builder() - .tableName(identifier) - .comment(remarks) - .tableCat(tableCat) - .tableType(tableType) + .tableName(identifier) // 使用传入的表名标识符 + .comment(remarks) // 设置表注释 + .tableCat(tableCat) // 设置表所属目录 + .tableType(tableType) // 设置表类型 .build(); } else { + // 如果结果集为空,抛出表不存在异常 throw new MetadataNotFoundException("Table not found: " + identifier); } return tableMetaData; } catch (Exception e) { - throw new GeneratorCodeException(e.getMessage()); + // 捕获所有异常并转换为业务异常 + throw new GeneratorCodeException("Failed to get metadata for table: " + identifier + ". Error: " + e.getMessage(), e); } } /** - * 获取[当前/所有]数据库表 + * 批量获取指定数据库中所有表的元数据信息 + * 获取指定数据库中的所有表信息 * - * @return 所有表信息 + * @param dbName 数据库名称 + * @return 包含所有表元数据的列表,每个表的信息封装在TableMetaData对象中 + * @throws MetadataProviderException 如果获取元数据过程中发生异常 */ public List getTableMetadataBatch(String dbName) { + // 初始化返回结果列表 List allTableInfo = new ArrayList<>(); + // 使用try-with-resources确保Connection资源自动关闭 try (Connection conn = dataSource.getConnection()) { + // 获取数据库的元数据对象 DatabaseMetaData metaData = conn.getMetaData(); + + /* + 参数说明: + 1. dbName - 数据库/目录名称,null表示忽略 + 2. null - 模式/模式名称,null表示忽略 + 3. "%" - 表名称模式,"%"表示所有表 + 4. new String[]{"TABLE"} - 类型数组,这里只查询普通表 + */ ResultSet tables = metaData.getTables(dbName, null, "%", new String[]{"TABLE"}); + // 遍历查询结果集中的所有表 while (tables.next()) { - String tableName = tables.getString("TABLE_NAME"); - String remarks = tables.getString("REMARKS"); - String tableCat = tables.getString("TABLE_CAT"); - String tableType = tables.getString("TABLE_TYPE"); + // 从结果集中获取表的各种属性 + String tableName = tables.getString("TABLE_NAME"); // 表名 + String remarks = tables.getString("REMARKS"); // 表备注/注释 + String tableCat = tables.getString("TABLE_CAT"); // 表所属的目录(通常是数据库名) + String tableType = tables.getString("TABLE_TYPE"); // 表类型(这里应该是"TABLE") + // 使用Builder模式创建TableMetaData对象并设置属性 TableMetaData tableMetaData = TableMetaData.builder() - .tableName(tableName) - .comment(remarks) - .tableCat(tableCat) - .tableType(tableType) + .tableName(tableName) // 设置表名 + .comment(remarks) // 设置表注释 + .tableCat(tableCat) // 设置表所属目录 + .tableType(tableType) // 设置表类型 .build(); + // 将表元数据对象添加到结果列表 allTableInfo.add(tableMetaData); } } catch (Exception e) { + // 捕获任何异常并转换为自定义异常抛出 throw new MetadataProviderException("Failed to get batch table metadata", e); } + // 返回包含所有表元数据的列表 return allTableInfo; } /** - * 获取当前表的列属性 + * 获取指定表的所有列信息列表 * - * @param identifier 表名称或sql - * @return 当前表所有的列内容 + * @param identifier 要查询的表名(大小写敏感度取决于数据库实现) + * @return 包含所有列元数据的列表,每个列的信息封装在ColumnMetaData对象中 + * @throws MetadataProviderException 当获取列元数据过程中发生异常时抛出 */ - @Override public List getColumnInfoList(String identifier) { + // 使用try-with-resources确保Connection自动关闭 try (Connection connection = dataSource.getConnection()) { + // 获取数据库元数据对象 DatabaseMetaData metaData = connection.getMetaData(); + + // 使用LinkedHashMap保持列的顺序与数据库一致 Map map = new LinkedHashMap<>(); - // 当前表的主键 + + // 获取当前表的所有主键列名集合 Set primaryKeyColumns = getPrimaryKeys(identifier); - // 当前表的列信息 + /* + 获取指定表的所有列信息 + 参数说明: + 1. null - 不限制数据库目录(catalog) + 2. null - 不限制模式(schema) + 3. identifier - 要查询的表名 + 4. null - 不限制列名模式(获取所有列) + */ try (ResultSet columnsRs = metaData.getColumns(null, null, identifier, null)) { + // 遍历结果集中的所有列 while (columnsRs.next()) { ColumnMetaData column = new ColumnMetaData(); - // 列字段 - String columnName = columnsRs.getString("COLUMN_NAME"); - // 数据库类型 - String typeName = columnsRs.getString("TYPE_NAME"); - // 设置列字段 - column.setColumnName(columnName); - // 列字段转成 下划线 -> 小驼峰 - column.setLowercaseName(MysqlTypeConvertUtil.convertToCamelCase(column.getColumnName())); - // 列字段转成 下划线 -> 大驼峰名称 - column.setUppercaseName(MysqlTypeConvertUtil.convertToCamelCase(column.getColumnName(), true)); - // 字段类型 + // 获取列的基本信息 + String columnName = columnsRs.getString("COLUMN_NAME"); // 列名(原始字段名) + String typeName = columnsRs.getString("TYPE_NAME"); // 数据库类型名称 + + // 设置列基本信息 + column.setColumnName(columnName); // 设置原始列名 + + // 列名格式转换:下划线命名 -> 小驼峰命名(首字母小写) + String lowercaseName = MysqlTypeConvertUtil.convertToCamelCase(column.getColumnName(), false); + column.setLowercaseName(lowercaseName); + + // 列名格式转换:下划线命名 -> 大驼峰命名(首字母大写) + String uppercaseName = MysqlTypeConvertUtil.convertToCamelCase(column.getColumnName(), true); + column.setUppercaseName(uppercaseName); + + // 设置数据库类型 column.setJdbcType(typeName); - // 字段类型转 Java 类型 + + // 数据库类型 -> Java类型转换 String javaType = MysqlTypeConvertUtil.convertToJavaType(typeName); column.setJavaType(javaType); - // 字段类型转 JavaScript 类型 + + // Java类型 -> JavaScript类型转换(首字母小写) column.setJavascriptType(StringUtils.uncapitalize(javaType)); - // 备注信息 + + // 设置列注释(可能为null) column.setComment(columnsRs.getString("REMARKS")); - // 确保 primaryKeyColumns 不为空 + // 如果主键集合不为空,检查当前列是否是主键 if (!primaryKeyColumns.isEmpty()) { - // 是否是主键 boolean isPrimaryKey = primaryKeyColumns.contains(columnName); column.setIsPrimaryKey(isPrimaryKey); } + // 将列信息存入Map,避免重复(使用putIfAbsent保证不覆盖已有值) map.putIfAbsent(column.getColumnName(), column); } } - return new ArrayList<>(map.values()); + // 将Map中的值转换为List返回 + return map.values().stream().distinct().toList(); } catch (Exception e) { + // 捕获所有异常并转换为自定义异常,包含表名和原始异常信息 throw new MetadataProviderException("Failed to get table metadata for: " + identifier, e); } } diff --git a/generator-code-server/src/main/java/cn/bunny/core/provider/SqlMetadataProvider.java b/generator-code-server/src/main/java/cn/bunny/core/provider/SqlMetadataProvider.java index c23c0b7..dd7ea82 100644 --- a/generator-code-server/src/main/java/cn/bunny/core/provider/SqlMetadataProvider.java +++ b/generator-code-server/src/main/java/cn/bunny/core/provider/SqlMetadataProvider.java @@ -99,9 +99,12 @@ public class SqlMetadataProvider implements IMetadataProvider { columnInfo.setJavascriptType(StringUtils.uncapitalize(javaType)); // 列字段转成 下划线 -> 小驼峰 - columnInfo.setLowercaseName(MysqlTypeConvertUtil.convertToCamelCase(column.getColumnName())); + String lowercaseName = MysqlTypeConvertUtil.convertToCamelCase(column.getColumnName(), false); + columnInfo.setLowercaseName(lowercaseName); + // 列字段转成 下划线 -> 大驼峰名称 - columnInfo.setUppercaseName(MysqlTypeConvertUtil.convertToCamelCase(column.getColumnName(), true)); + String uppercaseName = MysqlTypeConvertUtil.convertToCamelCase(column.getColumnName(), true); + columnInfo.setUppercaseName(uppercaseName); // 解析注释 List columnSpecs = column.getColumnSpecs(); diff --git a/generator-code-server/src/main/java/cn/bunny/core/template/AbstractTemplateGenerator.java b/generator-code-server/src/main/java/cn/bunny/core/template/AbstractTemplateGenerator.java index 00a9c26..bc65880 100644 --- a/generator-code-server/src/main/java/cn/bunny/core/template/AbstractTemplateGenerator.java +++ b/generator-code-server/src/main/java/cn/bunny/core/template/AbstractTemplateGenerator.java @@ -6,6 +6,7 @@ import org.apache.velocity.VelocityContext; import java.io.StringWriter; import java.util.List; +import java.util.stream.Collectors; /** * 模板方法模式 @@ -16,7 +17,7 @@ public abstract class AbstractTemplateGenerator { /** * 添加生成内容 */ - abstract void addContext(VelocityContext context); + protected abstract void addContext(VelocityContext context); /** * Velocity 生成模板 @@ -24,39 +25,50 @@ public abstract class AbstractTemplateGenerator { * @param context VelocityContext * @param writer StringWriter 写入 */ - abstract void templateMerge(VelocityContext context, StringWriter writer); + protected abstract void templateMerge(VelocityContext context, StringWriter writer); /** - * 生成模板 + * 生成代码模板 * - * @param tableMetaData 表属性 - * @param columnInfoList 列属性数组 - * @return StringWriter + * @param tableMeta 表元数据 + * @param columns 列信息列表 + * @return 生成的代码内容 */ - public final StringWriter generatorCodeTemplate(TableMetaData tableMetaData, List columnInfoList) { + public StringWriter generateCode(TableMetaData tableMeta, List columns) { VelocityContext context = new VelocityContext(); + prepareVelocityContext(context, tableMeta, columns); + return mergeTemplate(context); + } - // 添加要生成的属性 - StringWriter writer = new StringWriter(); - List list = columnInfoList.stream().map(ColumnMetaData::getColumnName).distinct().toList(); - - // vm 不能直接写 `{` 需要转换下 + /** + * 准备Velocity上下文数据 + */ + private void prepareVelocityContext(VelocityContext context, TableMetaData tableMeta, List columns) { + // 特殊字符处理 context.put("leftBrace", "{"); + context.put("tableName", tableMeta.getTableName()); + context.put("columnInfoList", columns); + context.put("baseColumnList", getDistinctColumnNames(columns)); + addContext(context); // 子类可扩展 + } - // 当前的表名 - context.put("tableName", tableMetaData.getTableName()); - - // 当前表的列信息 - context.put("columnInfoList", columnInfoList); - - // 数据库sql列 - context.put("baseColumnList", String.join(",", list)); - - // 添加需要生成的内容 - addContext(context); + /** + * 获取去重的列名列表 + */ + private String getDistinctColumnNames(List columns) { + return columns.stream() + .map(ColumnMetaData::getColumnName) + .distinct() + .collect(Collectors.joining(",")); + } + /** + * 合并Velocity模板 + */ + private StringWriter mergeTemplate(VelocityContext context) { + StringWriter writer = new StringWriter(); templateMerge(context, writer); - return writer; } + } diff --git a/generator-code-server/src/main/java/cn/bunny/core/template/VmsTBaseTemplateGenerator.java b/generator-code-server/src/main/java/cn/bunny/core/template/VmsTBaseTemplateGenerator.java index 174faf8..a274501 100644 --- a/generator-code-server/src/main/java/cn/bunny/core/template/VmsTBaseTemplateGenerator.java +++ b/generator-code-server/src/main/java/cn/bunny/core/template/VmsTBaseTemplateGenerator.java @@ -38,7 +38,7 @@ public class VmsTBaseTemplateGenerator extends AbstractTemplateGenerator { * @param context VelocityContext */ @Override - void addContext(VelocityContext context) { + public void addContext(VelocityContext context) { // 当前的表名 String tableName = tableMetaData.getTableName(); // 表的注释内容 @@ -61,12 +61,12 @@ public class VmsTBaseTemplateGenerator extends AbstractTemplateGenerator { context.put("package", dto.getPackageName()); // 将类名称转成小驼峰 - String toCamelCase = MysqlTypeConvertUtil.convertToCamelCase(tableName); - context.put("classLowercaseName", toCamelCase); + String lowerCamelCase = MysqlTypeConvertUtil.convertToCamelCase(tableName, false); + context.put("classLowercaseName", lowerCamelCase); // 将类名称转成大驼峰 - String convertToCamelCase = MysqlTypeConvertUtil.convertToCamelCase(tableName, true); - context.put("classUppercaseName", convertToCamelCase); + String upperCameCase = MysqlTypeConvertUtil.convertToCamelCase(tableName, true); + context.put("classUppercaseName", upperCameCase); } /** @@ -76,7 +76,7 @@ public class VmsTBaseTemplateGenerator extends AbstractTemplateGenerator { * @param writer StringWriter 写入 */ @Override - void templateMerge(VelocityContext context, StringWriter writer) { + public void templateMerge(VelocityContext context, StringWriter writer) { Template servicePathTemplate = Velocity.getTemplate("vms/" + path, "UTF-8"); servicePathTemplate.merge(context, writer); } diff --git a/generator-code-server/src/main/java/cn/bunny/core/vms/VmsCodeGeneratorService.java b/generator-code-server/src/main/java/cn/bunny/core/vms/VmsCodeGeneratorService.java index 0c71e05..9f4f616 100644 --- a/generator-code-server/src/main/java/cn/bunny/core/vms/VmsCodeGeneratorService.java +++ b/generator-code-server/src/main/java/cn/bunny/core/vms/VmsCodeGeneratorService.java @@ -12,73 +12,64 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; -import java.io.StringWriter; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.stream.Collectors; +import java.util.stream.Stream; /** - * 负责处理代码生成逻辑的服务类 + * 代码生成服务,负责根据数据库表结构生成各种代码模板 */ @Service @RequiredArgsConstructor public class VmsCodeGeneratorService { - private final DatabaseMetadataProvider databaseMetadataProvider; private final SqlMetadataProvider sqlMetadataProvider; /** - * 根据DTO生成代码模板 + * 根据参数生成代码模板 * - * @param dto 包含生成参数的数据传输对象 - * @return 按表名分组的生成的代码模板列表 + * @param dto 包含表名、路径、SQL等生成参数 + * @return 按表名分组的代码模板列表 */ public Map> generateCode(VmsArgumentDto dto) { - // 提前获取可复用的数据 - final String sql = dto.getSql(); - final List tableNames = dto.getTableNames(); - final List paths = dto.getPath(); - - return tableNames.parallelStream() // 使用并行流提高多表处理效率 - .map(tableName -> { - // 获取表元数据和列信息 - TableMetaData tableMetaData = getTableMetadata(dto, tableName); - List columnInfoList = getColumnInfoList(sql, tableName); - - // 为每个路径生成模板 - return paths.stream() - .map(path -> generateTemplate(dto, path, tableMetaData, columnInfoList)) - .toList(); - }) - .flatMap(List::stream) + return dto.getTableNames().parallelStream() // 并行处理多表 + .flatMap(tableName -> generateTemplatesForTable(dto, tableName)) .collect(Collectors.groupingBy(GeneratorVo::getTableName)); } /** - * 生成单个模板 + * 为单个表生成所有路径的模板 */ - private GeneratorVo generateTemplate(VmsArgumentDto dto, String path, - TableMetaData tableMetaData, List columnInfoList) { - VmsTBaseTemplateGenerator generator = new VmsTBaseTemplateGenerator(dto, path, tableMetaData); - StringWriter writer = generator.generatorCodeTemplate(tableMetaData, columnInfoList); - String processedPath = VmsUtil.handleVmFilename(path, tableMetaData.getTableName()); + private Stream generateTemplatesForTable(VmsArgumentDto dto, String tableName) { + TableMetaData tableMeta = getTableMetadata(dto, tableName); + List columns = getColumnInfoList(dto, tableName); + + return dto.getPath().stream() + .map(path -> buildTemplateVo(dto, path, tableMeta, columns)); + } + + /** + * 构建单个模板VO对象 + */ + private GeneratorVo buildTemplateVo(VmsArgumentDto dto, String path, + TableMetaData tableMeta, List columns) { + VmsTBaseTemplateGenerator generator = new VmsTBaseTemplateGenerator(dto, path, tableMeta); + String code = generator.generateCode(tableMeta, columns).toString(); + String processedPath = VmsUtil.processVmPath(dto, path, tableMeta.getTableName()); return GeneratorVo.builder() .id(UUID.randomUUID().toString()) - .code(writer.toString()) - .comment(tableMetaData.getComment()) - .tableName(tableMetaData.getTableName()) + .code(code) + .comment(tableMeta.getComment()) + .tableName(tableMeta.getTableName()) .path(processedPath) .build(); } /** - * 获取表元数据 - * - * @param dto 生成参数 - * @param tableName 表名 - * @return 表元数据对象 + * 获取表元数据(根据是否有SQL选择不同数据源) */ private TableMetaData getTableMetadata(VmsArgumentDto dto, String tableName) { return StringUtils.hasText(dto.getSql()) @@ -87,16 +78,12 @@ public class VmsCodeGeneratorService { } /** - * 获取列信息列表 - * - * @param sql SQL语句 - * @param tableName 表名 - * @return 列元数据列表 + * 获取列信息(根据是否有SQL选择不同数据源) */ - private List getColumnInfoList(String sql, String tableName) { - return StringUtils.hasText(sql) - ? sqlMetadataProvider.getColumnInfoList(sql) - : databaseMetadataProvider.getColumnInfoList(tableName).stream().distinct().toList(); + private List getColumnInfoList(VmsArgumentDto dto, String tableName) { + return StringUtils.hasText(dto.getSql()) + ? sqlMetadataProvider.getColumnInfoList(dto.getSql()) + : databaseMetadataProvider.getColumnInfoList(tableName); } } \ No newline at end of file diff --git a/generator-code-server/src/main/java/cn/bunny/core/vms/VmsZipService.java b/generator-code-server/src/main/java/cn/bunny/core/vms/VmsZipService.java index 784e8ee..a5444fa 100644 --- a/generator-code-server/src/main/java/cn/bunny/core/vms/VmsZipService.java +++ b/generator-code-server/src/main/java/cn/bunny/core/vms/VmsZipService.java @@ -33,7 +33,8 @@ public class VmsZipService { */ public byte[] createZipFile(VmsArgumentDto dto) { // 将二维代码生成结果扁平化为一维列表 - List generatorVoList = codeGeneratorService.generateCode(dto).values().stream() + List generatorVoList = codeGeneratorService.generateCode(dto) + .values().stream() .flatMap(Collection::stream) .toList(); @@ -56,17 +57,20 @@ public class VmsZipService { * @throws RuntimeException 当文件添加失败时抛出,包含失败文件路径信息 */ private void addToZip(ZipOutputStream zipOutputStream, GeneratorVo generatorVo) { - try { - // 标准化文件路径:移除Velocity模板扩展名 - String path = generatorVo.getPath().replace(".vm", ""); + final String FILE_EXTENSION = ".vm"; + String voPath = generatorVo.getPath(); + + // 标准化文件路径:移除Velocity模板扩展名 + String path = voPath.replace(FILE_EXTENSION, ""); + try { zipOutputStream.putNextEntry(new ZipEntry(path)); // 以UTF-8编码写入文件内容,避免乱码问题 zipOutputStream.write(generatorVo.getCode().getBytes(StandardCharsets.UTF_8)); zipOutputStream.closeEntry(); } catch (IOException e) { - throw new RuntimeException("Failed to add file to ZIP: " + generatorVo.getPath(), e); + throw new RuntimeException("Failed to add file to ZIP: " + voPath, e); } } } \ No newline at end of file diff --git a/generator-code-server/src/main/java/cn/bunny/domain/entity/DatabaseInfoMetaData.java b/generator-code-server/src/main/java/cn/bunny/domain/entity/DatabaseInfoMetaData.java index 1b2af63..33080e9 100644 --- a/generator-code-server/src/main/java/cn/bunny/domain/entity/DatabaseInfoMetaData.java +++ b/generator-code-server/src/main/java/cn/bunny/domain/entity/DatabaseInfoMetaData.java @@ -1,5 +1,6 @@ package cn.bunny.domain.entity; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -11,30 +12,31 @@ import java.util.List; @Builder @AllArgsConstructor @NoArgsConstructor +@Schema(name = "DatabaseInfoMetaData", description = "数据库信息") public class DatabaseInfoMetaData { - /* 数据库所有的数据库 */ + @Schema(name = "databaseList", description = "数据库所有的数据库") List databaseList; - /* 数据库产品名称 */ + @Schema(name = "databaseProductName", description = "数据库产品名称") private String databaseProductName; - /* 数据库产品版本 */ + @Schema(name = "databaseProductVersion", description = "数据库产品版本") private String databaseProductVersion; - /* 驱动名称 */ + @Schema(name = "driverName", description = "驱动名称") private String driverName; - /* 数据库驱动版本 */ + @Schema(name = "driverVersion", description = "数据库驱动版本") private String driverVersion; - /* 数据链接url */ + @Schema(name = "url", description = "数据链接url") private String url; - /* 数据库用户 */ + @Schema(name = "username", description = "数据库用户") private String username; - /* 当前数据库名称 */ + @Schema(name = "currentDatabase", description = "当前数据库名称") private String currentDatabase; } \ No newline at end of file diff --git a/generator-code-server/src/main/java/cn/bunny/exception/GeneratorCodeException.java b/generator-code-server/src/main/java/cn/bunny/exception/GeneratorCodeException.java index 656e4b9..b24d9c6 100644 --- a/generator-code-server/src/main/java/cn/bunny/exception/GeneratorCodeException.java +++ b/generator-code-server/src/main/java/cn/bunny/exception/GeneratorCodeException.java @@ -37,4 +37,10 @@ public class GeneratorCodeException extends RuntimeException { this.message = codeEnum.getMessage(); this.resultCodeEnum = codeEnum; } + + public GeneratorCodeException(String message, Exception exception) { + super(message); + this.message = message; + log.error(message, exception); + } } diff --git a/generator-code-server/src/main/java/cn/bunny/service/impl/VmsServiceImpl.java b/generator-code-server/src/main/java/cn/bunny/service/impl/VmsServiceImpl.java index 6d81ca7..ddcdbf5 100644 --- a/generator-code-server/src/main/java/cn/bunny/service/impl/VmsServiceImpl.java +++ b/generator-code-server/src/main/java/cn/bunny/service/impl/VmsServiceImpl.java @@ -7,8 +7,6 @@ import cn.bunny.domain.vo.GeneratorVo; import cn.bunny.domain.vo.VmsPathVo; import cn.bunny.service.VmsService; import cn.bunny.utils.ResourceFileUtil; -import cn.bunny.utils.VmsUtil; -import cn.hutool.crypto.digest.MD5; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; @@ -17,6 +15,7 @@ import org.springframework.stereotype.Service; import java.util.List; import java.util.Map; +import java.util.UUID; import java.util.stream.Collectors; /** @@ -35,46 +34,64 @@ public class VmsServiceImpl implements VmsService { } /** - * 获取vms文件路径 + * 获取VMS资源文件路径列表并按类型分组 * - * @return vms下的文件路径 + * @return 按类型分组的VMS路径Map,key为类型,value为对应类型的VMS路径列表 + * @throws RuntimeException 当获取资源路径失败时抛出 */ - @Override public Map> vmsResourcePathList() { - List vmsRelativeFiles; - Map> listMap; - try { - vmsRelativeFiles = ResourceFileUtil.getRelativeFiles("vms"); - listMap = vmsRelativeFiles.stream() - .map(vmFile -> { - String[] filepathList = vmFile.split("/"); - String filename = filepathList[filepathList.length - 1].replace(".vm", ""); + // 1. 获取vms目录下所有相对路径文件列表 + List vmsRelativeFiles = ResourceFileUtil.getRelativeFiles("vms"); - return VmsPathVo.builder() - .id(VmsUtil.generateDivId()) - .name(vmFile) - .label(filename) - .type(filepathList[0]) - .build(); - }) - .collect(Collectors.groupingBy(VmsPathVo::getType)); + // 2. 处理文件路径并分组 + return vmsRelativeFiles.stream() + .map(this::convertToVmsPathVo) // 转换为VO对象 + .collect(Collectors.groupingBy(VmsPathVo::getType)); // 按类型分组 } catch (Exception e) { - throw new RuntimeException("Get error of VMS path:" + e.getMessage()); + throw new RuntimeException("Failed to get VMS resource paths: " + e.getMessage(), e); } + } + /** + * 将文件路径字符串转换为VmsPathVo对象 + * + * @param vmFile 文件相对路径字符串 + * @return 转换后的VmsPathVo对象 + */ + private VmsPathVo convertToVmsPathVo(String vmFile) { + // 分割文件路径 + String[] filepathList = vmFile.split("/"); - return listMap; + // 获取文件名(不含扩展名) + String filename = filepathList[filepathList.length - 1].replace(".vm", ""); + + /* + 生成前端可用的唯一DOM元素ID + 格式: "id-" + 无横线的UUID (例如: "id-550e8400e29b41d4a716446655440000") + + 用途: + 1. 用于关联label标签和input元素的for属性 + 2. 确保列表项在前端有唯一标识 + */ + String id = "id-" + UUID.randomUUID().toString().replace("-", ""); + + return VmsPathVo.builder() + .id(id) + .name(vmFile) + .label(filename) + .type(filepathList[0]) // 使用路径的第一部分作为类型 + .build(); } @Override public ResponseEntity downloadByZip(VmsArgumentDto dto) { + // 创建ZIP文件 byte[] zipBytes = zipService.createZipFile(dto); // 下载文件名称 - long currentTimeMillis = System.currentTimeMillis(); - String digestHex = MD5.create().digestHex(currentTimeMillis + ""); - String generateZipFilename = "code-" + digestHex.substring(0, 6) + ".zip"; + String uuid = UUID.randomUUID().toString().split("-")[0]; + String generateZipFilename = "code-" + uuid + ".zip"; // 设置响应头 HttpHeaders headers = new HttpHeaders(); diff --git a/generator-code-server/src/main/java/cn/bunny/utils/MysqlTypeConvertUtil.java b/generator-code-server/src/main/java/cn/bunny/utils/MysqlTypeConvertUtil.java index fbb8da6..c11197d 100644 --- a/generator-code-server/src/main/java/cn/bunny/utils/MysqlTypeConvertUtil.java +++ b/generator-code-server/src/main/java/cn/bunny/utils/MysqlTypeConvertUtil.java @@ -5,7 +5,7 @@ import org.assertj.core.util.introspection.CaseFormatUtils; /* 类型转换,数据库转Java类型等 */ public class MysqlTypeConvertUtil { - + /** * 将数据库类型转换为Java类型 */ @@ -29,13 +29,6 @@ public class MysqlTypeConvertUtil { }; } - /** - * 下划线命名转驼峰命名 - */ - public static String convertToCamelCase(String name) { - return convertToCamelCase(name, false); - } - /** * 下划线命名转驼峰命名 * @@ -51,9 +44,7 @@ public class MysqlTypeConvertUtil { String lowerCamelCase = CaseFormatUtils.toCamelCase(name); // 首字母不大写 - if (!firstLetterCapital) { - return lowerCamelCase; - } + if (!firstLetterCapital) return lowerCamelCase; // 将小驼峰转成大驼峰 return CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, lowerCamelCase); diff --git a/generator-code-server/src/main/java/cn/bunny/utils/VmsUtil.java b/generator-code-server/src/main/java/cn/bunny/utils/VmsUtil.java index deff0b2..c04f1b5 100644 --- a/generator-code-server/src/main/java/cn/bunny/utils/VmsUtil.java +++ b/generator-code-server/src/main/java/cn/bunny/utils/VmsUtil.java @@ -1,13 +1,15 @@ package cn.bunny.utils; +import cn.bunny.domain.dto.VmsArgumentDto; import com.google.common.base.CaseFormat; import java.util.Map; -import java.util.UUID; +/** + * 代码生成工具类 + */ public class VmsUtil { - - private static final Map TYPE_MAPPINGS = Map.of( + private static final Map FILE_TYPE_SUFFIXES = Map.of( "controller", "Controller", "service", "Service", "serviceImpl", "ServiceImpl", @@ -18,55 +20,65 @@ public class VmsUtil { ); /** - * 处理 vm 文件名 + * 处理模板文件路径和命名 * - * @param path 文件路径 - * @param className 类名 + * @param dto 生成参数 + * @param path 原始模板路径 + * @param tableName 数据库表名 + * @return 处理后的文件路径 */ - public static String handleVmFilename(String path, String className) { - String[] splitPaths = path.split("/"); - int splitPathsSize = splitPaths.length - 1; + public static String processVmPath(VmsArgumentDto dto, String path, String tableName) { + String className = removeTablePrefixes(dto, tableName); + String lowerCamelCase = MysqlTypeConvertUtil.convertToCamelCase(tableName, false); + String[] pathParts = path.replace("$className", lowerCamelCase).split("/"); - // 大驼峰名称 - String CamelCase = MysqlTypeConvertUtil.convertToCamelCase(className, true); - // 小驼峰名称 - String smallCamelCase = MysqlTypeConvertUtil.convertToCamelCase(className); + // 处理文件名 + pathParts[pathParts.length - 1] = processFilename( + pathParts[pathParts.length - 1], + className + ); - // 当前文件名 - String filename = splitPaths[splitPathsSize]; - filename = filename.replace(".vm", ""); - - String[] split = filename.split("\\."); - // 文件名称 - String name = split[0]; - // 文件扩展名 - String extension = ""; - if (split.length >= 2) { - extension = split[1]; - } - - // 判断是否是 Java 或者 xml 文件 - String typeMappingsFilename = TYPE_MAPPINGS.get(name); - typeMappingsFilename = typeMappingsFilename == null ? "" : typeMappingsFilename; - if (filename.contains("java") || filename.contains("xml")) { - filename = CamelCase + typeMappingsFilename + "." + extension; - } - - if ((filename.contains("vue") || filename.contains("ts") || filename.contains("js")) - && !filename.contains("index")) { - filename = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, smallCamelCase) + "-" + name + "." + extension; - } - - splitPaths[splitPathsSize] = filename; - return String.join("/", splitPaths); + return String.join("/", pathParts); } /** - * 生成前端标签上的id - * - * @return id-UUID + * 移除表前缀 */ - public static String generateDivId() { - return "id-" + UUID.randomUUID().toString().replace("-", ""); + private static String removeTablePrefixes(VmsArgumentDto dto, String tableName) { + String[] prefixes = dto.getTablePrefixes().split("[,,]"); + for (String prefix : prefixes) { + if (tableName.startsWith(prefix)) { + return tableName.substring(prefix.length()); + } + } + return tableName; } -} + + /** + * 处理文件名生成 + */ + private static String processFilename(String filename, String tableName) { + filename = filename.replace(".vm", ""); + String[] parts = filename.split("\\."); + String baseName = parts[0]; + String extension = parts.length > 1 ? parts[1] : ""; + + String upperCamelCase = MysqlTypeConvertUtil.convertToCamelCase(tableName, true); + String lowerCamelCase = MysqlTypeConvertUtil.convertToCamelCase(tableName, false); + + // 如果包含Java和xml需要进行处理 + if (filename.contains("java") || filename.contains("xml")) { + return upperCamelCase + FILE_TYPE_SUFFIXES.getOrDefault(baseName, "") + "." + extension; + } + + if (filename.equals("api.ts") || filename.equals("store.ts")) { + return lowerCamelCase + ".ts"; + } + + if (filename.equals("dialog.vue")) { + return CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, lowerCamelCase) + "-dialog.vue"; + } + + return filename; + } +} \ No newline at end of file