Files
ozon-seller-kit/docs/v2.1/optimization-audit.md
T
2026-08-31 14:37:28 +08:00

12 KiB
Raw Blame History

前后端优化审查清单

审查日期:2026-08-29
状态:审查项持续跟踪;“图片原图与当前水印版管理”已于 2026-08-30 实施基础版本。
范围:商品试算页、图片生成/水印/导出链路,以及对应 FastAPI 服务端。


优先级说明

优先级 含义
P0 若服务不再局限于本机,需优先处理的安全或资源风险
P1 会影响数据可靠性、生成任务稳定性或存储成本
P2 提升维护效率、性能与用户体验

1. 服务端:任务可靠性

P1:生成任务应持久化并异步执行(第一阶段已实施)

  • 已实施:新增 background_jobs、background_job_items,保存请求快照、状态、进度、逐图结果、错误、重试计数与第三方任务 ID;套图和单张编辑均已迁入。
  • 已实施:server/worker.py 为独立 Worker;Pcore、RightAPI、通义万相在提交模型后先持久化外部任务 ID,再分次轮询,服务或 Worker 重启后继续原任务。
  • 已实施:前端可恢复商品活动任务、取消任务、仅重试失败图。
  • 边界:SQLite 适用于当前单机;跨机器/多实例前需迁移 PostgreSQL。同步供应商接口没有外部任务 ID,进程恰好中断时不能保证幂等。任务事件日志与历史清理策略留待后续。

P1:单张 AI 编辑改为异步任务(已实施)

  • 已实施:POST /api/suite/image-edit 创建并提交与套图共用的任务,立即返回 suite_id;单张编辑因此会走统一的模型、队列、水印、落库与追加素材链路。
  • 已实施:弹窗提交后交由试算页统一任务区跟踪;请求不再受前端 120 秒 HTTP 超时影响,并支持取消、仅重试失败图与刷新恢复。
  • 边界:任务事件日志、历史清理和供应商主动取消仍是后续项;同步供应商没有外部任务 ID 时无法保证中断后的幂等恢复。

P2:前端轮询避免异步 setInterval 重叠(已实施)

  • 已实施:套图和单张编辑均改为递归 setTimeout;只有前一次 GET /api/suites/{id} 完成后才安排下一次请求。
  • 已实施:使用 AbortController 取消过期/卸载请求;网络失败采用最多 3 次、6s→12s 的指数退避;套图任务 ID 写入 sessionStorage,刷新页面后可恢复跟踪。
  • 边界:状态已持久化,服务端重启不会导致 404;同步供应商恰在请求中断时需要重试,异步供应商则继续使用保存的外部任务 ID 轮询。

2. 服务端:图片、水印与存储生命周期

P1:统一管理原图与当前水印版(已实施基础版本)

  • 已实施:素材记录保存原图、当前展示图、对象 key 与水印配置;未加水印时原图即当前图,加水印时仅保留一张当前水印版,不保留水印历史。
  • 已实施:采集、上传、AI 生成图片统一迁入 products/{product_id}/assets/{asset_id}/;重新加水印在提交后回收旧水印对象,移除水印回到原图。
  • 已实施:删除单图清理素材目录,硬删除商品清理商品目录,并兼容清理旧路径引用。详见 image-asset-lifecycle.md。
  • 后续:可增加定期扫描与清理孤儿对象的后台任务,用于处理存储服务瞬时失败后的残留。

P1:批量水印改后台任务(第二阶段已实施)

  • 已实施:POST /api/assets/watermark 只创建 watermark_batch 持久化任务并返回任务 ID;Worker 逐图从原图合成、上传、切换当前展示版本和回收旧水印对象。
  • 已实施:页面独立轮询水印任务,刷新可恢复,支持取消、重试全部失败项和按单张重试;等待期间素材被修改时会拒绝旧任务覆盖,单项重试会重新冻结最新版本。
  • 边界:移除水印目前仍是同步短操作;若未来单次处理规模继续提高,可迁入同一 Worker 框架。

P2:统一图片格式、尺寸和元数据校验(已实施)

  • 已实施: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(已实施)

  • 已实施: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 导出的内存占用(已实施基础版本)

  • 已实施:上传与远程下载按块读取,默认单张限制 20 MB;超限返回 HTTP 413 和可读错误。限制可通过 .env 覆盖。
  • 已实施:每次采集最多 100 张;图片导出最多 50 张;ZIP 源图片总量和成品最大 200 MB。
  • 已实施:ZIP 使用 SpooledTemporaryFile,超过 2 MB 自动转临时磁盘,响应以 64 KB 分块输出,不再使用无上限 BytesIO。
  • 后续:若部署为多实例/容器,需要为临时目录增加独立容量配额与定期清理监控。

