Files
atlas-seller-kit/docs/requirements/collector-browser-extension.md
2026-09-10 17:45:46 +08:00

418 lines
34 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 浏览器采集插件:需求与技术方案
> 状态:核心闭环已实施;Ozon 公开多 SKU 解析与归并方案于 2026-09-07 落地,仍需用真实页面回归验证。本文是 Atlas Seller Kit 从来源商品详情页进入采集箱、商品编辑与后续 Ozon 上线的采集入口规范。
>
> 本文取代旧 `ozon-seller-kit` 采集插件的产品定位,但只复用经验证的页面采集思路,不复用旧项目的本地数据库、无鉴权接口、`/trial/:id` 页面、AI 套图或生图流程。
## 1. 目标、范围与非目标
### 1.1 目标
浏览器插件让用户在来源商品详情页一键把商品导入 Atlas。它必须:
1. 采集商品原始文本、参数、媒体、规格轴和真实 SKU 组合;
2. 把记录写入登录用户当前工作空间共用的采集箱;采集时不关联目标平台或门店;
3. 让用户选择“只进入采集箱”或“导入后直接编辑上架”;
4. 保存来源证据和素材转存任务,确保后续可以修正、重试、审计;
5. 尽可能保留真实多 SKU,不凭名称、图片或笛卡尔积猜测不存在的组合;
6. 一期提供 Ozon Seller 网页增强采集:在用户浏览器的现有登录会话中调用第 7.3 节列出的四个接口,保存经过净化的观察结果。
首期来源站点与旧采集插件相同:Ozon(`ozon.ru`、`ozon.kz`、`ozon.by`)、1688、淘宝、天猫。
### 1.2 非目标
- 插件内不展示采集详情、商品字段编辑、AI 智能规划、AI 文案、AI 生图、套图生成、图片导出或定价;这些工作均在 Atlas ERP 页面完成。
- 插件不直接调用 Atlas 的正式发布接口,不直接创建 Ozon offer,也不提取完整、上传或持久化 Ozon Seller Cookie。
- 插件不以网页上的规格名称、SKU 图片或“所有颜色 × 所有尺码”的笛卡尔积推测真实 SKU。
- Ozon Seller 网页内部接口仅用于可观察的实验性采集,不能成为生产发布、库存或定价的唯一数据源。
## 2. 用户体验
### 2.1 出现位置与外观
在受支持来源站的**商品详情页**右下角显示一个小型矩形悬浮窗。它应使用 Shadow DOM 隔离来源站 CSS,布局接近参考插件:品牌图标/名称、收起按钮和纵向操作按钮。非商品页或 URL 软导航到非商品页时隐藏。
插件不使用旧版的全高 iframe Side Panel。点击操作时仅显示短暂的加载、成功或失败状态;复杂信息回到 ERP 页面展示。
### 2.2 登录态
| 状态 | 悬浮窗内容 | 行为 |
|---|---|---|
| 未登录或令牌失效 | `登录 Atlas ERP` | 新标签打开 Atlas 登录页。完成授权后,插件自动刷新为已登录状态。 |
| 已登录 | `编辑上架`、`采集商品`、`进入 ERP` | 允许采集;采集箱记录只归属工作空间,不要求已有目标店铺。 |
首期不在悬浮窗中选择平台或店铺。服务端从插件授权身份只解析工作空间,创建未绑定目标平台/门店的采集记录和商品基础草稿。用户进入 Atlas ERP 编辑页时,页面使用页头当前选中的平台;首期页头默认选择 Ozon,服务端取得该平台发布配置并预选默认店铺。若当前平台没有可用门店,允许保留采集箱记录,但不能进入该平台的商品编辑/发布流程,应引导用户在店铺管理中添加门店。
首期按毛子 ERP 风格复用 Atlas Web 登录态:插件打开 Atlas Web 登录页,登录/会话恢复后从 Atlas Web 标签读取本站 localStorage 中的短期 access token,保存到 Chrome extension storage,后台请求统一携带 `Authorization: Bearer`。不得读取 refresh Cookie、来源站 Cookie 或来源站 localStorage;该受限例外及后续独立令牌升级方案见 [`../project/extension-login-alternatives.md`](../project/extension-login-alternatives.md)。
### 2.3 三个操作
| 操作 | 流程 | 成功结果 |
|---|---|---|
| 编辑上架 | 扫描当前页 → 上报采集记录 → 创建/复用未绑定目标的商品基础草稿 → 打开 ERP 编辑入口 | 新标签打开当前已有的 `/products/{productId}` 入口;后续编辑模块在此入口按页头当前平台取得发布配置并预选门店,素材继续后台转存。 |
| 采集商品 | 扫描当前页 → 上报采集记录 | 停留来源页,提示“已进入采集箱”;用户以后从采集箱编辑。 |
| 进入 ERP | 不扫描、不上报 | 新标签打开 Atlas ERP 首页。 |
“上报成功”定义为:数据库事务已创建或复用工作空间级采集记录、未绑定目标的商品草稿、SKU 草稿和素材转存任务。它不等待所有图片下载完成;各素材的 `pending/downloading/ready/failed` 状态在采集箱和编辑页可见,失败可单独重试。
## 3. 端到端流程与状态
```text
来源商品详情页
→ 插件认证、确认当前工作空间
→ 平台采集器(原始证据 + 规范化结果)
→ 扩展后台统一上报
→ Atlas API:幂等创建工作空间级采集记录、未绑定平台/门店的 SPU 基础草稿、真实 SKU 草稿、素材任务
→ Redis/BullMQ Worker:下载、校验、去重、转存到 MinIO/Kodo
→ 工作空间共用采集箱(无目标平台/门店;上架后仍保留)
├─ 当前平台下未上架 SKU:编辑并首次上架;已上架 SKU:仅编辑 Atlas 草稿,不能再次上架
└─ 删除/归档/重试
→ 文案、SKU 媒体、定价、AI 生图、发布校验
→ 发布快照 → 官方 Ozon Seller API 导入、轮询;库存由用户在 Ozon 后台设置
→ 在线商品:异步同步店铺全部 Ozon 商品(含非 Atlas 发布商品)
```
建议的采集状态:
| 状态 | 含义 |
|---|---|
| `collecting` | 插件正在扫描来源页。 |
| `received` | 服务端已接受原始结果并创建事务记录。 |
| `materializing` | 素材下载、转存、哈希去重进行中。 |
| `ready_for_edit` | 至少商品草稿可编辑;单个素材仍可能失败。 |
| `needs_review` | SKU 组合、规格或媒体证据不足,需要人工确认。 |
| `failed` | 采集请求或关键记录创建失败;保留可诊断错误。 |
| `archived` | 用户不再处理的采集记录。 |
同一工作空间内,以 `source_platform + source_item_key` 识别同一来源商品。重复“采集商品”或“编辑上架”保留已有采集记录和商品草稿 ID,并以本次页面重新采集的来源信息覆盖上一份**来源快照**:标题、价格、原始证据、规范化载荷与候选素材均以最新采集为准。旧素材任务取消,新素材任务必须带新的快照版本,旧 Worker 不得回写最新内容。
在尚未创建平台发布配置前,SKU 草稿可随最新来源快照更新;进入任一平台编辑器后,SKU 已可能含用户编辑、货号、价格或发布历史,来源刷新不得覆盖、删除或恢复这些 Atlas 草稿。此时同 `source_variant_key` 只复用既有 SKU ID,新来源 SKU 创建新的未上架平台 mapping,来源不再出现的 SKU 保留待用户明确处理。商品编辑模块后续提供来源差异审查/应用操作;采集端不能用删除或级联删除来伪造“覆盖”。
### 3.1 采集箱与目标店铺的绑定时点
采集来源平台与目标发布平台是不同概念:例如从 1688 采集的商品可以在后续发布到 Ozon,也可能未来发布到其他平台。因此采集箱和刚创建的商品草稿只携带 `workspace_id`、来源平台及来源证据,`target_platform_id`、`target_store_id` 均为未设置状态。
用户从采集箱点击“编辑”时,ERP 读取页头当前平台。服务端验证该平台存在于当前系统配置中,并取得/创建该商品在这个平台的发布配置,预选该工作空间在该平台的默认店铺;用户可在编辑页改选门店。A 平台与 B 平台的发布配置和 SKU 上架状态相互独立。扩展不能传入、指定或覆盖这个选择。
## 4. 统一采集数据模型
### 4.1 原始与规范化数据分层
采集结果不能直接当作已确认商品数据。至少保留三层:
1. **原始证据**:页面 URL、采集时间、来源页面状态片段/响应片段、字段路径、解析器版本、原始文本和来源素材 URL。
2. **规范化采集结果**:解析出的通用字段、规格轴、SKU 组合、媒体关系和可信度。
3. **商品草稿**:用户或 AI 确认后的 SPU/SKU、属性、媒体和价格;只有草稿能进入发布快照。
每个字段和媒体关系应携带 `source`、`confidence`、`raw_value`、`captured_at`。规格、重量或尺寸口径未知时保留原文并标记 `ambiguous`,不能直接写入实物或包装物流规格。
### 4.2 规范化载荷
```ts
interface CollectedProductPayload {
source: {
platform: 'ozon' | '1688' | 'taobao' | 'tmall';
itemKey: string;
canonicalUrl: string;
collectedAt: string;
parserVersion: string;
};
common: {
title?: CollectedValue;
description?: CollectedValue;
brand?: CollectedValue;
sourcePrice?: MoneyValue;
attributes: CollectedAttribute[];
categories: SourceCategory[];
};
variantAxes: SourceVariantAxis[];
variants: CollectedVariant[];
assets: CollectedAsset[];
evidence: SourceEvidence[];
}
interface CollectedVariant {
sourceVariantKey: string; // 来源稳定 ID;不可只用展示名称
sourceSku?: string;
selections: Array<{ axisKey: string; valueKey: string; valueName: string }>;
displayName: string;
sourcePrice?: MoneyValue;
sourceStock?: number;
physical?: RawMeasurementSet;
package?: RawMeasurementSet;
mediaBindings: VariantMediaBinding[];
confidence: 'verified' | 'partial';
evidenceRefs: string[];
}
```
`variantAxes` 描述颜色、尺码、容量等可选值;`variants` 只描述可售的真实组合。两者不能互相替代。
### 4.3 媒体关系与继承
| 关系 | 条件 | 编辑页/发布时的表现 |
|---|---|---|
| `variant_direct` | 来源明确给出这个 SKU 与素材的稳定关联 | 自动绑定给该 SKU。 |
| `axis_value` | 例如颜色值有稳定图片,SKU 组合明确包含该颜色 | 该素材可作为包含该颜色的 SKU 默认图,保留“来自颜色轴”证据。 |
| `product_candidate` | 主图、详情图或无法可靠关联的素材 | 进入商品候选池;用户可选给任一 SKU。 |
| `inherited_product_default` | SKU 已验证存在但无专属图 | SKU 卡片与发布预览解析父商品默认图;不复制资产、不声称它是来源 SKU 图。 |
这允许“真实多 SKU 但没有 SKU 图”的商品继续进入编辑、定价和发布,同时不制造错误的来源关联。发布快照必须记录最终解析到的媒体 ID 与关系来源。
## 5. 多 SKU 通用规则
1. 创建多个 SKU 的前提是来源提供稳定组合记录、SKU ID、变体 SKU、`propPath` 等可验证证据;单独的“红色”“M 码”选项不够。
2. `source_variant_key` 优先使用来源 SKU ID。不存在来源 ID 时,使用来源提供的属性 ID/值 ID 的有序组合,并记录构造规则与风险;不得只拼本地化展示名称。
3. 同一 `source_variant_key` 重采集应更新其采集记录和候选素材,不应再创建一个 SKU。Ozon 没有可依赖的公开 SPU/变体组 ID:同一来源站点下,本次真实 `source_sku` 集合与既有采集记录任一 SKU 相交时,应复用该记录并覆盖来源快照;若相交到多条历史记录,必须报重复冲突,不得静默合并或删除。
4. 只识别出规格轴但没有组合时,创建一个默认 SKU,状态为 `needs_review`;规格轴仍保留给编辑页辅助人工拆分。
5. 不使用笛卡尔积构造 SKU。来源若只返回“颜色选项”和“尺码选项”而没有组合可售表,无法证明每种组合都存在。
6. 来源价格、库存、重量、尺寸只作采集参考。发布前必须由用户确认有效包装规格、定价快照、目标店铺类目和目标语言内容。
## 6. 各平台详细方案
### 6.1 Ozon 公开商品页
#### 目标
按 Ozon 页面公开的真实 `sku` 发现同一前台变体组,并为每个发现的来源 SKU 建立 `CollectedVariant`。这里的来源 `sku` 是 Ozon 前台商品/变体标识,**不是 Atlas 目标店铺的 `offer_id`**;发布后每个 Atlas SKU 仍生成自己的 `offer_id`。
#### 采集步骤
1. 优先从当前商品页的白名单状态组件读取标题、价格、画廊、参数、类目和当前来源 SKU。`[id^="state-webAspects-"][data-state]` 的 `aspects[].variants[]` 是首选的已渲染真实变体表;`webShortCharacteristics` / `webDetailedCharacteristics` / `webCharacteristics` 的 `characteristics[]` 转为公共商品属性。不得扫描脚本文本或整页 SKU 字符串。画廊只读取 `webGallery.coverImage` 和 `webGallery.images[].src`,先将 Ozon `/wcN/` / `/cN/` 缩略 URL 还原为原图,再去重。
2. 若当前页只渲染了部分变体,使用只限 Ozon 域名、只接受数字 SKU 的 MAIN world bridge 请求 `/api/entrypoint-api.bx/page/json/v2?url=/modal/aspectsNew?product_id=<sku>`,从 `webAspectsModal-*.aspects[].variants[]` 提取每一条 `sku`、标题、价格、封面和规格文本。桥接只返回这些白名单 widget 状态,绝不暴露 Cookie、请求头或任意 URL 请求能力。详情图只允许从固定的第二页段 `webDescription`、`webRichContent`、`webFeatures` 中取图片字段;`webDescription.richAnnotationJson` 仅转换为描述纯文本,不采集图片 URL 以外的页面组件内容。
3. 将当前 SKU 和发现到的关联 SKU 放入有界队列;按真实 `sku` 去重。对每个 SKU 通过同一 bridge 请求 `/api/entrypoint-api.bx/page/json/v2?url=/product/<sku>/`,从 `webProductHeading`、`webPrice`、`webGallery`、`webAspects` 与当前 SKU 的完整 `webCharacteristics` 补齐详情,并将新发现的真实 SKU 继续入队。
4. 队列达到固定上限(建议 100)或无新 SKU 时停止。记录 `discovered_count`、`hydrated_count`、失败项与页面证据。
5. 每个发现的来源 SKU 创建一个 `CollectedVariant`;补全成功时记录为 `verified` 并把 `webGallery` 的每一张图片以 `source_main + variant_direct` 绑定到该 SKU。规格 `data.coverImage` 以 `source_sku + axis_value` 记录,不能因同一画廊的第二张图片而虚构 SKU 图片关系。没有专属图片时设置 `inherited_product_default`,不丢弃变体。不得用 `document.images`,以免把 Ozon Logo、支付图和推荐商品误采为商品素材。
#### 组身份与重复采集
Ozon 当前公开页面没有可依赖的 `group_id`、`model_id` 或 SPU ID。`/modal/aspectsNew` 的 `product_id` 参数以及链接的 `from_sku` 都是当前页面 SKU,页面 widget ID 也只是易变的前端实例 ID。因此:
1. `source.itemKey` 保存用户实际打开的 SKU,作为采集入口证据;它不是 SPU 主键。
2. 服务端以同一工作空间、同一来源平台的 `product_variants.source_sku` 与本次完整 SKU 集合求交集。唯一命中时保留既有 `collection_item` / `product` ID,用最新页面资料、SKU 与候选素材覆盖旧快照。
3. 排序 SKU 集合的哈希仅可作为诊断辅助,不能作为永久身份键;Ozon 随时可能增加、隐藏或拆分变体。
4. 多个既有采集记录同时命中时返回 `SOURCE_VARIANT_GROUP_AMBIGUOUS`,等待显式的人工合并能力,避免错误地合并不同商品。
#### 完整性边界
“采集完整”只表示当前公开页面明确暴露的同一变体组已经遍历完成,不能承诺获得 Ozon 系统中所有相似商品、其他卖家 offer、未在该组展示的变体,或受地区/库存/实验策略隐藏的内容。来源页面没有稳定 `sku` 的候选项不得创建 SKU。
#### 失败与回退
- 公开变体组不可读:只创建当前页面默认 SKU,并保留“未发现可验证关联 SKU”警告。
- 个别变体详情失败:保留已经发现的 `sourceVariantKey`,状态为 `partial`;用户可在采集箱触发重新补全。
- 页面内容延迟加载:先等待目标组件和有限滚动;不得抓取推荐、关联购买等非当前商品组件。
### 6.2 1688
#### 可复用的旧插件能力
旧项目的 `extensions/collector` 会通过 MAIN world bridge 读取 `window.context`,已能从中取得:
- `skuModel.skuProps`:规格轴和值,部分值带图片;
- `skuMapOriginal`:SKU 级价格、库存和组合信息;
- `pieceWeightScaleInfo`:部分 SKU 的长、宽、高和重量;
- 主图、视频、标题、类目、店铺、参数和来源 `offerId`。
但旧实现把 SKU 信息扁平为“带规格名的 SKU 图片”,其最终 `ScanResult` 和 `/api/materials` 上报均未保留真实 SKU 记录,必须重构。
#### 新解析方案
1. 读取并保存规格轴:每个属性维度的来源属性 ID、名称、值 ID、值名称和图片 URL。
2. 遍历 `skuMapOriginal` 的每个真实组合行。优先取行自身 SKU ID;若没有,读取行中完整的属性 ID/值 ID 组合生成 canonical key。
3. 通过属性 ID/值 ID,而不是文字名称,将组合行映射到规格轴。这样“黑色”在两个维度重复出现也不会错配。
4. 读取该组合行的价格、库存和可用状态;从 `pieceWeightScaleInfo` 以来源 SKU ID 或同一属性组合匹配重量/尺寸,保存原单位和证据路径。
5. 若颜色/样式轴图片明确属于组合中的一个值,创建 `axis_value` 媒体关系;无可靠关系时只放候选池,SKU 继承父商品默认图。
#### 禁止的回退
参考毛子 ERP 的 1688 DOM 逻辑会把“规格选项 × SKU 详情”做笛卡尔积,再用父商品主图填充图片。父图回退本身可用,但笛卡尔积会制造不存在的组合,Atlas 禁止采用。缺少真实组合表时只保留规格轴并创建默认 SKU。
### 6.3 淘宝与天猫
#### 可复用的页面数据
旧插件读取 `__ICE_APP_CONTEXT__`、`__general_skupanel_cache_data` 和 `__ICE_DATA_LOADER__`,目前只能提取规格轴和值图片。毛子 ERP 的实现展示了真实组合应如何解析:
- `skuBase.props`:属性维度和值,包含 `pid`、`vid`、名称和部分图片;
- `skuBase.skus`:每个真实可售组合的 `skuId` 和 `propPath`;
- `skuCore.sku2info[skuId]`:该组合的价格等 SKU 级信息。
`propPath` 中的 `pid:vid` 列表是组合证据。新插件须将它映射回 `skuBase.props`,生成如“颜色=红、尺码=M”的选择集,使用 `skuId` 作为 `sourceVariantKey`。
#### 采集策略
1. 优先读取当前页面已经加载的状态数据,不主动调用签名 MTop 接口。
2. 若状态中同时存在 `skuBase.skus` 与 `skuCore.sku2info`,按真实 `skuId` 创建多 SKU;价格缺失时标记 `partial`,不丢失组合。
3. 若只存在 `skuBase.props`,只保存规格轴和颜色等值图片;创建默认 SKU 并要求人工确认。
4. 颜色值图片可按 `axis_value` 关系继承给包含该颜色的真实组合;尺码、容量等无图片的组合继承父商品默认图。
毛子 ERP 会在某些场景直接携带浏览器登录态调用签名 MTop 详情接口,取得 `skuCore` 与详情描述。该做法可以作为独立的技术可行性研究,但不能成为首期生产依赖:它受签名算法、登录态、风控和来源站规则影响。若未来试验,必须显式开关、最小请求量、记录来源与失败原因,且不能把 Cookie 或签名凭据上传 Atlas。
### 6.4 平台能力与回退矩阵
| 平台 | 真实 SKU 组合证据 | 价格/库存 | SKU 图片策略 | 无组合时 |
|---|---|---|---|---|
| Ozon | 公开变体的 `sku` 与页面关联数据 | 逐 SKU 公开价格;库存通常不作为可信采集字段 | 逐 SKU封面,缺失时继承父图 | 当前页面默认 SKU。 |
| 1688 | `skuMapOriginal` 行与属性 ID/值 ID 组合 | 可采集价格、库存 | 颜色/样式值图片按轴继承;其余父图继承 | 默认 SKU + 规格轴。 |
| 淘宝/天猫 | `skuBase.skus.skuId + propPath` | `skuCore.sku2info` | 颜色轴图片按组合继承;其余父图继承 | 默认 SKU + 规格轴。 |
## 7. Ozon Seller 网页增强采集:观察性实验
### 7.1 目的与边界
毛子 ERP 在公开 Ozon 商品页采集外,还要求浏览器打开已登录的 `seller.ozon.ru`,并通过跨标签消息调用 Seller 网页的内部接口。Atlas **一期即实现**第 7.3 节四个接口的浏览器本地观察模式;用户须在插件设置中显式开启。
实验模式目标是把公开页面发现的来源 SKU 与 Seller 网页返回的补充数据并排保存,帮助评估是否值得采用;它不得:
- 自动修改 Atlas 商品、价格、包装规格、库存或发布数据;
- 提取完整 Cookie、保存/上传 Cookie,或将 Cookie 写入日志、数据库、队列和扩展持久化存储;
- 替代官方 `Client-Id + Api-Key` Seller API 的正式发布、库存和状态同步;
- 将返回的销量/浏览等数据表述为“当前店铺数据”,除非 Ozon 未来提供可验证的官方契约。
四个接口都属于 `seller.ozon.ru` 网页内部接口,不是公开 Seller API;官方公开文档未给出其稳定契约。接口变动、登录态不足或风控失败必须视为正常实验失败,而非商品采集失败。
### 7.2 参考插件的调用方式
毛子 ERP 的 background/content script 大致如下:
```text
公开 Ozon 商品页发现 source SKU
→ 检查是否有已登录 seller.ozon.ru 标签页
→ 从当前浏览器会话取得 company_id
→ 跨标签发消息给 seller.ozon.ru content script
→ 在 seller.ozon.ru 同源上下文中 fetch 内部接口
headers: x-o3-company-id / x-o3-language
credentials: include
→ 仅把响应数据(不含 Cookie)回传来源页
→ 毛子 ERP 加密后上传自己的后端
```
Atlas 只可借鉴“请求必须在已登录 Ozon 同源标签执行”的技术结构。四个请求由浏览器扩展在 Seller 同源标签的页面上下文执行,`credentials: include` 仅让浏览器自动附带现有会话 Cookie;Atlas 不取得 Cookie 正文。请求所需的 `company_id` 仅在本次浏览器会话内从已登录 Seller 页面状态或 `sc_company_id` Cookie 取得,不上传为会话凭据、不写入扩展持久化存储。
实验结果进入 `source_evidence` 或独立 `seller_enrichment_observations`,保存**净化后的**响应副本、字段清单、接口名、采集时间、当前来源 SKU、解析版本和错误信息。`company_id` 如确有诊断必要,只能保存不可逆摘要或单独受控的店铺匹配标记,不能保存完整 Cookie。
### 7.3 四个接口与已观察到的用途
| 接口 | 参考请求形态 | 已观察到的用途 | Atlas 实验记录 |
|---|---|---|---|
| `/api/v1/search-variant-model` | `{ name: sourceSku, limit: 50 }` | 按来源 SKU/名称查找变体模型候选。 | 候选数量、候选 `variant_id`、是否存在精确 SKU。 |
| `/api/v1/search` | `company_id` + SKU filter + `is_copy_allowed: false` | 以来源 SKU 查找内部变体记录,必要时取得 `variant_id`。 | 查询上下文、匹配项、选择理由。 |
| `/api/site/seller-prototype/create-bundle-by-variant-id` | `company_id`、`variant_id`、`source: SOURCE_UI_COPY_MERGED` | 获得复制/创建商品 bundle;代码可见其中包含商品图片、包装长宽高和重量等。 | bundle 的净化原始 JSON、可解析字段、与公开页差异。 |
| `/api/site/seller-analytics/what_to_sell/data/v3` | `company_id`、SKU、`period: monthly`、`stock: any_stock` | 参考插件读取销量/销售额相关字段、品牌、类目、浏览/点击/退款等指标;其他字段会透传到其后端。 | 全量字段名、数值、时间口径、账户差异、是否可重复。 |
前 3 个接口主要用于 SKU/变体定位和复制 bundle;经营指标主要来自第 4 个分析接口。截图中的 rFBS 佣金、跟卖列表和跟卖价在毛子 ERP 中还会通过其自身后端接口补充,不应误认为由这四个 Ozon 接口直接返回。
### 7.4 数据范围尚未确认的事项
`what_to_sell/data/v3` 请求需要 `company_id`,但仅从插件代码无法证明它返回“当前店铺指标”还是“平台/权限范围内的市场洞察”。毛子 ERP 会对公开来源商品的 SKU 发起请求,这说明它可能用于竞品/选品数据;但这不是可依赖的接口契约。
因此实验页面必须标注:`Ozon Seller 网页观察数据,范围与口径未确认,仅供研究`。
验证方法:使用两个不同店铺账户,对同一个不属于任一店铺的公开 SKU 对比响应;再和当前店铺已上架 SKU 的官方 Seller API 数据对比。只有在口径、权限和稳定性得到确认后,才能决定是否建设独立“选品洞察”功能。
### 7.5 一期 Atlas 本地观察流程与安全控制
1. 用户在插件设置中显式打开“采集 Ozon Seller 网页观察数据(实验)”;默认关闭。该开关和第 7.3 节四接口能力均在一期交付。
2. 用户自行在浏览器登录 `seller.ozon.ru` 并保留一个有效 Seller 标签页;插件只检测标签页和请求可用性,不显示、不导出、不持久化 Cookie。
3. 标准公开页面采集先完成。对每一个发现的 `sourceVariantKey`,扩展后台按低并发、有限数量,通过 Seller 标签页依次发起四个观察请求。
4. 回传前清除请求/响应中可能存在的会话字段、用户信息、店铺名称、授权头等无关信息;服务器再次执行结构化白名单与脱敏。
5. 结果写入工作空间隔离的观察记录。采集箱展示“公开页数据 / Seller 网页观察数据”的字段差异,但不会自动覆盖草稿。
6. 请求失败、账号无权、没有 Seller 标签页或内部接口变更时,只记录 `unavailable` 原因;“编辑上架”和“采集商品”仍正常完成。
### 7.6 Cookie 能力分级与毛子 ERP 调研结论
“不持久化 Cookie”不等于“不使用用户浏览器的现有登录态”。浏览器在 Seller 同源 `fetch(..., { credentials: 'include' })` 时会自动附带该站 Cookie;Cookie 不需要、也不应离开浏览器才能完成一期四接口观察。
已核对的毛子 ERP 插件行为:它具有 Chrome `cookies` 权限;其本地跨标签请求会读取 `sc_company_id` 并使用 `credentials: 'include'` 调用四个接口;此外,用户点击“绑定 Cookie”后,它会读取完整 Seller Cookie 列表并提交给毛子后端的 `/api.shop/set_cookies`。该后端后续具体调用范围无法仅凭插件产物证明。
| 级别 | Cookie 位置与用途 | 能力边界 | Atlas 决策 |
|---|---|---|---|
| **一级:浏览器本地观察** | 用户浏览器管理 Cookie;扩展仅在已登录 Seller 标签页同源发请求,不读取完整 Cookie 值。 | 一期调用第 7.3 节四接口,保存净化结果;用户需保持浏览器 Seller 登录态。 | **一期实现。** |
| 二级:加密保存 Cookie(实验) | 用户显式授权后,浏览器将会话 Cookie 加密上传;云端 Worker 在白名单内调用网页接口。 | 可在用户未打开浏览器时执行已验证的补充查询或定时刷新;不自动获得任何未经验证的新业务能力。 | **预留,不实现。** |
| 三级:Cookie 驱动业务自动化 | 云端以会话身份调用具有写入副作用的网页接口。 | 可能涉及发布、改价、库存、广告等高风险操作;接口稳定性、账号权限、平台规则均未确认。 | **不纳入当前规划。** |
二级若未来立项,必须逐接口白名单、独立风险评审和用户再次授权;按门店验证 `company_id` 匹配;Cookie 只做信封加密保存,队列仅传 `secret_id`;不得记录明文、不得返回前端,支持立即撤销、过期检测和完整审计。无论任何级别,正式商品发布、库存和状态同步仍以官方 `Client-Id + Api-Key` Seller API 为准。
## 8. Atlas API、任务与存储
### 8.1 插件接口建议
| 接口 | 用途 |
|---|---|
| `GET /api/extension/session` | 返回登录状态和工作空间是否可用;不返回平台、目标店铺或敏感门店凭据。 |
| `POST /api/extension/auth/exchange` | 用一次性授权码交换扩展短期令牌。 |
| `POST /api/extension/materials` | 接收 `CollectedProductPayload`;服务端仅解析当前工作空间,实施幂等创建未绑定目标平台/门店的采集记录和草稿。 |
| `GET /api/collection-items/{id}` | 查询采集记录、SKU 发现数、素材转存状态和警告。 |
| `POST /api/collection-items/{id}/retry-assets` | 重新投递失败素材。 |
| `POST /api/collection-items/{id}/seller-enrichment-observations` | 一期的本地观察开关启用时,接收已净化的四接口结果;不接收 Cookie。 |
不得让插件在请求体中随意指定 `workspace_id`、`platform_id` 或 `store_id`。服务端从令牌确定工作空间并在事务中应用 PostgreSQL RLS;目标平台和店铺仅在 ERP 编辑入口按第 3.1 节规则绑定。
### 8.2 素材转存
- 插件上报来源 URL、Referer origin、MIME/类型、组别、排序和媒体关系;不把二进制图片塞进常规 JSON。
- Worker 用受控请求下载来源素材,进行 MIME、大小、尺寸、哈希校验,再写入私有 MinIO/Kodo 对象存储;资产记录保存 `storage_provider + bucket + object_key`,不保存转存后文件的长期访问 URL。
- 下载受来源防盗链、过期 URL 或风控影响而失败时,保留来源 URL 和错误;用户可重新采集或在编辑页手动上传。
- 同一工作空间内用内容哈希和来源规范化 URL 去重,但同一资产可在多个商品/SKU 媒体关系中复用。
### 8.3 可恢复性
采集记录、素材任务和未来发布任务都应先写 PostgreSQL,再经 outbox 投递 BullMQ。第 7.3 节四接口请求必须在用户浏览器的 Seller 同源标签完成,不能投递给云端 Worker;扩展将结果或 `unavailable` 原因提交服务端后再落库。浏览器关闭、Worker 重启或 Redis 重建都不能造成已创建商品草稿重复或素材关系丢失。
## 9. 旧实现与参考实现的取舍
### 9.1 `ozon-seller-kit/extensions/collector`
可复用:WXT/TypeScript 扩展结构、Shadow DOM 注入、MAIN world bridge、来源素材分组、Ozon/1688/淘宝/天猫的基础页面识别、图片 URL 还原、详情图懒加载和后台代理请求。
不复用:全高 AI Side Panel、AI 规划/生图/导出、无鉴权 `POST /api/materials`、本地服务地址、单商品数据模型,以及把 `variantName` 当作 SKU 关系的做法。
### 9.2 `extension-v1` 与 `extension-v2`
两者的 `ProductJson` 都只有一个商品;`_images.sku[].variantName` 只是图片备注,不能表达真实多 SKU。v1 甚至只取淘宝/天猫第一条规格轴;v2 对 Ozon 仅保存变体名称和图片。它们只能作为页面选择器和素材采集参考。
### 9.3 毛子 ERP 插件
可借鉴:小悬浮窗和登录状态切换、淘宝/天猫 `skuId + propPath + sku2info` 的真实组合解析、Ozon 以公开 `sku` 发现变体并逐一补全详情、按 SKU 去重、无专属图时回退父商品图、Seller 网页内部接口的跨标签同源调用结构。
不复用:读取 ERP localStorage Token、长期保存访问令牌、上传 Cookie、依赖未公开 Seller 网页接口完成核心业务、1688 的规格轴与 SKU 明细笛卡尔积、将未证实的经营指标作为正式店铺事实。
## 10. 实施分期与验收
### 10.1 分期
1. **采集基础与 Seller 本地观察**:扩展认证、小浮窗、工作空间检查、统一载荷、无目标平台/门店的采集箱入库、素材转存、幂等和“编辑上架/采集商品”;同时实现第 7.3 节四接口的跨标签同源调用、净化存储、`unavailable` 回退和字段差异展示。
2. **真实多 SKU**:1688 `skuMapOriginal`、淘宝/天猫 `skuId + propPath`、Ozon 公开 `sku` 变体遍历;完善 SKU 媒体继承与采集箱警告。
3. **观察数据验证**:用双店铺账户完成字段字典、口径、权限范围和稳定性对照;不接入正式发布链路,也不升级为 Cookie 云端保存。
4. **编辑与发布闭环**:动态属性、AI 文案/媒体/类目、定价快照、发布校验、官方 Ozon Seller API 导入和轮询;库存由用户在 Ozon 后台设置。
### 10.2 验收样本矩阵
每个平台至少准备以下可重复样本并保存允许使用的页面快照:
| 场景 | 断言 |
|---|---|
| 单 SKU 商品 | 创建一个默认 SKU,主图和参数完整。 |
| 单规格轴、多 SKU | 创建来源实际出现的所有 SKU,不重复。 |
| 颜色 × 尺码 | 只创建来源组合表实际列出的组合;不得出现笛卡尔积假组合。 |
| 有颜色图、无尺码图 | 组合继承颜色轴图或父图,关系可追溯。 |
| 无 SKU 图 | 真实 SKU 均存在并继承父商品默认图。 |
| 个别变体详情/素材失败 | 保留发现的 SKU 和错误;可单独重试。 |
| 重复点击采集 | 复用同一采集记录和商品草稿。 |
| 无目标店铺/令牌过期 | 没有 Ozon 默认店铺时仍可采集;进入 Ozon 编辑时提示设置默认店铺。令牌过期时只显示登录引导,不写入数据。 |
| Ozon Seller 本地观察无权限、未登录或无 Seller 标签页 | 标准公开采集成功,四接口观察分别记录 `unavailable` 且不阻塞。 |
## 11. 待确认与后续文档
本文件已确认插件入口、工作空间共用采集箱、目标平台/门店绑定时点、多 SKU 基本原则和 Ozon Seller 网页观察实验的边界。实施前仍需补充:
1. `CollectedProductPayload` 的 OpenAPI/JSON Schema 与 PostgreSQL 表、迁移和 RLS 细节;
2. ERP 页头当前平台、该平台默认店铺的选择、切换和删除规则,以及采集草稿已绑定目标后的换平台策略;
3. 各来源真实页面快照测试集与解析器版本策略;
4. Ozon Seller 网页四接口的字段字典、双店铺对照结果、数据保留期和是否具备建设独立选品洞察的价值;
5. 采集箱列表、详情、重新采集、合并与归档的页面需求;
6. 正式 Ozon 发布任务的状态机、请求/响应审计与库存策略。
相关文档:[商品编辑页](../product/README.md)、[商品 API 草案](../product/api.md)、[技术与异步任务方案](../architecture/technology-and-deployment.md)、[Ozon Seller API 资料](../integrations/ozon-seller-api/README.md)。