alioth/star-tune/src/service/redis/redis.service.ts
nie 804d14d8fd feat(api):
1. 注册接口
2. 邮箱验证
3. 发送验证码
2025-05-31 01:25:24 +08:00

167 lines
4.4 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.

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<RedisConfig>('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<string>('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<void> {
const expireTime = ttl || this.configService.get('redis.ttl');
await this.client.set(key, value, { EX: expireTime });
}
/**
* 获取键值
* @param key 键
* @returns 值
*/
async get(key: string): Promise<string | null> {
return await this.client.get(key);
}
/**
* 删除键
* @param key 键
*/
async del(key: string): Promise<void> {
await this.client.del(key);
}
/**
* 检查键是否存在
* @param key 键
* @returns 是否存在
*/
async exists(key: string): Promise<boolean> {
return (await this.client.exists(key)) === 1;
}
/**
* 设置键的过期时间
* @param key 键
* @param ttl 过期时间(秒)
*/
async expire(key: string, ttl: number): Promise<void> {
await this.client.expire(key, ttl);
}
/**
* 尝试获取分布式锁
* @param key 锁的键
* @param ttl 锁的过期时间(秒)
* @returns 是否成功获取锁
*/
async lock(key: string, ttl: number = 30): Promise<boolean> {
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<void> {
const lockKey = `lock:${key}`;
await this.client.del(lockKey);
}
/**
* 获取键的剩余过期时间
* @param key 键
* @returns 剩余过期时间(秒),如果键不存在返回-2如果键没有过期时间返回-1
*/
async ttl(key: string): Promise<number> {
return await this.client.ttl(key);
}
/**
* 使用自动释放的分布式锁执行操作
* @param key 锁的键
* @param fn 需要在锁中执行的函数
* @param ttl 锁的过期时间(秒)
* @returns 函数执行的结果
*/
async withLock<T>(
key: string,
fn: () => Promise<T>,
ttl: number = 30,
): Promise<T> {
const locked = await this.lock(key, ttl);
if (!locked) {
throw new Error('无法获取锁');
}
try {
return await fn();
} finally {
await this.unlock(key);
}
}
}