# Ozon Seller Kit 总体架构 > 状态:架构设计(待确认) > 最后更新:2026-08-11 > 相关:[插件方案](./extension/plan.md) · [部署](./deployment.md) · [文案后端](./ai-copy-backend-plan.md) --- ## 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.py`、`api/`、`services/`、`schemas/`、`config/`)。四个部分并列后,根目录会同时出现 Python 包目录和三个前端项目目录,`api/` 这种名字看不出属于谁。收进 `server/` 后每个顶层目录一一对应一个部分。 代价:`start.command` 与 `docs/deployment.md` 里的启动路径要改,`.env` 加载路径 `parents[1]` 要跟着调。Python 内部 import 全是 `from api import ...` 这类顶层相对形式,只要工作目录切到 `server/` 就不受影响。 **这是唯一有破坏性的改动,建议在动** `studio/` **之前一次做完,不要拖到中途。** 如果你想零风险,也可以让后端留在根目录——架构其余部分不依赖这个决定。 ### 2.2 为什么 `web/` 不改名 改名会动 `main.py` 的挂载路径、`start.command`、部署文档,收益只是"名字更清楚"。目录名保持 `web/`,在里面放一个 README 标注定位即可。 路由上两代共存:`studio/` 上线后占 `/studio`,`web/` 继续在 `/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` 字段标明它处在哪个阶段:`collected` → `edited` → `published`。 契约细节见 `[docs/contracts/product-json.md](./contracts/product-json.md)`。 ### 3.2 契约真源与双语言实现 Pydantic(`server/schemas/`)是真源,因为服务端最终要用它做校验。由此派生: ``` server/schemas/product.py (Pydantic) │ ├── 导出 JSON Schema ──> packages/schema/product.schema.json │ │ │ └── 生成 TS 类型 ──> 插件 / 工作台 └── 服务端运行时校验 ``` 不上 codegen 流水线(单人项目不值得):插件端手写一份对应的 TS interface,配一个 fixture 文件双向跑一遍校验做契约测试。schema 变更时测试会红。 --- ## 4. 各部分职责与边界 ### ① 工具台 v1(`web/`) **冻结。** 只修 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_ImportProductsV3` 的 `images` 只接受**公网可访问的 URL**,Ozon 服务器会主动来拉。本地文件夹里的图必须先上传到图床/对象存储才能发布。这决定了服务端必须有图床能力,也决定了「本地文件夹」方案无法绕过服务端直接发布。 --- ## 5. 端到端数据流 ``` ① 浏览 Ozon 竞品页 │ 点插件按钮 → 侧边栏打开 → 人工确认页面加载完 → 点采集 ▼ ② 侧边栏表单化展示采集结果(可二次编辑),图片分组勾选 │ 选保存目录 → 导出 ▼ ③ 本地商品文件夹(product.json + images/) │ (可选)切到 1688 采同类商品,选同一文件夹追加 ▼ ④ 发布工作台:选择文件夹导入 │ 编辑字段 / 加水印 / 图生图 / 定类目 / 计价 ▼ ⑤ 服务端:图片上传图床 → 属性字典校验 → 提交 Ozon 草稿 ▼ ⑥ 回填 product_id 到 product.json,stage 置 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.command`、`settings.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.downloads` 的 `filename` 只能是下载目录下的相对路径,不接受绝对路径或 `..`,因此无法满足"用户选择保存目录",也无法读回 `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** | server:Ozon 发布 | M8 | 草稿进 Ozon 后台 | 4h | **M4 结束时插件功能完整**,可以采集 1688/淘宝商品到本地文件夹,跨平台追加不重复。M9 结束时全链路跑通。