diff --git a/generator-code-server/generator-code/src/main/java/cn/bunny/config/VmsHolder.java b/generator-code-server/generator-code/src/main/java/cn/bunny/config/VmsHolder.java index 0272948..b0e50d7 100644 --- a/generator-code-server/generator-code/src/main/java/cn/bunny/config/VmsHolder.java +++ b/generator-code-server/generator-code/src/main/java/cn/bunny/config/VmsHolder.java @@ -12,7 +12,7 @@ public class VmsHolder { @PostConstruct public void init() { Properties prop = new Properties(); - prop.put("resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); + prop.put("resource.loader.file.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); Velocity.init(prop); } } diff --git a/generator-code-server/generator-code/src/main/java/cn/bunny/controller/VmsController.java b/generator-code-server/generator-code/src/main/java/cn/bunny/controller/VmsController.java index 9d82778..064cb22 100644 --- a/generator-code-server/generator-code/src/main/java/cn/bunny/controller/VmsController.java +++ b/generator-code-server/generator-code/src/main/java/cn/bunny/controller/VmsController.java @@ -32,8 +32,8 @@ public class VmsController { @Operation(summary = "生成代码", description = "生成代码") @PostMapping("generator") - public Result> generator(@Valid @RequestBody VmsArgumentDto dto) { - List list = vmsService.generator(dto); + public Result>> generator(@Valid @RequestBody VmsArgumentDto dto) { + Map> list = vmsService.generator(dto); return Result.success(list); } diff --git a/generator-code-server/generator-code/src/main/java/cn/bunny/core/factory/ConcreteSqlParserDatabaseInfo.java b/generator-code-server/generator-code/src/main/java/cn/bunny/core/factory/ConcreteSqlParserDatabaseInfo.java index 27732b3..43ba162 100644 --- a/generator-code-server/generator-code/src/main/java/cn/bunny/core/factory/ConcreteSqlParserDatabaseInfo.java +++ b/generator-code-server/generator-code/src/main/java/cn/bunny/core/factory/ConcreteSqlParserDatabaseInfo.java @@ -16,7 +16,7 @@ import java.util.regex.Pattern; @Component public class ConcreteSqlParserDatabaseInfo extends AbstractDatabaseInfo { - + /** * 解析 sql 表信息 * diff --git a/generator-code-server/generator-code/src/main/java/cn/bunny/core/template/VmsArgumentDtoBaseVmsGeneratorTemplate.java b/generator-code-server/generator-code/src/main/java/cn/bunny/core/template/VmsArgumentDtoBaseVmsGeneratorTemplate.java index 949925d..8214cc3 100644 --- a/generator-code-server/generator-code/src/main/java/cn/bunny/core/template/VmsArgumentDtoBaseVmsGeneratorTemplate.java +++ b/generator-code-server/generator-code/src/main/java/cn/bunny/core/template/VmsArgumentDtoBaseVmsGeneratorTemplate.java @@ -18,14 +18,17 @@ public class VmsArgumentDtoBaseVmsGeneratorTemplate extends AbstractVmsGenerator private final VmsArgumentDto dto; private final String path; + private final String tableName; /** - * @param dto 类名称可以自定义,格式为 xxx_xxx - * @param path 当前路径 + * @param dto 类名称可以自定义,格式为 xxx_xxx + * @param path 当前路径 + * @param tableName 表名称 */ - public VmsArgumentDtoBaseVmsGeneratorTemplate(VmsArgumentDto dto, String path) { + public VmsArgumentDtoBaseVmsGeneratorTemplate(VmsArgumentDto dto, String path, String tableName) { this.dto = dto; this.path = path; + this.tableName = tableName; } /** @@ -51,22 +54,12 @@ public class VmsArgumentDtoBaseVmsGeneratorTemplate extends AbstractVmsGenerator // 设置包名称 context.put("package", dto.getPackageName()); - // 类名称如果是小驼峰,需要 [手写] 为 [下划线] 之后由 [代码 -> 小驼峰/大驼峰] - String className = dto.getClassName(); - // 去除表开头前缀 - String tablePrefixes = dto.getTablePrefixes(); - // 将 表前缀 转成数组 - String replaceTableName = ""; - for (String prefix : tablePrefixes.split("[,,]")) { - replaceTableName = className.replace(prefix, ""); - } - // 将类名称转成小驼峰 - String toCamelCase = TypeConvertUtil.convertToCamelCase(replaceTableName); + String toCamelCase = TypeConvertUtil.convertToCamelCase(tableName); context.put("classLowercaseName", toCamelCase); // 将类名称转成大驼峰 - String convertToCamelCase = TypeConvertUtil.convertToCamelCase(replaceTableName, true); + String convertToCamelCase = TypeConvertUtil.convertToCamelCase(tableName, true); context.put("classUppercaseName", convertToCamelCase); } @@ -78,7 +71,6 @@ public class VmsArgumentDtoBaseVmsGeneratorTemplate extends AbstractVmsGenerator */ @Override void templateMerge(VelocityContext context, StringWriter writer) { - // Velocity 生成模板 Template servicePathTemplate = Velocity.getTemplate("vms/" + path, "UTF-8"); servicePathTemplate.merge(context, writer); } diff --git a/generator-code-server/generator-code/src/main/java/cn/bunny/domain/dto/VmsArgumentDto.java b/generator-code-server/generator-code/src/main/java/cn/bunny/domain/dto/VmsArgumentDto.java index e842dda..3d710cc 100644 --- a/generator-code-server/generator-code/src/main/java/cn/bunny/domain/dto/VmsArgumentDto.java +++ b/generator-code-server/generator-code/src/main/java/cn/bunny/domain/dto/VmsArgumentDto.java @@ -1,9 +1,8 @@ package cn.bunny.domain.dto; import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Pattern; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -19,31 +18,23 @@ import java.util.List; public class VmsArgumentDto { @Schema(name = "author", description = "作者名称") - String author = "Bunny"; + String author; @Schema(name = "packageName", description = "包名称") - String packageName = "cn.bunny.services"; + String packageName; @Schema(name = "requestMapping", description = "requestMapping 名称") - String requestMapping = "/api"; + String requestMapping; - @NotBlank(message = "类名称不能为空") - @NotNull(message = "类名称不能为空") - @Pattern(regexp = "^(?:[a-z][a-z0-9_]*|[_a-z][a-z0-9_]*)$", message = "类名称不合法") - @Schema(name = "className", description = "类名称,格式为:xxx xxx_xxx") - private String className; - - @NotBlank(message = "表名称不能为空") @NotNull(message = "表名称不能为空") - @Pattern(regexp = "^(?:[a-z][a-z0-9_]*|[_a-z][a-z0-9_]*)$", message = "表名称不合法") - @Schema(name = "tableName", description = "表名称") - private String tableName; + @NotEmpty(message = "表名称不能为空") + private List tableNames; @Schema(name = "simpleDateFormat", description = "时间格式") - private String simpleDateFormat = "yyyy-MM-dd HH:mm:ss"; + private String simpleDateFormat; @Schema(name = "tablePrefixes", description = "去除表前缀") - private String tablePrefixes = "t_,sys_,qrtz_,log_"; + private String tablePrefixes; @Schema(name = "comment", description = "注释内容") private String comment; diff --git a/generator-code-server/generator-code/src/main/java/cn/bunny/exception/GlobalExceptionHandler.java b/generator-code-server/generator-code/src/main/java/cn/bunny/exception/GlobalExceptionHandler.java index 4ced964..4180e0e 100644 --- a/generator-code-server/generator-code/src/main/java/cn/bunny/exception/GlobalExceptionHandler.java +++ b/generator-code-server/generator-code/src/main/java/cn/bunny/exception/GlobalExceptionHandler.java @@ -75,6 +75,7 @@ public class GlobalExceptionHandler { public Result handleValidationExceptions(MethodArgumentNotValidException ex) { String errorMessage = ex.getBindingResult().getFieldErrors().stream() .map(DefaultMessageSourceResolvable::getDefaultMessage) + .distinct() .collect(Collectors.joining(", ")); return Result.error(null, 201, errorMessage); } diff --git a/generator-code-server/generator-code/src/main/java/cn/bunny/service/VmsService.java b/generator-code-server/generator-code/src/main/java/cn/bunny/service/VmsService.java index 06a9a4c..0a4a5b9 100644 --- a/generator-code-server/generator-code/src/main/java/cn/bunny/service/VmsService.java +++ b/generator-code-server/generator-code/src/main/java/cn/bunny/service/VmsService.java @@ -16,7 +16,7 @@ public interface VmsService { * @param dto VmsArgumentDto * @return 生成内容 */ - List generator(VmsArgumentDto dto); + Map> generator(VmsArgumentDto dto); /** * 获取vms文件路径 diff --git a/generator-code-server/generator-code/src/main/java/cn/bunny/service/impl/VmsServiceImpl.java b/generator-code-server/generator-code/src/main/java/cn/bunny/service/impl/VmsServiceImpl.java index 47bc7d1..f17cf78 100644 --- a/generator-code-server/generator-code/src/main/java/cn/bunny/service/impl/VmsServiceImpl.java +++ b/generator-code-server/generator-code/src/main/java/cn/bunny/service/impl/VmsServiceImpl.java @@ -1,14 +1,11 @@ package cn.bunny.service.impl; -import cn.bunny.core.factory.ConcreteDatabaseInfo; -import cn.bunny.core.factory.ConcreteSqlParserDatabaseInfo; -import cn.bunny.core.template.VmsArgumentDtoBaseVmsGeneratorTemplate; import cn.bunny.domain.dto.VmsArgumentDto; -import cn.bunny.domain.entity.ColumnMetaData; -import cn.bunny.domain.entity.TableMetaData; import cn.bunny.domain.vo.GeneratorVo; import cn.bunny.domain.vo.VmsPathVo; import cn.bunny.service.VmsService; +import cn.bunny.service.impl.vms.VmsCodeGeneratorService; +import cn.bunny.service.impl.vms.VmsZipService; import cn.bunny.utils.ResourceFileUtil; import cn.bunny.utils.VmsUtil; import cn.hutool.crypto.digest.MD5; @@ -18,83 +15,38 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; -import org.springframework.util.StringUtils; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.StringWriter; -import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import java.util.zip.ZipEntry; -import java.util.zip.ZipOutputStream; +/** + * VMS服务主实现类,负责协调各子服务完成代码生成、资源管理和打包下载功能 + */ @Service @RequiredArgsConstructor public class VmsServiceImpl implements VmsService { - private final ConcreteDatabaseInfo databaseInfoCore; - private final ConcreteSqlParserDatabaseInfo sqlParserDatabaseInfo; + private final VmsCodeGeneratorService codeGeneratorService; + private final VmsZipService zipService; - /** - * 生成服务端代码 - * - * @param dto VmsArgumentDto - * @return 生成内容 - */ @Override - public List generator(VmsArgumentDto dto) { - String tableName = dto.getTableName(); - String sql = dto.getSql(); - - // 表格属性名 和 列信息 - TableMetaData tableMetaData = StringUtils.hasText(dto.getSql()) - ? sqlParserDatabaseInfo.getTableMetadata(dto.getSql()) - : databaseInfoCore.getTableMetadata(dto.getTableName()); - - List columnInfoList = StringUtils.hasText(sql) - ? sqlParserDatabaseInfo.tableColumnInfo(sql) - : databaseInfoCore.tableColumnInfo(tableName).stream().distinct().toList(); - - - return dto.getPath().stream().map(path -> { - // 生成模板 - VmsArgumentDtoBaseVmsGeneratorTemplate vmsArgumentDtoBaseVmsGenerator = new VmsArgumentDtoBaseVmsGeneratorTemplate(dto, path); - StringWriter writer = vmsArgumentDtoBaseVmsGenerator.generatorCodeTemplate(tableMetaData, columnInfoList); - - // 处理 vm 文件名 - path = VmsUtil.handleVmFilename(path, dto.getClassName()); - - return GeneratorVo.builder() - .code(writer.toString()) - .comment(tableMetaData.getComment()) - .tableName(tableMetaData.getTableName()) - .path(path) - .build(); - }).toList(); + public Map> generator(VmsArgumentDto dto) { + return codeGeneratorService.generateCode(dto); } - /** - * 获取vms文件路径 - * - * @return vms下的文件路径 - */ @SneakyThrows @Override public Map> vmsResourcePathList() { - // 读取当前项目中所有的 vm 模板发给前端 List vmsRelativeFiles = ResourceFileUtil.getRelativeFiles("vms"); - return vmsRelativeFiles.stream().map(vmFile -> { + return vmsRelativeFiles.stream() + .map(vmFile -> { String[] filepathList = vmFile.split("/"); String filename = filepathList[filepathList.length - 1].replace(".vm", ""); - // 生成标签上的id - String id = VmsUtil.generateDivId(); return VmsPathVo.builder() - .id(id) + .id(VmsUtil.generateDivId()) .name(vmFile) .label(filename) .type(filepathList[0]) @@ -103,56 +55,22 @@ public class VmsServiceImpl implements VmsService { .collect(Collectors.groupingBy(VmsPathVo::getType)); } - /** - * 打包成zip下载 - * - * @param dto VmsArgumentDto - * @return zip 文件 - */ @Override public ResponseEntity downloadByZip(VmsArgumentDto dto) { - // 需要下载的数据 - List generatorVoList = generator(dto); + byte[] zipBytes = zipService.createZipFile(dto); - // 1. 创建临时ZIP文件 - ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - try (ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream)) { - // 2. 遍历并创建 - generatorVoList.forEach(generatorVo -> { - // zip中的路径 - String path = generatorVo.getPath().replace(".vm", ""); - - // zip中的文件 - String code = generatorVo.getCode(); - - ZipEntry zipEntry = new ZipEntry(path); - try { - // 如果有 / 会转成文件夹 - zipOutputStream.putNextEntry(zipEntry); - - // 写入文件 - zipOutputStream.write(code.getBytes(StandardCharsets.UTF_8)); - zipOutputStream.closeEntry(); - } catch (IOException e) { - throw new RuntimeException(e); - } - }); - } catch (IOException e) { - throw new RuntimeException(e); - } - - // 2.1 文件不重名 + // 下载文件名称 long currentTimeMillis = System.currentTimeMillis(); String digestHex = MD5.create().digestHex(currentTimeMillis + ""); + String generateZipFilename = digestHex + ".zip"; - // 3. 准备响应 + // 设置响应头 HttpHeaders headers = new HttpHeaders(); - headers.add("Content-Disposition", "attachment; filename=" + "vms-" + digestHex + ".zip"); + headers.add("Content-Disposition", "attachment; filename=" + generateZipFilename); headers.add("Cache-Control", "no-cache, no-store, must-revalidate"); headers.add("Pragma", "no-cache"); headers.add("Expires", "0"); - ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); - return new ResponseEntity<>(byteArrayInputStream.readAllBytes(), headers, HttpStatus.OK); + return new ResponseEntity<>(zipBytes, headers, HttpStatus.OK); } -} +} \ No newline at end of file diff --git a/generator-code-server/generator-code/src/main/java/cn/bunny/service/impl/vms/VmsCodeGeneratorService.java b/generator-code-server/generator-code/src/main/java/cn/bunny/service/impl/vms/VmsCodeGeneratorService.java new file mode 100644 index 0000000..967dc31 --- /dev/null +++ b/generator-code-server/generator-code/src/main/java/cn/bunny/service/impl/vms/VmsCodeGeneratorService.java @@ -0,0 +1,91 @@ +package cn.bunny.service.impl.vms; + +import cn.bunny.core.factory.ConcreteDatabaseInfo; +import cn.bunny.core.factory.ConcreteSqlParserDatabaseInfo; +import cn.bunny.core.template.VmsArgumentDtoBaseVmsGeneratorTemplate; +import cn.bunny.domain.dto.VmsArgumentDto; +import cn.bunny.domain.entity.ColumnMetaData; +import cn.bunny.domain.entity.TableMetaData; +import cn.bunny.domain.vo.GeneratorVo; +import cn.bunny.utils.VmsUtil; +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; + +/** + * 负责处理代码生成逻辑的服务类 + */ +@Service +@RequiredArgsConstructor +public class VmsCodeGeneratorService { + + private final ConcreteDatabaseInfo databaseInfoCore; + private final ConcreteSqlParserDatabaseInfo sqlParserDatabaseInfo; + + /** + * 根据DTO生成代码模板 + * + * @param dto 包含生成参数的数据传输对象 + * @return 生成的代码模板列表 + */ + public Map> generateCode(VmsArgumentDto dto) { + String sql = dto.getSql(); + + return dto.getTableNames().stream() + .map(tableName -> { + TableMetaData tableMetaData = getTableMetadata(dto, tableName); + List columnInfoList = getColumnInfoList(sql, tableName); + + return dto.getPath().stream() + .map(path -> { + VmsArgumentDtoBaseVmsGeneratorTemplate generator = new VmsArgumentDtoBaseVmsGeneratorTemplate(dto, path, tableName); + StringWriter writer = generator.generatorCodeTemplate(tableMetaData, columnInfoList); + String processedPath = VmsUtil.handleVmFilename(path, tableMetaData.getTableName()); + + return GeneratorVo.builder() + .id(UUID.randomUUID().toString()) + .code(writer.toString()) + .comment(tableMetaData.getComment()) + .tableName(tableMetaData.getTableName()) + .path(processedPath) + .build(); + }) + .toList(); + }) + .flatMap(List::stream) + .collect(Collectors.groupingBy(GeneratorVo::getTableName)); + } + + /** + * 获取表元数据 + * + * @param dto 生成参数 + * @param tableName 表名 + * @return 表元数据对象 + */ + private TableMetaData getTableMetadata(VmsArgumentDto dto, String tableName) { + return StringUtils.hasText(dto.getSql()) + ? sqlParserDatabaseInfo.getTableMetadata(dto.getSql()) + : databaseInfoCore.getTableMetadata(tableName); + } + + /** + * 获取列信息列表 + * + * @param sql SQL语句 + * @param tableName 表名 + * @return 列元数据列表 + */ + private List getColumnInfoList(String sql, String tableName) { + return StringUtils.hasText(sql) + ? sqlParserDatabaseInfo.tableColumnInfo(sql) + : databaseInfoCore.tableColumnInfo(tableName).stream().distinct().toList(); + } + +} \ No newline at end of file diff --git a/generator-code-server/generator-code/src/main/java/cn/bunny/service/impl/vms/VmsZipService.java b/generator-code-server/generator-code/src/main/java/cn/bunny/service/impl/vms/VmsZipService.java new file mode 100644 index 0000000..1100f64 --- /dev/null +++ b/generator-code-server/generator-code/src/main/java/cn/bunny/service/impl/vms/VmsZipService.java @@ -0,0 +1,62 @@ +package cn.bunny.service.impl.vms; + +import cn.bunny.domain.dto.VmsArgumentDto; +import cn.bunny.domain.vo.GeneratorVo; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +/** + * 负责处理ZIP打包和下载逻辑的服务类 + */ +@Service +@RequiredArgsConstructor +public class VmsZipService { + + private final VmsCodeGeneratorService codeGeneratorService; + + /** + * 创建ZIP文件字节数组 + * + * @param dto 生成参数 + * @return ZIP文件字节数组 + */ + public byte[] createZipFile(VmsArgumentDto dto) { + List generatorVoList = codeGeneratorService.generateCode(dto).values().stream().flatMap(Collection::stream).toList(); + + try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream)) { + + generatorVoList.forEach(generatorVo -> addToZip(zipOutputStream, generatorVo)); + return byteArrayOutputStream.toByteArray(); + } catch (IOException e) { + throw new RuntimeException("Failed to create ZIP file", e); + } + } + + /** + * 将单个生成结果添加到ZIP文件 + * + * @param zipOutputStream ZIP输出流 + * @param generatorVo 生成结果对象 + */ + private void addToZip(ZipOutputStream zipOutputStream, GeneratorVo generatorVo) { + try { + String path = generatorVo.getPath().replace(".vm", ""); + ZipEntry zipEntry = new ZipEntry(path); + zipOutputStream.putNextEntry(zipEntry); + 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); + } + } + +} \ No newline at end of file