#!/usr/bin/env node const fs = require('fs'); const path = require('path'); /** * 🎮 控制器生成器 * 专门负责生成NestJS控制器 */ class ControllerGenerator { constructor() { this.config = { phpBasePath: '/Users/wanwu/Documents/wwjcloud/wwjcloud-nsetjs/niucloud-php/niucloud', nestjsBasePath: '/Users/wanwu/Documents/wwjcloud/wwjcloud-nsetjs/wwjcloud-nest/src/core', discoveryResultPath: '/Users/wanwu/Documents/wwjcloud/wwjcloud-nsetjs/tools/php-discovery-result.json', // 前端API模块优先级列表(基于PHP和Java前端API一致性) frontendApiModules: [ 'addon', 'aliapp', 'auth', 'cloud', 'dict', 'diy', 'diy_form', 'h5', 'home', 'member', 'module', 'notice', 'pay', 'pc', 'personal', 'poster', 'printer', 'site', 'stat', 'sys', 'tools', 'upgrade', 'user', 'verify', 'weapp', 'wechat', 'wxoplatform' ] }; this.discoveryData = null; this.stats = { controllersCreated: 0, errors: 0 }; } /** * 运行控制器生成 */ async run() { try { console.log('🎮 启动控制器生成器...'); console.log('目标:生成NestJS控制器文件\n'); // 加载PHP文件发现结果 await this.loadDiscoveryData(); // 生成控制器 await this.generateControllers(); // 输出统计报告 this.printStats(); } catch (error) { console.error('❌ 控制器生成失败:', error); this.stats.errors++; } } /** * 加载PHP文件发现结果 */ async loadDiscoveryData() { try { const data = fs.readFileSync(this.config.discoveryResultPath, 'utf8'); this.discoveryData = JSON.parse(data); console.log(' ✅ 成功加载PHP文件发现结果'); } catch (error) { console.error('❌ 加载发现结果失败:', error); throw error; } } /** * 生成控制器 */ async generateControllers() { console.log(' 🔨 生成控制器...'); // 检查是否有控制器数据 if (!this.discoveryData.controllers || Object.keys(this.discoveryData.controllers).length === 0) { console.log(' ⚠️ 未发现PHP控制器,跳过生成'); return; } // 优先处理前端API模块 console.log(' 🎯 优先处理前端API模块(27个相同模块)...'); // 先处理前端API优先级模块 for (const moduleName of this.config.frontendApiModules) { if (this.discoveryData.controllers[moduleName]) { console.log(` 📋 处理前端API优先级模块: ${moduleName}`); for (const [controllerName, controllerInfo] of Object.entries(this.discoveryData.controllers[moduleName])) { const layer = controllerInfo.layer || 'adminapi'; await this.createController(moduleName, controllerName, controllerInfo, layer); } } else { console.log(` ⚠️ 前端API模块 ${moduleName} 在后端控制器中不存在`); } } // 再处理其他模块 for (const [moduleName, controllers] of Object.entries(this.discoveryData.controllers)) { if (!this.isFrontendApiPriorityModule(moduleName)) { console.log(` 📋 处理其他模块: ${moduleName}`); for (const [controllerName, controllerInfo] of Object.entries(controllers)) { const layer = controllerInfo.layer || 'adminapi'; await this.createController(moduleName, controllerName, controllerInfo, layer); } } } console.log(` ✅ 生成了 ${this.stats.controllersCreated} 个控制器`); } /** * 创建控制器 */ async createController(moduleName, controllerName, controllerInfo, layer) { const controllerPath = path.join( this.config.nestjsBasePath, moduleName, 'controllers', layer, `${this.toKebabCase(controllerName)}.controller.ts` ); // 确保目录存在 this.ensureDir(path.dirname(controllerPath)); // 检查是否有对应的PHP控制器文件 const phpControllerPath = path.join(this.config.phpBasePath, 'app', layer, 'controller', moduleName, `${this.toPascalCase(controllerName)}.php`); if (!fs.existsSync(phpControllerPath)) { console.log(` ❌ 未找到PHP控制器文件,跳过生成: ${phpControllerPath}`); return; } const content = this.generateControllerContent(moduleName, controllerName, layer); fs.writeFileSync(controllerPath, content); console.log(` ✅ 创建控制器: ${moduleName}/${layer}/${this.toKebabCase(controllerName)}.controller.ts`); } /** * 生成控制器内容 */ generateControllerContent(moduleName, controllerName, layer) { const className = `${this.toPascalCase(controllerName)}Controller`; // 根据PHP控制器的实际服务依赖生成导入 const serviceImports = this.getServiceImports(moduleName, controllerName, layer); // 过滤掉空的导入语句 const filteredServiceImports = serviceImports.split('\n').filter(line => { const trimmed = line.trim(); return trimmed !== '' && !trimmed.includes("} from '';"); }).join('\n'); // 根据层类型选择合适的基础设施 const infrastructureImports = this.getInfrastructureImports(layer); const infrastructureDecorators = this.getInfrastructureDecorators(layer); // 解析PHP控制器方法 const phpMethods = this.parsePHPControllerMethods(moduleName, controllerName, layer); // 获取构造函数服务,过滤掉空的 const constructorServices = this.getConstructorServices(moduleName, controllerName, layer); const filteredConstructorServices = constructorServices.split('\n').filter(line => { const trimmed = line.trim(); return trimmed !== '' && !trimmed.includes(": undefined"); }).join('\n'); return `import { Controller, Get, Post, Put, Delete, Body, Param, Query, UseGuards, UseInterceptors, UploadedFile, UploadedFiles, Session, Req } from '@nestjs/common'; import { FileInterceptor, FilesInterceptor } from '@nestjs/platform-express'; import { ApiTags, ApiOperation, ApiResponse, ApiConsumes } from '@nestjs/swagger'; import { Request } from 'express'; ${infrastructureImports} ${filteredServiceImports} /** * ${className} * 对应 PHP: ${controllerName} Controller * 对应 Java: @RestController * * 支持装饰器: * - @UploadedFile() - 单文件上传 (对应PHP Request::file()) * - @UploadedFiles() - 多文件上传 * - @Session() - 会话管理 (对应PHP Session::get()) * - @Req() - 请求对象 (对应PHP Request) */ @ApiTags('${moduleName}') @Controller('${this.getRoutePath(layer)}/${this.toCamelCase(moduleName)}') ${infrastructureDecorators} export class ${className} { constructor( ${filteredConstructorServices} ) {} ${phpMethods} }`; } /** * 检查模块是否为前端API优先级模块 */ isFrontendApiPriorityModule(moduleName) { return this.config.frontendApiModules.includes(moduleName); } /** * 获取正确的服务层路径 */ getCorrectServiceLayer(layer) { if (layer === 'adminapi') return 'admin'; if (layer === 'api') return 'api'; if (layer === 'core') return 'core'; return layer; } /** * 获取路由路径 */ getRoutePath(layer) { if (layer === 'adminapi') return 'adminapi'; if (layer === 'api') return 'api'; return layer; } /** * 获取基础设施导入 */ getInfrastructureImports(layer) { const imports = []; // 根据层类型选择合适的基础设施 if (layer === 'adminapi') { imports.push("import { JwtAuthGuard } from '@wwjCommon/guards/jwt-auth.guard';"); imports.push("import { RolesGuard } from '@wwjCommon/guards/roles.guard';"); imports.push("import { Roles } from '@wwjCommon/decorators/roles.decorator';"); } else if (layer === 'api') { imports.push("import { JwtAuthGuard } from '@wwjCommon/guards/jwt-auth.guard';"); imports.push("import { OptionalAuthGuard } from '@wwjCommon/guards/optional-auth.guard';"); } // 通用基础设施 imports.push("import { Public } from '@wwjCommon/decorators/public.decorator';"); imports.push("import { BusinessException } from '@wwjCommon/exceptions/business.exception';"); // 常用装饰器说明(已在主import中引入) imports.push("// @UploadedFile() - 单文件上传,配合 @UseInterceptors(FileInterceptor('file'))"); imports.push("// @UploadedFiles() - 多文件上传,配合 @UseInterceptors(FilesInterceptor('files'))"); imports.push("// @Session() - 获取会话对象,对应PHP Session::get()"); imports.push("// @Req() - 获取Request对象,对应PHP Request"); return imports.join('\n'); } /** * 获取基础设施装饰器 */ getInfrastructureDecorators(layer) { if (layer === 'adminapi') { } else if (layer === 'api') { return '@UseGuards(ApiCheckTokenGuard)'; } return ''; } /** * 生成守卫装饰器 */ generateGuardDecorators(layer) { if (layer === 'adminapi') { return '@UseGuards(JwtAuthGuard, RolesGuard)'; } else if (layer === 'api') { return '@UseGuards(JwtAuthGuard)'; } return ''; } /** * 生成返回类型 */ generateReturnType(serviceMethodName) { if (serviceMethodName) { return ': Promise'; } return ': Promise'; } /** * 从PHP路由文件提取API信息(HTTP方法 + 路径) */ extractRoutesFromPHP(moduleName, controllerName, layer) { const routeFilePath = path.join(this.config.phpBasePath, 'app', layer, 'route', `${moduleName}.php`); if (!fs.existsSync(routeFilePath)) { console.log(` ⚠️ 未找到路由文件: ${routeFilePath}`); return []; } try { const routeContent = fs.readFileSync(routeFilePath, 'utf-8'); const routes = []; // 提取Route::group内的内容 const groupMatch = routeContent.match(/Route::group\(['"]([^'"]+)['"],\s*function\s*\(\)\s*\{([\s\S]*?)\}\)/); if (groupMatch) { const groupPrefix = groupMatch[1]; const groupContent = groupMatch[2]; // 匹配所有路由定义: Route::方法('路径', '控制器.方法') const routePattern = /Route::(\w+)\(['"]([^'"]+)['"],\s*['"]([^'"]+)['"]\)/g; let match; while ((match = routePattern.exec(groupContent)) !== null) { const httpMethod = match[1]; // get, post, put, delete const routePath = match[2]; // 如 'site/:id' const controllerMethod = match[3]; // 如 'site.Site/lists' // 解析控制器方法: 'site.Site/lists' -> moduleName='site', controller='Site', method='lists' const methodParts = controllerMethod.split('/'); if (methodParts.length === 2) { const controllerPart = methodParts[0]; // 'site.Site' const methodName = methodParts[1]; // 'lists' const controllerParts = controllerPart.split('.'); if (controllerParts.length === 2) { const routeModule = controllerParts[0]; // 'site' const routeController = controllerParts[1]; // 'Site' // 只提取当前控制器的路由 if (routeModule === moduleName && routeController === controllerName) { routes.push({ httpMethod: httpMethod.toLowerCase(), routePath: routePath, methodName: methodName }); } } } } } return routes; } catch (error) { console.log(` ❌ 解析路由文件失败: ${error.message}`); return []; } } /** * 解析PHP控制器方法 * 结合路由信息和控制器方法体 */ parsePHPControllerMethods(moduleName, controllerName, layer) { // 1. 先从路由文件提取API信息(HTTP方法 + 路径) const routes = this.extractRoutesFromPHP(moduleName, controllerName, layer); if (routes.length > 0) { console.log(` ✅ 从路由文件提取到 ${routes.length} 个API`); } // 2. 读取PHP控制器文件 const phpControllerPath = path.join(this.config.phpBasePath, 'app', layer, 'controller', moduleName, `${controllerName}.php`); if (!fs.existsSync(phpControllerPath)) { return ' // 未找到PHP控制器文件,无法解析方法'; } try { const phpContent = fs.readFileSync(phpControllerPath, 'utf-8'); // 3. 提取所有public方法 const methodMatches = phpContent.match(/public function (\w+)\([^)]*\)\s*\{[\s\S]*?\n\s*\}/g); if (!methodMatches || methodMatches.length === 0) { return ' // 未找到PHP控制器方法'; } // 4. 为每个方法匹配路由信息 const methods = methodMatches.map(match => { // 提取方法名 const nameMatch = match.match(/public function (\w+)\(/); if (!nameMatch) return null; const methodName = nameMatch[1]; // 从路由信息中查找该方法的路由配置 const routeInfo = routes.find(r => r.methodName === methodName); // 如果没有路由信息,跳过此方法(说明没有对外暴露) if (!routeInfo) { console.log(` ⚠️ 方法 ${methodName} 没有对应的路由,跳过生成`); return null; } // 提取方法注释 const commentMatch = phpContent.match(new RegExp(`/\\*\\*[\\s\\S]*?\\*/\\s*public function ${methodName}`)); const description = commentMatch ? this.extractDescription(commentMatch[0]) : methodName; // 使用路由信息中的HTTP方法 const httpMethod = routeInfo.httpMethod.charAt(0).toUpperCase() + routeInfo.httpMethod.slice(1); // 生成NestJS方法(传入路由信息) return this.generateNestJSControllerMethod(methodName, description, httpMethod, match, moduleName, controllerName, layer, routeInfo); }).filter(method => method !== null); return methods.join('\n\n'); } catch (error) { console.log(` ❌ 解析PHP控制器失败: ${error.message}`); return ' // 解析PHP控制器失败'; } } /** * 提取方法描述 */ extractDescription(comment) { const descMatch = comment.match(/@description\s+(.+)/); return descMatch ? descMatch[1].trim() : ''; } /** * 确定HTTP方法 */ determineHttpMethod(methodName) { if (methodName.startsWith('get') || methodName.startsWith('list') || methodName.startsWith('info')) { return 'Get'; } else if (methodName.startsWith('add') || methodName.startsWith('create') || methodName.startsWith('set')) { return 'Post'; } else if (methodName.startsWith('edit') || methodName.startsWith('update') || methodName.startsWith('modify')) { return 'Put'; } else if (methodName.startsWith('del') || methodName.startsWith('delete') || methodName.startsWith('remove')) { return 'Delete'; } else { return 'Post'; // 默认使用Post } } /** * 生成NestJS控制器方法 */ generateNestJSControllerMethod(methodName, description, httpMethod, phpMethod, moduleName, controllerName, layer, routeInfo = null) { // 使用路由信息中的路径,如果没有则使用kebab-case方法名 const routePath = routeInfo ? routeInfo.routePath : this.toKebabCase(methodName); // 解析路径参数 (如 :id, :uid) const pathParams = routePath.match(/:(\w+)/g) || []; const hasPathParams = pathParams.length > 0; const serviceInstanceName = this.getServiceInstanceName(phpMethod, controllerName); // 解析PHP方法中实际调用的服务方法和参数 const { serviceMethodName, params, isDirectReturn } = this.parsePHPMethodCall(phpMethod); console.log(` 🔍 parsePHPMethodCall结果: serviceMethodName="${serviceMethodName}", params=${JSON.stringify(params)}, isDirectReturn=${isDirectReturn}`); // 生成参数列表(包含路径参数和请求体) const paramsList = []; // 1. 添加路径参数 if (hasPathParams) { pathParams.forEach(param => { const paramName = param.substring(1); // 移除 : 前缀 paramsList.push(`@Param('${paramName}') ${paramName}: string`); }); } // 2. 添加查询参数(如果有) if (params.length > 0) { params.forEach(p => { // 避免重复(如果参数名已经在路径参数中) const paramName = p.name; if (!pathParams.some(pp => pp.substring(1) === paramName)) { if (p.defaultValue) { paramsList.push(`@Query('${paramName}') ${paramName}: ${p.type || 'any'} = ${p.defaultValue}`); } else { paramsList.push(`@Query('${paramName}') ${paramName}: ${p.type || 'any'}`); } } }); } // 3. 添加请求体参数(对于POST/PUT请求) if (httpMethod === 'Post' || httpMethod === 'Put') { const dtoName = `${this.toPascalCase(methodName)}Dto`; paramsList.push(`@Body() data: ${dtoName}`); } const methodParams = paramsList.join(', '); // 生成路由信息注释(提前生成,所有return都要用) const routeComment = routeInfo ? ` * 路由: ${routeInfo.httpMethod.toUpperCase()} ${routePath}\n * PHP路由: Route::${routeInfo.httpMethod}('${routePath}', '${moduleName}.${controllerName}/${methodName}')` : ` * 基于PHP控制器方法: ${methodName}`; // 生成守卫装饰器 const guardDecorators = this.generateGuardDecorators(layer); // 生成返回类型 const returnType = this.generateReturnType(serviceMethodName); // 生成调用参数 - 需要根据PHP方法体中的实际调用生成 let callParams = ''; if (serviceMethodName) { // 解析PHP方法体中的服务调用参数 const serviceCallMatch = phpMethod.match(/\)\s*->\s*(\w+)\(([^)]*)\)/); if (serviceCallMatch) { const phpCallParams = serviceCallMatch[2]; if (phpCallParams.trim()) { // 解析PHP调用参数 const paramList = phpCallParams.split(',').map(p => p.trim()); const tsParams = paramList.map(param => { if (param === '$key') return 'key'; if (param === '$data') return 'data'; if (param.startsWith('$')) return param.substring(1); // 处理数组访问,如 $data['search'] -> data.search if (param.includes("['") && param.includes("']")) { const match = param.match(/\$(\w+)\['(\w+)'\]/); if (match) { return `${match[1]}.${match[2]}`; // 返回 data.search } } // 处理PHP数组语法,如 ['id' => $id] -> { id: id } if (param.includes("['") && param.includes("' =>")) { const arrayMatch = param.match(/\[([\s\S]*)\]/); if (arrayMatch) { const arrayContent = arrayMatch[1]; const keyValuePairs = arrayContent.split(',').map(pair => { const kvMatch = pair.match(/['"]([^'"]+)['"]\s*=>\s*\$(\w+)/); if (kvMatch) { return `${kvMatch[1]}: ${kvMatch[2]}`; } return pair.trim(); }); return `{ ${keyValuePairs.join(', ')} }`; } } return param; }); callParams = tsParams.join(', '); } } } // 如果没有解析到调用参数,使用默认参数 if (!callParams) { if (params.length > 0) { callParams = params.map(p => p.name).join(', '); } else { // 如果服务方法需要参数但控制器没有参数,添加默认参数 if (serviceMethodName) { // 根据方法名推断需要的参数 if (serviceMethodName.includes('List') || serviceMethodName.includes('Search')) { // 检查PHP服务方法是否真的需要参数 const phpServicePath = this.getPHPServicePath(moduleName, serviceMethodName); if (phpServicePath && this.checkPHPServiceMethodNeedsParams(phpServicePath, serviceMethodName)) { callParams = 'params'; } } else if (serviceMethodName.includes('Info') || serviceMethodName.includes('Detail')) { callParams = 'id'; } } } } // 如果是直接返回常量(如 keyBlackList 返回字典) console.log(` 🔍 检查isDirectReturn: ${isDirectReturn}, serviceMethodName: "${serviceMethodName}"`); if (isDirectReturn) { console.log(` 🔍 进入直接返回处理逻辑`); // 检查是否是空方法体 if (!serviceMethodName) { console.log(` 🔍 serviceMethodName为空,开始处理直接返回`); // 尝试从PHP方法体中提取字典调用 const dictMatch = phpMethod.match(/(\w+)::(\w+)\([^)]*\)/); if (dictMatch) { const dictName = dictMatch[1]; const dictMethod = dictMatch[2]; return ` /** * ${description || methodName} ${routeComment} */ @${httpMethod}('${routePath}') @ApiOperation({ summary: '${description || methodName}' }) async ${methodName}(${methodParams}) { try { // 基于PHP真实逻辑实现 // PHP原始方法: ${methodName} // 直接返回字典数据 return ${dictName}.${dictMethod}(); } catch (error) { throw new BusinessException('${methodName}操作失败', error); } }`; } // 尝试从PHP方法体中提取 return success(...) 调用 const bodyMatch = phpMethod.match(/\{([\s\S]*)\}/); const methodBody = bodyMatch ? bodyMatch[1] : ''; const successMatch = methodBody.match(/return\s+success\((.+)\);?\s*$/); console.log(` 🔍 检查return success匹配: ${methodBody.substring(0, 200)}...`); console.log(` 🔍 successMatch结果: ${successMatch ? '匹配成功' : '匹配失败'}`); if (successMatch) { console.log(` 🔍 匹配到的success数据: ${successMatch[1]}`); const successData = successMatch[1]; console.log(` 🔍 原始success数据: ${successData}`); // 转换PHP语法到TypeScript let convertedData = successData; console.log(` 🔍 步骤1 - 原始数据: ${convertedData}`); convertedData = convertedData.replace(/\[([^\]]+)\]/g, '{$1}'); // PHP数组 -> TypeScript对象 console.log(` 🔍 步骤2 - 数组转换: ${convertedData}`); convertedData = convertedData.replace(/'([^']+)'\s*=>/g, '$1:'); // PHP关联数组 -> TypeScript对象属性 console.log(` 🔍 步骤3 - 关联数组转换: ${convertedData}`); console.log(` 🔍 步骤4前 - 检查env匹配: ${convertedData}`); const envMatches = convertedData.match(/env\(([^)]+)\)/g); console.log(` 🔍 步骤4前 - env匹配结果: ${envMatches ? envMatches.join(', ') : '无匹配'}`); convertedData = convertedData.replace(/env\(([^)]+)\)/g, (match, p1) => { console.log(` 🔍 步骤4中 - 匹配到env: ${match}, 参数: ${p1}`); // 处理 env('key', default) 格式 const parts = p1.split(','); if (parts.length === 2) { const key = parts[0].trim().replace(/['"]/g, ''); const defaultValue = parts[1].trim(); const result = `process.env.${key} || ${defaultValue}`; console.log(` 🔍 步骤4中 - 转换结果: ${result}`); return result; } else { const key = p1.trim().replace(/['"]/g, ''); const result = `process.env.${key}`; console.log(` 🔍 步骤4中 - 转换结果: ${result}`); return result; } }); // PHP env() -> TypeScript process.env console.log(` 🔍 步骤4 - env转换: ${convertedData}`); convertedData = convertedData.replace(/,\s*}/g, '}'); // 移除尾随逗号 console.log(` 🔍 步骤5 - 移除尾随逗号: ${convertedData}`); convertedData = convertedData.replace(/,\s*]/g, ']'); // 移除尾随逗号 console.log(` 🔍 步骤6 - 移除尾随逗号: ${convertedData}`); convertedData = convertedData.replace(/process\.env\.'([^']+)'/g, 'process.env.$1'); // 移除process.env的引号 console.log(` 🔍 步骤7 - 移除引号: ${convertedData}`); // convertedData = convertedData.replace(/process\.env\.([^,}\s]+)/g, 'process.env.$1 || false'); // 注释掉,避免重复添加默认值 // console.log(` 🔍 步骤8 - 添加默认值: ${convertedData}`); console.log(` 🔍 转换后的数据: ${convertedData}`); return ` /** * ${description || methodName} ${routeComment} */ @${httpMethod}('${routePath}') @ApiOperation({ summary: '${description || methodName}' }) async ${methodName}(${methodParams}) { try { // 基于PHP真实逻辑实现 // PHP原始方法: ${methodName} // 直接返回数据 return { success: true, data: ${convertedData} }; } catch (error) { throw new BusinessException('${methodName}操作失败', error); } }`; } return ` /** * ${description || methodName} ${routeComment} */ @${httpMethod}('${routePath}') @ApiOperation({ summary: '${description || methodName}' }) async ${methodName}(${methodParams}) { try { // 基于PHP真实逻辑实现 // PHP原始方法: ${methodName} // 空方法体或直接返回,需要根据实际业务逻辑实现 return { message: '${methodName} - 待实现' }; } catch (error) { throw new BusinessException('${methodName}操作失败', error); } }`; } return ` /** * ${description || methodName} ${routeComment} */ @${httpMethod}('${routePath}') @ApiOperation({ summary: '${description || methodName}' }) async ${methodName}(${methodParams}) { try { // 基于PHP真实逻辑实现 // PHP原始方法: ${methodName} // TODO: 实现直接返回逻辑 return { message: '${methodName} - 需要实现直接返回逻辑' }; } catch (error) { throw new BusinessException('${methodName}操作失败', error); } }`; } // 检查是否有服务方法名 console.log(` 🔍 服务方法名: "${serviceMethodName}"`); if (!serviceMethodName) { return ` /** * ${description || methodName} ${routeComment} */ @${httpMethod}('${routePath}') @ApiOperation({ summary: '${description || methodName}' }) async ${methodName}(${methodParams}) { try { // 基于PHP真实逻辑实现 // PHP原始方法: ${methodName} // 服务方法名解析失败,需要手动实现 return { message: '${methodName} - 服务方法名解析失败,需要手动实现' }; } catch (error) { throw new BusinessException('${methodName}操作失败', error); } }`; } return ` /** * ${description || methodName} ${routeComment} */ @${httpMethod}('${routePath}') ${guardDecorators} @ApiOperation({ summary: '${description || methodName}' }) async ${methodName}(${methodParams})${returnType} { try { // 基于PHP真实逻辑实现 // PHP原始方法: ${methodName} ${this.generateDataExtraction(phpMethod)} return await this.${serviceInstanceName}.${serviceMethodName}(${callParams}); } catch (error) { throw new BusinessException('${methodName}操作失败', error); } }`; } /** * 解析PHP方法调用 * 提取服务方法名和参数 */ parsePHPMethodCall(phpMethod) { // 默认值 let serviceMethodName = ''; let params = []; let isDirectReturn = false; try { // 提取方法体 const bodyMatch = phpMethod.match(/\{([\s\S]*)\}/); if (!bodyMatch) { return { serviceMethodName: '', params: [], isDirectReturn: false }; } const methodBody = bodyMatch[1]; console.log(` 🔍 解析PHP方法体: ${methodBody.substring(0, 100)}...`); console.log(` 🔍 方法名: ${phpMethod.match(/function\s+(\w+)/)?.[1] || 'unknown'}`); // 转换PHP语法到TypeScript const convertedMethodBody = methodBody .replace(/::/g, '.') // PHP静态调用 -> TypeScript属性访问 .replace(/\$this->/g, 'this.') // PHP实例调用 -> TypeScript实例调用 .replace(/\)\s*->/g, ').') // PHP方法调用 -> TypeScript方法调用 .replace(/\$(\w+)/g, '$1') // PHP变量 -> TypeScript变量 .replace(/new\s+(\w+Service)\s*\([^)]*\)/g, (match, serviceName) => { // 将ServiceName转换为serviceName (camelCase) const camelCaseName = serviceName.charAt(0).toLowerCase() + serviceName.slice(1).replace('Service', ''); return `this.${camelCaseName}Service`; }) .replace(/new\s+(\w+Service)\s*\)/g, (match, serviceName) => { // 将ServiceName转换为serviceName (camelCase) const camelCaseName = serviceName.charAt(0).toLowerCase() + serviceName.slice(1).replace('Service', ''); return `this.${camelCaseName}Service`; }) // .replace(/success\(/g, 'return ') // 注释掉,避免干扰后续的success匹配 .replace(/error\(/g, 'throw new BusinessException(') // PHP错误 -> TypeScript异常 .replace(/env\(([^)]+)\)/g, 'process.env.$1') // PHP env() -> TypeScript process.env .replace(/process\.env\.'([^']+)'/g, 'process.env.$1') // 移除process.env的引号 .replace(/\[([^\]]+)\]/g, '{$1}') // PHP数组 -> TypeScript对象 .replace(/'([^']+)'\s*=>/g, '$1:') // PHP关联数组 -> TypeScript对象属性 .replace(/,\s*}/g, '}') // 移除尾随逗号 .replace(/,\s*]/g, ']') // 移除尾随逗号 .replace(/->/g, '.'); // 将剩余的 -> 转换为 . console.log(` 🔍 完整convertedMethodBody: ${convertedMethodBody}`); // Added debug log // 检查是否是直接返回常量/字典(如 SiteDict::getStatus()) // 在转换前检查原始方法体中的静态调用 if (methodBody.includes('::') && !methodBody.includes('->') && !methodBody.includes('new ')) { isDirectReturn = true; return { serviceMethodName: '', params: [], isDirectReturn: true }; } // 提取方法名(匹配 .方法名( 或 ).方法名(,优先匹配 new Service().method) // 优先匹配服务调用: new Service() ).method( let serviceCallMatch = convertedMethodBody.match(/\)\s*\.\s*(\w+)\(/); if (serviceCallMatch) { serviceMethodName = serviceCallMatch[1]; console.log(` ✅ 找到服务调用: ${serviceMethodName}`); } else { console.log(` ❌ 未找到服务调用,convertedMethodBody: ${convertedMethodBody.substring(0, 200)}...`); // 如果没有找到,再匹配普通的 .method( serviceCallMatch = convertedMethodBody.match(/\.(\w+)\(/); if (serviceCallMatch) { console.log(` 🔍 找到 ->method 调用: ${serviceCallMatch[1]}`); // 过滤掉 request、params 等非服务方法 const excludedMethods = ['request', 'params', 'param', 'input', 'validate']; if (!excludedMethods.includes(serviceCallMatch[1])) { serviceMethodName = serviceCallMatch[1]; console.log(` ✅ 设置服务方法名: ${serviceMethodName}`); } else { console.log(` ❌ 排除非服务方法: ${serviceCallMatch[1]}`); } } else { console.log(` ❌ 未找到 ->method 调用`); // 处理属性访问: .property (转换为方法调用) // 只匹配真正的属性访问,不匹配方法调用 const propertyMatch = convertedMethodBody.match(/\)\s*\.\s*(\w+)(?!\()/); if (propertyMatch) { const propertyName = propertyMatch[1]; // 检查是否是真正的属性访问(不是服务方法调用) // 服务方法通常以动词开头:get, set, add, edit, del, etc. const serviceMethodPrefixes = ['get', 'set', 'add', 'edit', 'del', 'update', 'create', 'delete', 'find', 'list', 'check', 'verify', 'upload', 'download', 'build', 'clear', 'release']; const isServiceMethod = serviceMethodPrefixes.some(prefix => propertyName.toLowerCase().startsWith(prefix)); if (!isServiceMethod) { // 将属性访问转换为方法调用,如 is_connected -> getIsConnected serviceMethodName = `get${propertyName.charAt(0).toUpperCase()}${propertyName.slice(1)}`; console.log(` ✅ 找到属性访问: ${propertyName} -> ${serviceMethodName}`); } else { console.log(` ❌ 跳过服务方法: ${propertyName}`); } } else { console.log(` ❌ 未找到属性访问,convertedMethodBody: ${convertedMethodBody.substring(0, 200)}...`); } } } console.log(` 🔍 当前serviceMethodName: "${serviceMethodName}"`); // 如果仍然没有找到方法名,检查原始PHP代码中的服务调用 if (!serviceMethodName) { console.log(` 🔍 开始检查原始PHP代码中的服务调用`); // 匹配 (new Service())->method( 模式 const originalServiceMatch = methodBody.match(/\(new\s+(\w+Service)\([^)]*\)\)\s*->\s*(\w+)\(/); if (originalServiceMatch) { serviceMethodName = originalServiceMatch[2]; console.log(` ✅ 找到服务调用: ${originalServiceMatch[1]}.${serviceMethodName}`); } else { console.log(` ❌ originalServiceMatch 不匹配`); // 匹配 new Service()->method( 模式 const directServiceMatch = methodBody.match(/new\s+(\w+Service)\([^)]*\)\s*->\s*(\w+)\(/); if (directServiceMatch) { serviceMethodName = directServiceMatch[2]; console.log(` ✅ 找到服务调用: ${directServiceMatch[1]}.${serviceMethodName}`); } else { console.log(` ❌ directServiceMatch 不匹配`); // 匹配变量服务调用: service.method( 模式 (在convertedMethodBody中$已被移除) const variableServiceMatch = convertedMethodBody.match(/(\w+_service)\s*\.\s*(\w+)\(/); if (variableServiceMatch) { serviceMethodName = variableServiceMatch[2]; console.log(` ✅ 找到变量服务调用: ${variableServiceMatch[1]}.${serviceMethodName}`); } else { console.log(` ❌ 变量服务调用正则不匹配: /(\\w+_service)\\s*\\.\\s*(\\w+)\\(/`); console.log(` 🔍 尝试匹配的内容: ${convertedMethodBody}`); } } } } else { console.log(` ✅ 已找到服务方法名: ${serviceMethodName}`); } // 处理空方法体的情况 if (!serviceMethodName && methodBody.trim() === '') { // 对于空方法体,返回空字符串,让调用方处理 return { serviceMethodName: '', params: [], isDirectReturn: true }; } // 处理直接返回的情况(如 return success(['app_debug' => env('app_debug', false)]);) // 只有当方法体非常简单(只有一行return语句)时才认为是直接返回 if (!serviceMethodName && methodBody.includes('return success(')) { const trimmedBody = methodBody.trim(); // 检查是否只有一行return语句(允许有注释) const lines = trimmedBody.split('\n').filter(line => line.trim() && !line.trim().startsWith('//') && !line.trim().startsWith('/*')); if (lines.length === 1 && lines[0].trim().startsWith('return success(')) { console.log(` ⚠️ 方法 ${phpMethod.match(/function\s+(\w+)/)?.[1]} 被识别为直接返回,但serviceMethodName为空`); isDirectReturn = true; return { serviceMethodName: '', params: [], isDirectReturn: true }; } } // 提取方法参数(从 $data 提取) const dataParamMatch = convertedMethodBody.match(/data\s*=\s*this.request.params\(\[([\s\S]*?)\]\);/); if (dataParamMatch) { const paramsStr = dataParamMatch[1]; const paramMatches = paramsStr.matchAll(/\[\s*'(\w+)'[^\]]*\]/g); for (const paramMatch of paramMatches) { const paramName = paramMatch[1]; // 避免重复参数 if (!params.some(p => p.name === paramName)) { params.push({ name: paramName, type: 'any' }); } } } // 提取路径参数(函数签名中的参数) const funcParamMatch = phpMethod.match(/function\s+\w+\(([\s\S]*?)\)/); if (funcParamMatch && funcParamMatch[1].trim()) { const funcParams = funcParamMatch[1].split(','); for (const funcParam of funcParams) { // 提取参数名,移除类型提示(如 string $key -> key) let paramName = funcParam.trim(); // 移除类型提示(如 "string $key" -> "$key") paramName = paramName.replace(/^\w+\s+/, ''); // 移除 $ 符号 paramName = paramName.replace('$', ''); // 处理默认值(如 "addon = ''" -> "addon: any = ''") if (paramName.includes('=')) { const [name, defaultValue] = paramName.split('=').map(s => s.trim()); if (name && !params.some(p => p.name === name)) { params.push({ name: name, type: 'any', defaultValue: defaultValue }); } } else if (paramName && !params.some(p => p.name === paramName)) { params.push({ name: paramName, type: 'any' }); } } } } catch (error) { console.log(` ⚠️ 解析PHP方法调用失败: ${error.message}`); } return { serviceMethodName, params, isDirectReturn }; } /** * 转换为kebab-case */ toKebabCase(str) { return str.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, ''); } /** * 获取PHP控制器的实际服务依赖 */ getServiceImports(moduleName, controllerName, layer) { const phpProjectPath = path.join(__dirname, '../../niucloud-php/niucloud'); const controllerPath = path.join(phpProjectPath, 'app', layer, 'controller', moduleName, `${this.toPascalCase(controllerName)}.php`); if (!fs.existsSync(controllerPath)) { return this.getDefaultServiceImport(moduleName, controllerName, layer); } try { const content = fs.readFileSync(controllerPath, 'utf8'); const serviceImports = []; // 解析 use 语句中的服务 const useMatches = content.match(/use\s+app\\service\\([^;]+);/g); if (useMatches) { for (const useMatch of useMatches) { const servicePath = useMatch.match(/use\s+app\\service\\([^;]+);/)[1]; const parts = servicePath.split('\\'); const serviceName = parts[parts.length - 1]; const serviceLayer = parts[0]; // 第一层是层级 (admin/api/core) const serviceModule = parts[1]; // 第二层是模块名 // 生成服务类名 - 保留层级区分 let serviceClassName = serviceName; if (serviceLayer === 'admin') { serviceClassName = serviceName; } else if (serviceLayer === 'api') { serviceClassName = serviceName; } else if (serviceLayer === 'core') { // 如果serviceName已经以Core开头,不再重复添加 serviceClassName = serviceName.startsWith('Core') ? serviceName : `Core${serviceName}`; } // 生成导入路径 const importPath = this.getServiceImportPath(serviceModule, serviceLayer, serviceName, moduleName); if (importPath) { serviceImports.push(`import { ${serviceClassName} } from '${importPath}';`); } } } return serviceImports.join('\n'); } catch (error) { console.warn(`⚠️ 解析PHP控制器失败: ${controllerPath}`); return this.getDefaultServiceImport(moduleName, controllerName, layer); } } /** * 获取默认服务导入 - 智能选择服务层 */ getDefaultServiceImport(moduleName, controllerName, layer) { // 基于真实PHP控制器解析服务依赖,智能选择服务层 const baseName = controllerName.replace(/Controller$/, ''); // 智能选择最佳服务层 const bestServiceLayer = this.selectBestServiceLayer(moduleName, layer); if (!bestServiceLayer) { console.log(` ⚠️ 模块 ${moduleName} 没有可用的服务层`); return ''; } let serviceClassName = `${baseName}Service`; if (bestServiceLayer === 'core') { // Core层服务需要Core前缀 serviceClassName = baseName.startsWith('Core') ? `${baseName}Service` : `Core${baseName}Service`; } // 检查服务文件是否存在 const servicePath = path.join(this.config.nestjsBasePath, moduleName, 'services', bestServiceLayer, `${this.toKebabCase(baseName)}.service.ts`); if (!fs.existsSync(servicePath)) { console.log(` ⚠️ 服务文件不存在: ${servicePath}`); return ''; } console.log(` ✅ 选择服务层: ${moduleName} -> ${bestServiceLayer} (${serviceClassName})`); return `import { ${serviceClassName} } from '../../services/${bestServiceLayer}/${this.toKebabCase(baseName)}.service';`; } /** * 获取服务导入路径 */ getServiceImportPath(serviceModule, serviceLayer, serviceName, currentModule) { const baseName = serviceName.replace('Service', ''); // 如果是跨模块服务,需要调整路径 if (serviceModule !== currentModule) { // 跨模块服务直接使用解析出的服务层 const servicePath = path.join(this.config.nestjsBasePath, serviceModule, 'services', serviceLayer, `${this.toKebabCase(baseName)}.service.ts`); if (!fs.existsSync(servicePath)) { return ''; } return `../../../${serviceModule}/services/${serviceLayer}/${this.toKebabCase(baseName)}.service`; } // 同模块服务 - 直接使用解析出的服务层 const servicePath = path.join(this.config.nestjsBasePath, currentModule, 'services', serviceLayer, `${this.toKebabCase(baseName)}.service.ts`); if (!fs.existsSync(servicePath)) { return ''; } return `../../services/${serviceLayer}/${this.toKebabCase(baseName)}.service`; } /** * 获取构造函数中的服务注入 */ getConstructorServices(moduleName, controllerName, layer) { const phpProjectPath = path.join(__dirname, '../../niucloud-php/niucloud'); const controllerPath = path.join(phpProjectPath, 'app', layer, 'controller', moduleName, `${this.toPascalCase(controllerName)}.php`); if (!fs.existsSync(controllerPath)) { return this.getDefaultConstructorService(moduleName, controllerName, layer); } try { const content = fs.readFileSync(controllerPath, 'utf8'); const services = []; // 获取控制器中的所有方法名,避免服务实例名与方法名重复 const methodMatches = content.match(/public\s+function\s+(\w+)\s*\(/g); const methodNames = methodMatches ? methodMatches.map(match => { const methodMatch = match.match(/public\s+function\s+(\w+)\s*\(/); return methodMatch ? methodMatch[1] : null; }).filter(name => name) : []; // 解析 use 语句中的服务 const useMatches = content.match(/use\s+app\\service\\([^;]+);/g); if (useMatches) { for (const useMatch of useMatches) { const servicePath = useMatch.match(/use\s+app\\service\\([^;]+);/)[1]; const parts = servicePath.split('\\'); const serviceName = parts[parts.length - 1]; const serviceLayer = parts[0]; // 第一层是层级 (admin/api/core) const serviceModule = parts[1]; // 第二层是模块名 // 生成服务类名 - 保留层级区分 let serviceClassName = serviceName; if (serviceLayer === 'admin') { serviceClassName = serviceName; } else if (serviceLayer === 'api') { serviceClassName = serviceName; } else if (serviceLayer === 'core') { // 如果serviceName已经以Core开头,不再重复添加 serviceClassName = serviceName.startsWith('Core') ? serviceName : `Core${serviceName}`; } // 检查服务文件是否存在 const serviceImportPath = this.getServiceImportPath(serviceModule, serviceLayer, serviceName, moduleName); if (serviceImportPath) { // 生成服务实例名,与getServiceInstanceName保持一致 const baseInstanceName = this.toCamelCase(serviceName.replace('Service', '')); const serviceInstanceName = `${baseInstanceName}Service`; services.push(`private readonly ${serviceInstanceName}: ${serviceClassName}`); } } } return services.join(',\n '); } catch (error) { console.warn(`⚠️ 解析PHP控制器失败: ${controllerPath}`); return this.getDefaultConstructorService(moduleName, controllerName, layer); } } /** * 获取默认构造函数服务 - 保留层级区分 */ getDefaultConstructorService(moduleName, controllerName, layer) { // 基于真实PHP控制器解析服务依赖,保留层级区分 const baseName = controllerName.replace(/Controller$/, ''); let serviceClassName = `${baseName}Service`; if (layer === 'admin') { serviceClassName = `${baseName}Service`; } else if (layer === 'api') { serviceClassName = `${baseName}Service`; } else if (layer === 'core') { // 如果baseName已经以Core开头,不再重复添加 serviceClassName = baseName.startsWith('Core') ? `${baseName}Service` : `Core${baseName}Service`; } // 智能选择最佳服务层 const bestServiceLayer = this.selectBestServiceLayer(moduleName, layer); if (!bestServiceLayer) { console.log(` ⚠️ 模块 ${moduleName} 没有可用的服务层`); return ''; } // 检查服务文件是否存在 const servicePath = path.join(this.config.nestjsBasePath, moduleName, 'services', bestServiceLayer, `${this.toKebabCase(baseName)}.service.ts`); if (!fs.existsSync(servicePath)) { console.log(` ⚠️ 服务文件不存在: ${servicePath}`); return ''; } // 根据选择的服务层调整类名 if (bestServiceLayer === 'core') { serviceClassName = baseName.startsWith('Core') ? `${baseName}Service` : `Core${baseName}Service`; } console.log(` ✅ 构造函数选择服务层: ${moduleName} -> ${bestServiceLayer} (${serviceClassName})`); const serviceInstanceName = this.toCamelCase(baseName); // 避免服务实例名与方法名重复 const methodNames = ['upgrade', 'login', 'captcha', 'logout', 'execute', 'operate']; const finalInstanceName = methodNames.includes(serviceInstanceName) ? `${serviceInstanceName}Service` : serviceInstanceName; return ` private readonly ${finalInstanceName}: ${serviceClassName}`; } /** * 获取服务实例名 * 根据PHP方法中的实际服务调用选择正确的服务实例 */ getServiceInstanceName(phpMethod, controllerName) { try { // 提取方法体 const bodyMatch = phpMethod.match(/\{([\s\S]*)\}/); if (!bodyMatch) { return this.getDefaultServiceInstanceName(controllerName); } const methodBody = bodyMatch[1]; // 查找 new Service() 调用(支持有参数的情况) const serviceMatch = methodBody.match(/new\s+(\w+Service)\s*\([^)]*\)/); if (serviceMatch) { const serviceName = serviceMatch[1]; // 转换为实例名:LoginService -> loginService,与构造函数注入保持一致 const instanceName = this.toCamelCase(serviceName.replace('Service', '')); return `${instanceName}Service`; } // 如果没有找到,使用默认逻辑 return this.getDefaultServiceInstanceName(controllerName); } catch (error) { return this.getDefaultServiceInstanceName(controllerName); } } /** * 获取默认服务实例名 */ getDefaultServiceInstanceName(controllerName) { const baseName = controllerName.replace(/Controller$/, ''); const serviceInstanceName = this.toCamelCase(baseName); // 这里不需要硬编码方法名,因为会在getConstructorServices中动态获取 return serviceInstanceName; } /** * 获取第一个服务实例名 */ getFirstServiceInstanceName(moduleName, controllerName, layer) { // 解析PHP控制器中实际注入的服务,获取第一个服务的实例名 const phpProjectPath = path.join(__dirname, '../../niucloud-php/niucloud'); const controllerPath = path.join(phpProjectPath, 'app', layer, 'controller', moduleName, `${this.toPascalCase(controllerName)}.php`); if (!fs.existsSync(controllerPath)) { // 默认使用控制器名,避免与方法名重复 return this.getDefaultServiceInstanceName(controllerName); } try { const content = fs.readFileSync(controllerPath, 'utf8'); // 解析 use 语句中的第一个服务 const useMatches = content.match(/use\s+app\\service\\([^;]+);/g); if (useMatches && useMatches.length > 0) { const firstUseMatch = useMatches[0]; const servicePath = firstUseMatch.match(/use\s+app\\service\\([^;]+);/)[1]; const parts = servicePath.split('\\'); const serviceName = parts[parts.length - 1]; // 生成服务实例名:CoreAddonService -> coreAddon return this.toCamelCase(serviceName.replace('Service', '')); } // 如果没有找到服务,使用控制器名,避免与方法名重复 return this.getDefaultServiceInstanceName(controllerName); } catch (error) { // 默认使用控制器名,避免与方法名重复 return this.getDefaultServiceInstanceName(controllerName); } } /** * 获取PHP服务文件路径 */ getPHPServicePath(moduleName, serviceMethodName) { // 这里需要根据实际的服务文件结构来解析 // 暂时返回null,避免复杂的路径解析 return null; } /** * 检查PHP服务方法是否需要参数 */ checkPHPServiceMethodNeedsParams(phpServicePath, serviceMethodName) { // 这里需要解析PHP服务文件来检查方法签名 // 暂时返回false,避免复杂的PHP解析 return false; } /** * 生成数据提取代码 * 解析PHP中的 $this->request->params() 调用 */ generateDataExtraction(phpMethod) { // 转换PHP语法到TypeScript let convertedMethod = phpMethod .replace(/::/g, '.') // PHP静态调用 -> TypeScript属性访问 .replace(/\$this->/g, 'this.') // PHP实例调用 -> TypeScript实例调用 .replace(/\$(\w+)/g, '$1') // PHP变量 -> TypeScript变量 .replace(/new\s+(\w+Service)\([^)]*\)/g, (match, serviceName) => { // 将ServiceName转换为serviceName (camelCase) const camelCaseName = serviceName.charAt(0).toLowerCase() + serviceName.slice(1).replace('Service', ''); return `this.${camelCaseName}`; }) .replace(/success\(/g, 'return ') // PHP成功返回 -> TypeScript返回 .replace(/error\(/g, 'throw new BusinessException(') // PHP错误 -> TypeScript异常 .replace(/\[([^\]]+)\]/g, '{$1}') // PHP数组 -> TypeScript对象 .replace(/'([^']+)'\s*=>/g, '$1:') // PHP关联数组 -> TypeScript对象属性 .replace(/,\s*}/g, '}') // 移除尾随逗号 .replace(/,\s*]/g, ']'); // 移除尾随逗号 // 查找 $this->request->params() 调用 const paramsMatch = convertedMethod.match(/params\(\[([\s\S]*?)\]/); if (paramsMatch) { const paramsContent = paramsMatch[1]; // 解析参数列表 const paramLines = paramsContent.split('\n').map(line => line.trim()).filter(line => line); const dataFields = []; paramLines.forEach(line => { // 匹配 [ 'field', default ] 格式,支持字符串和数字 const fieldMatch = line.match(/\[\s*['"]([^'"]+)['"]\s*,\s*([^'"]*)\s*\]/); if (fieldMatch) { const fieldName = fieldMatch[1]; const defaultValue = fieldMatch[2].trim(); // 避免重复字段 if (!dataFields.some(field => field.name === fieldName)) { dataFields.push({ name: fieldName, default: defaultValue }); } } }); if (dataFields.length > 0) { // 生成数据对象 const dataObject = dataFields.map(field => { if (field.default === '0' || field.default === 'false') { return `${field.name}: ${field.default}`; } else if (field.default === 'true') { return `${field.name}: ${field.default}`; } else if (field.default === '') { return `${field.name}: ''`; } else if (field.default && !isNaN(field.default)) { return `${field.name}: ${field.default}`; } else { return `${field.name}: ${field.default ? `'${field.default}'` : 'undefined'}`; } }).join(', '); return `const data = { ${dataObject} };`; } } // 如果没有找到params调用,但方法中使用了data变量,生成默认的data对象 if (convertedMethod.includes('$data[') || convertedMethod.includes('data[')) { // 尝试从方法体中提取使用的字段 const fieldMatches = [...convertedMethod.matchAll(/data\[([^\]]+)\]/g)]; if (fieldMatches.length > 0) { const fields = fieldMatches.map(match => { const field = match[1].trim().replace(/['"]/g, ''); return field; }); if (fields.length > 0) { const dataObject = fields.map(field => `${field}: ''`).join(', '); return `const data = { ${dataObject} };`; } } return `const data = { search: '' };`; } return ''; } /** * 转换为PascalCase */ toPascalCase(str) { return str.charAt(0).toUpperCase() + str.slice(1); } /** * 转换为camelCase */ toCamelCase(str) { return str.charAt(0).toLowerCase() + str.slice(1); } /** * 转换为kebab-case(我们框架的标准命名格式) */ toKebabCase(str) { return str .replace(/([A-Z])/g, '-$1') .replace(/^-/, '') .toLowerCase(); } /** * 检查模块是否有PHP控制器 */ hasPHPControllers(moduleName, layer) { const phpProjectPath = path.join(__dirname, '../../niucloud-php/niucloud'); const controllerPath = path.join(phpProjectPath, 'app', layer, 'controller', moduleName); if (!fs.existsSync(controllerPath)) return false; // 检查目录内是否有PHP文件 try { const files = fs.readdirSync(controllerPath); return files.some(file => file.endsWith('.php')); } catch (error) { return false; } } /** * 确保目录存在 */ ensureDir(dirPath) { if (!fs.existsSync(dirPath)) { fs.mkdirSync(dirPath, { recursive: true }); } } /** * 输出统计报告 */ printStats() { console.log('\n📊 控制器生成统计报告'); console.log('=================================================='); console.log(`✅ 创建控制器数量: ${this.stats.controllersCreated}`); console.log(`❌ 错误数量: ${this.stats.errors}`); console.log(`📈 成功率: ${this.stats.controllersCreated > 0 ? '100.00%' : '0.00%'}`); } /** * 获取模块可用的服务层 - 基于发现结果 */ getAvailableServiceLayers(moduleName) { if (!this.discoveryData || !this.discoveryData.services) { return []; } const layers = []; const moduleServices = this.discoveryData.services[moduleName]; if (moduleServices) { // 从发现结果中提取所有层级 const layerSet = new Set(); Object.values(moduleServices).forEach(service => { if (service.layer) { layerSet.add(service.layer); } }); layers.push(...layerSet); } return layers; } /** * 智能选择最佳服务层 */ selectBestServiceLayer(moduleName, controllerLayer) { const availableLayers = this.getAvailableServiceLayers(moduleName); // adminapi控制器:优先admin,其次core if (controllerLayer === 'adminapi') { if (availableLayers.includes('admin')) return 'admin'; if (availableLayers.includes('core')) return 'core'; } // api控制器:优先api,其次core if (controllerLayer === 'api') { if (availableLayers.includes('api')) return 'api'; if (availableLayers.includes('core')) return 'core'; } return null; // 没有可用的服务层 } /** * 查找服务文件 - 支持多层级查找 */ findServiceFile(moduleName, serviceName, preferredLayers) { const layers = preferredLayers || ['admin', 'api', 'core']; for (const layer of layers) { const servicePath = path.join(this.config.nestjsBasePath, moduleName, 'services', layer, `${this.toKebabCase(serviceName)}.service.ts`); if (fs.existsSync(servicePath)) { return { layer, path: servicePath }; } } return null; } } // 如果直接运行此文件 if (require.main === module) { const generator = new ControllerGenerator(); generator.run().catch(console.error); } module.exports = ControllerGenerator;