# 商品与 SKU 数据模型 > 目标数据库:PostgreSQL。单位统一存储为毫米(`mm`)和克(`g`)。所有租户数据必须拥有或可追溯至 `workspace_id`。 ## 1. 云端租户层 ```text users ── workspace_memberships ── workspaces └─ stores ── platform_connections ``` `stores` 只保存其唯一绑定的平台标识、店铺默认计价币种和连接状态;一个门店不能跨平台使用。市场/国家、默认语言、可用语言和可用币种属于平台能力配置,不能从门店推导。敏感凭据经加密后保存在专用连接密钥记录中,绝不放进前端、日志或商品 JSON。 商品编辑、定价和佣金规则中的 `market_code` 是当前**平台业务上下文**的一部分:首期 Ozon 使用平台默认市场 `RU`;未来平台支持多市场时,由顶部全局上下文选择或使用平台默认值,而非读取门店字段。 ## 2. 商品领域 ```text workspaces 1 ── N collection_items ── 0..1 products (SPU) 1 ── N product_variants (SKU) │ │ └─ N product_platform_profiles ── N product_variant_platform_listings (平台发布配置) (平台 SKU 首次发布映射) ``` `collection_items` 是工作空间级采集箱记录,保存来源平台、来源商品键、原始证据、规范化结果、素材任务和可空的草稿商品关联;它在采集阶段没有 `target_platform_id` 或 `target_store_id`。每个父商品至少有一条 SKU。`products` 仅是**采集箱商品**的跨平台可复用资料:来源标题、用户填写的型号、条形码、采买地址、货源备注、默认规格和来源证据;采集阶段型号允许为空,不能从来源数据猜测生成。首次上架前型号必须已由用户确认填写;不把任何一个平台的类目、价格、属性或上架状态直接写到该表。 `product_platform_profiles` 是 `product_id + platform_id` 唯一的平台发布配置。用户在页头选中平台并进入编辑时才创建或读取它;它保存当前目标门店、该平台的标题/简介/标签、类目和类型、平台通用属性及本平台草稿版本。A 平台与 B 平台独立配置、独立状态;A 已上架不会让 B 显示已上架。门店只是配置中可改的当前发布目标,实际每次发布仍会在 SKU 映射中固化当时的 `store_id`。 `product_variants` 保存:稳定来源键、展示名、删除状态、排序、采购价、SKU 规格、包装物流规格、实物规格和时间戳。`product_variant_platform_listings` 保存平台 SKU 维度的数据:`profile_id`、`variant_id`、用户填写的本平台 `offer_id`(货号)、售价、划线价、币种、平台变体属性、首次导入状态/失败原因、首次导入任务和当时 `store_id`、Ozon `product_id`。采集时不得根据 `source_sku` 自动填入 `offer_id`;用户填写后,它对同一门店/平台连接必须唯一,并且是首次上架的必填项。成功导入的 SKU 映射不可再次创建导入任务;删除 Atlas SKU 只软删 Atlas 关联,不删除平台商品和审计记录。 `platform_listings`(实际建表名待定)保存店铺级在线商品 SKU 镜像,一条记录对应 Ozon 的一个 `product_id` / `offer_id`,而非采集箱 SPU:`store_id`、平台 `product_id`、`offer_id`、平台 SKU、名称、价格、可见性、审核/错误、库存、图片摘要、完整同步快照、最近同步时间和来源(`atlas_published` / `external_sync`)。它是**平台 + 门店独有**的外部事实记录,绝不跨平台、跨门店复用,也不因关联了采集箱 SKU 就变成采集箱商品。它由异步同步任务更新,包含非 Atlas 发布的商品;通过 `store_id + offer_id/product_id` 可选关联 Atlas SKU。采集箱不因成功上架而删除,其当前平台状态由首次导入映射汇总;在线商品的 `visible` 只用于在线商品实际可见性展示。 ## 3. 双规格和继承 父商品可提供默认的实物/包装规格;SKU 可以逐字段覆盖: - 实物:`physical_length_mm`、`physical_width_mm`、`physical_height_mm`、`net_weight_g`;用于规格展示、文案和尺寸图。 - 包装物流:`package_length_mm`、`package_width_mm`、`package_height_mm`、`shipping_weight_g`;用于定价、物流校验和平台发布。 值按“SKU 覆盖 → 父商品默认”合并;两组规格独立合并。`null` 表示该字段继承,不是零,也不是 SKU 不存在。每组须有来源、原始文本、状态(`confirmed`/`ambiguous`/`missing`)和确认时间;口径未知的数据不能落入任一规格组。 ## 4. 动态属性和媒体 `product_attributes` 使用结构化行保存 `profile_id`、可空 `variant_id`、本地 key、平台属性 ID、复杂属性 ID、JSON 值、语言、来源与确认状态。平台 schema 驱动类型、必填、字典、多值和 `is_aspect`;`is_aspect=true` 默认 variant scope,但允许业务覆盖。AI 类目推荐保存为独立候选记录,包含输入证据摘要、候选类目、置信度、依据、模型版本、用户确认结果和时间,不可直接覆盖人工选择。 `product_assets`(落地时复用基础表 `media_assets` 或以其为资产事实来源)保存 `storage_provider`、`bucket`、`object_key`、内容哈希、MIME 类型、尺寸和归属关系;不保存长期有效或公开的对象存储 URL。`variant_media` 首期只保存 SKU 对图片资产稳定 ID 的选择、原图/水印派生图关系与顺序;视频相关字段可为未来预留但不在首期 UI 或发布请求中启用。资产归属一律用稳定 ID;采集平台无法稳定给 SKU 归属时,只作为商品级候选池。来源页面图片 URL 仅保存在采集证据中;前端预览/下载 URL 由服务端按 `object_key` 临时签发。 ## 5. 定价、任务和发布 `variant_pricing_snapshots` 保存确认时的输入、合并后的包装规格、公式版本、汇率、结果、告警和时间;报价快照还应关联 `profile_id`,因为货币和店铺定价方案属于平台发布配置。导出/发布时服务端重新校验,不能信任浏览器结果。所有 AI、媒体、导出和发布任务保存 `workspace_id`、`product_id`、可空/必填的 `profile_id` 与 `variant_id`,可重试、可恢复并隔离租户。