P0:服务脱离本机后补鉴权与资源归属

  • 现状:V2.1 按决策未启用鉴权,商品、素材、生成、导出、代理接口都可直接访问。
  • 涉及:server/main.py、全部业务路由。
  • 建议:上线局域网/公网前先接入认证、商品/素材归属校验、请求限流和审计日志。
  • 备注:若服务始终只绑定本机回环地址,可作为后续事项,但不应暴露端口。

4. 前端:可维护性与交互

P2:拆分 TrialSuitePanel(已实施)

  • 已实施:TrialSuitePanel.tsx 从约 1,829 行缩减为约 1,326 行;采集素材网格、生成结果网格、失败结果卡片提取到 TrialImageGrids.tsx,水印设置和水印任务状态提取到 TrialWatermarkSettings.tsx,图片分组、预览和结果类型统一到 trialImageTypes.ts。
  • 已实施:采集图与生成图统一复用卡片的选择、删除、下载、AI 编辑和预览行为,避免两处网格逻辑继续分叉。
  • 边界:套图和水印轮询保留在父组件,继续集中管理页面级状态、刷新与取消逻辑;若轮询逻辑再次扩张,再独立为 hook。

P2:图片多时采用懒加载/虚拟化(已实施基础版本)

  • 已实施:采集素材按分组、套图结果和历史生成记录均按每页 24 张分页;仅当前页创建 Ant Design Image 及预览节点,避免历史图片一次性进入 DOM。
  • 已实施:缩略图使用 loading="lazy",滚动接近可视区才请求图片资源。
  • 边界:当前采用分页而非滚动虚拟列表,预览导航限于当前页;若单个分组进一步增长到数百张以上,可基于真实使用情况再替换为虚拟网格或服务端游标分页。

P2:补触屏与键盘操作

  • 现状:删除、下载、AI 编辑等操作多为 hover 展示。
  • 建议:增加焦点可见性、键盘标签与快捷操作;移动端使用常显按钮或“更多”菜单。

P2:将水印设置按商品或用户保存

  • 现状:水印设置写入浏览器全局 localStorage,同一浏览器的所有商品共享。
  • 涉及:trialWatermark。
  • 建议:明确产品语义:若按店铺/用户通用则服务端保存偏好;若按商品不同则保存到商品配置。

5. 数据、导出与接口契约

P1:登记表与批量 CSV 服务端化

  • 现状:登记记录保存在 trialRegisterRecords localStorage,不跨设备、清理浏览器数据后丢失,也无法多人/多窗口协作。
  • 涉及:studio/src/pages/trial/TrialExportPanel.tsx。
  • 建议:建立登记记录表,并实现服务端批量 CSV 导出;保留当前本地模式作为离线兜底(如有需要)。

P2:减少前后端手工接口类型重复

  • 现状:前端服务层与 FastAPI schema 分别维护,请求字段随功能增加后容易漂移。
  • 涉及:studio/src/services/*.ts、server/schemas/*.py。
  • 建议:从 OpenAPI 自动生成 TypeScript 客户端,或至少以契约测试校验关键请求/响应。

P2:补充高频查询组合索引

  • 现状:商品与素材已有单列索引;随着数据增长,按商品、分组、排序、状态组合查询会变多。
  • 涉及:server/models/product.py、server/models/asset.py。
  • 建议:在真实数据库和访问量出现后,用慢查询确认并增加如 (product_id, group_key, sort_order) 的组合索引;避免过早添加无效索引。

6. 测试、交付与可观测性

P1:建立自动化测试基础

  • 现状:未发现前后端单测、契约测试或端到端测试目录;studio 仅提供 dev/build/preview 脚本。
  • 建议:
    1. 后端:计价、水印、文件名、任务状态、URL 安全校验、导出。
    2. 前端:价格计算、选择/导出、水印设置、图片操作组件。
    3. E2E:采集素材 → 试算 → 单张/套图生成 → 水印 → 导出。

P2:统一日志、指标和错误码

  • 现状:生成失败主要保存在任务错误文本或日志中,缺少 provider、模型、耗时、重试、存储失败等聚合指标。
  • 建议:结构化日志中加入 task_id、product_id、provider、model、耗时、错误类别;健康检查补充存储与关键第三方状态。

P1:先提交当前已验证改动,再做重构

  • 现状:工作区存在多个未提交的前后端改动。
  • 建议:按“水印/图片交互”“提示词”“导出”等模块提交;后续优化在独立提交或分支完成,便于回滚与定位。

建议实施顺序

  1. P0 资源与 URL 防护:SSRF、文件大小、ZIP 限制。
  2. P1 任务与文件可靠性:异步持久化任务、图片版本和清理策略。
  3. P1 测试基础:先覆盖水印、导出、任务、URL 安全。
  4. P1 登记表服务端化:持久化登记记录和批量导出。
  5. P2 前端拆分与性能:拆 TrialSuitePanel、图片虚拟化、触屏/键盘体验。
  6. P2 契约、索引、可观测性:依据真实使用量逐步完善。