243 lines
7.5 KiB
JavaScript
243 lines
7.5 KiB
JavaScript
#!/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);
|
||
} |