Files
wwjcloud-nest-v1/tools/migration-coordinator.js

1299 lines
40 KiB
JavaScript
Raw Normal View History

#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const ServiceGenerator = require('./generators/service-generator');
const EntityGenerator = require('./generators/entity-generator');
const ModuleGenerator = require('./generators/module-generator');
const ControllerGenerator = require('./generators/controller-generator');
const ValidatorGenerator = require('./generators/validator-generator');
// const MiddlewareGenerator = require('./generators/middleware-generator'); // 已废弃使用Core层Guards+Interceptors+Pipes
const RouteGenerator = require('./generators/route-generator');
const JobGenerator = require('./generators/job-generator');
const ListenerGenerator = require('./generators/listener-generator');
// const CommandGenerator = require('./generators/command-generator'); // 文件不存在,暂时注释
const DictGenerator = require('./generators/dict-generator');
const QualityGate = require('./generators/quality-gate');
const IncrementalUpdater = require('./incremental-updater');
/**
* 🎯 迁移协调器
* 协调所有工具的执行按步骤完成PHP到NestJS的迁移
*/
class MigrationCoordinator {
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',
enableJobs: true,
enableListeners: true,
enableCommands: false,
dryRun: false,
incrementalMode: process.env.INCREMENTAL === 'true' || process.argv.includes('--incremental')
};
this.stats = {
totalSteps: 0,
completedSteps: 0,
failedSteps: 0,
startTime: null,
endTime: null,
errors: 0
};
}
/**
* 🚀 启动完整自动化迁移工具
*/
async run() {
console.log('🚀 启动完整自动化迁移工具...');
if (this.config.incrementalMode) {
console.log('🔄 增量模式:仅处理变更的文件');
return await this.runIncrementalUpdate();
} else {
console.log('🏗️ 完整模式:重新生成所有文件');
return await this.runFullMigration();
}
}
/**
* 🔄 运行增量更新
*/
async runIncrementalUpdate() {
console.log('🔄 启动增量更新模式...\n');
try {
const incrementalUpdater = new IncrementalUpdater();
await incrementalUpdater.run();
console.log('✅ 增量更新完成');
return true;
} catch (error) {
console.error('❌ 增量更新失败:', error.message);
return false;
}
}
/**
* 🏗 运行完整迁移
*/
async runFullMigration() {
console.log('目标完整迁移PHP项目到NestJS包括所有组件\n');
this.stats.startTime = new Date();
try {
// 第1阶段加载PHP文件发现结果
console.log('📊 第1阶段加载PHP文件发现结果...');
await this.loadDiscoveryData();
console.log('✅ 第1阶段完成 - 数据加载成功');
// 第2阶段创建完整模块结构
console.log('📊 第2阶段创建完整模块结构...');
await this.createCompleteModuleStructure();
console.log('✅ 第2阶段完成 - 模块结构创建成功');
// 第3阶段生成实体数据模型层
console.log('📊 第3阶段生成实体...');
await this.generateEntities();
console.log('🔍 验证实体生成结果...');
await this.validateEntities();
console.log('✅ 第3阶段完成 - 实体生成和验证成功');
// 第4阶段生成服务业务逻辑层
console.log('📊 第4阶段生成服务...');
await this.generateServices();
console.log('🔍 验证服务生成结果...');
await this.validateServices();
console.log('✅ 第4阶段完成 - 服务生成和验证成功');
// 第5阶段生成验证器依赖服务
console.log('📊 第5阶段生成验证器...');
await this.generateValidators();
console.log('🔍 验证验证器生成结果...');
await this.validateValidators();
console.log('✅ 第5阶段完成 - 验证器生成和验证成功');
// 第6阶段生成控制器依赖服务和验证器
console.log('📊 第6阶段生成控制器...');
await this.generateControllersWithClassification();
console.log('🔍 验证控制器生成结果...');
await this.validateControllers();
console.log('✅ 第6阶段完成 - 控制器生成和验证成功');
// 第7阶段生成路由依赖控制器
console.log('📊 第7阶段生成路由...');
await this.generateRoutes();
console.log('🔍 验证路由生成结果...');
await this.validateRoutes();
console.log('✅ 第7阶段完成 - 路由生成和验证成功');
// 第8阶段生成任务
if (this.config.enableJobs) {
console.log('📊 第8阶段生成任务...');
await this.generateJobs();
console.log('🔍 验证任务生成结果...');
await this.validateJobs();
console.log('✅ 第8阶段完成 - 任务生成和验证成功');
} else {
console.log('⏭️ 跳过任务生成 (已禁用)');
}
// 第9阶段生成监听器
if (this.config.enableListeners) {
console.log('📊 第9阶段生成监听器...');
await this.generateListeners();
console.log('🔍 验证监听器生成结果...');
await this.validateListeners();
console.log('✅ 第9阶段完成 - 监听器生成和验证成功');
} else {
console.log('⏭️ 跳过监听器生成 (已禁用)');
}
// 第10阶段生成命令
if (this.config.enableCommands) {
console.log('📊 第10阶段生成命令...');
await this.generateCommands();
console.log('🔍 验证命令生成结果...');
await this.validateCommands();
console.log('✅ 第10阶段完成 - 命令生成和验证成功');
} else {
console.log('⏭️ 跳过命令生成 (已禁用)');
}
// 第11阶段生成字典
console.log('📊 第11阶段生成字典...');
await this.generateDicts();
console.log('🔍 验证字典生成结果...');
await this.validateDicts();
console.log('✅ 第11阶段完成 - 字典生成和验证成功');
// 第12阶段生成模块文件依赖所有组件
console.log('📊 第12阶段生成模块文件...');
await this.generateModuleFiles();
console.log('🔍 验证模块文件生成结果...');
await this.validateModuleFiles();
console.log('✅ 第12阶段完成 - 模块文件生成和验证成功');
// 第13阶段最终质量检查
console.log('📊 第13阶段最终质量检查...');
await this.runQualityGate();
console.log('✅ 第13阶段完成 - 质量检查通过');
// 第14阶段生成统计报告
console.log('📊 第14阶段生成统计报告...');
this.generateStatsReport();
console.log('✅ 第14阶段完成 - 统计报告生成成功');
} catch (error) {
console.error('❌ 迁移过程中发生错误:', error.message);
this.stats.errors++;
throw error;
} finally {
this.stats.endTime = new Date();
const duration = this.stats.endTime - this.stats.startTime;
console.log(`\n⏱️ 总耗时: ${(duration / 1000).toFixed(2)}`);
console.log('🎉 完整迁移流程完成!');
}
}
/**
* 加载PHP文件发现结果
*/
async loadDiscoveryData() {
try {
console.log(' 🔍 开始读取发现结果文件:', this.config.discoveryResultPath);
// 检查文件是否存在
if (!fs.existsSync(this.config.discoveryResultPath)) {
throw new Error(`发现结果文件不存在: ${this.config.discoveryResultPath}`);
}
// 获取文件大小
const stats = fs.statSync(this.config.discoveryResultPath);
console.log(` 📏 文件大小: ${(stats.size / 1024 / 1024).toFixed(2)} MB`);
console.log(' 📖 正在读取文件内容...');
const data = fs.readFileSync(this.config.discoveryResultPath, 'utf-8');
console.log(' 🔄 正在解析JSON数据...');
this.discoveryData = JSON.parse(data);
// 输出数据统计
const controllers = Object.keys(this.discoveryData.controllers || {}).length;
const services = Object.keys(this.discoveryData.services || {}).length;
const models = Object.keys(this.discoveryData.models || {}).length;
console.log(` 📊 数据统计: 控制器${controllers}个, 服务${services}个, 模型${models}`);
console.log(' ✅ 成功加载PHP文件发现结果');
} catch (error) {
console.error(' ❌ 加载发现数据失败:', error.message);
throw error;
}
}
/**
* 创建完整模块结构
*/
async createCompleteModuleStructure() {
console.log(' 🏗️ 开始创建模块结构...');
// 获取所有模块
const modules = new Set();
// 从控制器中提取模块
const controllerModules = Object.keys(this.discoveryData.controllers || {});
console.log(` 📁 从控制器提取到 ${controllerModules.length} 个模块:`, controllerModules.slice(0, 5).join(', ') + (controllerModules.length > 5 ? '...' : ''));
for (const [moduleName, controllers] of Object.entries(this.discoveryData.controllers)) {
console.log(` 🔨 创建控制器模块: ${moduleName}`);
modules.add(moduleName);
}
// 从服务中提取模块
const serviceModules = [];
for (const [layerName, services] of Object.entries(this.discoveryData.services)) {
for (const [serviceName, serviceInfo] of Object.entries(services)) {
const moduleName = this.extractModuleNameFromServicePath(serviceInfo.filePath);
if (!modules.has(moduleName)) {
serviceModules.push(moduleName);
console.log(` 🔨 创建服务模块: ${moduleName}`);
}
modules.add(moduleName);
}
}
console.log(` 📁 从服务提取到 ${serviceModules.length} 个新模块:`, serviceModules.slice(0, 5).join(', ') + (serviceModules.length > 5 ? '...' : ''));
// 从模型中提取模块
const modelModules = [];
for (const [moduleName, models] of Object.entries(this.discoveryData.models)) {
if (!modules.has(moduleName)) {
modelModules.push(moduleName);
console.log(` 🔨 创建模型模块: ${moduleName}`);
}
modules.add(moduleName);
}
console.log(` 📁 从模型提取到 ${modelModules.length} 个新模块:`, modelModules.slice(0, 5).join(', ') + (modelModules.length > 5 ? '...' : ''));
// 创建每个模块的目录结构
console.log(` 📂 开始创建 ${modules.size} 个模块的目录结构...`);
let processedCount = 0;
for (const moduleName of modules) {
processedCount++;
console.log(` 📁 [${processedCount}/${modules.size}] 创建模块目录: ${moduleName}`);
await this.createModuleStructure(moduleName);
}
console.log(' ✅ 模块结构创建完成');
}
/**
* 创建模块结构 - 基于PHP实际存在的层级
*/
async createModuleStructure(moduleName) {
const modulePath = path.join(this.config.nestjsBasePath, moduleName);
// 创建模块目录
if (!fs.existsSync(modulePath)) {
fs.mkdirSync(modulePath, { recursive: true });
}
// 检查PHP实际存在的层级只创建对应的目录
const phpLayers = this.getPHPLayersForModule(moduleName);
for (const layer of phpLayers) {
const fullPath = path.join(modulePath, layer);
if (!fs.existsSync(fullPath)) {
fs.mkdirSync(fullPath, { recursive: true });
}
}
}
/**
* 获取模块在PHP中实际存在的层级
*/
getPHPLayersForModule(moduleName) {
const layers = [];
// 检查控制器层级
if (this.hasPHPControllers(moduleName)) {
layers.push('controllers');
if (this.hasPHPAdminControllers(moduleName)) {
layers.push('controllers/adminapi');
}
if (this.hasPHPApiControllers(moduleName)) {
layers.push('controllers/api');
}
}
// 检查服务层级
if (this.hasPHPServices(moduleName)) {
layers.push('services');
if (this.hasPHPAdminServices(moduleName)) {
layers.push('services/admin');
}
if (this.hasPHPApiServices(moduleName)) {
layers.push('services/api');
}
if (this.hasPHPCoreServices(moduleName)) {
layers.push('services/core');
}
}
// 检查实体层级
if (this.hasPHPModels(moduleName)) {
layers.push('entity');
}
// 检查验证器层级
if (this.hasPHPValidators(moduleName)) {
layers.push('dto');
if (this.hasPHPAdminValidators(moduleName)) {
layers.push('dto/admin');
}
if (this.hasPHPApiValidators(moduleName)) {
layers.push('dto/api');
}
}
return layers;
}
/**
* 智能分类判断模块应该迁移到Core层还是跳过
*/
classifyModule(moduleName, phpFilePath) {
const BusinessLogicConverter = require('./generators/business-logic-converter');
const businessLogicConverter = new BusinessLogicConverter();
const className = path.basename(phpFilePath, '.php');
// 读取文件内容用于智能分析
let content = '';
try {
content = fs.readFileSync(phpFilePath, 'utf-8');
} catch (error) {
console.warn(`⚠️ 无法读取文件 ${phpFilePath}: ${error.message}`);
content = '';
}
const classification = businessLogicConverter.classifyFile(phpFilePath, className, content);
return {
moduleName,
classification, // 'CORE_BUSINESS' | 'INFRASTRUCTURE'
shouldMigrate: classification === 'CORE_BUSINESS',
skipReason: classification === 'INFRASTRUCTURE' ? '属于基础设施使用Common层服务' : null
};
}
/**
* 检查模块是否有PHP控制器
*/
hasPHPControllers(moduleName) {
const adminPath = path.join(this.config.phpBasePath, 'app/adminapi/controller', moduleName);
const apiPath = path.join(this.config.phpBasePath, 'app/api/controller', moduleName);
return fs.existsSync(adminPath) || fs.existsSync(apiPath);
}
/**
* 检查模块是否有PHP管理端控制器
*/
hasPHPAdminControllers(moduleName) {
const adminPath = path.join(this.config.phpBasePath, 'app/adminapi/controller', moduleName);
return fs.existsSync(adminPath);
}
/**
* 检查模块是否有PHP前台控制器
*/
hasPHPApiControllers(moduleName) {
const apiPath = path.join(this.config.phpBasePath, 'app/api/controller', moduleName);
return fs.existsSync(apiPath);
}
/**
* 检查模块是否有PHP服务
*/
hasPHPServices(moduleName) {
const adminPath = path.join(this.config.phpBasePath, 'app/service/admin', moduleName);
const apiPath = path.join(this.config.phpBasePath, 'app/service/api', moduleName);
const corePath = path.join(this.config.phpBasePath, 'app/service/core', moduleName);
return fs.existsSync(adminPath) || fs.existsSync(apiPath) || fs.existsSync(corePath);
}
/**
* 检查模块是否有PHP管理端服务
*/
hasPHPAdminServices(moduleName) {
const adminPath = path.join(this.config.phpBasePath, 'app/service/admin', moduleName);
return fs.existsSync(adminPath);
}
/**
* 检查模块是否有PHP前台服务
*/
hasPHPApiServices(moduleName) {
const apiPath = path.join(this.config.phpBasePath, 'app/service/api', moduleName);
return fs.existsSync(apiPath);
}
/**
* 检查模块是否有PHP核心服务
*/
hasPHPCoreServices(moduleName) {
const corePath = path.join(this.config.phpBasePath, 'app/service/core', moduleName);
return fs.existsSync(corePath);
}
/**
* 检查模块是否有PHP模型
*/
hasPHPModels(moduleName) {
const modelPath = path.join(this.config.phpBasePath, 'app/model', moduleName);
return fs.existsSync(modelPath);
}
/**
* 检查模块是否有PHP验证器
*/
hasPHPValidators(moduleName) {
const adminPath = path.join(this.config.phpBasePath, 'app/adminapi/validate', moduleName);
const apiPath = path.join(this.config.phpBasePath, 'app/api/validate', moduleName);
return fs.existsSync(adminPath) || fs.existsSync(apiPath);
}
/**
* 检查模块是否有PHP管理端验证器
*/
hasPHPAdminValidators(moduleName) {
const adminPath = path.join(this.config.phpBasePath, 'app/adminapi/validate', moduleName);
return fs.existsSync(adminPath);
}
/**
* 检查模块是否有PHP前台验证器
*/
hasPHPApiValidators(moduleName) {
const apiPath = path.join(this.config.phpBasePath, 'app/api/validate', moduleName);
return fs.existsSync(apiPath);
}
/**
* 智能分类生成控制器文件
*/
async generateControllersWithClassification() {
console.log('🔍 开始智能分类分析...');
const classificationResults = [];
const modulesToMigrate = [];
// 分析所有模块
for (const moduleName in this.discoveryData.controllers) {
const controllers = this.discoveryData.controllers[moduleName];
for (const controllerName in controllers) {
const controllerInfo = controllers[controllerName];
const classification = this.classifyModule(moduleName, controllerInfo.filePath);
classificationResults.push({
file: controllerInfo.filePath,
module: moduleName,
controller: controllerName,
classification: classification.classification,
shouldMigrate: classification.shouldMigrate,
skipReason: classification.skipReason
});
if (classification.shouldMigrate) {
modulesToMigrate.push(moduleName);
}
}
}
// 生成分类报告
console.log('\n📋 智能分类结果:');
console.log('='.repeat(80));
const businessModules = classificationResults.filter(r => r.classification === 'CORE_BUSINESS');
const infrastructureModules = classificationResults.filter(r => r.classification === 'INFRASTRUCTURE');
console.log(`✅ 业务模块 (需要迁移到Core层): ${businessModules.length}`);
businessModules.forEach(r => console.log(` - ${r.module}/${r.controller}`));
console.log(`⚠️ 基础设施模块 (使用Common层): ${infrastructureModules.length}`);
infrastructureModules.forEach(r => console.log(` - ${r.module}/${r.controller} (${r.skipReason})`));
console.log('\n🚀 开始生成业务模块...');
// 只迁移业务模块
const uniqueModules = [...new Set(modulesToMigrate)];
for (const moduleName of uniqueModules) {
console.log(`📁 生成模块: ${moduleName}`);
await this.generateControllersForModule(moduleName);
}
console.log('✅ 智能分类控制器生成完成!');
return { classificationResults, businessModules, infrastructureModules };
}
/**
* 为指定模块生成控制器
*/
async generateControllersForModule(moduleName) {
if (!this.hasPHPControllers(moduleName)) return;
const controllerGenerator = new ControllerGenerator();
await controllerGenerator.run();
this.stats.generatedControllers++;
}
/**
* 生成控制器
*/
async generateControllers() {
const controllerGenerator = new ControllerGenerator();
await controllerGenerator.run();
}
/**
* 生成服务
*/
async generateServices() {
const serviceGenerator = new ServiceGenerator();
await serviceGenerator.run();
}
/**
* 生成实体
*/
async generateEntities() {
console.log(' 🔨 生成实体文件...');
let processedCount = 0;
for (const [moduleName, models] of Object.entries(this.discoveryData.models)) {
console.log(` 📁 处理模块: ${moduleName}, 模型数量: ${Object.keys(models).length}`);
for (const [modelName, modelInfo] of Object.entries(models)) {
console.log(` 📊 处理模型: ${modelName}`);
try {
await this.createEntity(moduleName, modelName, modelInfo);
processedCount++;
console.log(` ✅ 成功创建实体: ${moduleName}/${modelName}`);
} catch (error) {
console.error(` ❌ 创建实体失败 ${moduleName}/${modelName}:`, error.message);
this.stats.errors++;
}
}
}
console.log(` ✅ 创建了 ${processedCount} 个实体`);
}
/**
* 创建实体
*/
async createEntity(moduleName, modelName, modelInfo) {
const entityPath = path.join(
this.config.nestjsBasePath,
moduleName,
'entity',
`${this.toKebabCase(modelName)}.entity.ts`
);
// 确保目录存在
const entityDir = path.dirname(entityPath);
if (!this.config.dryRun && !fs.existsSync(entityDir)) {
fs.mkdirSync(entityDir, { recursive: true });
}
// 生成实体内容
const entityContent = this.generateEntityContent(moduleName, modelName, modelInfo);
// 写入文件
if (!this.config.dryRun) fs.writeFileSync(entityPath, entityContent);
console.log(` ✅ 创建实体: ${moduleName}/${this.toKebabCase(modelName)}.entity.ts`);
}
/**
* 转换为kebab-case
*/
toKebabCase(str) {
return String(str)
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
.replace(/_/g, '-')
.toLowerCase();
}
/**
* 生成实体内容
*/
generateEntityContent(moduleName, modelName, modelInfo) {
const className = this.toPascalCase(modelName);
return `import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm';
@Entity('${this.toSnakeCase(modelName)}')
export class ${className} {
@PrimaryGeneratedColumn()
id: number;
@Column({ type: 'varchar', length: 255, nullable: true })
name: string;
@Column({ type: 'text', nullable: true })
description: string;
@CreateDateColumn()
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
}
`;
}
/**
* 生成验证器
*/
async generateValidators() {
const validatorGenerator = new ValidatorGenerator();
await validatorGenerator.run();
}
/**
* 生成中间件
*/
async generateMiddlewares() {
// const middlewareGenerator = new MiddlewareGenerator(); // 已废弃使用Core层Guards+Interceptors+Pipes
// await middlewareGenerator.run();
}
/**
* 生成路由
*/
async generateRoutes() {
const routeGenerator = new RouteGenerator();
await routeGenerator.run();
}
/**
* 生成任务
*/
async generateJobs() {
const jobGenerator = new JobGenerator();
await jobGenerator.run();
}
/**
* 生成监听器
*/
async generateListeners() {
const listenerGenerator = new ListenerGenerator();
await listenerGenerator.run();
}
/**
* 生成命令
*/
async generateCommands() {
// const commandGenerator = new CommandGenerator(); // 文件不存在,暂时跳过
// await commandGenerator.run();
console.log(' ⏭️ 跳过命令生成 (文件不存在)');
}
/**
* 生成特征 - 已废弃
* 原因PHP项目只有2个Trait文件NestJS不支持Trait概念
* 应改用 Injectable Service 模式
*/
async generateTraits() {
console.log(' ⏭️ 跳过特征生成 (已废弃:建议使用 Injectable Service)');
}
/**
* 生成字典
*/
async generateDicts() {
const dictGenerator = new DictGenerator();
await dictGenerator.run();
}
/**
* 生成模块文件
*/
async generateModuleFiles() {
console.log(' 🔨 生成模块文件...');
// 获取所有模块
const modules = new Set();
// 从发现数据中提取模块名
if (this.discoveryData && this.discoveryData.modules) {
Object.keys(this.discoveryData.modules).forEach(moduleName => {
modules.add(moduleName);
});
}
// 从控制器中提取模块名
if (this.discoveryData && this.discoveryData.controllers) {
Object.keys(this.discoveryData.controllers).forEach(controllerPath => {
const pathParts = controllerPath.split('/');
if (pathParts.length > 0) {
modules.add(pathParts[0]);
}
});
}
// 从服务中提取模块名
if (this.discoveryData && this.discoveryData.services) {
Object.keys(this.discoveryData.services).forEach(servicePath => {
const pathParts = servicePath.split('/');
if (pathParts.length > 0) {
modules.add(pathParts[0]);
}
});
}
const moduleArray = Array.from(modules);
for (const moduleName of moduleArray) {
await this.createModuleFile(moduleName);
}
console.log(' ✅ 模块文件生成完成');
}
/**
* 创建模块文件
*/
async createModuleFile(moduleName) {
const moduleDir = path.join(this.config.nestjsBasePath, moduleName);
const modulePath = path.join(moduleDir, `${moduleName}.module.ts`);
// 确保目录存在
if (!fs.existsSync(moduleDir)) {
fs.mkdirSync(moduleDir, { recursive: true });
}
const content = this.generateModuleContent(moduleName);
fs.writeFileSync(modulePath, content);
console.log(` ✅ 创建模块: ${moduleName}/${moduleName}.module.ts`);
}
/**
* 生成模块内容
*/
generateModuleContent(moduleName) {
const className = `${this.toPascalCase(moduleName)}Module`;
return `import { Module } from '@nestjs/common';
@Module({
imports: [],
controllers: [],
providers: [],
exports: [],
})
export class ${className} {}
`;
}
/**
* 从服务路径提取模块名
*/
extractModuleNameFromServicePath(filePath) {
const pathParts = filePath.split('/');
const serviceIndex = pathParts.findIndex(part => part === 'service');
if (serviceIndex > 0) {
return pathParts[serviceIndex - 1];
}
const fileName = path.basename(filePath, '.php');
if (fileName.includes('Service')) {
return fileName.replace('Service', '').toLowerCase();
}
return 'unknown';
}
/**
* 转换为驼峰命名
*/
toCamelCase(str) {
return str.replace(/(?:^\w|[A-Z]|\b\w)/g, (word, index) => {
return index === 0 ? word.toLowerCase() : word.toUpperCase();
}).replace(/\s+/g, '');
}
/**
* 转换为帕斯卡命名 - 处理连字符
*/
toPascalCase(str) {
return str.replace(/(^|-)([a-z])/g, (match, p1, p2) => p2.toUpperCase());
}
/**
* 转换为蛇形命名
*/
toSnakeCase(str) {
return str.replace(/([A-Z])/g, '_$1').toLowerCase().replace(/^_/, '');
}
/**
* 运行 Quality Gate 质量检查
*/
async runQualityGate() {
try {
const qualityGate = new QualityGate(path.dirname(this.config.nestjsBasePath));
const passed = await qualityGate.run();
if (passed) {
console.log(' ✅ Quality Gate 通过');
this.stats.completedSteps++;
} else {
console.log(' ❌ Quality Gate 失败');
this.stats.failedSteps++;
// Quality Gate 失败不中断流程,但记录错误
console.log(' ⚠️ 继续执行,但建议修复质量问题');
}
} catch (error) {
console.log(` ⚠️ Quality Gate 检查失败: ${error.message}`);
console.log(' 跳过质量检查,继续执行迁移流程');
this.stats.failedSteps++;
}
}
/**
* 生成统计报告
*/
generateStatsReport() {
console.log('\n📊 完整迁移统计报告');
console.log('='.repeat(50));
console.log(`✅ 完成步骤: ${this.stats.completedSteps}`);
console.log(`❌ 失败步骤: ${this.stats.failedSteps}`);
console.log(`📈 成功率: ${this.stats.completedSteps > 0 ? ((this.stats.completedSteps - this.stats.failedSteps) / this.stats.completedSteps * 100).toFixed(2) : 0}%`);
console.log(`⏱️ 总耗时: ${this.stats.endTime ? ((this.stats.endTime - this.stats.startTime) / 1000).toFixed(2) : 0}`);
}
/**
* 验证实体生成结果
*/
async validateEntities() {
try {
const entityFiles = this.findFilesByPattern('**/*.entity.ts');
console.log(` 📊 验证 ${entityFiles.length} 个实体文件...`);
let validCount = 0;
let errorCount = 0;
for (const file of entityFiles) {
try {
const content = fs.readFileSync(file, 'utf8');
if (content.includes('@Entity') && content.includes('export class')) {
validCount++;
} else {
console.log(` ⚠️ 实体文件格式异常: ${file}`);
errorCount++;
}
} catch (error) {
console.log(` ❌ 实体文件读取失败: ${file} - ${error.message}`);
errorCount++;
}
}
console.log(` ✅ 实体验证完成: ${validCount}个有效, ${errorCount}个错误`);
return errorCount === 0;
} catch (error) {
console.log(` ❌ 实体验证失败: ${error.message}`);
return false;
}
}
/**
* 验证服务生成结果
*/
async validateServices() {
try {
const serviceFiles = this.findFilesByPattern('**/*.service.ts');
console.log(` 📊 验证 ${serviceFiles.length} 个服务文件...`);
let validCount = 0;
let errorCount = 0;
for (const file of serviceFiles) {
try {
const content = fs.readFileSync(file, 'utf8');
if (content.includes('@Injectable') && content.includes('export class')) {
// 检查是否有语法错误
if (content.includes(']]') || content.includes('BusinessBusinessException')) {
console.log(` ⚠️ 服务文件有语法错误: ${file}`);
errorCount++;
} else {
validCount++;
}
} else {
console.log(` ⚠️ 服务文件格式异常: ${file}`);
errorCount++;
}
} catch (error) {
console.log(` ❌ 服务文件读取失败: ${file} - ${error.message}`);
errorCount++;
}
}
console.log(` ✅ 服务验证完成: ${validCount}个有效, ${errorCount}个错误`);
return errorCount === 0;
} catch (error) {
console.log(` ❌ 服务验证失败: ${error.message}`);
return false;
}
}
/**
* 验证验证器生成结果
*/
async validateValidators() {
try {
const validatorFiles = this.findFilesByPattern('**/*.validator.ts');
console.log(` 📊 验证 ${validatorFiles.length} 个验证器文件...`);
let validCount = 0;
let errorCount = 0;
for (const file of validatorFiles) {
try {
const content = fs.readFileSync(file, 'utf8');
if (content.includes('export class') && content.includes('validate')) {
validCount++;
} else {
console.log(` ⚠️ 验证器文件格式异常: ${file}`);
errorCount++;
}
} catch (error) {
console.log(` ❌ 验证器文件读取失败: ${file} - ${error.message}`);
errorCount++;
}
}
console.log(` ✅ 验证器验证完成: ${validCount}个有效, ${errorCount}个错误`);
return errorCount === 0;
} catch (error) {
console.log(` ❌ 验证器验证失败: ${error.message}`);
return false;
}
}
/**
* 验证控制器生成结果
*/
async validateControllers() {
try {
const controllerFiles = this.findFilesByPattern('**/*.controller.ts');
console.log(` 📊 验证 ${controllerFiles.length} 个控制器文件...`);
let validCount = 0;
let errorCount = 0;
for (const file of controllerFiles) {
try {
const content = fs.readFileSync(file, 'utf8');
if (content.includes('@Controller') && content.includes('export class')) {
validCount++;
} else {
console.log(` ⚠️ 控制器文件格式异常: ${file}`);
errorCount++;
}
} catch (error) {
console.log(` ❌ 控制器文件读取失败: ${file} - ${error.message}`);
errorCount++;
}
}
console.log(` ✅ 控制器验证完成: ${validCount}个有效, ${errorCount}个错误`);
return errorCount === 0;
} catch (error) {
console.log(` ❌ 控制器验证失败: ${error.message}`);
return false;
}
}
/**
* 验证路由生成结果
*/
async validateRoutes() {
try {
const routeFiles = this.findFilesByPattern('**/*.routes.ts');
console.log(` 📊 验证 ${routeFiles.length} 个路由文件...`);
let validCount = 0;
let errorCount = 0;
for (const file of routeFiles) {
try {
const content = fs.readFileSync(file, 'utf8');
if (content.includes('export') && content.includes('Routes')) {
validCount++;
} else {
console.log(` ⚠️ 路由文件格式异常: ${file}`);
errorCount++;
}
} catch (error) {
console.log(` ❌ 路由文件读取失败: ${file} - ${error.message}`);
errorCount++;
}
}
console.log(` ✅ 路由验证完成: ${validCount}个有效, ${errorCount}个错误`);
return errorCount === 0;
} catch (error) {
console.log(` ❌ 路由验证失败: ${error.message}`);
return false;
}
}
/**
* 验证任务生成结果
*/
async validateJobs() {
try {
const jobFiles = this.findFilesByPattern('**/*.job.ts');
console.log(` 📊 验证 ${jobFiles.length} 个任务文件...`);
let validCount = 0;
let errorCount = 0;
for (const file of jobFiles) {
try {
const content = fs.readFileSync(file, 'utf8');
if (content.includes('@Processor') && content.includes('export class')) {
validCount++;
} else {
console.log(` ⚠️ 任务文件格式异常: ${file}`);
errorCount++;
}
} catch (error) {
console.log(` ❌ 任务文件读取失败: ${file} - ${error.message}`);
errorCount++;
}
}
console.log(` ✅ 任务验证完成: ${validCount}个有效, ${errorCount}个错误`);
return errorCount === 0;
} catch (error) {
console.log(` ❌ 任务验证失败: ${error.message}`);
return false;
}
}
/**
* 验证监听器生成结果
*/
async validateListeners() {
try {
const listenerFiles = this.findFilesByPattern('**/*.listener.ts');
console.log(` 📊 验证 ${listenerFiles.length} 个监听器文件...`);
let validCount = 0;
let errorCount = 0;
for (const file of listenerFiles) {
try {
const content = fs.readFileSync(file, 'utf8');
if (content.includes('@OnEvent') && content.includes('export class')) {
validCount++;
} else {
console.log(` ⚠️ 监听器文件格式异常: ${file}`);
errorCount++;
}
} catch (error) {
console.log(` ❌ 监听器文件读取失败: ${file} - ${error.message}`);
errorCount++;
}
}
console.log(` ✅ 监听器验证完成: ${validCount}个有效, ${errorCount}个错误`);
return errorCount === 0;
} catch (error) {
console.log(` ❌ 监听器验证失败: ${error.message}`);
return false;
}
}
/**
* 验证命令生成结果
*/
async validateCommands() {
try {
const commandFiles = this.findFilesByPattern('**/*.command.ts');
console.log(` 📊 验证 ${commandFiles.length} 个命令文件...`);
let validCount = 0;
let errorCount = 0;
for (const file of commandFiles) {
try {
const content = fs.readFileSync(file, 'utf8');
if (content.includes('@Command') && content.includes('export class')) {
validCount++;
} else {
console.log(` ⚠️ 命令文件格式异常: ${file}`);
errorCount++;
}
} catch (error) {
console.log(` ❌ 命令文件读取失败: ${file} - ${error.message}`);
errorCount++;
}
}
console.log(` ✅ 命令验证完成: ${validCount}个有效, ${errorCount}个错误`);
return errorCount === 0;
} catch (error) {
console.log(` ❌ 命令验证失败: ${error.message}`);
return false;
}
}
/**
* 验证字典生成结果
*/
async validateDicts() {
try {
const dictFiles = this.findFilesByPattern('**/*.enum.ts');
console.log(` 📊 验证 ${dictFiles.length} 个字典文件...`);
let validCount = 0;
let errorCount = 0;
for (const file of dictFiles) {
try {
const content = fs.readFileSync(file, 'utf8');
if (content.includes('export enum') || content.includes('export const')) {
validCount++;
} else {
console.log(` ⚠️ 字典文件格式异常: ${file}`);
errorCount++;
}
} catch (error) {
console.log(` ❌ 字典文件读取失败: ${file} - ${error.message}`);
errorCount++;
}
}
console.log(` ✅ 字典验证完成: ${validCount}个有效, ${errorCount}个错误`);
return errorCount === 0;
} catch (error) {
console.log(` ❌ 字典验证失败: ${error.message}`);
return false;
}
}
/**
* 验证模块文件生成结果
*/
async validateModuleFiles() {
try {
const moduleFiles = this.findFilesByPattern('**/*.module.ts');
console.log(` 📊 验证 ${moduleFiles.length} 个模块文件...`);
let validCount = 0;
let errorCount = 0;
for (const file of moduleFiles) {
try {
const content = fs.readFileSync(file, 'utf8');
if (content.includes('@Module') && content.includes('export class')) {
validCount++;
} else {
console.log(` ⚠️ 模块文件格式异常: ${file}`);
errorCount++;
}
} catch (error) {
console.log(` ❌ 模块文件读取失败: ${file} - ${error.message}`);
errorCount++;
}
}
console.log(` ✅ 模块文件验证完成: ${validCount}个有效, ${errorCount}个错误`);
return errorCount === 0;
} catch (error) {
console.log(` ❌ 模块文件验证失败: ${error.message}`);
return false;
}
}
/**
* 查找匹配模式的文件
*/
findFilesByPattern(pattern) {
try {
const glob = require('glob');
const searchPath = path.join(this.config.nestjsBasePath, pattern);
return glob.sync(searchPath);
} catch (error) {
// 如果glob模块不存在使用简单的文件系统搜索
console.log(` ⚠️ glob模块不可用使用简单文件搜索: ${error.message}`);
return this.findFilesByPatternSimple(pattern);
}
}
/**
* 简单的文件搜索当glob不可用时
*/
findFilesByPatternSimple(pattern) {
const files = [];
const searchDir = this.config.nestjsBasePath;
if (!fs.existsSync(searchDir)) {
return files;
}
const walkDir = (dir) => {
const items = fs.readdirSync(dir);
for (const item of items) {
const fullPath = path.join(dir, item);
const stat = fs.statSync(fullPath);
if (stat.isDirectory()) {
walkDir(fullPath);
} else if (stat.isFile() && item.endsWith('.ts')) {
const relativePath = path.relative(this.config.nestjsBasePath, fullPath);
if (this.matchesPattern(relativePath, pattern)) {
files.push(fullPath);
}
}
}
};
walkDir(searchDir);
return files;
}
/**
* 简单的模式匹配
*/
matchesPattern(filePath, pattern) {
// 将glob模式转换为简单的字符串匹配
const simplePattern = pattern
.replace(/\*\*/g, '')
.replace(/\*/g, '')
.replace(/\.ts$/, '');
return filePath.includes(simplePattern);
}
}
// 如果直接运行此文件
if (require.main === module) {
const coordinator = new MigrationCoordinator();
coordinator.run().catch(console.error);
}
module.exports = MigrationCoordinator;