1598 lines
51 KiB
Markdown
1598 lines
51 KiB
Markdown
# 多平台小程序/H5/RN融合统一后台技术方案
|
||
|
||
## 项目概述
|
||
|
||
本项目是一个**AI图片/视频生成平台**的统一后台服务,支持微信、京东、百度、支付宝、字节跳动、QQ、飞书、快手等多个平台的小程序、H5和React Native应用。
|
||
|
||
### 核心业务功能
|
||
- **AI内容生成**: 用户上传图片+提示词,调用大模型生成图片/视频
|
||
- **积分系统**: 用户通过看广告获得积分,使用AI功能消耗积分
|
||
- **付费订阅**: 支持包月付费,获得更多积分和高级功能
|
||
- **多平台统一**: 抹平不同平台API差异,提供统一的服务接口
|
||
|
||
## 技术架构
|
||
|
||
### 1. 整体架构设计
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ 客户端层 (Client Layer) │
|
||
├─────────────────────────────────────────────────────────────┤
|
||
│ 微信小程序 │ 支付宝小程序 │ 百度小程序 │ 字节跳动小程序 │ H5 │ RN │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ API网关层 (API Gateway) │
|
||
├─────────────────────────────────────────────────────────────┤
|
||
│ 统一路由 │ 请求验证 │ 限流 │ 积分校验 │ 权限控制 │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ 业务服务层 (Business Layer) │
|
||
├─────────────────────────────────────────────────────────────┤
|
||
│ 用户服务 │ AI生成服务 │ 积分服务 │ 广告服务 │ 订阅服务 │ 文件服务 │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ 平台适配层 (Platform Adapter) │
|
||
├─────────────────────────────────────────────────────────────┤
|
||
│ 微信API │ 支付宝API │ 百度API │ 字节API │ 京东API │ QQ API │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ 第三方服务层 (External Services) │
|
||
├─────────────────────────────────────────────────────────────┤
|
||
│ 大模型API │ 文件存储 │ 广告平台 │ 支付网关 │ CDN服务 │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ 数据存储层 (Data Layer) │
|
||
├─────────────────────────────────────────────────────────────┤
|
||
│ MySQL │ 文件存储 │ 对象存储 │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 2. 核心模块设计
|
||
|
||
#### 2.1 平台识别模块 (Platform Detection)
|
||
- **功能**: 自动识别请求来源平台
|
||
- **实现方式**:
|
||
- 通过User-Agent识别
|
||
- 通过自定义Header标识
|
||
- 通过域名/路径前缀区分
|
||
|
||
#### 2.2 平台适配器模块 (Platform Adapters)
|
||
- **微信适配器**: 处理微信小程序/公众号API
|
||
- **支付宝适配器**: 处理支付宝小程序API
|
||
- **百度适配器**: 处理百度智能小程序API
|
||
- **字节跳动适配器**: 处理抖音/今日头条小程序API
|
||
- **京东适配器**: 处理京东小程序API
|
||
- **QQ适配器**: 处理QQ小程序API
|
||
- **飞书适配器**: 处理飞书小程序API
|
||
- **快手适配器**: 处理快手小程序API
|
||
|
||
#### 2.3 统一服务接口模块 (Unified Service Interface)
|
||
- **用户管理服务**: 统一用户登录、注册、信息管理
|
||
- **AI模板管理服务**: 统一模板注册、执行、参数验证
|
||
- **AI生成服务**: 图片/视频生成,调用大模型API
|
||
- **积分系统服务**: 积分获取、消耗、查询管理
|
||
- **广告服务**: 广告展示、观看验证、积分奖励
|
||
- **订阅服务**: 包月付费、会员权益管理
|
||
- **文件管理服务**: 图片上传、生成内容存储
|
||
|
||
## 技术选型
|
||
|
||
### 后端技术栈
|
||
- **框架**: NestJS (已选择)
|
||
- **数据库**: MySQL (主数据库)
|
||
- **ORM**: TypeORM (数据库操作)
|
||
|
||
### 开发工具
|
||
- **API文档**: Swagger/OpenAPI
|
||
- **代码质量**: ESLint + Prettier
|
||
- **测试**: Jest
|
||
- **容器化**: Docker
|
||
|
||
## 核心功能模块
|
||
|
||
### 1. 用户认证与授权
|
||
|
||
#### 1.1 统一登录流程
|
||
```typescript
|
||
interface UnifiedLoginRequest {
|
||
platform: PlatformType;
|
||
code: string;
|
||
userInfo?: any;
|
||
encryptedData?: string;
|
||
iv?: string;
|
||
}
|
||
|
||
interface UnifiedLoginResponse {
|
||
token: string;
|
||
refreshToken: string;
|
||
userInfo: UnifiedUserInfo;
|
||
platformSpecific?: any;
|
||
}
|
||
```
|
||
|
||
#### 1.2 平台差异处理
|
||
- **微信**: 使用code换取session_key和openid
|
||
- **支付宝**: 使用auth_code获取用户信息
|
||
- **百度**: 使用authorization_code获取access_token
|
||
- **字节跳动**: 使用code获取用户信息
|
||
- **其他平台**: 根据各自OAuth流程处理
|
||
|
||
### 2. 扩展服务预留
|
||
|
||
#### 2.1 服务扩展架构
|
||
```typescript
|
||
// 预留的服务接口基类
|
||
interface BaseService {
|
||
platform: PlatformType;
|
||
userId: string;
|
||
timestamp: Date;
|
||
}
|
||
|
||
// 扩展服务示例接口 (后续实现)
|
||
interface ExtensionServiceInterface {
|
||
// 支付服务接口 (预留)
|
||
payment?: PaymentServiceInterface;
|
||
|
||
// 推送服务接口 (预留)
|
||
push?: PushServiceInterface;
|
||
|
||
// 订单服务接口 (预留)
|
||
order?: OrderServiceInterface;
|
||
}
|
||
```
|
||
|
||
#### 2.2 模块化扩展设计
|
||
- **插件化架构**: 支持动态加载新的服务模块
|
||
- **统一接口标准**: 所有扩展服务遵循统一的接口规范
|
||
- **平台适配层**: 每个服务都有对应的平台适配器
|
||
- **配置驱动**: 通过配置文件控制服务的启用和禁用
|
||
|
||
### 3. 异步任务处理服务
|
||
|
||
#### 3.1 数据库异步处理
|
||
- **功能**: 使用数据库任务队列替代消息队列
|
||
- **实现方式**: 通过数据库表存储待处理任务,定时处理器执行
|
||
|
||
#### 3.2 业务异步处理
|
||
```typescript
|
||
// 用户注册异步处理
|
||
async processUserRegistration(userId: string, platform: string) {
|
||
// 用户数据处理
|
||
await this.userService.processRegistration(userId);
|
||
|
||
// 发放新用户积分奖励
|
||
await this.creditService.grantNewUserBonus(userId, platform);
|
||
|
||
// 同步到各平台
|
||
await this.platformService.syncUserData({ userId, platform });
|
||
}
|
||
|
||
// AI生成任务异步处理
|
||
async processAIGenerationTask(taskId: string) {
|
||
// 处理AI生成任务
|
||
await this.generationService.processGenerationTask(taskId);
|
||
}
|
||
|
||
// 广告观看完成异步处理
|
||
async processAdWatchCompleted(adWatchId: string, watchDuration: number, adData: any) {
|
||
// 发放广告观看奖励
|
||
await this.adService.completeAdWatch(adWatchId, watchDuration, adData);
|
||
}
|
||
```
|
||
|
||
## 数据库设计 (MySQL + TypeORM)
|
||
|
||
### 1. 用户实体 (User Entity)
|
||
```typescript
|
||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, OneToMany } from 'typeorm';
|
||
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 { UserSubscription } from './user-subscription.entity';
|
||
import { AdWatch } from './ad-watch.entity';
|
||
// import { TemplateExecution } from './template-execution.entity'; // 已移除,使用面向对象模板系统
|
||
|
||
@Entity('users')
|
||
export class User {
|
||
@PrimaryGeneratedColumn('uuid')
|
||
id: string;
|
||
|
||
@Column({ unique: true, length: 64 })
|
||
unifiedUserId: string;
|
||
|
||
@Column({ length: 100, nullable: true })
|
||
nickname: string;
|
||
|
||
@Column({ length: 500, nullable: true })
|
||
avatarUrl: string;
|
||
|
||
@Column({ length: 20, nullable: true })
|
||
phone: string;
|
||
|
||
@Column({ length: 100, nullable: true })
|
||
email: string;
|
||
|
||
@Column({ type: 'smallint', default: 1 })
|
||
status: number;
|
||
|
||
@CreateDateColumn()
|
||
createdAt: Date;
|
||
|
||
@UpdateDateColumn()
|
||
updatedAt: Date;
|
||
|
||
@OneToMany(() => PlatformUser, platformUser => platformUser.user)
|
||
platformUsers: PlatformUser[];
|
||
|
||
@OneToMany(() => ExtensionData, extensionData => extensionData.user)
|
||
extensionData: ExtensionData[];
|
||
|
||
@OneToMany(() => UserCredit, userCredit => userCredit.user)
|
||
credits: UserCredit[];
|
||
|
||
@OneToMany(() => GenerationTask, generationTask => generationTask.user)
|
||
generationTasks: GenerationTask[];
|
||
|
||
@OneToMany(() => UserSubscription, userSubscription => userSubscription.user)
|
||
subscriptions: UserSubscription[];
|
||
|
||
@OneToMany(() => AdWatch, adWatch => adWatch.user)
|
||
adWatches: AdWatch[];
|
||
|
||
// 注意: 模板执行记录通过面向对象方式管理,不存储在数据库中
|
||
}
|
||
```
|
||
|
||
### 2. 平台用户关联实体 (PlatformUser Entity)
|
||
```typescript
|
||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, ManyToOne, JoinColumn, Index } from 'typeorm';
|
||
import { User } from './user.entity';
|
||
|
||
export enum PlatformType {
|
||
WECHAT = 'wechat',
|
||
ALIPAY = 'alipay',
|
||
BAIDU = 'baidu',
|
||
BYTEDANCE = 'bytedance',
|
||
JD = 'jd',
|
||
QQ = 'qq',
|
||
FEISHU = 'feishu',
|
||
KUAISHOU = 'kuaishou',
|
||
H5 = 'h5',
|
||
RN = 'rn'
|
||
}
|
||
|
||
@Entity('platform_users')
|
||
@Index(['platform', 'platformUserId'], { unique: true })
|
||
export class PlatformUser {
|
||
@PrimaryGeneratedColumn('uuid')
|
||
id: string;
|
||
|
||
@Column()
|
||
userId: string;
|
||
|
||
@Column({
|
||
type: 'enum',
|
||
enum: PlatformType
|
||
})
|
||
platform: PlatformType;
|
||
|
||
@Column({ length: 100 })
|
||
platformUserId: string;
|
||
|
||
@Column({ type: 'json', nullable: true })
|
||
platformData: any;
|
||
|
||
@Column({ length: 500, nullable: true })
|
||
accessToken: string;
|
||
|
||
@Column({ length: 500, nullable: true })
|
||
refreshToken: string;
|
||
|
||
@Column({ type: 'timestamp', nullable: true })
|
||
expiresAt: Date;
|
||
|
||
@CreateDateColumn()
|
||
createdAt: Date;
|
||
|
||
@UpdateDateColumn()
|
||
updatedAt: Date;
|
||
|
||
@ManyToOne(() => User, user => user.platformUsers, { onDelete: 'CASCADE' })
|
||
@JoinColumn({ name: 'userId' })
|
||
user: User;
|
||
}
|
||
```
|
||
|
||
### 3. 扩展数据实体 (Extension Entity - 预留)
|
||
```typescript
|
||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, ManyToOne, JoinColumn } from 'typeorm';
|
||
import { User } from './user.entity';
|
||
import { PlatformType } from './platform-user.entity';
|
||
|
||
// 通用扩展数据实体 (用于存储各种扩展功能的数据)
|
||
@Entity('extension_data')
|
||
export class ExtensionData {
|
||
@PrimaryGeneratedColumn('uuid')
|
||
id: string;
|
||
|
||
@Column()
|
||
userId: string;
|
||
|
||
@Column({
|
||
type: 'enum',
|
||
enum: PlatformType
|
||
})
|
||
platform: PlatformType;
|
||
|
||
@Column({ length: 50 })
|
||
dataType: string; // 'order', 'payment', 'push', 'custom' 等
|
||
|
||
@Column({ length: 100, nullable: true })
|
||
referenceId: string; // 外部引用ID
|
||
|
||
@Column({ type: 'json' })
|
||
data: any; // 灵活的JSON数据存储
|
||
|
||
@Column({ type: 'json', nullable: true })
|
||
metadata: any; // 元数据
|
||
|
||
@Column({ length: 20, default: 'active' })
|
||
status: string;
|
||
|
||
@CreateDateColumn()
|
||
createdAt: Date;
|
||
|
||
@UpdateDateColumn()
|
||
updatedAt: Date;
|
||
|
||
@ManyToOne(() => User, user => user.extensionData, { onDelete: 'CASCADE' })
|
||
@JoinColumn({ name: 'userId' })
|
||
user: User;
|
||
}
|
||
```
|
||
|
||
### 4. 用户积分实体 (UserCredit Entity)
|
||
```typescript
|
||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, ManyToOne, JoinColumn } from 'typeorm';
|
||
import { User } from './user.entity';
|
||
import { PlatformType } from './platform-user.entity';
|
||
|
||
export enum CreditType {
|
||
REWARD = 'reward', // 奖励获得
|
||
CONSUME = 'consume', // 消费扣除
|
||
PURCHASE = 'purchase', // 购买获得
|
||
REFUND = 'refund' // 退款返还
|
||
}
|
||
|
||
export enum CreditSource {
|
||
AD_WATCH = 'ad_watch', // 看广告
|
||
SUBSCRIPTION = 'subscription', // 订阅赠送
|
||
AI_GENERATION = 'ai_generation', // AI生成消费
|
||
MANUAL = 'manual' // 手动调整
|
||
}
|
||
|
||
@Entity('user_credits')
|
||
export class UserCredit {
|
||
@PrimaryGeneratedColumn('uuid')
|
||
id: string;
|
||
|
||
@Column()
|
||
userId: string;
|
||
|
||
@Column({
|
||
type: 'enum',
|
||
enum: PlatformType
|
||
})
|
||
platform: PlatformType;
|
||
|
||
@Column({
|
||
type: 'enum',
|
||
enum: CreditType
|
||
})
|
||
type: CreditType;
|
||
|
||
@Column({
|
||
type: 'enum',
|
||
enum: CreditSource
|
||
})
|
||
source: CreditSource;
|
||
|
||
@Column({ type: 'integer' })
|
||
amount: number; // 正数为增加,负数为扣除
|
||
|
||
@Column({ type: 'integer' })
|
||
balance: number; // 操作后余额
|
||
|
||
@Column({ length: 200, nullable: true })
|
||
description: string;
|
||
|
||
@Column({ length: 100, nullable: true })
|
||
referenceId: string; // 关联的业务ID(如广告ID、任务ID等)
|
||
|
||
@Column({ type: 'json', nullable: true })
|
||
metadata: any;
|
||
|
||
@CreateDateColumn()
|
||
createdAt: Date;
|
||
|
||
@UpdateDateColumn()
|
||
updatedAt: Date;
|
||
|
||
@ManyToOne(() => User, user => user.credits, { onDelete: 'CASCADE' })
|
||
@JoinColumn({ name: 'userId' })
|
||
user: User;
|
||
}
|
||
```
|
||
|
||
### 5. AI生成任务实体 (GenerationTask Entity)
|
||
```typescript
|
||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, ManyToOne, JoinColumn } from 'typeorm';
|
||
import { User } from './user.entity';
|
||
import { PlatformType } from './platform-user.entity';
|
||
|
||
export enum GenerationType {
|
||
IMAGE = 'image',
|
||
VIDEO = 'video'
|
||
}
|
||
|
||
export enum TaskStatus {
|
||
PENDING = 'pending', // 等待处理
|
||
PROCESSING = 'processing', // 处理中
|
||
COMPLETED = 'completed', // 已完成
|
||
FAILED = 'failed', // 失败
|
||
CANCELLED = 'cancelled' // 已取消
|
||
}
|
||
|
||
@Entity('generation_tasks')
|
||
export class GenerationTask {
|
||
@PrimaryGeneratedColumn('uuid')
|
||
id: string;
|
||
|
||
@Column()
|
||
userId: string;
|
||
|
||
@Column({
|
||
type: 'enum',
|
||
enum: PlatformType
|
||
})
|
||
platform: PlatformType;
|
||
|
||
@Column({
|
||
type: 'enum',
|
||
enum: GenerationType
|
||
})
|
||
type: GenerationType;
|
||
|
||
@Column({ type: 'text' })
|
||
prompt: string; // 用户输入的提示词
|
||
|
||
@Column({ length: 500, nullable: true })
|
||
inputImageUrl: string; // 输入图片URL
|
||
|
||
@Column({ length: 500, nullable: true })
|
||
outputUrl: string; // 生成结果URL
|
||
|
||
@Column({ length: 500, nullable: true })
|
||
thumbnailUrl: string; // 缩略图URL
|
||
|
||
@Column({
|
||
type: 'enum',
|
||
enum: TaskStatus,
|
||
default: TaskStatus.PENDING
|
||
})
|
||
status: TaskStatus;
|
||
|
||
@Column({ type: 'integer' })
|
||
creditCost: number; // 消耗的积分
|
||
|
||
@Column({ length: 100, nullable: true })
|
||
aiModelId: string; // 使用的AI模型ID
|
||
|
||
@Column({ type: 'json', nullable: true })
|
||
parameters: any; // 生成参数
|
||
|
||
@Column({ type: 'text', nullable: true })
|
||
errorMessage: string; // 错误信息
|
||
|
||
@Column({ type: 'integer', nullable: true })
|
||
processingTime: number; // 处理时间(秒)
|
||
|
||
@CreateDateColumn()
|
||
createdAt: Date;
|
||
|
||
@UpdateDateColumn()
|
||
updatedAt: Date;
|
||
|
||
@ManyToOne(() => User, user => user.generationTasks, { onDelete: 'CASCADE' })
|
||
@JoinColumn({ name: 'userId' })
|
||
user: User;
|
||
}
|
||
```
|
||
|
||
### 6. 用户订阅实体 (UserSubscription Entity)
|
||
```typescript
|
||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, ManyToOne, JoinColumn } from 'typeorm';
|
||
import { User } from './user.entity';
|
||
import { PlatformType } from './platform-user.entity';
|
||
|
||
export enum SubscriptionPlan {
|
||
BASIC = 'basic', // 基础版
|
||
PREMIUM = 'premium', // 高级版
|
||
PRO = 'pro' // 专业版
|
||
}
|
||
|
||
export enum SubscriptionStatus {
|
||
ACTIVE = 'active', // 激活中
|
||
EXPIRED = 'expired', // 已过期
|
||
CANCELLED = 'cancelled', // 已取消
|
||
PENDING = 'pending' // 待激活
|
||
}
|
||
|
||
@Entity('user_subscriptions')
|
||
export class UserSubscription {
|
||
@PrimaryGeneratedColumn('uuid')
|
||
id: string;
|
||
|
||
@Column()
|
||
userId: string;
|
||
|
||
@Column({
|
||
type: 'enum',
|
||
enum: PlatformType
|
||
})
|
||
platform: PlatformType;
|
||
|
||
@Column({
|
||
type: 'enum',
|
||
enum: SubscriptionPlan
|
||
})
|
||
plan: SubscriptionPlan;
|
||
|
||
@Column({
|
||
type: 'enum',
|
||
enum: SubscriptionStatus,
|
||
default: SubscriptionStatus.PENDING
|
||
})
|
||
status: SubscriptionStatus;
|
||
|
||
@Column({ type: 'timestamp' })
|
||
startDate: Date;
|
||
|
||
@Column({ type: 'timestamp' })
|
||
endDate: Date;
|
||
|
||
@Column({ type: 'decimal', precision: 10, scale: 2 })
|
||
price: number; // 订阅价格
|
||
|
||
@Column({ length: 10, default: 'CNY' })
|
||
currency: string;
|
||
|
||
@Column({ type: 'integer' })
|
||
monthlyCredits: number; // 每月赠送积分
|
||
|
||
@Column({ length: 100, nullable: true })
|
||
paymentId: string; // 支付订单ID
|
||
|
||
@Column({ type: 'json', nullable: true })
|
||
features: any; // 订阅特权
|
||
|
||
@Column({ type: 'boolean', default: true })
|
||
autoRenew: boolean; // 自动续费
|
||
|
||
@CreateDateColumn()
|
||
createdAt: Date;
|
||
|
||
@UpdateDateColumn()
|
||
updatedAt: Date;
|
||
|
||
@ManyToOne(() => User, user => user.subscriptions, { onDelete: 'CASCADE' })
|
||
@JoinColumn({ name: 'userId' })
|
||
user: User;
|
||
}
|
||
```
|
||
|
||
### 7. 广告观看记录实体 (AdWatch Entity)
|
||
```typescript
|
||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, ManyToOne, JoinColumn } from 'typeorm';
|
||
import { User } from './user.entity';
|
||
import { PlatformType } from './platform-user.entity';
|
||
|
||
export enum AdType {
|
||
BANNER = 'banner', // 横幅广告
|
||
INTERSTITIAL = 'interstitial', // 插屏广告
|
||
REWARDED = 'rewarded', // 激励视频广告
|
||
NATIVE = 'native' // 原生广告
|
||
}
|
||
|
||
export enum AdStatus {
|
||
STARTED = 'started', // 开始观看
|
||
COMPLETED = 'completed', // 观看完成
|
||
SKIPPED = 'skipped', // 跳过
|
||
FAILED = 'failed' // 失败
|
||
}
|
||
|
||
@Entity('ad_watches')
|
||
export class AdWatch {
|
||
@PrimaryGeneratedColumn('uuid')
|
||
id: string;
|
||
|
||
@Column()
|
||
userId: string;
|
||
|
||
@Column({
|
||
type: 'enum',
|
||
enum: PlatformType
|
||
})
|
||
platform: PlatformType;
|
||
|
||
@Column({
|
||
type: 'enum',
|
||
enum: AdType
|
||
})
|
||
adType: AdType;
|
||
|
||
@Column({ length: 100 })
|
||
adId: string; // 广告ID
|
||
|
||
@Column({ length: 100, nullable: true })
|
||
adUnitId: string; // 广告位ID
|
||
|
||
@Column({
|
||
type: 'enum',
|
||
enum: AdStatus
|
||
})
|
||
status: AdStatus;
|
||
|
||
@Column({ type: 'integer', nullable: true })
|
||
watchDuration: number; // 观看时长(秒)
|
||
|
||
@Column({ type: 'integer', nullable: true })
|
||
rewardCredits: number; // 奖励积分
|
||
|
||
@Column({ type: 'json', nullable: true })
|
||
adData: any; // 广告相关数据
|
||
|
||
@CreateDateColumn()
|
||
createdAt: Date;
|
||
|
||
@UpdateDateColumn()
|
||
updatedAt: Date;
|
||
|
||
@ManyToOne(() => User, user => user.adWatches, { onDelete: 'CASCADE' })
|
||
@JoinColumn({ name: 'userId' })
|
||
user: User;
|
||
}
|
||
```
|
||
|
||
### 8. AI生成任务与模板系统集成说明
|
||
|
||
**重要说明**: 本项目采用面向对象的模板管理系统,不使用数据库存储模板定义和执行记录。
|
||
|
||
#### 8.1 模板系统架构
|
||
- **模板定义**: 通过TypeScript类定义,编译时类型检查
|
||
- **模板管理**: 通过TemplateManager内存管理,支持动态注册/卸载
|
||
- **执行记录**: 通过GenerationTask表记录AI生成任务,模板信息作为metadata存储
|
||
|
||
#### 8.2 与GenerationTask的关系
|
||
```typescript
|
||
// GenerationTask实体中存储模板相关信息
|
||
@Entity('generation_tasks')
|
||
export class GenerationTask {
|
||
// ... 其他字段
|
||
|
||
@Column({ type: 'json', nullable: true })
|
||
parameters: any; // 存储模板执行参数
|
||
|
||
@Column({ type: 'json', nullable: true })
|
||
metadata: any; // 存储模板代码、版本等信息
|
||
}
|
||
|
||
// 使用示例
|
||
const task = new GenerationTask();
|
||
task.metadata = {
|
||
templateCode: 'outfit_change_v1',
|
||
templateName: '智能换装',
|
||
templateVersion: '1.0.0',
|
||
executionId: 'exec_123456789'
|
||
};
|
||
```
|
||
|
||
### 4. TypeORM配置
|
||
```typescript
|
||
// src/config/database.config.ts
|
||
import { TypeOrmModuleOptions } from '@nestjs/typeorm';
|
||
import { ConfigService } from '@nestjs/config';
|
||
|
||
export const getDatabaseConfig = (configService: ConfigService): TypeOrmModuleOptions => ({
|
||
type: 'mysql',
|
||
host: configService.get('DB_HOST', 'localhost'),
|
||
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}'],
|
||
migrations: [__dirname + '/../migrations/*{.ts,.js}'],
|
||
synchronize: configService.get('NODE_ENV') === 'development',
|
||
logging: configService.get('NODE_ENV') === 'development',
|
||
ssl: configService.get('NODE_ENV') === 'production' ? { rejectUnauthorized: false } : false,
|
||
extra: {
|
||
max: 20, // 连接池最大连接数
|
||
idleTimeoutMillis: 30000, // 空闲连接超时时间
|
||
connectionTimeoutMillis: 2000, // 连接超时时间
|
||
},
|
||
});
|
||
```
|
||
|
||
### 5. 数据库迁移示例
|
||
```typescript
|
||
// src/migrations/001-create-initial-tables.ts
|
||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||
|
||
export class CreateInitialTables1703001000000 implements MigrationInterface {
|
||
name = 'CreateInitialTables1703001000000';
|
||
|
||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||
// 创建用户表
|
||
await queryRunner.query(`
|
||
CREATE TABLE "users" (
|
||
"id" uuid NOT NULL DEFAULT uuid_generate_v4(),
|
||
"unifiedUserId" character varying(64) NOT NULL,
|
||
"nickname" character varying(100),
|
||
"avatarUrl" character varying(500),
|
||
"phone" character varying(20),
|
||
"email" character varying(100),
|
||
"status" smallint NOT NULL DEFAULT '1',
|
||
"createdAt" TIMESTAMP NOT NULL DEFAULT now(),
|
||
"updatedAt" TIMESTAMP NOT NULL DEFAULT now(),
|
||
CONSTRAINT "UQ_users_unifiedUserId" UNIQUE ("unifiedUserId"),
|
||
CONSTRAINT "PK_users_id" PRIMARY KEY ("id")
|
||
)
|
||
`);
|
||
|
||
// 创建平台用户关联表
|
||
await queryRunner.query(`
|
||
CREATE TYPE "platform_type" AS ENUM(
|
||
'wechat', 'alipay', 'baidu', 'bytedance',
|
||
'jd', 'qq', 'feishu', 'kuaishou', 'h5', 'rn'
|
||
)
|
||
`);
|
||
|
||
await queryRunner.query(`
|
||
CREATE TABLE "platform_users" (
|
||
"id" uuid NOT NULL DEFAULT uuid_generate_v4(),
|
||
"userId" uuid NOT NULL,
|
||
"platform" "platform_type" NOT NULL,
|
||
"platformUserId" character varying(100) NOT NULL,
|
||
"platformData" json,
|
||
"accessToken" character varying(500),
|
||
"refreshToken" character varying(500),
|
||
"expiresAt" TIMESTAMP,
|
||
"createdAt" TIMESTAMP NOT NULL DEFAULT now(),
|
||
"updatedAt" TIMESTAMP NOT NULL DEFAULT now(),
|
||
CONSTRAINT "UQ_platform_users_platform_platformUserId" UNIQUE ("platform", "platformUserId"),
|
||
CONSTRAINT "PK_platform_users_id" PRIMARY KEY ("id"),
|
||
CONSTRAINT "FK_platform_users_userId" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE
|
||
)
|
||
`);
|
||
|
||
// 创建扩展数据表
|
||
await queryRunner.query(`
|
||
CREATE TABLE "extension_data" (
|
||
"id" uuid NOT NULL DEFAULT uuid_generate_v4(),
|
||
"userId" uuid NOT NULL,
|
||
"platform" "platform_type" NOT NULL,
|
||
"dataType" character varying(50) NOT NULL,
|
||
"referenceId" character varying(100),
|
||
"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(),
|
||
CONSTRAINT "PK_extension_data_id" PRIMARY KEY ("id"),
|
||
CONSTRAINT "FK_extension_data_userId" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE
|
||
)
|
||
`);
|
||
|
||
// 创建用户积分表
|
||
await queryRunner.query(`
|
||
CREATE TYPE "credit_type" AS ENUM('reward', 'consume', 'purchase', 'refund')
|
||
`);
|
||
|
||
await queryRunner.query(`
|
||
CREATE TYPE "credit_source" AS ENUM('ad_watch', 'subscription', 'ai_generation', 'manual')
|
||
`);
|
||
|
||
await queryRunner.query(`
|
||
CREATE TABLE "user_credits" (
|
||
"id" uuid NOT NULL DEFAULT uuid_generate_v4(),
|
||
"userId" uuid NOT NULL,
|
||
"platform" "platform_type" NOT NULL,
|
||
"type" "credit_type" NOT NULL,
|
||
"source" "credit_source" NOT NULL,
|
||
"amount" integer NOT NULL,
|
||
"balance" integer NOT NULL,
|
||
"description" character varying(200),
|
||
"referenceId" character varying(100),
|
||
"metadata" json,
|
||
"createdAt" TIMESTAMP NOT NULL DEFAULT now(),
|
||
"updatedAt" TIMESTAMP NOT NULL DEFAULT now(),
|
||
CONSTRAINT "PK_user_credits_id" PRIMARY KEY ("id"),
|
||
CONSTRAINT "FK_user_credits_userId" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE
|
||
)
|
||
`);
|
||
|
||
// 创建AI生成任务表
|
||
await queryRunner.query(`
|
||
CREATE TYPE "generation_type" AS ENUM('image', 'video')
|
||
`);
|
||
|
||
await queryRunner.query(`
|
||
CREATE TYPE "task_status" AS ENUM('pending', 'processing', 'completed', 'failed', 'cancelled')
|
||
`);
|
||
|
||
await queryRunner.query(`
|
||
CREATE TABLE "generation_tasks" (
|
||
"id" uuid NOT NULL DEFAULT uuid_generate_v4(),
|
||
"userId" uuid NOT NULL,
|
||
"platform" "platform_type" NOT NULL,
|
||
"type" "generation_type" NOT NULL,
|
||
"prompt" text NOT NULL,
|
||
"inputImageUrl" character varying(500),
|
||
"outputUrl" character varying(500),
|
||
"thumbnailUrl" character varying(500),
|
||
"status" "task_status" NOT NULL DEFAULT 'pending',
|
||
"creditCost" integer NOT NULL,
|
||
"aiModelId" character varying(100),
|
||
"parameters" json,
|
||
"errorMessage" text,
|
||
"processingTime" integer,
|
||
"createdAt" TIMESTAMP NOT NULL DEFAULT now(),
|
||
"updatedAt" TIMESTAMP NOT NULL DEFAULT now(),
|
||
CONSTRAINT "PK_generation_tasks_id" PRIMARY KEY ("id"),
|
||
CONSTRAINT "FK_generation_tasks_userId" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE
|
||
)
|
||
`);
|
||
|
||
// 创建用户订阅表
|
||
await queryRunner.query(`
|
||
CREATE TYPE "subscription_plan" AS ENUM('basic', 'premium', 'pro')
|
||
`);
|
||
|
||
await queryRunner.query(`
|
||
CREATE TYPE "subscription_status" AS ENUM('active', 'expired', 'cancelled', 'pending')
|
||
`);
|
||
|
||
await queryRunner.query(`
|
||
CREATE TABLE "user_subscriptions" (
|
||
"id" uuid NOT NULL DEFAULT uuid_generate_v4(),
|
||
"userId" uuid NOT NULL,
|
||
"platform" "platform_type" NOT NULL,
|
||
"plan" "subscription_plan" NOT NULL,
|
||
"status" "subscription_status" NOT NULL DEFAULT 'pending',
|
||
"startDate" TIMESTAMP NOT NULL,
|
||
"endDate" TIMESTAMP NOT NULL,
|
||
"price" decimal(10,2) NOT NULL,
|
||
"currency" character varying(10) NOT NULL DEFAULT 'CNY',
|
||
"monthlyCredits" integer NOT NULL,
|
||
"paymentId" character varying(100),
|
||
"features" json,
|
||
"autoRenew" boolean NOT NULL DEFAULT true,
|
||
"createdAt" TIMESTAMP NOT NULL DEFAULT now(),
|
||
"updatedAt" TIMESTAMP NOT NULL DEFAULT now(),
|
||
CONSTRAINT "PK_user_subscriptions_id" PRIMARY KEY ("id"),
|
||
CONSTRAINT "FK_user_subscriptions_userId" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE
|
||
)
|
||
`);
|
||
|
||
// 创建广告观看记录表
|
||
await queryRunner.query(`
|
||
CREATE TYPE "ad_type" AS ENUM('banner', 'interstitial', 'rewarded', 'native')
|
||
`);
|
||
|
||
await queryRunner.query(`
|
||
CREATE TYPE "ad_status" AS ENUM('started', 'completed', 'skipped', 'failed')
|
||
`);
|
||
|
||
await queryRunner.query(`
|
||
CREATE TABLE "ad_watches" (
|
||
"id" uuid NOT NULL DEFAULT uuid_generate_v4(),
|
||
"userId" uuid NOT NULL,
|
||
"platform" "platform_type" NOT NULL,
|
||
"adType" "ad_type" NOT NULL,
|
||
"adId" character varying(100) NOT NULL,
|
||
"adUnitId" character varying(100),
|
||
"status" "ad_status" NOT NULL,
|
||
"watchDuration" integer,
|
||
"rewardCredits" integer,
|
||
"adData" json,
|
||
"createdAt" TIMESTAMP NOT NULL DEFAULT now(),
|
||
"updatedAt" TIMESTAMP NOT NULL DEFAULT now(),
|
||
CONSTRAINT "PK_ad_watches_id" PRIMARY KEY ("id"),
|
||
CONSTRAINT "FK_ad_watches_userId" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE
|
||
)
|
||
`);
|
||
|
||
// 注意: AI模板系统采用面向对象设计,不需要数据库表
|
||
// 模板定义通过TypeScript类管理,执行记录通过GenerationTask表的metadata字段存储
|
||
|
||
// 创建索引
|
||
await queryRunner.query(`CREATE INDEX "IDX_platform_users_platform" ON "platform_users" ("platform")`);
|
||
await queryRunner.query(`CREATE INDEX "IDX_platform_users_userId" ON "platform_users" ("userId")`);
|
||
await queryRunner.query(`CREATE INDEX "IDX_extension_data_userId" ON "extension_data" ("userId")`);
|
||
await queryRunner.query(`CREATE INDEX "IDX_extension_data_platform" ON "extension_data" ("platform")`);
|
||
await queryRunner.query(`CREATE INDEX "IDX_extension_data_dataType" ON "extension_data" ("dataType")`);
|
||
await queryRunner.query(`CREATE INDEX "IDX_extension_data_referenceId" ON "extension_data" ("referenceId")`);
|
||
await queryRunner.query(`CREATE INDEX "IDX_extension_data_status" ON "extension_data" ("status")`);
|
||
await queryRunner.query(`CREATE INDEX "IDX_extension_data_createdAt" ON "extension_data" ("createdAt")`);
|
||
|
||
// 用户积分表索引
|
||
await queryRunner.query(`CREATE INDEX "IDX_user_credits_userId" ON "user_credits" ("userId")`);
|
||
await queryRunner.query(`CREATE INDEX "IDX_user_credits_platform" ON "user_credits" ("platform")`);
|
||
await queryRunner.query(`CREATE INDEX "IDX_user_credits_type" ON "user_credits" ("type")`);
|
||
await queryRunner.query(`CREATE INDEX "IDX_user_credits_source" ON "user_credits" ("source")`);
|
||
await queryRunner.query(`CREATE INDEX "IDX_user_credits_createdAt" ON "user_credits" ("createdAt")`);
|
||
|
||
// AI生成任务表索引
|
||
await queryRunner.query(`CREATE INDEX "IDX_generation_tasks_userId" ON "generation_tasks" ("userId")`);
|
||
await queryRunner.query(`CREATE INDEX "IDX_generation_tasks_platform" ON "generation_tasks" ("platform")`);
|
||
await queryRunner.query(`CREATE INDEX "IDX_generation_tasks_type" ON "generation_tasks" ("type")`);
|
||
await queryRunner.query(`CREATE INDEX "IDX_generation_tasks_status" ON "generation_tasks" ("status")`);
|
||
await queryRunner.query(`CREATE INDEX "IDX_generation_tasks_createdAt" ON "generation_tasks" ("createdAt")`);
|
||
|
||
// 用户订阅表索引
|
||
await queryRunner.query(`CREATE INDEX "IDX_user_subscriptions_userId" ON "user_subscriptions" ("userId")`);
|
||
await queryRunner.query(`CREATE INDEX "IDX_user_subscriptions_platform" ON "user_subscriptions" ("platform")`);
|
||
await queryRunner.query(`CREATE INDEX "IDX_user_subscriptions_status" ON "user_subscriptions" ("status")`);
|
||
await queryRunner.query(`CREATE INDEX "IDX_user_subscriptions_endDate" ON "user_subscriptions" ("endDate")`);
|
||
|
||
// 广告观看记录表索引
|
||
await queryRunner.query(`CREATE INDEX "IDX_ad_watches_userId" ON "ad_watches" ("userId")`);
|
||
await queryRunner.query(`CREATE INDEX "IDX_ad_watches_platform" ON "ad_watches" ("platform")`);
|
||
await queryRunner.query(`CREATE INDEX "IDX_ad_watches_adType" ON "ad_watches" ("adType")`);
|
||
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")`);
|
||
|
||
// 注意: 模板系统索引已移除,使用面向对象管理
|
||
}
|
||
|
||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||
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 "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"`);
|
||
}
|
||
}
|
||
```
|
||
|
||
### 6. Repository模式示例
|
||
```typescript
|
||
// src/repositories/user.repository.ts
|
||
import { Injectable } from '@nestjs/common';
|
||
import { InjectRepository } from '@nestjs/typeorm';
|
||
import { Repository } from 'typeorm';
|
||
import { User } from '../entities/user.entity';
|
||
import { PlatformUser, PlatformType } from '../entities/platform-user.entity';
|
||
import { ExtensionData } from '../entities/extension-data.entity';
|
||
|
||
@Injectable()
|
||
export class UserRepository {
|
||
constructor(
|
||
@InjectRepository(User)
|
||
private readonly userRepository: Repository<User>,
|
||
@InjectRepository(PlatformUser)
|
||
private readonly platformUserRepository: Repository<PlatformUser>,
|
||
@InjectRepository(ExtensionData)
|
||
private readonly extensionDataRepository: Repository<ExtensionData>,
|
||
) {}
|
||
|
||
async findByUnifiedUserId(unifiedUserId: string): Promise<User | null> {
|
||
return this.userRepository.findOne({
|
||
where: { unifiedUserId },
|
||
relations: ['platformUsers', 'extensionData'],
|
||
});
|
||
}
|
||
|
||
async findByPlatformUser(platform: PlatformType, platformUserId: string): Promise<User | null> {
|
||
const platformUser = await this.platformUserRepository.findOne({
|
||
where: { platform, platformUserId },
|
||
relations: ['user'],
|
||
});
|
||
return platformUser?.user || null;
|
||
}
|
||
|
||
async createUser(userData: Partial<User>): Promise<User> {
|
||
const user = this.userRepository.create(userData);
|
||
return this.userRepository.save(user);
|
||
}
|
||
|
||
async createPlatformUser(platformUserData: Partial<PlatformUser>): Promise<PlatformUser> {
|
||
const platformUser = this.platformUserRepository.create(platformUserData);
|
||
return this.platformUserRepository.save(platformUser);
|
||
}
|
||
|
||
async updatePlatformUser(
|
||
platform: PlatformType,
|
||
platformUserId: string,
|
||
updateData: Partial<PlatformUser>
|
||
): Promise<PlatformUser> {
|
||
await this.platformUserRepository.update(
|
||
{ platform, platformUserId },
|
||
updateData
|
||
);
|
||
return this.platformUserRepository.findOne({
|
||
where: { platform, platformUserId },
|
||
});
|
||
}
|
||
|
||
// 扩展数据操作方法
|
||
async createExtensionData(extensionData: Partial<ExtensionData>): Promise<ExtensionData> {
|
||
const data = this.extensionDataRepository.create(extensionData);
|
||
return this.extensionDataRepository.save(data);
|
||
}
|
||
|
||
async findExtensionData(
|
||
userId: string,
|
||
dataType: string,
|
||
platform?: PlatformType
|
||
): Promise<ExtensionData[]> {
|
||
const where: any = { userId, dataType };
|
||
if (platform) {
|
||
where.platform = platform;
|
||
}
|
||
return this.extensionDataRepository.find({ where });
|
||
}
|
||
|
||
async updateExtensionData(
|
||
id: string,
|
||
updateData: Partial<ExtensionData>
|
||
): Promise<ExtensionData> {
|
||
await this.extensionDataRepository.update(id, updateData);
|
||
return this.extensionDataRepository.findOne({ where: { id } });
|
||
}
|
||
}
|
||
```
|
||
|
||
## API设计规范
|
||
|
||
### 1. 统一请求格式
|
||
```typescript
|
||
interface UnifiedRequest<T = any> {
|
||
platform: PlatformType;
|
||
version: string;
|
||
timestamp: number;
|
||
signature?: string;
|
||
data: T;
|
||
}
|
||
```
|
||
|
||
### 2. 统一响应格式
|
||
```typescript
|
||
interface UnifiedResponse<T = any> {
|
||
code: number;
|
||
message: string;
|
||
data: T;
|
||
timestamp: number;
|
||
traceId: string;
|
||
}
|
||
```
|
||
|
||
### 3. 错误码规范
|
||
- 1000-1999: 系统级错误
|
||
- 2000-2999: 业务级错误
|
||
- 3000-3999: 平台特定错误
|
||
- 4000-4999: 客户端错误
|
||
- 5000-5999: 服务端错误
|
||
|
||
## 部署架构
|
||
|
||
### 1. 微服务部署
|
||
```yaml
|
||
# docker-compose.yml 示例
|
||
version: '3.8'
|
||
services:
|
||
api-gateway:
|
||
image: nginx:alpine
|
||
ports:
|
||
- "80:80"
|
||
- "443:443"
|
||
|
||
main-service:
|
||
build: .
|
||
environment:
|
||
- NODE_ENV=production
|
||
- DB_HOST=mysql
|
||
- DB_PORT=3306
|
||
- DB_USERNAME=${MYSQL_USER}
|
||
- DB_PASSWORD=${MYSQL_PASSWORD}
|
||
- DB_DATABASE=${MYSQL_DB}
|
||
depends_on:
|
||
- mysql
|
||
|
||
mysql:
|
||
image: mysql:8.0
|
||
environment:
|
||
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
|
||
MYSQL_USER: ${MYSQL_USER}
|
||
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
|
||
MYSQL_DATABASE: ${MYSQL_DB}
|
||
volumes:
|
||
- mysql_data:/var/lib/mysql
|
||
ports:
|
||
- "3306:3306"
|
||
|
||
volumes:
|
||
mysql_data:
|
||
```
|
||
|
||
### 2. 负载均衡配置
|
||
- 使用Nginx作为反向代理
|
||
- 支持多实例部署
|
||
- 健康检查和自动故障转移
|
||
|
||
|
||
## API文档配置 (Swagger/OpenAPI)
|
||
|
||
### 1. Swagger配置
|
||
```typescript
|
||
// src/config/swagger.config.ts
|
||
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
|
||
import { INestApplication } from '@nestjs/common';
|
||
|
||
export function setupSwagger(app: INestApplication): void {
|
||
const config = new DocumentBuilder()
|
||
.setTitle('多平台小程序统一后台API')
|
||
.setDescription('支持微信、支付宝、百度、字节跳动等多平台的统一后台服务')
|
||
.setVersion('1.0.0')
|
||
.addBearerAuth(
|
||
{
|
||
type: 'http',
|
||
scheme: 'bearer',
|
||
bearerFormat: 'JWT',
|
||
name: 'JWT',
|
||
description: 'Enter JWT token',
|
||
in: 'header',
|
||
},
|
||
'JWT-auth',
|
||
)
|
||
.addTag('用户管理', '用户注册、登录、信息管理')
|
||
.addTag('平台适配', '各平台特定接口和数据同步')
|
||
.addTag('扩展服务', '预留的扩展功能接口')
|
||
.addServer('http://localhost:3000', '开发环境')
|
||
.addServer('https://api.example.com', '生产环境')
|
||
.build();
|
||
|
||
const document = SwaggerModule.createDocument(app, config);
|
||
SwaggerModule.setup('api/docs', app, document, {
|
||
swaggerOptions: {
|
||
persistAuthorization: true,
|
||
tagsSorter: 'alpha',
|
||
operationsSorter: 'alpha',
|
||
},
|
||
customSiteTitle: '多平台API文档',
|
||
customfavIcon: '/favicon.ico',
|
||
customJs: [
|
||
'https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/4.15.5/swagger-ui-bundle.min.js',
|
||
'https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/4.15.5/swagger-ui-standalone-preset.min.js',
|
||
],
|
||
customCssUrl: [
|
||
'https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/4.15.5/swagger-ui.min.css',
|
||
],
|
||
});
|
||
}
|
||
```
|
||
|
||
### 2. DTO示例 (带Swagger装饰器)
|
||
```typescript
|
||
// src/dto/user-login.dto.ts
|
||
import { ApiProperty } from '@nestjs/swagger';
|
||
import { IsString, IsEnum, IsOptional } from 'class-validator';
|
||
import { PlatformType } from '../entities/platform-user.entity';
|
||
|
||
export class UserLoginDto {
|
||
@ApiProperty({
|
||
description: '平台类型',
|
||
enum: PlatformType,
|
||
example: PlatformType.WECHAT,
|
||
})
|
||
@IsEnum(PlatformType)
|
||
platform: PlatformType;
|
||
|
||
@ApiProperty({
|
||
description: '平台授权码',
|
||
example: '081234567890abcdef',
|
||
})
|
||
@IsString()
|
||
code: string;
|
||
|
||
@ApiProperty({
|
||
description: '加密用户数据',
|
||
required: false,
|
||
example: 'encrypted_user_data_string',
|
||
})
|
||
@IsOptional()
|
||
@IsString()
|
||
encryptedData?: string;
|
||
|
||
@ApiProperty({
|
||
description: '加密向量',
|
||
required: false,
|
||
example: 'iv_string',
|
||
})
|
||
@IsOptional()
|
||
@IsString()
|
||
iv?: string;
|
||
}
|
||
|
||
export class UserLoginResponseDto {
|
||
@ApiProperty({
|
||
description: 'JWT访问令牌',
|
||
example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
|
||
})
|
||
token: string;
|
||
|
||
@ApiProperty({
|
||
description: '刷新令牌',
|
||
example: 'refresh_token_string',
|
||
})
|
||
refreshToken: string;
|
||
|
||
@ApiProperty({
|
||
description: '用户信息',
|
||
type: 'object',
|
||
example: {
|
||
id: 'user-uuid',
|
||
nickname: '用户昵称',
|
||
avatarUrl: 'https://example.com/avatar.jpg',
|
||
},
|
||
})
|
||
userInfo: any;
|
||
}
|
||
```
|
||
|
||
### 3. Controller示例 (带Swagger装饰器)
|
||
```typescript
|
||
// src/controllers/user.controller.ts
|
||
import { Controller, Post, Body, Get, UseGuards } from '@nestjs/common';
|
||
import {
|
||
ApiTags,
|
||
ApiOperation,
|
||
ApiResponse,
|
||
ApiBearerAuth,
|
||
ApiBody,
|
||
} from '@nestjs/swagger';
|
||
import { UserLoginDto, UserLoginResponseDto } from '../dto/user-login.dto';
|
||
import { JwtAuthGuard } from '../guards/jwt-auth.guard';
|
||
import { CurrentUser } from '../decorators/current-user.decorator';
|
||
|
||
@ApiTags('用户管理')
|
||
@Controller('api/v1/users')
|
||
export class UserController {
|
||
@Post('login')
|
||
@ApiOperation({
|
||
summary: '用户登录',
|
||
description: '支持多平台用户登录,返回JWT令牌',
|
||
})
|
||
@ApiBody({
|
||
type: UserLoginDto,
|
||
description: '登录请求参数',
|
||
})
|
||
@ApiResponse({
|
||
status: 200,
|
||
description: '登录成功',
|
||
type: UserLoginResponseDto,
|
||
})
|
||
@ApiResponse({
|
||
status: 400,
|
||
description: '请求参数错误',
|
||
schema: {
|
||
type: 'object',
|
||
properties: {
|
||
code: { type: 'number', example: 400 },
|
||
message: { type: 'string', example: '请求参数错误' },
|
||
data: { type: 'null' },
|
||
},
|
||
},
|
||
})
|
||
@ApiResponse({
|
||
status: 401,
|
||
description: '授权失败',
|
||
schema: {
|
||
type: 'object',
|
||
properties: {
|
||
code: { type: 'number', example: 401 },
|
||
message: { type: 'string', example: '平台授权失败' },
|
||
data: { type: 'null' },
|
||
},
|
||
},
|
||
})
|
||
async login(@Body() loginDto: UserLoginDto) {
|
||
return this.userService.login(loginDto);
|
||
}
|
||
|
||
@Get('profile')
|
||
@UseGuards(JwtAuthGuard)
|
||
@ApiBearerAuth('JWT-auth')
|
||
@ApiOperation({
|
||
summary: '获取用户信息',
|
||
description: '获取当前登录用户的详细信息',
|
||
})
|
||
@ApiResponse({
|
||
status: 200,
|
||
description: '获取成功',
|
||
schema: {
|
||
type: 'object',
|
||
properties: {
|
||
code: { type: 'number', example: 200 },
|
||
message: { type: 'string', example: '获取成功' },
|
||
data: {
|
||
type: 'object',
|
||
properties: {
|
||
id: { type: 'string', example: 'user-uuid' },
|
||
nickname: { type: 'string', example: '用户昵称' },
|
||
avatarUrl: { type: 'string', example: 'https://example.com/avatar.jpg' },
|
||
phone: { type: 'string', example: '13800138000' },
|
||
email: { type: 'string', example: 'user@example.com' },
|
||
},
|
||
},
|
||
},
|
||
},
|
||
})
|
||
@ApiResponse({
|
||
status: 401,
|
||
description: '未授权',
|
||
schema: {
|
||
type: 'object',
|
||
properties: {
|
||
code: { type: 'number', example: 401 },
|
||
message: { type: 'string', example: '未授权访问' },
|
||
data: { type: 'null' },
|
||
},
|
||
},
|
||
})
|
||
async getProfile(@CurrentUser() user: any) {
|
||
return this.userService.getProfile(user.id);
|
||
}
|
||
}
|
||
```
|
||
|
||
### 4. 扩展服务接口文档示例
|
||
```typescript
|
||
// src/controllers/extension.controller.ts
|
||
import { Controller, Post, Body, Get, Param, Query } from '@nestjs/common';
|
||
import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiQuery } from '@nestjs/swagger';
|
||
|
||
@ApiTags('扩展服务')
|
||
@Controller('api/v1/extensions')
|
||
export class ExtensionController {
|
||
@Post('data')
|
||
@ApiOperation({
|
||
summary: '创建扩展数据',
|
||
description: '为用户创建扩展功能数据,支持多种数据类型',
|
||
})
|
||
@ApiResponse({
|
||
status: 200,
|
||
description: '数据创建成功',
|
||
schema: {
|
||
type: 'object',
|
||
properties: {
|
||
code: { type: 'number', example: 200 },
|
||
message: { type: 'string', example: '数据创建成功' },
|
||
data: {
|
||
type: 'object',
|
||
properties: {
|
||
id: { type: 'string', example: 'ext_123456789' },
|
||
userId: { type: 'string', example: 'user_123456789' },
|
||
dataType: { type: 'string', example: 'custom' },
|
||
referenceId: { type: 'string', example: 'ref_123456789' },
|
||
status: { type: 'string', example: 'active' },
|
||
},
|
||
},
|
||
},
|
||
},
|
||
})
|
||
async createExtensionData(@Body() extensionDto: any) {
|
||
return this.extensionService.createData(extensionDto);
|
||
}
|
||
|
||
@Get('data/:userId')
|
||
@ApiOperation({
|
||
summary: '查询用户扩展数据',
|
||
description: '根据用户ID查询扩展数据',
|
||
})
|
||
@ApiParam({
|
||
name: 'userId',
|
||
description: '用户ID',
|
||
example: 'user_123456789',
|
||
})
|
||
@ApiQuery({
|
||
name: 'dataType',
|
||
description: '数据类型',
|
||
required: false,
|
||
example: 'custom',
|
||
})
|
||
@ApiResponse({
|
||
status: 200,
|
||
description: '查询成功',
|
||
schema: {
|
||
type: 'object',
|
||
properties: {
|
||
code: { type: 'number', example: 200 },
|
||
message: { type: 'string', example: '查询成功' },
|
||
data: {
|
||
type: 'array',
|
||
items: {
|
||
type: 'object',
|
||
properties: {
|
||
id: { type: 'string', example: 'ext_123456789' },
|
||
dataType: { type: 'string', example: 'custom' },
|
||
data: { type: 'object', description: '扩展数据内容' },
|
||
createdAt: { type: 'string', example: '2023-12-01T10:00:00Z' },
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
})
|
||
async getExtensionData(
|
||
@Param('userId') userId: string,
|
||
@Query('dataType') dataType?: string
|
||
) {
|
||
return this.extensionService.getUserData(userId, dataType);
|
||
}
|
||
}
|
||
```
|
||
|
||
### 5. 在main.ts中启用Swagger
|
||
```typescript
|
||
// src/main.ts
|
||
import { NestFactory } from '@nestjs/core';
|
||
import { ValidationPipe } from '@nestjs/common';
|
||
import { AppModule } from './app.module';
|
||
import { setupSwagger } from './config/swagger.config';
|
||
|
||
async function bootstrap() {
|
||
const app = await NestFactory.create(AppModule);
|
||
|
||
// 启用全局验证管道
|
||
app.useGlobalPipes(new ValidationPipe({
|
||
whitelist: true,
|
||
forbidNonWhitelisted: true,
|
||
transform: true,
|
||
}));
|
||
|
||
// 设置API前缀
|
||
app.setGlobalPrefix('api/v1');
|
||
|
||
// 启用CORS
|
||
app.enableCors();
|
||
|
||
// 设置Swagger文档
|
||
setupSwagger(app);
|
||
|
||
await app.listen(3000);
|
||
|
||
console.log('🚀 应用启动成功!');
|
||
console.log('📖 API文档地址: http://localhost:3000/api/docs');
|
||
}
|
||
bootstrap();
|
||
```
|
||
|
||
## 安全策略
|
||
|
||
### 1. 数据安全
|
||
- 敏感数据加密存储
|
||
- 传输过程HTTPS加密
|
||
- 定期数据备份
|
||
|
||
### 2. 接口安全
|
||
- API签名验证
|
||
- 请求频率限制
|
||
- IP白名单机制
|
||
|
||
### 3. 平台安全
|
||
- 各平台密钥安全管理
|
||
- 定期密钥轮换
|
||
- 权限最小化原则
|
||
|
||
## 开发计划
|
||
|
||
### Phase 1: 基础架构搭建 (4周)
|
||
- [ ] 项目架构设计和技术选型
|
||
- [ ] 基础框架搭建 (NestJS + TypeORM)
|
||
- [ ] PostgreSQL数据库设计和初始化
|
||
- [ ] TypeORM实体定义和迁移脚本
|
||
- [ ] 基础中间件开发 (认证、日志、异常处理)
|
||
- [ ] Docker容器化配置
|
||
|
||
### Phase 2: 核心业务功能开发 (10周)
|
||
- [ ] 平台识别和适配器开发
|
||
- [ ] 用户认证服务
|
||
- [ ] AI模板管理系统 (模板注册、参数验证、执行引擎)
|
||
- [ ] AI提供商适配器 (Stability AI、RunwayML等)
|
||
- [ ] 积分系统开发 (积分获取、消耗、查询)
|
||
- [ ] 广告服务集成 (观看验证、奖励发放)
|
||
- [ ] AI生成服务开发 (任务管理、模型调用)
|
||
- [ ] 文件存储服务 (图片上传、结果存储)
|
||
|
||
### Phase 3: 高级功能开发 (6周)
|
||
- [ ] 订阅服务开发 (包月付费、会员权益)
|
||
- [ ] 预定义模板开发 (换装、抠图、风格转换等)
|
||
- [ ] 模板版本管理和回滚机制
|
||
- [ ] 文件处理优化 (压缩、缩略图生成)
|
||
- [ ] 任务队列优化 (失败重试、状态监控)
|
||
- [ ] API文档完善
|
||
- [ ] 管理后台开发 (模板管理界面)
|
||
|
||
### Phase 4: 测试和优化 (3周)
|
||
- [ ] 单元测试和集成测试
|
||
- [ ] 性能测试和优化
|
||
- [ ] 安全测试
|
||
- [ ] API文档和使用指南完善
|
||
|
||
### Phase 5: 部署和上线 (2周)
|
||
- [ ] 生产环境部署
|
||
- [ ] 基础功能验证
|
||
- [ ] 性能调优
|
||
- [ ] 正式上线
|
||
|
||
### 后续扩展计划 (按需开发)
|
||
- [ ] 更多AI模型集成 (DALL-E、Midjourney API)
|
||
- [ ] 高级图像处理 (风格转换、图像修复)
|
||
- [ ] 社交功能 (作品分享、用户互动)
|
||
- [ ] 数据分析和报表 (用户行为、生成统计)
|
||
- [ ] 更多平台适配器 (快手、飞书等)
|
||
- [ ] 内容审核系统 (AI生成内容安全检测)
|
||
|
||
## 总结
|
||
|
||
本方案为AI图片/视频生成平台提供了完整的多平台统一后台解决方案,通过模块化设计和标准化接口,既满足了当前的核心业务需求,又为未来的功能扩展预留了充分的空间。
|
||
|
||
## 🎯 **核心业务价值**
|
||
|
||
### 💡 **AI生成能力**
|
||
- **统一模板管理**: 通过模板系统抹平不同AI模型的调用差异
|
||
- **多模态生成**: 支持图片和视频生成,满足不同用户需求
|
||
- **智能任务管理**: 异步处理,支持大规模并发生成任务
|
||
- **模型适配**: 灵活对接多种AI服务提供商 (Stability AI、RunwayML等)
|
||
- **参数验证**: 完整的输入参数验证和类型转换
|
||
- **版本管理**: 支持模板版本控制和平滑升级
|
||
|
||
### 💰 **商业模式支持**
|
||
- **积分经济**: 完整的积分获取、消耗、管理体系
|
||
- **广告变现**: 多种广告类型,灵活的奖励机制
|
||
- **订阅服务**: 包月付费,会员权益管理
|
||
- **成本控制**: 基于积分的使用限制,有效控制AI调用成本
|
||
|
||
### 🔧 **技术优势**
|
||
1. **统一性**: 抹平8大平台API差异和AI模型调用差异,统一的开发体验
|
||
2. **可扩展性**: 模块化架构,支持快速添加新功能、平台和AI模型
|
||
3. **模板化**: 通过模板系统标准化AI调用,简化前端集成
|
||
4. **高性能**: PostgreSQL 数据库,支持高并发处理
|
||
5. **数据安全**: JSONB灵活存储,完整的数据备份和恢复机制
|
||
6. **异步处理**: 基于数据库任务队列的异步处理,提升用户体验
|
||
7. **类型安全**: TypeScript + TypeORM,编译时错误检查
|
||
8. **智能缓存**: 活跃模板缓存,提升模板执行性能
|