# All files / middleware cache.ts

0% Statements 0/57
0% Branches 0/1
0% Functions 0/1
0% Lines 0/57

Press n or j to go to the next uncovered block, b, p or k for the previous block.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98

     import { redisService } from '../services/redis.service';

    interface CacheOptions {
      ttl: number;           // 缓存时间（秒）
      keyPrefix?: string;    // 键前缀
      keyGenerator?: (ctx: any) => string; // 自定义键生成器
    }

    /**
     * 缓存中间件
     * @param options 缓存配置
     */
    export function createCache(options: CacheOptions) {
      return async (ctx: any, next: any) => {
        // 如果 Redis 不可用，跳过缓存
        if (!redisService.isAvailable()) {
          return await next();
        }

        try {
          // 生成缓存键
          const cacheKey = options.keyGenerator
            ? `${options.keyPrefix || 'cache'}:${options.keyGenerator(ctx)}`
            : `${options.keyPrefix || 'cache'}:${ctx.method}:${ctx.url}`;

          // 尝试从缓存获取
          const cachedData = await redisService.get(cacheKey);
          if (cachedData) {
            ctx.set('X-Cache', 'HIT');
            ctx.body = JSON.parse(cachedData);
            return;
          }

          // 执行请求
          await next();

          // 缓存成功的响应
          if (ctx.status === 200 && ctx.body) {
            await redisService.set(cacheKey, JSON.stringify(ctx.body), options.ttl);
            ctx.set('X-Cache', 'MISS');
          }
        } catch (error) {
          console.error('[Cache] 缓存中间件错误:', (error as Error).message);
          // 缓存失败不影响请求
          return await next();
        }
      };
    }

    /**
     * 清除缓存中间件
     * @param keyPrefix 键前缀
     */
    export function clearCache(keyPrefix: string) {
      return async (ctx: any, next: any) => {
        if (redisService.isAvailable()) {
          await redisService.delPattern(`${keyPrefix}:*`);
        }
        return await next();
      };
    }

    /**
     * 常用缓存配置
     */

    // 用户信息缓存（5 分钟）
    export const userCache = createCache({
      ttl: 300,
      keyPrefix: 'user',
      keyGenerator: (ctx) => ctx.state.user?.id || 'anonymous',
    });

    // 音色列表缓存（1 小时）
    export const voiceListCache = createCache({
      ttl: 3600,
      keyPrefix: 'voice:list',
    });

    // 书籍详情缓存（10 分钟）
    export const bookDetailCache = createCache({
      ttl: 600,
      keyPrefix: 'book:detail',
      keyGenerator: (ctx) => ctx.params.id || ctx.query.id,
    });

    // 热门书籍缓存（5 分钟）
    export const hotBooksCache = createCache({
      ttl: 300,
      keyPrefix: 'book:hot',
    });

    // 会员权益缓存（1 小时）
    export const memberBenefitsCache = createCache({
      ttl: 3600,
      keyPrefix: 'member:benefits',
    });