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:
@@ -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',
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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: '模板代码已存在',
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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
102
src/utils/response.util.ts
Normal 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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user