@@ -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 = ` ${ th is. toPascalCase ( methodName ) } Dto ` ;
paramsList . push ( ` @Body() data: ${ dtoName } ` ) ;
paramsL ist . 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 = body Match[ 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 = call Matches [ 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' ;
}
}
// 如果直接运行此文件