/** * 管理员模块工具函数 * * 功能描述: * - 提供管理员模块通用的工具函数 * - 消除重复代码,提高代码复用性 * - 统一处理常见的业务逻辑 * * 职责分离: * - 工具函数集中管理 * - 重复逻辑抽象 * - 通用功能封装 * * 最近修改: * - 2026-01-08: 重构 - 文件夹扁平化,移动到上级目录并更新import路径 (修改者: moyin) * - 2026-01-08: 代码质量优化 - 提取魔法数字为常量,添加用户格式化工具和操作监控工具 (修改者: moyin) * - 2026-01-08: 功能新增 - 创建管理员模块工具函数 (修改者: moyin) * * @author moyin * @version 1.3.0 * @since 2026-01-08 * @lastModified 2026-01-08 */ import { PAGINATION_LIMITS, REQUEST_ID_PREFIXES, SENSITIVE_FIELDS } from './admin_constants'; /** * 请求ID生成常量 */ const REQUEST_ID_RANDOM_LENGTH = 9; // 随机字符串长度 const REQUEST_ID_RANDOM_START = 2; // 跳过'0.'前缀 /** * 安全限制查询数量 * * @param limit 请求的限制数量 * @param maxLimit 最大允许的限制数量 * @returns 安全的限制数量 */ export function safeLimitValue(limit: number, maxLimit: number): number { return Math.min(Math.max(limit, 1), maxLimit); } /** * 安全限制偏移量 * * @param offset 请求的偏移量 * @returns 安全的偏移量(不小于0) */ export function safeOffsetValue(offset: number): number { return Math.max(offset, PAGINATION_LIMITS.DEFAULT_OFFSET); } /** * 生成唯一的请求ID * * @param prefix 请求ID前缀 * @returns 唯一的请求ID */ export function generateRequestId(prefix: string = REQUEST_ID_PREFIXES.GENERAL): string { return `${prefix}_${Date.now()}_${Math.random().toString(36).substring(REQUEST_ID_RANDOM_START, REQUEST_ID_RANDOM_START + REQUEST_ID_RANDOM_LENGTH)}`; } /** * 获取当前时间戳字符串 * * @returns ISO格式的时间戳字符串 */ export function getCurrentTimestamp(): string { return new Date().toISOString(); } /** * 清理请求体中的敏感信息 * * @param body 请求体对象 * @returns 清理后的请求体 */ export function sanitizeRequestBody(body: any): any { if (!body || typeof body !== 'object') { return body; } const sanitized = { ...body }; for (const field of SENSITIVE_FIELDS) { if (sanitized[field]) { sanitized[field] = '***REDACTED***'; } } return sanitized; } /** * 提取客户端IP地址 * * @param request 请求对象 * @returns 客户端IP地址 */ export function extractClientIp(request: any): string { return request.ip || request.connection?.remoteAddress || request.socket?.remoteAddress || (request.connection?.socket as any)?.remoteAddress || request.headers['x-forwarded-for']?.split(',')[0] || request.headers['x-real-ip'] || 'unknown'; } /** * 创建标准的成功响应 * * @param data 响应数据 * @param message 响应消息 * @param requestIdPrefix 请求ID前缀 * @returns 标准格式的成功响应 */ export function createSuccessResponse( data: T, message: string, requestIdPrefix?: string ): { success: true; data: T; message: string; timestamp: string; request_id: string; } { return { success: true, data, message, timestamp: getCurrentTimestamp(), request_id: generateRequestId(requestIdPrefix) }; } /** * 创建标准的错误响应 * * @param message 错误消息 * @param errorCode 错误码 * @param requestIdPrefix 请求ID前缀 * @returns 标准格式的错误响应 */ export function createErrorResponse( message: string, errorCode?: string, requestIdPrefix?: string ): { success: false; message: string; error_code?: string; timestamp: string; request_id: string; } { return { success: false, message, error_code: errorCode, timestamp: getCurrentTimestamp(), request_id: generateRequestId(requestIdPrefix) }; } /** * 创建标准的列表响应 * * @param items 列表项 * @param total 总数 * @param limit 限制数量 * @param offset 偏移量 * @param message 响应消息 * @param requestIdPrefix 请求ID前缀 * @returns 标准格式的列表响应 */ export function createListResponse( items: T[], total: number, limit: number, offset: number, message: string, requestIdPrefix?: string ): { success: true; data: { items: T[]; total: number; limit: number; offset: number; has_more: boolean; }; message: string; timestamp: string; request_id: string; } { return { success: true, data: { items, total, limit, offset, has_more: offset + items.length < total }, message, timestamp: getCurrentTimestamp(), request_id: generateRequestId(requestIdPrefix) }; } /** * 限制保留天数在合理范围内 * * @param daysToKeep 请求的保留天数 * @param minDays 最少保留天数 * @param maxDays 最多保留天数 * @returns 安全的保留天数 */ export function safeDaysToKeep(daysToKeep: number, minDays: number, maxDays: number): number { return Math.max(minDays, Math.min(daysToKeep, maxDays)); } /** * 用户数据格式化工具 */ export class UserFormatter { /** * 格式化用户基本信息 * * @param user 用户实体 * @returns 格式化的用户信息 */ static formatBasicUser(user: any) { return { id: user.id.toString(), username: user.username, nickname: user.nickname, email: user.email, phone: user.phone, role: user.role, status: user.status, email_verified: user.email_verified, avatar_url: user.avatar_url, created_at: user.created_at, updated_at: user.updated_at }; } /** * 格式化用户详细信息(包含GitHub ID) * * @param user 用户实体 * @returns 格式化的用户详细信息 */ static formatDetailedUser(user: any) { return { ...this.formatBasicUser(user), github_id: user.github_id }; } } /** * 操作性能监控工具 */ export class OperationMonitor { /** * 执行带性能监控的操作 * * @param operationName 操作名称 * @param context 操作上下文 * @param operation 要执行的操作 * @param logger 日志记录器 * @returns 操作结果 */ static async executeWithMonitoring( operationName: string, context: Record, operation: () => Promise, logger: (level: 'log' | 'warn' | 'error', message: string, context: Record) => void ): Promise { const startTime = Date.now(); logger('log', `开始${operationName}`, { operation: operationName, ...context }); try { const result = await operation(); const duration = Date.now() - startTime; logger('log', `${operationName}成功`, { operation: operationName, duration, ...context }); return result; } catch (error) { const duration = Date.now() - startTime; logger('error', `${operationName}失败`, { operation: operationName, duration, error: error instanceof Error ? error.message : String(error), ...context }); throw error; } } }