feat: 统一API响应格式规范

- 创建 ResponseUtil 工具类,提供标准化的API响应格式
- 统一所有控制器的响应结构:code、message、data、timestamp、traceId
- 修复 template.controller.ts 响应格式,使用 ResponseUtil.success()
- 修复 app.controller.ts 回调接口,使用标准化错误处理
- 更新 unified-user.controller.ts 使用 ResponseUtil
- 解决 Swagger ApiResponse 类型冲突问题
- 使用 crypto.randomUUID() 生成追踪ID,避免外部依赖
This commit is contained in:
imeepos 2025-09-04 20:02:16 +08:00
parent 0e33292d7a
commit e261cb027d
4 changed files with 148 additions and 61 deletions

View File

@ -2,11 +2,7 @@ import { Body, Controller, Post } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { TemplateExecutionEntity, ExecutionStatus } from './entities/template-execution.entity';
interface ApiResponse<T = any> {
status: boolean | string;
data: T;
msg: string;
}
import { ResponseUtil, ApiResponse } from './utils/response.util';
@Controller()
export class AppController {
@ -15,11 +11,11 @@ export class AppController {
private readonly executionRepository: Repository<TemplateExecutionEntity>,
) {}
@Post('callback')
async callback(@Body() body: any): Promise<ApiResponse<any>> {
async callback(@Body() body: any): Promise<ApiResponse<string | null>> {
console.log(`🚀 [回调] 开始执行回调`);
console.log(`📋 回调参数: ${JSON.stringify(body, null, 2)}`);
const task_id = body.task_id;
if (!task_id) return { status: false, msg: `缺少task_id`, data: null }
if (!task_id) return ResponseUtil.error('缺少task_id', 400)
const res = body.data;
let resultUrl = ``
if (res.status) {
@ -37,7 +33,7 @@ export class AppController {
if (!execution) {
console.error(`未找到 taskId 为 ${task_id} 的执行记录`);
return { status: false, msg: `未找到对应的执行记录`, data: null };
return ResponseUtil.error('未找到对应的执行记录', 404);
}
// 更新执行状态
@ -65,10 +61,6 @@ export class AppController {
await this.executionRepository.update(execution.id, updateData);
return {
status: true,
data: resultUrl,
msg: 'success',
};
return ResponseUtil.success(resultUrl, '回调处理成功');
}
}

View File

@ -16,7 +16,7 @@ import {
import {
ApiTags,
ApiOperation,
ApiResponse,
ApiResponse as SwaggerApiResponse,
ApiParam,
ApiQuery,
ApiBody,
@ -39,6 +39,7 @@ import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { ApiCommonResponses } from '../decorators/api-common-responses.decorator';
import { PlatformAuthGuard } from 'src/platform/guards/platform-auth.guard';
import { ResponseUtil, ApiResponse } from '../utils/response.util';
@ApiTags('AI模板系统')
@Controller('templates')
@ -59,12 +60,12 @@ export class TemplateController {
})
@ApiParam({ name: 'templateId', description: '模板ID', example: 1 })
@ApiBody({ type: TemplateExecuteDto })
@ApiResponse({
@SwaggerApiResponse({
status: 200,
description: '执行成功',
type: TemplateExecuteResponseDto,
})
@ApiResponse({
@SwaggerApiResponse({
status: 400,
description: '参数错误',
schema: {
@ -121,7 +122,7 @@ export class TemplateController {
example: 'character_figurine_v1',
})
@ApiBody({ type: TemplateExecuteDto })
@ApiResponse({
@SwaggerApiResponse({
status: 200,
description: '执行成功',
type: TemplateExecuteResponseDto,
@ -130,7 +131,7 @@ export class TemplateController {
@Param('code') code: string,
@Body() body: { imageUrl: string },
@Request() req
) {
): Promise<ApiResponse<number>> {
try {
const { imageUrl } = body;
@ -167,10 +168,7 @@ export class TemplateController {
const savedExecution = await this.executionRepository.save(execution);
// 返回任务id (执行记录的ID)
return {
success: true,
data: savedExecution.id,
};
return ResponseUtil.success(savedExecution.id, '模板执行已启动');
} catch (error) {
console.error(error)
throw new HttpException(
@ -187,7 +185,7 @@ export class TemplateController {
})
@ApiParam({ name: 'templateId', description: '模板ID', example: 1 })
@ApiBody({ type: BatchExecuteDto })
@ApiResponse({
@SwaggerApiResponse({
status: 200,
description: '批量执行成功',
schema: {
@ -247,34 +245,32 @@ export class TemplateController {
summary: '获取所有模板列表',
description: '获取所有可用的AI生成模板支持按类型筛选',
})
@ApiResponse({
@SwaggerApiResponse({
status: 200,
description: '获取成功',
type: [TemplateListDto],
})
async getAllTemplates() {
async getAllTemplates(): Promise<ApiResponse<any[]>> {
try {
const templates = await this.templateFactory.getAllTemplates();
return {
success: true,
data: templates.map((template) => ({
id: template.id,
code: template.code,
name: template.name,
description: template.description,
creditCost: template.creditCost,
version: template.version,
templateType: template.templateType,
templateClass: template.templateClass,
tags: template.tags,
inputExample: template.inputExampleUrl,
outputExample: template.outputExampleUrl,
sortOrder: template.sortOrder,
isActive: template.isActive,
createdAt: template.createdAt,
updatedAt: template.updatedAt,
})),
};
const data = templates.map((template) => ({
id: template.id,
code: template.code,
name: template.name,
description: template.description,
creditCost: template.creditCost,
version: template.version,
templateType: template.templateType,
templateClass: template.templateClass,
tags: template.tags,
inputExample: template.inputExampleUrl,
outputExample: template.outputExampleUrl,
sortOrder: template.sortOrder,
isActive: template.isActive,
createdAt: template.createdAt,
updatedAt: template.updatedAt,
}));
return ResponseUtil.success(data, '获取模板列表成功');
} catch (error) {
throw new HttpException(
error.message || 'Failed to fetch templates',
@ -288,7 +284,7 @@ export class TemplateController {
summary: '获取图片模板列表',
description: '获取所有图片类型的AI生成模板',
})
@ApiResponse({
@SwaggerApiResponse({
status: 200,
description: '获取成功',
type: [TemplateListDto],
@ -313,7 +309,7 @@ export class TemplateController {
summary: '获取视频模板列表',
description: '获取所有视频类型的AI生成模板',
})
@ApiResponse({
@SwaggerApiResponse({
status: 200,
description: '获取成功',
type: [TemplateListDto],
@ -350,7 +346,7 @@ export class TemplateController {
description: '推荐数量',
example: 5,
})
@ApiResponse({
@SwaggerApiResponse({
status: 200,
description: '推荐成功',
schema: {
@ -395,12 +391,12 @@ export class TemplateController {
description: '根据模板ID获取详细信息',
})
@ApiParam({ name: 'templateId', description: '模板ID', example: 1 })
@ApiResponse({
@SwaggerApiResponse({
status: 200,
description: '获取成功',
type: TemplateListDto,
})
@ApiResponse({
@SwaggerApiResponse({
status: 404,
description: '模板不存在',
})
@ -545,7 +541,7 @@ export class TemplateController {
description: '创建一个新的AI生成模板',
})
@ApiBody({ type: CreateTemplateDto })
@ApiResponse({
@SwaggerApiResponse({
status: 200,
description: '创建成功',
schema: {
@ -559,7 +555,7 @@ export class TemplateController {
},
},
})
@ApiResponse({
@SwaggerApiResponse({
status: 409,
description: '模板代码已存在',
})
@ -601,16 +597,16 @@ export class TemplateController {
})
@ApiParam({ name: 'templateId', description: '模板ID', example: 1 })
@ApiBody({ type: UpdateTemplateDto })
@ApiResponse({
@SwaggerApiResponse({
status: 200,
description: '更新成功',
type: TemplateListDto,
})
@ApiResponse({
@SwaggerApiResponse({
status: 404,
description: '模板不存在',
})
@ApiResponse({
@SwaggerApiResponse({
status: 409,
description: '模板代码已存在',
})

View File

@ -23,6 +23,7 @@ import {
} from '../dto/platform-login.dto';
import { PlatformAuthGuard } from '../guards/platform-auth.guard';
import { PlatformType } from '../../entities/platform-user.entity';
import { ResponseUtil, ApiResponse as ApiResponseType } from '../../utils/response.util';
@ApiTags('用户管理')
@Controller('users')
@ -55,16 +56,12 @@ export class UnifiedUserController {
})
@ApiResponse({ status: 400, description: '请求参数错误' })
@ApiResponse({ status: 401, description: '平台授权失败' })
async login(@Body() loginDto: PlatformLoginDto) {
async login(@Body() loginDto: PlatformLoginDto): Promise<ApiResponseType<any>> {
const result = await this.unifiedUserService.login(
loginDto.platform,
loginDto,
);
return {
code: 200,
message: '登录成功',
data: result,
};
return ResponseUtil.success(result, '登录成功');
}
@Post('register')

102
src/utils/response.util.ts Normal file
View File

@ -0,0 +1,102 @@
import { HttpStatus } from '@nestjs/common';
import * as crypto from 'crypto';
/**
*
* API响应结构
*/
export interface ApiResponse<T = any> {
code: number;
message: string;
data?: T;
timestamp: number;
traceId: string;
}
export class ResponseUtil {
/**
* ID
* @returns
*/
private static generateTraceId(): string {
return crypto.randomUUID();
}
/**
*
* @param data
* @param message
* @param code HTTP状态码
* @returns
*/
static success<T>(
data?: T,
message: string = 'success',
code: number = HttpStatus.OK,
): ApiResponse<T> {
return {
code,
message,
data,
timestamp: Date.now(),
traceId: this.generateTraceId(),
};
}
/**
* 使
* @param message
* @param code HTTP状态码
* @returns
*/
static error(
message: string = 'Internal Server Error',
code: number = HttpStatus.INTERNAL_SERVER_ERROR,
): ApiResponse<null> {
return {
code,
message,
data: null,
timestamp: Date.now(),
traceId: this.generateTraceId(),
};
}
/**
*
* @param items
* @param total
* @param page
* @param limit
* @param message
* @returns
*/
static paginated<T>(
items: T[],
total: number,
page: number,
limit: number,
message: string = 'success',
): ApiResponse<{
items: T[];
pagination: {
total: number;
page: number;
limit: number;
totalPages: number;
};
}> {
return this.success(
{
items,
pagination: {
total,
page,
limit,
totalPages: Math.ceil(total / limit),
},
},
message,
);
}
}