refactor: 移除模板迁移API接口,改为migrations阶段处理
- 删除 template-migration.service.ts 文件 - 移除控制器中的迁移相关API接口 (/migrate, /sync, /admin/migration-report) - 清理文档中的迁移服务相关内容 - 添加 data-source.ts 配置文件用于运行数据库迁移 - 模板迁移功能完全通过数据库migration脚本处理
This commit is contained in:
16
data-source.ts
Normal file
16
data-source.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { DataSource } from 'typeorm';
|
||||||
|
|
||||||
|
export const AppDataSource = new DataSource({
|
||||||
|
type: 'mysql',
|
||||||
|
host: 'mysql-6bc9094abd49-public.rds.volces.com',
|
||||||
|
port: 53305,
|
||||||
|
username: 'root',
|
||||||
|
password: 'WsJWXfwFx0bq6DE2kmB6',
|
||||||
|
database: 'nano_camera_miniapp',
|
||||||
|
entities: ['src/**/*.entity.ts'],
|
||||||
|
migrations: ['src/migrations/*.ts'],
|
||||||
|
synchronize: false,
|
||||||
|
logging: process.env.NODE_ENV === 'development',
|
||||||
|
charset: 'utf8mb4',
|
||||||
|
timezone: '+08:00',
|
||||||
|
});
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
- **代码层**:抽象类定义执行逻辑和接口规范
|
- **代码层**:抽象类定义执行逻辑和接口规范
|
||||||
- **数据库层**:存储具体模板的配置信息
|
- **数据库层**:存储具体模板的配置信息
|
||||||
- **Entity层**:动态从数据库加载配置,创建模板实例
|
- **Entity层**:动态从数据库加载配置,创建模板实例
|
||||||
|
- **Migration层**:通过数据库迁移脚本初始化模板配置数据
|
||||||
|
|
||||||
## 🏗️ 架构层次分析
|
## 🏗️ 架构层次分析
|
||||||
|
|
||||||
@@ -373,18 +374,8 @@ export class TemplateController {
|
|||||||
@Param('templateId', ParseIntPipe) templateId: number,
|
@Param('templateId', ParseIntPipe) templateId: number,
|
||||||
@Body() { imageUrl }: { imageUrl: string }
|
@Body() { imageUrl }: { imageUrl: string }
|
||||||
) {
|
) {
|
||||||
// 动态创建模板实例并执行
|
// 通过工厂服务创建模板实例并执行
|
||||||
const config = await this.templateFactory.templateRepository.findOne({
|
const template = await this.templateFactory.createTemplate(templateId);
|
||||||
where: { id: templateId }
|
|
||||||
});
|
|
||||||
|
|
||||||
let template;
|
|
||||||
if (config.templateType === TemplateType.IMAGE) {
|
|
||||||
template = await this.templateFactory.createImageTemplate(templateId);
|
|
||||||
} else {
|
|
||||||
template = await this.templateFactory.createVideoTemplate(templateId);
|
|
||||||
}
|
|
||||||
|
|
||||||
return await template.execute(imageUrl);
|
return await template.execute(imageUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,83 +386,16 @@ export class TemplateController {
|
|||||||
|
|
||||||
@Get('image')
|
@Get('image')
|
||||||
async getImageTemplates() {
|
async getImageTemplates() {
|
||||||
return this.templateFactory.getTemplatesByType(TemplateType.IMAGE);
|
return this.templateFactory.getImageTemplates();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('video')
|
@Get('video')
|
||||||
async getVideoTemplates() {
|
async getVideoTemplates() {
|
||||||
return this.templateFactory.getTemplatesByType(TemplateType.VIDEO);
|
return this.templateFactory.getVideoTemplates();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. 数据迁移脚本
|
|
||||||
```typescript
|
|
||||||
// 将现有代码中的模板配置迁移到数据库
|
|
||||||
// src/migrations/migrate-templates.ts
|
|
||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
|
||||||
import { Repository } from 'typeorm';
|
|
||||||
import { N8nTemplateEntity, TemplateType } from '../entities/n8n-template.entity';
|
|
||||||
|
|
||||||
// 导入现有模板
|
|
||||||
import { PhotoRestoreTemplate } from '../templates/n8nTemplates/PhotoRestoreTemplate';
|
|
||||||
import { CharacterFigurineTemplate } from '../templates/n8nTemplates/CharacterFigurineTemplate';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class TemplateMigrationService {
|
|
||||||
constructor(
|
|
||||||
@InjectRepository(N8nTemplateEntity)
|
|
||||||
private templateRepository: Repository<N8nTemplateEntity>,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
async migrateExistingTemplates() {
|
|
||||||
// 迁移现有模板配置
|
|
||||||
const templates = [
|
|
||||||
new PhotoRestoreTemplate(),
|
|
||||||
new CharacterFigurineTemplate(),
|
|
||||||
// ... 其他模板
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const template of templates) {
|
|
||||||
const existing = await this.templateRepository.findOne({
|
|
||||||
where: { code: template.code }
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!existing) {
|
|
||||||
const entity = new N8nTemplateEntity();
|
|
||||||
entity.code = template.code;
|
|
||||||
entity.name = template.name;
|
|
||||||
entity.description = template.description;
|
|
||||||
entity.creditCost = template.creditCost;
|
|
||||||
entity.version = template.version;
|
|
||||||
entity.inputExampleUrl = template.input;
|
|
||||||
entity.outputExampleUrl = template.output;
|
|
||||||
entity.tags = template.tags;
|
|
||||||
|
|
||||||
// 判断模板类型
|
|
||||||
if (template instanceof N8nImageGenerateTemplate) {
|
|
||||||
entity.templateType = TemplateType.IMAGE;
|
|
||||||
entity.imageModel = template.imageModel;
|
|
||||||
entity.imagePrompt = template.imagePrompt;
|
|
||||||
} else if (template instanceof N8nVideoGenerateTemplate) {
|
|
||||||
entity.templateType = TemplateType.VIDEO;
|
|
||||||
entity.imageModel = template.imageModel;
|
|
||||||
entity.imagePrompt = template.imagePrompt;
|
|
||||||
entity.videoModel = template.videoModel;
|
|
||||||
entity.videoPrompt = template.videoPrompt;
|
|
||||||
entity.duration = template.duration;
|
|
||||||
entity.aspectRatio = template.aspectRatio;
|
|
||||||
}
|
|
||||||
|
|
||||||
entity.templateClass = template.constructor.name;
|
|
||||||
|
|
||||||
await this.templateRepository.save(entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## ✅ 架构优势
|
## ✅ 架构优势
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import { TemplateManager } from './templates/types';
|
|||||||
import { typeOrmConfig } from './config/database.config';
|
import { typeOrmConfig } from './config/database.config';
|
||||||
import { N8nTemplateEntity } from './entities/n8n-template.entity';
|
import { N8nTemplateEntity } from './entities/n8n-template.entity';
|
||||||
import { N8nTemplateFactoryService } from './services/n8n-template-factory.service';
|
import { N8nTemplateFactoryService } from './services/n8n-template-factory.service';
|
||||||
import { TemplateMigrationService } from './services/template-migration.service';
|
|
||||||
import { TemplateController } from './controllers/template.controller';
|
import { TemplateController } from './controllers/template.controller';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
@@ -19,8 +18,7 @@ import { TemplateController } from './controllers/template.controller';
|
|||||||
TemplateService,
|
TemplateService,
|
||||||
TemplateManager,
|
TemplateManager,
|
||||||
N8nTemplateFactoryService,
|
N8nTemplateFactoryService,
|
||||||
TemplateMigrationService
|
|
||||||
],
|
],
|
||||||
exports: [N8nTemplateFactoryService, TemplateMigrationService]
|
exports: [N8nTemplateFactoryService]
|
||||||
})
|
})
|
||||||
export class AppModule {}
|
export class AppModule {}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import {
|
|||||||
HttpStatus
|
HttpStatus
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { N8nTemplateFactoryService } from '../services/n8n-template-factory.service';
|
import { N8nTemplateFactoryService } from '../services/n8n-template-factory.service';
|
||||||
import { TemplateMigrationService } from '../services/template-migration.service';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 模板控制器
|
* 模板控制器
|
||||||
@@ -19,8 +18,7 @@ import { TemplateMigrationService } from '../services/template-migration.service
|
|||||||
@Controller('templates')
|
@Controller('templates')
|
||||||
export class TemplateController {
|
export class TemplateController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly templateFactory: N8nTemplateFactoryService,
|
private readonly templateFactory: N8nTemplateFactoryService
|
||||||
private readonly migrationService: TemplateMigrationService
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -281,69 +279,4 @@ export class TemplateController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 迁移现有代码模板到数据库
|
|
||||||
* @returns 迁移结果
|
|
||||||
*/
|
|
||||||
@Post('migrate')
|
|
||||||
async migrateTemplates() {
|
|
||||||
try {
|
|
||||||
await this.migrationService.migrateAllExistingTemplates();
|
|
||||||
const report = await this.migrationService.getMigrationReport();
|
|
||||||
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
message: 'Templates migrated successfully',
|
|
||||||
data: report
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
throw new HttpException(
|
|
||||||
error.message || 'Migration failed',
|
|
||||||
HttpStatus.INTERNAL_SERVER_ERROR
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 同步现有模板配置
|
|
||||||
* @returns 同步结果
|
|
||||||
*/
|
|
||||||
@Post('sync')
|
|
||||||
async syncTemplates() {
|
|
||||||
try {
|
|
||||||
await this.migrationService.syncExistingTemplates();
|
|
||||||
const report = await this.migrationService.getMigrationReport();
|
|
||||||
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
message: 'Templates synchronized successfully',
|
|
||||||
data: report
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
throw new HttpException(
|
|
||||||
error.message || 'Synchronization failed',
|
|
||||||
HttpStatus.INTERNAL_SERVER_ERROR
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取迁移状态报告
|
|
||||||
* @returns 迁移状态报告
|
|
||||||
*/
|
|
||||||
@Get('admin/migration-report')
|
|
||||||
async getMigrationReport() {
|
|
||||||
try {
|
|
||||||
const report = await this.migrationService.getMigrationReport();
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
data: report
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
throw new HttpException(
|
|
||||||
error.message || 'Failed to get migration report',
|
|
||||||
HttpStatus.INTERNAL_SERVER_ERROR
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,280 +0,0 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
|
||||||
import { Repository } from 'typeorm';
|
|
||||||
import { N8nTemplateEntity, TemplateType } from '../entities/n8n-template.entity';
|
|
||||||
import { N8nImageGenerateTemplate, N8nVideoGenerateTemplate } from '../templates/n8nTemplate';
|
|
||||||
|
|
||||||
// 导入现有模板
|
|
||||||
import { PhotoRestoreTemplate } from '../templates/n8nTemplates/PhotoRestoreTemplate';
|
|
||||||
import { CharacterFigurineTemplate } from '../templates/n8nTemplates/CharacterFigurineTemplate';
|
|
||||||
import { PetFigurineTemplate } from '../templates/n8nTemplates/PetFigurineTemplate';
|
|
||||||
import { CosplayRealPersonTemplate } from '../templates/n8nTemplates/CosplayRealPersonTemplate';
|
|
||||||
import { GarageOpeningTemplate } from '../templates/n8nTemplates/GarageOpeningTemplate';
|
|
||||||
import { JapaneseMagazineTemplate } from '../templates/n8nTemplates/JapaneseMagazineTemplate';
|
|
||||||
import { NuclearExplosionTemplate } from '../templates/n8nTemplates/NuclearExplosionTemplate';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 模板迁移服务
|
|
||||||
* 负责将现有代码中的模板配置迁移到数据库
|
|
||||||
*/
|
|
||||||
@Injectable()
|
|
||||||
export class TemplateMigrationService {
|
|
||||||
constructor(
|
|
||||||
@InjectRepository(N8nTemplateEntity)
|
|
||||||
private templateRepository: Repository<N8nTemplateEntity>,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 迁移所有现有模板到数据库
|
|
||||||
*/
|
|
||||||
async migrateAllExistingTemplates(): Promise<void> {
|
|
||||||
console.log('开始迁移现有模板到数据库...');
|
|
||||||
|
|
||||||
// 创建模板实例
|
|
||||||
const templates = [
|
|
||||||
new PhotoRestoreTemplate(),
|
|
||||||
new CharacterFigurineTemplate(),
|
|
||||||
new PetFigurineTemplate(),
|
|
||||||
new CosplayRealPersonTemplate(),
|
|
||||||
new GarageOpeningTemplate(),
|
|
||||||
new JapaneseMagazineTemplate(),
|
|
||||||
new NuclearExplosionTemplate(),
|
|
||||||
];
|
|
||||||
|
|
||||||
let migratedCount = 0;
|
|
||||||
let skippedCount = 0;
|
|
||||||
|
|
||||||
for (const template of templates) {
|
|
||||||
try {
|
|
||||||
const result = await this.migrateTemplate(template);
|
|
||||||
if (result) {
|
|
||||||
migratedCount++;
|
|
||||||
console.log(`✅ 成功迁移模板: ${template.code} - ${template.name}`);
|
|
||||||
} else {
|
|
||||||
skippedCount++;
|
|
||||||
console.log(`⏭️ 跳过已存在模板: ${template.code} - ${template.name}`);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`❌ 迁移模板失败: ${template.code}`, error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`\n📊 迁移完成统计:`);
|
|
||||||
console.log(` • 新增模板: ${migratedCount} 个`);
|
|
||||||
console.log(` • 跳过模板: ${skippedCount} 个`);
|
|
||||||
console.log(` • 总模板数: ${templates.length} 个\n`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 迁移单个模板
|
|
||||||
* @param template 模板实例
|
|
||||||
* @returns 是否创建了新记录
|
|
||||||
*/
|
|
||||||
async migrateTemplate(template: any): Promise<boolean> {
|
|
||||||
// 检查模板是否已存在
|
|
||||||
const existing = await this.templateRepository.findOne({
|
|
||||||
where: { code: template.code }
|
|
||||||
});
|
|
||||||
|
|
||||||
if (existing) {
|
|
||||||
return false; // 已存在,跳过
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建新的实体
|
|
||||||
const entity = new N8nTemplateEntity();
|
|
||||||
|
|
||||||
// 基础属性
|
|
||||||
entity.code = template.code;
|
|
||||||
entity.name = template.name;
|
|
||||||
entity.description = template.description;
|
|
||||||
entity.creditCost = template.creditCost;
|
|
||||||
entity.version = template.version;
|
|
||||||
entity.inputExampleUrl = template.input;
|
|
||||||
entity.outputExampleUrl = template.output;
|
|
||||||
entity.tags = template.tags;
|
|
||||||
entity.templateClass = template.constructor.name;
|
|
||||||
|
|
||||||
// 判断模板类型并设置相应属性
|
|
||||||
if (template instanceof N8nImageGenerateTemplate) {
|
|
||||||
entity.templateType = TemplateType.IMAGE;
|
|
||||||
entity.imageModel = template.imageModel;
|
|
||||||
entity.imagePrompt = template.imagePrompt;
|
|
||||||
} else if (template instanceof N8nVideoGenerateTemplate) {
|
|
||||||
entity.templateType = TemplateType.VIDEO;
|
|
||||||
entity.imageModel = template.imageModel;
|
|
||||||
entity.imagePrompt = template.imagePrompt;
|
|
||||||
entity.videoModel = template.videoModel;
|
|
||||||
entity.videoPrompt = template.videoPrompt;
|
|
||||||
entity.duration = template.duration;
|
|
||||||
entity.aspectRatio = template.aspectRatio;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存到数据库
|
|
||||||
await this.templateRepository.save(entity);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 清空所有模板数据(谨慎使用)
|
|
||||||
*/
|
|
||||||
async clearAllTemplates(): Promise<void> {
|
|
||||||
console.log('⚠️ 清空所有模板数据...');
|
|
||||||
await this.templateRepository.clear();
|
|
||||||
console.log('✅ 所有模板数据已清空');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 同步现有模板配置(更新已存在的模板)
|
|
||||||
*/
|
|
||||||
async syncExistingTemplates(): Promise<void> {
|
|
||||||
console.log('开始同步现有模板配置...');
|
|
||||||
|
|
||||||
const templates = [
|
|
||||||
new PhotoRestoreTemplate(),
|
|
||||||
new CharacterFigurineTemplate(),
|
|
||||||
new PetFigurineTemplate(),
|
|
||||||
new CosplayRealPersonTemplate(),
|
|
||||||
new GarageOpeningTemplate(),
|
|
||||||
new JapaneseMagazineTemplate(),
|
|
||||||
new NuclearExplosionTemplate(),
|
|
||||||
];
|
|
||||||
|
|
||||||
let updatedCount = 0;
|
|
||||||
let createdCount = 0;
|
|
||||||
|
|
||||||
for (const template of templates) {
|
|
||||||
try {
|
|
||||||
const existing = await this.templateRepository.findOne({
|
|
||||||
where: { code: template.code }
|
|
||||||
});
|
|
||||||
|
|
||||||
if (existing) {
|
|
||||||
// 更新现有模板
|
|
||||||
await this.updateTemplateFromCode(existing, template);
|
|
||||||
updatedCount++;
|
|
||||||
console.log(`🔄 更新模板: ${template.code} - ${template.name}`);
|
|
||||||
} else {
|
|
||||||
// 创建新模板
|
|
||||||
await this.migrateTemplate(template);
|
|
||||||
createdCount++;
|
|
||||||
console.log(`➕ 创建模板: ${template.code} - ${template.name}`);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`❌ 同步模板失败: ${template.code}`, error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`\n📊 同步完成统计:`);
|
|
||||||
console.log(` • 更新模板: ${updatedCount} 个`);
|
|
||||||
console.log(` • 创建模板: ${createdCount} 个`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 从代码模板更新数据库实体
|
|
||||||
*/
|
|
||||||
private async updateTemplateFromCode(entity: N8nTemplateEntity, template: any): Promise<void> {
|
|
||||||
// 更新基础属性
|
|
||||||
entity.name = template.name;
|
|
||||||
entity.description = template.description;
|
|
||||||
entity.creditCost = template.creditCost;
|
|
||||||
entity.version = template.version;
|
|
||||||
entity.inputExampleUrl = template.input;
|
|
||||||
entity.outputExampleUrl = template.output;
|
|
||||||
entity.tags = template.tags;
|
|
||||||
entity.templateClass = template.constructor.name;
|
|
||||||
|
|
||||||
// 更新类型特定属性
|
|
||||||
if (template instanceof N8nImageGenerateTemplate) {
|
|
||||||
entity.templateType = TemplateType.IMAGE;
|
|
||||||
entity.imageModel = template.imageModel;
|
|
||||||
entity.imagePrompt = template.imagePrompt;
|
|
||||||
} else if (template instanceof N8nVideoGenerateTemplate) {
|
|
||||||
entity.templateType = TemplateType.VIDEO;
|
|
||||||
entity.imageModel = template.imageModel;
|
|
||||||
entity.imagePrompt = template.imagePrompt;
|
|
||||||
entity.videoModel = template.videoModel;
|
|
||||||
entity.videoPrompt = template.videoPrompt;
|
|
||||||
entity.duration = template.duration;
|
|
||||||
entity.aspectRatio = template.aspectRatio;
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.templateRepository.save(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取迁移状态报告
|
|
||||||
*/
|
|
||||||
async getMigrationReport(): Promise<{
|
|
||||||
totalInDatabase: number;
|
|
||||||
totalInCode: number;
|
|
||||||
missingInDatabase: string[];
|
|
||||||
extraInDatabase: string[];
|
|
||||||
}> {
|
|
||||||
// 数据库中的模板
|
|
||||||
const dbTemplates = await this.templateRepository.find({
|
|
||||||
select: ['code', 'name']
|
|
||||||
});
|
|
||||||
|
|
||||||
// 代码中的模板
|
|
||||||
const codeTemplates = [
|
|
||||||
new PhotoRestoreTemplate(),
|
|
||||||
new CharacterFigurineTemplate(),
|
|
||||||
new PetFigurineTemplate(),
|
|
||||||
new CosplayRealPersonTemplate(),
|
|
||||||
new GarageOpeningTemplate(),
|
|
||||||
new JapaneseMagazineTemplate(),
|
|
||||||
new NuclearExplosionTemplate(),
|
|
||||||
];
|
|
||||||
|
|
||||||
const dbCodes = new Set(dbTemplates.map(t => t.code));
|
|
||||||
const codeCodes = new Set(codeTemplates.map(t => t.code));
|
|
||||||
|
|
||||||
// 找出差异
|
|
||||||
const missingInDatabase = codeTemplates
|
|
||||||
.filter(t => !dbCodes.has(t.code))
|
|
||||||
.map(t => `${t.code} (${t.name})`);
|
|
||||||
|
|
||||||
const extraInDatabase = dbTemplates
|
|
||||||
.filter(t => !codeCodes.has(t.code as any))
|
|
||||||
.map(t => `${t.code} (${t.name})`);
|
|
||||||
|
|
||||||
return {
|
|
||||||
totalInDatabase: dbTemplates.length,
|
|
||||||
totalInCode: codeTemplates.length,
|
|
||||||
missingInDatabase,
|
|
||||||
extraInDatabase
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 打印迁移状态报告
|
|
||||||
*/
|
|
||||||
async printMigrationReport(): Promise<void> {
|
|
||||||
const report = await this.getMigrationReport();
|
|
||||||
|
|
||||||
console.log('\n📋 模板迁移状态报告:');
|
|
||||||
console.log('='.repeat(50));
|
|
||||||
console.log(`数据库中的模板数量: ${report.totalInDatabase}`);
|
|
||||||
console.log(`代码中的模板数量: ${report.totalInCode}`);
|
|
||||||
|
|
||||||
if (report.missingInDatabase.length > 0) {
|
|
||||||
console.log('\n❌ 缺失在数据库中的模板:');
|
|
||||||
report.missingInDatabase.forEach(template => {
|
|
||||||
console.log(` • ${template}`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (report.extraInDatabase.length > 0) {
|
|
||||||
console.log('\n⚠️ 数据库中多余的模板:');
|
|
||||||
report.extraInDatabase.forEach(template => {
|
|
||||||
console.log(` • ${template}`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (report.missingInDatabase.length === 0 && report.extraInDatabase.length === 0) {
|
|
||||||
console.log('\n✅ 数据库与代码模板完全同步!');
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('='.repeat(50));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user