docs: 更新数据库配置从PostgreSQL到MySQL

- 更新multi-platform-integration-solution.md中的数据库配置
  - 数据库类型从PostgreSQL改为MySQL
  - 端口从5432改为3306
  - 默认用户名从postgres改为root
  - 所有jsonb字段类型改为json
  - Docker配置更新为MySQL 8.0
- 更新swagger-api-documentation.md中的数据存储描述
  - 从"PostgreSQL + JSONB"改为"MySQL + JSON"
This commit is contained in:
imeepos 2025-09-04 14:43:01 +08:00
parent 528d841d82
commit a513202155
2 changed files with 488 additions and 201 deletions

View File

@ -53,7 +53,7 @@
┌─────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────┐
│ 数据存储层 (Data Layer) │ │ 数据存储层 (Data Layer) │
├─────────────────────────────────────────────────────────────┤ ├─────────────────────────────────────────────────────────────┤
PostgreSQL │ Redis │ 文件存储 │ RabbitMQ │ 对象存储 MySQL │ 文件存储 │ 对象存储
└─────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────┘
``` ```
@ -89,9 +89,8 @@
### 后端技术栈 ### 后端技术栈
- **框架**: NestJS (已选择) - **框架**: NestJS (已选择)
- **数据库**: PostgreSQL (主数据库) + Redis (缓存) - **数据库**: MySQL (主数据库)
- **ORM**: TypeORM (数据库操作) - **ORM**: TypeORM (数据库操作)
- **消息队列**: RabbitMQ
### 开发工具 ### 开发工具
- **API文档**: Swagger/OpenAPI - **API文档**: Swagger/OpenAPI
@ -158,88 +157,40 @@ interface ExtensionServiceInterface {
- **平台适配层**: 每个服务都有对应的平台适配器 - **平台适配层**: 每个服务都有对应的平台适配器
- **配置驱动**: 通过配置文件控制服务的启用和禁用 - **配置驱动**: 通过配置文件控制服务的启用和禁用
### 3. 消息队列服务 (RabbitMQ) ### 3. 异步任务处理服务
#### 3.1 业务队列配置 #### 3.1 数据库异步处理
- **功能**: 使用数据库任务队列替代消息队列
- **实现方式**: 通过数据库表存储待处理任务,定时处理器执行
#### 3.2 业务异步处理
```typescript ```typescript
interface QueueConfig { // 用户注册异步处理
// 用户相关队列 async processUserRegistration(userId: string, platform: string) {
USER_REGISTRATION: 'user.registration';
USER_LOGIN: 'user.login';
USER_UPDATE: 'user.update';
// AI生成相关队列
AI_GENERATION_TASK: 'ai.generation.task';
AI_GENERATION_COMPLETED: 'ai.generation.completed';
AI_GENERATION_FAILED: 'ai.generation.failed';
// 积分系统队列
CREDIT_REWARD: 'credit.reward';
CREDIT_CONSUME: 'credit.consume';
// 广告系统队列
AD_WATCH_COMPLETED: 'ad.watch.completed';
AD_REWARD_GRANTED: 'ad.reward.granted';
// 订阅系统队列
SUBSCRIPTION_ACTIVATED: 'subscription.activated';
SUBSCRIPTION_EXPIRED: 'subscription.expired';
// 注意: 模板系统采用面向对象设计通过AI_GENERATION_*队列处理
// 平台同步队列
PLATFORM_SYNC: 'platform.sync';
PLATFORM_DATA_SYNC: 'platform.data.sync';
}
```
#### 3.2 业务消息处理
```typescript
// 用户注册消息处理
@RabbitSubscribe({
exchange: 'user.exchange',
routingKey: 'user.registration',
queue: 'user.registration.queue',
})
async handleUserRegistration(message: UserRegistrationMessage) {
// 用户数据处理 // 用户数据处理
await this.userService.processRegistration(message.userId); await this.userService.processRegistration(userId);
// 发放新用户积分奖励 // 发放新用户积分奖励
await this.creditService.grantNewUserBonus(message.userId, message.platform); await this.creditService.grantNewUserBonus(userId, platform);
// 同步到各平台 // 同步到各平台
await this.platformService.syncUserData(message); await this.platformService.syncUserData({ userId, platform });
} }
// AI生成任务处理 // AI生成任务异步处理
@RabbitSubscribe({ async processAIGenerationTask(taskId: string) {
exchange: 'ai.exchange',
routingKey: 'ai.generation.task',
queue: 'ai.generation.task.queue',
})
async handleAIGenerationTask(message: AIGenerationTaskMessage) {
// 处理AI生成任务 // 处理AI生成任务
await this.generationService.processGenerationTask(message.taskId); await this.generationService.processGenerationTask(taskId);
} }
// 广告观看完成处理 // 广告观看完成异步处理
@RabbitSubscribe({ async processAdWatchCompleted(adWatchId: string, watchDuration: number, adData: any) {
exchange: 'ad.exchange',
routingKey: 'ad.watch.completed',
queue: 'ad.watch.completed.queue',
})
async handleAdWatchCompleted(message: AdWatchCompletedMessage) {
// 发放广告观看奖励 // 发放广告观看奖励
await this.adService.completeAdWatch( await this.adService.completeAdWatch(adWatchId, watchDuration, adData);
message.adWatchId,
message.watchDuration,
message.adData
);
} }
``` ```
## 数据库设计 (PostgreSQL + TypeORM) ## 数据库设计 (MySQL + TypeORM)
### 1. 用户实体 (User Entity) ### 1. 用户实体 (User Entity)
```typescript ```typescript
@ -339,7 +290,7 @@ export class PlatformUser {
@Column({ length: 100 }) @Column({ length: 100 })
platformUserId: string; platformUserId: string;
@Column({ type: 'jsonb', nullable: true }) @Column({ type: 'json', nullable: true })
platformData: any; platformData: any;
@Column({ length: 500, nullable: true }) @Column({ length: 500, nullable: true })
@ -390,10 +341,10 @@ export class ExtensionData {
@Column({ length: 100, nullable: true }) @Column({ length: 100, nullable: true })
referenceId: string; // 外部引用ID referenceId: string; // 外部引用ID
@Column({ type: 'jsonb' }) @Column({ type: 'json' })
data: any; // 灵活的JSON数据存储 data: any; // 灵活的JSON数据存储
@Column({ type: 'jsonb', nullable: true }) @Column({ type: 'json', nullable: true })
metadata: any; // 元数据 metadata: any; // 元数据
@Column({ length: 20, default: 'active' }) @Column({ length: 20, default: 'active' })
@ -469,7 +420,7 @@ export class UserCredit {
@Column({ length: 100, nullable: true }) @Column({ length: 100, nullable: true })
referenceId: string; // 关联的业务ID如广告ID、任务ID等 referenceId: string; // 关联的业务ID如广告ID、任务ID等
@Column({ type: 'jsonb', nullable: true }) @Column({ type: 'json', nullable: true })
metadata: any; metadata: any;
@CreateDateColumn() @CreateDateColumn()
@ -548,7 +499,7 @@ export class GenerationTask {
@Column({ length: 100, nullable: true }) @Column({ length: 100, nullable: true })
aiModelId: string; // 使用的AI模型ID aiModelId: string; // 使用的AI模型ID
@Column({ type: 'jsonb', nullable: true }) @Column({ type: 'json', nullable: true })
parameters: any; // 生成参数 parameters: any; // 生成参数
@Column({ type: 'text', nullable: true }) @Column({ type: 'text', nullable: true })
@ -633,7 +584,7 @@ export class UserSubscription {
@Column({ length: 100, nullable: true }) @Column({ length: 100, nullable: true })
paymentId: string; // 支付订单ID paymentId: string; // 支付订单ID
@Column({ type: 'jsonb', nullable: true }) @Column({ type: 'json', nullable: true })
features: any; // 订阅特权 features: any; // 订阅特权
@Column({ type: 'boolean', default: true }) @Column({ type: 'boolean', default: true })
@ -709,7 +660,7 @@ export class AdWatch {
@Column({ type: 'integer', nullable: true }) @Column({ type: 'integer', nullable: true })
rewardCredits: number; // 奖励积分 rewardCredits: number; // 奖励积分
@Column({ type: 'jsonb', nullable: true }) @Column({ type: 'json', nullable: true })
adData: any; // 广告相关数据 adData: any; // 广告相关数据
@CreateDateColumn() @CreateDateColumn()
@ -740,10 +691,10 @@ export class AdWatch {
export class GenerationTask { export class GenerationTask {
// ... 其他字段 // ... 其他字段
@Column({ type: 'jsonb', nullable: true }) @Column({ type: 'json', nullable: true })
parameters: any; // 存储模板执行参数 parameters: any; // 存储模板执行参数
@Column({ type: 'jsonb', nullable: true }) @Column({ type: 'json', nullable: true })
metadata: any; // 存储模板代码、版本等信息 metadata: any; // 存储模板代码、版本等信息
} }
@ -764,10 +715,10 @@ import { TypeOrmModuleOptions } from '@nestjs/typeorm';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
export const getDatabaseConfig = (configService: ConfigService): TypeOrmModuleOptions => ({ export const getDatabaseConfig = (configService: ConfigService): TypeOrmModuleOptions => ({
type: 'postgres', type: 'mysql',
host: configService.get('DB_HOST', 'localhost'), host: configService.get('DB_HOST', 'localhost'),
port: configService.get('DB_PORT', 5432), port: configService.get('DB_PORT', 3306),
username: configService.get('DB_USERNAME', 'postgres'), username: configService.get('DB_USERNAME', 'root'),
password: configService.get('DB_PASSWORD', 'password'), password: configService.get('DB_PASSWORD', 'password'),
database: configService.get('DB_DATABASE', 'mini_app_platform'), database: configService.get('DB_DATABASE', 'mini_app_platform'),
entities: [__dirname + '/../**/*.entity{.ts,.js}'], entities: [__dirname + '/../**/*.entity{.ts,.js}'],
@ -823,7 +774,7 @@ export class CreateInitialTables1703001000000 implements MigrationInterface {
"userId" uuid NOT NULL, "userId" uuid NOT NULL,
"platform" "platform_type" NOT NULL, "platform" "platform_type" NOT NULL,
"platformUserId" character varying(100) NOT NULL, "platformUserId" character varying(100) NOT NULL,
"platformData" jsonb, "platformData" json,
"accessToken" character varying(500), "accessToken" character varying(500),
"refreshToken" character varying(500), "refreshToken" character varying(500),
"expiresAt" TIMESTAMP, "expiresAt" TIMESTAMP,
@ -843,8 +794,8 @@ export class CreateInitialTables1703001000000 implements MigrationInterface {
"platform" "platform_type" NOT NULL, "platform" "platform_type" NOT NULL,
"dataType" character varying(50) NOT NULL, "dataType" character varying(50) NOT NULL,
"referenceId" character varying(100), "referenceId" character varying(100),
"data" jsonb NOT NULL, "data" json NOT NULL,
"metadata" jsonb, "metadata" json,
"status" character varying(20) NOT NULL DEFAULT 'active', "status" character varying(20) NOT NULL DEFAULT 'active',
"createdAt" TIMESTAMP NOT NULL DEFAULT now(), "createdAt" TIMESTAMP NOT NULL DEFAULT now(),
"updatedAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(),
@ -873,7 +824,7 @@ export class CreateInitialTables1703001000000 implements MigrationInterface {
"balance" integer NOT NULL, "balance" integer NOT NULL,
"description" character varying(200), "description" character varying(200),
"referenceId" character varying(100), "referenceId" character varying(100),
"metadata" jsonb, "metadata" json,
"createdAt" TIMESTAMP NOT NULL DEFAULT now(), "createdAt" TIMESTAMP NOT NULL DEFAULT now(),
"updatedAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(),
CONSTRAINT "PK_user_credits_id" PRIMARY KEY ("id"), CONSTRAINT "PK_user_credits_id" PRIMARY KEY ("id"),
@ -903,7 +854,7 @@ export class CreateInitialTables1703001000000 implements MigrationInterface {
"status" "task_status" NOT NULL DEFAULT 'pending', "status" "task_status" NOT NULL DEFAULT 'pending',
"creditCost" integer NOT NULL, "creditCost" integer NOT NULL,
"aiModelId" character varying(100), "aiModelId" character varying(100),
"parameters" jsonb, "parameters" json,
"errorMessage" text, "errorMessage" text,
"processingTime" integer, "processingTime" integer,
"createdAt" TIMESTAMP NOT NULL DEFAULT now(), "createdAt" TIMESTAMP NOT NULL DEFAULT now(),
@ -935,7 +886,7 @@ export class CreateInitialTables1703001000000 implements MigrationInterface {
"currency" character varying(10) NOT NULL DEFAULT 'CNY', "currency" character varying(10) NOT NULL DEFAULT 'CNY',
"monthlyCredits" integer NOT NULL, "monthlyCredits" integer NOT NULL,
"paymentId" character varying(100), "paymentId" character varying(100),
"features" jsonb, "features" json,
"autoRenew" boolean NOT NULL DEFAULT true, "autoRenew" boolean NOT NULL DEFAULT true,
"createdAt" TIMESTAMP NOT NULL DEFAULT now(), "createdAt" TIMESTAMP NOT NULL DEFAULT now(),
"updatedAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(),
@ -964,7 +915,7 @@ export class CreateInitialTables1703001000000 implements MigrationInterface {
"status" "ad_status" NOT NULL, "status" "ad_status" NOT NULL,
"watchDuration" integer, "watchDuration" integer,
"rewardCredits" integer, "rewardCredits" integer,
"adData" jsonb, "adData" json,
"createdAt" TIMESTAMP NOT NULL DEFAULT now(), "createdAt" TIMESTAMP NOT NULL DEFAULT now(),
"updatedAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(),
CONSTRAINT "PK_ad_watches_id" PRIMARY KEY ("id"), CONSTRAINT "PK_ad_watches_id" PRIMARY KEY ("id"),
@ -1172,49 +1123,28 @@ services:
build: . build: .
environment: environment:
- NODE_ENV=production - NODE_ENV=production
- DB_HOST=postgres - DB_HOST=mysql
- DB_PORT=5432 - DB_PORT=3306
- DB_USERNAME=${POSTGRES_USER} - DB_USERNAME=${MYSQL_USER}
- DB_PASSWORD=${POSTGRES_PASSWORD} - DB_PASSWORD=${MYSQL_PASSWORD}
- DB_DATABASE=${POSTGRES_DB} - DB_DATABASE=${MYSQL_DB}
- REDIS_HOST=redis
- REDIS_PORT=6379
- RABBITMQ_URL=amqp://rabbitmq:5672
depends_on: depends_on:
- postgres - mysql
- redis
- rabbitmq
postgres: mysql:
image: postgres:15-alpine image: mysql:8.0
environment: environment:
POSTGRES_USER: ${POSTGRES_USER} MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} MYSQL_USER: ${MYSQL_USER}
POSTGRES_DB: ${POSTGRES_DB} MYSQL_PASSWORD: ${MYSQL_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DB}
volumes: volumes:
- postgres_data:/var/lib/postgresql/data - mysql_data:/var/lib/mysql
ports: ports:
- "5432:5432" - "3306:3306"
redis:
image: redis:alpine
ports:
- "6379:6379"
rabbitmq:
image: rabbitmq:3-management-alpine
environment:
RABBITMQ_DEFAULT_USER: ${RABBITMQ_USER}
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASSWORD}
ports:
- "5672:5672"
- "15672:15672" # 管理界面
volumes:
- rabbitmq_data:/var/lib/rabbitmq
volumes: volumes:
postgres_data: mysql_data:
rabbitmq_data:
``` ```
### 2. 负载均衡配置 ### 2. 负载均衡配置
@ -1222,75 +1152,6 @@ volumes:
- 支持多实例部署 - 支持多实例部署
- 健康检查和自动故障转移 - 健康检查和自动故障转移
## RabbitMQ 配置与管理
### 1. 交换机配置
```typescript
// src/config/rabbitmq.config.ts
export const RabbitMQConfig = {
exchanges: [
{
name: 'user.exchange',
type: 'topic',
options: { durable: true }
},
{
name: 'payment.exchange',
type: 'topic',
options: { durable: true }
},
{
name: 'push.exchange',
type: 'topic',
options: { durable: true }
},
{
name: 'platform.exchange',
type: 'topic',
options: { durable: true }
}
],
queues: [
{
name: 'user.registration.queue',
options: { durable: true }
},
{
name: 'payment.success.queue',
options: { durable: true }
},
{
name: 'push.message.queue',
options: { durable: true }
}
]
};
```
### 2. 消息生产者示例
```typescript
// src/services/message-producer.service.ts
@Injectable()
export class MessageProducerService {
constructor(
@Inject('RABBITMQ_CONNECTION')
private readonly connection: Connection,
) {}
async publishPaymentSuccess(paymentData: PaymentSuccessMessage) {
const channel = await this.connection.createChannel();
await channel.publish(
'payment.exchange',
'payment.success',
Buffer.from(JSON.stringify(paymentData)),
{ persistent: true }
);
await channel.close();
}
}
```
## API文档配置 (Swagger/OpenAPI) ## API文档配置 (Swagger/OpenAPI)
@ -1675,7 +1536,6 @@ bootstrap();
- [ ] 广告服务集成 (观看验证、奖励发放) - [ ] 广告服务集成 (观看验证、奖励发放)
- [ ] AI生成服务开发 (任务管理、模型调用) - [ ] AI生成服务开发 (任务管理、模型调用)
- [ ] 文件存储服务 (图片上传、结果存储) - [ ] 文件存储服务 (图片上传、结果存储)
- [ ] RabbitMQ消息队列集成
### Phase 3: 高级功能开发 (6周) ### Phase 3: 高级功能开发 (6周)
- [ ] 订阅服务开发 (包月付费、会员权益) - [ ] 订阅服务开发 (包月付费、会员权益)
@ -1730,8 +1590,8 @@ bootstrap();
1. **统一性**: 抹平8大平台API差异和AI模型调用差异统一的开发体验 1. **统一性**: 抹平8大平台API差异和AI模型调用差异统一的开发体验
2. **可扩展性**: 模块化架构支持快速添加新功能、平台和AI模型 2. **可扩展性**: 模块化架构支持快速添加新功能、平台和AI模型
3. **模板化**: 通过模板系统标准化AI调用简化前端集成 3. **模板化**: 通过模板系统标准化AI调用简化前端集成
4. **高性能**: PostgreSQL + Redis + RabbitMQ,支持高并发处理 4. **高性能**: PostgreSQL 数据库,支持高并发处理
5. **数据安全**: JSONB灵活存储完整的数据备份和恢复机制 5. **数据安全**: JSONB灵活存储完整的数据备份和恢复机制
6. **异步处理**: 基于消息队列的异步任务处理,提升用户体验 6. **异步处理**: 基于数据库任务队列的异步处理,提升用户体验
7. **类型安全**: TypeScript + TypeORM编译时错误检查 7. **类型安全**: TypeScript + TypeORM编译时错误检查
8. **智能缓存**: 活跃模板缓存,提升模板执行性能 8. **智能缓存**: 活跃模板缓存,提升模板执行性能

View File

@ -20,13 +20,19 @@ export function setupSwagger(app: INestApplication): void {
.setDescription(` .setDescription(`
## 功能特性 ## 功能特性
- 🔐 统一用户认证 (微信/支付宝/百度/字节跳动等) - 🔐 统一用户认证 (微信/支付宝/百度/字节跳动等)
- 🔄 平台数据同步 (RabbitMQ异步处理) - 🔄 平台数据同步 (数据库异步处理)
- 🧩 可扩展架构 (支持后续添加支付、推送等功能) - 🧩 可扩展架构 (支持后续添加支付、推送等功能)
- 📊 灵活数据存储 (PostgreSQL + JSONB) - 📊 灵活数据存储 (MySQL + JSON)
## 认证方式 ## 认证方式
使用JWT Bearer Token进行API认证 使用JWT Bearer Token进行API认证
## AI模板系统
- 🎨 动态模板管理 (数据库配置 + 代码执行)
- 🚀 N8n工作流集成 (图片/视频生成)
- 📊 使用统计分析 (性能监控 + 用户行为)
- 🎛️ 运营管理后台 (A/B测试 + 个性化推荐)
## 响应格式 ## 响应格式
所有API响应都遵循统一格式 所有API响应都遵循统一格式
\`\`\`json \`\`\`json
@ -539,9 +545,427 @@ export class UserController {
} }
``` ```
## 7. 环境配置 ## 7. AI模板管理 API
### 7.1 开发环境配置 ### 7.1 模板数据传输对象
```typescript
// src/dto/template.dto.ts
import { ApiProperty } from '@nestjs/swagger';
import { IsString, IsNumber, IsEnum, IsOptional, IsArray, IsBoolean } from 'class-validator';
export enum TemplateType {
IMAGE = 'image',
VIDEO = 'video'
}
export class TemplateListDto {
@ApiProperty({ description: '模板ID', example: 1 })
id: number;
@ApiProperty({ description: '模板代码', example: 'character_figurine_v1' })
code: string;
@ApiProperty({ description: '模板名称', example: '人物手办' })
name: string;
@ApiProperty({ description: '模板描述', example: '将人物照片制作成精细的角色手办模型' })
description: string;
@ApiProperty({ enum: TemplateType, description: '模板类型', example: 'video' })
templateType: TemplateType;
@ApiProperty({ description: '积分消耗', example: 28 })
creditCost: number;
@ApiProperty({ description: '版本号', example: '1.0.0' })
version: string;
@ApiProperty({ description: '输入示例URL', required: false })
inputExampleUrl?: string;
@ApiProperty({ description: '输出示例URL', required: false })
outputExampleUrl?: string;
@ApiProperty({ type: [String], description: '标签数组', example: ['人物', '手办', '模型'] })
tags: string[];
@ApiProperty({ description: '是否启用', example: true })
isActive: boolean;
@ApiProperty({ description: '创建时间', example: '2024-01-01T00:00:00Z' })
createdAt: Date;
}
export class TemplateExecuteDto {
@ApiProperty({
description: '输入图片URL',
example: 'https://cdn.roasmax.cn/upload/3d590851eb584e92aa415a964e93260e.jpg'
})
@IsString()
imageUrl: string;
}
export class TemplateExecuteResponseDto {
@ApiProperty({ description: '执行状态', example: true })
success: boolean;
@ApiProperty({ description: '生成结果URL(图片模板)', required: false })
imageUrl?: string;
@ApiProperty({ description: '生成结果URL(视频模板)', required: false })
videoUrl?: string;
@ApiProperty({ description: '缩略图URL', required: false })
thumbnailUrl?: string;
@ApiProperty({ description: '任务ID', example: 'req_1704067200_abc123' })
taskId: string;
@ApiProperty({ description: '执行耗时(毫秒)', example: 5000 })
executionTime: number;
@ApiProperty({ description: '消耗积分', example: 28 })
creditCost: number;
}
export class TemplateStatsDto {
@ApiProperty({ description: '总模板数', example: 8 })
total: number;
@ApiProperty({ description: '启用模板数', example: 8 })
enabled: number;
@ApiProperty({ description: '图片模板数', example: 3 })
imageTemplates: number;
@ApiProperty({ description: '视频模板数', example: 5 })
videoTemplates: number;
}
```
### 7.2 模板管理控制器
```typescript
// src/controllers/template.controller.ts
import {
Controller, Get, Post, Param, Body, Query, UseGuards, ParseIntPipe
} from '@nestjs/common';
import {
ApiTags,
ApiOperation,
ApiResponse,
ApiBearerAuth,
ApiParam,
ApiQuery
} from '@nestjs/swagger';
import { JwtAuthGuard } from '../guards/jwt-auth.guard';
import { CurrentUser } from '../decorators/current-user.decorator';
import { ApiCommonResponses, ApiAuthResponses } from '../decorators/api-common-responses.decorator';
import { N8nTemplateFactoryService } from '../services/n8n-template-factory.service';
import { TemplateListDto, TemplateExecuteDto, TemplateExecuteResponseDto, TemplateStatsDto } from '../dto/template.dto';
@ApiTags('🎨 AI模板系统')
@Controller('templates')
@ApiCommonResponses()
export class TemplateController {
constructor(
private readonly templateFactory: N8nTemplateFactoryService,
) {}
@Get()
@ApiOperation({
summary: '获取所有模板列表',
description: '获取所有可用的AI生成模板支持按类型筛选'
})
@ApiQuery({
name: 'type',
required: false,
enum: ['image', 'video'],
description: '模板类型筛选'
})
@ApiResponse({
status: 200,
description: '获取成功',
type: [TemplateListDto]
})
async getTemplates(@Query('type') type?: 'image' | 'video') {
if (type === 'image') {
return this.templateFactory.getTemplatesByType('image');
} else if (type === 'video') {
return this.templateFactory.getTemplatesByType('video');
}
return this.templateFactory.getAllTemplates();
}
@Get('stats')
@ApiOperation({
summary: '获取模板统计信息',
description: '获取模板总数、类型分布等统计信息'
})
@ApiResponse({
status: 200,
description: '获取成功',
type: TemplateStatsDto
})
async getTemplateStats() {
const allTemplates = await this.templateFactory.getAllTemplates();
const imageTemplates = allTemplates.filter(t => t.templateType === 'image');
const videoTemplates = allTemplates.filter(t => t.templateType === 'video');
return {
total: allTemplates.length,
enabled: allTemplates.filter(t => t.isActive).length,
imageTemplates: imageTemplates.length,
videoTemplates: videoTemplates.length,
};
}
@Get(':templateId')
@ApiOperation({
summary: '获取模板详情',
description: '根据模板ID获取详细信息'
})
@ApiParam({
name: 'templateId',
description: '模板ID',
example: 1
})
@ApiResponse({
status: 200,
description: '获取成功',
type: TemplateListDto
})
async getTemplateById(@Param('templateId', ParseIntPipe) templateId: number) {
return this.templateFactory.getTemplateById(templateId);
}
@Get('code/:templateCode')
@ApiOperation({
summary: '通过代码获取模板详情',
description: '根据模板代码获取详细信息'
})
@ApiParam({
name: 'templateCode',
description: '模板代码',
example: 'character_figurine_v1'
})
@ApiResponse({
status: 200,
description: '获取成功',
type: TemplateListDto
})
async getTemplateByCode(@Param('templateCode') templateCode: string) {
return this.templateFactory.getTemplateByCode(templateCode);
}
@Post(':templateId/execute')
@UseGuards(JwtAuthGuard)
@ApiBearerAuth('JWT-auth')
@ApiAuthResponses()
@ApiOperation({
summary: '执行模板生成',
description: '根据模板ID执行AI生成任务支持图片和视频生成'
})
@ApiParam({
name: 'templateId',
description: '模板ID',
example: 1
})
@ApiResponse({
status: 200,
description: '执行成功',
type: TemplateExecuteResponseDto
})
@ApiResponse({
status: 400,
description: '参数错误',
schema: {
example: {
code: 400,
message: '输入图片URL不能为空',
data: null
}
}
})
@ApiResponse({
status: 402,
description: '积分不足',
schema: {
example: {
code: 402,
message: '积分不足,当前余额: 10需要: 28',
data: null
}
}
})
async executeTemplate(
@Param('templateId', ParseIntPipe) templateId: number,
@Body() executeDto: TemplateExecuteDto,
@CurrentUser() user: any
) {
const startTime = Date.now();
// 获取模板配置
const templateConfig = await this.templateFactory.getTemplateById(templateId);
// 创建动态模板实例并执行
let template;
if (templateConfig.templateType === 'image') {
template = await this.templateFactory.createImageTemplate(templateId);
} else {
template = await this.templateFactory.createVideoTemplate(templateId);
}
const result = await template.execute(executeDto.imageUrl);
const executionTime = Date.now() - startTime;
return {
success: true,
imageUrl: templateConfig.templateType === 'image' ? result : undefined,
videoUrl: templateConfig.templateType === 'video' ? result : undefined,
taskId: `req_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`,
executionTime,
creditCost: templateConfig.creditCost
};
}
@Post('code/:templateCode/execute')
@UseGuards(JwtAuthGuard)
@ApiBearerAuth('JWT-auth')
@ApiAuthResponses()
@ApiOperation({
summary: '通过代码执行模板',
description: '根据模板代码执行AI生成任务支持图片和视频生成'
})
@ApiParam({
name: 'templateCode',
description: '模板代码',
example: 'character_figurine_v1'
})
@ApiResponse({
status: 200,
description: '执行成功',
type: TemplateExecuteResponseDto
})
async executeTemplateByCode(
@Param('templateCode') templateCode: string,
@Body() executeDto: TemplateExecuteDto,
@CurrentUser() user: any
) {
const startTime = Date.now();
// 创建动态模板实例并执行
const template = await this.templateFactory.createTemplateByCode(templateCode);
const result = await template.execute(executeDto.imageUrl);
const executionTime = Date.now() - startTime;
return {
success: true,
imageUrl: template.templateType === 'image' ? result : undefined,
videoUrl: template.templateType === 'video' ? result : undefined,
taskId: `req_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`,
executionTime,
creditCost: template.creditCost
};
}
}
```
### 7.3 API使用示例
#### 获取所有模板
```bash
curl -X GET "http://localhost:3000/api/templates" \
-H "accept: application/json"
```
响应示例:
```json
{
"code": 200,
"message": "success",
"data": [
{
"id": 1,
"code": "character_figurine_v1",
"name": "人物手办",
"description": "将人物照片制作成精细的角色手办模型,展示在收藏家房间中,并生成抚摸手办的视频",
"templateType": "video",
"creditCost": 28,
"version": "1.0.0",
"inputExampleUrl": "https://cdn.roasmax.cn/upload/3d590851eb584e92aa415a964e93260e.jpg",
"outputExampleUrl": "https://file.302.ai/gpt/imgs/20250828/2283106b31faf2066e1a72d955f65bca.jpg",
"tags": ["人物", "手办", "模型", "收藏", "PVC", "角色模型", "视频生成"],
"isActive": true,
"createdAt": "2024-01-01T00:00:00Z"
}
]
}
```
#### 执行模板生成
```bash
curl -X POST "http://localhost:3000/api/templates/1/execute" \
-H "accept: application/json" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"imageUrl": "https://cdn.roasmax.cn/upload/3d590851eb584e92aa415a964e93260e.jpg"
}'
```
响应示例:
```json
{
"code": 200,
"message": "success",
"data": {
"success": true,
"videoUrl": "https://n8n.bowongai.com/files/generated_video_abc123.mp4",
"taskId": "req_1704067200_abc123",
"executionTime": 5000,
"creditCost": 28
}
}
```
### 7.4 错误处理
```typescript
// 常见错误响应
export const TemplateApiErrors = {
TEMPLATE_NOT_FOUND: {
code: 404,
message: '模板不存在',
data: null
},
INSUFFICIENT_CREDITS: {
code: 402,
message: '积分不足',
data: { required: 28, current: 10 }
},
INVALID_IMAGE_URL: {
code: 400,
message: '无效的图片URL',
data: null
},
TEMPLATE_EXECUTION_FAILED: {
code: 500,
message: 'AI生成失败请稍后重试',
data: null
},
TEMPLATE_DISABLED: {
code: 403,
message: '模板已禁用',
data: null
}
};
```
## 8. 环境配置
### 8.1 开发环境配置
```typescript ```typescript
// src/main.ts // src/main.ts
import { NestFactory } from '@nestjs/core'; import { NestFactory } from '@nestjs/core';
@ -584,7 +1008,7 @@ async function bootstrap() {
bootstrap(); bootstrap();
``` ```
## 8. 访问API文档 ## 9. 访问API文档
启动应用后访问以下地址查看API文档 启动应用后访问以下地址查看API文档
@ -598,3 +1022,6 @@ API文档包含
- 📝 请求/响应示例 - 📝 请求/响应示例
- 🧪 在线接口测试功能 - 🧪 在线接口测试功能
- 📊 数据模型定义 - 📊 数据模型定义
- 🎨 AI模板系统接口 (支持图片/视频生成)
- 💾 动态模板配置管理
- 📈 模板使用统计和监控