feat: 实现模板执行记录系统和完整的CRUD API接口
- 新增TemplateExecutionEntity实体替代原有的GenerationTask方式 - 实现模板执行记录的完整生命周期管理,包含状态跟踪和性能指标 - 添加模板管理的完整CRUD操作API(创建、更新、删除、查询) - 更新数据库迁移文件,包含n8n_templates和template_executions表 - 完善模板工厂服务,支持执行记录管理和统计分析 - 重构DynamicN8nImageTemplate和DynamicN8nVideoTemplate类名 - 添加用户相关实体:User、PlatformUser、ExtensionData、UserCredit等 - 实现完整的数据库表结构和索引优化
This commit is contained in:
@@ -198,10 +198,9 @@ import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateCol
|
||||
import { PlatformUser } from './platform-user.entity';
|
||||
import { ExtensionData } from './extension-data.entity';
|
||||
import { UserCredit } from './user-credit.entity';
|
||||
import { GenerationTask } from './generation-task.entity';
|
||||
import { TemplateExecutionEntity } from './template-execution.entity';
|
||||
import { UserSubscription } from './user-subscription.entity';
|
||||
import { AdWatch } from './ad-watch.entity';
|
||||
// import { TemplateExecution } from './template-execution.entity'; // 已移除,使用面向对象模板系统
|
||||
|
||||
@Entity('users')
|
||||
export class User {
|
||||
@@ -241,8 +240,8 @@ export class User {
|
||||
@OneToMany(() => UserCredit, userCredit => userCredit.user)
|
||||
credits: UserCredit[];
|
||||
|
||||
@OneToMany(() => GenerationTask, generationTask => generationTask.user)
|
||||
generationTasks: GenerationTask[];
|
||||
@OneToMany(() => TemplateExecutionEntity, execution => execution.userId)
|
||||
templateExecutions: TemplateExecutionEntity[];
|
||||
|
||||
@OneToMany(() => UserSubscription, userSubscription => userSubscription.user)
|
||||
subscriptions: UserSubscription[];
|
||||
@@ -250,7 +249,7 @@ export class User {
|
||||
@OneToMany(() => AdWatch, adWatch => adWatch.user)
|
||||
adWatches: AdWatch[];
|
||||
|
||||
// 注意: 模板执行记录通过面向对象方式管理,不存储在数据库中
|
||||
// 注意: 现在使用 TemplateExecutionEntity 实体管理模板执行记录
|
||||
}
|
||||
```
|
||||
|
||||
@@ -675,37 +674,51 @@ export class AdWatch {
|
||||
}
|
||||
```
|
||||
|
||||
### 8. AI生成任务与模板系统集成说明
|
||||
### 8. 模板执行记录系统说明
|
||||
|
||||
**重要说明**: 本项目采用面向对象的模板管理系统,不使用数据库存储模板定义和执行记录。
|
||||
**重要说明**: 本项目采用混合式模板管理系统,使用数据库存储模板配置和执行记录。
|
||||
|
||||
#### 8.1 模板系统架构
|
||||
- **模板定义**: 通过TypeScript类定义,编译时类型检查
|
||||
- **模板管理**: 通过TemplateManager内存管理,支持动态注册/卸载
|
||||
- **执行记录**: 通过GenerationTask表记录AI生成任务,模板信息作为metadata存储
|
||||
- **模板定义**: 通过 N8nTemplateEntity 存储模板配置
|
||||
- **模板管理**: 通过 N8nTemplateFactoryService 管理模板实例化
|
||||
- **执行记录**: 通过 TemplateExecutionEntity 表记录AI生成任务的完整过程
|
||||
|
||||
#### 8.2 与GenerationTask的关系
|
||||
#### 8.2 TemplateExecutionEntity 实体结构
|
||||
```typescript
|
||||
// GenerationTask实体中存储模板相关信息
|
||||
@Entity('generation_tasks')
|
||||
export class GenerationTask {
|
||||
// ... 其他字段
|
||||
// 现在使用专门的模板执行记录实体
|
||||
@Entity('template_executions')
|
||||
export class TemplateExecutionEntity {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ type: 'json', nullable: true })
|
||||
parameters: any; // 存储模板执行参数
|
||||
@Column({ name: 'template_id' })
|
||||
templateId: number;
|
||||
|
||||
@Column({ type: 'json', nullable: true })
|
||||
metadata: any; // 存储模板代码、版本等信息
|
||||
@Column({ name: 'user_id' })
|
||||
userId: string;
|
||||
|
||||
@Column({ type: 'enum', enum: PlatformType })
|
||||
platform: PlatformType;
|
||||
|
||||
@Column({ type: 'enum', enum: ExecutionType })
|
||||
type: ExecutionType;
|
||||
|
||||
@Column({ type: 'text' })
|
||||
prompt: string;
|
||||
|
||||
@Column({ name: 'output_url', length: 500, nullable: true })
|
||||
outputUrl: string;
|
||||
|
||||
@Column({ type: 'enum', enum: ExecutionStatus, default: ExecutionStatus.PENDING })
|
||||
status: ExecutionStatus;
|
||||
|
||||
@Column({ name: 'credit_cost', default: 0 })
|
||||
creditCost: number;
|
||||
|
||||
@ManyToOne(() => N8nTemplateEntity)
|
||||
@JoinColumn({ name: 'template_id' })
|
||||
template: N8nTemplateEntity;
|
||||
}
|
||||
|
||||
// 使用示例
|
||||
const task = new GenerationTask();
|
||||
task.metadata = {
|
||||
templateCode: 'outfit_change_v1',
|
||||
templateName: '智能换装',
|
||||
templateVersion: '1.0.0',
|
||||
executionId: 'exec_123456789'
|
||||
};
|
||||
```
|
||||
|
||||
### 4. TypeORM配置
|
||||
@@ -923,8 +936,72 @@ export class CreateInitialTables1703001000000 implements MigrationInterface {
|
||||
)
|
||||
`);
|
||||
|
||||
// 注意: AI模板系统采用面向对象设计,不需要数据库表
|
||||
// 模板定义通过TypeScript类管理,执行记录通过GenerationTask表的metadata字段存储
|
||||
// 创建N8N模板配置表
|
||||
await queryRunner.query(`
|
||||
CREATE TABLE "n8n_templates" (
|
||||
"id" bigint NOT NULL AUTO_INCREMENT,
|
||||
"code" varchar(100) NOT NULL UNIQUE,
|
||||
"name" varchar(200) NOT NULL,
|
||||
"description" text,
|
||||
"creditCost" int NOT NULL DEFAULT 0,
|
||||
"version" varchar(50) NOT NULL,
|
||||
"inputExampleUrl" text,
|
||||
"outputExampleUrl" text,
|
||||
"tags" json,
|
||||
"templateType" enum('image', 'video') NOT NULL,
|
||||
"templateClass" varchar(100) NOT NULL,
|
||||
"imageModel" varchar(100),
|
||||
"imagePrompt" text,
|
||||
"videoModel" varchar(100),
|
||||
"videoPrompt" text,
|
||||
"duration" int,
|
||||
"aspectRatio" varchar(20),
|
||||
"isActive" boolean DEFAULT TRUE,
|
||||
"sortOrder" int DEFAULT 0,
|
||||
"createdAt" TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
CONSTRAINT "PK_n8n_templates_id" PRIMARY KEY ("id")
|
||||
)
|
||||
`);
|
||||
|
||||
// 创建模板执行记录表
|
||||
await queryRunner.query(`
|
||||
CREATE TYPE "execution_status" AS ENUM('pending', 'processing', 'completed', 'failed', 'cancelled')
|
||||
`);
|
||||
|
||||
await queryRunner.query(`
|
||||
CREATE TYPE "execution_type" AS ENUM('image', 'video')
|
||||
`);
|
||||
|
||||
await queryRunner.query(`
|
||||
CREATE TABLE "template_executions" (
|
||||
"id" bigint NOT NULL AUTO_INCREMENT,
|
||||
"templateId" bigint NOT NULL,
|
||||
"userId" uuid NOT NULL,
|
||||
"platform" "platform_type" NOT NULL,
|
||||
"type" "execution_type" NOT NULL,
|
||||
"prompt" text NOT NULL,
|
||||
"inputImageUrl" varchar(500),
|
||||
"outputUrl" varchar(500),
|
||||
"thumbnailUrl" varchar(500),
|
||||
"status" "execution_status" DEFAULT 'pending',
|
||||
"progress" int DEFAULT 0,
|
||||
"errorMessage" text,
|
||||
"executionResult" json,
|
||||
"creditCost" int DEFAULT 0,
|
||||
"creditTransactionId" varchar(100),
|
||||
"inputParams" json,
|
||||
"executionConfig" json,
|
||||
"startedAt" TIMESTAMP NULL,
|
||||
"completedAt" TIMESTAMP NULL,
|
||||
"executionDuration" int,
|
||||
"createdAt" TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
CONSTRAINT "PK_template_executions_id" PRIMARY KEY ("id"),
|
||||
CONSTRAINT "FK_template_executions_templateId" FOREIGN KEY ("templateId") REFERENCES "n8n_templates"("id") ON DELETE CASCADE,
|
||||
CONSTRAINT "FK_template_executions_userId" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE
|
||||
)
|
||||
`);
|
||||
|
||||
// 创建索引
|
||||
await queryRunner.query(`CREATE INDEX "IDX_platform_users_platform" ON "platform_users" ("platform")`);
|
||||
@@ -963,23 +1040,36 @@ export class CreateInitialTables1703001000000 implements MigrationInterface {
|
||||
await queryRunner.query(`CREATE INDEX "IDX_ad_watches_status" ON "ad_watches" ("status")`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_ad_watches_createdAt" ON "ad_watches" ("createdAt")`);
|
||||
|
||||
// 注意: 模板系统索引已移除,使用面向对象管理
|
||||
// N8n模板表索引
|
||||
await queryRunner.query(`CREATE INDEX "IDX_n8n_templates_code" ON "n8n_templates" ("code")`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_n8n_templates_templateType" ON "n8n_templates" ("templateType")`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_n8n_templates_templateClass" ON "n8n_templates" ("templateClass")`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_n8n_templates_isActive" ON "n8n_templates" ("isActive")`);
|
||||
|
||||
// 模板执行记录表索引
|
||||
await queryRunner.query(`CREATE INDEX "IDX_template_executions_templateId" ON "template_executions" ("templateId")`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_template_executions_userId" ON "template_executions" ("userId")`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_template_executions_platform" ON "template_executions" ("platform")`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_template_executions_status" ON "template_executions" ("status")`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_template_executions_type" ON "template_executions" ("type")`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_template_executions_creditTransactionId" ON "template_executions" ("creditTransactionId")`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`DROP TABLE "template_executions"`);
|
||||
await queryRunner.query(`DROP TABLE "n8n_templates"`);
|
||||
await queryRunner.query(`DROP TABLE "ad_watches"`);
|
||||
await queryRunner.query(`DROP TABLE "user_subscriptions"`);
|
||||
await queryRunner.query(`DROP TABLE "generation_tasks"`);
|
||||
await queryRunner.query(`DROP TABLE "user_credits"`);
|
||||
await queryRunner.query(`DROP TABLE "extension_data"`);
|
||||
await queryRunner.query(`DROP TABLE "platform_users"`);
|
||||
await queryRunner.query(`DROP TABLE "users"`);
|
||||
await queryRunner.query(`DROP TYPE "execution_type"`);
|
||||
await queryRunner.query(`DROP TYPE "execution_status"`);
|
||||
await queryRunner.query(`DROP TYPE "ad_status"`);
|
||||
await queryRunner.query(`DROP TYPE "ad_type"`);
|
||||
await queryRunner.query(`DROP TYPE "subscription_status"`);
|
||||
await queryRunner.query(`DROP TYPE "subscription_plan"`);
|
||||
await queryRunner.query(`DROP TYPE "task_status"`);
|
||||
await queryRunner.query(`DROP TYPE "generation_type"`);
|
||||
await queryRunner.query(`DROP TYPE "credit_source"`);
|
||||
await queryRunner.query(`DROP TYPE "credit_type"`);
|
||||
await queryRunner.query(`DROP TYPE "platform_type"`);
|
||||
|
||||
@@ -201,8 +201,8 @@ import { N8nTemplateEntity, TemplateType } from './n8n-template.entity';
|
||||
import { N8nImageGenerateTemplate, N8nVideoGenerateTemplate } from '../templates/n8nTemplate';
|
||||
import axios from 'axios';
|
||||
|
||||
// 动态图片模板实体
|
||||
export class N8nImageGenerateTemplateEntity extends N8nImageGenerateTemplate {
|
||||
// 动态图片模板实现
|
||||
export class DynamicN8nImageTemplate extends N8nImageGenerateTemplate {
|
||||
private config: N8nTemplateEntity;
|
||||
|
||||
constructor(
|
||||
@@ -212,7 +212,7 @@ export class N8nImageGenerateTemplateEntity extends N8nImageGenerateTemplate {
|
||||
super();
|
||||
}
|
||||
|
||||
async onInit(): Promise<N8nImageGenerateTemplateEntity> {
|
||||
async onInit(): Promise<DynamicN8nImageTemplate> {
|
||||
this.config = await this.templateRepository.findOne({
|
||||
where: { id: this.templateId, templateType: TemplateType.IMAGE, isActive: true }
|
||||
});
|
||||
@@ -239,8 +239,8 @@ export class N8nImageGenerateTemplateEntity extends N8nImageGenerateTemplate {
|
||||
// 执行方法继承自N8nImageGenerateTemplate,无需修改
|
||||
}
|
||||
|
||||
// 动态视频模板实体
|
||||
export class N8nVideoGenerateTemplateEntity extends N8nVideoGenerateTemplate {
|
||||
// 动态视频模板实现
|
||||
export class DynamicN8nVideoTemplate extends N8nVideoGenerateTemplate {
|
||||
private config: N8nTemplateEntity;
|
||||
|
||||
constructor(
|
||||
@@ -250,7 +250,7 @@ export class N8nVideoGenerateTemplateEntity extends N8nVideoGenerateTemplate {
|
||||
super();
|
||||
}
|
||||
|
||||
async onInit(): Promise<N8nVideoGenerateTemplateEntity> {
|
||||
async onInit(): Promise<DynamicN8nVideoTemplate> {
|
||||
this.config = await this.templateRepository.findOne({
|
||||
where: { id: this.templateId, templateType: TemplateType.VIDEO, isActive: true }
|
||||
});
|
||||
@@ -282,36 +282,194 @@ export class N8nVideoGenerateTemplateEntity extends N8nVideoGenerateTemplate {
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 模板工厂服务
|
||||
### 3. 模板执行记录表 (template_executions)
|
||||
```sql
|
||||
CREATE TABLE template_executions (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
|
||||
-- 关联字段
|
||||
template_id BIGINT NOT NULL COMMENT '模板ID,关联n8n_templates.id',
|
||||
user_id VARCHAR(100) NOT NULL COMMENT '用户ID,关联users.id',
|
||||
platform ENUM('wechat', 'alipay', 'baidu', 'bytedance', 'jd', 'qq', 'feishu', 'kuaishou', 'h5', 'rn') NOT NULL COMMENT '执行平台',
|
||||
|
||||
-- 执行内容
|
||||
type ENUM('image', 'video') NOT NULL COMMENT '生成类型',
|
||||
prompt TEXT NOT NULL COMMENT '用户输入提示词',
|
||||
input_image_url VARCHAR(500) COMMENT '输入图片URL',
|
||||
output_url VARCHAR(500) COMMENT '生成结果URL',
|
||||
thumbnail_url VARCHAR(500) COMMENT '缩略图URL',
|
||||
|
||||
-- 执行状态和结果
|
||||
status ENUM('pending', 'processing', 'completed', 'failed', 'cancelled') NOT NULL DEFAULT 'pending' COMMENT '执行状态',
|
||||
progress INT DEFAULT 0 COMMENT '执行进度(0-100)',
|
||||
error_message TEXT COMMENT '错误信息',
|
||||
execution_result JSON COMMENT '执行结果详情',
|
||||
|
||||
-- 积分和计费
|
||||
credit_cost INT NOT NULL DEFAULT 0 COMMENT '消耗积分数',
|
||||
credit_transaction_id VARCHAR(100) COMMENT '积分交易ID,关联user_credits.id',
|
||||
|
||||
-- 执行参数和配置
|
||||
input_params JSON COMMENT '输入参数',
|
||||
execution_config JSON COMMENT '执行时的配置快照',
|
||||
|
||||
-- 性能指标
|
||||
started_at TIMESTAMP NULL COMMENT '开始执行时间',
|
||||
completed_at TIMESTAMP NULL COMMENT '完成时间',
|
||||
execution_duration INT COMMENT '执行耗时(秒)',
|
||||
|
||||
-- 系统字段
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
|
||||
KEY idx_template_id (template_id),
|
||||
KEY idx_user_id (user_id),
|
||||
KEY idx_platform (platform),
|
||||
KEY idx_status (status),
|
||||
KEY idx_type (type),
|
||||
KEY idx_created_at (created_at),
|
||||
KEY idx_credit_transaction_id (credit_transaction_id),
|
||||
|
||||
FOREIGN KEY (template_id) REFERENCES n8n_templates(id) ON DELETE CASCADE
|
||||
) COMMENT '模板执行记录表';
|
||||
```
|
||||
|
||||
### 4. 模板执行记录实体
|
||||
```typescript
|
||||
// src/entities/template-execution.entity.ts
|
||||
import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn, ManyToOne, JoinColumn, Index } from 'typeorm';
|
||||
import { N8nTemplateEntity } from './n8n-template.entity';
|
||||
import { PlatformType } from './platform-user.entity';
|
||||
|
||||
export enum ExecutionStatus {
|
||||
PENDING = 'pending',
|
||||
PROCESSING = 'processing',
|
||||
COMPLETED = 'completed',
|
||||
FAILED = 'failed',
|
||||
CANCELLED = 'cancelled'
|
||||
}
|
||||
|
||||
export enum ExecutionType {
|
||||
IMAGE = 'image',
|
||||
VIDEO = 'video'
|
||||
}
|
||||
|
||||
@Entity('template_executions')
|
||||
@Index(['templateId'])
|
||||
@Index(['userId'])
|
||||
@Index(['platform'])
|
||||
@Index(['status'])
|
||||
@Index(['type'])
|
||||
@Index(['creditTransactionId'])
|
||||
export class TemplateExecutionEntity {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ name: 'template_id' })
|
||||
templateId: number;
|
||||
|
||||
@Column({ name: 'user_id' })
|
||||
userId: string;
|
||||
|
||||
@Column({ type: 'enum', enum: PlatformType })
|
||||
platform: PlatformType;
|
||||
|
||||
@Column({ type: 'enum', enum: ExecutionType })
|
||||
type: ExecutionType;
|
||||
|
||||
@Column({ type: 'text' })
|
||||
prompt: string;
|
||||
|
||||
@Column({ name: 'input_image_url', length: 500, nullable: true })
|
||||
inputImageUrl: string;
|
||||
|
||||
@Column({ name: 'output_url', length: 500, nullable: true })
|
||||
outputUrl: string;
|
||||
|
||||
@Column({ name: 'thumbnail_url', length: 500, nullable: true })
|
||||
thumbnailUrl: string;
|
||||
|
||||
@Column({ type: 'enum', enum: ExecutionStatus, default: ExecutionStatus.PENDING })
|
||||
status: ExecutionStatus;
|
||||
|
||||
@Column({ default: 0 })
|
||||
progress: number;
|
||||
|
||||
@Column({ name: 'error_message', type: 'text', nullable: true })
|
||||
errorMessage: string;
|
||||
|
||||
@Column({ name: 'execution_result', type: 'json', nullable: true })
|
||||
executionResult: any;
|
||||
|
||||
// 积分和计费相关
|
||||
@Column({ name: 'credit_cost', default: 0 })
|
||||
creditCost: number;
|
||||
|
||||
@Column({ name: 'credit_transaction_id', length: 100, nullable: true })
|
||||
creditTransactionId: string;
|
||||
|
||||
// 执行参数和配置
|
||||
@Column({ name: 'input_params', type: 'json', nullable: true })
|
||||
inputParams: any;
|
||||
|
||||
@Column({ name: 'execution_config', type: 'json', nullable: true })
|
||||
executionConfig: any;
|
||||
|
||||
// 性能指标
|
||||
@Column({ name: 'started_at', nullable: true })
|
||||
startedAt: Date;
|
||||
|
||||
@Column({ name: 'completed_at', nullable: true })
|
||||
completedAt: Date;
|
||||
|
||||
@Column({ name: 'execution_duration', nullable: true })
|
||||
executionDuration: number;
|
||||
|
||||
@CreateDateColumn({ name: 'created_at' })
|
||||
createdAt: Date;
|
||||
|
||||
@UpdateDateColumn({ name: 'updated_at' })
|
||||
updatedAt: Date;
|
||||
|
||||
@ManyToOne(() => N8nTemplateEntity)
|
||||
@JoinColumn({ name: 'template_id' })
|
||||
template: N8nTemplateEntity;
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 模板工厂服务
|
||||
```typescript
|
||||
// src/services/n8n-template-factory.service.ts
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { N8nTemplateEntity, TemplateType } from '../entities/n8n-template.entity';
|
||||
import { N8nImageGenerateTemplateEntity, N8nVideoGenerateTemplateEntity } from '../entities/n8n-template-instance.entity';
|
||||
import { TemplateExecutionEntity, ExecutionStatus } from '../entities/template-execution.entity';
|
||||
import { DynamicN8nImageTemplate, DynamicN8nVideoTemplate } from '../templates/n8n-dynamic-template';
|
||||
|
||||
@Injectable()
|
||||
export class N8nTemplateFactoryService {
|
||||
constructor(
|
||||
@InjectRepository(N8nTemplateEntity)
|
||||
private templateRepository: Repository<N8nTemplateEntity>,
|
||||
@InjectRepository(TemplateExecutionEntity)
|
||||
private executionRepository: Repository<TemplateExecutionEntity>,
|
||||
) {}
|
||||
|
||||
// 创建图片模板实例
|
||||
async createImageTemplate(templateId: number): Promise<N8nImageGenerateTemplateEntity> {
|
||||
const instance = new N8nImageGenerateTemplateEntity(templateId, this.templateRepository);
|
||||
async createImageTemplate(templateId: number): Promise<DynamicN8nImageTemplate> {
|
||||
const instance = new DynamicN8nImageTemplate(templateId, this.templateRepository);
|
||||
return await instance.onInit();
|
||||
}
|
||||
|
||||
// 创建视频模板实例
|
||||
async createVideoTemplate(templateId: number): Promise<N8nVideoGenerateTemplateEntity> {
|
||||
const instance = new N8nVideoGenerateTemplateEntity(templateId, this.templateRepository);
|
||||
async createVideoTemplate(templateId: number): Promise<DynamicN8nVideoTemplate> {
|
||||
const instance = new DynamicN8nVideoTemplate(templateId, this.templateRepository);
|
||||
return await instance.onInit();
|
||||
}
|
||||
|
||||
// 通过模板代码创建实例
|
||||
async createTemplateByCode(code: string): Promise<N8nImageGenerateTemplateEntity | N8nVideoGenerateTemplateEntity> {
|
||||
async createTemplateByCode(code: string): Promise<DynamicN8nImageTemplate | DynamicN8nVideoTemplate> {
|
||||
const config = await this.templateRepository.findOne({
|
||||
where: { code, isActive: true }
|
||||
});
|
||||
@@ -327,6 +485,42 @@ export class N8nTemplateFactoryService {
|
||||
}
|
||||
}
|
||||
|
||||
// 创建执行记录
|
||||
async createExecution(data: Partial<TemplateExecutionEntity>): Promise<TemplateExecutionEntity> {
|
||||
const execution = this.executionRepository.create(data);
|
||||
return this.executionRepository.save(execution);
|
||||
}
|
||||
|
||||
// 更新执行记录
|
||||
async updateExecution(id: number, data: Partial<TemplateExecutionEntity>): Promise<TemplateExecutionEntity> {
|
||||
await this.executionRepository.update(id, data);
|
||||
return this.executionRepository.findOne({ where: { id } });
|
||||
}
|
||||
|
||||
// 获取用户执行记录
|
||||
async getUserExecutions(userId: string, limit = 20): Promise<TemplateExecutionEntity[]> {
|
||||
return this.executionRepository.find({
|
||||
where: { userId },
|
||||
relations: ['template'],
|
||||
order: { createdAt: 'DESC' },
|
||||
take: limit
|
||||
});
|
||||
}
|
||||
|
||||
// 获取模板执行统计
|
||||
async getTemplateExecutionStats(templateId: number): Promise<any> {
|
||||
const stats = await this.executionRepository
|
||||
.createQueryBuilder('execution')
|
||||
.select('execution.status', 'status')
|
||||
.addSelect('COUNT(*)', 'count')
|
||||
.addSelect('AVG(execution.executionDuration)', 'avgDuration')
|
||||
.where('execution.templateId = :templateId', { templateId })
|
||||
.groupBy('execution.status')
|
||||
.getRawMany();
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
// 获取所有可用模板
|
||||
async getAllTemplates(): Promise<N8nTemplateEntity[]> {
|
||||
return this.templateRepository.find({
|
||||
@@ -347,22 +541,9 @@ export class N8nTemplateFactoryService {
|
||||
|
||||
## 🚀 使用示例
|
||||
|
||||
### 1. 您期望的使用方式
|
||||
### 1. 带执行记录的模板执行
|
||||
```typescript
|
||||
// 通过ID创建并执行视频模板
|
||||
const template = await new N8nVideoGenerateTemplateEntity(templateId, templateRepo)
|
||||
.onInit()
|
||||
.then(e => e.execute(imgUrl));
|
||||
|
||||
// 通过工厂服务创建
|
||||
const factory = new N8nTemplateFactoryService(templateRepo);
|
||||
const template = await factory.createVideoTemplate(1);
|
||||
const result = await template.execute(imageUrl);
|
||||
```
|
||||
|
||||
### 2. 在Controller中的使用
|
||||
```typescript
|
||||
// src/controllers/template.controller.ts
|
||||
// 在Controller中使用,自动记录执行过程
|
||||
@Controller('templates')
|
||||
export class TemplateController {
|
||||
constructor(
|
||||
@@ -372,11 +553,69 @@ export class TemplateController {
|
||||
@Post(':templateId/execute')
|
||||
async executeTemplate(
|
||||
@Param('templateId', ParseIntPipe) templateId: number,
|
||||
@Body() { imageUrl }: { imageUrl: string }
|
||||
@Body() { imageUrl, taskId }: { imageUrl: string, taskId: string },
|
||||
@CurrentUser() user: any
|
||||
) {
|
||||
// 通过工厂服务创建模板实例并执行
|
||||
const template = await this.templateFactory.createTemplate(templateId);
|
||||
return await template.execute(imageUrl);
|
||||
// 1. 创建执行记录
|
||||
const execution = await this.templateFactory.createExecution({
|
||||
taskId,
|
||||
templateId,
|
||||
userId: user.id,
|
||||
status: ExecutionStatus.PENDING,
|
||||
inputParams: { imageUrl },
|
||||
progress: 0
|
||||
});
|
||||
|
||||
try {
|
||||
// 2. 更新状态为处理中
|
||||
await this.templateFactory.updateExecution(execution.id, {
|
||||
status: ExecutionStatus.PROCESSING,
|
||||
startedAt: new Date(),
|
||||
progress: 10
|
||||
});
|
||||
|
||||
// 3. 创建模板实例并执行
|
||||
const template = await this.templateFactory.createTemplateByCode(templateId);
|
||||
|
||||
// 4. 更新进度
|
||||
await this.templateFactory.updateExecution(execution.id, { progress: 50 });
|
||||
|
||||
// 5. 执行模板
|
||||
const result = await template.execute(imageUrl);
|
||||
|
||||
// 6. 记录执行完成
|
||||
await this.templateFactory.updateExecution(execution.id, {
|
||||
status: ExecutionStatus.COMPLETED,
|
||||
completedAt: new Date(),
|
||||
progress: 100,
|
||||
executionResult: result,
|
||||
executionDuration: Math.floor((Date.now() - execution.startedAt.getTime()) / 1000)
|
||||
});
|
||||
|
||||
return { success: true, data: result, executionId: execution.id };
|
||||
|
||||
} catch (error) {
|
||||
// 7. 记录执行失败
|
||||
await this.templateFactory.updateExecution(execution.id, {
|
||||
status: ExecutionStatus.FAILED,
|
||||
completedAt: new Date(),
|
||||
errorMessage: error.message,
|
||||
executionDuration: execution.startedAt ?
|
||||
Math.floor((Date.now() - execution.startedAt.getTime()) / 1000) : 0
|
||||
});
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@Get('executions/:userId')
|
||||
async getUserExecutions(@Param('userId') userId: string) {
|
||||
return this.templateFactory.getUserExecutions(userId);
|
||||
}
|
||||
|
||||
@Get(':templateId/stats')
|
||||
async getTemplateStats(@Param('templateId', ParseIntPipe) templateId: number) {
|
||||
return this.templateFactory.getTemplateExecutionStats(templateId);
|
||||
}
|
||||
|
||||
@Get()
|
||||
@@ -386,16 +625,36 @@ export class TemplateController {
|
||||
|
||||
@Get('image')
|
||||
async getImageTemplates() {
|
||||
return this.templateFactory.getImageTemplates();
|
||||
return this.templateFactory.getTemplatesByType(TemplateType.IMAGE);
|
||||
}
|
||||
|
||||
@Get('video')
|
||||
async getVideoTemplates() {
|
||||
return this.templateFactory.getVideoTemplates();
|
||||
return this.templateFactory.getTemplatesByType(TemplateType.VIDEO);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 执行记录查询示例
|
||||
```typescript
|
||||
// 查询用户最近的执行记录
|
||||
const userExecutions = await templateFactory.getUserExecutions('user123', 10);
|
||||
|
||||
// 查询特定模板的执行统计
|
||||
const stats = await templateFactory.getTemplateExecutionStats(1);
|
||||
// 返回格式: [
|
||||
// { status: 'completed', count: '85', avgDuration: '12.5' },
|
||||
// { status: 'failed', count: '5', avgDuration: '8.2' },
|
||||
// { status: 'processing', count: '2', avgDuration: null }
|
||||
// ]
|
||||
|
||||
// 根据执行ID查询详细信息
|
||||
const execution = await executionRepository.findOne({
|
||||
where: { id: executionId },
|
||||
relations: ['template']
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
## ✅ 架构优势
|
||||
|
||||
@@ -409,17 +668,44 @@ export class TemplateController {
|
||||
- **运营友好**:通过管理后台可以调整模板参数、启用/禁用模板
|
||||
- **A/B测试**:可以创建同一功能的不同版本进行测试
|
||||
- **统计分析**:可以跟踪每个模板的使用情况和效果
|
||||
- **执行跟踪**:完整记录每次模板执行的过程、结果和性能指标
|
||||
- **故障诊断**:详细的错误记录和执行时长,便于问题排查
|
||||
|
||||
### 3. 使用体验优化
|
||||
```typescript
|
||||
// 简洁的API调用
|
||||
const result = await new N8nVideoGenerateTemplateEntity(id, repo)
|
||||
.onInit()
|
||||
.then(e => e.execute(imgUrl));
|
||||
// 带执行记录的模板调用
|
||||
const execution = await templateFactory.createExecution({
|
||||
taskId: 'task_123',
|
||||
templateId: 1,
|
||||
userId: 'user_456',
|
||||
inputParams: { imageUrl: 'https://...' }
|
||||
});
|
||||
|
||||
// 工厂模式调用
|
||||
const template = await templateFactory.createTemplateByCode('character_figurine_v1');
|
||||
const result = await template.execute(imageUrl);
|
||||
|
||||
// 查询执行历史
|
||||
const history = await templateFactory.getUserExecutions('user_456');
|
||||
|
||||
// 查看模板统计
|
||||
const stats = await templateFactory.getTemplateExecutionStats(1);
|
||||
```
|
||||
|
||||
## 📈 执行记录的业务价值
|
||||
|
||||
### 1. 运营数据支持
|
||||
- **使用频率分析**:哪些模板最受欢迎
|
||||
- **成功率监控**:模板执行的成功率和失败原因
|
||||
- **性能监控**:平均执行时长和性能瓶颈
|
||||
|
||||
### 2. 用户体验优化
|
||||
- **执行历史查看**:用户可以查看自己的执行记录
|
||||
- **进度显示**:实时显示模板执行进度
|
||||
- **错误反馈**:清晰的错误信息和重试建议
|
||||
|
||||
### 3. 系统稳定性
|
||||
- **故障排查**:详细的执行日志便于问题定位
|
||||
- **性能优化**:基于执行数据优化模板参数
|
||||
- **容量规划**:基于使用数据进行系统容量规划
|
||||
|
||||
这种设计完美融合了您的面向对象架构优势和数据库配置管理的灵活性!
|
||||
Reference in New Issue
Block a user