furniture-3d-system-Wu/furniture-3d-system/docs/架构设计/数据库主数据治理与外部只读契约.md

18 KiB
Raw Blame History

数据库主数据治理与外部只读契约

创建时间:2026-05-29 适用范围:furniture-3d-system 主业务数据库、产品资料链路、外部系统只读接入。

配套文档:

  • docs/架构设计/外部只读视图字段字典-v1.md
  • docs/架构设计/外部只读契约巡检说明.md
  • docs/架构设计/数据库质量巡检基线-20260529.md
  • docs/架构设计/内部主数据地基治理方案.md
  • docs/架构设计/内部主数据巡检基线-20260529.md
  • scripts/sql/grant-external-readonly.sql
  • scripts/sql/preflight-intake-non-integrated-sku-items.sql
  • scripts/sql/check-external-readonly-contract.sql
  • scripts/sql/check-internal-master-data-foundation.sql
  • scripts/verify-master-data-foundation.js

1. 结论

多个外部系统会依赖本系统数据库时,不能直接开放业务原表。本系统必须先把内部主数据真相源梳理清楚,再通过稳定的外部契约层对外开放。

固定原则:

  • PostgreSQL 是本系统主业务数据库。
  • 数据库保存业务数据、文件 URL、对象 key、元数据;图片、模型、附件等素材本体不直接存入数据库。
  • 外部系统只能读专用只读契约层,不能直接读 ProductSKU、IntakeNonIntegratedProduct.payload 等业务原表。
  • 外部系统主同步、去重和幂等更新必须优先使用 barcode_business_key;内部 id / product_id / sku_id / intake_product_id / intake_sku_item_id 只能用于问题追踪。
  • 内部业务表允许随系统演进调整;外部契约必须版本化、稳定、可追溯。
  • 对外开放字段必须白名单化,默认不开放内部流程、权限、草稿、审批、临时 UI 状态。

2. 当前数据库与素材现状

2.1 主业务库

当前 Prisma datasource 使用 postgresql。项目运行时通过 @prisma/adapter-pg 和 pg 连接池访问 PostgreSQL。

mysql2 只用于 JST/聚水潭等外部库读取同步,例如 SKU 库存、供应商和仓库目录读取。它不是本项目主业务库。外部 MySQL 同步写入本系统前必须先确认来源库身份和目标 PostgreSQL 身份,不能把 MySQL 当作第二套内部主数据真相。

2.2 素材存放

数据和素材是分开存放的。

当前主要形态:

  • 业务表中保存 modelUrl、thumbnailUrl、images、fileUrl、storageKey 等引用。
  • 非集成产品附件默认存放在 .data/non-integrated-assets,通过 /api/intake/non-integrated/assets/... 受权限控制读取。
  • 旧附件可能仍在 public/non-integrated-test-assets。
  • 资产缩略图、AI 生图等服务可通过 S3/MinIO 工具链上传,并在数据库保存公开 URL。

外部系统不应拿服务器文件系统权限。正确方式是读取数据库契约层里的 URL,再通过受控 HTTP 地址访问素材。

3. 主链数据真相源

3.1 资产中心

资产中心负责资产源数据。

数据对象 当前主表 真相源定位 对外开放建议
系列 AssetSeries 资产系列主数据 可开放编码、名称、分类、启用状态
资产族 AssetFamily 同系列下的资产族主数据 可开放编码、名称、类型、系列归属、状态
规格款 AssetVariant 具体可调用资产规格 可开放编码、名称、条码、尺寸、颜色、材质、图片、模型、重量、成本
花色/材质 Material 板材花色和材质配置 可开放编码、名称、贴图/缩略图、PBR 参数中的必要字段
五金件 Hardware 标准五金主数据 可开放编码、名称、分类、规格、模型、缩略图、尺寸

注意:

  • AssetVariant 是资产中心里最接近后续 SKU 资料来源的结构化表。
  • AssetVariant.businessData、assemblyBasis、compatibilityRules 等 JSON 字段不能整体对外开放,需要按字段白名单投影。
  • 已删除、草稿、审核中资产不能默认对外开放,必须按状态过滤。

3.2 辅助装配与草稿组

辅助装配结果进入草稿组。草稿组是正式入库前的承接层,不是最终正式产品真相。

数据对象 当前主表 真相源定位 对外开放建议
草稿组 DraftGroup 一次辅助装配结果集合 默认不作为外部主数据,可按内部管理需要开放只读摘要
准 SKU PreSku 入库前的准 SKU 快照 默认不直接对外开放;正式入库后以 ProductSKU 为准
准 SKU 包装 PreSkuPackagingPlan 入库前包装草案 默认不直接对外开放;正式化后再进入正式产品/轻仓库契约

