From 42fa667d3c397eb3d7d4c66b205a368001275d25 Mon Sep 17 00:00:00 2001 From: imeepos Date: Thu, 4 Sep 2025 18:22:49 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E5=96=84=E6=A8=A1=E6=9D=BF?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E6=8E=92=E5=BA=8F=E5=8A=9F=E8=83=BD=E5=92=8C?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=A0=BC=E5=BC=8F=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - N8nTemplateEntity已包含sortOrder排序字段 - 所有模板列表接口均按sortOrder倒序排列 - 修复ESLint代码格式问题 --- src/config/swagger.config.ts | 8 +- src/controllers/template.controller.ts | 52 ++++--- .../api-common-responses.decorator.ts | 2 +- src/dto/common-response.dto.ts | 9 +- src/dto/error-response.dto.ts | 2 +- src/dto/template.dto.ts | 147 +++++++++++++++--- src/entities/template-execution.entity.ts | 2 +- src/generate-swagger-json.ts | 2 +- src/main.ts | 19 +-- src/platform/adapters/bytedance.adapter.ts | 5 +- src/platform/adapters/wechat.adapter.ts | 5 +- .../controllers/unified-user.controller.ts | 56 +++---- 12 files changed, 217 insertions(+), 92 deletions(-) diff --git a/src/config/swagger.config.ts b/src/config/swagger.config.ts index 43b1951..de09777 100644 --- a/src/config/swagger.config.ts +++ b/src/config/swagger.config.ts @@ -4,7 +4,8 @@ import { INestApplication } from '@nestjs/common'; export function setupSwagger(app: INestApplication): void { const config = new DocumentBuilder() .setTitle('多平台小程序统一后台API') - .setDescription(` + .setDescription( + ` ## 功能特性 - 🔐 统一用户认证 (微信/支付宝/百度/字节跳动等) - 🎨 AI模板系统 (图片/视频生成) @@ -26,7 +27,8 @@ export function setupSwagger(app: INestApplication): void { "traceId": "trace-uuid" } \`\`\` - `) + `, + ) .setVersion('1.0.0') .setContact('开发团队', 'https://example.com', 'dev@example.com') .setLicense('MIT', 'https://opensource.org/licenses/MIT') @@ -76,4 +78,4 @@ export function setupSwagger(app: INestApplication): void { ], }); console.log('✅ Swagger documentation setup completed at /api/docs'); -} \ No newline at end of file +} diff --git a/src/controllers/template.controller.ts b/src/controllers/template.controller.ts index 3446d14..c80f5b7 100644 --- a/src/controllers/template.controller.ts +++ b/src/controllers/template.controller.ts @@ -68,9 +68,9 @@ export class TemplateController { example: { code: 400, message: 'imageUrl is required', - data: null - } - } + data: null, + }, + }, }) async executeTemplateById( @Param('templateId', ParseIntPipe) templateId: number, @@ -111,7 +111,11 @@ export class TemplateController { summary: '通过代码执行模板', description: '根据模板代码执行AI生成任务,支持图片和视频生成', }) - @ApiParam({ name: 'code', description: '模板代码', example: 'character_figurine_v1' }) + @ApiParam({ + name: 'code', + description: '模板代码', + example: 'character_figurine_v1', + }) @ApiBody({ type: TemplateExecuteDto }) @ApiResponse({ status: 200, @@ -169,11 +173,11 @@ export class TemplateController { totalCount: 2, results: [ { inputUrl: 'image1.jpg', outputUrl: 'output1.jpg' }, - { inputUrl: 'image2.jpg', outputUrl: 'output2.jpg' } - ] - } - } - } + { inputUrl: 'image2.jpg', outputUrl: 'output2.jpg' }, + ], + }, + }, + }, }) async batchExecuteTemplate( @Param('templateId', ParseIntPipe) templateId: number, @@ -309,8 +313,18 @@ export class TemplateController { summary: '推荐模板', description: '根据用户偏好标签推荐适合的模板', }) - @ApiQuery({ name: 'tags', required: false, description: '用户偏好标签,逗号分隔', example: '人物,手办' }) - @ApiQuery({ name: 'limit', required: false, description: '推荐数量', example: 5 }) + @ApiQuery({ + name: 'tags', + required: false, + description: '用户偏好标签,逗号分隔', + example: '人物,手办', + }) + @ApiQuery({ + name: 'limit', + required: false, + description: '推荐数量', + example: 5, + }) @ApiResponse({ status: 200, description: '推荐成功', @@ -319,10 +333,10 @@ export class TemplateController { success: true, data: { userTags: ['人物', '手办'], - recommendedTemplates: [] - } - } - } + recommendedTemplates: [], + }, + }, + }, }) async recommendTemplates( @Query('tags') tags?: string, @@ -515,10 +529,10 @@ export class TemplateController { data: { id: 1, code: 'new_template_v1', - name: '新模板' - } - } - } + name: '新模板', + }, + }, + }, }) @ApiResponse({ status: 409, diff --git a/src/decorators/api-common-responses.decorator.ts b/src/decorators/api-common-responses.decorator.ts index 1ca3d55..4972b0e 100644 --- a/src/decorators/api-common-responses.decorator.ts +++ b/src/decorators/api-common-responses.decorator.ts @@ -15,4 +15,4 @@ export function ApiAuthResponses() { ApiResponse(CommonErrorResponses.Unauthorized), ApiResponse(CommonErrorResponses.Forbidden), ); -} \ No newline at end of file +} diff --git a/src/dto/common-response.dto.ts b/src/dto/common-response.dto.ts index 224f517..dd8b3c4 100644 --- a/src/dto/common-response.dto.ts +++ b/src/dto/common-response.dto.ts @@ -21,7 +21,12 @@ export class PaginationDto { @ApiProperty({ description: '页码', example: 1, minimum: 1 }) page: number; - @ApiProperty({ description: '每页数量', example: 10, minimum: 1, maximum: 100 }) + @ApiProperty({ + description: '每页数量', + example: 10, + minimum: 1, + maximum: 100, + }) limit: number; } @@ -40,4 +45,4 @@ export class PaginationResponseDto { @ApiProperty({ description: '总页数', example: 10 }) totalPages: number; -} \ No newline at end of file +} diff --git a/src/dto/error-response.dto.ts b/src/dto/error-response.dto.ts index 6c78654..ae06fcd 100644 --- a/src/dto/error-response.dto.ts +++ b/src/dto/error-response.dto.ts @@ -50,4 +50,4 @@ export const CommonErrorResponses = { description: '服务器内部错误', type: ErrorResponseDto, }, -}; \ No newline at end of file +}; diff --git a/src/dto/template.dto.ts b/src/dto/template.dto.ts index d48d026..858712b 100644 --- a/src/dto/template.dto.ts +++ b/src/dto/template.dto.ts @@ -20,7 +20,10 @@ export class CreateTemplateDto { @IsString() name: string; - @ApiProperty({ description: '模板描述', example: '将人物照片制作成精细的角色手办模型' }) + @ApiProperty({ + description: '模板描述', + example: '将人物照片制作成精细的角色手办模型', + }) @IsString() description: string; @@ -33,30 +36,53 @@ export class CreateTemplateDto { @IsString() version: string; - @ApiProperty({ description: '输入示例URL', required: false, example: 'https://example.com/input.jpg' }) + @ApiProperty({ + description: '输入示例URL', + required: false, + example: 'https://example.com/input.jpg', + }) @IsString() @IsOptional() inputExampleUrl?: string; - @ApiProperty({ description: '输出示例URL', required: false, example: 'https://example.com/output.jpg' }) + @ApiProperty({ + description: '输出示例URL', + required: false, + example: 'https://example.com/output.jpg', + }) @IsString() @IsOptional() outputExampleUrl?: string; - @ApiProperty({ description: '标签数组', required: false, example: ['人物', '手办', '模型'] }) + @ApiProperty({ + description: '标签数组', + required: false, + example: ['人物', '手办', '模型'], + }) @IsArray() @IsOptional() tags?: string[]; - @ApiProperty({ description: '模板类型', enum: TemplateType, example: 'image' }) + @ApiProperty({ + description: '模板类型', + enum: TemplateType, + example: 'image', + }) @IsEnum(TemplateType) templateType: TemplateType; - @ApiProperty({ description: '模板类名', example: 'CharacterFigurineTemplate' }) + @ApiProperty({ + description: '模板类名', + example: 'CharacterFigurineTemplate', + }) @IsString() templateClass: string; - @ApiProperty({ description: '图片模型', required: false, example: 'flux-dev' }) + @ApiProperty({ + description: '图片模型', + required: false, + example: 'flux-dev', + }) @IsString() @IsOptional() imageModel?: string; @@ -66,7 +92,11 @@ export class CreateTemplateDto { @IsOptional() imagePrompt?: string; - @ApiProperty({ description: '视频模型', required: false, example: 'runway-gen3' }) + @ApiProperty({ + description: '视频模型', + required: false, + example: 'runway-gen3', + }) @IsString() @IsOptional() videoModel?: string; @@ -76,7 +106,13 @@ export class CreateTemplateDto { @IsOptional() videoPrompt?: string; - @ApiProperty({ description: '视频时长(秒)', required: false, example: 10, minimum: 1, maximum: 300 }) + @ApiProperty({ + description: '视频时长(秒)', + required: false, + example: 10, + minimum: 1, + maximum: 300, + }) @IsNumber() @IsOptional() @Min(1) @@ -100,22 +136,39 @@ export class CreateTemplateDto { } export class UpdateTemplateDto { - @ApiProperty({ description: '模板代码', required: false, example: 'character_figurine_v1' }) + @ApiProperty({ + description: '模板代码', + required: false, + example: 'character_figurine_v1', + }) @IsString() @IsOptional() code?: string; - @ApiProperty({ description: '模板名称', required: false, example: '人物手办' }) + @ApiProperty({ + description: '模板名称', + required: false, + example: '人物手办', + }) @IsString() @IsOptional() name?: string; - @ApiProperty({ description: '模板描述', required: false, example: '将人物照片制作成精细的角色手办模型' }) + @ApiProperty({ + description: '模板描述', + required: false, + example: '将人物照片制作成精细的角色手办模型', + }) @IsString() @IsOptional() description?: string; - @ApiProperty({ description: '积分消耗', required: false, example: 28, minimum: 0 }) + @ApiProperty({ + description: '积分消耗', + required: false, + example: 28, + minimum: 0, + }) @IsNumber() @Min(0) @IsOptional() @@ -126,17 +179,29 @@ export class UpdateTemplateDto { @IsOptional() version?: string; - @ApiProperty({ description: '输入示例URL', required: false, example: 'https://example.com/input.jpg' }) + @ApiProperty({ + description: '输入示例URL', + required: false, + example: 'https://example.com/input.jpg', + }) @IsString() @IsOptional() inputExampleUrl?: string; - @ApiProperty({ description: '输出示例URL', required: false, example: 'https://example.com/output.jpg' }) + @ApiProperty({ + description: '输出示例URL', + required: false, + example: 'https://example.com/output.jpg', + }) @IsString() @IsOptional() outputExampleUrl?: string; - @ApiProperty({ description: '标签数组', required: false, example: ['人物', '手办', '模型'] }) + @ApiProperty({ + description: '标签数组', + required: false, + example: ['人物', '手办', '模型'], + }) @IsArray() @IsOptional() tags?: string[]; @@ -146,12 +211,20 @@ export class UpdateTemplateDto { @IsOptional() templateType?: TemplateType; - @ApiProperty({ description: '模板类名', required: false, example: 'CharacterFigurineTemplate' }) + @ApiProperty({ + description: '模板类名', + required: false, + example: 'CharacterFigurineTemplate', + }) @IsString() @IsOptional() templateClass?: string; - @ApiProperty({ description: '图片模型', required: false, example: 'flux-dev' }) + @ApiProperty({ + description: '图片模型', + required: false, + example: 'flux-dev', + }) @IsString() @IsOptional() imageModel?: string; @@ -161,7 +234,11 @@ export class UpdateTemplateDto { @IsOptional() imagePrompt?: string; - @ApiProperty({ description: '视频模型', required: false, example: 'runway-gen3' }) + @ApiProperty({ + description: '视频模型', + required: false, + example: 'runway-gen3', + }) @IsString() @IsOptional() videoModel?: string; @@ -171,7 +248,13 @@ export class UpdateTemplateDto { @IsOptional() videoPrompt?: string; - @ApiProperty({ description: '视频时长(秒)', required: false, example: 10, minimum: 1, maximum: 300 }) + @ApiProperty({ + description: '视频时长(秒)', + required: false, + example: 10, + minimum: 1, + maximum: 300, + }) @IsNumber() @IsOptional() @Min(1) @@ -197,7 +280,7 @@ export class UpdateTemplateDto { export class TemplateExecuteDto { @ApiProperty({ description: '输入图片URL', - example: 'https://cdn.example.com/upload/image.jpg' + example: 'https://cdn.example.com/upload/image.jpg', }) @IsString() imageUrl: string; @@ -236,10 +319,17 @@ export class TemplateListDto { @ApiProperty({ description: '模板名称', example: '人物手办' }) name: string; - @ApiProperty({ description: '模板描述', example: '将人物照片制作成精细的角色手办模型' }) + @ApiProperty({ + description: '模板描述', + example: '将人物照片制作成精细的角色手办模型', + }) description: string; - @ApiProperty({ enum: TemplateType, description: '模板类型', example: 'video' }) + @ApiProperty({ + enum: TemplateType, + description: '模板类型', + example: 'video', + }) templateType: TemplateType; @ApiProperty({ description: '积分消耗', example: 28 }) @@ -254,7 +344,11 @@ export class TemplateListDto { @ApiProperty({ description: '输出示例URL', required: false }) outputExampleUrl?: string; - @ApiProperty({ type: [String], description: '标签数组', example: ['人物', '手办', '模型'] }) + @ApiProperty({ + type: [String], + description: '标签数组', + example: ['人物', '手办', '模型'], + }) tags: string[]; @ApiProperty({ description: '是否启用', example: true }) @@ -267,7 +361,10 @@ export class TemplateListDto { export class BatchExecuteDto { @ApiProperty({ description: '输入图片URL数组', - example: ['https://cdn.example.com/upload/image1.jpg', 'https://cdn.example.com/upload/image2.jpg'] + example: [ + 'https://cdn.example.com/upload/image1.jpg', + 'https://cdn.example.com/upload/image2.jpg', + ], }) @IsArray() imageUrls: string[]; diff --git a/src/entities/template-execution.entity.ts b/src/entities/template-execution.entity.ts index a294ecf..2bbfb90 100644 --- a/src/entities/template-execution.entity.ts +++ b/src/entities/template-execution.entity.ts @@ -59,7 +59,7 @@ export class TemplateExecutionEntity { userId: string; /** 执行平台 - 任务发起的平台来源 */ - @Column({ type: 'enum', enum: PlatformType, enumName: 'PlatformType', }) + @Column({ type: 'enum', enum: PlatformType, enumName: 'PlatformType' }) platform: PlatformType; /** 执行类型 - 标识生成内容的类型(图片或视频) */ diff --git a/src/generate-swagger-json.ts b/src/generate-swagger-json.ts index e6e8083..79e94fc 100644 --- a/src/generate-swagger-json.ts +++ b/src/generate-swagger-json.ts @@ -40,4 +40,4 @@ async function generateSwaggerJson() { await app.close(); } -generateSwaggerJson().catch(console.error); \ No newline at end of file +generateSwaggerJson().catch(console.error); diff --git a/src/main.ts b/src/main.ts index dd2e010..053104e 100644 --- a/src/main.ts +++ b/src/main.ts @@ -7,11 +7,13 @@ async function bootstrap() { const app = await NestFactory.create(AppModule); // 启用全局验证管道 - app.useGlobalPipes(new ValidationPipe({ - whitelist: true, - forbidNonWhitelisted: true, - transform: true, - })); + app.useGlobalPipes( + new ValidationPipe({ + whitelist: true, + forbidNonWhitelisted: true, + transform: true, + }), + ); // 设置Swagger文档(在全局前缀之前) try { @@ -23,14 +25,13 @@ async function bootstrap() { // 设置API前缀(排除docs路径) app.setGlobalPrefix('api/v1', { - exclude: ['docs', 'docs/(.*)'] + exclude: ['docs', 'docs/(.*)'], }); // 启用 CORS 支持多平台访问 app.enableCors({ - origin: process.env.NODE_ENV === 'production' - ? ['https://example.com'] - : true, + origin: + process.env.NODE_ENV === 'production' ? ['https://example.com'] : true, methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'], credentials: true, diff --git a/src/platform/adapters/bytedance.adapter.ts b/src/platform/adapters/bytedance.adapter.ts index 9c40ab9..5876bcb 100644 --- a/src/platform/adapters/bytedance.adapter.ts +++ b/src/platform/adapters/bytedance.adapter.ts @@ -6,7 +6,10 @@ import { ConfigService } from '@nestjs/config'; import { JwtService } from '@nestjs/jwt'; import { BaseAdapter } from './base.adapter'; import { UserEntity } from '../../entities/user.entity'; -import { PlatformUserEntity, PlatformType } from '../../entities/platform-user.entity'; +import { + PlatformUserEntity, + PlatformType, +} from '../../entities/platform-user.entity'; import { PlatformLoginData, PlatformRegisterData, diff --git a/src/platform/adapters/wechat.adapter.ts b/src/platform/adapters/wechat.adapter.ts index cc40cee..4f36e4f 100644 --- a/src/platform/adapters/wechat.adapter.ts +++ b/src/platform/adapters/wechat.adapter.ts @@ -6,7 +6,10 @@ import { ConfigService } from '@nestjs/config'; import { JwtService } from '@nestjs/jwt'; import { BaseAdapter } from './base.adapter'; import { UserEntity } from '../../entities/user.entity'; -import { PlatformUserEntity, PlatformType } from '../../entities/platform-user.entity'; +import { + PlatformUserEntity, + PlatformType, +} from '../../entities/platform-user.entity'; import { PlatformLoginData, PlatformRegisterData, diff --git a/src/platform/controllers/unified-user.controller.ts b/src/platform/controllers/unified-user.controller.ts index a89c865..39a33a5 100644 --- a/src/platform/controllers/unified-user.controller.ts +++ b/src/platform/controllers/unified-user.controller.ts @@ -47,11 +47,11 @@ export class UnifiedUserController { userInfo: { id: 'user-uuid', nickname: '用户昵称', - avatarUrl: 'https://example.com/avatar.jpg' - } - } - } - } + avatarUrl: 'https://example.com/avatar.jpg', + }, + }, + }, + }, }) @ApiResponse({ status: 400, description: '请求参数错误' }) @ApiResponse({ status: 401, description: '平台授权失败' }) @@ -81,10 +81,10 @@ export class UnifiedUserController { message: '注册成功', data: { userId: 'user-uuid', - unifiedUserId: 'unified-user-123' - } - } - } + unifiedUserId: 'unified-user-123', + }, + }, + }, }) async register(@Body() registerDto: PlatformLoginDto) { const result = await this.unifiedUserService.register( @@ -119,10 +119,10 @@ export class UnifiedUserController { avatarUrl: 'https://example.com/avatar.jpg', phone: '13800138000', email: 'user@example.com', - platformUsers: [] - } - } - } + platformUsers: [], + }, + }, + }, }) @ApiResponse({ status: 401, description: '未授权访问' }) async getProfile(@Request() req) { @@ -147,9 +147,9 @@ export class UnifiedUserController { schema: { example: { code: 200, - message: '更新成功' - } - } + message: '更新成功', + }, + }, }) @ApiResponse({ status: 401, description: '未授权访问' }) async updateProfile(@Request() req, @Body() updateDto: UserInfoUpdateDto) { @@ -177,9 +177,9 @@ export class UnifiedUserController { schema: { example: { code: 200, - message: '绑定成功' - } - } + message: '绑定成功', + }, + }, }) @ApiResponse({ status: 401, description: '未授权访问' }) @ApiResponse({ status: 409, description: '平台账号已绑定其他用户' }) @@ -208,9 +208,9 @@ export class UnifiedUserController { schema: { example: { code: 200, - message: '解绑成功' - } - } + message: '解绑成功', + }, + }, }) @ApiResponse({ status: 401, description: '未授权访问' }) @ApiResponse({ status: 404, description: '平台绑定不存在' }) @@ -241,16 +241,16 @@ export class UnifiedUserController { { type: 'wechat', name: '微信小程序', - supported: true + supported: true, }, { type: 'alipay', name: '支付宝小程序', - supported: true - } - ] - } - } + supported: true, + }, + ], + }, + }, }) async getSupportedPlatforms() { const platforms = this.unifiedUserService.getSupportedPlatforms();