Files
wwjcloud-nest-v1/tools/generators/controller-generator.js
wanwu b1e16be25d feat: 重构多语言模块,符合NestJS规范
- 重构LanguageUtils为LanguageService,实现ILanguageService接口
- 移除自定义验证管道和装饰器,使用标准NestJS验证
- 集成框架ValidatorService进行业务验证
- 简化目录结构,移除不必要的子目录
- 支持模块化语言包加载(common、user、order等)
- 统一API响应格式(code、msg、data、timestamp)
- 添加ValidationExceptionFilter处理多语言验证错误
- 完善多语言示例和文档
2025-10-06 10:56:59 +08:00

1473 lines
58 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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<ApiResponse>';
}
return ': Promise<any>';
}
/**
* 从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;