🚀 核心更新: - ✅ 完善 NestJS 企业级架构设计 - ✅ 优化配置中心和基础设施层 - ✅ 增强第三方服务集成能力 - ✅ 完善多租户架构支持 - 🎯 对标 Java Spring Boot 和 PHP ThinkPHP 📦 新增文件: - wwjcloud-nest 完整框架结构 - Docker 容器化配置 - 管理后台界面 - 数据库迁移脚本 🔑 Key: ebb38b43ec39f355f071294fd1cf9c42
329 lines
7.7 KiB
TypeScript
329 lines
7.7 KiB
TypeScript
import { Injectable, Inject, Logger } from '@nestjs/common';
|
|
import type {
|
|
CacheInterface,
|
|
CacheOptions,
|
|
CacheHelper,
|
|
GroupCacheInterface,
|
|
} from './cache.interface';
|
|
|
|
/**
|
|
* 缓存服务
|
|
* 基于 NestJS 官方示例实现
|
|
* 参考: https://docs.nestjs.cn/fundamentals/dependency-injection
|
|
* 对应 Java: CachedService
|
|
*/
|
|
@Injectable()
|
|
export class CacheService implements CacheInterface {
|
|
private readonly logger = new Logger(CacheService.name);
|
|
|
|
constructor(
|
|
@Inject('CACHE_PROVIDER') private readonly cacheProvider: CacheInterface,
|
|
@Inject('GROUP_CACHE_PROVIDER')
|
|
private readonly groupCacheProvider: GroupCacheInterface,
|
|
) {}
|
|
|
|
/**
|
|
* 获取缓存
|
|
*/
|
|
async get<T = any>(key: string): Promise<T | null> {
|
|
try {
|
|
return await this.cacheProvider.get<T>(key);
|
|
} catch (error) {
|
|
this.logger.error(`Failed to get cache key: ${key}`, error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 设置缓存
|
|
*/
|
|
async set(key: string, value: any, ttl?: number): Promise<void> {
|
|
try {
|
|
await this.cacheProvider.set(key, value, ttl);
|
|
} catch (error) {
|
|
this.logger.error(`Failed to set cache key: ${key}`, error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 删除缓存
|
|
*/
|
|
async del(key: string): Promise<void> {
|
|
try {
|
|
await this.cacheProvider.del(key);
|
|
} catch (error) {
|
|
this.logger.error(`Failed to delete cache key: ${key}`, error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 批量删除缓存
|
|
*/
|
|
async delMany(keys: string[]): Promise<void> {
|
|
try {
|
|
await this.cacheProvider.delMany(keys);
|
|
} catch (error) {
|
|
this.logger.error(
|
|
`Failed to delete cache keys: ${keys.join(', ')}`,
|
|
error,
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 检查缓存是否存在
|
|
*/
|
|
async exists(key: string): Promise<boolean> {
|
|
try {
|
|
return await this.cacheProvider.exists(key);
|
|
} catch (error) {
|
|
this.logger.error(`Failed to check cache key: ${key}`, error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 设置过期时间
|
|
*/
|
|
async expire(key: string, ttl: number): Promise<void> {
|
|
try {
|
|
await this.cacheProvider.expire(key, ttl);
|
|
} catch (error) {
|
|
this.logger.error(`Failed to set expire for cache key: ${key}`, error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取过期时间
|
|
*/
|
|
async ttl(key: string): Promise<number> {
|
|
try {
|
|
return await this.cacheProvider.ttl(key);
|
|
} catch (error) {
|
|
this.logger.error(`Failed to get ttl for cache key: ${key}`, error);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取所有键
|
|
*/
|
|
async keys(pattern?: string): Promise<string[]> {
|
|
try {
|
|
return await this.cacheProvider.keys(pattern);
|
|
} catch (error) {
|
|
this.logger.error(
|
|
`Failed to get cache keys with pattern: ${pattern}`,
|
|
error,
|
|
);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 清空所有缓存
|
|
*/
|
|
async flush(): Promise<void> {
|
|
try {
|
|
await this.cacheProvider.flush();
|
|
} catch (error) {
|
|
this.logger.error('Failed to flush cache', error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取缓存统计信息
|
|
*/
|
|
async stats() {
|
|
try {
|
|
return await this.cacheProvider.stats();
|
|
} catch (error) {
|
|
this.logger.error('Failed to get cache stats', error);
|
|
return {
|
|
hits: 0,
|
|
misses: 0,
|
|
keys: 0,
|
|
memory: 0,
|
|
uptime: 0,
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 缓存装饰器实现
|
|
* 对应 Java: cache(String key, CacheHelper<T> cacheHelper)
|
|
*/
|
|
async cache<T>(key: string, cacheHelper: CacheHelper<T>): Promise<T> {
|
|
return this.cacheWithOptions<T>({ key }, cacheHelper);
|
|
}
|
|
|
|
/**
|
|
* 带选项的缓存
|
|
* 对应 Java: cache(boolean cache, String key, CacheHelper<T> cacheHelper)
|
|
*/
|
|
async cacheWithOptions<T>(
|
|
options: CacheOptions,
|
|
cacheHelper: CacheHelper<T>,
|
|
): Promise<T> {
|
|
const { key, ttl, enabled = true, condition } = options;
|
|
|
|
if (!enabled || !key) {
|
|
return await cacheHelper.execute(key || '');
|
|
}
|
|
|
|
try {
|
|
// 尝试从缓存获取
|
|
let result = await this.get<T>(key);
|
|
|
|
if (result === null) {
|
|
// 缓存未命中,执行函数
|
|
result = await cacheHelper.execute(key);
|
|
|
|
if (result !== null && result !== undefined) {
|
|
// 检查缓存条件
|
|
if (!condition || condition([], result)) {
|
|
await this.set(key, result, ttl);
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
} catch (error) {
|
|
this.logger.error(`Cache operation failed for key: ${key}`, error);
|
|
// 降级到直接执行
|
|
return await cacheHelper.execute(key);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 记住结果(带参数)
|
|
* 对应 Java: remember(List<Object> paramList, CacheHelper<T> cacheHelper)
|
|
*/
|
|
async remember<T>(paramList: any[], cacheHelper: CacheHelper<T>): Promise<T> {
|
|
const key = this.computeUniqueKey(paramList);
|
|
return this.cache<T>(key, cacheHelper);
|
|
}
|
|
|
|
/**
|
|
* 记住对象结果
|
|
* 对应 Java: rememberObject(List<Object> paramList, CacheHelper<T> cacheHelper)
|
|
*/
|
|
async rememberObject<T>(
|
|
paramList: any[],
|
|
cacheHelper: CacheHelper<T>,
|
|
): Promise<T> {
|
|
return this.remember<T>(paramList, cacheHelper);
|
|
}
|
|
|
|
/**
|
|
* 分组缓存
|
|
* 对应 Java: tag(String group)
|
|
*/
|
|
tag(group: string) {
|
|
return {
|
|
set: (key: string, value: any, ttl?: number) =>
|
|
this.groupCacheProvider.set(group, key, value, ttl),
|
|
get: <T = any>(key: string) => this.groupCacheProvider.get<T>(group, key),
|
|
del: (key: string) => this.groupCacheProvider.del(group, key),
|
|
clear: () => this.groupCacheProvider.clear(group),
|
|
keys: () => this.groupCacheProvider.keys(group),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 计算唯一键
|
|
* 对应 Java: CacheUtils.computeUniqueKey(paramList)
|
|
*/
|
|
private computeUniqueKey(paramList: any[]): string {
|
|
if (!paramList || paramList.length === 0) {
|
|
return 'empty';
|
|
}
|
|
|
|
const keyParts = paramList.map((param) => {
|
|
if (param === null || param === undefined) {
|
|
return 'null';
|
|
}
|
|
if (typeof param === 'object') {
|
|
return JSON.stringify(param);
|
|
}
|
|
return String(param);
|
|
});
|
|
|
|
return keyParts.join(':');
|
|
}
|
|
|
|
/**
|
|
* 批量操作
|
|
*/
|
|
async mget<T = any>(keys: string[]): Promise<(T | null)[]> {
|
|
const results: (T | null)[] = [];
|
|
for (const key of keys) {
|
|
results.push(await this.get<T>(key));
|
|
}
|
|
return results;
|
|
}
|
|
|
|
/**
|
|
* 批量设置
|
|
*/
|
|
async mset(
|
|
keyValuePairs: Array<{ key: string; value: any; ttl?: number }>,
|
|
): Promise<void> {
|
|
for (const { key, value, ttl } of keyValuePairs) {
|
|
await this.set(key, value, ttl);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 原子递增
|
|
*/
|
|
async incr(key: string, increment: number = 1): Promise<number> {
|
|
const current = (await this.get<number>(key)) || 0;
|
|
const newValue = current + increment;
|
|
await this.set(key, newValue);
|
|
return newValue;
|
|
}
|
|
|
|
/**
|
|
* 原子递减
|
|
*/
|
|
async decr(key: string, decrement: number = 1): Promise<number> {
|
|
return this.incr(key, -decrement);
|
|
}
|
|
|
|
/**
|
|
* 设置哈希字段
|
|
*/
|
|
async hset(key: string, field: string, value: any): Promise<void> {
|
|
const hash = (await this.get<Record<string, any>>(key)) || {};
|
|
hash[field] = value;
|
|
await this.set(key, hash);
|
|
}
|
|
|
|
/**
|
|
* 获取哈希字段
|
|
*/
|
|
async hget<T = any>(key: string, field: string): Promise<T | null> {
|
|
const hash = await this.get<Record<string, any>>(key);
|
|
return hash ? hash[field] || null : null;
|
|
}
|
|
|
|
/**
|
|
* 删除哈希字段
|
|
*/
|
|
async hdel(key: string, field: string): Promise<void> {
|
|
const hash = await this.get<Record<string, any>>(key);
|
|
if (hash) {
|
|
delete hash[field];
|
|
await this.set(key, hash);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取哈希所有字段
|
|
*/
|
|
async hgetall(key: string): Promise<Record<string, any>> {
|
|
return (await this.get<Record<string, any>>(key)) || {};
|
|
}
|
|
}
|