注意:

  • PreSku.familySelection、variantSelection、displaySummary、bomPreview、packagingSnapshot 都是 JSON 快照,适合内部承接,不适合原样对外。
  • 外部系统如果需要看“待入库草稿”,应单独设计 external.draft_pre_skus_v1,并明确它不是正式商品资料。

3.3 正式产品资料

正式产品资料以 Product 和 ProductSKU 为核心。

数据对象 当前主表 真相源定位 对外开放建议
正式 SPU/型号 Product 正式产品型号主数据 可开放型号、名称、分类、缩略图、模型、状态
正式 SKU ProductSKU 正式产品 SKU 主数据 必须作为外部产品资料的核心出口;skuCode / name 为结构化主字段
正式 SKU 资产资料副本 ProductSkuAssetProfile 正式 SKU 对资产中心资料的结构化同步副本 开放型号、规格码、条码、尺寸、颜色、材质、图片、来源状态
SKU 板件 Panel SKU/产品结构件 需要时开放结构件编号、名称、尺寸、材质摘要
SKU 孔位 Hole SKU/产品孔位结构 仅向需要 3D/制造数据的系统开放,不作为普通商品资料
SKU 五金 ProductHardware SKU/产品五金用量和位置 可按 BOM 需求开放编码、数量、安装关系摘要
包装方案 PackagingPlan / PackagedItem 正式包装与库存承接 需单独按轻仓库口径整理

ProductSkuAssetProfile 保存正式 SKU 的资产资料同步副本,包括资产来源 ID、型号、规格码、条码、尺寸、颜色、材质、图片、来源状态等。ProductSKU.attributes.assetSkuSnapshot 仅作为兼容快照保留,不能再作为正式商品资料的主读真相。

代码层边界由 npm run db:verify-master-data:code 和 node scripts/verify-master-data-code-boundaries.js --mode compatibility-snapshot-read 共同兜底:正式 SKU 的 attributes、准 SKU 的 displaySummary / familySelection、非集成产品的 payload 只能留在已分类的兼容、迁移或过程入口里;高优先级或未分类读点会进入阻断风险,不能作为对外契约或内部主读的依据。

普通正式 SKU 的创建、CAD 导入、产品上传和编辑链路必须写入 ProductSKU.name。attributes.skuName 只允许作为兼容输入或历史兜底;对外视图、DTO 和轻仓库概览优先读取 ProductSKU.name。

外部正式产品契约统一携带稳定业务键:

  • external.product_skus_v1.product_business_key 来自 Product.modelNumber 的规范化大写值。
  • external.product_skus_v1.sku_business_key 来自 ProductSKU.skuCode 的规范化大写值。
  • external.product_skus_v1.barcode_business_key 来自产品条码的规范化大写值,是外部系统最小商品身份。
  • external.product_assets_v1 和 external.product_bom_v1 也必须携带同一套业务键,避免外部系统为了关联素材或 BOM 被迫使用内部技术 ID。
  • external.product_catalog_wide_v1 在同一业务键口径上聚合正式 SKU,以及已进入 ready 状态但尚未正式落库的 non-integrated SKU;素材数量、BOM 摘要、仓库、质检和限价摘要优先按 barcode_business_key 关联。

3.4 非集成开品资料

非集成开品数据集中在 IntakeNonIntegratedProduct。

字段 定位
id 内部产品记录 ID
spu / spuBase 产品编码和基础编码
brand 品牌
designer / operator / purchaser 人员字段
visibility / departmentId / teamId 权限范围
payload 大型业务资料快照

payload 包含 SKU、附件、BOM、空间布局、审批状态、文档工作区、权限确认、临时 UI 状态等大量内容。它是当前最大风险点,不能直接开放给外部系统。

