/** * 真实Redis服务实现 * * 功能描述: * - 连接真实的Redis服务器进行数据操作 * - 实现完整的Redis基础操作功能 * - 提供连接管理和错误处理机制 * - 支持自动重连和连接状态监控 * * 职责分离: * - 连接管理:负责Redis服务器的连接建立和维护 * - 数据操作:实现IRedisService接口的所有方法 * - 错误处理:处理网络异常和Redis操作错误 * - 日志记录:记录连接状态和操作日志 * * 最近修改: * - 2025-01-07: 代码规范优化 - 为所有公共方法添加完整的三级注释,包含业务逻辑和示例代码 * - 2025-01-07: 代码规范优化 - 为主要方法添加完整的三级注释,包含业务逻辑和示例代码 * - 2025-01-07: 代码规范优化 - 完善文件头注释和方法注释,添加详细业务逻辑说明 * * @author moyin * @version 1.0.3 * @since 2025-01-07 * @lastModified 2025-01-07 */ import { Injectable, Logger, OnModuleDestroy } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import Redis from 'ioredis'; import { IRedisService } from './redis.interface'; /** * 真实Redis服务 * * 职责: * - 连接到真实的Redis服务器 * - 实现完整的Redis操作接口 * - 管理连接生命周期和错误处理 * * 主要方法: * - initializeRedis() - 初始化Redis连接 * - set/get/del() - 基础键值操作 * - expire/ttl() - 过期时间管理 * - sadd/srem/smembers() - 集合操作 * * 使用场景: * - 生产环境的Redis数据存储 * - 高性能和高并发的数据访问需求 */ @Injectable() export class RealRedisService implements IRedisService, OnModuleDestroy { private readonly logger = new Logger(RealRedisService.name); private redis: Redis; constructor(private configService: ConfigService) { this.initializeRedis(); } /** * 初始化Redis连接 * * 业务逻辑: * 1. 从环境变量读取Redis连接配置 * 2. 创建Redis客户端实例并配置连接参数 * 3. 设置连接事件监听器 * 4. 配置重连策略和错误处理 * * @throws Error Redis连接配置错误时 * * @example * ```typescript * // 在构造函数中自动调用 * constructor(configService: ConfigService) { * this.initializeRedis(); * } * ``` */ private initializeRedis(): void { const redisConfig = { host: this.configService.get('REDIS_HOST', 'localhost'), port: this.configService.get('REDIS_PORT', 6379), password: this.configService.get('REDIS_PASSWORD') || undefined, db: this.configService.get('REDIS_DB', 0), retryDelayOnFailover: 100, maxRetriesPerRequest: 3, lazyConnect: true, }; this.redis = new Redis(redisConfig); this.redis.on('connect', () => { this.logger.log('Redis连接成功'); }); this.redis.on('error', (error) => { this.logger.error('Redis连接错误', error); }); this.redis.on('close', () => { this.logger.warn('Redis连接关闭'); }); } /** * 设置键值对 * * 业务逻辑: * 1. 验证键和值的有效性 * 2. 根据TTL参数决定使用set还是setex命令 * 3. 执行Redis设置操作 * 4. 记录操作日志和错误处理 * * @param key 键名,不能为空 * @param value 值,支持字符串类型 * @param ttl 可选的过期时间(秒),不设置则永不过期 * @returns Promise 操作完成的Promise * @throws Error 当Redis操作失败时 * * @example * ```typescript * await redisService.set('user:123', 'userData', 3600); * ``` */ async set(key: string, value: string, ttl?: number): Promise { try { if (ttl && ttl > 0) { await this.redis.setex(key, ttl, value); } else { await this.redis.set(key, value); } this.logger.debug(`设置Redis键: ${key}, TTL: ${ttl || '永不过期'}`); } catch (error) { this.logger.error(`设置Redis键失败: ${key}`, error); throw error; } } async setIfAbsent(key: string, value: string, ttl?: number): Promise { try { const result = ttl && ttl > 0 ? await this.redis.set(key, value, 'EX', ttl, 'NX') : await this.redis.set(key, value, 'NX'); return result === 'OK'; } catch (error) { this.logger.error(`原子设置Redis键失败: ${key}`, error); throw error; } } /** * 获取键对应的值 * * 业务逻辑: * 1. 验证键名的有效性 * 2. 执行Redis get命令 * 3. 返回查询结果 * 4. 处理查询异常 * * @param key 键名,不能为空 * @returns Promise 键对应的值,不存在返回null * @throws Error 当Redis操作失败时 * * @example * ```typescript * const value = await redisService.get('user:123'); * ``` */ async get(key: string): Promise { try { return await this.redis.get(key); } catch (error) { this.logger.error(`获取Redis键失败: ${key}`, error); throw error; } } /** * 删除指定的键 * * 业务逻辑: * 1. 验证键名的有效性 * 2. 执行Redis del命令删除键 * 3. 检查删除操作的结果 * 4. 记录删除操作日志 * 5. 返回删除是否成功 * * @param key 键名,不能为空 * @returns Promise 删除成功返回true,键不存在返回false * @throws Error 当Redis操作失败时 * * @example * ```typescript * const deleted = await redisService.del('user:123'); * console.log(deleted ? '删除成功' : '键不存在'); * ``` */ async del(key: string): Promise { try { const result = await this.redis.del(key); this.logger.debug(`删除Redis键: ${key}, 结果: ${result > 0}`); return result > 0; } catch (error) { this.logger.error(`删除Redis键失败: ${key}`, error); throw error; } } /** * 检查键是否存在 * * 业务逻辑: * 1. 验证键名的有效性 * 2. 执行Redis exists命令 * 3. 检查返回结果是否大于0 * 4. 处理查询异常 * 5. 返回键的存在状态 * * @param key 键名,不能为空 * @returns Promise 键存在返回true,不存在返回false * @throws Error 当Redis操作失败时 * * @example * ```typescript * const exists = await redisService.exists('user:123'); * if (exists) { * console.log('用户数据存在'); * } * ``` */ async exists(key: string): Promise { try { const result = await this.redis.exists(key); return result > 0; } catch (error) { this.logger.error(`检查Redis键存在性失败: ${key}`, error); throw error; } } /** * 设置键的过期时间 * * 业务逻辑: * 1. 验证键名和TTL参数的有效性 * 2. 执行Redis expire命令设置过期时间 * 3. 记录过期时间设置日志 * 4. 处理设置异常 * * @param key 键名,不能为空 * @param ttl 过期时间(秒),必须大于0 * @returns Promise 操作完成的Promise * @throws Error 当Redis操作失败时 * * @example * ```typescript * await redisService.expire('user:123', 3600); // 1小时后过期 * ``` */ async expire(key: string, ttl: number): Promise { try { await this.redis.expire(key, ttl); this.logger.debug(`设置Redis键过期时间: ${key}, TTL: ${ttl}秒`); } catch (error) { this.logger.error(`设置Redis键过期时间失败: ${key}`, error); throw error; } } /** * 获取键的剩余过期时间 * * 业务逻辑: * 1. 验证键名的有效性 * 2. 执行Redis ttl命令查询剩余时间 * 3. 返回剩余时间或状态码 * 4. 处理查询异常 * * @param key 键名,不能为空 * @returns Promise 剩余时间(秒),-1表示永不过期,-2表示键不存在 * @throws Error 当Redis操作失败时 * * @example * ```typescript * const ttl = await redisService.ttl('user:123'); * if (ttl > 0) { * console.log(`还有${ttl}秒过期`); * } else if (ttl === -1) { * console.log('永不过期'); * } else { * console.log('键不存在'); * } * ``` */ async ttl(key: string): Promise { try { return await this.redis.ttl(key); } catch (error) { this.logger.error(`获取Redis键TTL失败: ${key}`, error); throw error; } } /** * 清空所有数据 * * 业务逻辑: * 1. 执行Redis flushall命令清空所有数据 * 2. 记录清空操作日志 * 3. 处理清空异常 * * @returns Promise 操作完成的Promise * @throws Error 当Redis操作失败时 * * @example * ```typescript * await redisService.flushall(); * console.log('所有数据已清空'); * ``` */ async flushall(): Promise { try { await this.redis.flushall(); this.logger.log('清空所有Redis数据'); } catch (error) { this.logger.error('清空Redis数据失败', error); throw error; } } /** * 设置键值对并指定过期时间 * * 业务逻辑: * 1. 验证键、值和TTL参数的有效性 * 2. 执行Redis setex命令同时设置值和过期时间 * 3. 记录操作日志 * 4. 处理设置异常 * * @param key 键名,不能为空 * @param ttl 过期时间(秒),必须大于0 * @param value 值,支持字符串类型 * @returns Promise 操作完成的Promise * @throws Error 当Redis操作失败时 * * @example * ```typescript * await redisService.setex('session:abc', 1800, 'sessionData'); * ``` */ async setex(key: string, ttl: number, value: string): Promise { try { await this.redis.setex(key, ttl, value); this.logger.debug(`设置Redis键(setex): ${key}, TTL: ${ttl}秒`); } catch (error) { this.logger.error(`设置Redis键失败(setex): ${key}`, error); throw error; } } /** * 键值自增操作 * * 业务逻辑: * 1. 验证键名的有效性 * 2. 执行Redis incr命令进行自增操作 * 3. 获取自增后的新值 * 4. 记录自增操作日志 * 5. 返回新值 * * @param key 键名,不能为空 * @returns Promise 自增后的新值 * @throws Error 当Redis操作失败或值不是数字时 * * @example * ```typescript * const newValue = await redisService.incr('counter'); * console.log(`计数器新值: ${newValue}`); * ``` */ async incr(key: string): Promise { try { const result = await this.redis.incr(key); this.logger.debug(`自增Redis键: ${key}, 新值: ${result}`); return result; } catch (error) { this.logger.error(`自增Redis键失败: ${key}`, error); throw error; } } /** * 向集合添加成员 * * 业务逻辑: * 1. 验证键名和成员的有效性 * 2. 执行Redis sadd命令添加成员到集合 * 3. 记录添加操作日志 * 4. 处理添加异常 * * @param key 集合键名,不能为空 * @param member 要添加的成员,不能为空 * @returns Promise 操作完成的Promise * @throws Error 当Redis操作失败时 * * @example * ```typescript * await redisService.sadd('users', 'user123'); * ``` */ async sadd(key: string, member: string): Promise { try { await this.redis.sadd(key, member); this.logger.debug(`添加集合成员: ${key} -> ${member}`); } catch (error) { this.logger.error(`添加集合成员失败: ${key}`, error); throw error; } } /** * 从集合移除成员 * * 业务逻辑: * 1. 验证键名和成员的有效性 * 2. 执行Redis srem命令从集合中移除成员 * 3. 记录移除操作日志 * 4. 处理移除异常 * * @param key 集合键名,不能为空 * @param member 要移除的成员,不能为空 * @returns Promise 操作完成的Promise * @throws Error 当Redis操作失败时 * * @example * ```typescript * await redisService.srem('users', 'user123'); * ``` */ async srem(key: string, member: string): Promise { try { await this.redis.srem(key, member); this.logger.debug(`移除集合成员: ${key} -> ${member}`); } catch (error) { this.logger.error(`移除集合成员失败: ${key}`, error); throw error; } } /** * 获取集合的所有成员 * * 业务逻辑: * 1. 验证键名的有效性 * 2. 执行Redis smembers命令获取集合所有成员 * 3. 返回成员列表 * 4. 处理查询异常 * * @param key 集合键名,不能为空 * @returns Promise 集合成员列表,集合不存在返回空数组 * @throws Error 当Redis操作失败时 * * @example * ```typescript * const members = await redisService.smembers('users'); * console.log('用户列表:', members); * ``` */ async smembers(key: string): Promise { try { return await this.redis.smembers(key); } catch (error) { this.logger.error(`获取集合成员失败: ${key}`, error); throw error; } } async keys(pattern: string): Promise { try { return await this.redis.keys(pattern); } catch (error) { this.logger.error(`查找Redis键失败: ${pattern}`, error); throw error; } } /** * 模块销毁时的清理操作 * * 业务逻辑: * 1. 检查Redis连接是否存在 * 2. 断开Redis连接 * 3. 记录连接断开日志 * 4. 释放相关资源 * * @returns void 无返回值 * * @example * ```typescript * // NestJS框架会在模块销毁时自动调用 * onModuleDestroy() { * // 自动清理Redis连接 * } * ``` */ onModuleDestroy(): void { if (this.redis) { this.redis.disconnect(); this.logger.log('Redis连接已断开'); } } }