import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { createClient, RedisClientType } from 'redis'; import { CustomLogger } from '@/common/logger/logger.service'; // Redis 配置接口 interface RedisConfig { connectName: string; username: string; password: string; database: number; host: string; port: number; ttl: number; } @Injectable() export class RedisService implements OnModuleInit, OnModuleDestroy { public client: RedisClientType; constructor( private configService: ConfigService, private logger: CustomLogger, ) { const config = this.configService.get('redis'); if (!config) { throw new Error('Redis 配置未找到'); } this.client = createClient({ name: config.connectName, username: config.username, password: config.password, database: config.database, url: `redis://${config.username}:${config.password}@${config.host}:${config.port}/${config.database}`, }); this.client.on('connect', () => { const connectName = this.configService.get('redis.connectName'); void this.client.set('SI HI', connectName || '').then((result) => { this.logger.log(result || '', 'InitRedis'); }); }); this.client.on('error', (err: Error) => { this.logger.error(err.message, err.stack, 'InitRedis'); }); } async onModuleInit() { await this.client.connect(); } async onModuleDestroy() { await this.client.quit(); } /** * 设置键值对 * @param key 键 * @param value 值 * @param ttl 过期时间(秒) */ async set(key: string, value: string, ttl?: number): Promise { const expireTime = ttl || this.configService.get('redis.ttl'); await this.client.set(key, value, { EX: expireTime }); } /** * 获取键值 * @param key 键 * @returns 值 */ async get(key: string): Promise { return await this.client.get(key); } /** * 删除键 * @param key 键 */ async del(key: string): Promise { await this.client.del(key); } /** * 检查键是否存在 * @param key 键 * @returns 是否存在 */ async exists(key: string): Promise { return (await this.client.exists(key)) === 1; } /** * 设置键的过期时间 * @param key 键 * @param ttl 过期时间(秒) */ async expire(key: string, ttl: number): Promise { await this.client.expire(key, ttl); } /** * 尝试获取分布式锁 * @param key 锁的键 * @param ttl 锁的过期时间(秒) * @returns 是否成功获取锁 */ async lock(key: string, ttl: number = 30): Promise { const lockKey = `lock:${key}`; const lockValue = Date.now().toString(); // 使用 SET NX 命令尝试获取锁 const result = await this.client.set(lockKey, lockValue, { NX: true, // 只有当key不存在时才设置 EX: ttl, // 设置过期时间 }); return result === 'OK'; } /** * 释放分布式锁 * @param key 锁的键 */ async unlock(key: string): Promise { const lockKey = `lock:${key}`; await this.client.del(lockKey); } /** * 获取键的剩余过期时间 * @param key 键 * @returns 剩余过期时间(秒),如果键不存在返回-2,如果键没有过期时间返回-1 */ async ttl(key: string): Promise { return await this.client.ttl(key); } /** * 使用自动释放的分布式锁执行操作 * @param key 锁的键 * @param fn 需要在锁中执行的函数 * @param ttl 锁的过期时间(秒) * @returns 函数执行的结果 */ async withLock( key: string, fn: () => Promise, ttl: number = 30, ): Promise { const locked = await this.lock(key, ttl); if (!locked) { throw new Error('无法获取锁'); } try { return await fn(); } finally { await this.unlock(key); } } }