当前治理口径:

  • IntakeNonIntegratedSkuItem 是非集成 SKU 明细的结构化承接表。
  • payload.skuItems 作为历史过程快照保留,用于同步差异巡检,不作为对外主数据来源。
  • IntakeNonIntegratedProductProfile 承接产品名称、主图、品类、销售分类、年份、状态、开发类型、货品类型、材质类型、生产类型、主要负责人等产品头主数据。
  • IntakeNonIntegratedProductLimitRow 承接产品限价行,来源是 productLimitRows 的结构化同步副本,不允许外部系统直接读取 payload 推断限价。
  • external.intake_products_v1 读取 IntakeNonIntegratedProductProfile,不再直接从 payload / openingReference 读取产品头主数据。
  • external.intake_product_limits_v1 读取 IntakeNonIntegratedProductLimitRow;存量商品明细 默认限价不适用,产品开发明细 和 模块化产品明细 到 产品限价 节点确认后才视为正式限价。
  • external.intake_products_v1 / external.intake_sku_items_v1 显式输出稳定业务键:product_business_key = 品牌:SPU,sku_business_key = 品牌:SPU:SKU编码;这些键用于产品归组和兼容关联。
  • 非空 skuCode / barcode 由数据库唯一索引兜底;外部正式商品最小身份以条码规范化后的 barcode_business_key 为准。
  • 软删除非集成产品时,后端和数据库触发器都会清理结构化 SKU 明细,避免已删除记录占用编码池。

非集成对外只能做白名单投影,例如:

  • 产品基础:spu、品牌、产品名称、品类、年份、负责人、状态。
  • SKU 明细:图片、型号、SKU 编码、规格名称、条码、尺寸、颜色、材质、版本号、供应商、成本等。
  • BOM 摘要:只开放已确认的物料编码、名称、规格、数量、单位、成本字段。
  • 素材:只开放可访问 URL、文件名、类型、大小,不开放内部路径和权限状态。

4. 当前主要风险

风险 1:结构化表和 JSON payload 并存

正式产品链较结构化,非集成开品链大量依赖 payload Json。这不是马上不可用,但对外开放时风险很高。

治理要求:

  • 外部系统不得直接读取整段 payload。
  • 必须先沉淀白名单字段。
  • 高频外部依赖字段应逐步从 payload 治理为结构化列或稳定视图字段。

风险 2:同一业务概念存在多个位置

图片、型号、规格码、条码、尺寸、颜色、材质可能同时出现在资产中心、草稿准 SKU、正式 SKU 快照、非集成 payload 中。

治理要求:

  • 资产源数据以 AssetVariant 为准。
  • 草稿准 SKU 基础资料以 PreSkuBasicInfo 为准,displaySummary 只是展示兼容快照。
  • 正式产品对外以 ProductSKU 的结构化字段和 ProductSkuAssetProfile 副本为准。
  • 非集成开品对外以 IntakeNonIntegratedProductProfile 和 IntakeNonIntegratedSkuItem 的白名单投影结果为准。
  • 页面展示字段不能反过来成为外部系统真相源。
  • 内部兼容快照读点必须持续通过 compatibility-snapshot-read 专项盘点,确保新增 DTO、Repository 或页面 helper 不会绕开结构化字段重新读取 JSON 快照。

风险 3:内部流程字段泄露

payload 内含审批、确认、上传、文档工作区、权限相关状态。直接开放会把内部流程暴露给外部系统。

治理要求:

  • 外部契约层默认排除权限、审批、草稿、删除、锁定原因、内部人员 ID 等字段。
  • 需要开放状态时,只开放业务状态摘要,例如 source_status、product_status、sku_status。

风险 4:素材 URL 混合来源

当前素材来源可能是 S3/MinIO URL、内部 API URL、旧 public 路径或历史 data URL。

治理要求:

  • 外部契约层输出统一的 asset_url 字段。
  • 内部路径、storageKey 默认不对外开放;如外部系统确需同步文件,可单独开放 asset_key,并配合签名 URL 或下载 API。
  • 历史 data URL 需要逐步迁移成文件 URL。

风险 5:外部系统直接绑定内部表结构

如果直接给外部系统 SELECT 原始表,一旦内部迁移字段、改 JSON 结构或拆表,外部系统会一起故障。

治理要求:

  • 新建独立 external schema。
  • 所有外部读取只走 external.*_v1 视图或后端只读 API。
  • 重大变更新增 v2,保留 v1 兼容窗口。

5. 外部只读契约层建议

推荐 PostgreSQL 中新增独立 schema:

CREATE SCHEMA IF NOT EXISTS external;

第一批建议视图:

视图 用途
external.product_skus_v1 正式产品 SKU 主数据
external.product_catalog_wide_v1 一条码一行的商品目录聚合宽表,覆盖正式条码与可消费的 non-integrated ready 条码,以及产品资料、开发阶段负责人、限价、库存库位摘要、包装库存摘要和最新质检摘要
external.intake_product_limits_v1 非集成产品限价白名单
external.warehouse_locations_v1 仓库库位资料
external.sku_warehouse_stock_v1 正式 SKU 库存和库位
external.packaged_items_v1 已关联正式 SKU 的包装库存项
external.sku_quality_inspections_v1 SKU 仓库/首批质检记录
external.product_assets_v1 SKU 图片、模型、资产来源、素材引用
external.product_bom_v1 正式 SKU 的板材、组件、五金摘要
external.intake_products_v1 非集成开品主信息白名单
external.intake_sku_items_v1 非集成 SKU 明细白名单
external.materials_v1 花色/材质基础资料
external.asset_variants_v1 可被正式产品引用的资产规格款

