feat(extension): 采集插件功能开发

This commit is contained in:
2026-09-06 22:32:39 +08:00
parent 87db74fb89
commit 4c0f3a4a79
35 changed files with 790 additions and 20 deletions
+1 -1
View File
@@ -57,7 +57,7 @@ Headers:扩展 Bearer token 与 `Idempotency-Key` 必填。请求体为已定
{ "data": {
"collection_item": { "id": "uuid", "status": "materializing", "source_title": "商品标题", "variant_count": 3 },
"product_id": "uuid", "reused": false,
"editor_entry_path": "/collection-items/uuid/edit",
"editor_entry_path": "/products/uuid",
"materialization": { "total": 12, "queued": 12, "succeeded": 0, "failed": 0 }
} }
```
+24 -7
View File
@@ -1,12 +1,12 @@
# Atlas Seller Kit 交接说明
> 更新:2026-09-04。此文是新 AI 对话、架构讨论与开发工作的首读入口。开始开发前,必须先阅读并遵守 [`development-collaboration-contract.md`](./development-collaboration-contract.md)。
> 更新:2026-09-06。此文是新 AI 对话、架构讨论与开发工作的首读入口。开始开发前,必须先阅读并遵守 [`development-collaboration-contract.md`](./development-collaboration-contract.md)。
## 1. 项目定位
这是一个全新的云端产品,不是 `ozon-seller-kit` 的本地版重构。目标用户可拥有多个门店,每个门店只能绑定一个平台;平台自身可支持多个市场、国家、语言和币种。因此语言、币种、类目和发布规则不能写死为 Ozon 或中文/俄文。
当前已完成 pnpm monorepo 初始化:包含 Web、NestJS API、BullMQ Worker、WXT 浏览器扩展、共享 API 契约、数据库包、本地基础设施 Compose 与依赖锁文件。API 已实现健康/readiness、集中配置与安全基础、认证、个人工作空间上下文、平台目录/上下文、门店、水印/素材与采集箱基础管理;Worker 已具备 Ozon 凭据验证及采集记录清理消费链路,仓库同步与来源素材转存尚未接入。Web 已具备完整前端框架(布局、路由、主题)、登录/注册页、页头平台选择器、店铺管理、水印管理与真实采集箱页面;浏览器扩展仍为可运行骨架。不要从旧项目复制 SQLite 模型、无鉴权 API、旧 `/trial/:id` 或商品编辑页组件。可选择性复用已经验证过的纯计价公式、采集扩展思路和 AI 任务经验,但必须先适配多租户和云端边界。
当前已完成 pnpm monorepo 初始化:包含 Web、NestJS API、BullMQ Worker、WXT 浏览器扩展、共享 API 契约、数据库包、本地基础设施 Compose 与依赖锁文件。API 已实现健康/readiness、集中配置与安全基础、认证、个人工作空间上下文、平台目录/上下文、门店、水印/素材、采集箱与扩展采集入库;Worker 已具备 Ozon 凭据验证、采集记录清理和来源素材转存消费链路,仓库同步尚未接入。Web 已具备完整前端框架(布局、路由、主题)、登录/注册页、页头平台选择器、店铺管理、水印管理、真实采集箱页面和采集数据测试页;浏览器扩展已提供 PKCE 授权、悬浮窗及 Ozon/1688/淘宝/天猫首版页面状态解析。不要从旧项目复制 SQLite 模型、无鉴权 API、旧 `/trial/:id` 或商品编辑页组件。可选择性复用已经验证过的纯计价公式、采集扩展思路和 AI 任务经验,但必须先适配多租户和云端边界。
当前运行时基线:Node 24.20.0、pnpm 11.25.0、PostgreSQL 18.6、Redis 7.4.11、MinIO `RELEASE.2025-09-07T16-13-09Z`;Web 默认端口为 `8100`,API 默认端口为 `8200`,扩展 dev server 默认端口为 `8110`(仅本地开发用),Worker 无 HTTP 端口。完整目录、依赖边界与版本表见 [`architecture/project-structure.md`](../architecture/project-structure.md) 和 [`architecture/technology-and-deployment.md`](../architecture/technology-and-deployment.md)。
@@ -87,6 +87,21 @@
- Web 的采集箱已替换占位页,支持容量显示、标题/来源筛选、手动创建、批量删除、清空确认、空/加载/错误状态。商品编辑入口明确保留给下一任务,未伪造可发布能力。
- `apps/api/test/collection.e2e.test.ts` 覆盖手动创建、幂等重放、列表读取、乐观锁冲突和软删除;该测试、本次受影响的 API/Worker/Web TypeScript 检查及 Web 生产构建均已通过。任务清单见 [`m2.1-collection-box-foundation.md`](./m2.1-collection-box-foundation.md)。
### 1.9 开发进度(2026-09-05):M2.2 / M2.3 扩展采集首个闭环
- 已实现扩展 PKCE 授权码、扩展专用 access/refresh token 轮换、现有 Atlas Web 会话的静默授权分支,以及扩展会话检查/撤销。扩展不读取 Web token 或 refresh Cookie;回调 URI 必须按已安装扩展 ID 精确登记。
- 已实现 `POST /api/extension/materials`:从扩展身份推导工作空间,创建或复用未绑定目标平台/门店的采集记录、SPU、SKU 草稿和来源素材任务。采集载荷严格限制为来源证据、规范化字段和素材 URL,不能携带店铺、Cookie、API Key 或目标平台。
- Worker 已消费素材转存 outbox 事件,执行 HTTPS 公网地址校验、大小/图片格式限制、哈希去重与 MinIO/Kodo 转存;单个素材失败不回滚采集箱商品。
- 浏览器扩展已实现 Ozon 公开商品页的小型 Shadow DOM 悬浮窗及未登录/已登录状态。Ozon 首版解析器以当前公开 SKU 和公开变体状态为证据,不生成笛卡尔积假 SKU。Ozon Seller 四接口本地观察、Ozon 变体详情逐页补全及 1688/淘宝/天猫解析仍未开始。
- 详情、边界和本地配置见 [`m2.2-m2.3-extension-collection.md`](./m2.2-m2.3-extension-collection.md)。
### 1.10 开发进度(2026-09-06):M2.4 来源解析器与采集测试页
- 扩展浮窗现覆盖 1688 商品详情、淘宝商品详情和天猫商品详情。1688 仅遍历 `skuMapOriginal` 中实际存在的组合;淘宝/天猫仅在同时取得 `skuBase.skus[].skuId + propPath` 时创建多 SKU,并以 `skuCore.sku2info` 补充参考价格。规格轴本身从不展开为笛卡尔积。
- 缺少真实组合证据时,扩展只保存规格轴,服务端创建一个默认 SKU 并标记为待确认;颜色等轴值图片仍作为候选素材,绝不伪造来源 SKU 图片绑定。
- “编辑上架”暂时不进入未完成的商品编辑模块,改为打开 `/collection-test/{collectionItemId}`。该只读测试页展示实际保存的规范化载荷、原始证据、SKU 草稿和素材转存任务,用于验证每个平台的采集结果。`GET /api/collection-items/{id}` 已提供该工作空间隔离的检查读模型。
- 已通过 API/Worker TypeScript 检查、Web 与扩展生产构建;`apps/api/test/extension.e2e.test.ts` 已覆盖 PKCE 授权、扩展会话、采集幂等复用和测试页所用检查读模型。尚未在真实来源商品页执行样本回归,接手者应按第 6 节先完成该验证。
## 2. 已确认产品决策
| 主题 | 决策 |
@@ -139,17 +154,17 @@ User → Workspace → Membership / Role
- 所有接口遵循统一鉴权、工作空间隔离、页头平台上下文、金额字符串、错误格式、`Idempotency-Key`、`If-Match` 和异步 `operation` 约定。
- **响应信封是强制契约:** 成功单资源或同步动作返回 `{ "data": T }`,列表返回 `{ "data": T[], "meta": PaginationMeta }`,失败返回 `{ "error": ApiError }`,`204 No Content` 不返回响应体。项目不采用完整 JSON:API;任何调用方不得自行增加嵌套 `data` 或以 `data: null` 表示错误。完整规则见 [`api/conventions.md`](../api/conventions.md)。
- 关键异步动作必须在同一事务写入业务记录、`operations`(如需用户可见进度)和 `outbox_events`;Dispatcher 至少一次投递 BullMQ,Worker 必须幂等。
- DDL 已通过本地隔离 PostgreSQL 执行验证;迁移执行器、认证/工作空间基础、幂等与 Outbox 写入闭环、Outbox Dispatcher、Ozon 凭据验证 Worker、上传意图清理 Worker 与采集记录清理 Worker 均已实现。仓库同步、来源素材转存、发布和在线商品同步 Worker 仍待实现。
- DDL 已通过本地隔离 PostgreSQL 执行验证;迁移执行器、认证/工作空间基础、幂等与 Outbox 写入闭环、Outbox Dispatcher、Ozon 凭据验证 Worker、上传意图清理 Worker、采集记录清理 Worker 与来源素材转存 Worker 均已实现。仓库同步、发布和在线商品同步 Worker 仍待实现。
仍需在对应模块开始编码前完成的 P1/P2 决策和数据模型补丁,集中记录在 [`reviews/implementation-readiness-open-items.md`](../reviews/implementation-readiness-open-items.md)。
## 6. 下一步建议
下一步建议按采集入口顺序推进:
下一步建议按以下顺序推进:
1. M2.2:实现扩展 PKCE 授权、`POST /api/extension/materials` 与来源素材转存 Worker;素材只传来源 URL 和净化证据,不传 Cookie、店铺凭据或二进制。
2. M2.3:实现插件悬浮窗与 Ozon 公开商品页解析器,验证真实 SKU 组合、素材关联和重复采集复用。
3. M2.4:实现 1688、淘宝、天猫解析器及各自的稳定 SKU 证据规则。
1. 用允许保存的单 SKU、单规格轴和颜色×尺码页面样本,在 Ozon、1688、淘宝、天猫逐项验证采集结果;页面状态未暴露组合数据时补 MAIN world bridge 或站点适配器,但绝不以规格轴猜测 SKU。
2. 完成 Ozon 公开变体队列的逐 SKU hydrate;再独立实施 Seller 网页四接口本地观察实验,默认关闭、仅保存净化结果且不影响标准采集。
3. 将当前 `/collection-test/{collectionItemId}` 只读检查面替换为正式商品编辑入口:按页头平台创建发布配置、预选同平台默认门店,并接入 SKU/媒体/类目编辑。
4. 进入商品编辑/在线同步前,关闭评审中的 P1:SKU 与平台发布配置同商品归属、未映射属性持久化、统一乐观锁 Repository 规则;平台能力 Schema 已在 M2.1 按固定键落地。在线商品归档策略和 Client ID 编辑边界仍待确认。
账户资料的已确认补充:`users.avatar_asset_id` 只保存素材引用,绝不保存公开 URL;`users.revision` 用于 `PATCH /api/auth/me` 的 `If-Match`。资料更新已实现幂等与版本冲突检测;修改密码已实现旧密码校验、按 IP/用户限流、撤销旧会话并轮换新会话。头像上传必须复用素材模块的预签名直传与校验流程,不引入 base64 直传;素材模块尚未实现时头像只能为 `null`。
@@ -165,4 +180,6 @@ User → Workspace → Membership / Role
- [数据库角色与 RLS](../architecture/database-security.md)
- [实现前待决与待落地事项](../reviews/implementation-readiness-open-items.md)
- [浏览器采集插件需求与技术方案](../requirements/collector-browser-extension.md)
- [M2.2 / M2.3 扩展采集交接](./m2.2-m2.3-extension-collection.md)
- [M2.4 来源解析与测试页交接](./m2.4-source-parsers-and-inspection.md)
- [技术栈、异步任务与部署方案](../architecture/technology-and-deployment.md)
@@ -0,0 +1,34 @@
# M2.2 / M2.3:扩展授权、素材转存与 Ozon 公开页采集
> 状态:已完成首个可运行闭环(2026-09-05)。Seller 网页四接口观察实验不属于本批次,仍待独立任务实施。
## 已交付
1. 扩展使用 PKCE 授权码登录,而不是读取 Atlas Web 的 localStorage 或 refresh Cookie。`GET /api/extension/authorize` 会优先复用用户浏览器已有的 Atlas Web 会话:会话有效则无感签发一次性 code;没有会话才跳转登录页,成功登录后回到原授权请求。
2. 服务端保存 code 与扩展 refresh token 的哈希,code 60 秒且一次性使用,扩展 access token 15 分钟、refresh token 30 天并轮换。令牌含扩展专用 audience,不能被普通 Web/API 身份守卫接受。
3. 扩展上报仅携带来源、规范化 SKU/规格轴、候选素材 URL 和证据;`POST /api/extension/materials` 从扩展令牌解析工作空间,不接受目标平台、门店、Cookie 或 API Key。相同工作空间、来源平台和来源 item key 复用同一采集记录。
4. 上报事务创建来源采集记录、SPU、来源确认 SKU 和素材转存任务;素材异步事件经 Transactional Outbox 进入 Worker。Worker 仅下载 HTTPS 公网地址,限制重定向、私网地址、20MB 和 PNG/JPEG/WebP,按 SHA-256 去重后写入对象存储。单张失败不会使商品草稿丢失。
5. Ozon (`ru/kz/by`) 商品详情页显示 Shadow DOM 小浮窗。未登录只显示“登录 Atlas ERP”;已登录显示“编辑上架 / 采集商品 / 进入 ERP”。编辑上架成功后打开现有 `/products/{productId}` ERP 编辑入口;采集商品只提示进入采集箱。
6. Ozon 解析器保留当前公开 SKU,并只从带 `webProducts`、`webAspects` 或 `aspects` 标识的页面状态发现关联 SKU;不会扫描整页 SKU 或把规格轴做笛卡尔积。来源未给可确认组合时,仍保留当前 SKU。主图作为商品候选素材上报,不伪造 SKU 图片关系。
## 配置与安全前提
- `EXTENSION_COLLECTOR_REDIRECT_URI` 必须精确等于已安装扩展的 `chrome.identity.getRedirectURL('callback')`;不可用通配符代替。
- 本地默认 API 为 `http://localhost:8200`。生产构建应为 Atlas API 域配置扩展 host permission 与 `ATLAS_API_ORIGIN`,并登记对应扩展 ID 的回调 URI。
- 扩展 refresh token 仅存 Chrome extension storage;Web refresh Cookie 从不返回给扩展。Worker 不接触浏览器 Cookie。
## 尚未实现
- Ozon 变体队列逐页 hydrate、SKU 级公开详情补全;当前只消费已加载公开页面状态。
- 1688、淘宝、天猫解析器,以及 Ozon Seller 网页四接口本地观察实验。
- 采集箱详情页的素材关联可视化、重试 UI 和商品编辑/发布闭环。
## 验收命令
```sh
./packages/contracts/node_modules/.bin/tsc -p packages/contracts/tsconfig.json
./apps/api/node_modules/.bin/tsc -p apps/api/tsconfig.json --noEmit
./apps/worker/node_modules/.bin/tsc -p apps/worker/tsconfig.json --noEmit
(cd apps/extension && ./node_modules/.bin/wxt prepare && ./node_modules/.bin/tsc -p tsconfig.json --noEmit && ./node_modules/.bin/wxt build)
(cd apps/web && ./node_modules/.bin/vite build)
```
@@ -0,0 +1,27 @@
# M2.4:1688、淘宝、天猫解析器与采集数据测试页
> 状态:已完成第一版页面状态解析与结果检查面(2026-09-06)。
## 真实 SKU 证据规则
| 来源 | 创建多 SKU 的唯一证据 | 禁止行为 |
|---|---|---|
| 1688 | `skuMapOriginal` 的实际组合行;优先使用行内 `skuId`,否则以来源组合键为稳定 key。 | 用 `skuProps` 的规格值排列组合制造 SKU。 |
| 淘宝 / 天猫 | `skuBase.skus[]` 的 `skuId + propPath`;`propPath` 映射回 `skuBase.props`。 | 只有 `props` 时把颜色、尺码等规格轴展开成 SKU。 |
`skuCore.sku2info[skuId]` 只补充已验证 SKU 的参考价格,不能单独证明一个 SKU 存在。来源未给出组合表时,服务端保留规格轴、创建默认 SKU,并让该记录进入 `needs_review`。
## 测试页
扩展的“编辑上架”在采集成功后打开 `/collection-test/{collectionItemId}`,而不是进入尚未完成的商品编辑模块。页面只读显示:
- 采集来源、解析器版本、状态、SKU 数和素材任务数;
- 实际创建的 SKU 草稿及其来源 key;
- 每张候选素材的转存状态和失败原因;
- 持久化的 `normalized_payload` 与 `raw_evidence` JSON。
这让测试者能直接比对页面上的规格组合和服务端保存数据。它不是商品编辑页,也不会把来源价格、属性或媒体关系当作已确认发布数据。
## 已知边界
页面状态结构会随来源站灰度变动;当前解析器只读取已在页面脚本中可见的 JSON 对象。若页面数据仅存在于运行时内存或经签名接口延迟获取,本版会保留默认 SKU/规格轴而不猜测组合。后续需要使用允许的快照样本补充 MAIN world bridge 和针对站点版本的适配器测试。
@@ -49,7 +49,7 @@
| 操作 | 流程 | 成功结果 |
|---|---|---|
| 编辑上架 | 扫描当前页 → 上报采集记录 → 创建/复用未绑定目标的商品基础草稿 → 打开 ERP 编辑入口 | 新标签打开 `/collection-items/{collectionItemId}/edit`;ERP 按页头当前平台取得发布配置并预选门店后进入 `/products/{productId}/editor`,素材继续后台转存。 |
| 编辑上架 | 扫描当前页 → 上报采集记录 → 创建/复用未绑定目标的商品基础草稿 → 打开 ERP 编辑入口 | 新标签打开当前已有的 `/products/{productId}` 入口;后续编辑模块在此入口按页头当前平台取得发布配置并预选门店,素材继续后台转存。 |
| 采集商品 | 扫描当前页 → 上报采集记录 | 停留来源页,提示“已进入采集箱”;用户以后从采集箱编辑。 |
| 进入 ERP | 不扫描、不上报 | 新标签打开 Atlas ERP 首页。 |