🎨 重构后端生成内容
This commit is contained in:
parent
d26208635f
commit
1f2896b863
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,8 +32,8 @@ public class VmsController {
|
|||
|
||||
@Operation(summary = "生成代码", description = "生成代码")
|
||||
@PostMapping("generator")
|
||||
public Result<List<GeneratorVo>> generator(@Valid @RequestBody VmsArgumentDto dto) {
|
||||
List<GeneratorVo> list = vmsService.generator(dto);
|
||||
public Result<Map<String, List<GeneratorVo>>> generator(@Valid @RequestBody VmsArgumentDto dto) {
|
||||
Map<String, List<GeneratorVo>> list = vmsService.generator(dto);
|
||||
return Result.success(list);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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<String> 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;
|
||||
|
|
|
@ -75,6 +75,7 @@ public class GlobalExceptionHandler {
|
|||
public Result<String> handleValidationExceptions(MethodArgumentNotValidException ex) {
|
||||
String errorMessage = ex.getBindingResult().getFieldErrors().stream()
|
||||
.map(DefaultMessageSourceResolvable::getDefaultMessage)
|
||||
.distinct()
|
||||
.collect(Collectors.joining(", "));
|
||||
return Result.error(null, 201, errorMessage);
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ public interface VmsService {
|
|||
* @param dto VmsArgumentDto
|
||||
* @return 生成内容
|
||||
*/
|
||||
List<GeneratorVo> generator(VmsArgumentDto dto);
|
||||
Map<String, List<GeneratorVo>> generator(VmsArgumentDto dto);
|
||||
|
||||
/**
|
||||
* 获取vms文件路径
|
||||
|
|
|
@ -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<GeneratorVo> 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<ColumnMetaData> 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<String, List<GeneratorVo>> generator(VmsArgumentDto dto) {
|
||||
return codeGeneratorService.generateCode(dto);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取vms文件路径
|
||||
*
|
||||
* @return vms下的文件路径
|
||||
*/
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public Map<String, List<VmsPathVo>> vmsResourcePathList() {
|
||||
// 读取当前项目中所有的 vm 模板发给前端
|
||||
List<String> 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<byte[]> downloadByZip(VmsArgumentDto dto) {
|
||||
// 需要下载的数据
|
||||
List<GeneratorVo> 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);
|
||||
}
|
||||
}
|
|
@ -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<String, List<GeneratorVo>> generateCode(VmsArgumentDto dto) {
|
||||
String sql = dto.getSql();
|
||||
|
||||
return dto.getTableNames().stream()
|
||||
.map(tableName -> {
|
||||
TableMetaData tableMetaData = getTableMetadata(dto, tableName);
|
||||
List<ColumnMetaData> 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<ColumnMetaData> getColumnInfoList(String sql, String tableName) {
|
||||
return StringUtils.hasText(sql)
|
||||
? sqlParserDatabaseInfo.tableColumnInfo(sql)
|
||||
: databaseInfoCore.tableColumnInfo(tableName).stream().distinct().toList();
|
||||
}
|
||||
|
||||
}
|
|
@ -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<GeneratorVo> 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue