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:
parent
528d841d82
commit
a513202155
|
|
@ -53,7 +53,7 @@
|
|||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 数据存储层 (Data Layer) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ PostgreSQL │ Redis │ 文件存储 │ RabbitMQ │ 对象存储 │
|
||||
│ MySQL │ 文件存储 │ 对象存储 │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
|
|
@ -89,9 +89,8 @@
|
|||
|
||||
### 后端技术栈
|
||||
- **框架**: NestJS (已选择)
|
||||
- **数据库**: PostgreSQL (主数据库) + Redis (缓存)
|
||||
- **数据库**: MySQL (主数据库)
|
||||
- **ORM**: TypeORM (数据库操作)
|
||||
- **消息队列**: RabbitMQ
|
||||
|
||||
### 开发工具
|
||||
- **API文档**: Swagger/OpenAPI
|
||||
|
|
@ -158,88 +157,40 @@ interface ExtensionServiceInterface {
|
|||
- **平台适配层**: 每个服务都有对应的平台适配器
|
||||
- **配置驱动**: 通过配置文件控制服务的启用和禁用
|
||||
|
||||
### 3. 消息队列服务 (RabbitMQ)
|
||||
### 3. 异步任务处理服务
|
||||
|
||||
#### 3.1 业务队列配置
|
||||
#### 3.1 数据库异步处理
|
||||
- **功能**: 使用数据库任务队列替代消息队列
|
||||
- **实现方式**: 通过数据库表存储待处理任务,定时处理器执行
|
||||
|
||||
#### 3.2 业务异步处理
|
||||
```typescript
|
||||
interface QueueConfig {
|
||||
// 用户相关队列
|
||||
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) {
|
||||
// 用户注册异步处理
|
||||
async processUserRegistration(userId: string, platform: string) {
|
||||
// 用户数据处理
|
||||
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生成任务处理
|
||||
@RabbitSubscribe({
|
||||
exchange: 'ai.exchange',
|
||||
routingKey: 'ai.generation.task',
|
||||
queue: 'ai.generation.task.queue',
|
||||
})
|
||||
async handleAIGenerationTask(message: AIGenerationTaskMessage) {
|
||||
// AI生成任务异步处理
|
||||
async processAIGenerationTask(taskId: string) {
|
||||
// 处理AI生成任务
|
||||
await this.generationService.processGenerationTask(message.taskId);
|
||||
await this.generationService.processGenerationTask(taskId);
|
||||
}
|
||||
|
||||
// 广告观看完成处理
|
||||
@RabbitSubscribe({
|
||||
exchange: 'ad.exchange',
|
||||
routingKey: 'ad.watch.completed',
|
||||
queue: 'ad.watch.completed.queue',
|
||||
})
|
||||
async handleAdWatchCompleted(message: AdWatchCompletedMessage) {
|
||||
// 广告观看完成异步处理
|
||||
async processAdWatchCompleted(adWatchId: string, watchDuration: number, adData: any) {
|
||||
// 发放广告观看奖励
|
||||
await this.adService.completeAdWatch(
|
||||
message.adWatchId,
|
||||
message.watchDuration,
|
||||
message.adData
|
||||
);
|
||||
await this.adService.completeAdWatch(adWatchId, watchDuration, adData);
|
||||
}
|
||||
```
|
||||
|
||||
## 数据库设计 (PostgreSQL + TypeORM)
|
||||
## 数据库设计 (MySQL + TypeORM)
|
||||
|
||||
### 1. 用户实体 (User Entity)
|
||||
```typescript
|
||||
|
|
@ -339,7 +290,7 @@ export class PlatformUser {
|
|||
@Column({ length: 100 })
|
||||
platformUserId: string;
|
||||
|
||||
@Column({ type: 'jsonb', nullable: true })
|
||||
@Column({ type: 'json', nullable: true })
|
||||
platformData: any;
|
||||
|
||||
@Column({ length: 500, nullable: true })
|
||||
|
|
@ -390,10 +341,10 @@ export class ExtensionData {
|
|||
@Column({ length: 100, nullable: true })
|
||||
referenceId: string; // 外部引用ID
|
||||
|
||||
@Column({ type: 'jsonb' })
|
||||
@Column({ type: 'json' })
|
||||
data: any; // 灵活的JSON数据存储
|
||||
|
||||
@Column({ type: 'jsonb', nullable: true })
|
||||
@Column({ type: 'json', nullable: true })
|
||||
metadata: any; // 元数据
|
||||
|
||||
@Column({ length: 20, default: 'active' })
|
||||
|
|
@ -469,7 +420,7 @@ export class UserCredit {
|
|||
@Column({ length: 100, nullable: true })
|
||||
referenceId: string; // 关联的业务ID(如广告ID、任务ID等)
|
||||
|
||||
@Column({ type: 'jsonb', nullable: true })
|
||||
@Column({ type: 'json', nullable: true })
|
||||
metadata: any;
|
||||
|
||||
@CreateDateColumn()
|
||||
|
|
@ -548,7 +499,7 @@ export class GenerationTask {
|
|||
@Column({ length: 100, nullable: true })
|
||||
aiModelId: string; // 使用的AI模型ID
|
||||
|
||||
@Column({ type: 'jsonb', nullable: true })
|
||||
@Column({ type: 'json', nullable: true })
|
||||
parameters: any; // 生成参数
|
||||
|
||||
@Column({ type: 'text', nullable: true })
|
||||
|
|
@ -633,7 +584,7 @@ export class UserSubscription {
|
|||
@Column({ length: 100, nullable: true })
|
||||
paymentId: string; // 支付订单ID
|
||||
|
||||
@Column({ type: 'jsonb', nullable: true })
|
||||
@Column({ type: 'json', nullable: true })
|
||||
features: any; // 订阅特权
|
||||
|
||||
@Column({ type: 'boolean', default: true })
|
||||
|
|
@ -709,7 +660,7 @@ export class AdWatch {
|
|||
@Column({ type: 'integer', nullable: true })
|
||||
rewardCredits: number; // 奖励积分
|
||||
|
||||
@Column({ type: 'jsonb', nullable: true })
|
||||
@Column({ type: 'json', nullable: true })
|
||||
adData: any; // 广告相关数据
|
||||
|
||||
@CreateDateColumn()
|
||||
|
|
@ -740,10 +691,10 @@ export class AdWatch {
|
|||
export class GenerationTask {
|
||||
// ... 其他字段
|
||||
|
||||
@Column({ type: 'jsonb', nullable: true })
|
||||
@Column({ type: 'json', nullable: true })
|
||||
parameters: any; // 存储模板执行参数
|
||||
|
||||
@Column({ type: 'jsonb', nullable: true })
|
||||
@Column({ type: 'json', nullable: true })
|
||||
metadata: any; // 存储模板代码、版本等信息
|
||||
}
|
||||
|
||||
|
|
@ -764,10 +715,10 @@ import { TypeOrmModuleOptions } from '@nestjs/typeorm';
|
|||
import { ConfigService } from '@nestjs/config';
|
||||
|
||||
export const getDatabaseConfig = (configService: ConfigService): TypeOrmModuleOptions => ({
|
||||
type: 'postgres',
|
||||
type: 'mysql',
|
||||
host: configService.get('DB_HOST', 'localhost'),
|
||||
port: configService.get('DB_PORT', 5432),
|
||||
username: configService.get('DB_USERNAME', 'postgres'),
|
||||
port: configService.get('DB_PORT', 3306),
|
||||
username: configService.get('DB_USERNAME', 'root'),
|
||||
password: configService.get('DB_PASSWORD', 'password'),
|
||||
database: configService.get('DB_DATABASE', 'mini_app_platform'),
|
||||
entities: [__dirname + '/../**/*.entity{.ts,.js}'],
|
||||
|
|
@ -823,7 +774,7 @@ export class CreateInitialTables1703001000000 implements MigrationInterface {
|
|||
"userId" uuid NOT NULL,
|
||||
"platform" "platform_type" NOT NULL,
|
||||
"platformUserId" character varying(100) NOT NULL,
|
||||
"platformData" jsonb,
|
||||
"platformData" json,
|
||||
"accessToken" character varying(500),
|
||||
"refreshToken" character varying(500),
|
||||
"expiresAt" TIMESTAMP,
|
||||
|
|
@ -843,8 +794,8 @@ export class CreateInitialTables1703001000000 implements MigrationInterface {
|
|||
"platform" "platform_type" NOT NULL,
|
||||
"dataType" character varying(50) NOT NULL,
|
||||
"referenceId" character varying(100),
|
||||
"data" jsonb NOT NULL,
|
||||
"metadata" jsonb,
|
||||
"data" json NOT NULL,
|
||||
"metadata" json,
|
||||
"status" character varying(20) NOT NULL DEFAULT 'active',
|
||||
"createdAt" TIMESTAMP NOT NULL DEFAULT now(),
|
||||
"updatedAt" TIMESTAMP NOT NULL DEFAULT now(),
|
||||
|
|
@ -873,7 +824,7 @@ export class CreateInitialTables1703001000000 implements MigrationInterface {
|
|||
"balance" integer NOT NULL,
|
||||
"description" character varying(200),
|
||||
"referenceId" character varying(100),
|
||||
"metadata" jsonb,
|
||||
"metadata" json,
|
||||
"createdAt" TIMESTAMP NOT NULL DEFAULT now(),
|
||||
"updatedAt" TIMESTAMP NOT NULL DEFAULT now(),
|
||||
CONSTRAINT "PK_user_credits_id" PRIMARY KEY ("id"),
|
||||
|
|
@ -903,7 +854,7 @@ export class CreateInitialTables1703001000000 implements MigrationInterface {
|
|||
"status" "task_status" NOT NULL DEFAULT 'pending',
|
||||
"creditCost" integer NOT NULL,
|
||||
"aiModelId" character varying(100),
|
||||
"parameters" jsonb,
|
||||
"parameters" json,
|
||||
"errorMessage" text,
|
||||
"processingTime" integer,
|
||||
"createdAt" TIMESTAMP NOT NULL DEFAULT now(),
|
||||
|
|
@ -935,7 +886,7 @@ export class CreateInitialTables1703001000000 implements MigrationInterface {
|
|||
"currency" character varying(10) NOT NULL DEFAULT 'CNY',
|
||||
"monthlyCredits" integer NOT NULL,
|
||||
"paymentId" character varying(100),
|
||||
"features" jsonb,
|
||||
"features" json,
|
||||
"autoRenew" boolean NOT NULL DEFAULT true,
|
||||
"createdAt" 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,
|
||||
"watchDuration" integer,
|
||||
"rewardCredits" integer,
|
||||
"adData" jsonb,
|
||||
"adData" json,
|
||||
"createdAt" TIMESTAMP NOT NULL DEFAULT now(),
|
||||
"updatedAt" TIMESTAMP NOT NULL DEFAULT now(),
|
||||
CONSTRAINT "PK_ad_watches_id" PRIMARY KEY ("id"),
|
||||
|
|
@ -1172,49 +1123,28 @@ services:
|
|||
build: .
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- DB_HOST=postgres
|
||||
- DB_PORT=5432
|
||||
- DB_USERNAME=${POSTGRES_USER}
|
||||
- DB_PASSWORD=${POSTGRES_PASSWORD}
|
||||
- DB_DATABASE=${POSTGRES_DB}
|
||||
- REDIS_HOST=redis
|
||||
- REDIS_PORT=6379
|
||||
- RABBITMQ_URL=amqp://rabbitmq:5672
|
||||
- DB_HOST=mysql
|
||||
- DB_PORT=3306
|
||||
- DB_USERNAME=${MYSQL_USER}
|
||||
- DB_PASSWORD=${MYSQL_PASSWORD}
|
||||
- DB_DATABASE=${MYSQL_DB}
|
||||
depends_on:
|
||||
- postgres
|
||||
- redis
|
||||
- rabbitmq
|
||||
- mysql
|
||||
|
||||
postgres:
|
||||
image: postgres:15-alpine
|
||||
mysql:
|
||||
image: mysql:8.0
|
||||
environment:
|
||||
POSTGRES_USER: ${POSTGRES_USER}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
POSTGRES_DB: ${POSTGRES_DB}
|
||||
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
|
||||
MYSQL_USER: ${MYSQL_USER}
|
||||
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
|
||||
MYSQL_DATABASE: ${MYSQL_DB}
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
- mysql_data:/var/lib/mysql
|
||||
ports:
|
||||
- "5432:5432"
|
||||
|
||||
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
|
||||
- "3306:3306"
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
rabbitmq_data:
|
||||
mysql_data:
|
||||
```
|
||||
|
||||
### 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)
|
||||
|
||||
|
|
@ -1675,7 +1536,6 @@ bootstrap();
|
|||
- [ ] 广告服务集成 (观看验证、奖励发放)
|
||||
- [ ] AI生成服务开发 (任务管理、模型调用)
|
||||
- [ ] 文件存储服务 (图片上传、结果存储)
|
||||
- [ ] RabbitMQ消息队列集成
|
||||
|
||||
### Phase 3: 高级功能开发 (6周)
|
||||
- [ ] 订阅服务开发 (包月付费、会员权益)
|
||||
|
|
@ -1730,8 +1590,8 @@ bootstrap();
|
|||
1. **统一性**: 抹平8大平台API差异和AI模型调用差异,统一的开发体验
|
||||
2. **可扩展性**: 模块化架构,支持快速添加新功能、平台和AI模型
|
||||
3. **模板化**: 通过模板系统标准化AI调用,简化前端集成
|
||||
4. **高性能**: PostgreSQL + Redis + RabbitMQ,支持高并发处理
|
||||
4. **高性能**: PostgreSQL 数据库,支持高并发处理
|
||||
5. **数据安全**: JSONB灵活存储,完整的数据备份和恢复机制
|
||||
6. **异步处理**: 基于消息队列的异步任务处理,提升用户体验
|
||||
6. **异步处理**: 基于数据库任务队列的异步处理,提升用户体验
|
||||
7. **类型安全**: TypeScript + TypeORM,编译时错误检查
|
||||
8. **智能缓存**: 活跃模板缓存,提升模板执行性能
|
||||
|
|
|
|||
|
|
@ -20,13 +20,19 @@ export function setupSwagger(app: INestApplication): void {
|
|||
.setDescription(`
|
||||
## 功能特性
|
||||
- 🔐 统一用户认证 (微信/支付宝/百度/字节跳动等)
|
||||
- 🔄 平台数据同步 (RabbitMQ异步处理)
|
||||
- 🔄 平台数据同步 (数据库异步处理)
|
||||
- 🧩 可扩展架构 (支持后续添加支付、推送等功能)
|
||||
- 📊 灵活数据存储 (PostgreSQL + JSONB)
|
||||
- 📊 灵活数据存储 (MySQL + JSON)
|
||||
|
||||
## 认证方式
|
||||
使用JWT Bearer Token进行API认证
|
||||
|
||||
## AI模板系统
|
||||
- 🎨 动态模板管理 (数据库配置 + 代码执行)
|
||||
- 🚀 N8n工作流集成 (图片/视频生成)
|
||||
- 📊 使用统计分析 (性能监控 + 用户行为)
|
||||
- 🎛️ 运营管理后台 (A/B测试 + 个性化推荐)
|
||||
|
||||
## 响应格式
|
||||
所有API响应都遵循统一格式:
|
||||
\`\`\`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
|
||||
// src/main.ts
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
|
|
@ -584,7 +1008,7 @@ async function bootstrap() {
|
|||
bootstrap();
|
||||
```
|
||||
|
||||
## 8. 访问API文档
|
||||
## 9. 访问API文档
|
||||
|
||||
启动应用后,访问以下地址查看API文档:
|
||||
|
||||
|
|
@ -598,3 +1022,6 @@ API文档包含:
|
|||
- 📝 请求/响应示例
|
||||
- 🧪 在线接口测试功能
|
||||
- 📊 数据模型定义
|
||||
- 🎨 AI模板系统接口 (支持图片/视频生成)
|
||||
- 💾 动态模板配置管理
|
||||
- 📈 模板使用统计和监控
|
||||
|
|
|
|||
Loading…
Reference in New Issue