# 产品档案与词云归档:技术设计 > 前置需求:[requirements.md](requirements.md) ## 1. 设计结论 词云生成任务仍可在设计期间生成位置库,但该文件只是一份**临时工作区产物**;它在成功生成后默认保留 30 天。用户从画布执行“加入产品列表”时,系统才把当前可见画布实际使用的词云位置库复制为产品档案中的不可变快照。产品档案不依赖原始任务目录,因此原始任务后续清理不会影响已加工产品的查询能力。 “加入产品列表”是设计系统内可执行的归档授权;实体加工和上市不由当前系统自动推断。后续可从生产/MES/电商接口写入产品状态,但不改变“已归档产品数据不自动删除”的原则。 ## 2. 生命周期 ```mermaid stateDiagram-v2 [*] --> 临时设计数据: 词云生成成功 临时设计数据 --> 临时设计数据: 设计、插入或移除画布 临时设计数据 --> 待清理: 30 天未归档 临时设计数据 --> 产品档案: 加入产品列表 产品档案 --> 待清理: 解除关联或删除产品 待清理 --> [*]: 30 天宽限期结束 产品档案 --> 产品档案: 更新产品资料/添加图片 ``` 清理前 7 天,系统在“产品档案/数据清理”管理界面显示提醒。首期不依赖邮件或外部通知服务。待清理状态允许恢复或延长;到达物理清理时间才删除文件。 ## 3. 归档识别规则 归档由后端根据提交的画布文档重新计算,不能仅相信前端传来的词云列表。 1. 读取当前 `CanvasDocument` 的 `layers` 和 `elements`。 2. 排除不可见图层中的元素。 3. 在剩余元素中收集插入式素材的 `assetId`。 4. 从素材元数据读取其词云来源;首期兼容现有 `job_id`,新字段统一为 `source_job_id` 与 `source_kind=wordcloud`。 5. 仅接受存在成功任务且包含 `word_locations` SQLite 的来源;普通图片、无来源 SVG、WCD 合成任务等不计入词云档案。 6. 以 `source_job_id` 去重;同一词云多次出现只归档一份位置库。 7. 若零份词云可归档,允许创建产品但明确标记“无词云归档数据”;不得虚报归档成功。 词云从画布删除后不会出现在本次文档扫描中,因此不会随本次产品版本归档。已经归档的历史产品版本不因后来编辑画布而被改写。 ## 4. 数据与文件边界 ### 4.1 产品元数据 SQLite 新增独立且持久化的 `service_products/products.db`,而不是把产品数据放在当前未挂载的 `service_metadata/app.db` 中。 | 表 | 核心字段 | 用途 | |---|---|---| | `products` | `product_id`, `source`, `external_product_id`, `name`, `sku`, `specification`, `status`, `cover_image_id`, timestamps | 产品主档;`(source, external_product_id)` 唯一 | | `product_versions` | `version_id`, `product_id`, `design_snapshot_path`, `design_digest`, `wordcloud_count`, timestamps | 一次“加入产品列表”产生一个不可变设计版本 | | `product_wordcloud_archives` | `archive_id`, `version_id`, `source_job_id`, `source_asset_id`, `db_path`, `db_checksum`, timestamps | 产品版本与一份位置库快照的关系 | | `product_images` | `image_id`, `product_id`, `version_id?`, `kind`, `path`, `remote_source?`, `is_cover`, timestamps | 图片集合;首期使用 `design_preview` | | `cleanup_records` | `subject_type`, `subject_id`, `state`, `purge_after`, `reminded_at`, `reason` | 临时任务和已删除档案的可恢复清理状态 | `external_product_id` 是接口幂等键,不作为默认显示字段。网页创建产品的 `product_id` 由服务生成;可选 SKU/规格用于人类识别。 ### 4.2 档案文件 ```text service_products/ products.db / / design-preview.png wordclouds/ .db ``` - `design-preview.png` 是加入产品时的完整设计预览快照,首期默认作为封面。 - 每份 `.db` 为原始任务 `wordcloud_hd.db` 的验证后拷贝,保留原有 `word_locations` 表结构。 - 词云查找使用产品档案快照,不再要求原始 `service_workspace/` 存在。 - 图片模型支持未来的 `reality_photo`、`external_product_image`,并始终由 `cover_image_id` 指向当前封面。远端图片应下载/复制入档案,而非只保存易失效 URL。 ### 4.3 原始任务与档案的关系 `job_id` 是来源追溯键,不是产品档案的身份。每次归档记录来源任务、素材和数据库校验和;当相同外部产品重新加入新的设计时,增加新的 `product_version`,旧版本继续可追溯。 ## 5. 服务边界与接口 ### 5.1 ProductArchiveStore 新增产品档案存储层,负责:产品幂等创建/更新、版本创建、数据库文件原子复制、封面文件保存、软删除、恢复和清理候选查询。文件复制采用临时文件、SQLite 可读性检查、校验和计算和原子重命名;任一失败都回滚元数据和临时文件,不能出现“列表显示已归档但数据库缺失”。 ### 5.2 ProductProvider 定义产品来源适配层: - `manual`:网页创建。 - `external`:外部产品接口同步。首期只约定稳定 ID 和名称;SKU、规格、封面 URL 为可选字段。 外部接口尚未在当前仓库中实现,因此首期不绑定具体 URL、认证方式或字段名。适配层以 `source + external_product_id` 进行幂等 upsert,避免按名称错误合并。 ### 5.3 建议 API | 接口 | 责任 | |---|---| | `GET /api/products` | 搜索产品列表;按名称/SKU/状态筛选 | | `POST /api/products` | 网页创建产品 | | `POST /api/products/sync` | 由产品接口适配器批量 upsert;不在首期绑定外部协议 | | `GET /api/products/{product_id}` | 产品、图片、版本及词云归档摘要 | | `POST /api/products/{product_id}/versions` | 提交当前画布;服务端识别可见词云、保存预览并创建归档版本 | | `GET /api/products/{product_id}/archives/{archive_id}/locations` | 查询该产品词云名字位置 | | `DELETE /api/products/{product_id}` | 软删除,进入 30 天待清理期 | | `POST /api/products/{product_id}/restore` | 恢复待清理产品 | | `GET /api/maintenance/cleanup-candidates` | 管理员查看清理预览 | | `POST /api/maintenance/cleanup-run` | 仅清理已到期且未被档案引用的数据 | 产品、档案查询和清理接口沿用生产订单管理权限;加入产品动作也必须带同一管理身份,避免把包含人员名字的位置库暴露给匿名调用。 ## 6. 前端体验 ### 6.1 画布中的入口 在画布顶栏或文件操作区提供「加入产品列表」。点击后: 1. 扫描当前画布并显示“已检测到 N 份画布词云,将全部归档”。 2. 用户搜索接口同步产品,或选择「新建产品」。 3. 对新建产品填写名称,规格/SKU 为可选。 4. 显示完整设计预览缩略图,说明“将作为产品封面;以后可替换为实景图”。 5. 确认后显示“已归档至产品《名称》· N 份词云位置数据已长期保存”。 没有可归档词云时,仍可建立产品,但确认界面和产品状态必须显示“无词云归档数据”。 ### 6.2 产品列表与产品详情 - 列表的主识别信息为封面、产品名称和规格/SKU,不显示完整 ID。 - 列表状态为「已归档词云数据」「无词云数据」「待清理」等人类可读标签。 - 详情页展示当前封面、大号设计预览、归档版本时间线、每版本词云数量及后续图片集合入口。 - 完整 ID 仅在“系统信息”折叠区提供复制,供排错和接口对接。 视觉沿用当前深色工作台的工业化风格:现有 `DM Sans` / `DM Mono`、背景 `#101114`、面板 `#1c1f24`、操作强调 `#6ea8ff`、归档成功 `#58d69c`、待处理 `#e4b95a`。这是对通用 UI 规范字体/颜色限制的窄范围品牌继承。 ## 7. 清理策略 | 对象 | 初始状态 | 提醒 | 物理清理 | |---|---|---|---| | 成功生成但未归档的词云任务 | 临时 | 第 23 天在管理界面提示 | 第 30 天 | | 已归档产品/词云版本 | 已归档 | 无自动删除 | 仅在显式解除关联/删除后 | | 已解除关联或删除的产品档案 | 待清理 | 可恢复期内显示 | 30 天后 | 清理执行时应删除完整的原始任务目录或档案版本目录,而不是仅删数据库的一部分;这避免留下指向失效数据库的元数据。清理器必须重新检查是否仍存在产品归档关联,避免并发“加入产品”和清理导致误删。 现有按素材引用跳过清理的逻辑需要改为按产品档案引用保护:普通画布素材引用只延长临时设计保留期,不构成永久保留理由。 ## 8. 部署、迁移与兼容 当前 Docker Compose 挂载了工作区/素材目录,但未挂载 `service_metadata` 和 `service_orders`。产品档案必须新增持久化卷,例如 `wordcloud_products -> /app/service_products`;同时建议为元数据与订单目录补充独立持久化卷,避免容器重建后丢失关联记录。 上线时: 1. 创建产品存储与 schema,不修改既有 `word_locations` 结构。 2. 既有任务默认视为临时数据,按其完成时间纳入清理候选;不自动把旧任务提升为产品档案。 3. 只有用户通过“加入产品列表”确认的设计才创建新档案快照。 4. 在第一次实际清理前,以只读 dry-run 展示候选清单,确认后才开启物理删除。 ## 9. 验证策略 - 单元测试:可见图层筛选、来源识别、去重、隐藏图层排除、外部 ID 幂等、宽限期计算。 - 服务测试:数据库快照原子复制和回滚、归档后原始任务删除仍可查询、软删除与恢复、到期清理二次引用检查。 - 前端测试:归档扫描摘要、零词云提示、产品名称展示、设计预览默认封面、待清理状态提示。 - 人工验证:将多份词云插入/删除/隐藏后加入产品;确认实际归档数量与画布可见内容一致;清理 dry-run 不包含已归档位置库。 ## 10. 未纳入本阶段的开放项 - 外部产品接口的真实协议、认证与字段映射。 - 实景图上传、图片多选与外部图片下载任务。 - 实体加工、上市、退市状态如何从外部系统回传。 - 多用户角色与精细权限;首期复用订单后台管理身份。