From 7ec761220f7aa52e771c1a3f9061f4ce79e4bef6 Mon Sep 17 00:00:00 2001 From: npmrun <1549469775@qq.com> Date: Fri, 22 May 2026 09:11:35 +0800 Subject: [PATCH] docs: add cache system implementation plan Co-Authored-By: Claude Opus 4.7 --- .../2026-05-22-cache-system-implementation-plan.md | 489 +++++++++++++++++++++ 1 file changed, 489 insertions(+) create mode 100644 docs/superpowers/plans/2026-05-22-cache-system-implementation-plan.md diff --git a/docs/superpowers/plans/2026-05-22-cache-system-implementation-plan.md b/docs/superpowers/plans/2026-05-22-cache-system-implementation-plan.md new file mode 100644 index 0000000..403e3de --- /dev/null +++ b/docs/superpowers/plans/2026-05-22-cache-system-implementation-plan.md @@ -0,0 +1,489 @@ +# 可降级缓存系统实现计划 + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 构建 `packages/cache` 包,实现 Redis 主存储 + Memory 内存降级的通用 K-V 缓存系统 + +**Architecture:** 分层适配器架构:`CacheDriver` 接口定义 get/set/del/exists/clear,`MemoryDriver` 和 `RedisDriver` 分别实现, `CacheManager` 按 drivers 数组顺序尝试调用方透明降级 + +**Tech Stack:** TypeScript, ioredis, Node.js + +--- + +## 文件结构 + +``` +packages/cache/ +├── package.json +├── index.ts +└── lib/ + ├── types.ts ← CacheDriver 接口、CacheManagerOptions、CacheEntry + ├── managers/ + │ └── cache-manager.ts ← CacheManager 实现(降级逻辑) + └── drivers/ + ├── base.ts ← 抽象基类 AbstractCacheDriver + ├── memory-driver.ts ← Map 实现 + └── redis-driver.ts ← ioredis 实现 +``` + +--- + +## Task 1: 项目脚手架 + +**Files:** +- Create: `packages/cache/package.json` +- Create: `packages/cache/index.ts` +- Create: `packages/cache/tsconfig.json` +- Create: `packages/cache/lib/types.ts` + +- [ ] **Step 1: 创建 package.json** + +```json +{ + "name": "cache", + "version": "0.1.0", + "type": "module", + "exports": { + ".": "./index.ts" + }, + "dependencies": { + "ioredis": "^5.4.1" + }, + "devDependencies": { + "@types/node": "^20.0.0" + } +} +``` + +Run: `ls packages/cache/package.json` +Expected: 文件存在 + +- [ ] **Step 2: 创建 types.ts** + +```typescript +export interface CacheEntry { + value: T + expiresAt: number | null // Unix ms,null = 永不过期 +} + +export interface CacheDriver { + name: string + get(key: string): Promise + set(key: string, value: T, ttl?: number): Promise + del(key: string): Promise + exists(key: string): Promise + clear(): Promise +} + +export interface CacheManagerOptions { + drivers: CacheDriver[] + defaultTtl?: number +} +``` + +Run: `cat packages/cache/lib/types.ts` +Expected: 文件存在,CacheDriver、CacheManagerOptions、CacheEntry 导出 + +- [ ] **Step 3: 创建 index.ts 骨架** + +```typescript +export * from './lib/types' +``` + +Run: `cat packages/cache/index.ts` +Expected: `export * from './lib/types'` + +- [ ] **Step 4: 提交** + +```bash +git add packages/cache/package.json packages/cache/index.ts packages/cache/tsconfig.json packages/cache/lib/types.ts +git commit -m "feat(cache): scaffold package and define core types + +Co-Authored-By: Claude Opus 4.7 " +``` + +--- + +## Task 2: MemoryDriver + +**Files:** +- Create: `packages/cache/lib/drivers/memory-driver.ts` + +- [ ] **Step 1: 实现 MemoryDriver** + +```typescript +import type { CacheDriver } from '../types' + +interface MemoryEntry { + value: unknown + expiresAt: number | null +} + +export class MemoryDriver implements CacheDriver { + name = 'memory' + private store = new Map() + + async get(key: string): Promise { + const entry = this.store.get(key) + if (!entry) return null + + if (entry.expiresAt !== null && entry.expiresAt < Date.now()) { + this.store.delete(key) + return null + } + + return entry.value as T + } + + async set(key: string, value: T, ttl = 0): Promise { + const expiresAt = ttl > 0 ? Date.now() + ttl * 1000 : null + this.store.set(key, { value, expiresAt }) + } + + async del(key: string): Promise { + this.store.delete(key) + } + + async exists(key: string): Promise { + const entry = this.store.get(key) + if (!entry) return false + + if (entry.expiresAt !== null && entry.expiresAt < Date.now()) { + this.store.delete(key) + return false + } + + return true + } + + async clear(): Promise { + this.store.clear() + } +} +``` + +Run: `cat packages/cache/lib/drivers/memory-driver.ts` +Expected: 文件存在,类导出 + +- [ ] **Step 2: 更新 index.ts 导出 MemoryDriver** + +```typescript +export * from './lib/types' +export { MemoryDriver } from './lib/drivers/memory-driver' +``` + +Run: `cat packages/cache/index.ts` +Expected: 包含 MemoryDriver 导出 + +- [ ] **Step 3: 提交** + +```bash +git add packages/cache/lib/drivers/memory-driver.ts packages/cache/index.ts +git commit -m "feat(cache): implement MemoryDriver + +Co-Authored-By: Claude Opus 4.7 " +``` + +--- + +## Task 3: RedisDriver + +**Files:** +- Create: `packages/cache/lib/drivers/redis-driver.ts` + +- [ ] **Step 1: 实现 RedisDriver** + +```typescript +import Redis from 'ioredis' +import type { CacheDriver } from '../types' + +export interface RedisDriverOptions { + host: string + port: number + password?: string + db?: number +} + +export class RedisDriver implements CacheDriver { + name = 'redis' + private redis: Redis + + constructor(options: RedisDriverOptions) { + this.redis = new Redis({ + host: options.host, + port: options.port, + password: options.password, + db: options.db ?? 0, + lazyConnect: true, + }) + } + + private serialize(value: T): string { + return JSON.stringify(value) + } + + private deserialize(data: string): T { + return JSON.parse(data) as T + } + + async get(key: string): Promise { + const data = await this.redis.get(key) + if (!data) return null + return this.deserialize(data) + } + + async set(key: string, value: T, ttl = 0): Promise { + const serialized = this.serialize(value) + if (ttl > 0) { + await this.redis.set(key, serialized, 'EX', ttl) + } else { + await this.redis.set(key, serialized) + } + } + + async del(key: string): Promise { + await this.redis.del(key) + } + + async exists(key: string): Promise { + const result = await this.redis.exists(key) + return result === 1 + } + + async clear(): Promise { + await this.redis.flushdb() + } + + async disconnect(): Promise { + await this.redis.quit() + } +} +``` + +Run: `cat packages/cache/lib/drivers/redis-driver.ts` +Expected: 文件存在,RedisDriver 导出,connect lazy + +- [ ] **Step 2: 更新 index.ts 导出 RedisDriver** + +```typescript +export * from './lib/types' +export { MemoryDriver } from './lib/drivers/memory-driver' +export { RedisDriver } from './lib/drivers/redis-driver' +export type { RedisDriverOptions } from './lib/drivers/redis-driver' +``` + +Run: `cat packages/cache/index.ts` +Expected: 包含 RedisDriver 导出 + +- [ ] **Step 3: 提交** + +```bash +git add packages/cache/lib/drivers/redis-driver.ts packages/cache/index.ts +git commit -m "feat(cache): implement RedisDriver with ioredis + +Co-Authored-By: Claude Opus 4.7 " +``` + +--- + +## Task 4: CacheManager + +**Files:** +- Create: `packages/cache/lib/managers/cache-manager.ts` + +- [ ] **Step 1: 实现 CacheManager** + +```typescript +import type { CacheDriver, CacheManagerOptions } from '../types' + +export class CacheManager { + private drivers: CacheDriver[] + private defaultTtl: number + + constructor(options: CacheManagerOptions) { + if (!options.drivers || options.drivers.length === 0) { + throw new Error('[cache] at least one driver is required') + } + this.drivers = options.drivers + this.defaultTtl = options.defaultTtl ?? 0 + } + + async get(key: string): Promise { + for (const driver of this.drivers) { + try { + const value = await driver.get(key) + return value + } catch (err) { + console.warn(`[cache] ${driver.name} get failed:`, err) + continue + } + } + return null + } + + async set(key: string, value: T, ttl?: number): Promise { + const effectiveTtl = ttl ?? this.defaultTtl + await Promise.all( + this.drivers.map(async (driver) => { + try { + await driver.set(key, value, effectiveTtl) + } catch (err) { + console.warn(`[cache] ${driver.name} set failed:`, err) + } + }) + ) + } + + async del(key: string): Promise { + await Promise.all( + this.drivers.map(async (driver) => { + try { + await driver.del(key) + } catch (err) { + console.warn(`[cache] ${driver.name} del failed:`, err) + } + }) + ) + } + + async exists(key: string): Promise { + for (const driver of this.drivers) { + try { + const exists = await driver.exists(key) + if (exists) return true + } catch (err) { + console.warn(`[cache] ${driver.name} exists failed:`, err) + continue + } + } + return false + } + + async clear(): Promise { + await Promise.all( + this.drivers.map(async (driver) => { + try { + await driver.clear() + } catch (err) { + console.warn(`[cache] ${driver.name} clear failed:`, err) + } + }) + ) + } +} +``` + +Run: `cat packages/cache/lib/managers/cache-manager.ts` +Expected: 文件存在,CacheManager 导出 + +- [ ] **Step 2: 更新 index.ts 导出 CacheManager** + +```typescript +export * from './lib/types' +export { MemoryDriver } from './lib/drivers/memory-driver' +export { RedisDriver } from './lib/drivers/redis-driver' +export { CacheManager } from './lib/managers/cache-manager' +export type { RedisDriverOptions } from './lib/drivers/redis-driver' +``` + +Run: `cat packages/cache/index.ts` +Expected: 包含 CacheManager 导出 + +- [ ] **Step 3: 提交** + +```bash +git add packages/cache/lib/managers/cache-manager.ts packages/cache/index.ts +git commit -m "feat(cache): implement CacheManager with fallback logic + +Co-Authored-By: Claude Opus 4.7 " +``` + +--- + +## Task 5: createCache 工厂函数 + +**Files:** +- Modify: `packages/cache/index.ts` + +- [ ] **Step 1: 实现 createCache 工厂函数** + +```typescript +import { CacheManager } from './lib/managers/cache-manager' +import { MemoryDriver } from './lib/drivers/memory-driver' +import { RedisDriver } from './lib/drivers/redis-driver' +import type { CacheDriver, CacheManagerOptions } from './lib/types' +import type { RedisDriverOptions } from './lib/drivers/redis-driver' + +export * from './lib/types' +export { MemoryDriver } from './lib/drivers/memory-driver' +export { RedisDriver } from './lib/drivers/redis-driver' +export { CacheManager } from './lib/managers/cache-manager' +export type { RedisDriverOptions } from './lib/drivers/redis-driver' + +export interface CacheFactoryOptions { + redis?: RedisDriverOptions + memory?: boolean + defaultTtl?: number +} + +export function createCache(options: CacheFactoryOptions): CacheManager { + const drivers: CacheDriver[] = [] + + if (options.redis) { + drivers.push(new RedisDriver(options.redis)) + } + + if (options.memory !== false) { + drivers.push(new MemoryDriver()) + } + + if (drivers.length === 0) { + throw new Error('[cache] at least one driver (redis or memory) is required') + } + + return new CacheManager({ drivers, defaultTtl: options.defaultTtl }) +} +``` + +Run: `cat packages/cache/index.ts` +Expected: createCache 工厂函数存在 + +- [ ] **Step 2: 提交** + +```bash +git add packages/cache/index.ts +git commit -m "feat(cache): add createCache factory function + +Co-Authored-By: Claude Opus 4.7 " +``` + +--- + +## Task 6: 验证构建 + +- [ ] **Step 1: 安装依赖** + +Run: `cd /home/dash/coding/nuxt-app && bun add -w cache` +Expected: ioredis 被添加到 packages/cache/package.json + +- [ ] **Step 2: 类型检查** + +Run: `cd packages/cache && npx tsc --noEmit` +Expected: 无错误输出 + +- [ ] **Step 3: 提交** + +```bash +git add -A +git commit -m "chore(cache): add dependencies and verify build + +Co-Authored-By: Claude Opus 4.7 " +``` + +--- + +## 自检清单 + +- [ ] spec 覆盖:CacheDriver 接口 ✓ / MemoryDriver ✓ / RedisDriver ✓ / CacheManager ✓ / createCache ✓ / 降级逻辑 ✓ +- [ ] 无 placeholder:无 TBD/TODO/类似占位符 +- [ ] 类型一致性:get/set/del/exists/clear 方法签名在各驱动一致 ✓ \ No newline at end of file