Initial TrueGrowth source import
This commit is contained in:
469
docs/UNIFIED_CACHE_DESIGN.md
Normal file
469
docs/UNIFIED_CACHE_DESIGN.md
Normal file
@@ -0,0 +1,469 @@
|
||||
# 统一图片缓存方案设计文档
|
||||
|
||||
## 文档信息
|
||||
|
||||
- **创建日期**: 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: 初始版本,完成架构设计
|
||||
Reference in New Issue
Block a user