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

View File

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

View File

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