Refine seller store implementation
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# 工作交接说明(HANDOFF)
|
||||
|
||||
> 更新:2026-08-30
|
||||
> 更新:2026-08-31
|
||||
> 读者:下一个接手工作的 AI / 开发者(工具无关)。先读本文再动手,避免重复探索。
|
||||
> 配套文档:[`README`](../../README.md)(项目总览)、[`collect.md`](./collect.md)(采集)、[`trial-page.md`](./trial-page.md)(试算页)、[`image-suite.md`](./image-suite.md)(生图)、[`background-jobs.md`](./background-jobs.md)(**后台任务中心方案、实施状态与路线图**)、[`image-asset-lifecycle.md`](./image-asset-lifecycle.md)(图片与水印存储规则)、[`api.md`](./api.md)(契约)、[`backend-structure.md`](./backend-structure.md)(后端结构)
|
||||
|
||||
@@ -73,6 +73,10 @@ pnpm -C extensions/collector build # 产物 .output/chrome-mv3
|
||||
| **本轮:生成结果区** | 「导出图片」按钮(水印按钮右侧,复用 POST /api/export/images,ZIP 内分「套图结果/单张生图」文件夹);「单张 AI 生图」小节改名「生图记录」(落库历史,刷新后保留) |
|
||||
| **本轮:图片生命周期** | 采集、上传与 AI 图片统一保存为 `original + current`;只保留当前水印版;重加/移除水印自动回收旧对象;删除单图/硬删商品会清理本地或七牛对象;具体规则见 [`image-asset-lifecycle.md`](./image-asset-lifecycle.md) |
|
||||
| **本轮:图片资源上限** | 单张上传/下载默认 20 MB;采集最多 100 张;导出最多 50 张、总量 200 MB;ZIP 超过 2 MB 自动落临时磁盘并分块返回,配置见 `.env.example` |
|
||||
| **本轮:远程图片 SSRF 防护** | 代理、采集转存、AI 参考图、远程导出仅允许公网 HTTP(S) 地址;每跳重定向复验,限制连接/读写/总耗时与跳转次数;本项目 `/media` 素材继续本地直读,正常防盗链代理不受影响 |
|
||||
| **本轮:图片格式与元数据校验** | 上传、采集转存、生成图统一按真实字节验证 JPEG/PNG/WebP/GIF/BMP,默认限制 4,000 万像素;静态图纠正 EXIF 方向并落库实际宽高。历史 39 条本地图像已回填尺寸;`server/scripts/backfill_asset_dimensions.py` 可用于其他旧库 |
|
||||
| **本轮:试算页图片组件拆分与按页加载** | `TrialSuitePanel.tsx` 拆出采集/结果/失败图片网格(`TrialImageGrids.tsx`)、水印设置(`TrialWatermarkSettings.tsx`)与共享类型;采集分组、套图结果、历史结果按每页 24 张渲染,缩略图启用原生懒加载。套图/水印轮询仍由父组件集中管理。 |
|
||||
| **本轮:商品轮播视频** | 试算页独立的「07 视频制作」卡片:AI 图优先选择、2–5 图按选择顺序合成为 810×1080/30fps/H.264 无音轨 MP4;进行中卡片为 3:4 灰底加载态,成片可预览、下载或删除。任务复用 `background_jobs`,刷新恢复进行中/失败/完成状态;取消与删除都会立即移除记录及视频文件。Worker 依赖系统 `ffmpeg`/`ffprobe`(Ubuntu 安装 `apt install -y ffmpeg`)。 |
|
||||
| **本轮:生图任务与轮询** | 单张 AI 编辑改为提交统一异步任务并立即返回 `suite_id`;套图与单张弹窗均采用无重叠递归轮询、取消在途请求与失败退避;套图任务 ID 可在刷新页面后从浏览器会话恢复 |
|
||||
| **本轮:持久化图片任务** | 套图、单张编辑与批量添加水印已迁入数据库任务表和独立 Worker;Pcore/RightAPI/通义万相保存第三方任务 ID 并可在重启后恢复轮询;水印逐图进度可刷新恢复、取消并按失败项重试,详见 [`background-jobs.md`](./background-jobs.md) |
|
||||
| **本轮:关键 bug 修复** | ① axios 全局 Content-Type 导致 FormData 被 JSON 化 → 上传 422(`api.ts` 移除全局头,**今后勿设**);② TrialPage `load()` 置 loading 导致整页闪 Spin、卸载面板(杀生图轮询/丢勾选)→ 改静默刷新;③ web v1 页面因 cdn.tailwindcss.com 被墙整页无样式 → tailwind 运行时固化到 `web/vendor/tailwind.js` |
|
||||
@@ -90,7 +94,7 @@ pnpm -C extensions/collector build # 产物 .output/chrome-mv3
|
||||
1. **Pcore 生图接入 + 规划器修复 + 张数策略**
|
||||
`server/services/{generator,planner,suite_service,prompts/__init__}.py`、`server/{api/suite.py,schemas/suite.py,config/settings.py}`、`studio/src/services/suite.ts`、`.env.example`、`docs/v2.1/{image-suite,api}.md`
|
||||
2. **试算页 UI 迭代 + 采集箱批量操作 + 请求层修复**
|
||||
`studio/src/pages/trial/{TrialSuitePanel,TrialInfoPanel,TrialPage}.tsx`、`studio/src/pages/collection/CollectionPage.tsx`、`studio/src/services/api.ts`
|
||||
`studio/src/pages/trial/{TrialSuitePanel,TrialImageGrids,TrialWatermarkSettings,trialImageTypes,TrialInfoPanel,TrialPage}.tsx`、`studio/src/pages/collection/CollectionPage.tsx`、`studio/src/services/api.ts`
|
||||
3. **web v1 样式修复(CDN 固化)+ start.command 插件自动构建**
|
||||
`web/ozonSeller.html`、`web/vendor/tailwind.js`(新目录)、`start.command`
|
||||
4. **图片生命周期与水印回收**:`server/services/image_assets.py`、`server/services/storage.py`、`server/{api/materials,api/products,models/asset}.py`、迁移 `a41e8c7f4b20_add_asset_image_lifecycle.py`、试算页水印入口
|
||||
@@ -132,8 +136,8 @@ pnpm -C extensions/collector build # 产物 .output/chrome-mv3
|
||||
| 01 商品信息(采买地址图标、型号/货号联动) | `pages/trial/TrialInfoPanel.tsx` | `api/materials.py` |
|
||||
| 02 价格试算 | `pages/trial/TrialPricingPanel.tsx` + `pricing/pricing.ts` | — |
|
||||
| 03 俄文文案 | `pages/product/CopyPanel.tsx`(共用) | `api/ai.py` + `services/deepseek.py` + `prompts/copy_ru.py` |
|
||||
| 04 图片与生图(7 列网格/悬浮按钮/图名) | `pages/trial/TrialSuitePanel.tsx` + `AiImageGenModal.tsx` | `api/suite.py` + `services/{jobs,job_worker,suite_service,generator,planner,prompts/,watermark,image_assets}.py` |
|
||||
| 图片原图/水印/删除回收 | `TrialSuitePanel.tsx`(水印设置) | `api/materials.py`、`api/products.py`、`services/{image_assets,storage}.py` |
|
||||
| 04 图片与生图(7 列网格/悬浮按钮/图名) | `pages/trial/TrialSuitePanel.tsx`(状态/轮询)+ `TrialImageGrids.tsx`(素材/结果卡片与分页)+ `AiImageGenModal.tsx` | `api/suite.py` + `services/{jobs,job_worker,suite_service,generator,planner,prompts/,watermark,image_assets}.py` |
|
||||
| 图片原图/水印/删除回收 | `TrialSuitePanel.tsx`(状态与调用)+ `TrialWatermarkSettings.tsx`(设置与任务状态) | `api/materials.py`、`api/products.py`、`services/{image_assets,storage}.py` |
|
||||
| 生图供应商路由 | `services/suite.ts`(IMAGE_MODEL_OPTIONS 带供应商名;DEFAULT_IMAGE_MODEL) | `schemas/suite.py`(MODEL_PROVIDERS/`@pcore` 约定)+ `services/generator.py`(generate_pcore 等) |
|
||||
| 05 登记与导出 | `pages/trial/TrialExportPanel.tsx`(localStorage `trialRegisterRecords`) | — |
|
||||
| 生成结果导出 | `TrialSuitePanel.tsx` handleExportResult(套图+单张分组 ZIP) | `api/export.py` |
|
||||
|
||||
+14
-3
@@ -101,7 +101,14 @@
|
||||
|
||||
`POST /api/assets/watermark/remove` 当前仍为短时同步操作,返回 `{ "updated_ids": [], "failed": [] }`。两接口均只保留原图和当前展示版本:重新加水印会在数据库提交后删除旧水印文件;前端展示、单张下载与导出均使用 `stored_url`。
|
||||
|
||||
## 8. 导出与代理(Phase B / D)
|
||||
## 8. 商品轮播视频
|
||||
|
||||
- `POST /api/products/{product_id}/video-jobs`:请求 `{ "asset_ids": ["uuid", "uuid"] }`,按传入顺序选择当前商品的 2–5 张已转存图片,立即返回 `{ "job_id": "uuid" }`。服务端只接受素材 ID,不接受任意图片 URL。
|
||||
- `GET /api/products/{product_id}/video-jobs`、`GET /api/video-jobs/{job_id}`:读取任务和成片状态;`POST /api/video-jobs/{job_id}/cancel` 会取消并立即清理任务记录,`POST /api/video-jobs/{job_id}/retry` 重试失败任务,`DELETE /api/video-jobs/{job_id}` 删除成片及记录,`GET /api/video-jobs/{job_id}/download` 下载 MP4。
|
||||
- Worker 使用 FFmpeg 合成无音轨轮播视频:AI 生成图在前端选择器中优先展示;支持 2–5 张,按 4.5 / 3.5 / 3.0 / 2.8 秒单图停留并使用 0.35 秒淡入淡出。默认输出为 `810×1080`、`30 fps`、`H.264`、`yuv420p`、最大 45 MB,结果存入 `products/{product_id}/videos/{job_id}/result.mp4`。
|
||||
- 需要系统安装 `ffmpeg` 与 `ffprobe`(Ubuntu:`apt install -y ffmpeg`);路径和大小/超时限制可通过 `VIDEO_*` 环境变量覆盖。任务复用 `background_jobs`,无需新增视频业务表;进行中、失败和已完成成片可在刷新后继续查看,取消会立即移除任务及临时文件,不保留记录。
|
||||
|
||||
## 9. 导出与代理(Phase B / D)
|
||||
|
||||
| 接口 | 方法 | Req | Res |
|
||||
|---|---|---|---|
|
||||
@@ -111,11 +118,15 @@
|
||||
|
||||
图片资源上限:默认单张上传/远程下载 20 MB;一次导出最多 50 张,ZIP 内源图片总量与成品 ZIP 均最多 200 MB。超过限制返回 HTTP `413` 及可读错误提示;ZIP 超过 2 MB 自动写入临时磁盘,并以分块响应下载。
|
||||
|
||||
## 9. 采集相关(Phase C 使用,均已实现)
|
||||
图片校验与元数据:图片上传、采集转存和 AI 生成图写入素材库时,服务端以实际字节识别 JPEG、PNG、WebP、GIF 或 BMP,不信任 URL 后缀和上传声明的 MIME。解码失败或不支持的格式返回/记录为失败;默认限制为 40,000,000 像素(`IMAGE_MAX_PIXELS`),并记录 `product_assets.width`、`height`。静态图片若带 EXIF 方向,会在落库前纠正并移除该方向标记;动画图不重编码,以避免丢帧。`POST /api/materials/bytes` 成功响应会额外返回 `width`、`height`,不合规图片返回 `422`。
|
||||
|
||||
远程图片安全规则:`/api/proxy-image`、采集图转存、AI 参考图及远程导出统一只请求公网 `http(s)` 地址;`localhost`、回环、内网、链路本地、多播和其他保留 IP 会返回 `400`。重定向不会自动跟随:服务端最多跟随 3 跳,并在每一跳重新解析和校验目标地址。已转存的本项目 `/media/...` 素材直接读取本地磁盘,不经过 HTTP 回环请求。下载仍受 `IMAGE_DOWNLOAD_MAX_BYTES` 限制,连接/读写/总超时与重定向次数可通过 `IMAGE_DOWNLOAD_CONNECT_TIMEOUT`、`IMAGE_DOWNLOAD_READ_TIMEOUT`、`IMAGE_DOWNLOAD_TOTAL_TIMEOUT`、`IMAGE_DOWNLOAD_MAX_REDIRECTS` 配置;下载超时返回 `504`。
|
||||
|
||||
## 10. 采集相关(Phase C 使用,均已实现)
|
||||
|
||||
- `POST /api/materials`:插件上传采集结果;**响应含 `product_id`**(插件用于打开 `/trial/{id}`)。
|
||||
- `GET /api/collected?platform=&itemId=`:采集前查重。
|
||||
|
||||
## 10. 冻结接口(不投入,保留代码)
|
||||
## 11. 冻结接口(不投入,保留代码)
|
||||
|
||||
`/api/publish/*`、`/api/shops/*`、`/api/categories/*`——Ozon API 直传链路(D1)。
|
||||
|
||||
@@ -54,22 +54,25 @@
|
||||
- 已实施:页面独立轮询水印任务,刷新可恢复,支持取消、重试全部失败项和按单张重试;等待期间素材被修改时会拒绝旧任务覆盖,单项重试会重新冻结最新版本。
|
||||
- 边界:移除水印目前仍是同步短操作;若未来单次处理规模继续提高,可迁入同一 Worker 框架。
|
||||
|
||||
### P2:统一图片格式、尺寸和元数据校验
|
||||
### P2:统一图片格式、尺寸和元数据校验(已实施)
|
||||
|
||||
- 现状:存储层主要依据 URL 后缀或 Content-Type 推断格式,未统一校验文件大小、像素、实际 MIME 类型和方向信息。
|
||||
- 涉及:`server/services/storage.py`、`server/api/materials.py`。
|
||||
- 建议:上传/下载后用图片库校验真实格式,限制像素与文件大小,按需要压缩、纠正 EXIF 方向并记录宽高。
|
||||
- 已实施:`normalize_image_bytes()` 在上传、采集转存和 AI 生成图写入素材库时,使用 Pillow 解码真实字节,不再信任 URL 后缀或客户端 Content-Type;仅接受 JPEG、PNG、WebP、GIF、BMP。
|
||||
- 已实施:维持原有单文件字节上限,并新增 `IMAGE_MAX_PIXELS`(默认 40,000,000)限制;强制完整解码,拒绝损坏、伪造或像素过大的图片。
|
||||
- 已实施:静态图纠正 EXIF 方向;记录可信的实际宽高到 `product_assets.width`、`height`。动画图只校验并保留原字节,不重编码以避免丢帧。
|
||||
- 已实施:历史本地库的 39 条图片素材已通过 `server/scripts/backfill_asset_dimensions.py` 回填宽高;该脚本不重写图片文件,可用于其他旧库。
|
||||
- 边界:当前不做强制压缩/转码,避免无谓损失采集原图质量;后续若存储成本成为问题,再按真实运营数据引入按格式和尺寸分级压缩策略。
|
||||
|
||||
---
|
||||
|
||||
## 3. 服务端:安全与资源保护
|
||||
|
||||
### P0:限制图片代理和导出 URL,防止 SSRF
|
||||
### P0:限制图片代理和导出 URL,防止 SSRF(已实施)
|
||||
|
||||
- 现状:图片代理、单图下载、ZIP 导出会请求客户端传入的 HTTP(S) URL,并跟随重定向。
|
||||
- 涉及:`server/api/proxy.py`、`server/api/export.py`、`server/services/storage.py`。
|
||||
- 风险:若服务可被非本机访问,可能被用于读取内网地址、云元数据地址,或下载超大文件。
|
||||
- 建议:解析并拒绝回环、内网、链路本地与保留 IP;每次重定向后再次校验;设置域名白名单/允许列表、最大响应体、连接与总耗时上限。
|
||||
- 已实施:`services.storage.download_bytes()` 统一校验代理、采集转存、AI 参考图和远程导出的 URL,仅允许公网 `http(s)` 地址;拒绝 `localhost`、回环、内网、链路本地、多播及其他保留 IP。
|
||||
- 已实施:关闭 HTTP 客户端自动重定向,最多手动跟随 3 跳,并在每一跳重新解析和校验目标地址;限制连接、读写和整次下载总耗时。原有的流式大小上限继续生效。
|
||||
- 兼容:项目自身已经转存的 `/media/...` 素材仍通过受限本地文件读取;采集扩展的 `GET /api/proxy-image?url=https://公网源图` 防盗链回退不受影响。
|
||||
- 涉及:`server/api/proxy.py`、`server/api/export.py`、`server/services/storage.py`、`.env.example`。
|
||||
- 边界:当前策略按 DNS 解析结果判定公网地址;若未来服务面向高风险公网环境,可进一步使用出站网络策略或固定 DNS/代理以降低 DNS 重绑定窗口。
|
||||
|
||||
### P0:限制上传与 ZIP 导出的内存占用(已实施基础版本)
|
||||
|
||||
@@ -89,16 +92,17 @@
|
||||
|
||||
## 4. 前端:可维护性与交互
|
||||
|
||||
### P2:拆分 TrialSuitePanel
|
||||
### P2:拆分 TrialSuitePanel(已实施)
|
||||
|
||||
- 现状:`TrialSuitePanel.tsx` 已约 1,400 行,采集图和生成图卡片、悬浮操作、选择、下载等逻辑有重复。
|
||||
- 建议:拆分为素材网格、结果网格、图片动作层、预览下载、水印设置、套图轮询等子组件/hooks。
|
||||
- 收益:降低后续 UI 修改遗漏风险,便于复用和测试。
|
||||
- 已实施:`TrialSuitePanel.tsx` 从约 1,829 行缩减为约 1,326 行;采集素材网格、生成结果网格、失败结果卡片提取到 `TrialImageGrids.tsx`,水印设置和水印任务状态提取到 `TrialWatermarkSettings.tsx`,图片分组、预览和结果类型统一到 `trialImageTypes.ts`。
|
||||
- 已实施:采集图与生成图统一复用卡片的选择、删除、下载、AI 编辑和预览行为,避免两处网格逻辑继续分叉。
|
||||
- 边界:套图和水印轮询保留在父组件,继续集中管理页面级状态、刷新与取消逻辑;若轮询逻辑再次扩张,再独立为 hook。
|
||||
|
||||
### P2:图片多时采用懒加载/虚拟化
|
||||
### P2:图片多时采用懒加载/虚拟化(已实施基础版本)
|
||||
|
||||
- 现状:图片网格会一次性创建全部 Ant Design Image 与预览节点。
|
||||
- 建议:大量历史生成图时采用分页、虚拟列表或按需加载;缩略图使用合适尺寸和 `loading="lazy"`。
|
||||
- 已实施:采集素材按分组、套图结果和历史生成记录均按每页 24 张分页;仅当前页创建 Ant Design `Image` 及预览节点,避免历史图片一次性进入 DOM。
|
||||
- 已实施:缩略图使用 `loading="lazy"`,滚动接近可视区才请求图片资源。
|
||||
- 边界:当前采用分页而非滚动虚拟列表,预览导航限于当前页;若单个分组进一步增长到数百张以上,可基于真实使用情况再替换为虚拟网格或服务端游标分页。
|
||||
|
||||
### P2:补触屏与键盘操作
|
||||
|
||||
|
||||
Reference in New Issue
Block a user