Web 端本地缓存、首页与会员弹窗体验优化
新增 IndexedDB 与最近文件 L1 缓存、云保存/打开链路;首页支持下载与清空本地缓存;修复清空 storage 后语言初始化报错;优化 design2 背景加载与侧栏标签样式;完善会员激活/支付弹层样式;后端补充文件缩略图字段与 thumb 接口。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -168,7 +168,8 @@ GET /api/v1/pay/orders?page=1&size=8
|
||||
| `GET /api/v1/templates` | 首页模板列表(仅 `id/name/type/updated_at`;缩略图按需拉取) |
|
||||
| `GET /api/v1/templates/{id}` | 模板 JSON 数据(门户标准读取端点,见下) |
|
||||
| `GET /api/v1/templates/{id}/thumb` | 模板缩略图(从 `.soon` 内 `frontDisplayPic` 提取) |
|
||||
| `GET /api/v1/templates/{id}/file` | **legacy**:裸 `.soon` 流;门户禁止调用,仅供兼容 |
|
||||
| `GET /api/v1/templates/{id}/file` | 裸 `.soon` 流(`Cache-Control: max-age=86400`);门户读缓存首选 |
|
||||
| `GET /api/v1/files/{id}/thumb` | 云文件缩略图(需登录;从 `.soon` 内 `frontDisplayPic` 提取) |
|
||||
| `GET /api/v1/soon-models` | 兼容别名,同 `GET /api/v1/templates` |
|
||||
|
||||
#### `GET /api/v1/templates/{id}`(门户读取模板数据)
|
||||
@@ -196,10 +197,11 @@ GET /api/v1/pay/orders?page=1&size=8
|
||||
| 前缀 | 含义 | readJsonFile | 保存 writeFile |
|
||||
|------|------|--------------|----------------|
|
||||
| `soondesign_file:{id}:v{ver}` | 云端已登记文件 | `GET /files/{id}` | `PUT /files/{id}` |
|
||||
| `soondesign_template:{id}` | 云端模板(只读源) | `GET /templates/{id}` | `POST /files`(首次保存新建) |
|
||||
| `soondesign_template:{id}` | 云端模板(只读源) | `GET /templates/{id}/file` | `POST /files`(首次保存新建) |
|
||||
| `soondesign_session:...` | 本地/临时会话 | sessionStorage | 已登录 `POST /files`;未登录写 session |
|
||||
|
||||
- **保存/打开**走 JSON API;**仅**首页「下载」走 `GET /files/{id}/download` 落盘 `.soon`
|
||||
- 门户**读缓存**与最近文件本地优先策略见 [`WEB-LOCAL-CACHE.md`](WEB-LOCAL-CACHE.md);模板首次加载推荐 `GET /templates/{id}/file`(流式 `.soon`,避免 `GET /templates/{id}` 二次包装)
|
||||
|
||||
---
|
||||
|
||||
@@ -236,3 +238,4 @@ GET /api/v1/pay/orders?page=1&size=8
|
||||
|------|------|
|
||||
| 2026-06-08 | 初版:统一 page/size/total;files 兼容 limit/offset;pay/orders 分页 |
|
||||
| 2026-06-08 | 新增 GET /templates/{id} JSON 契约;Web 虚拟 key 前缀;/file 标 legacy |
|
||||
| 2026-06-08 | 新增 GET /files/{id}/thumb;files list 增 `has_thumb`;模板 /file 长缓存 |
|
||||
|
||||
@@ -0,0 +1,400 @@
|
||||
# Web 端本地缓存与最近文件优化(开发计划 · 细化版)
|
||||
|
||||
> 状态:P0~P7 已实施。与 [API-PAGINATION.md](API-PAGINATION.md)「Web 虚拟 key」交叉引用。
|
||||
|
||||
---
|
||||
|
||||
## 0. 背景与约束
|
||||
|
||||
### 0.1 问题(已核实代码)
|
||||
|
||||
| 现象 | 根因 | 代码位置 |
|
||||
|------|------|----------|
|
||||
| `GET /templates/{id}` 20~30s | PHP 二次 `json_encode` + 大响应 + 客户端双次 parse | `TemplateService::fetchPublicJson`;`web.js` ~268 行 |
|
||||
| 重复打开仍打源站 | 无 IndexedDB | 全库无 `indexedDB` |
|
||||
| 首页最近文件慢 | 每卡 `readJsonFile` 全量 | `index.js` ~381 行 |
|
||||
| 保存后不显示 | Web `onCloudWriteDone` 未写 history | `design1/output.js` ~1266;仅 fs 分支 `saveHistory` |
|
||||
| session 易超配额 | 大 JSON 写 session/localStorage | `cloud-files.js` `soonPutSoonSession` |
|
||||
|
||||
### 0.2 硬约束
|
||||
|
||||
- **不改** `.soon` 格式;**保留** base64。
|
||||
- **不上 CDN**;靠本机 IDB + 传输路径精简 + L1 元数据。
|
||||
- 保存仍 `POST/PUT /files` 传完整 `json`;**仅读**本地优先。
|
||||
- 配置:`frontend-web/config/local.js`、`backend-web/config/local.php`。
|
||||
|
||||
### 0.3 三层架构
|
||||
|
||||
| 层 | 存储 | 内容 | 体积 |
|
||||
|----|------|------|------|
|
||||
| L1 | `localStorage` `soondesign_recent` | key/name/type/kind/fileId/thumbRef/time | <50KB |
|
||||
| L2 | IndexedDB `soondesign_local` | 完整 `.soon` 对象 + thumbs | ≤20 条 blob |
|
||||
| L3 | HTTP API | miss / 保存 / 可选元数据对账 | 按需 |
|
||||
|
||||
### 0.4 脚本加载顺序(三页统一)
|
||||
|
||||
`index.web.html` / `design1.web.html` / `design2.web.html`:
|
||||
|
||||
```
|
||||
config/local.js → auth → cloud-files.js → soon-local-store.js → soon-recent.js → web.js → 页面逻辑
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 1. 数据契约
|
||||
|
||||
### 1.1 L1:`soondesign_recent`
|
||||
|
||||
```json
|
||||
{
|
||||
"version": 1,
|
||||
"items": [
|
||||
{
|
||||
"key": "soondesign_file:12:v3",
|
||||
"name": "我的卡片.soon",
|
||||
"type": 1,
|
||||
"kind": "cloud",
|
||||
"fileId": 12,
|
||||
"thumbRef": "soondesign_file:12:v3",
|
||||
"time": "2026-06-08 14:30:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- `thumbRef`:与 L2 `thumbs` 的 cacheKey 相同(非独立字符串前缀)。
|
||||
- 上限 **20**;`soonRecentUpsert` 去重后 unshift,截断尾部。
|
||||
- **首页以 L1 为展示主数据源**;`GET /files` 仅后台补 `name`/`version`,不默认注入未打开过的云端文件。
|
||||
|
||||
### 1.2 L2:IndexedDB `soondesign_local` v1
|
||||
|
||||
| Store | keyPath | value |
|
||||
|-------|---------|--------|
|
||||
| `blobs` | `cacheKey` | `{ json, name, type, source, updatedAt, savedAt, bytes }` |
|
||||
| `thumbs` | `cacheKey` | `{ dataUrl, savedAt }` |
|
||||
|
||||
**cacheKey**
|
||||
|
||||
| 来源 | cacheKey | 失效 |
|
||||
|------|----------|------|
|
||||
| 云文件 | `soondesign_file:{id}:v{ver}` | PUT 后 version+1 → 新 key;旧 blob 可 `soonLocalRemove` |
|
||||
| 模板 | `soondesign_template:{id}` | 见 §1.4 |
|
||||
| 会话 | `soondesign_session:...` | 迁入云 key 后 `soonLocalRenameKey` |
|
||||
|
||||
**LRU**:`soonLocalEvictLRU(20)` 在每次 `put` 后执行;仅删 blob+thumb,**不删 L1**。
|
||||
|
||||
### 1.3 L3:读取端点
|
||||
|
||||
| 场景 | 端点 | 禁止 |
|
||||
|------|------|------|
|
||||
| 模板首次 | `GET /api/v1/templates/{id}/file` | 门户默认不用 `GET /templates/{id}` 包装 |
|
||||
| 云文件 miss | `GET /api/v1/files/{id}` | — |
|
||||
| thumb 兜底 | `GET /templates/{id}/thumb`;P6 `GET /files/{id}/thumb` | 禁止为 thumb 拉全量 json |
|
||||
| 保存 | `POST`/`PUT /files` | 不变 |
|
||||
|
||||
### 1.4 模板缓存失效策略(细化)
|
||||
|
||||
1. 首页点模板前,`index.js` `openTemplateItem` 写入 `window._soonTemplateMeta = { id, name, type, updated_at }`(列表已有 `updated_at`)。
|
||||
2. IDB `blobs` 的 `meta.updatedAt` 存该 `updated_at`。
|
||||
3. `readJsonFile` IDB hit 时:若 `_soonTemplateMeta.updated_at` 存在且与 blob.meta.updatedAt 不一致 → 删 blob,`miss` 走网络。
|
||||
4. 网络:`/templates/{id}/file` + parse;`soonLocalPut` 带最新 `updatedAt`。
|
||||
5. 未经过首页的深链打开:无 meta 比较,信任 IDB(可接受;刷新列表后校正)。
|
||||
|
||||
### 1.5 共享工具 `soonExtractThumbFromJson(json)`
|
||||
|
||||
- 位置:`soon-local-store.js` 或 `soon-recent.js`。
|
||||
- 取 `json.frontDisplayPic`;须 `data:image/` 开头。
|
||||
- 超过 **102400** 字节则返回 `''`(不写 thumbs,首页用 type 默认图)。
|
||||
- 打开/保存/导入成功后统一调用。
|
||||
|
||||
---
|
||||
|
||||
## 2. 核心流程
|
||||
|
||||
### 2.1 打开(readJsonFile)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant UI as design_page
|
||||
participant IDB as L2_IDB
|
||||
participant L1 as L1_recent
|
||||
participant API as L3_server
|
||||
|
||||
UI->>IDB: soonLocalGet(cacheKey)
|
||||
alt hit_and_valid
|
||||
IDB-->>UI: json
|
||||
else miss_or_stale
|
||||
UI->>API: GET file_or_files
|
||||
API-->>UI: soon_bytes
|
||||
UI->>IDB: soonLocalPut + PutThumb
|
||||
UI->>L1: soonRecentUpsert
|
||||
end
|
||||
UI->>UI: openFile_loadFromJSON
|
||||
```
|
||||
|
||||
**补充接入点**(P3):`doOpenWithJson` 完成画布加载后 `soonRecentUpsert`(模板/云文件/会话均记录,kind 由 key 推断)。
|
||||
|
||||
### 2.2 保存(writeFile + onCloudWriteDone)
|
||||
|
||||
**模板首次保存**:`soondesign_template:*` 或空 key 时弹窗,默认文件名 `design` + `yyMMddHHmmss` + `.soon`(如 `design260610103010.soon`);保存成功后 L1 用 cloud/local 条目替换原 template 项(`soonRecentOnCloudSave` + `prevOpenKey`)。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant UI as save
|
||||
participant IDB as L2
|
||||
participant L1 as L1
|
||||
participant API as server
|
||||
|
||||
UI->>IDB: soonLocalPut当前json
|
||||
UI->>L1: soonRecentUpsert
|
||||
alt cloud_key
|
||||
UI->>API: PUT
|
||||
API-->>UI: new_version
|
||||
UI->>IDB: RenameKey_or_new_put
|
||||
UI->>L1: upsert新key
|
||||
else new_or_template
|
||||
UI->>API: POST
|
||||
end
|
||||
```
|
||||
|
||||
### 2.3 首页最近文件
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Index as index
|
||||
participant L1 as L1
|
||||
participant IDB as thumbs
|
||||
participant API as metadata_optional
|
||||
|
||||
Index->>L1: soonRecentList
|
||||
loop each_card
|
||||
Index->>IDB: soonLocalGetThumb(key)
|
||||
alt no_thumb
|
||||
Index->>Index: soonAsset默认图或thumb_URL
|
||||
end
|
||||
end
|
||||
opt logged_in
|
||||
Index->>API: listCloudFiles元数据
|
||||
Index->>L1: 仅补name/version不增项
|
||||
end
|
||||
```
|
||||
|
||||
### 2.4 删除卡片
|
||||
|
||||
| 操作 | L1 | L2 | 服务器 |
|
||||
|------|----|----|--------|
|
||||
| 删本地项 | `soonRecentRemove` | `soonLocalRemove` | — |
|
||||
| 删云文件 | `soonRecentRemove` | `soonLocalRemove` | `DELETE /files/{id}` |
|
||||
| 旧 `removeLocalHistoryPath` | 改为调上述 API | 同上 | — |
|
||||
|
||||
---
|
||||
|
||||
## 3. 分阶段任务(可独立验收)
|
||||
|
||||
### Phase 0:契约文档 — 已完成
|
||||
|
||||
- [x] 本文档
|
||||
- [x] `API-PAGINATION.md` 链接与 `/file` 推荐说明
|
||||
|
||||
---
|
||||
|
||||
### Phase 1:IndexedDB 模块(1.5h)
|
||||
|
||||
**新建** [`frontend-web/js/common/soon-local-store.js`](frontend-web/js/common/soon-local-store.js)
|
||||
|
||||
| 任务 ID | 任务 | 细节 |
|
||||
|---------|------|------|
|
||||
| P1.1 | `soonLocalOpen` | `indexedDB.open('soondesign_local', 1)`;`onupgradeneeded` 建 `blobs`/`thumbs` |
|
||||
| P1.2 | `soonLocalGet/Put` | `json` 存对象;`bytes` 估算 `JSON.stringify` 长度 |
|
||||
| P1.3 | `soonLocalPutThumb/GetThumb` | thumbs store |
|
||||
| P1.4 | `soonLocalRemove` | 同时删 blobs+thumbs |
|
||||
| P1.5 | `soonLocalRenameKey` | 读旧→写新→删旧(session→cloud) |
|
||||
| P1.6 | `soonLocalEvictLRU` | 按 `savedAt` 升序删至 ≤20 |
|
||||
| P1.7 | `soonExtractThumbFromJson` | §1.5 |
|
||||
| P1.8 | 降级 | `indexedDB` 不可用 → 所有 get 返回 null,put 静默失败 |
|
||||
| P1.9 | HTML 引入 | 三页在 `web.js` 前引入 |
|
||||
|
||||
**验收**
|
||||
|
||||
- [ ] DevTools Application → IDB 可见 stores
|
||||
- [ ] put/get 往返;第 21 条触发 evict
|
||||
- [ ] 隐私模式不抛未捕获异常
|
||||
|
||||
---
|
||||
|
||||
### Phase 2:web.js 读写穿透(2h)
|
||||
|
||||
**改** [`frontend-web/js/platform/web.js`](frontend-web/js/platform/web.js)
|
||||
|
||||
| 任务 ID | 任务 | 细节 |
|
||||
|---------|------|------|
|
||||
| P2.1 | `resolveCacheKey(key)` | 模板/云/ session 统一;云用完整 `soondesign_file:id:vN` |
|
||||
| P2.2 | read 模板 stale | §1.4;miss 走 `GET .../templates/{id}/file` + `text()` + `JSON.parse` |
|
||||
| P2.3 | read 云文件 | IDB miss → 现有 `authedFetch files/{id}`;成功后 put |
|
||||
| P2.4 | read session | IDB → sessionStorage 兜底(过渡期) |
|
||||
| P2.5 | read 后写 thumb | `soonExtractThumbFromJson` + `soonLocalPutThumb` |
|
||||
| P2.6 | writeFile 成功 | `soonLocalPut`;云 POST/PUT 成功后若 key 变化 `soonLocalRenameKey` |
|
||||
| P2.7 | write 未登录 | session key + IDB 双写;**停止** `localStorage.setItem(sessionKey, 大JSON)` |
|
||||
|
||||
**验收**
|
||||
|
||||
- [ ] 模板首次:Network 仅 `/templates/{id}/file`;二次 0 请求
|
||||
- [ ] 云文件二次打开无 `GET /files/{id}`
|
||||
- [ ] PUT 后 `openAs.name` 与新 version key 一致且 IDB 可命中
|
||||
|
||||
---
|
||||
|
||||
### Phase 3:L1 最近列表 + 保存/打开写 L1(2h)
|
||||
|
||||
**新建** [`frontend-web/js/common/soon-recent.js`](frontend-web/js/common/soon-recent.js)
|
||||
|
||||
| 任务 ID | 任务 | 细节 |
|
||||
|---------|------|------|
|
||||
| P3.1 | `soonRecentUpsert` | 按 `key` 去重;字段 §1.1;`getDate()` 时间格式与现 `saveHistory` 一致 |
|
||||
| P3.2 | `soonRecentList/Remove` | — |
|
||||
| P3.3 | `soonRecentKindFromKey` | `cloud`/`template`/`local` |
|
||||
| P3.4 | `soonRecentMigrateFromHistory` | 读 `soondesign_history`;`path`→`key`;无 thumb;**仅执行一次**(`soondesign_recent_migrated` 标记) |
|
||||
| P3.5 | `onCloudWriteDone` | design1/2 `output.js`:upsert(`res.fileKey`,`res.name`, soonType) |
|
||||
| P3.6 | `saveHistory` | Web:`soonRecentUpsert`;Electron fs 路径保留 |
|
||||
| P3.7 | `cloud-files` | `soonOpenSoonJsonLocally`、import 成功 upsert |
|
||||
| P3.8 | `openFile` 加载成功 | design1 `doOpenWithJson` 末尾;design2 `saveInitialState` 末尾 upsert |
|
||||
| P3.9 | `index` 开模板 | `openTemplateItem` 设置 `_soonTemplateMeta`(§1.4) |
|
||||
|
||||
**验收**
|
||||
|
||||
- [ ] 云端 Ctrl+S 后回首页立即见卡片
|
||||
- [ ] 打开模板/云文件后 L1 有对应项
|
||||
- [ ] 旧 history 用户升级后 L1 有条目(可无 thumb)
|
||||
|
||||
---
|
||||
|
||||
### Phase 4:首页 loadHistory 改造(1.5h)
|
||||
|
||||
**改** [`frontend-web/js/index.js`](frontend-web/js/index.js)
|
||||
|
||||
| 任务 ID | 任务 | 细节 |
|
||||
|---------|------|------|
|
||||
| P4.1 | 数据源 | `soonRecentList()` 为主;启动时 `soonRecentMigrateFromHistory` |
|
||||
| P4.2 | 删除 merge 云端追加 | 移除 `mergeRecentItems` 中「云端未见过则 push」逻辑;改为 `enrichFromCloud(items, cloudMeta)` 只补 name/version |
|
||||
| P4.3 | 渲染 thumb | `soonLocalGetThumb(item.key)` → fallback `soonAsset(bg_1/2)`;模板项可用 `/thumb` URL(`updated_at` 作 `?v=`) |
|
||||
| P4.4 | 删除 readJsonFile 循环 | 移除 ~381 行;**删除** `fileExists` 灰显逻辑(改为始终可点;打开时 miss 再拉+toast) |
|
||||
| P4.5 | 删除 | `removeLocalHistoryPath` → `soonRecentRemove` + `soonLocalRemove` |
|
||||
| P4.6 | 刷新 | `pageshow`/`visibilitychange` 调 `loadHistory`(60s 节流 `_recentLoadedAt`) |
|
||||
| P4.7 | 分页 | 保留 `fileListState` 对 L1 items 分页 |
|
||||
|
||||
**验收**
|
||||
|
||||
- [ ] 首页 Network:0× 全量 `files/{id}` / `templates/{id}`
|
||||
- [ ] 12 卡仅 thumb 图或 0 额外请求(IDB 命中)
|
||||
- [ ] 从设计页返回可见新保存项
|
||||
|
||||
---
|
||||
|
||||
### Phase 5:session 迁移与清理(1h)
|
||||
|
||||
| 任务 ID | 任务 | 细节 |
|
||||
|---------|------|------|
|
||||
| P5.1 | `soonPutSoonSession` | 正文 IDB;sessionStorage 仅存 `{key}` 标记或短指针 |
|
||||
| P5.2 | `readJsonFile` session | 优先 IDB |
|
||||
| P5.3 | `soonTryConsumeCloudImport` | 成功:`soonLocalRenameKey` + `soonRecentUpsert` + 删 session |
|
||||
| P5.4 | 清理 | 移除 `cloud-files`/`index` 对大 JSON 的 localStorage 写入 |
|
||||
| P5.5 | `index` 删卡 | 清 sessionStorage 旧 key(保留现有 769 行逻辑并接 IDB) |
|
||||
|
||||
**验收**
|
||||
|
||||
- [ ] 本地 .soon → 编辑 → 保存 → key 为 `soondesign_file:*`
|
||||
- [ ] sessionStorage 无 >1MB 字符串
|
||||
|
||||
---
|
||||
|
||||
### Phase 6:后端轻量增强(可选,1h)
|
||||
|
||||
| 任务 ID | 文件 | 细节 |
|
||||
|---------|------|------|
|
||||
| P6.1 | `TemplateService::outputFile` | `Cache-Control: public, max-age=86400, must-revalidate` |
|
||||
| P6.2 | `FileController` + 路由 | `GET /api/v1/files/{id}/thumb` |
|
||||
| P6.3 | `FileService` | 上传/更新时 `extractThumb` 入 DB(可复用 `TemplateService` 逻辑);`list` 增 `has_thumb` |
|
||||
| P6.4 | `docs/API-PAGINATION.md` | 登记 thumb 端点 |
|
||||
|
||||
**验收**
|
||||
|
||||
- [ ] `curl -I .../templates/1/file` 含 `max-age=86400`
|
||||
- [ ] 云 thumb 返回 `image/jpeg`
|
||||
|
||||
---
|
||||
|
||||
### Phase 7:全链路验收(0.5h)
|
||||
|
||||
| # | 场景 | Network | UI |
|
||||
|---|------|---------|-----|
|
||||
| 1 | 首次开模板 | 1× `/templates/{id}/file` | 画布 OK |
|
||||
| 2 | 再次开同模板 | 0 全量 | <1s |
|
||||
| 3 | 模板改 updated_at 后再开 | 1× `/file` | 新内容 |
|
||||
| 4 | 保存新文件(登录) | POST | 回首页有卡 |
|
||||
| 5 | 再次开云文件 | 0 全量 | PUT 可保存 |
|
||||
| 6 | 首页 12 最近 | 0 全量 json | thumb OK |
|
||||
| 7 | 未登录保存 | 无 API | L1+IDB |
|
||||
| 8 | 删云文件卡 | DELETE | L1+IDB 清除 |
|
||||
| 9 | IDB 满 20 再开第 21 | 1× 网络 | 最旧 blob 淘汰 |
|
||||
| 10 | 模板列表 | 1× `/templates` 元数据 | 不变 |
|
||||
|
||||
**部署**:前端 P1~P5 同批;强刷 `soon-local-store.js`/`soon-recent.js`/`web.js`/`index.js`;P6 可稍后。
|
||||
|
||||
---
|
||||
|
||||
## 4. 改动文件清单
|
||||
|
||||
| 阶段 | 文件 |
|
||||
|------|------|
|
||||
| P1 | `soon-local-store.js`;`pages/index|design1|design2.web.html` |
|
||||
| P2 | `platform/web.js` |
|
||||
| P3 | `soon-recent.js`;`design1/2/output.js`;`cloud-files.js`;`index.js`(meta) |
|
||||
| P4 | `index.js` |
|
||||
| P5 | `cloud-files.js`;`web.js` |
|
||||
| P6 | `TemplateService.php`;`FileController.php`;`FileService.php`;`public/index.php`;`API-PAGINATION.md` |
|
||||
|
||||
**不改动**:`.soon` schema;`design*-back.js`;Electron(后续可选对齐)。
|
||||
|
||||
---
|
||||
|
||||
## 5. 依赖关系
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
P0[P0_done]
|
||||
P1[P1_IDB]
|
||||
P2[P2_web_bridge]
|
||||
P3[P3_recent_L1]
|
||||
P4[P4_index]
|
||||
P5[P5_session]
|
||||
P6[P6_backend_opt]
|
||||
P7[P7_QA]
|
||||
|
||||
P0 --> P1 --> P2 --> P3 --> P4 --> P5 --> P7
|
||||
P2 --> P6 --> P7
|
||||
```
|
||||
|
||||
**顺序**:P1 → P2 → P3 → P4 → P5 → P7;P6 与 P3/P4 并行。
|
||||
|
||||
---
|
||||
|
||||
## 6. 风险与缓解
|
||||
|
||||
| 风险 | 缓解 |
|
||||
|------|------|
|
||||
| IDB 配额 | LRU 20;thumb ≤100KB;evict 失败 toast |
|
||||
| 多设备 | 云 version 为准;409 提示刷新 |
|
||||
| 模板 stale 漏检 | 首页列表带 `updated_at`;§1.4 |
|
||||
| PUT 后双 version 缓存 | `onCloudWriteDone` rename + 删旧 key |
|
||||
| 隐私模式 | 降级 session+网络,与现网一致 |
|
||||
| 旧用户无 recent | `soonRecentMigrateFromHistory` 一次 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 不在 scope
|
||||
|
||||
- CDN;`.soon` 剥离 base64;Electron IDB;CRDT 离线合并。
|
||||
|
||||
---
|
||||
|
||||
**确认后**按 P1→P7 实施;每阶段勾选任务 ID 验收后再进入下一阶段。
|
||||
Reference in New Issue
Block a user