#!/usr/bin/env node /** * PHP与NestJS项目自动映射检查器 * 检查PHP项目与NestJS项目的模块、控制器、服务等对应关系 */ const fs = require('fs'); const path = require('path'); class AutoMappingChecker { constructor() { this.projectRoot = process.cwd(); this.phpPath = path.join(this.projectRoot, 'niucloud-php/niucloud'); this.nestjsPath = path.join(this.projectRoot, 'wwjcloud/src'); this.results = { modules: [], controllers: [], services: [], models: [], summary: { total: 0, matched: 0, missing: 0 } }; } /** * 检查目录是否存在 */ checkDirectories() { if (!fs.existsSync(this.phpPath)) { console.error('❌ PHP项目路径不存在:', this.phpPath); return false; } if (!fs.existsSync(this.nestjsPath)) { console.error('❌ NestJS项目路径不存在:', this.nestjsPath); return false; } return true; } /** * 获取PHP控制器列表 */ getPhpControllers() { const controllers = []; const adminApiPath = path.join(this.phpPath, 'app/adminapi/controller'); const apiPath = path.join(this.phpPath, 'app/api/controller'); // 扫描管理端控制器 if (fs.existsSync(adminApiPath)) { this.scanPhpControllers(adminApiPath, 'adminapi', controllers); } // 扫描前台控制器 if (fs.existsSync(apiPath)) { this.scanPhpControllers(apiPath, 'api', controllers); } return controllers; } /** * 扫描PHP控制器 */ scanPhpControllers(dir, type, controllers) { const entries = fs.readdirSync(dir, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(dir, entry.name); if (entry.isDirectory()) { // 递归扫描子目录 this.scanPhpControllers(fullPath, type, controllers); } else if (entry.isFile() && entry.name.endsWith('.php')) { const relativePath = path.relative(path.join(this.phpPath, 'app', type, 'controller'), fullPath); const modulePath = path.dirname(relativePath); const fileName = path.basename(entry.name, '.php'); controllers.push({ type, module: modulePath === '.' ? 'root' : modulePath, name: fileName, phpPath: fullPath, relativePath }); } } } /** * 获取NestJS控制器列表 */ getNestjsControllers() { const controllers = []; const commonPath = path.join(this.nestjsPath, 'common'); if (!fs.existsSync(commonPath)) { return controllers; } const modules = fs.readdirSync(commonPath, { withFileTypes: true }) .filter(entry => entry.isDirectory()) .map(entry => entry.name); for (const module of modules) { const modulePath = path.join(commonPath, module); // 检查adminapi控制器 const adminApiPath = path.join(modulePath, 'controllers/adminapi'); if (fs.existsSync(adminApiPath)) { this.scanNestjsControllers(adminApiPath, 'adminapi', module, controllers); } // 检查api控制器 const apiPath = path.join(modulePath, 'controllers/api'); if (fs.existsSync(apiPath)) { this.scanNestjsControllers(apiPath, 'api', module, controllers); } } return controllers; } /** * 扫描NestJS控制器 */ scanNestjsControllers(dir, type, module, controllers) { if (!fs.existsSync(dir)) return; const entries = fs.readdirSync(dir, { withFileTypes: true }); for (const entry of entries) { if (entry.isFile() && entry.name.endsWith('.controller.ts')) { const fileName = path.basename(entry.name, '.controller.ts'); controllers.push({ type, module, name: fileName, nestjsPath: path.join(dir, entry.name) }); } } } /** * 检查控制器映射 */ checkControllerMapping() { const phpControllers = this.getPhpControllers(); const nestjsControllers = this.getNestjsControllers(); console.log('\n📋 控制器映射检查结果:'); console.log('='.repeat(50)); for (const phpController of phpControllers) { const matched = nestjsControllers.find(nestjs => nestjs.type === phpController.type && this.normalizeModuleName(nestjs.module) === this.normalizeModuleName(phpController.module) && this.normalizeControllerName(nestjs.name) === this.normalizeControllerName(phpController.name) ); const status = matched ? '✅' : '❌'; const moduleDisplay = phpController.module === 'root' ? '/' : phpController.module; console.log(`${status} ${phpController.type}/${moduleDisplay}/${phpController.name}.php`); if (matched) { console.log(` → ${matched.module}/${matched.name}.controller.ts`); this.results.summary.matched++; } else { console.log(` → 缺失对应的NestJS控制器`); this.results.summary.missing++; } this.results.summary.total++; this.results.controllers.push({ php: phpController, nestjs: matched, matched: !!matched }); } } /** * 标准化模块名 */ normalizeModuleName(name) { if (name === 'root' || name === '.' || name === '/') return ''; return name.toLowerCase().replace(/[_\-]/g, ''); } /** * 标准化控制器名 */ normalizeControllerName(name) { return name.toLowerCase().replace(/[_\-]/g, ''); } /** * 检查服务映射 */ checkServiceMapping() { console.log('\n🔧 服务映射检查:'); console.log('='.repeat(50)); const phpServicePath = path.join(this.phpPath, 'app/service'); const nestjsCommonPath = path.join(this.nestjsPath, 'common'); if (!fs.existsSync(phpServicePath)) { console.log('❌ PHP服务目录不存在'); return; } if (!fs.existsSync(nestjsCommonPath)) { console.log('❌ NestJS通用服务目录不存在'); return; } // 简化的服务检查 const phpServices = this.getPhpServices(phpServicePath); const nestjsServices = this.getNestjsServices(nestjsCommonPath); for (const phpService of phpServices) { const matched = nestjsServices.find(nestjs => this.normalizeServiceName(nestjs.name) === this.normalizeServiceName(phpService.name) ); const status = matched ? '✅' : '❌'; console.log(`${status} ${phpService.name}.php`); if (matched) { console.log(` → ${matched.module}/${matched.name}.service.ts`); } } } /** * 获取PHP服务列表 */ getPhpServices(dir) { const services = []; if (!fs.existsSync(dir)) return services; const entries = fs.readdirSync(dir, { withFileTypes: true }); for (const entry of entries) { if (entry.isFile() && entry.name.endsWith('.php')) { services.push({ name: path.basename(entry.name, '.php'), path: path.join(dir, entry.name) }); } } return services; } /** * 获取NestJS服务列表 */ getNestjsServices(dir) { const services = []; if (!fs.existsSync(dir)) return services; const modules = fs.readdirSync(dir, { withFileTypes: true }) .filter(entry => entry.isDirectory()) .map(entry => entry.name); for (const module of modules) { const servicesPath = path.join(dir, module, 'services'); if (fs.existsSync(servicesPath)) { this.scanNestjsServices(servicesPath, module, services); } } return services; } /** * 扫描NestJS服务 */ scanNestjsServices(dir, module, services) { const entries = fs.readdirSync(dir, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(dir, entry.name); if (entry.isDirectory()) { this.scanNestjsServices(fullPath, module, services); } else if (entry.isFile() && entry.name.endsWith('.service.ts')) { services.push({ module, name: path.basename(entry.name, '.service.ts'), path: fullPath }); } } } /** * 标准化服务名 */ normalizeServiceName(name) { return name.toLowerCase().replace(/service$/, '').replace(/[_\-]/g, ''); } /** * 生成统计报告 */ generateSummary() { console.log('\n📊 检查统计:'); console.log('='.repeat(50)); console.log(`总计检查项: ${this.results.summary.total}`); console.log(`匹配成功: ${this.results.summary.matched} (${((this.results.summary.matched / this.results.summary.total) * 100).toFixed(1)}%)`); console.log(`缺失项目: ${this.results.summary.missing} (${((this.results.summary.missing / this.results.summary.total) * 100).toFixed(1)}%)`); if (this.results.summary.missing > 0) { console.log('\n⚠️ 需要关注的缺失项:'); const missingItems = this.results.controllers.filter(item => !item.matched); for (const item of missingItems.slice(0, 10)) { // 只显示前10个 console.log(` - ${item.php.type}/${item.php.module}/${item.php.name}.php`); } if (missingItems.length > 10) { console.log(` ... 还有 ${missingItems.length - 10} 个缺失项`); } } } /** * 运行完整检查 */ async run() { console.log('🚀 PHP与NestJS项目自动映射检查器'); console.log('='.repeat(50)); if (!this.checkDirectories()) { process.exit(1); } try { this.checkControllerMapping(); this.checkServiceMapping(); this.generateSummary(); console.log('\n✅ 检查完成!'); if (this.results.summary.missing > 0) { console.log('\n💡 建议: 根据缺失项创建对应的NestJS文件'); process.exit(1); } } catch (error) { console.error('❌ 检查过程中出现错误:', error.message); process.exit(1); } } } // 运行检查器 if (require.main === module) { const checker = new AutoMappingChecker(); checker.run().catch(console.error); } module.exports = AutoMappingChecker;