# 前后端优化审查清单 > 审查日期: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`](./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 契约、索引、可观测性**:依据真实使用量逐步完善。