Expand seller store functionality

This commit is contained in:
R524809
2026-09-07 17:51:11 +08:00
parent 4c0f3a4a79
commit ceb644a3f9
35 changed files with 32883 additions and 364 deletions
+11 -2
View File
@@ -25,9 +25,18 @@
### 2.2 登录与会话
用户用邮箱和密码登录。登录成功进入默认工作空间与首页;若后续一个账户拥有多个工作空间,再增加切换入口。首期需要提供退出登录;忘记密码、短信登录、第三方登录、设备管理和多因素认证均不在本轮范围。
用户用邮箱和密码登录。登录成功进入默认工作空间与首页;若后续一个账户拥有多个工作空间,再增加切换入口。首期需要提供退出登录;短信登录、第三方登录、设备管理和多因素认证均不在本轮范围。
### 2.3 全局平台与当前店铺上下文
### 2.3 忘记密码(待开发任务)
该功能已列入下一轮账户认证开发,尚未实现。登录页增加“忘记密码”入口,用户可提交邮箱并通过邮件中的一次性链接设置新密码。
- 请求重置时,无论邮箱是否已注册,页面均显示相同的成功提示,避免枚举账户;服务端须按 IP 与邮箱摘要限流,且不得记录明文密码、重置令牌或完整重置链接。
- 重置链接使用随机、不透明、一次性令牌;数据库只保存其哈希与过期时间。令牌须短时有效,使用、过期或再次申请后不得重复使用。
- 用户在重置页输入新密码与确认密码。成功后更新密码哈希、撤销该账户所有已有 session,并引导用户用新密码登录;旧 access token 与插件缓存随之失效。
- 开发前须补充 `docs/api/auth.md`、数据库迁移、邮件投递配置与失败/限流错误契约;交付须包含 API、Web 页面与邮件链接的集成测试,且测试不能输出令牌或密码。
### 2.4 全局平台与当前店铺上下文
平台和当前店铺是全站工作上下文,不在每个业务页面重复选择。页头在用户信息左侧放置“平台选择器”;用户切换平台后,菜单、类目、物流商、佣金规则和页面数据均切换到该平台范围。
@@ -1,6 +1,6 @@
# 浏览器采集插件:需求与技术方案
> 状态:已确认核心方向,待实施。本文是 Atlas Seller Kit 从来源商品详情页进入采集箱、商品编辑与后续 Ozon 上线的采集入口规范。
> 状态:核心闭环已实施;Ozon 公开多 SKU 解析与归并方案于 2026-09-07 落地,仍需用真实页面回归验证。本文是 Atlas Seller Kit 从来源商品详情页进入采集箱、商品编辑与后续 Ozon 上线的采集入口规范。
>
> 本文取代旧 `ozon-seller-kit` 采集插件的产品定位,但只复用经验证的页面采集思路,不复用旧项目的本地数据库、无鉴权接口、`/trial/:id` 页面、AI 套图或生图流程。
@@ -43,7 +43,7 @@
首期不在悬浮窗中选择平台或店铺。服务端从插件授权身份只解析工作空间,创建未绑定目标平台/门店的采集记录和商品基础草稿。用户进入 Atlas ERP 编辑页时,页面使用页头当前选中的平台;首期页头默认选择 Ozon,服务端取得该平台发布配置并预选默认店铺。若当前平台没有可用门店,允许保留采集箱记录,但不能进入该平台的商品编辑/发布流程,应引导用户在店铺管理中添加门店。
推荐采用扩展专用的短期授权码流程:插件打开 Atlas 登录/授权页,登录页完成后向 `chrome-extension://{extension-id}/...` 回传一次性 code,插件交换短期 access token 与可轮换 refresh token。令牌只放 Chrome 扩展专属存储,后台请求统一携带 `Authorization: Bearer`。不得像参考插件一样直接读取 ERP 网页 localStorage 中的长期 Token。
首期按毛子 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 三个操作
@@ -84,7 +84,7 @@
| `failed` | 采集请求或关键记录创建失败;保留可诊断错误。 |
| `archived` | 用户不再处理的采集记录。 |
同一工作空间内,以 `source_platform + source_item_key` 进行幂等。重复“采集商品”应返回已有采集记录;重复“编辑上架”打开同一未绑定目标的商品草稿或其已绑定的编辑入口。只有用户在 ERP 中明确选择“重新采集并合并/创建副本”时,才允许覆盖或复制。
同一工作空间内,以 `source_platform + source_item_key` 识别同一来源商品。重复“采集商品”或“编辑上架”保留已有采集记录和商品草稿 ID,但以本次页面重新采集的来源信息覆盖上一份来源快照:标题、价格、原始证据、规范化载荷、来源 SKU 与候选素材均以最新采集为准。旧素材任务取消,新素材任务必须带新的快照版本,旧 Worker 不得回写最新内容。用户希望保留旧版本或创建副本的需求,留待正式商品编辑模块提供明确操作后再设计。
### 3.1 采集箱与目标店铺的绑定时点
@@ -161,7 +161,7 @@ interface CollectedVariant {
1. 创建多个 SKU 的前提是来源提供稳定组合记录、SKU ID、变体 SKU、`propPath` 等可验证证据;单独的“红色”“M 码”选项不够。
2. `source_variant_key` 优先使用来源 SKU ID。不存在来源 ID 时,使用来源提供的属性 ID/值 ID 的有序组合,并记录构造规则与风险;不得只拼本地化展示名称。
3. 同一 `source_variant_key` 重采集应更新其采集记录和候选素材,不应再创建一个 SKU。
3. 同一 `source_variant_key` 重采集应更新其采集记录和候选素材,不应再创建一个 SKU。Ozon 没有可依赖的公开 SPU/变体组 ID:同一来源站点下,本次真实 `source_sku` 集合与既有采集记录任一 SKU 相交时,应复用该记录并覆盖来源快照;若相交到多条历史记录,必须报重复冲突,不得静默合并或删除。
4. 只识别出规格轴但没有组合时,创建一个默认 SKU,状态为 `needs_review`;规格轴仍保留给编辑页辅助人工拆分。
5. 不使用笛卡尔积构造 SKU。来源若只返回“颜色选项”和“尺码选项”而没有组合可售表,无法证明每种组合都存在。
6. 来源价格、库存、重量、尺寸只作采集参考。发布前必须由用户确认有效包装规格、定价快照、目标店铺类目和目标语言内容。
@@ -176,11 +176,20 @@ interface CollectedVariant {
#### 采集步骤
1. 从当前商品页的白名单状态组件读取标题、价格、画廊、视频、参数、类目和当前来源 SKU。
2. 从包含产品变体的页面状态(参考实现中的 `webProductsModal` / `aspects[].variants` 一类组件)提取每一条 `sku`、标题、价格、划线价、封面、规格文本和关联链接。旧采集插件只保留了 `webAspects` 的名称与图片,丢弃了 `sku`,新实现不得如此。
3. 将当前 SKU 和发现到的关联 SKU 放入有界队列;按 `sku` 去重。对每个 SKU 加载该 SKU 的公开详情页面/页面 JSON,补齐图片、视频、描述与可能继续出现的关联 SKU。
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`;没有专属图片时设置 `inherited_product_default`,不丢弃变体。
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`,等待显式的人工合并能力,避免错误地合并不同商品。
#### 完整性边界