# 存储与数据库重构方案(规划) > 状态:第一阶段部分已落地(任务元数据落 SQLite、任务清理审计、素材 sha256)。 > 目的:后端当前仍以“内存 + 文件 + 任务级 SQLite + meta.json”为主要存储方式。本文档规划后续迁移到 PostgreSQL 元数据库,并优化任务生命周期和贴纸持久化。 ## 1. 当前现状 | 数据 | 当前存储 | 问题 | |---|---|---| | 任务状态 / 事件 | `JobManager._jobs` 仅存内存 | 重启服务后任务记录丢失 | | 任务输入 / 输出 | `backend/service_workspace/{job_id}` | 提交任务即建目录,未完成或被放弃的任务会遗留文件 | | 词云坐标结果 | 每个任务生成一个 `word_locations.db`(SQLite) | 每个任务自带一份 SQLite 文件,查询分散 | | 贴纸素材 | `backend/service_assets/asset_xxx/asset.svg` + `meta.json` | 素材元数据不是数据库,tint 等前端信息还依赖 localStorage | | 设计模板 / 工程 | `service_design_templates` / `service_projects` 目录 + JSON | 模板和工程之间缺少数据库关联 | | 画布文档 | 前端 localStorage | 无法跨设备,也无法作为后端权威数据 | 注意:当前不能认为系统已经在使用 PostgreSQL。代码中出现的 `*.db` 是词云算法自己写的 SQLite 结果文件,例如 `backend/core/pipeline.py` 的 `word_locations` 表。 ## 2. 目标架构 整体原则: - **文件继续存文件系统或对象存储**(SVG / PNG / 遮罩 / Excel / 字体)。 - **业务元数据和引用关系存 PostgreSQL**。 - 数据库保存路径引用,不保存大文件内容。 目标模型: | 表 | 用途 | 说明 | |---|---|---| | `jobs` | 任务主表 | job_id、状态、参数 JSONB、产物引用、创建时间 | | `job_events` | 任务进度事件 | SSE 进度事件落库,服务重启后可恢复 | | `assets` | 贴纸 / 素材表 | 素材元数据、文件路径、来源 job、sha256、tint | | `design_documents` | 画布文档 | CanvasDocument JSONB,绑定模板/工程 | | `design_templates` | 模板 | 模板元数据 + 画布文档引用 | | `projects` | 工程 | 模板 + 画布文档 + 素材引用 | ### jobs 表字段建议 ```text id uuid pk status text -- submitted/running/success/failed/cancelled stage text progress int message text params jsonb -- 用户提交的词云参数 input_files jsonb -- mask/excel/font 引用 artifacts jsonb -- png/svg/svg_stroke/db/metrics 路径或文件 id error text created_at timestamptz updated_at timestamptz retention_until timestamptz -- 清理时间 ``` ### assets 表字段建议 ```text id uuid pk name text type text -- wordcloud / upload / shape / reference mime_type text storage_key text -- 文件系统路径或对象存储 key width int height int file_size bigint sha256 text -- 用于导入去重 source_job_id uuid nullable tint text nullable created_at timestamptz deleted_at timestamptz nullable ``` ## 3. 任务存储链路 现状是 `POST /api/jobs` 提交时直接创建 job 目录和保存上传文件。 目标改动: 1. `POST /api/jobs` 只写 `jobs` 表,状态为 `submitted` 或 `queued`。 2. 上传文件先落到临时上传区,或延迟到进入 runner 前再落盘。 3. runner 真正开始时才创建任务的 `input/` 和 `output/` 目录。 4. 任务完成后把产物路径/文件 id 写入 `jobs.artifacts`。 5. 增加后台清理任务: - 清理 `completed` 且未被贴纸/工程引用的任务文件。 - 支持按 `retention_until` 保留最近结果。 - 被用户导入为贴纸的任务文件可延长保留时间。 这样不会每次申请都攒下一堆用不上的目录和文件。 ## 4. 贴纸持久化 贴纸在当前 `frontend/src/lib/stickerLibrary.ts` 中已经走后端 `POST /api/assets`,但元数据仍写在 `meta.json`,tint 还保存在 localStorage。 目标改动: - `assets` 表作为贴纸唯一权威来源。 - `POST /api/assets`:写文件系统 + 写 `assets` 表,返回 `asset_id`。 - `GET /api/assets`:从数据库读取列表。 - `PATCH /api/assets/{id}`:更新 tint、name 等元数据。 - `DELETE /api/assets/{id}`:物理删除文件 + 记录,或软删除防止破坏设计文档引用。 - `POST /api/assets/from-job/{job_id}`:沿用同一逻辑,写入 `source_job_id`。 - 前端不再依赖 localStorage 保存贴纸 tint,加载和更新都走 API。 ## 5. 画布文档与模板 当前画布保存在 localStorage,模板保存成目录 JSON。 目标改动: - `design_documents` 保存 `CanvasDocument` JSONB。 - 画布每次保存调用 `PUT /api/documents/{id}`。 - `design_templates` 引用 `design_documents`,同时记录 `reference_asset_ids` 和封面图。 - 后续实现画布导出导入时,导入包可直接写入 `design_documents`,并把包内素材批量写入 `assets` 表。 ## 6. PostgreSQL 接入方式 建议: - 引入 SQLAlchemy(或 asyncpg)作为数据库访问层。 - 使用 Alembic 管理 migration。 - 在 `docker-compose.yml` 增加 PostgreSQL 服务。 - 通过环境变量注入 `DATABASE_URL`,本地开发和 Docker 使用不同配置。 - 暂不把词云算法的 `word_locations` 表强制迁移到 PostgreSQL,可以保留 SQLite 作为任务内部产物,再通过导出接口把需要的布局结果写入 `jobs` 或独立布局表中。 ## 7. 分阶段实施 ### 阶段一:接入 PostgreSQL,先做贴纸和任务元数据(任务元数据已用 SQLite 先行落地) - 建 `assets` / `jobs` / `job_events` 表。 - `assets` 接口从文件 meta 迁移到 DB。 - 提交任务仍可使用现有 runner,但把任务状态写入 DB。 - 不改动词云算法核心。 ### 阶段二:任务生命周期优化(清理审计已落地) - `POST /api/jobs` 只记账,不提前建目录。 - runner 开始前再落 input/output。 - 增加 TTL 清理任务。 - 任务列表、任务详情改为从 DB 查询。 ### 阶段三:画布文档和导入包 - 建 `design_documents` / `design_templates` / `projects` 表。 - 画布保存从 localStorage 改为后端文档接口。 - `wcd` 导入导出包直接对接这些表。 ## 8. 风险与注意点 - 现有任务接口依赖内存中的 `JobManager`,迁到 DB 后需要兼容 SSE 进度事件。 - 文件迁移只能做增量:老素材目录可先保留,新写入走 DB。 - 删除素材要检查 `design_documents` 引用,避免出现缺失贴纸。 - tint 从前端 localStorage 迁移到 DB 时,需要兼容旧浏览器状态。 ## 10. 已落地实现 - `backend/service/metadata_store.py`:SQLite 元数据 `jobs` / `job_events`。 - `backend/service/job_manager.py`:任务状态和事件落库,服务重启可恢复。 - `backend/service/storage.py`:任务目录占用、过期审计、可清理能力。 - `backend/service/storage_metrics.py`:dry-run 指标和显式 `--apply` 清理。 - `backend/service/app.py`:`GET /api/maintenance/storage-summary`。 - `docs/DATA_STORAGE_OPTIMIZATION.md`:完整空间/效能指标。 > 注:当前项目没有接入 PostgreSQL。代码里的 `*.db` 是词云算法自己的 SQLite 结果文件;新加的 `service_metadata/app.db` 是业务元数据先行层。`jobs` / `job_events` 表结构设计上可平滑迁移到 PostgreSQL。 ## 9. 相关文件参考 - `backend/service/app.py`:目前的任务、素材、模板 API。 - `backend/service/job_manager.py`:内存中的任务状态。 - `backend/service/storage.py`:任务目录创建。 - `backend/service/runner.py`:任务运行与产物扫描。 - `backend/core/pipeline.py`:词云结果 SQLite 写入。 - `frontend/src/lib/stickerLibrary.ts`:前端贴纸库。 - `docker-compose.yml`:服务编排,后续加 PostgreSQL。