解决抖音图片审核"无效图片URL"问题,并设计统一异步架构方案: ## 核心修复 - 增强 validateImageUrl 方法,支持文件扩展名 + Content-Type 双重验证 - 修复抖音审核失败问题:binary/octet-stream 响应头导致的验证失败 ## 架构设计 - 设计统一异步内容审核架构,抹平平台差异(同步/异步) - 创建 EnhancedBaseContentAdapter 统一接口 - 实现平台适配器模式,WeChat同步转异步,Douyin原生异步 - 添加回调驱动的模板执行流程 ## 数据库增强 - 新增 taskId 字段关联审核任务和模板执行 - 添加 PENDING_AUDIT 状态支持异步审核流程 - 创建 MySQL 优化的数据库迁移脚本 ## 文档完善 - 更新内容审核设计文档,包含详细序列图 - 创建 5 阶段升级文档,含 MySQL 专项优化 - 提供完整的向后兼容迁移方案 ## 测试改进 - 更新单元测试,修复 validateStatus 参数 - 增强错误处理和边界情况测试覆盖
103 lines
2.0 KiB
TypeScript
103 lines
2.0 KiB
TypeScript
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,
|
||
);
|
||
}
|
||
}
|