diff --git a/wwjcloud-nest-v1/tools/java-to-nestjs-migration/converters/service-method-converter.js b/wwjcloud-nest-v1/tools/java-to-nestjs-migration/converters/service-method-converter.js index 6b34370e..642c51b1 100644 --- a/wwjcloud-nest-v1/tools/java-to-nestjs-migration/converters/service-method-converter.js +++ b/wwjcloud-nest-v1/tools/java-to-nestjs-migration/converters/service-method-converter.js @@ -21,6 +21,7 @@ const StringConverter = require('./utils/string.converter'); const CollectionConverter = require('./utils/collection.converter'); const JsonConverter = require('./utils/json.converter'); const ObjectConverter = require('./utils/object.converter'); +const JavaApiConverter = require('./utils/java-api.converter'); const QueryWrapperConverter = require('./mybatis/query-wrapper.converter'); const MapperConverter = require('./mybatis/mapper.converter'); @@ -45,6 +46,7 @@ class ServiceMethodConverter { this.collection = new CollectionConverter(); this.json = new JsonConverter(); this.object = new ObjectConverter(); + this.javaApi = new JavaApiConverter(); this.queryWrapper = new QueryWrapperConverter(); this.mapper = new MapperConverter(); @@ -90,6 +92,7 @@ class ServiceMethodConverter { tsBody = this.collection.convert(tsBody); tsBody = this.json.convert(tsBody); tsBody = this.object.convert(tsBody); + tsBody = this.javaApi.convert(tsBody); // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // 【阶段4】MyBatis → TypeORM转换 @@ -173,6 +176,15 @@ class ServiceMethodConverter { const configImports = this.config.analyzeImports(convertedBody); configImports.forEach(imp => imports.boot.add(imp)); + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + // 【Java API转换】fs/path (Node.js模块) + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + const javaApiImports = this.javaApi.analyzeImports(convertedBody); + javaApiImports.forEach(imp => { + if (imp === 'node:fs') imports.nodeModules.add('fs'); + if (imp === 'node:path') imports.nodeModules.add('path'); + }); + return { nestjs: Array.from(imports.nestjs), boot: Array.from(imports.boot), diff --git a/wwjcloud-nest-v1/tools/java-to-nestjs-migration/converters/utils/java-api.converter.js b/wwjcloud-nest-v1/tools/java-to-nestjs-migration/converters/utils/java-api.converter.js new file mode 100644 index 00000000..26e25301 --- /dev/null +++ b/wwjcloud-nest-v1/tools/java-to-nestjs-migration/converters/utils/java-api.converter.js @@ -0,0 +1,114 @@ +/** + * Java API转换器 + * + * Java特有API → Node.js/TypeScript API + * + * - path.toFile() → path (Path对象在Node.js中就是string) + * - file.path → file (File对象在Node.js中就是path string) + * - Files.readString(path) → fs.readFileSync(path, 'utf-8') + * - Files.write(path, content) → fs.writeFileSync(path, content) + */ +class JavaApiConverter { + /** + * 转换Java API + */ + convert(javaCode) { + let tsCode = javaCode; + + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + // 【File/Path API】→ Node.js string paths + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + // path.toFile() → path (在Node.js中path就是string) + tsCode = tsCode.replace(/(\w+)\.toFile\(\)/g, '$1'); + + // file.path → file (在Node.js中file就是path string) + tsCode = tsCode.replace(/(\w+)\.path\b/g, '$1'); + + // Paths.get(...) → path.join(...) + tsCode = tsCode.replace(/Paths\.get\(/g, 'path.join('); + + // File.separator → path.sep + tsCode = tsCode.replace(/File\.separator/g, 'path.sep'); + + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + // 【Files API】→ fs module + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + // Files.readString(path) → fs.readFileSync(path, 'utf-8') + tsCode = tsCode.replace(/Files\.readString\(([^)]+)\)/g, "fs.readFileSync($1, 'utf-8')"); + + // Files.readAllBytes(path) → fs.readFileSync(path) + tsCode = tsCode.replace(/Files\.readAllBytes\(([^)]+)\)/g, 'fs.readFileSync($1)'); + + // Files.write(path, content) → fs.writeFileSync(path, content) + tsCode = tsCode.replace(/Files\.write\(([^,]+),\s*([^)]+)\)/g, 'fs.writeFileSync($1, $2)'); + + // Files.exists(path) → fs.existsSync(path) + tsCode = tsCode.replace(/Files\.exists\(([^)]+)\)/g, 'fs.existsSync($1)'); + + // Files.createDirectories(path) → fs.mkdirSync(path, { recursive: true }) + tsCode = tsCode.replace(/Files\.createDirectories\(([^)]+)\)/g, 'fs.mkdirSync($1, { recursive: true })'); + + // Files.delete(path) → fs.unlinkSync(path) + tsCode = tsCode.replace(/Files\.delete\(([^)]+)\)/g, 'fs.unlinkSync($1)'); + + // Files.list(path) → fs.readdirSync(path) + tsCode = tsCode.replace(/Files\.list\(([^)]+)\)/g, 'fs.readdirSync($1)'); + + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + // 【Stream/Optional API】→ TypeScript + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + // .orElse(null) → || null + tsCode = tsCode.replace(/\.orElse\(null\)/g, ' || null'); + + // .orElse(defaultValue) → || defaultValue + tsCode = tsCode.replace(/\.orElse\(([^)]+)\)/g, ' || $1'); + + // .isPresent() → (删除,TypeScript直接判断truthy) + // 注意:这个需要上下文判断,暂时保留 + + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + // 【Charset/Encoding】→ 字符串编码 + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + // Charset.forName("UTF-8") → 'utf-8' + tsCode = tsCode.replace(/Charset\.forName\("UTF-8"\)/g, "'utf-8'"); + tsCode = tsCode.replace(/Charset\.forName\('UTF-8'\)/g, "'utf-8'"); + + // System.getProperty("sun.jnu.encoding") → 'utf-8' (默认UTF-8) + tsCode = tsCode.replace(/System\.getProperty\("sun\.jnu\.encoding"\)/g, "'utf-8'"); + + // StandardCharsets.UTF_8 → 'utf-8' + tsCode = tsCode.replace(/StandardCharsets\.UTF_8/g, "'utf-8'"); + + return tsCode; + } + + /** + * 分析需要的imports + */ + analyzeImports(tsCode) { + const imports = new Set(); + + // 检查是否使用了fs + if (tsCode.includes('fs.readFileSync') || tsCode.includes('fs.writeFileSync') || + tsCode.includes('fs.existsSync') || tsCode.includes('fs.mkdirSync') || + tsCode.includes('fs.unlinkSync') || tsCode.includes('fs.readdirSync') || + tsCode.includes('fs.statSync')) { + imports.add('node:fs'); + } + + // 检查是否使用了path + if (tsCode.includes('path.join') || tsCode.includes('path.sep') || + tsCode.includes('path.dirname') || tsCode.includes('path.basename')) { + imports.add('node:path'); + } + + return Array.from(imports); + } +} + +module.exports = JavaApiConverter; + diff --git a/wwjcloud-nest-v1/tools/java-to-nestjs-migration/generators/service-generator.js b/wwjcloud-nest-v1/tools/java-to-nestjs-migration/generators/service-generator.js index dcb0b9ec..d8f1dcd0 100644 --- a/wwjcloud-nest-v1/tools/java-to-nestjs-migration/generators/service-generator.js +++ b/wwjcloud-nest-v1/tools/java-to-nestjs-migration/generators/service-generator.js @@ -177,7 +177,7 @@ ${methods} } /** - * 从方法参数中提取DTO/VO类型 + * 从方法参数和方法体中提取DTO/VO类型 * * @param {object} javaService - Java服务对象 * @returns {string[]} DTO/VO类型列表 @@ -190,27 +190,40 @@ ${methods} } javaService.methods.forEach(method => { - if (!method.parameters) { - return; + // 1. 从参数中提取 + if (method.parameters) { + method.parameters.forEach(param => { + const paramType = param.type; + if (!paramType) { + return; + } + + // 检查是否是DTO/VO/Param类型(通常包含这些后缀或首字母大写) + if (paramType.includes('Dto') || paramType.includes('Vo') || paramType.includes('Param')) { + // 提取纯类型名(去除包名) + const simpleType = paramType.split('.').pop(); + dtos.add(simpleType); + } else if (paramType[0] === paramType[0].toUpperCase() && !paramType.startsWith('String') && !paramType.startsWith('Integer') && !paramType.startsWith('Long')) { + // 其他业务类型(首字母大写,但排除Java基本类型) + const simpleType = paramType.split('.').pop(); + dtos.add(simpleType); + } + }); } - method.parameters.forEach(param => { - const paramType = param.type; - if (!paramType) { - return; + // 2. 从方法体中提取VO类型(通过正则匹配) + if (method.methodBody) { + // 匹配 new XxxVo(), XxxVo xxx = ..., List, etc. + const voMatches = method.methodBody.match(/\b([A-Z]\w*(Vo|Dto|Param))\b/g); + if (voMatches) { + voMatches.forEach(vo => { + // 排除Java基本类型 + if (!['String', 'Integer', 'Long', 'Double', 'Float', 'Boolean', 'List', 'Map', 'Set'].includes(vo)) { + dtos.add(vo); + } + }); } - - // 检查是否是DTO/VO/Param类型(通常包含这些后缀或首字母大写) - if (paramType.includes('Dto') || paramType.includes('Vo') || paramType.includes('Param')) { - // 提取纯类型名(去除包名) - const simpleType = paramType.split('.').pop(); - dtos.add(simpleType); - } else if (paramType[0] === paramType[0].toUpperCase() && !paramType.startsWith('String') && !paramType.startsWith('Integer') && !paramType.startsWith('Long')) { - // 其他业务类型(首字母大写,但排除Java基本类型) - const simpleType = paramType.split('.').pop(); - dtos.add(simpleType); - } - }); + } }); return Array.from(dtos); @@ -389,8 +402,16 @@ ${methods} // 添加DTO导入 if (javaService.dtos && javaService.dtos.length > 0) { javaService.dtos.forEach(dto => { - const dtoName = this.namingUtils.generateDtoName(dto); - const dtoFileName = this.namingUtils.generateFileName(dto, 'dto'); + // 清理泛型语法:List → List, Record → Record + let cleanDto = dto.replace(/<[^>]+>/g, ''); + + // 跳过Java基础类型和集合类型(不是DTO) + if (['List', 'ArrayList', 'Map', 'HashMap', 'Set', 'HashSet', 'Record', 'String', 'Integer', 'Long', 'Boolean'].includes(cleanDto)) { + return; + } + + const dtoName = this.namingUtils.generateDtoName(cleanDto); + const dtoFileName = this.namingUtils.generateFileName(cleanDto, 'dto'); imports.push(`import { ${dtoName} } from '../dtos/${dtoFileName.replace('.ts', '')}';`); }); }