Files
whale-town-end-v2/src/core/redis/real_redis.service.ts
2026-07-23 00:59:00 +08:00

511 lines
14 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 真实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<string>('REDIS_HOST', 'localhost'),
port: this.configService.get<number>('REDIS_PORT', 6379),
password: this.configService.get<string>('REDIS_PASSWORD') || undefined,
db: this.configService.get<number>('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<void> 操作完成的Promise
* @throws Error 当Redis操作失败时
*
* @example
* ```typescript
* await redisService.set('user:123', 'userData', 3600);
* ```
*/
async set(key: string, value: string, ttl?: number): Promise<void> {
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<boolean> {
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<string | null> 键对应的值不存在返回null
* @throws Error 当Redis操作失败时
*
* @example
* ```typescript
* const value = await redisService.get('user:123');
* ```
*/
async get(key: string): Promise<string | null> {
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<boolean> 删除成功返回true键不存在返回false
* @throws Error 当Redis操作失败时
*
* @example
* ```typescript
* const deleted = await redisService.del('user:123');
* console.log(deleted ? '删除成功' : '键不存在');
* ```
*/
async del(key: string): Promise<boolean> {
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<boolean> 键存在返回true不存在返回false
* @throws Error 当Redis操作失败时
*
* @example
* ```typescript
* const exists = await redisService.exists('user:123');
* if (exists) {
* console.log('用户数据存在');
* }
* ```
*/
async exists(key: string): Promise<boolean> {
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<void> 操作完成的Promise
* @throws Error 当Redis操作失败时
*
* @example
* ```typescript
* await redisService.expire('user:123', 3600); // 1小时后过期
* ```
*/
async expire(key: string, ttl: number): Promise<void> {
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<number> 剩余时间(秒),-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<number> {
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<void> 操作完成的Promise
* @throws Error 当Redis操作失败时
*
* @example
* ```typescript
* await redisService.flushall();
* console.log('所有数据已清空');
* ```
*/
async flushall(): Promise<void> {
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<void> 操作完成的Promise
* @throws Error 当Redis操作失败时
*
* @example
* ```typescript
* await redisService.setex('session:abc', 1800, 'sessionData');
* ```
*/
async setex(key: string, ttl: number, value: string): Promise<void> {
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<number> 自增后的新值
* @throws Error 当Redis操作失败或值不是数字时
*
* @example
* ```typescript
* const newValue = await redisService.incr('counter');
* console.log(`计数器新值: ${newValue}`);
* ```
*/
async incr(key: string): Promise<number> {
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<void> 操作完成的Promise
* @throws Error 当Redis操作失败时
*
* @example
* ```typescript
* await redisService.sadd('users', 'user123');
* ```
*/
async sadd(key: string, member: string): Promise<void> {
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<void> 操作完成的Promise
* @throws Error 当Redis操作失败时
*
* @example
* ```typescript
* await redisService.srem('users', 'user123');
* ```
*/
async srem(key: string, member: string): Promise<void> {
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<string[]> 集合成员列表,集合不存在返回空数组
* @throws Error 当Redis操作失败时
*
* @example
* ```typescript
* const members = await redisService.smembers('users');
* console.log('用户列表:', members);
* ```
*/
async smembers(key: string): Promise<string[]> {
try {
return await this.redis.smembers(key);
} catch (error) {
this.logger.error(`获取集合成员失败: ${key}`, error);
throw error;
}
}
async keys(pattern: string): Promise<string[]> {
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连接已断开');
}
}
}