feat: 完成PHP到NestJS迁移工具和代码生成
- ✅ 成功运行迁移工具,生成28个模块的完整NestJS代码 - ✅ 生成所有实体、服务、控制器、验证器等组件 - ✅ 修复npm依赖冲突,更新package-lock.json - ✅ 添加Docker测试脚本和配置文件 - ✅ 完善迁移工具的调试日志和错误处理 - 🔧 包含增量更新工具和质量检查工具 - 📊 迁移统计:28个模块,数千个文件,耗时26.47秒 主要变更: - wwjcloud-nest/src/core/* - 生成的业务模块代码 - tools/* - 迁移工具和辅助脚本 - wwjcloud-nest/package.json - 依赖更新 - docker/* - 容器化配置和测试脚本
This commit is contained in:
@@ -132,6 +132,7 @@ class ControllerGenerator {
|
||||
|
||||
const content = this.generateControllerContent(moduleName, controllerName, layer);
|
||||
fs.writeFileSync(controllerPath, content);
|
||||
this.stats.controllersCreated += 1;
|
||||
console.log(` ✅ 创建控制器: ${moduleName}/${layer}/${this.toKebabCase(controllerName)}.controller.ts`);
|
||||
}
|
||||
|
||||
@@ -140,36 +141,48 @@ class ControllerGenerator {
|
||||
*/
|
||||
generateControllerContent(moduleName, controllerName, layer) {
|
||||
const className = `${this.toPascalCase(controllerName)}Controller`;
|
||||
|
||||
// 根据PHP控制器的实际服务依赖生成导入
|
||||
|
||||
// 基础设施导入和服务导入
|
||||
const infraImports = this.getInfrastructureImports(layer);
|
||||
const serviceImports = this.getServiceImports(moduleName, controllerName, layer);
|
||||
|
||||
|
||||
// 过滤掉空的导入语句
|
||||
const filteredServiceImports = serviceImports.split('\n').filter(line => {
|
||||
const trimmed = line.trim();
|
||||
return trimmed !== '' && !trimmed.includes("} from '';");
|
||||
return trimmed !== '' && !trimmed.includes("} from '';" );
|
||||
}).join('\n');
|
||||
|
||||
// 根据层类型选择合适的基础设施
|
||||
if (layer === 'adminapi') {
|
||||
imports.push("import { AuthGuard } from '@wwjCommon/infra/auth/auth.guard';");
|
||||
imports.push("import { RbacGuard } from '@wwjCommon/infra/auth/rbac.guard';");
|
||||
imports.push("import { Roles } from '@wwjCommon/infra/auth/decorators';");
|
||||
} else if (layer === 'api') {
|
||||
imports.push("import { AuthGuard } from '@wwjCommon/infra/auth/auth.guard';");
|
||||
}
|
||||
|
||||
// 通用基础设施
|
||||
imports.push("import { Public } from '@wwjCommon/infra/auth/decorators';");
|
||||
imports.push("import { BadRequestException } from '@nestjs/common';");
|
||||
|
||||
// 常用装饰器说明(已在主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');
|
||||
|
||||
const constructorServices = this.getConstructorServices(
|
||||
moduleName,
|
||||
this.toPascalCase(controllerName),
|
||||
this.getCorrectServiceLayer(layer)
|
||||
);
|
||||
const routePrefix = this.getRoutePath(layer);
|
||||
const guardsDecorator = this.generateGuardDecorators(layer);
|
||||
|
||||
// 解析PHP控制器方法生成NestJS方法
|
||||
const methodsContent = this.parsePHPControllerMethods(
|
||||
moduleName,
|
||||
this.toPascalCase(controllerName),
|
||||
layer
|
||||
);
|
||||
|
||||
return `import { Controller, Get, Post, Put, Delete, Body, Param, Query, UseGuards, UploadedFile, UploadedFiles, UseInterceptors } from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
|
||||
${infraImports}
|
||||
${filteredServiceImports}
|
||||
|
||||
@ApiTags('${moduleName}')
|
||||
${guardsDecorator}
|
||||
@Controller('${routePrefix}/${this.toKebabCase(moduleName)}')
|
||||
export class ${className} {
|
||||
constructor(
|
||||
${constructorServices}
|
||||
) {}
|
||||
|
||||
${methodsContent}
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -206,15 +219,15 @@ imports.push("import { Roles } from '@wwjCommon/infra/auth/decorators';");
|
||||
|
||||
// 根据层类型选择合适的基础设施
|
||||
if (layer === 'adminapi') {
|
||||
imports.push("import { AuthGuard } from '@wwjCommon/infra/auth/auth.guard';");
|
||||
imports.push("import { RbacGuard } from '@wwjCommon/infra/auth/rbac.guard';");
|
||||
imports.push("import { Roles } from '@wwjCommon/infra/auth/decorators';");
|
||||
imports.push("import { AuthGuard } from '@wwjCommon/auth/auth.guard';");
|
||||
imports.push("import { RbacGuard } from '@wwjCommon/auth/rbac.guard';");
|
||||
imports.push("import { Roles } from '@wwjCommon/auth/decorators';");
|
||||
} else if (layer === 'api') {
|
||||
imports.push("import { AuthGuard } from '@wwjCommon/infra/auth/auth.guard';");
|
||||
imports.push("import { AuthGuard } from '@wwjCommon/auth/auth.guard';");
|
||||
}
|
||||
|
||||
// 通用基础设施
|
||||
imports.push("import { Public } from '@wwjCommon/infra/auth/decorators';");
|
||||
imports.push("import { Public } from '@wwjCommon/auth/decorators';");
|
||||
imports.push("import { BadRequestException } from '@nestjs/common';");
|
||||
|
||||
// 常用装饰器说明(已在主import中引入)
|
||||
@@ -412,304 +425,67 @@ imports.push("import { Roles } from '@wwjCommon/infra/auth/decorators';");
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成NestJS控制器方法
|
||||
* 生成NestJS控制器方法(严格适配V1,不解析PHP方法体、不做兼容推断)
|
||||
*/
|
||||
generateNestJSControllerMethod(methodName, description, httpMethod, phpMethod, moduleName, controllerName, layer, routeInfo = null) {
|
||||
// 使用路由信息中的路径,如果没有则使用kebab-case方法名
|
||||
// 路由以发现结果为准,缺失则使用方法名的kebab-case
|
||||
const routePath = routeInfo ? routeInfo.routePath : this.toKebabCase(methodName);
|
||||
|
||||
// 解析路径参数 (如 :id, :uid)
|
||||
|
||||
// 路径参数(如 :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}`);
|
||||
|
||||
// 生成参数列表(包含路径参数和请求体)
|
||||
|
||||
// 服务实例名仅由控制器名派生,保持V1注入一致性
|
||||
const serviceInstanceName = this.getServiceInstanceName('', controllerName);
|
||||
|
||||
// 服务方法名与控制器方法名保持一致(严格映射)
|
||||
const serviceMethodName = methodName;
|
||||
|
||||
// 生成参数列表(路径参数 + Query/Body)
|
||||
const paramsList = [];
|
||||
|
||||
// 1. 添加路径参数
|
||||
|
||||
// 1) 路径参数
|
||||
if (hasPathParams) {
|
||||
pathParams.forEach(param => {
|
||||
const paramName = param.substring(1); // 移除 : 前缀
|
||||
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请求)
|
||||
|
||||
// 2) Query 或 Body(按HTTP方法严格区分)
|
||||
if (httpMethod === 'Post' || httpMethod === 'Put') {
|
||||
const dtoName = `${this.toPascalCase(methodName)}Dto`;
|
||||
paramsList.push(`@Body() data: ${dtoName}`);
|
||||
paramsList.push(`@Body() data: any`);
|
||||
} else {
|
||||
paramsList.push(`@Query() query: any`);
|
||||
}
|
||||
|
||||
|
||||
const methodParams = paramsList.join(', ');
|
||||
|
||||
// 生成路由信息注释(提前生成,所有return都要用)
|
||||
const routeComment = routeInfo
|
||||
|
||||
// 路由信息注释
|
||||
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 BadRequestException('${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 BadRequestException('${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 BadRequestException('${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 BadRequestException('${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 BadRequestException('${methodName}操作失败', error);
|
||||
}
|
||||
}`;
|
||||
}
|
||||
|
||||
|
||||
// 生成服务调用参数(严格:路径参数 + query/data)
|
||||
const paramNames = pathParams.map(p => p.substring(1));
|
||||
const callParams = (httpMethod === 'Post' || httpMethod === 'Put')
|
||||
? [...paramNames, 'data'].join(', ')
|
||||
: [...paramNames, 'query'].join(', ');
|
||||
|
||||
return ` /**
|
||||
* ${description || methodName}
|
||||
${routeComment}
|
||||
*/
|
||||
@${httpMethod}('${routePath}')
|
||||
${guardDecorators}
|
||||
@ApiOperation({ summary: '${description || methodName}' })
|
||||
async ${methodName}(${methodParams})${returnType} {
|
||||
@ApiOperation({ summary: ${JSON.stringify(description || methodName)} })
|
||||
${methodName}(${methodParams})${returnType} {
|
||||
try {
|
||||
// 基于PHP真实逻辑实现
|
||||
// PHP原始方法: ${methodName}
|
||||
${this.generateDataExtraction(phpMethod)}
|
||||
return await this.${serviceInstanceName}.${serviceMethodName}(${callParams});
|
||||
return this.${serviceInstanceName}.${serviceMethodName}(${callParams});
|
||||
} catch (error) {
|
||||
throw new BadRequestException('${methodName}操作失败', error);
|
||||
}
|
||||
@@ -721,452 +497,67 @@ ${routeComment}
|
||||
* 提取服务方法名和参数
|
||||
*/
|
||||
parsePHPMethodCall(phpMethod) {
|
||||
// 默认值
|
||||
// 安全默认值,避免 undefined 导致异常
|
||||
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 BadRequestException(') // 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}`);
|
||||
// 判断是否直接 return(非 success 包装)
|
||||
isDirectReturn = /return\s+(?!success\()/i.test(phpMethod);
|
||||
|
||||
// 提取服务调用(支持 new XxxService()->method(...) 或 XxxService::method(...))
|
||||
const callMatches = [...phpMethod.matchAll(/(?:new\s+\w+Service\s*\([^)]*\)\s*->|\w+Service::)\s*(\w+)\s*\(([^)]*)\)/g)];
|
||||
if (callMatches.length > 0) {
|
||||
const lastCall = callMatches[callMatches.length - 1];
|
||||
serviceMethodName = lastCall[1] || '';
|
||||
const rawParams = (lastCall[2] || '').trim();
|
||||
if (rawParams) {
|
||||
const tokens = rawParams.split(',').map(p => p.trim()).filter(Boolean);
|
||||
params = tokens.map(token => {
|
||||
// 支持 $data['search']、$id、'constant' 等形式
|
||||
const arrMatch = token.match(/\$(\w+)\['([^']+)'\]/);
|
||||
if (arrMatch) {
|
||||
return { name: arrMatch[2], type: 'any' };
|
||||
}
|
||||
} 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}`);
|
||||
const varMatch = token.match(/\$(\w+)/);
|
||||
if (varMatch) {
|
||||
return { name: varMatch[1], type: 'any' };
|
||||
}
|
||||
}
|
||||
}
|
||||
} 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 });
|
||||
const strMatch = token.match(/['"]([^'\"]+)['"]/);
|
||||
if (strMatch) {
|
||||
return { name: strMatch[1], type: 'any' };
|
||||
}
|
||||
} else if (paramName && !params.some(p => p.name === paramName)) {
|
||||
params.push({ name: paramName, type: 'any' });
|
||||
}
|
||||
return { name: token, type: 'any' };
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log(` ⚠️ 解析PHP方法调用失败: ${error.message}`);
|
||||
|
||||
// 额外:解析 $this->request->params([['field', default], ...]) 获得查询字段
|
||||
const paramsBlockMatch = phpMethod.match(/params\(\[([\s\S]*?)\]\)/);
|
||||
if (paramsBlockMatch) {
|
||||
const block = paramsBlockMatch[1];
|
||||
const fieldMatches = [...block.matchAll(/\[\s*['"]([^'\"]+)['"]\s*,\s*([^\]]*)\]/g)];
|
||||
if (fieldMatches.length > 0) {
|
||||
const fields = fieldMatches.map(m => {
|
||||
const name = m[1];
|
||||
const defaultRaw = (m[2] || '').trim();
|
||||
const defaultValue = defaultRaw.replace(/['"]/g, '');
|
||||
return { name, type: 'any', defaultValue };
|
||||
});
|
||||
// 合并(避免重复字段)
|
||||
const seen = new Set(params.map(p => p.name));
|
||||
fields.forEach(f => { if (!seen.has(f.name)) params.push(f); });
|
||||
}
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
// 保持安全默认值
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认服务实例名
|
||||
*/
|
||||
@@ -1438,6 +829,46 @@ ${routeComment}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成服务导入语句
|
||||
*/
|
||||
getServiceImports(moduleName, controllerName, layer) {
|
||||
try {
|
||||
const pascal = this.toPascalCase(controllerName);
|
||||
const kebab = this.toKebabCase(controllerName);
|
||||
const serviceLayer = this.getCorrectServiceLayer(layer);
|
||||
if (!serviceLayer) return '';
|
||||
const importPath = `../services/${serviceLayer}/${kebab}.service`;
|
||||
return `import { ${pascal}Service } from '${importPath}';`;
|
||||
} catch (e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成构造函数注入的服务
|
||||
*/
|
||||
getConstructorServices(moduleName, controllerNamePascal, serviceLayer) {
|
||||
const prop = this.toCamelCase(controllerNamePascal) + 'Service';
|
||||
const type = `${controllerNamePascal}Service`;
|
||||
return `private readonly ${prop}: ${type}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取服务实例名(与构造函数属性一致)
|
||||
*/
|
||||
getServiceInstanceName(phpMethod, controllerName) {
|
||||
try {
|
||||
const serviceMatch = phpMethod && phpMethod.match(/new\s+(\w+)Service\s*\(/);
|
||||
if (serviceMatch) {
|
||||
const base = serviceMatch[1];
|
||||
return this.toCamelCase(base) + 'Service';
|
||||
}
|
||||
} catch {}
|
||||
const baseName = (controllerName || '').replace(/Controller$/, '');
|
||||
return this.toCamelCase(baseName) + 'Service';
|
||||
}
|
||||
}
|
||||
|
||||
// 如果直接运行此文件
|
||||
|
||||
Reference in New Issue
Block a user