契约层允许保留内部技术 ID 作为追踪字段,但外部主同步必须优先使用 barcode_business_key;product_business_key 用于 SPU/型号归组,sku_business_key 用于 SKU 编码兼容关联。仓库库位优先使用 warehouse_location_code,质检记录自身去重使用 record_key。独立 SQL 巡检和 npm run db:verify-master-data:all 都会检查核心外部视图的业务键缺失或重复风险。

只读账号授权原则:

GRANT USAGE ON SCHEMA external TO external_core_reader;
GRANT SELECT ON external.product_catalog_wide_v1 TO external_core_reader;
GRANT SELECT ON external.intake_product_limits_v1 TO external_core_reader;
GRANT SELECT ON external.warehouse_locations_v1 TO external_core_reader;
GRANT SELECT ON external.sku_warehouse_stock_v1 TO external_core_reader;
GRANT SELECT ON external.packaged_items_v1 TO external_core_reader;
GRANT SELECT ON external.sku_quality_inspections_v1 TO external_core_reader;

不要给外部账号使用 SELECT ON ALL TABLES IN SCHEMA external 这类全量授权;新增外部视图必须单独评审后追加白名单。

禁止:

  • 给外部账号授权 public schema 全表读取。
  • 给外部账号授权业务原表。
  • 给外部账号写权限。
  • 把数据库超级用户、应用用户连接串给外部系统。

6. 治理优先级

P0:先定契约,不先大改表

先完成数据字典和视图草案。不要在真相源没定前重构所有表。

产出:

  • 核心表职责清单。
  • 对外字段白名单。
  • 第一版 external 视图 SQL 草案。
  • 只读账号权限方案。

P1:正式产品链结构化稳定

把外部系统最需要的数据优先从 Product / ProductSKU / AssetVariant / Material / Hardware 组合成稳定视图。

重点字段:

  • SPU/型号、SKU 编码、规格名称、规格码、条码。
  • 图片、3D 模型。
  • 尺寸、颜色、材质。
  • 重量、成本、供应商。
  • 来源资产 ID、来源状态、同步时间。

P2:非集成 payload 白名单投影

只对外投影确认后的产品资料和 SKU 明细。不要把 payload 的流程数据整体暴露。

重点字段:

  • openingReference 中的产品基础资料。
  • skuItems 中的 SKU 明细。
  • 已确认 BOM 摘要。
  • 附件 URL 摘要。

P3:逐步治理高频字段

如果某些字段长期被多个系统依赖,就不应该永远藏在 JSON 里。后续应逐步结构化。

候选字段:

  • 产品正式编码。
  • SKU 条码。
  • 规格码和版本号。
  • 主图 URL。
  • 材质、颜色、尺寸。
  • 供应商编码。
  • 成本、重量。

7. 当前不建议做的事

  • 不建议马上把所有 JSON 字段拆表,成本高且容易影响现有业务。
  • 不建议直接让外部系统读 IntakeNonIntegratedProduct.payload。
  • 不建议把素材二进制塞进 PostgreSQL。
  • 不建议通过共享应用数据库账号解决只读需求。
  • 不建议先开放再补契约文档。

8. 下一步执行清单

  1. 输出第一版外部字段字典。
  2. 写并迁移 external.product_skus_v1 / product_assets_v1 / product_bom_v1 / intake_products_v1 / intake_sku_items_v1 等 SQL 视图。
  3. 在测试库验证视图字段、稳定业务键和样例数据。
  4. 建只读账号权限脚本。
  5. 明确素材访问方式:公开 URL、登录态 API、签名 URL 三选一。
  6. 和外部系统确认字段命名、增量同步方式、删除/失效状态口径。
  7. 再决定是否把高频 JSON 字段结构化。
  8. 部署后运行 scripts/sql/check-external-readonly-contract.sql,确认视图、字段、授权和关键数据质量。

9. 规范审查

结论:passed

这是自审,不是独立复审。

  • 架构:文档明确了主数据真相源、外部契约层和只读账号边界,没有破坏现有分层。
  • 视觉:本次不涉及前端视觉。
  • 根因:没有用“直接开放表权限”做表面处理,而是先治理内部数据库地基和对外契约。