# 采集箱与浏览器插件 API > 采集箱只属于工作空间:采集时没有目标平台、目标门店或店铺凭据。列表上架状态则按 ERP 页头当前平台汇总;它不是 Ozon 在线商品可见性,也不因切换到另一平台而继承。 ## 1. 浏览器扩展授权 首期按毛子 ERP 风格复用 Atlas Web 登录态。用户从插件点击登录后,扩展打开固定 Atlas Web 页面;用户在该页完成登录或会话恢复后,Web 将当前短期 access token 镜像到本站 `localStorage["atlas.extension.web-access"]`。扩展每 2 秒仅从该 Atlas 标签读取这个值,保存到 Chrome extension storage,并以 Bearer token 调用下述接口。 这是维护者批准的自托管插件例外:扩展不读取 refresh cookie、Cookie、来源站 localStorage 或来源站授权信息;Web token 不得进入日志、数据库、Outbox 或采集载荷。Token 失效返回 `401` 后,插件清除自己的缓存并要求用户再次从 Atlas Web 登录同步。完整取舍见 [`../project/extension-login-alternatives.md`](../project/extension-login-alternatives.md)。 `GET /api/extension/session` 使用从 Atlas Web 同步的 Bearer token,返回最小化状态: ```json { "data": { "authenticated": true, "workspace": { "id": "uuid", "available": true }, "can_collect": true } } ``` 不返回平台、默认店铺、Cookie、Ozon 凭据或 Web refresh token。令牌失效返回 `401`,插件清除缓存并只展示“登录 Atlas ERP”。 ## 2. 插件上报采集结果 `POST /api/extension/materials` Headers:扩展 Bearer token 与 `Idempotency-Key` 必填。请求体为已定义的 `CollectedProductPayload`,其 `source` 至少包含 `platform`、`itemKey`、`canonicalUrl`、`collectedAt`、`parserVersion`;`variants` 仅列来源有稳定证据的真实组合,媒体只传来源 URL、`mediaType`(`image` / `video`)、可选 `posterUrl`、关系和证据,不传二进制。 2026-10-06 SKU 容量修订:单次采集的 `variants` 上限从 100 提高到 1000,商品草稿完整保存采用同一上限,以支持真实多 SKU 来源商品从采集进入编辑。插件本地校验和 API 共享同一常量 `PRODUCT_VARIANT_LIMIT`;超过 1000 时明确提示「单个商品最多支持 1000 个 SKU」,整个采集失败,不截断 SKU 或创建部分商品。此限制不等于工作空间采集商品数配额,也不改变首次发布每批最多 50 个 SKU、媒体候选和其他字段的既有约束。采集响应 `collection_item.variant_count` 返回该商品实际未删除 SKU 数,复采仍保持商品和同 key SKU 身份。 请求体不得包含或传入 `workspace_id`、`target_platform_id`、`platform_id`、`store_id`、`api_key`、Cookie、授权头或来源站会话字段。服务端从扩展令牌解析工作空间,并将来源 `platform` 映射为系统已启用的来源平台。 服务端在一个事务中:检查工作空间采集箱上限 → 按 `workspace + source_platform + source_item_key` 查找活动记录 → 创建记录,或以本次页面快照覆盖已有记录的来源 URL、标题、价格、原始证据、规范化载荷与候选素材。采集阶段的图片和视频只保存为 `pending_selection` 候选记录,**不下载媒体、不投递素材 Worker**;用户在编辑流程中选择媒体后,才调用按需转存接口。重复上报不会增加采集箱配额,且会保留原有 `collection_item` 与 `product` ID;旧快照中尚未完成的候选或任务会被取消,新快照使用新的来源快照版本,旧 Worker 不得回写新快照。 尚未创建任何 `product_platform_profile` 时,SKU 草稿仍是来源投影:同一 `source_variant_key` 原位更新,新 key 创建,缺失 key 软删除。**一旦进入任一平台编辑器,SKU 草稿及其平台 mapping 不再是可替换来源投影。** 此后的重复采集仍更新来源证据、规范化载荷和候选素材;同 key 复用原 SKU ID 且不覆盖用户编辑字段,新发现的来源 SKU 会为每个已有平台配置创建 `not_listed` mapping,来源未返回的 SKU 不自动删除或恢复。来源差异的人工审查 UI 另由商品编辑模块提供,采集接口不得通过删除 SKU 或级联删除映射来“覆盖”草稿。 1688 结构化采集以 `skuModel.skuInfoMap` 的真实可售记录生成 SKU;每条记录的 `price`(CNY)按两位小数四舍五入后写入对应 SKU 的 `purchase_cost_cny`,`skuProps.value` 中按规格名称匹配的图片作为带 `variantSourceKey` 的 `source_sku` 候选。尚无平台编辑草稿时,重复采集会刷新来源采购价;进入编辑器后不覆盖用户修改过的采购价。SKU 对应的来源图片可在编辑页预览,用户选择后才转存并保存为正式主图。 2026-10-05 货源地址修订:插件已通过 `source.canonicalUrl` 上报商品详情页地址(1688、淘宝、天猫、Ozon 均适用)。新建商品时同时初始化 `products.purchase_url`,供编辑器「货源链接」使用。编辑快照的 `product.common.purchase_url` 优先返回手工保存的非空链接,未填写时回退该采集记录的 `source_url`,使历史已采集商品无需重新采集即可显示来源地址;手动创建且无来源地址的商品仍为空。重复采集更新来源地址,已填写的商品货源链接保持不变;清空货源链接表示恢复使用采集来源地址。接口字段结构不变。 成功返回 `201`(新建)或 `200`(复用): ```json { "data": { "collection_item": { "id": "uuid", "status": "ready_for_edit", "source_title": "商品标题", "variant_count": 3 }, "product_id": "uuid", "reused": false, "refreshed": false, "editor_entry_path": "/products/product-uuid/editor?collection_item_id=collection-uuid", "materialization": { "total": 12, "pending_selection": 12, "queued": 0, "succeeded": 0, "failed": 0 } } } ``` 达到工作空间上限时返回 `422 COLLECTION_LIMIT_REACHED`,并附 `{current_count, limit}`;不能因重复点击或并发请求绕过 1000 条限制。采集阶段不会因下载来源媒体失败而失败;关键商品/SKU 解析失败才使记录进入 `failed` 并返回可展示的脱敏摘要。 ## 3. Ozon Seller 网页本地观察 `POST /api/collection-items/{collection_item_id}/seller-enrichment-observations` 仅接受扩展 token。用户在浏览器已登录 `seller.ozon.ru` 标签页内,同源调用已白名单的四个网页内部接口后,将**净化结果**或不可用原因上报: ```json { "interface_code": "whitelisted-interface-code", "source_variant_key": "optional-source-variant-id", "status": "observed", "observed_at": "2026-09-02T08:00:00Z", "parser_version": "collector-1.0.0", "sanitized_payload": { "only": "whitelisted business fields" } } ``` 不可用时使用 `status: "unavailable"` 并传 `unavailable_reason`;请求、响应在客户端和服务端均须过滤 Cookie、`Authorization`、店铺/个人无关信息、会话 ID 及完整 `company_id`。标准公开页面采集已成功时,本接口失败或未调用不得改变采集箱主状态、更不能阻断编辑。 服务端解析 `source_variant_key` 到当前采集商品的 SKU 后写入 `collection_seller_enrichment_observations`。接口代码不在白名单、记录不属于该工作空间或 payload 含禁用会话字段时返回 `422`;不把原始敏感 payload 写日志。 ## 4. 采集箱列表与详情 `GET /api/collection-items?cursor={opaque}&limit=20&q={keyword}&source_platform_code={code}&listing_status={not_listed|partially_listed|listed}` 要求 `X-Atlas-Platform-Id`,按 `updated_at DESC, id DESC` 返回当前工作空间未删除记录。`q` 模糊匹配标题;`source_platform_code` 是**来源**筛选,不等于页头目标平台;`listing_status` 是按页头目标平台汇总的状态。 ```json { "data": [{ "id": "uuid", "product_id": "uuid", "source": { "kind": "platform", "platform_code": "1688", "url": "https://…" }, "title": "商品标题", "thumbnail": { "asset_id": "uuid", "preview_url": "short-lived-or-null", "status": "succeeded" }, "source_price": { "min": "20.00", "max": "35.00", "currency_code": "CNY" }, "variant_summary": { "count": 3, "needs_review": false }, "note": "待确认材质", "collection_status": "ready_for_edit", "listing_summary": { "status": "partially_listed", "detail": "1 个 SKU 已导入;2 个等待首次上架", "last_error": null }, "created_at": "2026-09-02T08:00:00Z", "updated_at": "2026-09-02T08:05:00Z", "revision": 4 }], "meta": { "next_cursor": null, "has_more": false, "active_count": 12, "collection_limit": 1000 } } ``` `listing_summary.status` 映射 UI 的未上架/已部分上架/已上架;`collection_status` 是采集/素材处理状态,二者绝不混用。采集测试页应直接使用候选的 `source_url` 预览图片,视频则用 `preload=none` 的播放器,不等待转存、也不为预览创建存储对象;商品未命名时 `title` 返回 `null`,前端展示“未命名商品”。 `GET /api/collection-items/{collection_item_id}` 无平台 header 时仅返回采集基础信息、SKU/素材转存摘要、净化 Seller 观察结果摘要与警告;带 header 时附加 `listing_summary`。观察结果详情只对当前工作空间成员可见。 `PATCH /api/collection-items/{collection_item_id}` Headers:`If-Match`、`Idempotency-Key` 必填。首期仅允许修改 `note`;标题、SKU 和素材在商品编辑 API 中修改。响应返回更新后的采集记录。`If-Match` 对应记录的 `revision`,包括采集 Worker 状态更新在内的每次修改都递增;不得覆盖 Worker 的状态更新。 ## 5. 手动创建、按需转存、删除与素材重试 `POST /api/collection-items` Headers:`Idempotency-Key` 必填。 ```json { "title": "未命名手工商品", "note": "可选备注" } ``` 创建 `source_kind=manual` 的采集记录、空商品草稿和默认 SKU,返回 `201`、`collection_item`、`product_id` 与 `editor_entry_path`。它仍不绑定目标平台/店铺,也不绕过之后的类目、定价和首次发布校验。 `POST /api/collection-items/{collection_item_id}/asset-materializations` Headers:`Idempotency-Key` 必填。请求 `{ "transfer_ids": ["uuid"] }`;只能选择当前来源快照中状态为 `pending_selection` 的候选图片或视频。服务端将所选候选改为 `queued`,创建 operation,并通过 Outbox 投递 `collection.assets.materialize.requested` 给 Worker;返回 `202` 和 operation。该接口是商品编辑、加水印、AI 处理和发布流程共用的按需转存原语,不应在采集测试页自动调用。 `POST /api/collection-items/{collection_item_id}/asset-retries` Headers:`Idempotency-Key` 必填。请求 `{ "transfer_ids": ["uuid"] }`;只能重试该采集记录中 `failed` 或 `cancelled` 的转存任务。返回 `202` 和 operation。不能用它重试来源商品解析或已成功素材。 `POST /api/collection-items/bulk-delete` Headers:`Idempotency-Key` 必填。请求 `{ "ids": ["uuid"] }`,原子软删除其中当前工作空间的全部有效记录,返回 `202`、`deleted_count` 和延迟素材清理 operation。已上架记录同样可删除,但响应/前端确认文案必须说明:不删除、不下架任何 Ozon 商品,也不移除在线商品镜像。 `POST /api/collection-items/clear` Headers:`Idempotency-Key` 必填。先由列表读取 `active_count` 并展示二次确认;确认请求: ```json { "confirm": true, "expected_active_count": 12 } ``` 服务端锁定工作空间后比较当前有效数量;数量变化返回 `409 COLLECTION_COUNT_CHANGED`,要求用户重新确认。匹配时软删除全部有效记录并返回 `202`、删除数与延迟清理 operation。它不影响平台商品、发布快照或在线商品。 ## 6. 与商品编辑接口的边界 2026-10-05 插件入口修订:插件采集返回的 `editor_entry_path` 为 `/products/{product_id}/editor?collection_item_id={collection_item_id}`。「编辑上架」采集成功后打开此地址并提示「已打开商品编辑页」;「采集商品」仍只采集、不打开编辑页。新采集和重复采集均使用响应的真实商品 ID,复采继续进入原商品。入口携带的采集记录 ID 供 Web 在当前页头平台下调用 `POST /api/collection-items/{id}/editor-contexts` 初始化或复用上下文,再读取商品编辑快照;返回的商品 ID 必须与路径一致。无默认店铺时保留已有 `DEFAULT_STORE_REQUIRED` 提示,不在采集接口中猜测目标平台或绑定店铺。已有上下文沿用用户编辑资料和发布状态,不重复上架。手动创建的旧 `/products/{product_id}` 入口及采集测试页路由仍保留。