- ✅ 成功运行迁移工具,生成28个模块的完整NestJS代码 - ✅ 生成所有实体、服务、控制器、验证器等组件 - ✅ 修复npm依赖冲突,更新package-lock.json - ✅ 添加Docker测试脚本和配置文件 - ✅ 完善迁移工具的调试日志和错误处理 - 🔧 包含增量更新工具和质量检查工具 - 📊 迁移统计:28个模块,数千个文件,耗时26.47秒 主要变更: - wwjcloud-nest/src/core/* - 生成的业务模块代码 - tools/* - 迁移工具和辅助脚本 - wwjcloud-nest/package.json - 依赖更新 - docker/* - 容器化配置和测试脚本
1299 lines
40 KiB
JavaScript
1299 lines
40 KiB
JavaScript
#!/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;
|