|
|
@@ -0,0 +1,884 @@
|
|
|
+package com.payment.platform.module.generator.service;
|
|
|
+
|
|
|
+import cn.hutool.core.bean.BeanUtil;
|
|
|
+import cn.hutool.core.util.StrUtil;
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
+import com.payment.platform.common.exception.BusinessException;
|
|
|
+import com.payment.platform.common.response.PageResult;
|
|
|
+import com.payment.platform.module.generator.dto.*;
|
|
|
+import com.payment.platform.module.generator.entity.GenTableColumnEntity;
|
|
|
+import com.payment.platform.module.generator.entity.GenTableEntity;
|
|
|
+import com.payment.platform.module.generator.mapper.GenTableColumnMapper;
|
|
|
+import com.payment.platform.module.generator.mapper.GenTableMapper;
|
|
|
+import lombok.RequiredArgsConstructor;
|
|
|
+import org.springframework.jdbc.core.JdbcTemplate;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+
|
|
|
+import java.io.IOException;
|
|
|
+import java.nio.file.Files;
|
|
|
+import java.nio.file.Path;
|
|
|
+import java.nio.file.Paths;
|
|
|
+import java.util.*;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+@Service
|
|
|
+@RequiredArgsConstructor
|
|
|
+public class GenService {
|
|
|
+
|
|
|
+ private final GenTableMapper genTableMapper;
|
|
|
+ private final GenTableColumnMapper genTableColumnMapper;
|
|
|
+ private final JdbcTemplate jdbcTemplate;
|
|
|
+
|
|
|
+ // ==================== 表管理 ====================
|
|
|
+
|
|
|
+ public PageResult<GenTableVO> getTablePage(int pageNo, int pageSize) {
|
|
|
+ Page<GenTableEntity> page = new Page<>(pageNo, pageSize);
|
|
|
+ LambdaQueryWrapper<GenTableEntity> w = new LambdaQueryWrapper<>();
|
|
|
+ w.orderByDesc(GenTableEntity::getUpdatedTime);
|
|
|
+ Page<GenTableEntity> result = genTableMapper.selectPage(page, w);
|
|
|
+ return PageResult.of(pageNo, pageSize, result.getTotal(),
|
|
|
+ result.getRecords().stream().map(e -> BeanUtil.copyProperties(e, GenTableVO.class)).collect(Collectors.toList()));
|
|
|
+ }
|
|
|
+
|
|
|
+ public Map<String, Object> getTableDetail(Long id) {
|
|
|
+ GenTableEntity table = requireTable(id);
|
|
|
+ List<GenTableColumnEntity> columns = genTableColumnMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<GenTableColumnEntity>().eq(GenTableColumnEntity::getTableId, id)
|
|
|
+ .orderByAsc(GenTableColumnEntity::getSort));
|
|
|
+ GenTableVO tableVO = BeanUtil.copyProperties(table, GenTableVO.class);
|
|
|
+ List<GenTableColumnVO> columnVOs = columns.stream()
|
|
|
+ .map(e -> BeanUtil.copyProperties(e, GenTableColumnVO.class)).collect(Collectors.toList());
|
|
|
+ Map<String, Object> result = new LinkedHashMap<>();
|
|
|
+ result.put("table", tableVO);
|
|
|
+ result.put("columns", columnVOs);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从数据库导入表结构到代码生成器。
|
|
|
+ * 读取 information_schema,创建 gen_table 和 gen_table_column 记录。
|
|
|
+ */
|
|
|
+ @Transactional
|
|
|
+ public GenTableVO importTable(String tableName) {
|
|
|
+ if (StrUtil.isBlank(tableName)) throw new BusinessException(400, "表名不能为空");
|
|
|
+ // 检查是否已导入
|
|
|
+ GenTableEntity exist = genTableMapper.selectOne(
|
|
|
+ new LambdaQueryWrapper<GenTableEntity>().eq(GenTableEntity::getTableName, tableName));
|
|
|
+ if (exist != null) throw new BusinessException(400, "表 " + tableName + " 已导入,不能重复导入");
|
|
|
+
|
|
|
+ // 查询表注释
|
|
|
+ String tableComment = queryTableComment(tableName);
|
|
|
+
|
|
|
+ // 构建 GenTableEntity
|
|
|
+ GenTableEntity table = new GenTableEntity();
|
|
|
+ table.setTableName(tableName);
|
|
|
+ table.setTableComment(tableComment);
|
|
|
+ table.setClassName(toPascalCase(tableName));
|
|
|
+ table.setBusinessName(toCamelCase(tableName));
|
|
|
+ table.setFunctionName(StrUtil.isNotBlank(tableComment) ? tableComment : tableName);
|
|
|
+ table.setModuleName(toCamelCase(tableName));
|
|
|
+ table.setPackageName("module_" + toCamelCase(tableName));
|
|
|
+ genTableMapper.insert(table);
|
|
|
+
|
|
|
+ // 查询列信息并导入
|
|
|
+ List<Map<String, Object>> dbColumns = queryTableColumns(tableName);
|
|
|
+ if (dbColumns.isEmpty()) throw new BusinessException(400, "表 " + tableName + " 无列信息");
|
|
|
+
|
|
|
+ int sort = 1;
|
|
|
+ for (Map<String, Object> col : dbColumns) {
|
|
|
+ GenTableColumnEntity column = buildColumnFromDb(col, table.getId());
|
|
|
+ column.setSort(sort++);
|
|
|
+ genTableColumnMapper.insert(column);
|
|
|
+ }
|
|
|
+
|
|
|
+ return BeanUtil.copyProperties(genTableMapper.selectById(table.getId()), GenTableVO.class);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 重新同步表结构:对比 DB 最新列信息与已导入的列,增/删/改。
|
|
|
+ */
|
|
|
+ @Transactional
|
|
|
+ public void syncTable(Long id) {
|
|
|
+ GenTableEntity table = requireTable(id);
|
|
|
+ List<GenTableColumnEntity> existingColumns = genTableColumnMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<GenTableColumnEntity>().eq(GenTableColumnEntity::getTableId, id));
|
|
|
+
|
|
|
+ Map<String, GenTableColumnEntity> existingMap = new LinkedHashMap<>();
|
|
|
+ for (GenTableColumnEntity c : existingColumns) {
|
|
|
+ existingMap.put(c.getColumnName(), c);
|
|
|
+ }
|
|
|
+
|
|
|
+ List<Map<String, Object>> dbColumns = queryTableColumns(table.getTableName());
|
|
|
+ Set<String> dbColNames = new LinkedHashSet<>();
|
|
|
+ int sort = 1;
|
|
|
+ for (Map<String, Object> col : dbColumns) {
|
|
|
+ String colName = (String) col.get("COLUMN_NAME");
|
|
|
+ dbColNames.add(colName);
|
|
|
+
|
|
|
+ if (existingMap.containsKey(colName)) {
|
|
|
+ // 已有列:更新类型、注释等 DB 元数据,保留用户配置
|
|
|
+ GenTableColumnEntity existCol = existingMap.get(colName);
|
|
|
+ fillDbMetadata(existCol, col);
|
|
|
+ existCol.setSort(sort++);
|
|
|
+ genTableColumnMapper.updateById(existCol);
|
|
|
+ } else {
|
|
|
+ // 新增列
|
|
|
+ GenTableColumnEntity newCol = buildColumnFromDb(col, table.getId());
|
|
|
+ newCol.setSort(sort++);
|
|
|
+ genTableColumnMapper.insert(newCol);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 删除 DB 中已不存在的列
|
|
|
+ for (GenTableColumnEntity c : existingColumns) {
|
|
|
+ if (!dbColNames.contains(c.getColumnName())) {
|
|
|
+ genTableColumnMapper.deleteById(c.getId());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 删除业务表及其所有列(批量)。
|
|
|
+ */
|
|
|
+ @Transactional
|
|
|
+ public void deleteTables(List<Long> ids) {
|
|
|
+ if (ids == null || ids.isEmpty()) throw new BusinessException(400, "删除失败,删除对象不能为空");
|
|
|
+ for (Long id : ids) {
|
|
|
+ genTableColumnMapper.delete(new LambdaQueryWrapper<GenTableColumnEntity>().eq(GenTableColumnEntity::getTableId, id));
|
|
|
+ genTableMapper.deleteById(id);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 删除单个业务表及其所有列。
|
|
|
+ */
|
|
|
+ @Transactional
|
|
|
+ public void deleteTable(Long id) {
|
|
|
+ requireTable(id);
|
|
|
+ genTableColumnMapper.delete(new LambdaQueryWrapper<GenTableColumnEntity>().eq(GenTableColumnEntity::getTableId, id));
|
|
|
+ genTableMapper.deleteById(id);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 数据库表浏览 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询数据库中所有表(分页),返回表名和注释。
|
|
|
+ */
|
|
|
+ public PageResult<Map<String, Object>> getDbTablePage(int pageNo, int pageSize) {
|
|
|
+ String countSql = "SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_TYPE = 'BASE TABLE'";
|
|
|
+ Integer total = jdbcTemplate.queryForObject(countSql, Integer.class);
|
|
|
+ if (total == null) total = 0;
|
|
|
+
|
|
|
+ int offset = (pageNo - 1) * pageSize;
|
|
|
+ String dataSql = "SELECT TABLE_NAME, TABLE_COMMENT, CREATE_TIME FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_TYPE = 'BASE TABLE' ORDER BY CREATE_TIME DESC LIMIT " + pageSize + " OFFSET " + offset;
|
|
|
+ List<Map<String, Object>> records = jdbcTemplate.queryForList(dataSql);
|
|
|
+
|
|
|
+ return PageResult.of(pageNo, pageSize, (long) total, records);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 表创建和更新 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 手动创建 gen_table 记录(不从 DB 导入)。
|
|
|
+ */
|
|
|
+ @Transactional
|
|
|
+ public GenTableVO createTable(Map<String, Object> body) {
|
|
|
+ String tableName = (String) body.getOrDefault("table_name", body.get("tableName"));
|
|
|
+ if (StrUtil.isBlank(tableName)) throw new BusinessException(400, "表名不能为空");
|
|
|
+
|
|
|
+ GenTableEntity exist = genTableMapper.selectOne(
|
|
|
+ new LambdaQueryWrapper<GenTableEntity>().eq(GenTableEntity::getTableName, tableName));
|
|
|
+ if (exist != null) throw new BusinessException(400, "表 " + tableName + " 已存在");
|
|
|
+
|
|
|
+ GenTableEntity table = new GenTableEntity();
|
|
|
+ table.setTableName(tableName);
|
|
|
+ table.setTableComment((String) body.getOrDefault("table_comment", ""));
|
|
|
+ table.setClassName((String) body.getOrDefault("class_name", toPascalCase(tableName)));
|
|
|
+ table.setBusinessName((String) body.getOrDefault("business_name", toCamelCase(tableName)));
|
|
|
+ table.setFunctionName((String) body.getOrDefault("function_name", table.getTableComment()));
|
|
|
+ table.setModuleName((String) body.getOrDefault("module_name", toCamelCase(tableName)));
|
|
|
+ table.setPackageName((String) body.getOrDefault("package_name", "module_" + toCamelCase(tableName)));
|
|
|
+ genTableMapper.insert(table);
|
|
|
+ return BeanUtil.copyProperties(genTableMapper.selectById(table.getId()), GenTableVO.class);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更新 gen_table 记录。
|
|
|
+ */
|
|
|
+ @Transactional
|
|
|
+ public GenTableVO updateTable(Long id, Map<String, Object> body) {
|
|
|
+ GenTableEntity table = requireTable(id);
|
|
|
+ if (body.containsKey("table_name") || body.containsKey("tableName")) {
|
|
|
+ table.setTableName((String) body.getOrDefault("table_name", body.get("tableName")));
|
|
|
+ }
|
|
|
+ if (body.containsKey("table_comment")) table.setTableComment((String) body.get("table_comment"));
|
|
|
+ if (body.containsKey("class_name")) table.setClassName((String) body.get("class_name"));
|
|
|
+ if (body.containsKey("business_name")) table.setBusinessName((String) body.get("business_name"));
|
|
|
+ if (body.containsKey("function_name")) table.setFunctionName((String) body.get("function_name"));
|
|
|
+ if (body.containsKey("module_name")) table.setModuleName((String) body.get("module_name"));
|
|
|
+ if (body.containsKey("package_name")) table.setPackageName((String) body.get("package_name"));
|
|
|
+ genTableMapper.updateById(table);
|
|
|
+ return BeanUtil.copyProperties(genTableMapper.selectById(id), GenTableVO.class);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 按表名同步 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 按表名同步表结构(与 syncTable 逻辑相同,通过表名查找 gen_table)。
|
|
|
+ */
|
|
|
+ @Transactional
|
|
|
+ public void syncTableByName(String tableName) {
|
|
|
+ GenTableEntity table = genTableMapper.selectOne(
|
|
|
+ new LambdaQueryWrapper<GenTableEntity>().eq(GenTableEntity::getTableName, tableName));
|
|
|
+ if (table == null) throw new BusinessException(404, "业务表不存在: " + tableName);
|
|
|
+ syncTable(table.getId());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 预览同步表结构将产生的变更。
|
|
|
+ */
|
|
|
+ public Map<String, Object> previewTableSync(String tableName) {
|
|
|
+ GenTableEntity table = genTableMapper.selectOne(
|
|
|
+ new LambdaQueryWrapper<GenTableEntity>().eq(GenTableEntity::getTableName, tableName));
|
|
|
+ if (table == null) throw new BusinessException(404, "业务表不存在: " + tableName);
|
|
|
+
|
|
|
+ List<GenTableColumnEntity> existingColumns = genTableColumnMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<GenTableColumnEntity>().eq(GenTableColumnEntity::getTableId, table.getId()));
|
|
|
+
|
|
|
+ Map<String, GenTableColumnEntity> existingMap = new LinkedHashMap<>();
|
|
|
+ for (GenTableColumnEntity c : existingColumns) {
|
|
|
+ existingMap.put(c.getColumnName(), c);
|
|
|
+ }
|
|
|
+
|
|
|
+ List<Map<String, Object>> dbColumns = queryTableColumns(tableName);
|
|
|
+ List<String> toAdd = new ArrayList<>();
|
|
|
+ List<String> toRemove = new ArrayList<>();
|
|
|
+ List<String> toUpdate = new ArrayList<>();
|
|
|
+
|
|
|
+ Set<String> dbColNames = new LinkedHashSet<>();
|
|
|
+ for (Map<String, Object> col : dbColumns) {
|
|
|
+ String colName = (String) col.get("COLUMN_NAME");
|
|
|
+ dbColNames.add(colName);
|
|
|
+ if (existingMap.containsKey(colName)) {
|
|
|
+ toUpdate.add(colName);
|
|
|
+ } else {
|
|
|
+ toAdd.add(colName);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ for (GenTableColumnEntity c : existingColumns) {
|
|
|
+ if (!dbColNames.contains(c.getColumnName())) {
|
|
|
+ toRemove.add(c.getColumnName());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<String, Object> result = new LinkedHashMap<>();
|
|
|
+ result.put("table_name", tableName);
|
|
|
+ result.put("add_columns", toAdd);
|
|
|
+ result.put("update_columns", toUpdate);
|
|
|
+ result.put("remove_columns", toRemove);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 代码生成扩展 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 按表名生成代码。
|
|
|
+ */
|
|
|
+ @Transactional
|
|
|
+ public List<String> generateCodeByTableName(String tableName) {
|
|
|
+ GenTableEntity table = genTableMapper.selectOne(
|
|
|
+ new LambdaQueryWrapper<GenTableEntity>().eq(GenTableEntity::getTableName, tableName));
|
|
|
+ if (table == null) throw new BusinessException(404, "业务表不存在: " + tableName);
|
|
|
+ return generateCode(table.getId());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 批量生成代码。
|
|
|
+ */
|
|
|
+ @Transactional
|
|
|
+ public List<String> batchGenerateCode(List<Long> ids) {
|
|
|
+ if (ids == null || ids.isEmpty()) throw new BusinessException(400, "请选择要生成代码的表");
|
|
|
+ List<String> allPaths = new ArrayList<>();
|
|
|
+ for (Long id : ids) {
|
|
|
+ allPaths.addAll(generateCode(id));
|
|
|
+ }
|
|
|
+ return allPaths;
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 代码生成 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 预览生成代码,返回 Map<文件名, 代码内容>。
|
|
|
+ */
|
|
|
+ public Map<String, String> previewCode(Long tableId) {
|
|
|
+ GenTableEntity table = requireTable(tableId);
|
|
|
+ List<GenTableColumnEntity> columns = genTableColumnMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<GenTableColumnEntity>().eq(GenTableColumnEntity::getTableId, tableId)
|
|
|
+ .orderByAsc(GenTableColumnEntity::getSort));
|
|
|
+ return buildGeneratedCode(table, columns);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生成代码文件到 java/src/main/java/... 目录。
|
|
|
+ * 返回生成的文件路径列表。
|
|
|
+ */
|
|
|
+ @Transactional
|
|
|
+ public List<String> generateCode(Long tableId) {
|
|
|
+ GenTableEntity table = requireTable(tableId);
|
|
|
+ List<GenTableColumnEntity> columns = genTableColumnMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<GenTableColumnEntity>().eq(GenTableColumnEntity::getTableId, tableId)
|
|
|
+ .orderByAsc(GenTableColumnEntity::getSort));
|
|
|
+
|
|
|
+ Map<String, String> codeMap = buildGeneratedCode(table, columns);
|
|
|
+
|
|
|
+ // 确定基础路径: java/src/main/java
|
|
|
+ String basePath = resolveBasePath();
|
|
|
+ String rawPkg = StrUtil.isNotBlank(table.getPackageName())
|
|
|
+ ? table.getPackageName() : "module_" + table.getModuleName();
|
|
|
+ // 去除 module_ 前缀得到目录段(module_example -> example)
|
|
|
+ String pkgPath = rawPkg.startsWith("module_") ? rawPkg.substring("module_".length()) : rawPkg;
|
|
|
+ pkgPath = pkgPath.replace('.', '/');
|
|
|
+ String businessName = StrUtil.isNotBlank(table.getBusinessName())
|
|
|
+ ? table.getBusinessName() : toCamelCase(table.getTableName());
|
|
|
+
|
|
|
+ List<String> filePaths = new ArrayList<>();
|
|
|
+ for (Map.Entry<String, String> entry : codeMap.entrySet()) {
|
|
|
+ String fileName = entry.getKey();
|
|
|
+ String content = entry.getValue();
|
|
|
+
|
|
|
+ // 文件名格式例如: "entity/DictEntity.java"
|
|
|
+ String fullPath = basePath + "/module/" + pkgPath + "/" + businessName + "/" + fileName;
|
|
|
+ try {
|
|
|
+ Path filePath = Paths.get(fullPath);
|
|
|
+ Files.createDirectories(filePath.getParent());
|
|
|
+ Files.writeString(filePath, content);
|
|
|
+ filePaths.add(fullPath);
|
|
|
+ } catch (IOException e) {
|
|
|
+ throw new BusinessException(500, "生成文件失败: " + fileName + ", " + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return filePaths;
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 代码模板构建 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据表与列信息构建全部代码模板,返回 Map<相对文件名, 代码内容>。
|
|
|
+ */
|
|
|
+ private Map<String, String> buildGeneratedCode(GenTableEntity table, List<GenTableColumnEntity> columns) {
|
|
|
+ String className = StrUtil.isNotBlank(table.getClassName())
|
|
|
+ ? table.getClassName() : toPascalCase(table.getTableName());
|
|
|
+ String moduleName = StrUtil.isNotBlank(table.getModuleName())
|
|
|
+ ? table.getModuleName() : toCamelCase(table.getTableName());
|
|
|
+ String businessName = StrUtil.isNotBlank(table.getBusinessName())
|
|
|
+ ? table.getBusinessName() : toCamelCase(table.getTableName());
|
|
|
+ String functionName = StrUtil.isNotBlank(table.getFunctionName())
|
|
|
+ ? table.getFunctionName() : table.getTableComment();
|
|
|
+ // Java 包路径:去除 module_ 前缀(因为外层已有 com.payment.platform.module)
|
|
|
+ String rawPkg = StrUtil.isNotBlank(table.getPackageName())
|
|
|
+ ? table.getPackageName() : "module_" + moduleName;
|
|
|
+ String pkg = rawPkg.startsWith("module_") ? rawPkg.substring("module_".length()) : rawPkg;
|
|
|
+
|
|
|
+ // 过滤出非主键的业务字段(用于 DTO)
|
|
|
+ GenTableColumnEntity pkCol = columns.stream()
|
|
|
+ .filter(c -> Boolean.TRUE.equals(c.getIsPk())).findFirst().orElse(null);
|
|
|
+ List<GenTableColumnEntity> bizCols = columns.stream()
|
|
|
+ .filter(c -> !Boolean.TRUE.equals(c.getIsPk())).collect(Collectors.toList());
|
|
|
+
|
|
|
+ Map<String, String> result = new LinkedHashMap<>();
|
|
|
+
|
|
|
+ // Entity
|
|
|
+ result.put("entity/" + className + "Entity.java", buildEntity(pkg, className, businessName, table.getTableName(), columns));
|
|
|
+
|
|
|
+ // Mapper
|
|
|
+ result.put("mapper/" + className + "Mapper.java", buildMapper(pkg, className, businessName));
|
|
|
+
|
|
|
+ // VO
|
|
|
+ result.put("dto/" + className + "VO.java", buildVO(pkg, className, businessName, columns));
|
|
|
+
|
|
|
+ // CreateDTO
|
|
|
+ result.put("dto/" + className + "CreateDTO.java", buildCreateDTO(pkg, className, businessName, bizCols));
|
|
|
+
|
|
|
+ // UpdateDTO
|
|
|
+ result.put("dto/" + className + "UpdateDTO.java", buildUpdateDTO(pkg, className, businessName, bizCols));
|
|
|
+
|
|
|
+ // Service
|
|
|
+ result.put("service/" + className + "Service.java", buildService(pkg, className, businessName, functionName, columns));
|
|
|
+
|
|
|
+ // Controller
|
|
|
+ result.put("controller/" + className + "Controller.java", buildController(pkg, className, moduleName, businessName));
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ private String buildEntity(String pkg, String className, String businessName, String tableName, List<GenTableColumnEntity> columns) {
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
+ sb.append("package com.payment.platform.module.").append(pkg).append(".").append(businessName).append(".entity;\n\n");
|
|
|
+ sb.append("import com.baomidou.mybatisplus.annotation.TableName;\n");
|
|
|
+ sb.append("import com.payment.platform.common.base.UserBaseEntity;\n");
|
|
|
+ sb.append("import lombok.Data;\n");
|
|
|
+ sb.append("import lombok.EqualsAndHashCode;\n\n");
|
|
|
+ // 导入必要的 Java 类型
|
|
|
+ Set<String> imports = new LinkedHashSet<>();
|
|
|
+ for (GenTableColumnEntity c : columns) {
|
|
|
+ String javaType = dbTypeToJavaType(c.getColumnType());
|
|
|
+ if (javaType.contains("BigDecimal")) imports.add("import java.math.BigDecimal;");
|
|
|
+ if (javaType.contains("LocalDate") && !javaType.contains("LocalDateTime")) imports.add("import java.time.LocalDate;");
|
|
|
+ if (javaType.contains("LocalDateTime")) imports.add("import java.time.LocalDateTime;");
|
|
|
+ if (javaType.contains("LocalTime")) imports.add("import java.time.LocalTime;");
|
|
|
+ if (javaType.contains("OffsetDateTime")) imports.add("import java.time.OffsetDateTime;");
|
|
|
+ }
|
|
|
+ for (String imp : imports) sb.append(imp).append("\n");
|
|
|
+ sb.append("\n");
|
|
|
+ sb.append("@Data\n");
|
|
|
+ sb.append("@EqualsAndHashCode(callSuper = true)\n");
|
|
|
+ sb.append("@TableName(\"").append(tableName).append("\")\n");
|
|
|
+ sb.append("public class ").append(className).append("Entity extends UserBaseEntity {\n\n");
|
|
|
+ for (GenTableColumnEntity c : columns) {
|
|
|
+ String javaType = dbTypeToJavaType(c.getColumnType());
|
|
|
+ String fieldName = toCamelCase(c.getColumnName());
|
|
|
+ sb.append(" private ").append(javaType).append(" ").append(fieldName).append(";\n");
|
|
|
+ }
|
|
|
+ sb.append("}\n");
|
|
|
+ return sb.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ private String buildMapper(String pkg, String className, String businessName) {
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
+ sb.append("package com.payment.platform.module.").append(pkg).append(".").append(businessName).append(".mapper;\n\n");
|
|
|
+ sb.append("import com.baomidou.mybatisplus.core.mapper.BaseMapper;\n");
|
|
|
+ sb.append("import com.payment.platform.module.").append(pkg).append(".").append(businessName).append(".entity.").append(className).append("Entity;\n");
|
|
|
+ sb.append("import org.apache.ibatis.annotations.Mapper;\n\n");
|
|
|
+ sb.append("@Mapper\n");
|
|
|
+ sb.append("public interface ").append(className).append("Mapper extends BaseMapper<").append(className).append("Entity> {\n");
|
|
|
+ sb.append("}\n");
|
|
|
+ return sb.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ private String buildVO(String pkg, String className, String businessName, List<GenTableColumnEntity> columns) {
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
+ sb.append("package com.payment.platform.module.").append(pkg).append(".").append(businessName).append(".dto;\n\n");
|
|
|
+ sb.append("import io.swagger.v3.oas.annotations.media.Schema;\n");
|
|
|
+ sb.append("import lombok.Data;\n\n");
|
|
|
+ // 时间类型导入
|
|
|
+ Set<String> imports = new LinkedHashSet<>();
|
|
|
+ for (GenTableColumnEntity c : columns) {
|
|
|
+ String javaType = dbTypeToJavaType(c.getColumnType());
|
|
|
+ if (javaType.contains("BigDecimal")) imports.add("import java.math.BigDecimal;");
|
|
|
+ if (javaType.contains("LocalDate") && !javaType.contains("LocalDateTime")) imports.add("import java.time.LocalDate;");
|
|
|
+ if (javaType.contains("LocalDateTime")) imports.add("import java.time.LocalDateTime;");
|
|
|
+ if (javaType.contains("LocalTime")) imports.add("import java.time.LocalTime;");
|
|
|
+ if (javaType.contains("OffsetDateTime")) imports.add("import java.time.OffsetDateTime;");
|
|
|
+ }
|
|
|
+ imports.add("import java.time.OffsetDateTime;");
|
|
|
+ for (String imp : imports) sb.append(imp).append("\n");
|
|
|
+ sb.append("\n");
|
|
|
+ sb.append("@Data\n");
|
|
|
+ sb.append("public class ").append(className).append("VO {\n\n");
|
|
|
+ sb.append(" @Schema(description = \"ID\")\n private Long id;\n\n");
|
|
|
+ sb.append(" @Schema(description = \"创建时间\")\n private OffsetDateTime createdTime;\n\n");
|
|
|
+ sb.append(" @Schema(description = \"更新时间\")\n private OffsetDateTime updatedTime;\n\n");
|
|
|
+ for (GenTableColumnEntity c : columns) {
|
|
|
+ String fieldName = toCamelCase(c.getColumnName());
|
|
|
+ String comment = StrUtil.isNotBlank(c.getColumnComment()) ? c.getColumnComment() : fieldName;
|
|
|
+ String javaType = dbTypeToJavaType(c.getColumnType());
|
|
|
+ sb.append(" @Schema(description = \"").append(comment).append("\")\n");
|
|
|
+ sb.append(" private ").append(javaType).append(" ").append(fieldName).append(";\n\n");
|
|
|
+ }
|
|
|
+ sb.append("}\n");
|
|
|
+ return sb.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ private String buildCreateDTO(String pkg, String className, String businessName, List<GenTableColumnEntity> bizCols) {
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
+ sb.append("package com.payment.platform.module.").append(pkg).append(".").append(businessName).append(".dto;\n\n");
|
|
|
+ sb.append("import io.swagger.v3.oas.annotations.media.Schema;\n");
|
|
|
+ sb.append("import jakarta.validation.constraints.NotBlank;\n");
|
|
|
+ sb.append("import lombok.Data;\n\n");
|
|
|
+ Set<String> imports = new LinkedHashSet<>();
|
|
|
+ for (GenTableColumnEntity c : bizCols) {
|
|
|
+ String javaType = dbTypeToJavaType(c.getColumnType());
|
|
|
+ if (javaType.contains("BigDecimal")) imports.add("import java.math.BigDecimal;");
|
|
|
+ if (javaType.contains("LocalDate") && !javaType.contains("LocalDateTime")) imports.add("import java.time.LocalDate;");
|
|
|
+ if (javaType.contains("LocalDateTime")) imports.add("import java.time.LocalDateTime;");
|
|
|
+ if (javaType.contains("LocalTime")) imports.add("import java.time.LocalTime;");
|
|
|
+ }
|
|
|
+ for (String imp : imports) sb.append(imp).append("\n");
|
|
|
+ if (!imports.isEmpty()) sb.append("\n");
|
|
|
+ sb.append("@Data\n");
|
|
|
+ sb.append("public class ").append(className).append("CreateDTO {\n\n");
|
|
|
+ for (GenTableColumnEntity c : bizCols) {
|
|
|
+ if (Boolean.TRUE.equals(c.getIsInsert()) || c.getIsInsert() == null) {
|
|
|
+ String fieldName = toCamelCase(c.getColumnName());
|
|
|
+ String comment = StrUtil.isNotBlank(c.getColumnComment()) ? c.getColumnComment() : fieldName;
|
|
|
+ String javaType = dbTypeToJavaType(c.getColumnType());
|
|
|
+ sb.append(" @Schema(description = \"").append(comment).append("\")\n");
|
|
|
+ sb.append(" private ").append(javaType).append(" ").append(fieldName).append(";\n\n");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ sb.append("}\n");
|
|
|
+ return sb.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ private String buildUpdateDTO(String pkg, String className, String businessName, List<GenTableColumnEntity> bizCols) {
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
+ sb.append("package com.payment.platform.module.").append(pkg).append(".").append(businessName).append(".dto;\n\n");
|
|
|
+ sb.append("import io.swagger.v3.oas.annotations.media.Schema;\n");
|
|
|
+ sb.append("import lombok.Data;\n\n");
|
|
|
+ Set<String> imports = new LinkedHashSet<>();
|
|
|
+ for (GenTableColumnEntity c : bizCols) {
|
|
|
+ String javaType = dbTypeToJavaType(c.getColumnType());
|
|
|
+ if (javaType.contains("BigDecimal")) imports.add("import java.math.BigDecimal;");
|
|
|
+ if (javaType.contains("LocalDate") && !javaType.contains("LocalDateTime")) imports.add("import java.time.LocalDate;");
|
|
|
+ if (javaType.contains("LocalDateTime")) imports.add("import java.time.LocalDateTime;");
|
|
|
+ if (javaType.contains("LocalTime")) imports.add("import java.time.LocalTime;");
|
|
|
+ }
|
|
|
+ for (String imp : imports) sb.append(imp).append("\n");
|
|
|
+ if (!imports.isEmpty()) sb.append("\n");
|
|
|
+ sb.append("@Data\n");
|
|
|
+ sb.append("public class ").append(className).append("UpdateDTO {\n\n");
|
|
|
+ for (GenTableColumnEntity c : bizCols) {
|
|
|
+ if (Boolean.TRUE.equals(c.getIsEdit()) || c.getIsEdit() == null) {
|
|
|
+ String fieldName = toCamelCase(c.getColumnName());
|
|
|
+ String comment = StrUtil.isNotBlank(c.getColumnComment()) ? c.getColumnComment() : fieldName;
|
|
|
+ String javaType = dbTypeToJavaType(c.getColumnType());
|
|
|
+ sb.append(" @Schema(description = \"").append(comment).append("\")\n");
|
|
|
+ sb.append(" private ").append(javaType).append(" ").append(fieldName).append(";\n\n");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ sb.append("}\n");
|
|
|
+ return sb.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ private String buildService(String pkg, String className, String businessName,
|
|
|
+ String functionName, List<GenTableColumnEntity> columns) {
|
|
|
+ String camelBiz = toCamelCase(businessName);
|
|
|
+ GenTableColumnEntity pkCol = columns.stream()
|
|
|
+ .filter(c -> Boolean.TRUE.equals(c.getIsPk())).findFirst().orElse(null);
|
|
|
+ String pkField = pkCol != null ? toCamelCase(pkCol.getColumnName()) : "id";
|
|
|
+ String pkType = pkCol != null ? dbTypeToJavaType(pkCol.getColumnType()) : "Long";
|
|
|
+
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
+ sb.append("package com.payment.platform.module.").append(pkg).append(".").append(camelBiz).append(".service;\n\n");
|
|
|
+ sb.append("import cn.hutool.core.bean.BeanUtil;\n");
|
|
|
+ sb.append("import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\n");
|
|
|
+ sb.append("import com.baomidou.mybatisplus.extension.plugins.pagination.Page;\n");
|
|
|
+ sb.append("import com.payment.platform.common.exception.BusinessException;\n");
|
|
|
+ sb.append("import com.payment.platform.common.response.PageResult;\n");
|
|
|
+ sb.append("import com.payment.platform.module.").append(pkg).append(".").append(camelBiz).append(".dto.*;\n");
|
|
|
+ sb.append("import com.payment.platform.module.").append(pkg).append(".").append(camelBiz).append(".entity.").append(className).append("Entity;\n");
|
|
|
+ sb.append("import com.payment.platform.module.").append(pkg).append(".").append(camelBiz).append(".mapper.").append(className).append("Mapper;\n");
|
|
|
+ sb.append("import lombok.RequiredArgsConstructor;\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");
|
|
|
+ sb.append("import java.util.stream.Collectors;\n\n");
|
|
|
+ sb.append("@Service\n");
|
|
|
+ sb.append("@RequiredArgsConstructor\n");
|
|
|
+ sb.append("public class ").append(className).append("Service {\n\n");
|
|
|
+ sb.append(" private final ").append(className).append("Mapper mapper;\n\n");
|
|
|
+
|
|
|
+ // getDetail
|
|
|
+ sb.append(" public ").append(className).append("VO getDetail(").append(pkType).append(" ").append(pkField).append(") {\n");
|
|
|
+ sb.append(" return BeanUtil.copyProperties(requireEntity(").append(pkField).append("), ").append(className).append("VO.class);\n");
|
|
|
+ sb.append(" }\n\n");
|
|
|
+
|
|
|
+ // getPage
|
|
|
+ sb.append(" public PageResult<").append(className).append("VO> getPage(int pageNo, int pageSize) {\n");
|
|
|
+ sb.append(" Page<").append(className).append("Entity> page = new Page<>(pageNo, pageSize);\n");
|
|
|
+ sb.append(" LambdaQueryWrapper<").append(className).append("Entity> w = new LambdaQueryWrapper<>();\n");
|
|
|
+ sb.append(" w.orderByDesc(").append(className).append("Entity::getUpdatedTime);\n");
|
|
|
+ sb.append(" Page<").append(className).append("Entity> result = mapper.selectPage(page, w);\n");
|
|
|
+ sb.append(" return PageResult.of(pageNo, pageSize, result.getTotal(),\n");
|
|
|
+ sb.append(" result.getRecords().stream().map(e -> BeanUtil.copyProperties(e, ").append(className).append("VO.class)).collect(Collectors.toList()));\n");
|
|
|
+ sb.append(" }\n\n");
|
|
|
+
|
|
|
+ // list
|
|
|
+ sb.append(" public List<").append(className).append("VO> getList() {\n");
|
|
|
+ sb.append(" return mapper.selectList(new LambdaQueryWrapper<>()).stream()\n");
|
|
|
+ sb.append(" .map(e -> BeanUtil.copyProperties(e, ").append(className).append("VO.class)).collect(Collectors.toList());\n");
|
|
|
+ sb.append(" }\n\n");
|
|
|
+
|
|
|
+ // create
|
|
|
+ sb.append(" @Transactional\n");
|
|
|
+ sb.append(" public ").append(className).append("VO create(").append(className).append("CreateDTO dto) {\n");
|
|
|
+ sb.append(" ").append(className).append("Entity entity = BeanUtil.copyProperties(dto, ").append(className).append("Entity.class);\n");
|
|
|
+ sb.append(" mapper.insert(entity);\n");
|
|
|
+ sb.append(" return BeanUtil.copyProperties(mapper.selectById(entity.getId()), ").append(className).append("VO.class);\n");
|
|
|
+ sb.append(" }\n\n");
|
|
|
+
|
|
|
+ // update
|
|
|
+ sb.append(" @Transactional\n");
|
|
|
+ sb.append(" public ").append(className).append("VO update(").append(pkType).append(" ").append(pkField).append(", ").append(className).append("UpdateDTO dto) {\n");
|
|
|
+ sb.append(" ").append(className).append("Entity exist = requireEntity(").append(pkField).append(");\n");
|
|
|
+ sb.append(" BeanUtil.copyProperties(dto, exist);\n");
|
|
|
+ sb.append(" mapper.updateById(exist);\n");
|
|
|
+ sb.append(" return BeanUtil.copyProperties(mapper.selectById(").append(pkField).append("), ").append(className).append("VO.class);\n");
|
|
|
+ sb.append(" }\n\n");
|
|
|
+
|
|
|
+ // delete
|
|
|
+ sb.append(" @Transactional\n");
|
|
|
+ sb.append(" public void delete(List<").append(pkType).append("> ids) {\n");
|
|
|
+ sb.append(" if (ids == null || ids.isEmpty()) throw new BusinessException(400, \"删除失败,删除对象不能为空\");\n");
|
|
|
+ sb.append(" mapper.deleteBatchIds(ids);\n");
|
|
|
+ sb.append(" }\n\n");
|
|
|
+
|
|
|
+ // requireEntity
|
|
|
+ sb.append(" private ").append(className).append("Entity requireEntity(").append(pkType).append(" ").append(pkField).append(") {\n");
|
|
|
+ sb.append(" ").append(className).append("Entity entity = mapper.selectById(").append(pkField).append(");\n");
|
|
|
+ sb.append(" if (entity == null) throw new BusinessException(404, \"").append(functionName).append("不存在\");\n");
|
|
|
+ sb.append(" return entity;\n");
|
|
|
+ sb.append(" }\n");
|
|
|
+
|
|
|
+ sb.append("}\n");
|
|
|
+ return sb.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ private String buildController(String pkg, String className, String moduleName, String businessName) {
|
|
|
+ String camelBiz = toCamelCase(businessName);
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
+ sb.append("package com.payment.platform.module.").append(pkg).append(".").append(camelBiz).append(".controller;\n\n");
|
|
|
+ sb.append("import com.payment.platform.common.response.PageResult;\n");
|
|
|
+ sb.append("import com.payment.platform.common.response.Result;\n");
|
|
|
+ sb.append("import com.payment.platform.module.").append(pkg).append(".").append(camelBiz).append(".dto.*;\n");
|
|
|
+ sb.append("import com.payment.platform.module.").append(pkg).append(".").append(camelBiz).append(".service.").append(className).append("Service;\n");
|
|
|
+ sb.append("import jakarta.validation.Valid;\n");
|
|
|
+ sb.append("import lombok.RequiredArgsConstructor;\n");
|
|
|
+ sb.append("import org.springframework.web.bind.annotation.*;\n\n");
|
|
|
+ sb.append("import java.util.List;\n\n");
|
|
|
+ sb.append("@RestController\n");
|
|
|
+ sb.append("@RequestMapping(\"/").append(moduleName).append("/").append(businessName).append("\")\n");
|
|
|
+ sb.append("@RequiredArgsConstructor\n");
|
|
|
+ sb.append("public class ").append(className).append("Controller {\n\n");
|
|
|
+ sb.append(" private final ").append(className).append("Service service;\n\n");
|
|
|
+
|
|
|
+ sb.append(" @GetMapping(\"/list\")\n");
|
|
|
+ sb.append(" public Result<List<").append(className).append("VO>> getList() {\n");
|
|
|
+ sb.append(" return Result.ok(service.getList());\n");
|
|
|
+ sb.append(" }\n\n");
|
|
|
+
|
|
|
+ sb.append(" @GetMapping(\"/page\")\n");
|
|
|
+ sb.append(" public Result<PageResult<").append(className).append("VO>> getPage(\n");
|
|
|
+ sb.append(" @RequestParam(defaultValue = \"1\") int pageNo,\n");
|
|
|
+ sb.append(" @RequestParam(defaultValue = \"10\") int pageSize) {\n");
|
|
|
+ sb.append(" return Result.ok(service.getPage(pageNo, pageSize));\n");
|
|
|
+ sb.append(" }\n\n");
|
|
|
+
|
|
|
+ sb.append(" @GetMapping(\"/detail/{id}\")\n");
|
|
|
+ sb.append(" public Result<").append(className).append("VO> getDetail(@PathVariable Long id) {\n");
|
|
|
+ sb.append(" return Result.ok(service.getDetail(id));\n");
|
|
|
+ sb.append(" }\n\n");
|
|
|
+
|
|
|
+ sb.append(" @PostMapping(\"/create\")\n");
|
|
|
+ sb.append(" public Result<").append(className).append("VO> create(@Valid @RequestBody ").append(className).append("CreateDTO dto) {\n");
|
|
|
+ sb.append(" return Result.ok(service.create(dto));\n");
|
|
|
+ sb.append(" }\n\n");
|
|
|
+
|
|
|
+ sb.append(" @PutMapping(\"/update/{id}\")\n");
|
|
|
+ sb.append(" public Result<").append(className).append("VO> update(@PathVariable Long id, @RequestBody ").append(className).append("UpdateDTO dto) {\n");
|
|
|
+ sb.append(" return Result.ok(service.update(id, dto));\n");
|
|
|
+ sb.append(" }\n\n");
|
|
|
+
|
|
|
+ sb.append(" @DeleteMapping(\"/delete\")\n");
|
|
|
+ sb.append(" public Result<Void> delete(@RequestBody List<Long> ids) {\n");
|
|
|
+ sb.append(" service.delete(ids);\n");
|
|
|
+ sb.append(" return Result.ok();\n");
|
|
|
+ sb.append(" }\n");
|
|
|
+
|
|
|
+ sb.append("}\n");
|
|
|
+ return sb.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 数据库元数据查询 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询 information_schema 获取表注释。
|
|
|
+ */
|
|
|
+ private String queryTableComment(String tableName) {
|
|
|
+ try {
|
|
|
+ String sql = "SELECT TABLE_COMMENT FROM information_schema.TABLES WHERE TABLE_NAME = ? LIMIT 1";
|
|
|
+ List<String> result = jdbcTemplate.query(sql, (rs, rowNum) -> rs.getString("TABLE_COMMENT"), tableName);
|
|
|
+ return result.isEmpty() ? "" : (result.get(0) != null ? result.get(0) : "");
|
|
|
+ } catch (Exception e) {
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询 information_schema 获取列信息。
|
|
|
+ */
|
|
|
+ private List<Map<String, Object>> queryTableColumns(String tableName) {
|
|
|
+ String sql = "SELECT COLUMN_NAME, COLUMN_COMMENT, COLUMN_TYPE, COLUMN_DEFAULT, " +
|
|
|
+ "CHARACTER_MAXIMUM_LENGTH, COLUMN_KEY, EXTRA, IS_NULLABLE " +
|
|
|
+ "FROM information_schema.COLUMNS WHERE TABLE_NAME = ? " +
|
|
|
+ "ORDER BY ORDINAL_POSITION";
|
|
|
+ return jdbcTemplate.queryForList(sql, tableName);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 将 DB 元数据填充到已有列实体(保留用户自定义的生成配置字段)。
|
|
|
+ */
|
|
|
+ private void fillDbMetadata(GenTableColumnEntity column, Map<String, Object> dbCol) {
|
|
|
+ column.setColumnName((String) dbCol.get("COLUMN_NAME"));
|
|
|
+ column.setColumnComment(dbCol.get("COLUMN_COMMENT") != null ? (String) dbCol.get("COLUMN_COMMENT") : "");
|
|
|
+ column.setColumnType((String) dbCol.get("COLUMN_TYPE"));
|
|
|
+ column.setColumnLength(dbCol.get("CHARACTER_MAXIMUM_LENGTH") != null
|
|
|
+ ? String.valueOf(dbCol.get("CHARACTER_MAXIMUM_LENGTH")) : "");
|
|
|
+ column.setColumnDefault(dbCol.get("COLUMN_DEFAULT") != null ? (String) dbCol.get("COLUMN_DEFAULT") : "");
|
|
|
+ column.setIsPk("PRI".equals(dbCol.get("COLUMN_KEY")));
|
|
|
+ column.setIsIncrement("auto_increment".equalsIgnoreCase((String) dbCol.get("EXTRA")));
|
|
|
+ column.setIsNullable("YES".equalsIgnoreCase((String) dbCol.get("IS_NULLABLE")));
|
|
|
+ column.setIsUnique(false); // DB 不单独查询 UNIQUE,默认 false
|
|
|
+ // keep pythonType / pythonField / isInsert / isEdit / isList / isQuery / queryType / htmlType / dictType as-is
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从 DB 元数据构建新的列实体。
|
|
|
+ */
|
|
|
+ private GenTableColumnEntity buildColumnFromDb(Map<String, Object> dbCol, Long tableId) {
|
|
|
+ GenTableColumnEntity column = new GenTableColumnEntity();
|
|
|
+ column.setTableId(tableId);
|
|
|
+ fillDbMetadata(column, dbCol);
|
|
|
+
|
|
|
+ String colName = column.getColumnName();
|
|
|
+ String colType = column.getColumnType();
|
|
|
+
|
|
|
+ // 推断 pythonField (驼峰)
|
|
|
+ column.setPythonField(toCamelCase(colName));
|
|
|
+ // 推断 pythonType
|
|
|
+ column.setPythonType(dbTypeToPythonType(colType));
|
|
|
+
|
|
|
+ // 推断 isInsert / isEdit / isList / isQuery
|
|
|
+ boolean isPk = Boolean.TRUE.equals(column.getIsPk());
|
|
|
+ column.setIsInsert(!isPk);
|
|
|
+ column.setIsEdit(!isPk);
|
|
|
+ column.setIsList(!isPk);
|
|
|
+ column.setIsQuery(!isPk);
|
|
|
+ if (isPk) {
|
|
|
+ column.setQueryType(null);
|
|
|
+ } else {
|
|
|
+ column.setQueryType("EQ");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 推断 htmlType
|
|
|
+ String upperType = colType.toUpperCase();
|
|
|
+ if (upperType.contains("TEXT") || upperType.contains("LONGTEXT")) {
|
|
|
+ column.setHtmlType("textarea");
|
|
|
+ } else {
|
|
|
+ column.setHtmlType("input");
|
|
|
+ }
|
|
|
+ column.setDictType("");
|
|
|
+
|
|
|
+ return column;
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 类型映射工具方法 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * DB 列类型 -> Java 类型字符串。
|
|
|
+ */
|
|
|
+ static String dbTypeToJavaType(String columnType) {
|
|
|
+ if (columnType == null) return "String";
|
|
|
+ String t = columnType.toLowerCase().trim();
|
|
|
+ if (t.startsWith("varchar") || t.startsWith("char") || t.equals("text")
|
|
|
+ || t.equals("mediumtext") || t.equals("longtext") || t.equals("tinytext")
|
|
|
+ || t.equals("json") || t.equals("enum") || t.equals("set")) return "String";
|
|
|
+ if (t.startsWith("bigint")) return "Long";
|
|
|
+ if (t.startsWith("int") || t.startsWith("tinyint") || t.startsWith("smallint")
|
|
|
+ || t.startsWith("mediumint")) return "Integer";
|
|
|
+ if (t.startsWith("decimal") || t.startsWith("numeric")) return "BigDecimal";
|
|
|
+ if (t.startsWith("float")) return "Float";
|
|
|
+ if (t.startsWith("double")) return "Double";
|
|
|
+ if (t.startsWith("datetime") || t.startsWith("timestamp")) return "OffsetDateTime";
|
|
|
+ if (t.startsWith("date")) return "LocalDate";
|
|
|
+ if (t.startsWith("time")) return "LocalTime";
|
|
|
+ if (t.equals("bit") || t.equals("tinyint(1)")) return "Boolean";
|
|
|
+ if (t.contains("blob")) return "byte[]";
|
|
|
+ return "String";
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * DB 列类型 -> Python 类型字符串(为兼容 Python 参考实现中的 pythonType 字段)。
|
|
|
+ */
|
|
|
+ static String dbTypeToPythonType(String columnType) {
|
|
|
+ if (columnType == null) return "str";
|
|
|
+ String t = columnType.toLowerCase().trim();
|
|
|
+ if (t.startsWith("varchar") || t.startsWith("char") || t.equals("text")
|
|
|
+ || t.equals("mediumtext") || t.equals("longtext") || t.equals("tinytext")
|
|
|
+ || t.equals("json") || t.equals("enum") || t.equals("set")) return "str";
|
|
|
+ if (t.startsWith("bigint") || t.startsWith("int") || t.startsWith("tinyint")
|
|
|
+ || t.startsWith("smallint") || t.startsWith("mediumint")) return "int";
|
|
|
+ if (t.startsWith("decimal") || t.startsWith("numeric") || t.startsWith("float")
|
|
|
+ || t.startsWith("double")) return "float";
|
|
|
+ if (t.startsWith("datetime") || t.startsWith("timestamp") || t.startsWith("date")
|
|
|
+ || t.startsWith("time")) return "datetime";
|
|
|
+ if (t.equals("bit") || t.equals("tinyint(1)")) return "bool";
|
|
|
+ if (t.contains("blob")) return "bytes";
|
|
|
+ return "str";
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * snake_case -> camelCase
|
|
|
+ */
|
|
|
+ static String toCamelCase(String name) {
|
|
|
+ if (name == null || name.isEmpty()) return name;
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
+ boolean upper = false;
|
|
|
+ for (char c : name.toCharArray()) {
|
|
|
+ if (c == '_') {
|
|
|
+ upper = true;
|
|
|
+ } else {
|
|
|
+ sb.append(upper ? Character.toUpperCase(c) : c);
|
|
|
+ upper = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return sb.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * snake_case -> PascalCase
|
|
|
+ */
|
|
|
+ static String toPascalCase(String name) {
|
|
|
+ String camel = toCamelCase(name);
|
|
|
+ if (camel == null || camel.isEmpty()) return camel;
|
|
|
+ return Character.toUpperCase(camel.charAt(0)) + camel.substring(1);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 文件路径解析 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 尝试解析 Java 源码根目录。
|
|
|
+ * 策略:从 classpath 向上查找 java/src/main/java 目录。
|
|
|
+ */
|
|
|
+ private String resolveBasePath() {
|
|
|
+ // 尝试从当前 working directory 定位
|
|
|
+ String cwd = System.getProperty("user.dir");
|
|
|
+ Path candidate = Paths.get(cwd, "java/src/main/java");
|
|
|
+ if (Files.exists(candidate)) return candidate.toString();
|
|
|
+
|
|
|
+ // fallback: 从类路径推断
|
|
|
+ try {
|
|
|
+ Path classPath = Paths.get(getClass().getProtectionDomain().getCodeSource().getLocation().toURI());
|
|
|
+ // 向上走到项目根: .../target/classes -> .../java/src/main/java
|
|
|
+ Path projectRoot = classPath;
|
|
|
+ while (projectRoot != null && !projectRoot.getFileName().toString().equals("java")) {
|
|
|
+ projectRoot = projectRoot.getParent();
|
|
|
+ }
|
|
|
+ if (projectRoot != null) {
|
|
|
+ Path srcPath = projectRoot.resolve("src/main/java");
|
|
|
+ if (Files.exists(srcPath)) return srcPath.toString();
|
|
|
+ }
|
|
|
+ } catch (Exception ignored) {
|
|
|
+ }
|
|
|
+ // 最终 fallback
|
|
|
+ return cwd + "/java/src/main/java";
|
|
|
+ }
|
|
|
+
|
|
|
+ private GenTableEntity requireTable(Long id) {
|
|
|
+ GenTableEntity e = genTableMapper.selectById(id);
|
|
|
+ if (e == null) throw new BusinessException(404, "业务表不存在");
|
|
|
+ return e;
|
|
|
+ }
|
|
|
+}
|