Files
ozon-seller-kit/docs/architecture.md
T
2026-08-14 18:27:45 +08:00

16 KiB
Raw Blame History

Ozon Seller Kit 总体架构

状态:架构设计(待确认) 最后更新:2026-08-11 相关:插件方案 · 部署 · 文案后端


1. 项目定位

围绕 Ozon 跨境上品的一套自用工具链,覆盖采集 → 编辑 → 发布全链路,货源来自 Ozon 竞品页(跟卖)与 1688/淘宝/拼多多(补素材)。

四个组成部分,各自独立可用、通过明确契约衔接:

# 部分 形态 状态 职责
工具台 v1 静态页(原生 JS + Tailwind CDN 在用 计价、上品登记、水印、俄文文案。冻结维护,不重构
采集插件 Chrome MV3 扩展 🔨 待开发 Ozon/1688 商品页采集 → 落本地文件夹
发布工作台 Web 应用 📋 待设计 导入本地文件夹 → 编辑/图片处理 → 提交发布
服务端 FastAPI 🔨 部分就绪 AI 文案、图片处理、Ozon API 代理、凭证保管

关键边界:① 与 ③ 并存不互相替代。① 是已验证好用的轻量工具,③ 是面向"整商品发布"的新流程;③ 成熟后 ① 的计价器可能被吸收,但那是以后的事。


2. 目录结构

ozon-seller-kit/
├── server/                    # ④ 服务端(FastAPI
│   ├── main.py
│   ├── api/                   # ai / image / ozon / collection
│   ├── services/
│   ├── schemas/               # Pydantic = 数据契约真源
│   ├── config/
│   └── requirements.txt
│
├── web/                       # ① 工具台 v1 —— 原地冻结,不改目录名
│   ├── ozonSeller.html
│   ├── js/  css/  imgs/
│   └── README.md              # 标注「v1,仅修 bug」
│
├── extension/                 # ② 采集插件(WXT + TS
│   ├── entrypoints/
│   ├── src/{profiles,collector,export,storage}/
│   └── package.json
│
├── studio/                    # ③ 发布工作台
│   └── (技术栈待定,见 §8)
│
├── packages/
│   └── schema/                # 跨端共享契约(TS 类型 + JSON Schema
│
├── reference/
│   └── 1688-extension/        # 反编译参考资料,非本项目代码
│
├── docs/                      # 全部文档集中于此
│   ├── architecture.md        # ← 本文
│   ├── deployment.md
│   ├── ai-copy-backend-plan.md
│   ├── contracts/             # 数据契约说明
│   ├── extension/             # 插件方案
│   └── studio/                # 发布工作台方案
│
├── start.command
└── .env / .env.example

两处与现状不同,需要迁移(§9):后端从仓库根收进 server/seller-helper/ 拆解为 reference/ + docs/extension/

2.1 为什么后端要收进 server/

现状后端散在仓库根(main.pyapi/services/schemas/config/)。四个部分并列后,根目录会同时出现 Python 包目录和三个前端项目目录,api/ 这种名字看不出属于谁。收进 server/ 后每个顶层目录一一对应一个部分。

代价:start.commanddocs/deployment.md 里的启动路径要改,.env 加载路径 parents[1] 要跟着调。Python 内部 import 全是 from api import ... 这类顶层相对形式,只要工作目录切到 server/ 就不受影响。

这是唯一有破坏性的改动,建议在动 studio/ 之前一次做完,不要拖到中途。 如果你想零风险,也可以让后端留在根目录——架构其余部分不依赖这个决定。

2.2 为什么 web/ 不改名

改名会动 main.py 的挂载路径、start.command、部署文档,收益只是"名字更清楚"。目录名保持 web/,在里面放一个 README 标注定位即可。

路由上两代共存:studio/ 上线后占 /studioweb/ 继续在 /ozonSeller.html。谁占根路径是路由配置问题,跟目录名无关。


3. 核心数据契约:商品文件夹

这是整个架构最重要的一个决定。 四个部分之间不直接调用彼此的代码,只认一个约定:磁盘上的「商品文件夹」。

儿童保温杯_316不锈钢/
├── product.json          # 商品数据(唯一真源)
├── sources.json          # 采集溯源:哪个平台、哪个 URL、什么时候
└── images/
    ├── main/             # main-001.jpg …
    ├── sku/              # sku-001-blue.jpg(文件名带规格名)
    ├── detail/           # detail-001.jpg …
    └── video/

数据流因此变成:

插件 ──写──> 商品文件夹 ──读──> 发布工作台 ──调用──> 服务端 ──> Ozon API
                 ↑
              1688 插件二次采集追加

三个好处:

  1. 插件和工作台完全解耦。插件不需要知道工作台存在,反之亦然。任一端重写不影响另一端。
  2. 中间产物可见可改。采集结果就是普通文件夹,能用 Finder 看、能手动补图、能备份、能在两台机器间拷。
  3. 跨平台合并天然成立。Ozon 采完,切到 1688 采同类商品,选同一个文件夹继续写入——就是往同一目录追加文件,不需要任何服务端参与。

3.1 product.json

结构对齐 Ozon ProductAPI_ImportProductsV3,但不等于它的请求体。区别在于采集阶段拿不到的字段留空,由工作台补齐:

字段 采集阶段 工作台补齐
name / description 原文(可能是俄文) 改写/翻译
images 本地相对路径 上传图床后换成公网 URL
offer_id 必须填自己的货号(跟卖场景下不能沿用竞品的)
description_category_id 类目选择/推荐
attributes[].id 查类目属性字典映射
尺寸重量 参数表里有就带上 校验补全
price / old_price 采到竞品价,仅供参考 计价器算出

所以 product.json 有个 _meta.stage 字段标明它处在哪个阶段:collectededitedpublished

契约细节见 [docs/contracts/product-json.md](./contracts/product-json.md)

3.2 契约真源与双语言实现

Pydanticserver/schemas/)是真源,因为服务端最终要用它做校验。由此派生:

