#!/usr/bin/env node /** * NestJS vs Java 路由规范化对比脚本(简化版) * 使用内置模块,解决参数风格差异、空子路径等问题 */ const fs = require('fs'); const path = require('path'); // 路由规范化函数 function normalizeRoute(route) { return route .replace(/:([^/]+)/g, '{$1}') // :param -> {param} .replace(/\/$/g, '') // 移除尾部斜杠 .replace(/^\/$/, ''); // 处理根路径 } // 查找所有控制器文件 function findControllers(dir, extension, controllers = []) { try { const items = fs.readdirSync(dir); for (const item of items) { const fullPath = path.join(dir, item); const stat = fs.statSync(fullPath); if (stat.isDirectory()) { findControllers(fullPath, extension, controllers); } else if (item.endsWith(extension)) { controllers.push(fullPath); } } } catch (error) { console.warn(`警告: 无法访问目录 ${dir}: ${error.message}`); } return controllers; } // 提取NestJS路由信息 function extractNestJSRoutes(controllerPath) { try { const content = fs.readFileSync(controllerPath, 'utf8'); const routes = []; // 提取类级Controller路径 const controllerMatch = content.match(/@Controller\(["'`]([^"'`]+)["'`]\)/); const basePath = controllerMatch ? controllerMatch[1] : ''; if (!basePath) return routes; // 提取方法级路由 - 改进正则 const methodPattern = /@(Get|Post|Put|Delete|Patch)\((["'`]([^"'`]*)["'`])?\)[\s\S]*?(async\s+)?(\w+)\s*\(/g; let match; while ((match = methodPattern.exec(content)) !== null) { const method = match[1].toUpperCase(); const subPath = match[3] || ''; const fullPath = normalizeRoute(path.posix.join(basePath, subPath)); routes.push({ method, path: fullPath, basePath, subPath, file: path.basename(controllerPath) }); } return routes; } catch (error) { console.warn(`警告: 无法读取文件 ${controllerPath}: ${error.message}`); return []; } } // 提取Java路由信息 function extractJavaRoutes(controllerPath) { try { const content = fs.readFileSync(controllerPath, 'utf8'); const routes = []; // 提取类级RequestMapping路径 const classMappingMatch = content.match(/@RequestMapping\(["'`]([^"'`]+)["'`]\)/); const basePath = classMappingMatch ? classMappingMatch[1] : ''; if (!basePath) return routes; // 提取方法级路由 - 改进正则 const methodPattern = /@(GetMapping|PostMapping|PutMapping|DeleteMapping|RequestMapping)\((["'`]([^"'`]*)["'`])?\)[\s\S]*?(\w+)\s*\(/g; let match; while ((match = methodPattern.exec(content)) !== null) { const annotation = match[1]; const subPath = match[3] || ''; let method = 'GET'; if (annotation === 'PostMapping') method = 'POST'; else if (annotation === 'PutMapping') method = 'PUT'; else if (annotation === 'DeleteMapping') method = 'DELETE'; const fullPath = normalizeRoute(path.posix.join(basePath, subPath)); routes.push({ method, path: fullPath, basePath, subPath, file: path.basename(controllerPath) }); } return routes; } catch (error) { console.warn(`警告: 无法读取文件 ${controllerPath}: ${error.message}`); return []; } } // 主对比函数 function compareRoutes(nestDir, javaDir) { console.log('🔍 正在扫描NestJS控制器...'); const nestControllers = findControllers(nestDir, '.controller.ts'); console.log(`✅ 找到 ${nestControllers.length} 个NestJS控制器`); console.log('🔍 正在扫描Java控制器...'); const javaControllers = findControllers(javaDir, 'Controller.java'); console.log(`✅ 找到 ${javaControllers.length} 个Java控制器`); const nestRoutes = []; const javaRoutes = []; // 提取NestJS路由 nestControllers.forEach(file => { const routes = extractNestJSRoutes(file); nestRoutes.push(...routes); }); // 提取Java路由 javaControllers.forEach(file => { const routes = extractJavaRoutes(file); javaRoutes.push(...routes); }); console.log(`\n📊 NestJS路由总数: ${nestRoutes.length}`); console.log(`📊 Java路由总数: ${javaRoutes.length}`); // 对比分析 const comparison = { total: { nest: nestRoutes.length, java: javaRoutes.length }, matches: [], missingInNest: [], missingInJava: [], nestRoutes: nestRoutes, javaRoutes: javaRoutes }; // 标准化路由键用于对比 const getRouteKey = (route) => `${route.method}:${route.path}`; const nestRouteMap = new Map(nestRoutes.map(r => [getRouteKey(r), r])); const javaRouteMap = new Map(javaRoutes.map(r => [getRouteKey(r), r])); // 查找匹配项 for (const [key, nestRoute] of nestRouteMap) { if (javaRouteMap.has(key)) { comparison.matches.push({ route: key, nestFile: nestRoute.file, javaFile: javaRouteMap.get(key).file }); } else { comparison.missingInJava.push(nestRoute); } } // 查找Java中缺失的项 for (const [key, javaRoute] of javaRouteMap) { if (!nestRouteMap.has(key)) { comparison.missingInNest.push(javaRoute); } } return comparison; } // 执行对比 console.log('🚀 NestJS vs Java 路由规范化对比开始...\n'); const nestDir = '/Users/wanwu/Documents/wanwujie/wwjcloud-nsetjs/wwjcloud-nest-v1/wwjcloud/libs/wwjcloud-core/src/controllers'; const javaDir = '/Users/wanwu/Documents/wanwujie/wwjcloud-nsetjs/niucloud-java/niucloud-core/src/main/java/com/niu/core/controller'; try { const result = compareRoutes(nestDir, javaDir); console.log('\n📊 对比结果:'); console.log(`✅ 匹配路由: ${result.matches.length}`); console.log(`❌ NestJS缺失: ${result.missingInNest.length}`); console.log(`⚠️ Java缺失: ${result.missingInJava.length}`); console.log(`📈 覆盖率: ${((comparison.matches.length / comparison.total.java) * 100).toFixed(1)}%`); // 详细分析缺失情况 if (result.missingInNest.length > 0) { console.log('\n🔍 NestJS缺失路由详情(按模块分组):'); const missingByModule = {}; result.missingInNest.forEach(route => { const module = route.path.split('/')[1] || 'root'; if (!missingByModule[module]) missingByModule[module] = []; missingByModule[module].push(route); }); Object.keys(missingByModule).sort().forEach(module => { console.log(`\n ${module}:`); missingByModule[module].forEach(route => { console.log(` ${route.method} ${route.path} (${route.file})`); }); }); } // 生成修复建议 if (result.missingInNest.length > 0) { console.log('\n🔧 修复建议:'); console.log('1. 检查是否存在路由风格差异(:param vs {param})'); console.log('2. 验证空子路径是否正确处理(@Post(""))'); console.log('3. 确认路径前缀是否统一(adminapi vs api)'); console.log('4. 检查模块分组逻辑是否一致'); } console.log('\n✅ 对比完成!'); // 保存详细结果到文件 const outputFile = '/Users/wanwu/Documents/wanwujie/wwjcloud-nsetjs/route-comparison-result.json'; fs.writeFileSync(outputFile, JSON.stringify(result, null, 2)); console.log(`\n📄 详细结果已保存到: ${outputFile}`); } catch (error) { console.error('❌ 对比过程中发生错误:', error.message); process.exit(1); }