Files
TrueGrowth/docs/UNIFIED_CACHE_DESIGN.md

470 lines
13 KiB
Markdown
Raw Permalink 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.
# 统一图片缓存方案设计文档
## 文档信息
- **创建日期**: 2026-01-07
- **版本**: 1.0
- **状态**: 实现中
## 背景
当前项目中存在三个独立的缓存系统:
1. Service Worker 的 Cache API`drawnix-images`
2. 应用层的 Media Cache Service`drawnix-media-cache` IndexedDB
3. 应用层的 URL Cache Service`aitu-url-cache` IndexedDB
这种分散的缓存架构导致了以下问题:
- 缓存逻辑不统一,维护困难
- Service Worker 缓存没有任务元数据(模型、提示词、参数)
- 无法根据缓存时间智能决策传 URL 还是 base64
- 缺少缓存满时的用户友好提示
## 设计目标
1. **统一缓存管理**:一个服务协调 SW 和应用层缓存
2. **完整元数据存储**:记录缓存时间、生成参数、模型信息
3. **智能图片传递**:调用 AI 接口时1天内用 URL超过1天用 base64
4. **用户可控清理**:缓存满时提示打开素材库手动删除
5. **向下兼容**:从旧数据库迁移数据
## 架构设计
### 整体架构
```
┌─────────────────────────────────────────────────────────────┐
│ 浏览器主线程 │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Unified Cache Manager (统一缓存管理器) │ │
│ │ │ │
│ │ - 协调 SW 和 IndexedDB │ │
│ │ - 处理元数据存储 │ │
│ │ - 智能图片传递逻辑 │ │
│ │ - 缓存满检测和提示 │ │
│ └──────────────────────────────────────────────────────┘ │
│ ↕ (postMessage) │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ IndexedDB: drawnix-unified-cache │ │
│ │ │ │
│ │ 存储结构: │ │
│ │ - url (主键) │ │
│ │ - type, mimeType, size │ │
│ │ - cachedAt, lastUsed │ │
│ │ - metadata: { taskId, prompt, model, params } │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
↕ (fetch intercept)
┌─────────────────────────────────────────────────────────────┐
│ Service Worker 线程 │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 图片请求拦截器 │ │
│ │ │ │
│ │ 1. 拦截图片请求 │ │
│ │ 2. 检查 Cache API │ │
│ │ ↓ 命中 → 直接返回 │ │
│ │ ↓ 未命中 → 从网络获取 │ │
│ │ 3. 存入 Cache API │ │
│ │ 4. 通知主线程更新 IndexedDB 元数据 │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Cache API: drawnix-images │ │
│ │ (快速响应,不存元数据) │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
### 数据库结构
#### IndexedDB: `drawnix-unified-cache`
```typescript
interface CachedMedia {
url: string; // 主键
type: 'image' | 'video';
mimeType: string;
size: number; // 字节数
cachedAt: number; // 缓存时间戳
lastUsed: number; // 最后使用时间戳
metadata: {
taskId?: string; // 任务ID
prompt?: string; // 生成提示词
model?: string; // 使用的模型
params?: any; // 生成参数
[key: string]: any; // 扩展字段
};
}
```
**索引**
- `url` (主键)
- `cachedAt` (用于按时间排序)
- `lastUsed` (用于 LRU 清理)
### 核心流程
#### 1. 图片生成并插入画布
```
任务完成 → 获得图片 URL
插入画布(使用原始 URL不变更现有逻辑
浏览器发起图片请求
SW 拦截请求
检查 Cache API
├─ 命中 → 立即返回缓存
└─ 未命中
从网络获取图片
存入 Cache API
postMessage 通知主线程
{
type: 'IMAGE_CACHED',
url: '...',
size: 12345,
mimeType: 'image/png'
}
主线程收到消息
存入 IndexedDB (包含元数据)
```
#### 2. 调用 AI 接口传递图片
```
需要传图片给 AI 接口
调用 UnifiedCacheManager.getImageForAI(url)
查询 IndexedDB 获取 cachedAt
计算 age = Date.now() - cachedAt
age < 24小时
├─ 是 → 返回 { type: 'url', value: originalUrl }
└─ 否
从 Cache API 获取 blob通过 fetch
压缩图片(如果需要)
转换为 base64
返回 { type: 'base64', value: base64Data }
```
#### 3. 缓存满处理
```
Cache API 或 IndexedDB 写入失败
捕获 QuotaExceededError
触发 onQuotaExceeded 回调
显示 TDesign Dialog:
"缓存空间已满,是否打开素材库清理缓存?"
[取消] [打开素材库]
用户点击"打开素材库"
打开媒体库面板(显示所有缓存项)
用户选择并删除缓存项
删除流程:
1. 从 IndexedDB 删除记录
2. postMessage 通知 SW 删除 Cache API 条目
3. 刷新缓存列表
```
## API 设计
### UnifiedCacheService
```typescript
class UnifiedCacheService {
/**
* 注册图片元数据(任务完成时调用)
*/
async registerImageMetadata(
url: string,
metadata: {
taskId: string;
prompt?: string;
model?: string;
params?: any;
}
): Promise<void>;
/**
* 获取适合传给 AI 的图片数据
* 自动决策返回 URL 或 base64
*/
async getImageForAI(
url: string,
options?: {
maxAge?: number; // 默认 24 小时
maxSize?: number; // 最大文件大小(超过则压缩)
quality?: number; // 压缩质量 0-1
}
): Promise<{
type: 'url' | 'base64';
value: string;
}>;
/**
* 获取缓存信息
*/
async getCacheInfo(url: string): Promise<{
isCached: boolean;
cachedAt?: number;
age?: number;
size?: number;
metadata?: any;
}>;
/**
* 手动缓存图片
*/
async cacheImage(
url: string,
metadata?: any
): Promise<boolean>;
/**
* 删除缓存
*/
async deleteCache(url: string): Promise<void>;
/**
* 批量删除缓存
*/
async deleteCacheBatch(urls: string[]): Promise<number>;
/**
* 获取所有缓存项元数据
*/
async getAllCacheMetadata(): Promise<CachedMedia[]>;
/**
* 获取存储使用情况
*/
async getStorageUsage(): Promise<{
used: number;
quota: number;
percentage: number;
}>;
/**
* 清空所有缓存
*/
async clearAllCache(): Promise<void>;
/**
* 监听缓存满事件
*/
onQuotaExceeded(callback: () => void): () => void;
/**
* 订阅缓存变化
*/
subscribe(callback: () => void): () => void;
}
```
### Service Worker 消息协议
#### 主线程 → SW
```typescript
// 删除缓存请求
{
type: 'DELETE_CACHE',
url: string
}
// 批量删除缓存请求
{
type: 'DELETE_CACHE_BATCH',
urls: string[]
}
// 清空所有缓存
{
type: 'CLEAR_ALL_CACHE'
}
```
#### SW → 主线程
```typescript
// 图片已缓存通知
{
type: 'IMAGE_CACHED',
url: string,
size: number,
mimeType: string,
timestamp: number
}
// 缓存已删除通知
{
type: 'CACHE_DELETED',
url: string
}
// 缓存空间不足警告
{
type: 'QUOTA_WARNING',
usage: number,
quota: number
}
```
## 数据迁移方案
### 迁移步骤
1. **检测旧数据库**
- 检查 `aitu-media-cache` 是否存在
- 检查 `aitu-url-cache` 是否存在
2. **创建新数据库**
- 打开 `drawnix-unified-cache`
- 创建 object store 和索引
3. **迁移 Media Cache 数据**
```typescript
// 从 aitu-media-cache.media 读取
oldRecord = {
url, originalUrl, taskId, prompt,
blob, type, mimeType, size, cachedAt
}
// 转换为新格式
newRecord = {
url: oldRecord.originalUrl || oldRecord.url,
type: oldRecord.type,
mimeType: oldRecord.mimeType,
size: oldRecord.size,
cachedAt: oldRecord.cachedAt || Date.now(),
lastUsed: Date.now(),
metadata: {
taskId: oldRecord.taskId,
prompt: oldRecord.prompt
}
}
```
4. **迁移 URL Cache 数据**
```typescript
// 从 aitu-url-cache.media-cache 读取
oldRecord = {
url, type, data (base64), blob, mimeType, size, cachedAt
}
// 转换为新格式(不迁移 blob让 SW 重新缓存)
newRecord = {
url: oldRecord.url,
type: oldRecord.type,
mimeType: oldRecord.mimeType,
size: oldRecord.size,
cachedAt: oldRecord.cachedAt || Date.now(),
lastUsed: Date.now(),
metadata: {}
}
```
5. **删除旧数据库**
```typescript
indexedDB.deleteDatabase('aitu-media-cache');
indexedDB.deleteDatabase('aitu-url-cache');
```
6. **记录迁移状态**
- localStorage 设置标记:`drawnix_cache_migrated: 'true'`
### 迁移触发时机
- 应用启动时自动检测并迁移
- 迁移过程显示进度提示(可选)
- 迁移完成后发送通知
## 实现计划
### Phase 1: 基础服务实现(核心)
- [x] 创建设计文档
- [ ] 实现 `UnifiedCacheService` 类
- [ ] 实现 IndexedDB 操作
- [ ] 实现与 SW 的通信协议
- [ ] 实现数据迁移逻辑
### Phase 2: Service Worker 集成
- [ ] 修改 SW 图片缓存逻辑
- [ ] 添加缓存通知消息
- [ ] 实现 SW 端的缓存删除响应
### Phase 3: 智能传递逻辑
- [ ] 实现 `getImageForAI()` 方法
- [ ] 添加图片压缩功能
- [ ] 添加缓存时间检测
### Phase 4: UI 集成
- [ ] 更新 TaskItem 使用新服务
- [ ] 实现缓存满提示 Dialog
- [ ] 更新媒体库显示缓存信息
- [ ] 添加缓存管理 UI
### Phase 5: 测试与优化
- [ ] 单元测试
- [ ] 集成测试
- [ ] 性能优化
- [ ] 文档完善
## 注意事项
1. **缓存策略**
- Service Worker 的 Cache API 用于快速响应(不存元数据)
- IndexedDB 用于元数据管理和查询
- 不在 IndexedDB 中存储 blob避免双重存储
2. **性能考虑**
- SW 缓存命中后立即返回,不阻塞
- 元数据更新使用异步消息,不影响图片加载速度
- 批量操作使用事务优化
3. **兼容性**
- 保持现有插入画布逻辑不变
- 渐进式迁移,旧代码依然可用
- 提供降级方案(如 SW 不可用)
4. **安全性**
- 验证 URL 来源
- 限制缓存大小
- 清理过期缓存
## 参考资料
- [Service Worker API - MDN](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API)
- [IndexedDB API - MDN](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API)
- [Cache API - MDN](https://developer.mozilla.org/en-US/docs/Web/API/Cache)
- [Storage API - MDN](https://developer.mozilla.org/en-US/docs/Web/API/Storage_API)
## 更新日志
- 2026-01-07: 初始版本,完成架构设计