server/schemas/product.py  (Pydantic)
        │
        ├── 导出 JSON Schema ──> packages/schema/product.schema.json
        │                              │
        │                              └── 生成 TS 类型 ──> 插件 / 工作台
        └── 服务端运行时校验

不上 codegen 流水线(单人项目不值得):插件端手写一份对应的 TS interface,配一个 fixture 文件双向跑一遍校验做契约测试。schema 变更时测试会红。


4. 各部分职责与边界

① 工具台 v1web/

冻结。 只修 bug,不加功能、不重构、不迁技术栈。它当前的价值是「已经好用」,任何改动都是风险。

新需求一律进 studio/。计价逻辑(web/js/app.js)后续会被 studio 复用——那时候是读它、抄它的公式,不是改它。

② 采集插件(extension/

只做四件事:识别页面、提取素材、分组、写文件夹。

不做:LLM 调用、图片处理、Ozon API、持有任何密钥。

一期只支持 Ozon(跟卖是第一优先级),1688 二期。详见 [docs/extension/plan.md](./extension/plan.md)

③ 发布工作台(studio/

导入本地文件夹 → 编辑 → 发布。三块能力:

  • 表单编辑product.json 各字段,类目选择,属性映射,计价(复用 v1 公式)
  • 图片处理:水印、白底、图内翻译、图生图 —— 重活走服务端
  • 发布:提交 Ozon 草稿,回填 product_id

④ 服务端(server/

唯一持有密钥的地方。当前有 /api/ai/*(文案);待补 /api/image/*/api/ozon/*

Ozon 发布有个硬约束值得提前知道ProductAPI_ImportProductsV3images 只接受公网可访问的 URL,Ozon 服务器会主动来拉。本地文件夹里的图必须先上传到图床/对象存储才能发布。这决定了服务端必须有图床能力,也决定了「本地文件夹」方案无法绕过服务端直接发布。


5. 端到端数据流

① 浏览 Ozon 竞品页
      │  点插件按钮 → 侧边栏打开 → 人工确认页面加载完 → 点采集
      ▼
② 侧边栏表单化展示采集结果(可二次编辑),图片分组勾选
      │  选保存目录 → 导出
      ▼
③ 本地商品文件夹(product.json + images/
      │  (可选)切到 1688 采同类商品,选同一文件夹追加
      ▼
④ 发布工作台:选择文件夹导入
      │  编辑字段 / 加水印 / 图生图 / 定类目 / 计价
      ▼
⑤ 服务端:图片上传图床 → 属性字典校验 → 提交 Ozon 草稿
      ▼
⑥ 回填 product_id 到 product.jsonstage 置 published

每一步的产物都落盘,中断了可以从任意一步接着来。


6. 技术栈

部分 技术栈 说明
① web 原生 JS + Tailwind CDN 不动
② extension WXT + React + TS strict WXT 比 Plasmo 活跃
③ studio 待定(见 §8
④ server FastAPI + Pydantic 已有
共享 pnpm workspace 只为 packages/schema 共享,不上 turborepo

pnpm workspace 的唯一目的是让插件和 studio 共用契约类型。pnpm-workspace.yaml 三行搞定,不引入构建编排复杂度。


7. 服务端演进

当前是无状态的:只有 AI 文案代理,没有数据库。按需要逐步加,不要一次上齐:

阶段 触发条件 要加什么
现在 无状态,/api/ai/*
S1 studio 要处理图片 /api/image/*(水印/白底/翻译),仍无状态:收图返图
S2 要发布到 Ozon /api/ozon/* + 图床 + 类目字典缓存(SQLite 够用)
S3 图片处理变慢(图生图、视频) 任务队列 + /api/job/:id 轮询
S4 想要跨设备同步 商品库落库,本地文件夹降级为导入导出格式

S1、S2 都不需要数据库,类目字典用文件缓存即可。S4 是个大改动,只在真的有多设备需求时才做——本地文件夹方案的一个优点就是单机场景下完全不需要它。


8. 已定决策

D1 · 后端迁入 server/

代价是改 start.commandsettings.py.env 路径、deployment.md。Python 内部 import 不受影响(工作目录切到 server/ 即可)。这是唯一有破坏性的改动,在开工 studio 前一次做完。

D2 · studio 用 React + Vite + TS

studio 的核心是"几十个字段的结构化表单 + 图片批处理",正是原生 JS 最吃力的场景。与插件同栈,图片处理组件和契约类型可两边复用。v1 的计价公式(web/js/app.js)抄过来即可,不改原文件。

D3 · 保存用 File System Access API

chrome.downloadsfilename 只能是下载目录下的相对路径,不接受绝对路径或 ..,因此无法满足"用户选择保存目录",也无法读回 sources.json 做去重。

File System Access 在 side panel(扩展页面上下文)可用,showDirectoryPicker() 拿到的 handle 存进 IndexedDB 后跨会话免重复授权,正好支撑"Ozon 采完切 1688 追加到同一文件夹"。chrome.downloads 保留为降级路径(用户拒绝授权时)。

细节见 [docs/extension/plan-revision.md](./extension/plan-revision.md) R1。


9. 迁移计划

一次性做完,中途不要停在半路:

① 后端收拢
   main.py api/ services/ schemas/ config/ requirements.txt  →  server/
   改 server/config/settings.py: parents[1] → parents[1](指向 server/.env 仍在仓库根则用 parents[2]
   改 start.command: cd server 后再 uvicorn
   改 docs/deployment.md 中所有路径

② 参考资料归位
   seller-helper/1688-extension/  →  reference/1688-extension/

③ 文档集中
   seller-helper/docs/方案设计.md      →  docs/extension/legacy-v1.md
   seller-helper/docs/方案设计-V2.md    →  docs/extension/legacy-v2.md
   seller-helper/docs/插件开发方案.md   →  docs/extension/plan.md
   seller-helper/extension-plan/IMPLEMENTATION_PLAN.md → 合并进 docs/extension/plan.md

④ 上一轮错放的代码归位
   seller-helper/extension-plan/profiles-ozon.ts        →  extension/src/profiles/ozon.ts
   seller-helper/extension-plan/download-implementation.ts → extension/src/export/download.ts
   (建插件项目时再放,现在先留在 docs/extension/ 作为草案附件)

⑤ 删空目录
   seller-helper/

⑥ web/ 加 README 标注 v1 冻结

reference/1688-extension/ 要不要进 git:它是反编译产物,约 40 个 bundle。建议加进 .gitignore,保留在本地即可——设计结论已经写进 docs/extension/plan.md §2,原始 bundle 只在需要再次查证时才用。


10. 里程碑(已调整优先级)

插件先行:1688/淘宝 → Ozon。理由见 [docs/extension/1688-taobao-implementation.md](./extension/1688-taobao-implementation.md)

# 内容 依赖 产出 工作量
M0 目录迁移 + 文档集中 已完成,服务正常起
M1 插件:1688 采集引擎 M0 Console 里能跑 scanCurrentPage() 4h
M2 插件:淘宝 profile M1 淘宝页面同样可用 1h
M3 插件:Side Panel + File System Access M2 生成完整商品文件夹到本地 4h
M4 插件:sources.json 去重 M3 二次采集追加不重复 1h
M5 契约真源:product.json Pydantic M0 server/schemas/product.py 2h
M6 插件:Ozon profile 实测 M4 Ozon 选择器验证(需真实页面链接) 2h
M7 studio:导入文件夹 + 表单编辑 M5 能改能存 8h
M8 studio + server:图片处理 M7 水印/白底可用 6h
M9 serverOzon 发布 M8 草稿进 Ozon 后台 4h

M4 结束时插件功能完整,可以采集 1688/淘宝商品到本地文件夹,跨平台追加不重复。M9 结束时全链路跑通。