/** * 用户服务基类 * * 功能描述: * - 提供统一的异常处理机制 * - 定义通用的错误处理方法 * - 统一日志记录格式 * - 敏感信息脱敏处理 * * 职责分离: * - 异常处理:统一的错误格式化和异常转换 * - 日志管理:结构化日志记录和敏感信息脱敏 * - 性能监控:操作成功和失败的统计记录 * - 搜索优化:搜索异常的特殊处理机制 * * 最近修改: * - 2026-01-07: 代码规范优化 - 完善注释规范,添加完整的文件头和方法注释 * - 2026-01-07: 功能新增 - 添加敏感信息脱敏处理和结构化日志记录 * * @author moyin * @version 1.0.1 * @since 2025-01-07 * @lastModified 2026-01-07 */ import { Logger, ConflictException, NotFoundException, BadRequestException } from '@nestjs/common'; export abstract class BaseUsersService { protected readonly logger = new Logger(this.constructor.name); /** * 统一的错误格式化方法 * * @param error 原始错误对象 * @returns 格式化后的错误信息字符串 */ protected formatError(error: unknown): string { if (error instanceof Error) { return error.message; } return String(error); } /** * 统一的异常处理方法 * * @param error 原始错误 * @param operation 操作名称 * @param context 上下文信息 * @throws 处理后的标准异常 */ protected handleServiceError(error: unknown, operation: string, context?: Record): never { const errorMessage = this.formatError(error); // 记录错误日志 this.logger.error(`${operation}失败`, { operation, error: errorMessage, context: context ? this.sanitizeLogData(context) : undefined, timestamp: new Date().toISOString() }, error instanceof Error ? error.stack : undefined); // 如果是已知的业务异常,直接重新抛出 if (error instanceof ConflictException || error instanceof NotFoundException || error instanceof BadRequestException) { throw error; } // 系统异常转换为BadRequestException throw new BadRequestException(`${operation}失败,请稍后重试`); } /** * 搜索异常的特殊处理(返回空结果而不抛出异常) * * @param error 原始错误 * @param operation 操作名称 * @param context 上下文信息 * @returns 空数组 */ protected handleSearchError(error: unknown, operation: string, context?: Record): any[] { const errorMessage = this.formatError(error); this.logger.warn(`${operation}失败,返回空结果`, { operation, error: errorMessage, context: context ? this.sanitizeLogData(context) : undefined, timestamp: new Date().toISOString() }); return []; } /** * 记录操作成功日志 * * @param operation 操作名称 * @param context 上下文信息 * @param duration 操作耗时 */ protected logSuccess(operation: string, context?: Record, duration?: number): void { this.logger.log(`${operation}成功`, { operation, context: context ? this.sanitizeLogData(context) : undefined, duration, timestamp: new Date().toISOString() }); } /** * 记录操作开始日志 * * @param operation 操作名称 * @param context 上下文信息 */ protected logStart(operation: string, context?: Record): void { this.logger.log(`开始${operation}`, { operation, context: context ? this.sanitizeLogData(context) : undefined, timestamp: new Date().toISOString() }); } /** * 脱敏处理敏感信息 * * @param data 原始数据 * @returns 脱敏后的数据 */ protected sanitizeLogData(data: Record): Record { const sanitized = { ...data }; // 脱敏邮箱 if (sanitized.email) { const email = sanitized.email; const [localPart, domain] = email.split('@'); if (localPart && domain) { sanitized.email = `${localPart.substring(0, 2)}***@${domain}`; } } // 脱敏手机号 if (sanitized.phone) { const phone = sanitized.phone; if (phone.length > 4) { sanitized.phone = `${phone.substring(0, 3)}****${phone.substring(phone.length - 2)}`; } } // 移除密码哈希 if (sanitized.password_hash) { sanitized.password_hash = '[REDACTED]'; } return sanitized; } }