# Atlas Seller Kit 交接说明 > 更新:2026-09-04。此文是新 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 已具备完整前端框架(布局、路由、主题)、登录/注册页、页头平台选择器与店铺管理页(已接入真实 API),其余业务页面为占位;扩展为可运行骨架。不要从旧项目复制 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)。 ### 1.1 开发进度(2026-09-02) 今日完成三项工作,全部通过 typecheck、生产构建,并经浏览器或启动实测: **Web 前端框架(`apps/web`)** - 参照 `ozon-seller-kit/studio` 搭建:紫色主题(`colorPrimary #8b5cf6`、圆角 8)、`index.css` CSS 变量、可折叠侧边栏 + 吸顶页头 + 移动端 Drawer 的 `MainLayout`、`@` 路径别名、`StrictMode` 入口。 - 主题显式 `fontSize: 14`;输入类控件默认高度 32→34(`controlHeight`)、小尺寸 24→26(`controlHeightSM`);body 字号同步 14px。 - 依赖使用最新稳定版:React 19.2.8、antd 6.6.2,新增 `@ant-design/icons` 6.3.4;沿用仓库精确版本号约定。 - 左侧菜单按 [`requirements/account-store-watermark.md`](../requirements/account-store-watermark.md) 的导航约定:在线商品(默认首页)、采集箱、店铺管理、水印管理、定价工具,均为占位页;另有菜单外的商品 AI 编辑页 `/products/:id`。 - 基础设施:`services/api.ts`(axios 封装 + 统一错误提取)、`config/env.ts`(`VITE_` 变量)、统一占位组件 `components/PagePlaceholder`。 - Web tsconfig 覆盖 base 的 `module: Node16` 为 `ESNext + bundler`(base 面向 NestJS;Node16 会要求相对导入带扩展名);TypeScript 7 已移除 `baseUrl`,`paths` 直接相对 tsconfig 解析为 `"@/*": ["./src/*"]`。 **注册与登录页面(契约:[`api/auth.md`](../api/auth.md))** - `/login`、`/register` 两页共用 `AuthLayout`(品牌视觉与主布局一致)。注册字段:显示名称(选填 ≤80)、邮箱、密码、确认密码;登录失败统一 401 文案,注册重复邮箱 409 落到邮箱字段提示。 - 会话基础:access token 仅内存持有(`services/authToken.ts`),不进入任何持久化存储;`api.ts` 拦截器自动附加 Bearer 头,401 时用 refresh cookie 换新 token 并重放一次(并发去重),刷新失败整页跳转 `/login`。 - 实测发现并修复:antd v6 中空值会同时触发 `required` 与 `type: 'email'` 两条错误,已改为非空才校验格式的 `emailFormatRule`(`utils/validators.ts`)。 - 浏览器实测通过:表单校验、页面互链、API 未启动时的错误提示路径。密码复杂度按契约不在前端硬编码,仅提示以服务端校验为准。 - 留待 API 认证模块就绪后:接入真实 `/api/auth/*` 调用、主布局路由守卫、刷新页面后的会话恢复;auth 类型届时迁移至 `@atlas/contracts`。 **Worker 与扩展骨架修复** - Worker:`ioredis` 是 BullMQ 的可选 peer 依赖,pnpm 严格隔离下必须由应用显式声明,已新增 `ioredis 6.0.0`;`main.ts` 改为显式构造 IORedis 实例并设置 `maxRetriesPerRequest: null`(原生 ESM 运行必需,也是后续 BullMQ Worker 的硬性要求)。`tsx` dev 与 `node dist/main.js` 两种运行方式均验证可连接本地 Redis。 - 扩展:WXT dev server 从默认的 3000–3010 漂移改为固定 `8110`(`EXTENSION_PORT` 可覆盖,`strictPort` 开启);`.output/` 构建产物已加入 `.gitignore` 并移出 git 跟踪。端口约定同步更新至交接、部署与项目结构三份文档及 `.env.example`。 ### 1.2 开发进度(2026-09-03 下午):店铺管理与平台上下文前端 **Web 接入平台上下文与门店管理 API(契约:[`api/stores-and-platforms.md`](../api/stores-and-platforms.md))** - 页头新增平台选择器:`services/platform.tsx` 的 `PlatformProvider` 加载 `GET /api/platforms` 目录与 `GET /api/platform-context`(`X-Atlas-Platform-Id` header),默认取目录第一家(Ozon 优先);选择结果仅在 localStorage 保存平台 id(非敏感 UI 偏好)。页头组件 `layouts/PlatformSelector.tsx`,tooltip 展示当前平台默认店铺。 - 店铺管理页 `pages/shops/ShopsPage.tsx` 覆盖已实现的门店接口:游标分页列表(“加载更多”)、创建(202 + operation,提示“验证中”)、编辑(`If-Match` 乐观锁,409 时刷新列表并提示重新编辑)、设为默认、手动重新验证凭据、仓库同步(凭据未验证时展示 422 脱敏错误)、软删除(`DEFAULT_STORE_REPLACEMENT_REQUIRED` 转为可操作提示)。API Key 只存在于提交瞬间的内存表单,不写入任何存储。凭据验证与仓库同步的实际执行仍依赖未实现的 Outbox Dispatcher + Worker,店铺会停留在“验证中”。 - 基础设施:`services/api.ts` 新增 `getList`(保留 `{ data, meta }` 分页信封)与 `apiErrorCode`(业务错误码提取);`services/stores.ts` 封装门店接口并按契约生成 `Idempotency-Key`。 - `@atlas/contracts` 补充只读响应类型 `Platform`、`PlatformContext`、`CursorPaginationMeta`、`StoreOperationResponse`(前端 AI 按冻结契约补类型,无行为变更,后端请复核)。 - 修复 API 阻断性 bug(跨领域改动,原因:阻断本功能联调;影响 `apps/api/src/stores/stores.controller.ts`):tsx/esbuild 不生成 `design:paramtypes` 装饰器元数据,控制器裸类型构造参数在 dev 运行时注入 `undefined`,`GET /api/platforms` 返回 500。已改为与 `auth.controller.ts` 一致的显式 `@Inject(...)`;服务层原本就使用显式 token,不受影响。 - 浏览器实测(全新迁移的本地 PostgreSQL 18 + Redis 7):注册 → 平台选择器 → 创建/编辑/设默认/删除保护/同步 422 错误路径 → 刷新后会话恢复与列表持久化,全部通过;contracts/api/web typecheck 与 web production build 通过。 ### 1.3 开发进度(2026-09-03 晚):店铺表单与界面收口 - 店铺创建与编辑表单补齐空白字符拦截;API Key 输入上限与冻结契约的 8192 字符保持一致,不在前端自行收紧。 - 设置页补充取消操作;全局主题目前使用 `borderRadius: 10`、默认控件高度 34,小尺寸控件高度 26,输入类控件单独使用 16px 字号与 40px 高度。按钮保留 Ant Design 默认字号、高度与内边距。 - 页头面包屑改用 12px `RightOutlined` 分隔符,并完成条目、分隔符与侧栏折叠按钮的垂直对齐;页头水平内边距调整为 `0 20px 0 10px`。店铺与设置页的视觉调整不改变接口或领域行为。 - Ozon 平台现支持 `CNY` 与 `RUB`;新建 Ozon 店铺时默认预选 `CNY`。默认币种由平台能力 `default_currency_code` 提供,服务端仍以平台支持币种表校验提交值。 ### 1.4 开发进度(2026-09-04):凭据验证 Worker - Worker 轮询 Transactional Outbox,按工作空间隔离认领事件并投递 BullMQ;任务只携带 store、operation 与 secret ID,绝不携带 API Key。 - `store_credential_verification` 任务仅在 Worker 内读取当前未撤销密钥、短暂解密并调用 Ozon `POST /v1/description-category/tree`(`{ "language": "ZH_HANS" }`)进行低风险验证。该请求已用真实店铺验证可用;仓库接口留待仓库同步模块按其实际请求契约接入。401 表示认证失败;403 单独提示账户/权限不匹配;其他确定性 4xx 记录脱敏 HTTP 状态并标为验证请求错误,不误报为凭据无效。临时网络、限流和 Ozon 服务端错误最多指数退避重试 5 次。 - 验证成功回写 `active / valid`;密钥被替换或门店删除时,旧任务取消且不得覆盖新状态。前端在验证中只显示一个标签,避免与“未验证”同时出现。 ### 1.5 开发进度(2026-09-04):水印模板 API - 已实现字体目录、按工作空间隔离的水印模板列表/详情/创建/编辑/软删除,以及店铺推荐水印绑定与解绑。文字/图片模板均创建不可变渲染版本;仅改名不新增版本,渲染参数或类型变化会新增版本。 - 删除模板会解除所有当前店铺绑定,但不删除历史版本或图片资产。模板与店铺写操作均要求 `Idempotency-Key`;编辑要求 `If-Match`。 - 图片模板只可引用当前工作空间内、已完成安全校验的 `media_asset`;素材直传、图片校验和短时预览 URL 的实现见 1.6,前端仍不能自行拼接对象存储地址。 ### 1.6 开发进度(2026-09-04):水印图片素材直传 - 已实现仅供 `watermark_image` 使用的两阶段素材接口:创建 10 分钟有效的 MinIO/S3 预签名 PUT 上传意图、完成上传后验证对象存在、大小、SHA-256、PNG/JPEG/WebP 魔数与 1–8192 px 尺寸,并创建或复用工作空间内的 `media_asset`。 - 已实现素材短时预览 URL(10 分钟);普通 API 返回永不包含 bucket、object key 或长期 URL。图片模板只接受当前工作空间内、类型为 PNG/JPEG/WebP 的已验证素材。 - 本地环境默认使用 `MINIO_ROOT_USER` / `MINIO_ROOT_PASSWORD` 连接 `http://localhost:9000`;生产必须显式配置最小权限的 `OBJECT_STORAGE_ACCESS_KEY` / `OBJECT_STORAGE_SECRET_KEY`。 ### 1.7 开发进度(2026-09-04):上传意图清理 Worker - Worker 启动时立即执行、之后每 5 分钟扫描一次所有工作空间:将超过有效期仍为 `pending` 的上传意图置为 `expired`。 - 对 `failed` / `expired` 意图,Worker 删除其 MinIO/S3 临时对象;对象不存在也视为已清理。仅对象存储删除成功才写入 `object_cleaned_at`,删除暂时失败会保留记录并在下次扫描重试。 - 新增迁移 `007_media_upload_cleanup.sql`,该字段与清理索引必须在部署 Worker 前执行。 ## 2. 已确认产品决策 | 主题 | 决策 | |---|---| | 商品模型 | 每个商品是一个 SPU,且始终至少有一条 SKU;单规格商品使用默认 SKU,多规格商品使用多条 SKU。 | | 平台可售单元 | 一个 SKU 对应一个独立平台 offer;Ozon 首个平台中对应 `/v3/product/import.items[]` 的一项和独立 `offer_id`。 | | 通用属性 | 标题、品牌、型号、主题标签、简介、条形码、采买地址、货源备注及未来商品级属性;类目和平台属性属于独立的平台发布配置。首期不做富内容。 | | SKU 属性 | SKU 名称、图片、货号、采购价、售价、划线价、包装重量和尺寸、商品重量和尺寸、商品颜色、颜色名称等。首期不支持视频。 | | 型号/货号 | 型号(`model`)是父商品通用字段;`offer_id` 是当前平台下的每个 SKU 字段。采集时两者可为空;首次上架前必须由用户填写,系统不从标题、规格或来源 SKU 自动生成。被成功导入的 `offer_id` 作为历史事实锁定。 | | 规格 | 实物规格与包装物流规格严格分离;尺寸图只读取实物规格,计价/物流/发布只读取包装规格。 | | 继承 | 可继承字段的 `null` 表示该字段没有覆盖父商品默认值,不表示 SKU 不存在、名称为空或商品为单规格。 | | 尺寸图 | 必须逐 SKU 生成,任务保存 SKU ID 和实物规格快照。 | | 定价 | SKU 上点“定价”打开弹窗,以有效包装数据计算;用户确认后回填 SKU 销售价和划线价。 | | 编辑体验 | 新商品 AI 编辑页取代旧商品编辑页;旧试算页不再追加多 SKU 功能。 | | 采集插件 | 采集 Ozon、1688、淘宝、天猫详情页;未登录只显示登录入口,已登录显示“编辑上架 / 采集商品 / 进入 ERP”。采集箱为工作空间共用,采集记录不关联目标平台或门店;用户进入 ERP 编辑时,按页头当前平台(首期默认 Ozon)取得该平台发布配置并预选默认店铺。A/B 平台的上架状态独立。详情见 [`requirements/collector-browser-extension.md`](../requirements/collector-browser-extension.md)。 | | 采集 SKU | 只有来源给出稳定 SKU/组合证据时创建多个 SKU;不能把规格轴做笛卡尔积。没有专属 SKU 图时,真实 SKU 继承父商品默认图而不复制资产。 | | 媒体 | 采集主图/详情图是商品候选池;来源明确给出 SKU、或可靠的规格轴值(如颜色)与图片关系时才自动绑定。其余素材由编辑页人工选择。 | | 发布与在线商品 | 商品采集、编辑、定价、AI 媒体和首次导入至 Ozon 后台是核心闭环。正式发布使用官方 Seller API,按一 SKU 一 import item/offer 执行并轮询结果;首期不传库存或仓库,用户在 Ozon 后台设置库存。已导入 SKU 不可再次上架,未上架 SKU 仍可首次上架;SPU 在当前平台汇总为未上架/已部分上架/已上架。在线商品由异步 Ozon API 同步,按 Ozon 商品 SKU 展示,亦包含非 Atlas 发布商品。 | ## 3. Ozon 已核对结论 仓内旧项目的 `docs/ozon-seller-api/04-product-import.md` 记录:Ozon `/v3/product/import` 每个 item 都有独立 `offer_id`、价格、包装尺寸/重量和图片,尺寸/重量不能为零;单次最多 100 项。`03-category-attributes.md` 记录类目属性的 `is_aspect=true` 表示变体属性。 因此 Ozon 适配器应以“一 SKU 一 import item/offer”为最小稳定单位。现有资料没有可强制把多个 item 合并为一个 Ozon 前台变体组的 API 契约,不能依赖自动归并;这是未来 Ozon 联调阶段要在真实店铺或沙盒验证的能力。 ## 4. 云端架构底线 1. 使用 PostgreSQL;所有业务行必须受 `workspace_id` 或可安全追溯至工作空间的外键约束。 2. 首期注册后静默创建一个个人工作空间,并只建立一个 `owner` 成员;不展示工作空间、成员、角色或店铺级权限管理。后续有团队协作需求时再扩展。 3. 门店只能绑定一个平台,不能跨平台使用;市场/国家、默认语言、支持语言和支持币种属于平台能力配置,不属于门店。门店保留已确认的默认计价币种与加密凭据;上述信息均不得写进商品核心表。 4. 本地素材使用 MinIO;共享测试和生产素材使用七牛 Kodo,不使用应用服务器本地目录作为持久存储。 5. AI、生图、导出、采集转存和发布使用 Redis + BullMQ 队列/Worker,任务可重试、可恢复且携带工作空间与 SKU 上下文。关键可重试写操作使用持久化幂等记录;异步任务与业务事实在同一 PostgreSQL 事务写入 Transactional Outbox,再由 Dispatcher 投递队列。Redis/BullMQ 不作为业务事实唯一来源。 6. 用户界面语言、采集源语言、商品编辑语言和发布目标语言相互独立;文案及属性值需要支持多语言版本。 7. 一期实现插件中显式开启的 Ozon Seller 网页本地观察:在用户浏览器的已登录 Seller 标签页调用四个已记录内部接口,并仅上传净化结果;不得提取完整、保存或上传 Cookie,不得阻塞标准采集、不得替代官方 Seller API 发布或把未验证经营数据视为本店事实。加密保存 Cookie 仅作为未来二级实验能力预留。 建议的核心层级: ```text User → Workspace → Membership / Role → Store → Platform connection / default currency → Product (SPU) → ProductVariant (SKU) → attributes / media / pricing snapshots / tasks ``` ## 5. 已冻结的第一阶段 API 契约 第一阶段 HTTP API 的接口范围已讨论完成,当前状态为**设计冻结、部分业务接口已实现**。认证、平台目录/上下文、创建/查询/编辑/默认/删除门店、异步操作读取及仓库摘要读取已有实现与集成测试;门店凭据验证、仓库同步的实际平台调用仍待 Worker 消费 Outbox 后完成。后续编码以 [`docs/api/`](../api/README.md) 为唯一 HTTP 契约来源;旧 [`product/api.md`](../product/api.md) 仅作历史背景,不能作为实现依据。 - 覆盖账户认证、平台/门店、水印与素材、定价、浏览器采集/采集箱、商品编辑/首次发布、在线商品读取与同步。 - 所有接口遵循统一鉴权、工作空间隔离、页头平台上下文、金额字符串、错误格式、`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 写入闭环以及上述门店 HTTP 模块已实现。Outbox Dispatcher 与平台 Worker 仍未实现。 仍需在对应模块开始编码前完成的 P1/P2 决策和数据模型补丁,集中记录在 [`reviews/implementation-readiness-open-items.md`](../reviews/implementation-readiness-open-items.md)。 ## 6. 下一步建议 当前优先完成门店纵向闭环,而不是继续扩展商品接口: 1. 验证并补齐 Outbox Dispatcher 与凭据验证 Worker 的集成测试:覆盖成功、401/403、限流/超时重试、旧密钥任务取消和无密钥日志。 2. 在凭据验证通过后实现仓库同步 Worker、幂等 upsert 和状态回写,并补充失败重试、死信与集成测试。 3. ~~Web 接入真实认证和门店管理 API~~ 已基本完成:登录/注册、会话刷新、平台上下文、门店列表/创建/编辑/默认切换/删除均已上线(见 1.2);剩余水印绑定入口待素材/水印模块交付后接入。前端不得保存 API Key 或 refresh token。 4. ~~接着实现素材直传和水印管理~~ 已完成后端上传、校验、预览与临时对象清理;接着实现水印实际渲染、采集箱与商品编辑。浏览器扩展仅在采集 API 稳定后实现实际解析器。 5. 在开始商品编辑/在线同步模块前,关闭评审文档中的 P1 事项:同商品归属约束、未映射属性持久化、乐观锁 Repository 规则、平台能力 Schema;并确认在线商品归档策略和 Client ID 编辑边界。 账户资料的已确认补充:`users.avatar_asset_id` 只保存素材引用,绝不保存公开 URL;`users.revision` 用于 `PATCH /api/auth/me` 的 `If-Match`。资料更新已实现幂等与版本冲突检测;修改密码已实现旧密码校验、按 IP/用户限流、撤销旧会话并轮换新会话。头像上传必须复用素材模块的预签名直传与校验流程,不引入 base64 直传;素材模块尚未实现时头像只能为 `null`。 ## 7. 文档导航 - [文档总索引](../README.md) - [第一阶段 HTTP API 索引](../api/README.md) - [API 统一约定](../api/conventions.md) - [基础模块数据模型与 DDL](../architecture/foundation-data-model.md) - [商品领域数据模型与 DDL](../architecture/product-domain-data-model.md) - [幂等与 Transactional Outbox](../architecture/transactional-outbox-and-idempotency.md) - [数据库角色与 RLS](../architecture/database-security.md) - [实现前待决与待落地事项](../reviews/implementation-readiness-open-items.md) - [浏览器采集插件需求与技术方案](../requirements/collector-browser-extension.md) - [技术栈、异步任务与部署方案](../architecture/technology-and-deployment.md)