docs: 更新文档

This commit is contained in:
R524809
2026-09-20 15:23:44 +08:00
parent 880e441bc6
commit e0e07be4ea
19 changed files with 164 additions and 312 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
# Atlas Seller Kit 文档
> 先确认产品需求,再进入数据库、接口和开发设计。本目录不代表已实施功能。
> 本目录同时包含需求、架构、API 契约、实施记录和外部平台资料。是否已经实现以各文档顶部状态及 [`project/HANDOFF.md`](./project/HANDOFF.md) 的最新进度为准,不能把所有章节都当成未来方案或已上线能力。
## 目录
+9 -2
View File
@@ -1,6 +1,6 @@
# 平台类目与属性管理 API
> 2026-09-18 新增契约,2026-09-20 增加平台刷新策略和类型属性批次。平台级目录供系统管理员维护;普通商品编辑器现有门店级 API 暂不改变。平台参考目录的归属见 [`platform-product-reference-catalog.md`](../architecture/platform-product-reference-catalog.md),新鲜度与优先级规则见 [`platform-catalog-cache-refresh-plan.md`](../architecture/platform-catalog-cache-refresh-plan.md)。
> 2026-09-18 新增契约,2026-09-20 增加平台刷新策略和类型属性批次。平台级目录供系统管理员维护,普通商品编辑器和首次发布预检也已切换到同一平台缓存;旧门店目录仅保留兼容/回滚。平台参考目录的归属见 [`platform-product-reference-catalog.md`](../architecture/platform-product-reference-catalog.md),新鲜度与优先级规则见 [`platform-catalog-cache-refresh-plan.md`](../architecture/platform-catalog-cache-refresh-plan.md)。
## 权限与安全
@@ -119,4 +119,11 @@ Headers:`Idempotency-Key`(UUID)必填。Body:
返回该平台最近的同步任务(最多 50 条),包括作用域、触发方式、状态、开始/完成时间、数量摘要及脱敏错误。系统管理员可据此查看手动和定时更新结果。
类目树调度固定为最近一次成功后 7 天。类型属性和属性字典没有周期性自动扫描;它们只由管理员操作、后续普通用户访问和发布前校验创建任务。
类目树调度固定为最近一次成功后 7 天。类型属性和属性字典没有周期性自动扫描;它们只由管理员操作、普通用户访问和发布预检创建任务。
## 管理页面交互
- 类目搜索框固定在树卡片顶部;搜索结果会展开到命中节点,普通目录节点可展开,只有活动的可发布类型可以选中查看属性。
- “缓存新鲜度设置”以操作区按钮打开弹窗,不在主页面常驻表单。保存成功后关闭弹窗并刷新策略、类目和当前属性状态;失败时保留弹窗及用户输入。
- “最近同步任务”的手动刷新按钮在请求期间显示 loading;成功显示 message,失败显示统一 API 错误。页面每 5 秒的自动轮询保持静默,不重复打扰管理员。
- 类型属性提供 missing+expired、missing+stale+expired 和全部三种批次按钮;全部同步使用红色危险按钮并二次确认。字典仍在选定属性后逐个手动同步。
@@ -210,6 +210,8 @@ Body:`{ "variant_ids": ["uuid", "uuid"] }`,最多 100 个且不能重复。
`refreshing` 时客户端使用相同 SKU 集合重新预检;不得退回过期缓存。`blocked` 表示目录资源可用但商品草稿仍有业务校验问题;`ready` 才能进入未来的发布快照创建事务。Ozon 最终仍可能拒绝 payload,该错误属于具体商品/门店的发布结果,不能据此创建门店级目录副本。
当前预检只对 profile 引用的类目执行本地活动状态校验,并对该类目的 schema 与实际使用字典执行新鲜度刷新;它尚未判断整棵类目树缓存是否过期,也不会因树过期先创建树同步任务。若本地类目不存在、已停用或不属于当前平台,API 返回 Atlas 自己的 `422 CATEGORY_INVALID`,这不是 Ozon 接口响应。后续真正发布 job 接入前,需要明确补齐树级新鲜度保护或确认“每周同步 + 本地活动状态”足以作为发布边界。
## 4. SKU 定价确认
`POST /api/product-platform-profiles/{profile_id}/variants/{variant_id}/pricing-confirms`
@@ -164,6 +164,8 @@ expired/missing ─────→ 高优先级刷新 → 短暂等待
- 发布使用到的字典值必须存在于该平台、类型和属性作用域的有效缓存。相关字典 expired/missing 时先刷新对应字典或目标查询作用域,不刷新该类型下所有字典。
- Ozon 最终仍可能返回平台校验错误;这类错误按商品/门店记录,必要时将对应平台参考资源标记为需要重新检查,但不能自动生成门店差异层。
当前实现的 `publish-preflights` 已覆盖类型属性和实际使用字典的新鲜度,但类目树仍只依赖固定每周同步和本地节点 `is_active`。预检尚未读取树级新鲜度或因树过期创建同步任务;本地类目不存在、已停用或跨平台时返回 Atlas 的 `422 CATEGORY_INVALID`。真正的 Ozon 发布 job 接入前,应明确补齐树级新鲜度保护,或以验证结果正式确认“每周全量同步 + 本地活动状态”就是接受的发布边界。
## 8. 系统后台调整
「平台类目与属性」页面保留现有平台选择、同步来源、类目树、属性和字典浏览能力,并调整为:
@@ -190,6 +192,8 @@ expired/missing ─────→ 高优先级刷新 → 短暂等待
设置保存后立即用于后续读取判断,但不主动扫描并创建数千任务。例如把过期天数从 30 改为 21 后,只有下次用户读取、发布校验或管理员批次触达该资源时才创建任务。修改必须记录操作者、修改时间和前后值。
管理页不直接常驻展示这组表单。操作区使用“缓存新鲜度设置”按钮打开弹窗;保存成功后关闭弹窗并刷新策略、类目及当前属性状态,失败时保留弹窗和用户输入。
### 8.3 状态展示
管理页至少展示:
@@ -200,9 +204,11 @@ expired/missing ─────→ 高优先级刷新 → 短暂等待
- 字典的相同新鲜度状态、完整/部分状态、候选值数量和最近错误;
- 高低优先级、触发来源(weekly/manual/admin_batch/user_access/publish)和任务复用关系。
## 9. 建议的数据与契约调整
“最近同步任务”的手动刷新按钮在请求期间展示 loading;成功显示 message,失败展示统一 API 错误。每 5 秒自动轮询保持静默,不能反复弹出提示或使按钮周期性闪烁。
确认后至少需要补充以下模型,具体 DDL 和字段名在实现阶段冻结:
## 9. 已实施的数据与契约调整
迁移 `025`~`027` 与相应 contracts/API 已落地以下模型:
1. 平台同步策略:保存属性、字典的新鲜和过期天数以及审计字段。
2. 资源同步状态:按 tree、类型属性、属性字典和字典搜索作用域保存 `last_success_at`、`is_complete`、最新任务及错误;不能仅依赖数据行的 `updated_at` 推断是否成功检查过。
@@ -210,7 +216,7 @@ expired/missing ─────→ 高优先级刷新 → 短暂等待
4. 任务优先级与触发来源:区分 high/low 及 weekly、manual、admin_batch、user_access、publish。
5. 商品编辑引用:从门店类目 UUID 迁移为平台类目 UUID;属性和值仍按当前商品/SKU 草稿保存,但校验引用平台 schema。
普通产品读取响应建议统一带:
普通产品读取响应现统一带:
```json
{
@@ -228,18 +234,18 @@ expired/missing ─────→ 高优先级刷新 → 短暂等待
}
```
管理 API 需要增加同步策略读取/修改和类型属性批次创建/读取;现有单项 `POST .../syncs` 保留给树和字典。最终端点、状态码和错误码必须在实现前更新 [`platform-catalog-admin.md`](../api/platform-catalog-admin.md) 与 [`product-editor-and-publishing.md`](../api/product-editor-and-publishing.md)。
管理 API 已提供同步策略读取/修改和类型属性批次创建/读取;现有单项 `POST .../syncs` 保留给树、兼容单类型属性和字典。最终端点、状态码和错误码见 [`platform-catalog-admin.md`](../api/platform-catalog-admin.md) 与 [`product-editor-and-publishing.md`](../api/product-editor-and-publishing.md)。
## 10. 实施顺序与验收
建议分四步实施,避免继续扩大门店级缓存:
方案按以下四步实施,均已完成:
1. **平台状态与调度**:增加策略、资源状态、批次和优先级;把树改为每周自动同步,关闭属性/字典周期自动扫描。
2. **后台能力**:加入新鲜度设置、三种范围的类型属性批次入口及统一批次进度,保留树及单字典手动同步。
3. **商品编辑迁移**:通过 Ozon 外部 ID 回填平台类目引用,切换属性和字典读取、按需刷新与任务等待;保留兼容回滚路径。
4. **发布迁移**:发布前使用平台 schema/字典状态执行定向高优先级刷新和最终校验,验证稳定后再讨论清理门店级目录表。已通过 `POST .../publish-preflights` 落地;未来 `publish-jobs` 必须先调用相同服务,不能自行绕过。
当前完成度:第 1~4 步已完成。第 3 步由 `027_product_editor_platform_catalog.sql` 回填平台类目引用并保留旧门店列;编辑器返回统一 cache 元数据,stale 创建低优先级任务并立即返回,expired/missing 创建高优先级任务、最多等待 5 秒后返回 `202`;部分字典的未命中关键词使用独立 `dictionary_search` 任务。第 4 步新增首次发布预检:只接受所选未导入 SKU,先刷新当前 schema,再从已确认草稿识别实际使用的字典;所有 high 任务共用 5 秒窗口,stale 继续 low 后台刷新,最终校验同时检查字典缓存有效期和候选值仍有效。
当前完成度:第 1~4 步已完成。第 3 步由 `027_product_editor_platform_catalog.sql` 回填平台类目引用并保留旧门店列;编辑器返回统一 cache 元数据,stale 创建低优先级任务并立即返回,expired/missing 创建高优先级任务、最多等待 5 秒后返回 `202`;部分字典的未命中关键词使用独立 `dictionary_search` 任务。第 4 步新增首次发布预检:只接受所选未导入 SKU,先刷新当前 schema,再从已确认草稿识别实际使用的字典;所有 high 任务共用 5 秒窗口,stale 继续 low 后台刷新,最终校验同时检查字典缓存有效期和候选值仍有效。真正的 Ozon 发布 job 尚未实现,且发布预检的树级新鲜度边界仍需在接入前确认或补齐。
验收至少覆盖:
@@ -25,9 +25,9 @@ Atlas 本阶段明确采用**平台级参考数据**:同一 `platform_id` 下
统一的后台同步服务按 `platform_id` 负责定时任务、首次缺失时的补齐和管理员手动请求。三种触发方式都创建有权限校验、幂等去重的 `platform_catalog_sync_runs + platform_catalog_outbox_events`;这是不带 `workspace_id` 的全局任务事实,不能借用租户 `operations/outbox_events`。Worker 通过平台适配器读取服务端安全存储的参考源凭据并调用对应平台。浏览器、日志、run 和 outbox 载荷不包含 API Key。管理员手动同步不得在 HTTP 请求内直接访问平台或绕过同一套限流、审计与事务发布流程。
1. **同步来源**:每个平台分别配置 Atlas 专用的参考源凭据及适配器;Ozon 首期使用专用参考门店凭据作为技术采集身份,而非普通客户工作空间门店。设置最小授权、密钥轮换、失效告警与备用源,不能未经授权借用客户门店密钥。若暂无专用凭据,保持现有门店级链路,待来源配置完成后迁移。
1. **同步来源**:每个平台分别配置 Atlas 专用的参考源凭据及适配器;Ozon 首期使用专用参考门店凭据作为技术采集身份,而非普通客户工作空间门店。设置最小授权、密钥轮换、失效告警与备用源,不能未经授权借用客户门店密钥。若暂无专用凭据,平台同步任务保持不可执行,读路径按 stale/expired/missing 规则返回;不得回退到客户门店级目录或临时借用某个用户的密钥。
2. **基线调度(当前代码事实)**:类目树最近成功时间达到 7 天后由调度器创建低优先级周任务;管理员仍可手动创建高优先级树任务。类型属性和字典已经退出周期自动候选,类型属性只从管理员显式创建的三种批次中逐批领取,字典只保留单项手动入口。普通商品读路径已按 14/21 天执行 fresh/stale/expired/missing 状态机,并创建或复用 user_access 平台任务。
3. **管理员页面**:按平台展示参考源状态、树、属性、字典候选值、最后成功时间、失败详情、排队任务和「同步类目树 / 指定类目属性 / 指定字典」操作。手动动作必须受系统管理员权限控制、记录操作者,并返回异步任务;页面不展示密钥明文。当前实现位于 `/system/platform-catalog`,全局系统管理员授权独立于工作空间角色。
3. **管理员页面**:按平台展示参考源状态、树、属性、字典候选值、最后成功时间、失败详情和排队任务;提供类目树手动同步、三种范围的类型属性批次和单字典同步。缓存新鲜度设置由操作按钮打开弹窗,最近任务手动刷新有 loading 与成功/失败提示,5 秒自动轮询保持静默。所有手动动作受系统管理员权限控制、记录操作者并返回异步任务;页面不展示密钥明文。当前实现位于 `/system/platform-catalog`,全局系统管理员授权独立于工作空间角色。
4. **读路径**:正常用户只读 Atlas 缓存。首次缺失时可排队补齐并显示准备中;已有数据过期时优先提供上次成功快照并后台更新。不能在每次打开编辑器或下拉框时同步请求 Ozon。大字典搜索按规范化关键词及上述字典作用域共享缓存,并去重并发补齐。
5. **原子性与监控**:P0 按平台及树、类型属性、字典作用域在单事务内 upsert,提交前读取方继续看到旧数据;失败保留上次成功缓存。仅在确认完整分页完成后才处理消失的字典值,部分同步记录 `is_complete=false`。当前尚未实现不可变版本快照、内容哈希、`next_refresh_at`、连续失败告警和异常删减保护,这些属于上线前可靠性加固,不能在文档中误记为已完成。
@@ -43,4 +43,4 @@ Ozon 字典分页每次最多 2000 个值;[搜索接口](https://docs.ozon.ru/
`022_store_catalog_cache.sql` 的门店级表和 profile 的 `store_product_category_id` 仍保留为兼容/回滚数据,但商品编辑器不再读取它们。当前事实来源是平台级类目、属性和 `platform_catalog_dictionary_values`。
迁移 `024`~`026` 建立平台参考来源、候选值、同步任务、策略和资源状态;`027_product_editor_platform_catalog.sql` 以 Ozon 外部类目/类型 ID 回填商品的 `platform_product_category_id`,并为部分字典增加关键词搜索任务作用域。旧门店列和表暂不删除,新保存会尽量反写可映射的门店类目 UUID,供回滚使用。清理旧表必须在发布链路迁移、稳定性验证和维护者批准后另行执行。Wildberries 等新平台应新增平台适配器及自己的外部 ID 映射,不复用 Ozon 的 `description_category_id + type_id` 语义。
迁移 `024`~`026` 建立平台参考来源、候选值、同步任务、策略和资源状态;`027_product_editor_platform_catalog.sql` 以 Ozon 外部类目/类型 ID 回填商品的 `platform_product_category_id`,并为部分字典增加关键词搜索任务作用域。旧门店列和表暂不删除,新保存会尽量反写可映射的门店类目 UUID,供回滚使用。清理旧表必须等真正的 Ozon 发布 job 复用平台预检、完成稳定性验证并取得维护者批准后另行执行。Wildberries 等新平台应新增平台适配器及自己的外部 ID 映射,不复用 Ozon 的 `description_category_id + type_id` 语义。
@@ -16,7 +16,7 @@
`products` / `product_variants` 只服务采集箱商品,保存来源、采购和跨平台可复用资料。`platform_listings` 则是目标平台、目标门店的实际商品镜像;它不能跨平台或跨门店复用,即使它关联了 Atlas SKU。`product_platform_profiles.market_code` 表示当前平台业务上下文,首期取平台默认 `RU`,不从门店派生。
类目/商品类型、属性定义和字典候选值是待迁移的**平台级**参考数据:同一平台所有门店共用,不跨 Ozon、Wildberries 等平台混用,也不在本阶段维护门店差异层。门店连接与商品/SKU 已填值仍按工作空间隔离。现有 `022_store_catalog_cache.sql` 的门店级缓存和 profile 外键继续有效,目标模型及迁移边界见 [`platform-product-reference-catalog.md`](./platform-product-reference-catalog.md)。
类目/商品类型、属性定义和字典候选值已经迁移为**平台级**参考数据:同一平台所有门店共用,不跨 Ozon、Wildberries 等平台混用,也不在本阶段维护门店差异层。门店连接与商品/SKU 已填值仍按工作空间隔离。迁移 `024`~`027` 已建立平台参考源、目录任务、刷新策略和商品 profile 的平台类目引用;`022_store_catalog_cache.sql` 的门店级缓存及旧 profile 外键只为兼容和回滚保留,不再作为商品编辑、保存校验或发布预检的事实来源。完整边界见 [`platform-product-reference-catalog.md`](./platform-product-reference-catalog.md)。
## 2. 关键关系与状态
@@ -172,6 +172,8 @@ async def fetch_category_tree(
### TypeScript(前端选择器)
> 以下嵌套 `CategoryNode` 和 `/api/categories/tree` 是早期资料中的概念示例,不是当前 Atlas 联调契约。当前商品编辑器从 `GET /api/product-platform-profiles/{profile_id}/category-options` 读取带 `parent_id` 的平台级扁平节点,再在前端重建树;请以[商品编辑与发布 API](../../api/product-editor-and-publishing.md)为准。
```typescript
interface CategoryNode {
description_category_id: number;
@@ -213,7 +215,7 @@ function toTreeData(nodes: CategoryNode[]): any[] {
## 缓存策略
目标方案见 [`platform-product-reference-catalog.md`](../../architecture/platform-product-reference-catalog.md):每个平台分别维护共享目录;定时自动同步与系统管理员手动触发共用异步任务、原子版本切换和失败保留旧版;前端只读 Atlas 缓存。当前运行中的门店级同步仍见下方「Atlas 当前集成」,不能把目标方案当作已经上线。
Atlas 已按 [`platform-product-reference-catalog.md`](../../architecture/platform-product-reference-catalog.md) 为每个平台分别维护共享目录:定时自动同步与系统管理员手动触发共用异步任务,失败保留上次成功版本;商品编辑器只读 Atlas 平台缓存。同平台的门店不再各自复制类目树,本阶段也不维护门店差异层。
---
@@ -225,11 +227,11 @@ function toTreeData(nodes: CategoryNode[]): any[] {
### Q2: 多久更新一次?
**A**: Ozon 接口未规定 Atlas 的缓存 TTL。目标架构建议由后台每日检查树,并提供管理员手动排队同步入口;间隔可按运行量调整,见[架构决策](../../architecture/platform-product-reference-catalog.md)。
**A**: Ozon 接口未规定 Atlas 的缓存 TTL。Atlas 固定在最近一次成功后 7 天自动同步完整树,并提供系统管理员手动排队同步入口;失败不会更新成功时间或清空旧树。
### Q3: 不同语言的类目树结构一样吗?
**A**: 接口支持语言参数,不应未经验证就假定各语言的 ID 和翻译映射完全相同。当前 Worker 为每个门店缓存 `ZH_HANS`;目标是由 Ozon 平台参考源统一缓存该语言,同平台所有门店共用。前端不能携带门店密钥直连 Ozon。
**A**: 接口支持语言参数,不应未经验证就假定各语言的 ID 和翻译映射完全相同。当前 Worker 使用 Ozon 平台参考源统一缓存 `ZH_HANS`,同平台所有门店共用。前端不能携带平台或门店密钥直连 Ozon。
### Q4: `type_id=0` 是什么意思?
@@ -239,16 +241,18 @@ function toTreeData(nodes: CategoryNode[]): any[] {
## Atlas 当前集成
- **Worker**:以门店凭证调用 Ozon,递归持久化完整 `children` 树及父子关系;缓存命中时不重复调用 Ozon。
- **API**:按当前目标门店返回缓存的扁平节点(包含 `parent_id`);不会将 `Client-Id` 或 `Api-Key` 返回给浏览器。
- **Worker**:使用系统后台配置的平台参考源凭据调用 Ozon,递归持久化完整 `children` 树及父子关系;同一平台活动任务按作用域合并。
- **API**:按当前平台返回缓存的扁平节点(包含 `parent_id`);不会将 `Client-Id` 或 `Api-Key` 返回给浏览器。
- **前端**:根据 `parent_id` 重建任意深度的 Cascader;目录可展开,只有可发布的叶子能写入商品草稿。
- **调试**:若需要查看 Ozon 原始响应,应由受权限保护的服务端调试能力获取并脱敏展示;浏览器不得直接用门店密钥请求 Ozon。
- **迁移目标**:平台级共享目录和统一异步管理同步尚未实现;本阶段不维护门店差异层,见[架构决策](../../architecture/platform-product-reference-catalog.md)。
- **同步维护**:完整树每 7 天自动同步,也可由系统管理员手动触发;树任务不自动展开为所有类型属性或字典任务。
- **发布边界**:当前发布预检校验所选本地类目仍活动,但尚未对树级新鲜度创建刷新任务;该边界需在真正发布 job 接入前确认或补齐。
---
## 相关文档
- [03-category-attributes.md](./03-category-attributes.md) —— 获取类目属性
- [docs/v2/database.md](../v2/database.md) §2.7 —— `category_tree` 表结构
- [docs/v2/ozon-publish.md](../v2/ozon-publish.md) §3 —— 类目字典缓存策略
- [平台商品参考目录](../../architecture/platform-product-reference-catalog.md) —— 平台级数据归属
- [平台目录缓存刷新方案](../../architecture/platform-catalog-cache-refresh-plan.md) —— 每周树同步和属性/字典按需刷新
- [商品编辑与发布 API](../../api/product-editor-and-publishing.md) —— 编辑器类目读取和发布预检
@@ -431,9 +431,11 @@ if missing:
## 7. 缓存策略
Ozon 只定义查询参数和分页规则,不规定 Atlas 的 TTL。2026-09-18 的[架构决策](../../architecture/platform-product-reference-catalog.md)将属性定义和字典候选值设为平台级参考数据:同一平台所有门店共用,本阶段不维护门店差异层;此方案尚未实现。当前代码仍以门店、类目和类型为作用域按需同步,用户从 Atlas 本地缓存读取。
Ozon 只定义查询参数和分页规则,不规定 Atlas 的 TTL。Atlas 已按 2026-09-18 的[架构决策](../../architecture/platform-product-reference-catalog.md)将属性定义和字典候选值迁移为平台级参考数据:同一平台所有门店共用,本阶段不维护门店差异层。商品编辑器、草稿校验和发布预检都从 Atlas 平台缓存读取,Ozon 请求统一由 Worker 使用平台参考源凭据执行。
目标同步方式:对活跃 `description_category_id + type_id` 的属性定义定期后台复核;字典候选值至少以类目、类型、属性、字典 ID 和语言限定作用域。`category_dependent=false` 可以作为进一步去重的依据,但在跨账号/跨类目验证前不能只以 `dictionary_id` 合并。小字典可完整分页并标记 `complete`;大字典先保存常用页与搜索命中,并明确标记 `partial`。搜索结果不是全集,不能据此清理未命中的旧值。定时和管理员手动同步必须共用异步任务链路,成功后原子切换版本,失败保留上次成功值。
类型属性和字典默认 14 天内 fresh,14~21 天(含 21 天)stale 时旧缓存可用并低优先级刷新,严格超过 21 天或从未同步时必须高优先级刷新成功后才能作为当前 schema、候选值或发布依据。系统不周期性扫描属性和字典;刷新由管理员批次/单字典操作、普通用户访问或发布预检触发。
字典候选值至少以平台、类目类型、Atlas 属性 UUID、外部值 ID 和语言限定作用域。`category_dependent=false` 可以作为进一步去重的依据,但在跨账号/跨类目验证前不能只以 `dictionary_id` 合并。小字典可完整分页并标记 `complete`;大字典保存已同步页与搜索命中并明确标记 `partial`。搜索结果不是全集,不能据此清理未命中的旧值;partial 字典的规范化关键词有独立缓存状态。成功后原子更新缓存,失败保留上次成功值。
---
@@ -510,106 +512,13 @@ Ozon 只定义查询参数和分页规则,不规定 Atlas 的 TTL。2026-09-18
---
## 10. V2 项目集成
## 10. Atlas 当前集成
### API 层(已实现)
```python
# server/api/categories.py
@router.get("/categories/{category_id}/attributes")
async def get_attributes(
category_id: int,
type_id: int = Query(...),
shop_id: str = Query(...),
db: AsyncSession = Depends(get_db)
):
shop = await get_shop(db, shop_id)
attrs = await ozon_client.get_category_attributes(
shop.client_id_dec,
shop.api_key_dec,
category_id,
type_id
)
return {"result": attrs}
@router.get("/categories/attribute/{attribute_id}/values")
async def get_attribute_values(
attribute_id: int,
category_id: int = Query(...),
type_id: int = Query(...),
q: str = Query(None), # 搜索关键词
shop_id: str = Query(...),
db: AsyncSession = Depends(get_db)
):
shop = await get_shop(db, shop_id)
if q and len(q) >= 2:
# 搜索接口
values = await ozon_client.search_attribute_values(
shop.client_id_dec, shop.api_key_dec,
attribute_id, category_id, type_id, q
)
else:
# 全量拉取(分页)
values = await ozon_client.get_attribute_values(
shop.client_id_dec, shop.api_key_dec,
attribute_id, category_id, type_id
)
return {"result": values}
```
### 前端(待实现)
```tsx
// studio/src/pages/product/components/AttributeMapper.tsx
import { Form, Select, Input, Tag } from 'antd';
export function AttributeMapper({ categoryId, typeId, rawParams, onChange }) {
const [attributes, setAttributes] = useState([]);
useEffect(() => {
fetch(`/api/categories/${categoryId}/attributes?type_id=${typeId}`)
.then(r => r.json())
.then(data => setAttributes(data.result));
}, [categoryId, typeId]);
// 自动匹配
const autoMatch = () => {
const matched = rawParams.map(p => {
const attr = attributes.find(a =>
normalize(a.name) === normalize(p.key)
);
return attr ? { ...p, attrId: attr.id, attr } : p;
});
return matched;
};
return (
<div>
{autoMatch().map((item, i) => (
<Form.Item
key={i}
label={item.key}
required={item.attr?.is_required}
>
{item.attr?.dictionary_id > 0 ? (
<Select
showSearch
placeholder="选择字典值"
onSearch={(q) => fetchValues(item.attr.id, q)}
/>
) : (
<Input defaultValue={item.value} />
)}
</Form.Item>
))}
</div>
);
}
```
- 商品编辑器通过 `GET /api/product-platform-profiles/{profile_id}/attribute-schema` 读取当前平台类型属性,响应包含 Atlas `attribute_id` 和统一 `cache` 元数据。
- 字典通过 `GET /api/product-platform-profiles/{profile_id}/attribute-dictionaries/{attribute_id}` 读取;`attribute_id` 是平台属性 UUID,不以门店或单独 `dictionary_id` 定位。
- fresh/stale 立即返回;expired/missing 创建或复用高优先级任务并最多等待 5 秒,仍未完成时返回 `202`。浏览器不会拿到平台凭据或未落库的 Ozon 临时结果。
- 发布前使用 `POST /api/product-platform-profiles/{profile_id}/publish-preflights`,只刷新当前类型 schema 和所选 SKU 实际使用的字典,再执行必填、scope、字典有效性及其他首次上架校验。
- 管理后台支持三种类型属性批次、单字典同步、缓存策略弹窗和最近任务观察;类目树固定每 7 天同步,属性与字典不做周期性全量扫描。
---
@@ -617,5 +526,5 @@ export function AttributeMapper({ categoryId, typeId, rawParams, onChange }) {
- [02-category-tree.md](./02-category-tree.md) —— 获取类目树
- [04-product-import.md](./04-product-import.md) —— 发布商品(使用属性)
- [docs/v2/database.md](../v2/database.md) §2.7 —— 属性缓存表结构
- [docs/v2/ozon-publish.md](../v2/ozon-publish.md) §3 —— 属性映射策略
- [平台目录缓存刷新方案](../../architecture/platform-catalog-cache-refresh-plan.md) —— 属性和字典缓存状态机
- [商品编辑与发布 API](../../api/product-editor-and-publishing.md) —— 当前 Atlas 端点与发布预检
@@ -479,9 +479,11 @@ POST /v3/product/info/list
---
## 10. V2 项目集成
## 10. 早期集成示例(当前未实现)
### API 层(已实现)
> 下方 `server/api/publish.py` 与前端组件来自早期 V2 方案,不存在于当前 NestJS/React 实现中,仅可帮助理解流程,不能作为联调契约。Atlas 目前只完成 `publish-preflights`;真正的发布 job、`/v3/product/import` 提交和结果轮询尚未实现。当前契约见[商品编辑与发布 API](../../api/product-editor-and-publishing.md)。
### 历史 API 示例
```python
# server/api/publish.py
@@ -613,4 +615,5 @@ export function PublishPanel({ productId }) {
- [03-category-attributes.md](./03-category-attributes.md) —— 获取属性
- [05-product-info.md](./05-product-info.md) —— 查询商品详情
- [09-stocks.md](./09-stocks.md) —— 设置库存(必须)
- [docs/v2/ozon-publish.md](../v2/ozon-publish.md) —— 发布集成方案
- [商品编辑与发布 API](../../api/product-editor-and-publishing.md) —— 当前 Atlas 预检与未来发布契约
- [平台目录缓存刷新方案](../../architecture/platform-catalog-cache-refresh-plan.md) —— 发布前属性/字典刷新边界
@@ -431,9 +431,11 @@ console.log('库存:', detail.stocks.present);
---
## 9. V2 项目集成
## 9. 早期集成示例(当前未实现)
### API 层(待实现)
> 下方 Python 示例来自早期 V2 方案,不是当前 NestJS 接口。商品详情与审核状态同步的实际实现进度以[项目交接](../../project/HANDOFF.md)和[在线商品 API](../../api/online-listings.md)为准。
### 历史 API 示例
```python
# server/api/products.py
@@ -554,4 +556,5 @@ get_product_info_cached.cache_clear()
- [04-product-import.md](./04-product-import.md) —— 创建/更新商品
- [06-product-list.md](./06-product-list.md) —— 商品列表分页
- [09-stocks.md](./09-stocks.md) —— 库存管理
- [docs/v2/ozon-publish.md](../v2/ozon-publish.md) —— 发布集成方案
- [在线商品 API](../../api/online-listings.md) —— 当前 Atlas 在线商品契约
- [项目交接](../../project/HANDOFF.md) —— 实施状态
@@ -323,9 +323,11 @@ async def sync_product_states():
---
## 9. V2 项目集成
## 9. 早期集成示例(当前未实现)
### API 层(待实现)
> 下方 Python 示例来自早期 V2 方案,不是当前 NestJS 接口。Ozon 仓库同步 Worker 尚未完成,当前契约与进度以[在线商品 API](../../api/online-listings.md)和[项目交接](../../project/HANDOFF.md)为准。
### 历史 API 示例
```python
# server/api/products.py
@@ -474,4 +476,5 @@ async def incremental_sync(last_sync_time: datetime):
- [04-product-import.md](./04-product-import.md) —— 创建/更新商品
- [05-product-info.md](./05-product-info.md) —— 查询商品详情
- [09-stocks.md](./09-stocks.md) —— 库存管理
- [docs/v2/database.md](../v2/database.md) —— products 表结构
- [在线商品 API](../../api/online-listings.md) —— 当前 Atlas 在线商品契约
- [商品域数据模型](../../architecture/product-domain-data-model.md) —— 采集箱与在线商品边界
@@ -425,9 +425,11 @@ async def sync_stocks_from_ozon():
---
## 9. V2 项目集成
## 9. 早期集成示例(当前未实现)
### API 层(待实现)
> 下方 Python 示例来自早期 V2 方案,不是当前 NestJS 接口。库存写入不属于当前平台目录与首次发布预检范围;实际状态以[项目交接](../../project/HANDOFF.md)和[在线商品 API](../../api/online-listings.md)为准。
### 历史 API 示例
```python
# server/api/products.py
@@ -595,5 +597,5 @@ async def update_large_batch(stocks: list):
- [04-product-import.md](./04-product-import.md) —— 创建商品(发布前置)
- [05-product-info.md](./05-product-info.md) —— 查询商品信息(含库存)
- [10-prices.md](./10-prices.md) —— 价格更新
- [docs/v2/ozon-publish.md](../v2/ozon-publish.md) §5 —— 发布链路(含库存设置)
- [在线商品 API](../../api/online-listings.md) —— 当前 Atlas 在线商品契约
- [项目交接](../../project/HANDOFF.md) —— 实施状态
+13 -17
View File
@@ -1,6 +1,6 @@
# Ozon Seller API 文档总览
> 本目录整理 Ozon Seller API 的核心接口文档,供 ozon-seller-kit 项目集成使用。
> 本目录整理 Ozon Seller API 的核心接口资料,并补充 Atlas 当前适配状态。Ozon 接口说明与 Atlas 实现状态必须区分:真正的 Ozon 商品导入 job 尚未实现。
> 官方文档:https://docs.ozon.ru/api/seller/zh/
---
@@ -15,10 +15,9 @@
| [04-product-import.md](./04-product-import.md) | 商品导入(创建/更新) |
| [05-product-info.md](./05-product-info.md) | 商品信息查询 |
| [06-product-list.md](./06-product-list.md) | 商品列表 |
| [07-import-by-sku.md](./07-import-by-sku.md) | 跟卖(按 SKU 复制) |
| [08-pictures.md](./08-pictures.md) | 图片更新 |
| [09-stocks.md](./09-stocks.md) | 库存管理 |
| [10-prices.md](./10-prices.md) | 价格更新 |
跟卖、独立图片更新和价格批量更新尚未形成当前仓库文档,不能使用旧目录链接推断已经实现。
---
@@ -85,18 +84,15 @@
---
## V2 项目集成清单
## Atlas 当前集成状态
| 接口 | 用途 | 实现状态 |
| Ozon 接口 | Atlas 用途 | 当前状态 |
|---|---|---|
| `/v1/description-category/tree` | 类目树 | ✅ API 已建(categories.py) |
| `/v1/description-category/attribute` | 类目属性 | ✅ API 已建 |
| `/v1/description-category/attribute/values` | 属性值字典 | ✅ API 已建 |
| `/v3/product/import` | 商品导入 | ✅ API 已建(publish.py) |
| `/v1/product/import/info` | 导入状态 | ✅ API 已建 |
| `/v3/product/list` | 商品列表 | 🟡 待建 |
| `/v3/product/info/list` | 商品详情 | 🟡 待建 |
| `/v1/product/import-by-sku` | 跟卖 | 🟡 待建(二期) |
| `/v1/product/pictures/import` | 图片更新 | 🟡 待建(二期) |
| `/v2/products/stocks` | 库存 | 🟡 待建(二期) |
| `/v1/product/import/prices` | 价格 | 🟡 待建(二期) |
| `/v1/description-category/tree` | 平台级类目/商品类型树 | 已实现 Worker 同步;每 7 天自动同步并支持管理员手动同步 |
| `/v1/description-category/attribute` | 平台级类型属性 | 已实现管理员批次、用户按需刷新和发布预检刷新 |
| `/v1/description-category/attribute/values`、`values/search` | 平台级字典候选值 | 已实现单字典同步、按需读取和关键词补齐 |
| `/v3/product/import` | 首次商品导入 | 尚未实现;当前只完成 `publish-preflights` |
| `/v1/product/import/info` | 导入结果轮询 | 尚未实现 |
| 商品列表、详情、库存、价格、跟卖、图片更新 | 后续商品运营能力 | 以对应模块交接状态为准,不在平台目录第 1~4 步范围内 |
Atlas 的平台目录接口、状态机和系统后台契约见[平台类目与属性管理 API](../../api/platform-catalog-admin.md)、[商品编辑与发布 API](../../api/product-editor-and-publishing.md)及[缓存刷新方案](../../architecture/platform-catalog-cache-refresh-plan.md)。
+26 -135
View File
@@ -1,143 +1,34 @@
# Ozon Seller API 文档整理完成
# Ozon Seller API 文档状态摘要
已完成 Ozon Seller API 的核心接口文档整理,涵盖商品发布、管理的完整流程。
> 本文件只汇总当前仓库中的 Ozon 资料和 Atlas 实施状态,不代表 Ozon 官方接口已经全部接入。官方行为以各接口文档与 Ozon 官方文档为准,Atlas 实现进度以 [`../../project/HANDOFF.md`](../../project/HANDOFF.md) 为准。
## 已完成的文档
## 已整理资料
### 核心文档(9 个)
| 文档 | 主题 | Atlas 当前状态 |
|---|---|---|
| [01-authentication.md](./01-authentication.md) | Client-Id、Api-Key 和错误处理 | 平台参考源凭据已由系统管理员配置并仅供 Worker 使用 |
| [02-category-tree.md](./02-category-tree.md) | 类目/商品类型树 | 平台级共享缓存已实现;每 7 天自动同步并支持管理员手动同步 |
| [03-category-attributes.md](./03-category-attributes.md) | 类型属性与字典值 | 平台级缓存、14/21 天状态机、管理员批次、按需刷新和发布预检已实现 |
| [04-product-import.md](./04-product-import.md) | `/v3/product/import` | 仅作为外部接口资料;Atlas 真正的发布 job 尚未实现 |
| [05-product-info.md](./05-product-info.md) | 商品信息查询 | 资料文档;实现状态以在线商品模块交接为准 |
| [06-product-list.md](./06-product-list.md) | 商品列表 | 资料文档;仓库同步 Worker 尚未完成 |
| [09-stocks.md](./09-stocks.md) | 库存管理 | 资料文档;不属于当前平台目录与首次发布预检范围 |
1. **README.md** - 总览与快速索引
2. **01-authentication.md** - 鉴权方式、Base URL、错误码、限流
3. **02-category-tree.md** - 类目树查询(选择类目)
4. **03-category-attributes.md** - 类目属性与字典值(属性映射)
5. **04-product-import.md** - 商品导入/创建/更新(核心接口)
6. **05-product-info.md** - 商品详情查询(审核状态、图片、属性)
7. **06-product-list.md** - 商品列表分页(ID 收集、批量查询)
8. **09-stocks.md** - 库存管理(必须设置才能上架)
跟卖、独立图片更新和价格批量更新尚未形成当前仓库文档。
### 待补充(二期)
## Atlas 当前目录与发布边界
- **07-import-by-sku.md** - 跟卖(按 SKU 复制 PDP)
- **08-pictures.md** - 图片更新
- **10-prices.md** - 价格批量更新
1. 类目树、类型属性和字典候选值按 `platform_id` 维护,同平台所有门店共享;本阶段没有门店差异层。
2. 类目树固定每 7 天自动同步。类型属性和字典不做周期性全量扫描,由管理员操作、普通用户访问或发布预检触发 Worker 任务。
3. 类型属性和字典默认 14 天内 fresh、14~21 天(含 21 天)stale、严格超过 21 天 expired;missing/expired 使用高优先级任务,stale 使用低优先级任务。
4. `POST /api/product-platform-profiles/{profile_id}/publish-preflights` 已实现,只刷新当前类型和所选 SKU 实际使用的字典,并执行本地发布校验。
5. 真正的 `product_publish_job`、发布快照、Ozon `/v3/product/import` 提交和结果轮询尚未实现;预检 `ready` 不表示商品已经提交或发布。
6. 发布预检目前只校验本地类目仍活动,尚未判断整棵类目树缓存是否过期;该边界需在真正发布 job 接入前确认或补齐。
---
## 当前应阅读的实现文档
## 文档特色
### 1. 完整的代码示例
- Python(服务端)示例
- TypeScript(前端)示例
- 实际可运行的代码片段
### 2. V2 项目集成指引
- 每个文档都包含"V2 项目集成"章节
- API 层实现示例(对齐 server/ 结构)
- 前端组件示例(对齐 studio/ 结构)
### 3. 最佳实践与常见问题
- 缓存策略
- 错误处理
- 性能优化
- 分页/批量操作
### 4. 实用场景
- 发布流程(端到端)
- 属性映射工作流
- 库存同步策略
- 审核状态检查
---
## 核心流程速查
### 完整发布流程
```
① 获取类目树 → 选择类目
POST /v1/description-category/tree
② 获取类目属性 → 映射属性
POST /v1/description-category/attribute
POST /v1/description-category/attribute/values/search
③ 组装请求体 → 发布商品
POST /v3/product/import → task_id
④ 轮询状态 → 获取 product_id
POST /v1/product/import/info → status=imported
⑤ 设置库存(必须)
POST /v2/products/stocks → 商品上架
⑥ 查询详情(可选)
POST /v3/product/info/list → 审核状态/图片/库存
```
### 关键约束总结
1. **类目**:只有末级类目(`disabled=false`)可建品
2. **必填字段**:name/description/category/price/尺寸重量/offer_id/images
3. **图片 URL**:必须 https 公网直链
4. **属性**:`is_required=true` 的必须填写
5. **异步任务**:`/import` 返回 task_id,需轮询状态
6. **库存必须设置**:不设置库存 = 商品不可购买
---
## 与 V2 项目的对应关系
| Ozon API | V2 后端 API | V2 前端页面 | 状态 |
|---|---|---|---|
| `/description-category/tree` | `/api/categories/tree` | CategoryPicker | ✅ 已建 |
| `/description-category/attribute` | `/api/categories/{id}/attributes` | AttributeMapper | ✅ 已建 |
| `/attribute/values` | `/api/categories/attribute/{id}/values` | - | ✅ 已建 |
| `/v3/product/import` | `/api/products/{id}/publish` | PublishPanel | ✅ 已建 |
| `/v1/product/import/info` | (background poll) | - | ✅ 已建 |
| `/v3/product/info/list` | `/api/products/{id}/ozon-detail` | OzonStatusBadge | 🟡 待建 |
| `/v3/product/list` | `/api/products/sync-from-ozon` | SyncButton | 🟡 待建 |
| `/v2/products/stocks` | `/api/products/{id}/set-stock` | StockPanel | 🟡 待建 |
---
## 使用建议
### 阅读顺序(新接入)
1. **01-authentication.md** - 了解鉴权与基础
2. **04-product-import.md** - 核心接口,先看这个
3. **02-category-tree.md** - 类目选择
4. **03-category-attributes.md** - 属性映射(难点)
5. **09-stocks.md** - 库存设置(必须)
6. 其他按需查阅
### 开发时查阅
- 看接口契约 → 查对应章节的"请求/响应"
- 看错误处理 → 查"常见错误"章节
- 看集成方式 → 查"V2 项目集成"章节
- 看最佳实践 → 查"使用场景"或"最佳实践"章节
---
## 下一步
### 立即可用
现有 9 个文档已覆盖 V2 项目一期的所有核心接口,可立即用于:
- 服务端 `ozon_client.py` 开发
- API 端点实现参考
- 前端组件开发参考
### 二期补充
需要时再补充:
- 跟卖(import-by-sku)
- 图片单独更新
- 价格批量更新
---
## 相关文档
- [docs/v2/ozon-publish.md](../v2/ozon-publish.md) - V2 发布集成方案(与本文档配套)
- [docs/v2/api.md](../v2/api.md) - V2 后端 API 设计
- [docs/v2/database.md](../v2/database.md) - V2 数据库设计
- [平台商品参考目录](../../architecture/platform-product-reference-catalog.md)
- [平台目录缓存刷新方案](../../architecture/platform-catalog-cache-refresh-plan.md)
- [平台类目与属性管理 API](../../api/platform-catalog-admin.md)
- [商品编辑与发布 API](../../api/product-editor-and-publishing.md)
- [项目交接](../../project/HANDOFF.md)
+1 -1
View File
@@ -1,6 +1,6 @@
# 商品 AI 编辑页
> 状态:已确认的产品需求与领域设计,尚未实施。它是 Atlas Seller Kit 的商品工作流规范。
> 状态:商品编辑主流程和 Ozon 动态属性 P0 已实施,平台类目/属性/字典读取及首次发布预检已迁移完成;定价确认、AI 任务和真正调用 Ozon 的发布 job 仍有未实施部分。准确接口状态以 [`../api/product-editor-and-publishing.md`](../api/product-editor-and-publishing.md) 和 [`../project/HANDOFF.md`](../project/HANDOFF.md) 为准。
## 目标
+1 -1
View File
@@ -1,6 +1,6 @@
# 商品编辑 API 契约
> 接口前缀与鉴权细节待基础架构确认。所有请求由当前用户、工作空间、门店上下文授权;客户端不能跨租户指定 ID。浏览器插件采集入口以 [`../requirements/collector-browser-extension.md`](../requirements/collector-browser-extension.md) 为准。
> 本文是早期产品级接口草案,部分路径和状态已经被实现契约取代,不应直接作为联调依据。当前端点、平台目录缓存和发布预检以 [`../api/product-editor-and-publishing.md`](../api/product-editor-and-publishing.md) 为准;所有请求仍须按当前用户、工作空间和平台/门店上下文授权,客户端不能跨租户指定 ID。浏览器插件采集入口以 [`../requirements/collector-browser-extension.md`](../requirements/collector-browser-extension.md) 为准。
## 1. 采集与读取
@@ -1,10 +1,10 @@
# Ozon 动态属性与 SKU 填报改造方案
> 状态:P0 已于 2026-09-18 实现,P1/P2 待实施。当前已完成必填展示、通用/变体属性分区、宽松草稿保存及服务端逐 SKU 校验;新 SKU 首次保存时关联属性、结构化字典值与发布闭环仍以后续阶段为准。
> 状态:P0 已于 2026-09-18 实现,平台目录读取和首次发布预检已于 2026-09-20 迁移到平台级缓存;P1 与真正调用 Ozon 的发布 job 待实施。当前已完成必填展示、通用/变体属性分区、宽松草稿保存、服务端逐 SKU 校验和发布前定向刷新;新 SKU 首次保存时关联属性、结构化字典值、发布快照与 Ozon 错误回填仍以后续阶段为准。
## 1. 目标与原则
Ozon 商品的必填属性由 `description_category_id` 与 `type_id` 对应的类目 schema 决定;同一 Ozon 平台的所有门店共用该 schema。编辑器不得为儿童玩具、服装、食品等类目分别硬编码字段,而应使用 Ozon 返回的 `is_required`、`is_aspect`、`dictionary_id`、`is_collection`、`max_value_count`、`type` 与复杂属性信息驱动展示、填报和发布校验。当前编辑器已经读取平台级类目、属性和字典缓存;发布链路迁移仍待后续步骤。
Ozon 商品的必填属性由 `description_category_id` 与 `type_id` 对应的类目 schema 决定;同一 Ozon 平台的所有门店共用该 schema。编辑器不得为儿童玩具、服装、食品等类目分别硬编码字段,而应使用 Ozon 返回的 `is_required`、`is_aspect`、`dictionary_id`、`is_collection`、`max_value_count`、`type` 与复杂属性信息驱动展示、填报和发布校验。当前编辑器和首次发布预检均读取平台级类目、属性和字典缓存;真正创建发布 job 并调用 Ozon 的闭环仍待后续实现。
页面继续以 Atlas 商品和 SKU 为编辑主体:主要信息保留稳定的商品级字段,Ozon 类目属性按通用属性和 SKU 变体属性拆分。保存草稿允许信息不完整;首次上架必须由服务端按当时有效的类目 schema 重新校验。
@@ -58,35 +58,41 @@ SKU 中的 Ozon 必填变体属性同样只在 Label 前显示红色 `*`。父
## 3. Schema 获取与展示状态
选择目标门店和末级类目后,系统使用门店缓存记录中的 `description_category_id + type_id` 获取属性 schema:
选择末级类目后,系统使用平台目录中的 `description_category_id + type_id` 获取属性 schema:
1. 优先读取当前门店的本地 schema 缓存。
2. 缓存不存在或超过约定有效期时,创建异步同步任务并显示「正在更新 Ozon 类目要求」。
3. 同步成功后重新构建动态属性表单。
4. 页面显示类目路径、商品类型和最后同步时间,并提供失败后的重试入口。
1. 优先读取当前平台共享的 Atlas schema 缓存;切换同平台目标门店不改变目录。
2. fresh 直接返回;stale(默认 14~21 天,含 21 天)返回旧缓存并创建/复用低优先级 Worker 任务。
3. expired(严格超过 21 天)或 missing 时创建/复用高优先级任务,API 最多等待 5 秒;仍未完成则返回 `202` 和 cache 元数据,前端轮询后重建表单。
4. 字典值使用 schema 返回的 Atlas `attribute_id` 精确限定平台、类型和属性作用域;部分字典的关键词未命中使用独立 `dictionary_search` 缓存状态。
5. 页面显示类目路径、商品类型和缓存状态,并提供失败后的重试入口。所有 Ozon 请求由 Worker 使用平台参考源凭据执行,浏览器和 API 进程不直连 Ozon。
以上描述当前门店级 schema 的获取流程。迁移到平台级参考目录后,直接读取当前平台共享 schema,不叠加门店差异;普通用户只请求 Atlas,Atlas 在缓存 stale、expired 或 missing 时创建/复用统一 Worker 任务,不由 API 进程直连 Ozon。14~21 天的旧缓存立即返回并低优先级刷新,严格超过 21 天或未缓存时等待高优先级任务;完整规则见[平台目录缓存刷新方案](../architecture/platform-catalog-cache-refresh-plan.md)。接口字段及草稿外键在 `docs/api/` 更新前不改变。
属性 schema 响应除现有字段外,计划补充:
完整规则见[平台目录缓存刷新方案](../architecture/platform-catalog-cache-refresh-plan.md)。当前属性 schema 契约为:
```ts
type ProductAttributeSchema = {
attribute_id: string;
platform_attribute_id: string;
complex_id: string;
name: string;
description: string | null;
group_id: string | null;
group_name: string | null;
value_type: 'text' | 'integer' | 'decimal' | 'boolean' | 'dictionary';
is_required: boolean;
is_collection: boolean;
max_value_count: number;
dictionary_id: string | null;
is_aspect: boolean;
synced_at: string;
editor_field:
| 'category'
| 'brand'
| 'model'
| 'tags'
| 'rich_content'
| 'description'
| null;
};
```
响应外层同时返回 `cache.status`、`last_success_at`、`fresh_until`、`expires_at`、完整性、刷新任务和重试间隔。`editor_field` 只按 Ozon 属性 ID 绑定已有编辑器字段,不按名称或翻译文本猜测。
切换类目后,新 schema 中仍可匹配的值可以保留;不再匹配的旧属性进入未映射属性区,不能进入新的发布 payload,也不应仅由前端直接清空而丢失。
## 4. 属性值表达
@@ -103,7 +109,7 @@ type ProductAttributeValue =
- 字典属性根据缓存中的 `dictionary_value_id` 解析对应显示值,不能信任客户端伪造的字典名称。
- 自由输入属性写入文本值,并按 Ozon 请求格式设置相应的空字典标识。
- 服务端校验字典值属于当前门店、当前类目和当前属性。
- 服务端校验字典值属于当前平台、当前类目类型和当前属性;门店不参与参考字典归属。
- 属性值数量、类型和复杂属性结构均由服务端复核。
## 5. 新增 SKU 与属性契约
@@ -138,7 +144,7 @@ SKU 条形码应保存到当前平台 listing 维度;现有商品级条形码
### 6.2 首次上架
实现并复用统一的服务端发布就绪校验:
本地草稿就绪校验使用:
```text
GET /api/product-platform-profiles/{profile_id}/validation
@@ -156,6 +162,14 @@ GET /api/product-platform-profiles/{profile_id}/validation
`POST /publish-jobs` 必须调用同一套校验,不能信任浏览器校验结果。保存草稿和创建发布任务应继续保持各自的事务与幂等边界。
真正创建发布 job 前还必须调用已实现的:
```text
POST /api/product-platform-profiles/{profile_id}/publish-preflights
```
预检只处理用户选中的 `not_listed` / `failed` SKU,按当前平台类型 schema 和实际使用的字典执行 fresh/stale/expired/missing 判断:stale 使用旧缓存并低优先级刷新,expired/missing 高优先级刷新且所有任务共享一个 5 秒等待窗口。目录可用后再执行统一本地校验;返回 `ready` 才能进入未来的发布快照事务。当前预检尚未检查整棵类目树的新鲜度,只验证 profile 引用的本地类目仍活动;真正发布 job 接入前需要补齐或确认这一边界。
## 7. Ozon 发布错误回填
本地 schema 无法覆盖 Ozon 所有条件校验。Ozon 拒绝某个 import item 时,系统应:
@@ -191,7 +205,7 @@ GET /api/product-platform-profiles/{profile_id}/validation
### P2:发布闭环
- 发布接口复用现有 validation 校验结果。
- 发布前定向刷新与 validation 复用已通过 `publish-preflights` 完成;真正发布接口必须调用同一服务。
- 按 SKU 生成 `/v3/product/import` 的 `items[]`。
- 正确生成普通属性与复杂属性。
- 固化发布快照并将 Ozon 错误回填到 SKU/属性。
+5 -1
View File
@@ -29,7 +29,11 @@
上架状态是“当前平台 + SKU”维度:`未上架`、`处理中`、`上架失败`、`已上架`。SPU 在当前平台的状态由未删除 SKU 汇总为 `未上架`、`已部分上架`、`已上架`。用户可勾选任意未上架 SKU 执行首次上架;已上架 SKU 不显示上架选择,也不发送更新请求。编辑已上架 SKU 时,保存按钮旁必须提示“仅保存 Atlas 草稿,不会更新 Ozon 商品”。
当前实现中,前端根据目标门店的本地类目 schema 渲染属性控件、必填提示、字典选择、多值限制及变体属性。门店 Ozon 凭据验证成功后,Worker 使用该门店凭据同步中文类目树;缓存为空时编辑器显示准备中状态,用户不能在编辑页发起树同步。选中类目但 schema 缺失时,仍可显式同步该类目属性。字典控件先搜索本地缓存,输入至少两个字符仍无结果时,可显式从 Ozon 搜索并缓存命中值。`is_aspect=true` 在编辑器中按 SKU 展示,其余属性按商品展示;保存仍由服务端复核 schema 与字典值。选择类目和属性与商品草稿同一次保存,`platform_type_id` 从服务端门店类目缓存取得。类目变更或 schema 下线后,失配属性保留为未映射草稿,不能自动进入发布请求。[待迁移目标](../architecture/platform-product-reference-catalog.md)是同平台门店共用参考目录,本阶段不维护门店差异层。
当前实现中,类目/商品类型树、类型属性和字典候选值均读取当前平台的共享参考目录,不再按目标门店复制;同平台切换门店不会清空或重新同步目录。平台参考源凭据仅由系统管理员配置,所有 Ozon 请求经 Worker 执行,浏览器和 API 进程都不直连 Ozon。
前端按平台 schema 渲染必填提示、字典选择、多值限制及变体属性。`is_aspect=true` 按 SKU 展示,其余属性按商品展示;品牌、型号、类型、主题标签、JSON 富内容和简介等已有编辑器字段严格按 Ozon 属性 ID 绑定,不在动态属性区重复出现。类型属性和字典默认 14 天内 fresh,14~21 天(含边界)stale 时继续返回旧缓存并低优先级刷新,严格超过 21 天或从未同步时创建高优先级任务、最多等待 5 秒,未完成则返回 `202` 并由前端轮询。类目树固定每 7 天自动同步,也可由系统管理员手动同步。
保存仍由服务端按平台 schema 与字典值复核。选择类目和属性与商品草稿同一次保存,`platform_type_id` 从平台类目记录取得;类目变更或 schema 下线后,失配属性保留为未映射草稿,不能自动进入发布请求。首次发布前已提供 `publish-preflights` 复用同一套目录新鲜度与本地校验逻辑;真正创建发布任务并调用 Ozon 的流程尚未实现。完整规则见[平台目录缓存刷新方案](../architecture/platform-catalog-cache-refresh-plan.md)。
类目、标题、型号、品牌以及默认包装重量和默认包装尺寸固定放在「主要信息」区。SKU 默认继承父商品包装规格,仅在包装不同的情况下填写覆盖值。平台动态必填字段统一使用表单标准表现,在 Label 前显示红色 `*`;不追加「Ozon 必填」标签,也不在区块标题显示完成数量。更完整的目标交互、属性值表达、SKU 关联和发布校验改造见 [`ozon-dynamic-attributes-and-sku-form-plan.md`](./ozon-dynamic-attributes-and-sku-form-plan.md)。
+11 -3
View File
@@ -281,7 +281,7 @@ M3.5 与 M3.6 均已收口。真实来源回归中若要提升 Ozon 自动类目
- 编辑器选择类目后立即加载属性 schema。`is_required=true` 的商品级属性始终展示在「产品属性」,`is_aspect=true` 的必填属性始终展示在对应 SKU 卡片;必填状态仅使用 Label 前的红色 `*`,选填属性仍可折叠。
- 动态属性控件已拆为独立组件,统一处理字典、布尔、数值、文本和多值字段;商品级与 SKU 级属性只负责各自的分区编排。主要信息继续保留类目、标题、型号、品牌和默认包装规格,SKU 展开区可覆盖包装及商品规格。
- 草稿保存不会因 Ozon 必填属性或包装规格为空而失败;已提交的非空属性仍由 API 校验类型、数量、字典归属、类目归属及 `is_aspect` 对应的商品/SKU scope。
- 新增 `GET /api/product-platform-profiles/{profile_id}/validation`,在独立校验模块中返回商品级问题和逐 SKU 问题。已导入 SKU 标为 `not_publishable_again`;必填通用属性和必填变体属性都以当前门店类目 schema 为准。
- 新增 `GET /api/product-platform-profiles/{profile_id}/validation`,在独立校验模块中返回商品级问题和逐 SKU 问题。已导入 SKU 标为 `not_publishable_again`;该阶段的必填通用属性和必填变体属性当时以门店类目 schema 为准,现已由 1.36 的第 3 步迁移为平台 schema。
- 类目 schema 新增 `editor_field` 语义绑定,严格按 Ozon 属性 ID 识别品牌 `85`、型号名称 `9048`、类型 `8229`、主题标签 `23171`、JSON 富内容 `11254` 和简介/产品描述 `4191`。这些字段由编辑器已有控件承载,动态属性区不再重复展示或预取字典;名称和翻译文本不参与判断。
- Contracts、API 与 Web TypeScript 检查、Web production build 和 `git diff --check` 已通过。API 集成测试已补充通用/逐 SKU 必填缺失和 scope 伪造场景;本轮环境无法连接 PostgreSQL/Redis,因此未实际跑通该集成测试,且未按约定启动 API 或 Worker。
@@ -293,7 +293,7 @@ M3.5 与 M3.6 均已收口。真实来源回归中若要提升 Ozon 自动类目
- 类目/商品类型树、类型属性定义和字典候选值归属 `platform_id`,同一平台的全部门店共用,不再按门店复制。Ozon 与未来 Wildberries 等平台分别使用自己的适配器和外部 ID 语义;本阶段**不创建、不读取门店差异层**。
- 门店连接、卖家凭据、商品/SKU 已填属性、发布状态和错误仍是工作空间业务数据。平台参考目录不保存客户商品、价格、库存或发布事实。
- 商品编辑器已由迁移 `027` 和新版 API 切到平台级类目 UUID、属性和字典缓存。`022_store_catalog_cache.sql` 的门店表与列暂时保留为兼容/回滚数据,不得在发布链路迁移完成前删除。
- 商品编辑器和首次发布预检已由迁移 `027` 与新版 API 切到平台级类目 UUID、属性和字典缓存。`022_store_catalog_cache.sql` 的门店表与列暂时保留为兼容/回滚数据;真正的 Ozon 发布 job 复用预检服务并完成稳定性验证前不得删除。
#### 1.33.2 类目树、属性和字典的标识规则
@@ -357,7 +357,7 @@ BullMQ Worker(最多 5 次,5 秒起指数退避)
1. 调整定时调度公平性:树之后不能等待全部类型属性完成才开始字典;应在属性和字典之间设置配额/轮转,并优先近期使用或管理员选中的类型。
2. 为超过 10000 项的大字典实现可续传游标或分片同步周期。当前任务会正确标记 `is_complete=false`,但下一次仍从头读取,不能持续推进到第 10001 项以后。
3. ~~将商品编辑器由门店级缓存迁移到平台级目录。~~ 已由第 3 步完成;后续只在发布迁移和稳定性验证后讨论清理 `022` 表。
3. ~~将商品编辑器由门店级缓存迁移到平台级目录。~~ 已由第 3 步完成,发布预检也已迁移;后续只在真正的 Ozon 发布 job 复用预检并完成稳定性验证后讨论清理 `022` 表。
4. 增加平台目录 PostgreSQL 集成测试,覆盖系统管理员隔离、凭据版本替换、手动幂等复用、定时去重、失败保留旧缓存、完整字典停用旧值及跨平台 ID 拒绝。
5. 在浏览器完成管理员菜单、直达路由、凭据弹窗、任务轮询、类目/属性/字典展示的回归,并确认 API/Worker 进程都使用迁移后的当前代码。
@@ -418,6 +418,14 @@ BullMQ Worker(最多 5 次,5 秒起指数退避)
- 最终本地校验除标题、型号、品牌、必填属性、offer ID、价格、包装和图片外,新增“所用字典缓存未过期且候选 ID 仍为活动值”检查。stale 字典可以完成本次发布校验并在后台刷新;expired/missing 会返回刷新中问题。
- 预检响应包含 schema 与每个已用字典的统一 cache 元数据,以及本次创建/复用的目录 runs。真正的 `product_publish_job`、发布快照、Ozon `/v3/product/import` 调用和轮询仍未实现;预检 ready 不能解释为已提交或已发布。
- 代码按目录编排、最终校验、HTTP 入口拆分在独立组件中,没有把逻辑继续堆入 `product-editor.service.ts`。API 集成测试覆盖 stale 的 low publish 任务、expired 的 high 提升、只刷新已用字典和不触达未用字典。
- 当前边界:预检只校验 profile 引用的平台类目仍存在且活动,并刷新该类型 schema/实际使用字典;尚未检查整棵类目树的缓存新鲜度,也不会因树过期创建树任务。本地类目无效时返回 Atlas 自己的 `422 CATEGORY_INVALID`,不是 Ozon 响应。真正发布 job 接入前需补齐或确认此边界。
### 1.38 文档同步与当前交接结论(2026-09-20)
- 已把平台级目录、第 1~4 步刷新方案、发布预检、缓存设置弹窗和最近任务刷新反馈同步到交接、架构、商品编辑、管理 API 与 Ozon 集成文档。
- 当前事实统一为:类目树每 7 天自动同步且支持管理员手动同步;类型属性和字典不做周期性全量扫描;普通访问和发布预检按 fresh/stale/expired/missing 创建或复用 high/low Worker 任务;同平台所有门店共享一份参考目录。
- 管理页面的缓存新鲜度设置由按钮打开弹窗;最近任务手动刷新有 loading 及成功/失败 message,5 秒自动轮询静默执行。
- 旧门店目录章节保留为带日期的历史实现记录,不代表当前读写路径。当前未完成重点是真正的 Ozon 发布 job、树级发布新鲜度边界、大字典续传以及平台目录 PostgreSQL 集成测试。
## 2. 已确认产品决策