8.8 KiB
平台类目与属性管理 API
2026-09-18 新增契约,2026-09-20 增加平台刷新策略和类型属性批次。平台级目录供系统管理员维护;普通商品编辑器现有门店级 API 暂不改变。平台参考目录的归属见
platform-product-reference-catalog.md,新鲜度与优先级规则见platform-catalog-cache-refresh-plan.md。
权限与安全
- 本模块接口都要求有效登录会话。除
GET /api/system/access外,必须具有数据库授予且未撤销的system_admin全局权限;工作空间owner/admin不等于系统管理员。未授权返回403 SYSTEM_ADMIN_REQUIRED。 - 系统管理员授权只能由受控数据库运维操作授予或撤销,不提供自助提权 HTTP 接口。权限变更在下一次请求时生效;前端菜单只是展示控制。
- 目录、同步来源和任务均按
platform_id分区,不使用客户工作空间的workspace_id承载平台任务。OzonClient-Id/Api-Key只经 TLS 提交至 API,Key 加密落库,不写日志、Outbox 或响应。 - Wildberries 等其他平台可展示目录空状态;未实现适配器时发起同步返回
422 PLATFORM_CATALOG_UNSUPPORTED。
首位系统管理员初始化
系统管理员不能通过产品界面自助获得权限。目标用户先完成正常注册,再由数据库运维账号执行以下 SQL;应用运行账号 atlas_app 已被禁止修改该表:
INSERT INTO atlas.system_admin_grants (user_id)
SELECT id FROM atlas.users WHERE lower(email) = lower('管理员邮箱')
ON CONFLICT (user_id) DO UPDATE SET revoked_at = NULL, granted_at = now();
撤销权限时设置 revoked_at = now(),不要删除用户或改变其工作空间角色。授权和撤销均在下一次请求时生效。
接口
GET /api/system/access
返回 { "data": { "is_system_admin": boolean } },供已登录 Web 决定是否展示系统管理入口;服务端权限判断仍以数据库授权为准。
GET /api/system/platform-catalog/platforms
返回 { "data": [{ "id", "code", "name", "source": { "configured", "client_id_hint", "updated_at" }, "summary": { "category_count", "last_tree_sync_at", "next_tree_sync_at", "last_error" } }] }。client_id_hint 仅为脱敏末尾字符,不返回 API Key、密文或指纹。next_tree_sync_at 固定为最近成功时间后 7 天;从未成功时为 null 并由调度器尽快创建首次任务。
PUT /api/system/platform-catalog/platforms/{platform_id}/source
Body:{ "client_id": "...", "api_key": "..." }。仅 Ozon 适配器接受当前配置;重复提交同一凭据不产生新版本或重复任务。首次配置或更换凭据时返回脱敏来源状态,并为该平台排队一次类目树同步;更换后旧版本的未完成任务不得用新密钥执行。
GET /api/system/platform-catalog/platforms/{platform_id}/categories
返回当前平台的全部树节点 { id, parent_id, external_category_id, external_type_id, name, path, is_active, synced_at, attribute_last_success_at, attribute_cache_status, attribute_last_error, attribute_last_error_at };按父子关系在客户端构建树,目录节点可展开,仅可发布类型叶子用于查看属性。纯目录节点的属性状态为 null;可发布类型状态为 fresh | stale | expired | missing。
GET /api/system/platform-catalog/platforms/{platform_id}/refresh-policy
返回平台目录刷新策略:
{
"data": {
"attribute_fresh_days": 14,
"attribute_expire_days": 21,
"dictionary_fresh_days": 14,
"dictionary_expire_days": 21,
"updated_at": "2026-09-20T00:00:00Z"
}
}
尚未显式配置的平台返回相同默认值。类目树固定每 7 天同步,不属于可配置策略。
PUT /api/system/platform-catalog/platforms/{platform_id}/refresh-policy
Body 必须完整提交 { "attribute_fresh_days", "attribute_expire_days", "dictionary_fresh_days", "dictionary_expire_days" }。四项均为 1..365 的整数,同类资源的 expire_days 必须严格大于 fresh_days。成功保存后立即影响后续状态判断,但不得因此主动扫描或创建属性/字典任务;服务端记录修改人和时间。
GET /api/system/platform-catalog/platforms/{platform_id}/categories/{category_id}/attributes
返回该类型的属性定义 { id, platform_attribute_id, complex_id, name, description, group_name, value_type, is_required, is_aspect, is_collection, max_value_count, dictionary_id, is_active, synced_at, last_success_at, cache_status, last_error_message, last_error_at, dictionary_last_success_at, dictionary_cache_status, dictionary_is_complete, dictionary_last_error_message, dictionary_last_error_at, dictionary_value_count }。类型级 last_success_at/cache_status 会在该类型的每个属性行重复,便于管理页展示;没有任何属性行时应结合属性汇总或资源状态判断 missing。category_id 不属于所选平台时返回 404。
GET /api/system/platform-catalog/platforms/{platform_id}/attributes/{attribute_id}/values?q={keyword}&page={number}
仅字典属性可查;page 从 1 开始、每页最多 100,返回 { "data": [{ "id", "external_value_id", "value", "info", "picture" }], "meta": { "page", "page_size", "total", "is_complete" } }。搜索只查 Atlas 缓存;is_complete=false 表示已缓存部分候选值,不表示 Ozon 字典只有这些值。
POST /api/system/platform-catalog/platforms/{platform_id}/syncs
Headers:Idempotency-Key(UUID)必填。Body:{ "scope": "tree" | "attributes" | "dictionary", "category_id"?: "uuid", "attribute_id"?: "uuid" }。树同步不带其他 ID;兼容的单类型属性同步需要当前平台的可发布 category_id;字典同步需要该类型下带字典的 attribute_id。同作用域已有 queued/running 任务时返回已有任务,并将本次幂等键持久映射到该任务;同一用户重试不会在原任务完成后意外创建第二个任务。成功排队返回 202 与 { "data": { "id", "platform_id", "scope", "status", "trigger", "priority", "created_at" } }。同步由 Worker 异步执行,失败保留上次成功缓存。树任务优先级独立决定;属性和字典根据资源年龄选择 low 或 high,missing/严格超过过期阈值为 high。
GET /api/system/platform-catalog/platforms/{platform_id}/attribute-sync-summary
返回当前策略下活动可发布类型的聚合数量:
{
"data": {
"total": 7365,
"fresh": 100,
"stale": 200,
"expired": 300,
"missing": 6765,
"active_batch_id": null,
"evaluated_at": "2026-09-20T00:00:00Z"
}
}
状态边界严格按平台策略和最近成功时间计算:age < fresh_days 为 fresh,fresh_days ≤ age ≤ expire_days 为 stale,age > expire_days 为 expired,从未成功为 missing。
POST /api/system/platform-catalog/platforms/{platform_id}/attribute-sync-batches
Headers:Idempotency-Key(UUID)必填。Body:
{ "mode": "missing_expired" | "missing_stale_expired" | "all" }
missing_expired固定 missing、expired 类型快照;missing_stale_expired固定 missing、stale、expired 类型快照;all固定全部活动可发布类型快照,包含 fresh。
没有命中类型时返回 422 CATALOG_ATTRIBUTE_BATCH_EMPTY。同一平台已有 queued/running 批次时返回 409 CATALOG_ATTRIBUTE_BATCH_ACTIVE 和现有批次 ID,避免两个全量扫描互相竞争。成功返回 202 与批次 { id, platform_id, mode, status, total_count, pending_count, running_count, succeeded_count, failed_count, cancelled_count, created_at, started_at, completed_at }。Worker 每分钟限量领取批次项;missing/expired 子任务为高优先级,fresh/stale 子任务为低优先级。批次创建后新增的类型不加入本批次。
GET /api/system/platform-catalog/platforms/{platform_id}/attribute-sync-batches?limit={number}
返回最近批次及实时聚合进度,最多 50 条。批次允许部分失败:任一子任务失败时最终状态为 failed,但成功类型继续保留且不会回滚。
GET /api/system/platform-catalog/platforms/{platform_id}/attribute-sync-batches/{batch_id}
返回单个批次的相同进度字段;批次不属于当前平台时返回 404。
GET /api/system/platform-catalog/platforms/{platform_id}/syncs?limit={number}
返回该平台最近的同步任务(最多 50 条),包括作用域、触发方式、状态、开始/完成时间、数量摘要及脱敏错误。系统管理员可据此查看手动和定时更新结果。
类目树调度固定为最近一次成功后 7 天。类型属性和属性字典没有周期性自动扫描;它们只由管理员操作、后续普通用户访问和发布前校验创建任务。