业务架构 · supplier

ARCH

业务架构以代码为锚:文档头里的四端签名一变,保鲜闸就逼人回看这份设计。

版本
0.2.4
状态
active
business
引用规范
["x-api", "x-data", "x-file", "x-address", "x-map", "x-curd", "x-detail", "x-auth", "x-order", "x-exception"]
真相源
true

真相源:.claude/arch/business/supplier/ARCH.md · 本页是它的构建产物,改内容改那份文件、重新构建即更新

供给方(supplier)业务架构

本文档为供给方域的业务架构真相源。技术契约细节(zod/service/repo 模板、CQRS 读写模板、ErrorCode、CURD/Detail 骨架、地址三元组契约、文件令牌链、拖拽 reorder)不在此复述,以 specRefs 指向的 x-api / x-data / x-file / x-address / x-map / x-curd / x-detail / x-auth / x-order / x-exception 十 spec 为准(单一真相源,防第二真相源)。本文只讲架构、双聚合、供给方生命周期、CQRS 数据流、四端消费、上下游边界、诚实风险。所有断言均带现网 file:行号 证据。
真相源:全部结论基于四端真实源码 file:行号实证 + 两次 CloudBase MCP 知识库核实(供应商地址 geocode 走通用能力 / 证书图 cloudID getTempFileURL 官方背书,均无供给方专属注意点)。
⚠️ 双聚合根:本域是双聚合根合并域——supplier_base(法人主体·主聚合,挂 contact/certificate/brand 三子表)+ brand_base(消费者认知贴牌·次聚合,有自身 CRUD + parentBrandId 自树),靠 supplier_brand_relation(纯 2 字段绑定表)缝合。二者是"谁在供货 / 贴牌"的一体两面,非两个独立限界上下文(详见 §一、§六)。

一、领域边界与全景架构图

1.1 Bounded Context 定义

供给方是电商域的上游供给侧主数据域(Upstream Supply-side Master Data),其边界职责唯一:把「谁在提供服务(法人供应商 supplier)」与「贴什么品牌交付(消费者认知 brand)」建模为两个可维护的主数据聚合,并通过多对多绑定表缝合成一个「供给方」概念,向商品核心(spu)供给 SUP/BRD 多态引用锚点

双聚合语义是理解全域的钥匙(brand_base.md:5,49,187,228-249):

聚合主键/前缀语义下游消费视角
供应商(法人主体)supplier_basesupplierId / SUP一个可派单/供货的法人实体(工商信息+服务能力+合作管理)Offer supplierId SUP 前缀多态 · 小厂直供场景
品牌(消费者认知贴牌)brand_basebrandId / BRD消费者认知的品牌方(可自引用真父子品牌树,小米→米家)Offer supplierId BRD 前缀多态 · 大厂品牌交付场景
法人↔品牌绑定supplier_brand_relation_id谁的法人主体持有哪些品牌(纯 2 字段 ID 集合)大厂 SPU 绑 BRD,可经此表反查法人 supplier

**边界铁律(brand_base.md:5,49,228-249)**:parentBrandId 只表达消费者认知的真父子品牌树(小米→米家);集团法人控股(海尔智家→海尔/卡萨帝)禁用父子,由 supplier_brand_relation 承载。brand=消费者认知贴牌,supplier=法人主体,两轴分离,靠关系表缝合。

1.2 四端全景架构图

                     上游主数据边界(Upstream · 旁系,本域不直接依赖)
   ┌──────────────────────────────────────────────────────────────────────┐
   │ category_base(商品分类主数据)—— 与供给方无直接 FK;两条 FK 轴在 spu 层汇合 │
   └────────────────────────────────────────────────────────────────────────┘

  ┌════════════════════════════════════════════════════════════════════════┐
  ║          供给方域(DATA 真相源 · 5 表族均已发布 2026-06-12)              ║
  ║                                                                          ║
  ║  supplier_base(主聚合根 · supplierId/SUP · 40业务字段 · 双地址三元组)    ║
  ║      │1                                                                   ║
  ║      ├──N→ supplier_contact_node   (联系人 · SCON · 12字段 · orderNo拖拽)║
  ║      ├──N→ supplier_certificate_node(资质证照 · SCRT · 13字段 · orderNo拖拽║
  ║      │                                · attachmentUrl cloudID)           ║
  ║      │N                                                                   ║
  ║      ▼  supplier_brand_relation(纯2字段ID集合 · uniq_supplier_brand)     ║
  ║      ▲     ↑ v1.1 索引层 DDL 补建 UNIQUE(模型层解耦,线上曾缺,§⑧)       ║
  ║      │N                                                                   ║
  ║  brand_base(次聚合根 · brandId/BRD · 15业务字段 · parentBrandId 自树)    ║
  ║      └──self──▶ parentBrandId(仅真品牌延伸,禁集团控股)                  ║
  ╚══════════════════════════════════════════════════════════════════════════╝
                   │ camelCase 全链路透传(数据透传原则)
        ┌──────────┴───────────────────────┐
        ▼                                   ▼
  ┌─────────────────────────────┐   ┌──────────────────────────────────────┐
  │ SERVER(CQRS 读写二分)        │   │ 地址派生边界(geocode 在 admin BFF)    │
  │ 写:ORM(create+自增回查)      │   │ server 零 geocode,三字段纯字符串透传    │
  │ 读:runReadSQL/$runSQL(MySQL) │   │ registered/office 双组各一次           │
  │ supplier-read/create/update/ │   │  geocodeAddressTriple(admin 前端)     │
  │  delete/order-update ·       │   └──────────────────────────────────────┘
  │  info-read · import/export · │
  │ contacts×4 · certificates×4 ·│
  │ brands-bind/unbind/read ·    │
  │ brand-create/update/delete/  │──(反向读 spu-suppliers-read 归 ecommerce/spu/ARCH.md)──▶
  │  read(树·手工排序)·         │
  │  import/export·status-toggle │
  └────────┬─────────────────────┘
           ▼
  ┌──────────────────┐  ┌────────────────────────────────────────────┐
  │ ADMIN(管理台)    │  │ CLIENT(微信小程序)                         │
  │ supplier.tsx      │  │  ✗ 无独立供给方模块                          │
  │  MasterList+Detail│  │  供给方展示名(providerType/shopName)由      │
  │  ├ contacts(inline)│ │  spu 商品核心 service-browse-read            │
  │  ├ certs(inline)  │  │  loadSupplierMap 多态派生(归 ecommerce/spu/ARCH.md)        │
  │  └ brands(Transfer)│ │  goods-own-adapter.js:150 仅取 shopName       │
  │ brand.tsx (CURD树) │  │  (client 从不直接读 supplier_base/brand_base)│
  └──────────────────┘  └────────────────────────────────────────────┘
           │
           ▼
  ┌────────────────────────────────────────────────────────────────┐
  │  下游供给锚点(Downstream · SUP/BRD 多态引用 · 单向供给)          │
  │  spu_supplier_relation.supplierId  ^(SUP|BRD)\d{29}$(接缝)      │
  │  ├─► sku_maintenance_node.supplierId(Offer 供给单元 · 商品核心)  │
  │  ├─► order_base.supplierId(订单快照冻结 · 订单域)               │
  │  └─► sop_step_node((spuId,supplierId) 联合键 · 工单域)          │
  └────────────────────────────────────────────────────────────────┘

数据流方向要点


二、数据模型(实体与聚合)

2.1 supplier_base — 供应商主聚合根(40 业务字段 + 8 系统字段,已发布 2026-06-12)

三大字段区(supplier_base.md:11-53):

代表字段出处
工商信息fullName / creditCode / legalPerson / companyType(enum) / companyScale(enum) / registeredCapital / foundDate / licenseExpirymd:11-24
服务能力serviceCategories / serviceMode(enum) / coverageArea(*Area 多选) / staffCount / dailyCapacity / responseTime(enum) / equipmentListmd:31-45
合作管理cooperationStatus(enum) / supplierLevel(enum) / supplierRating(enum) / supplierSource(enum) / dispatchPriority(enum) / handler / regionalManagermd:46-53
维度出处
主键supplierId 自动编号 SUP{DATETIMEUTC:yyyyMMddHHmmss}{SEQNUM:15} / varchar32 · maxLength32md:13 / json:141-156 / 主数据ID清单.md:346-360
required(8)fullName, shortName, orderNo, companyType, serviceCategories, serviceMode, coverageArea, cooperationStatusjson:22-31
枚举字段(9)companyType/companyScale/serviceMode/responseTime/cooperationStatus/supplierLevel/supplierRating/supplierSource/dispatchPriority(各 x-option-name)json 内 x-option-name 恰 9 次
排序orderNo x-system:true = 拖拽模式(白名单 supplier-order-update 驱动)json:252-260 类比
索引PK supplierId + 6 单列 IDX(fullName/shortName/companyType/serviceMode/cooperationStatus/orderNo)md:60-61
唯一约束仅主键唯一(无其他单列/联合 UNIQUE)json:154

2.2 brand_base — 品牌次聚合根(15 业务字段 + 8 系统字段,已发布 2026-06-12)

维度出处
主键brandId 自动编号 BRD{DATETIMEUTC:yyyyMMddHHmmss}{SEQNUM:15} / varchar32 · maxLength32md:13 / json:138-152 / 主数据ID清单.md:156-170
15 业务字段brandName/brandNameEn/brandAlias/brandDescription/brandOrigin/parentBrandId/brandType/brandLogo/remark/officialWebsite/authStatus/licenseImage/licenseExpireDate/status/orderNomd:13-28
required(5)brandName, brandType, authStatus, status, orderNojson:22-28
自关联父 FKparentBrandId varchar32 自引用 brandId(仅真品牌延伸,禁集团控股)md:19,48-49 / json:179-186
排序orderNo 无 x-system = 手工模式(前端 ProFormDigit 输入,无 order-update 云函数)json:266-272
索引PK + 5 单列 IDX + 组合索引 idxTypeStatus(brandType,status)md:34-36
唯一约束仅主键唯一json:151

2.3 supplier_contact_node — 联系人子表(12 业务字段,已发布 2026-06-12)

2.4 supplier_certificate_node — 资质证照子表(13 业务字段,已发布 2026-06-12)

2.5 supplier_brand_relation — 法人↔品牌绑定层(纯 2 FK 零业务字段,已发布 2026-06-12)

象限字段出处
FKsupplierId(→supplier_base.supplierId)md:13 / json:140-151
FKbrandId(→brand_base.brandId)md:14 / json:152-163
系统_id/createdAt/updatedAt/owner/createBy/updateBy/_mainDep/_openidjson:27-139
业务字段(纯 ID 集合,无 supplierName/logo 冗余)

2.6 地址三元组(仅 supplier_base 承载 · 双组 6 字段)

brand / contact / certificate 三表均无地址字段。supplier_base 双组:

registered 组(注册地址):
  registeredRegion (region · varchar+x-json)    ──▶ {codes,label}   AreaCascader
  registeredDetail (detail · varchar255)         ──▶ 门牌手填          textarea
  registeredCoordinate (coordinate · varchar128) ──▶ {lat,lng} GCJ02   BFF 零录入派生
office 组(办公地址):
  officeRegion / officeDetail / officeCoordinate  同上三角色
              ↑ x-address-group:registered/office 各三角色成套(标记驱动,x-address NS-DATA-0001)

2.7 文件字段(array cloudID · 全符合 x-data file 铁律)

type:array + items:{type:string} + x-storage-type:cloudID(无 format,物理 json 列):

字段出处
supplier_baselogo(供应商 Logo)md:16 / json:175-183
brand_basebrandLogo(品牌 Logo,文件 ID 前缀 {brandId}LOGO{...} 路径 wowo/brand/logos/)md:21 / json:202-210 / 主数据ID清单.md:1129-1147
brand_baselicenseImage(授权证书)md:25 / json:237-245
supplier_certificate_nodeattachmentUrl(证照附件)md:23 / json:231-239

2.8 关键字段语义


三、供给方生命周期

 建供应商 ──▶ 双地址 geocode ──▶ 联系人/证书 ──▶ 绑品牌 ──▶ 拖拽排序
    │            │                  │              │           │
 supplier-    admin BFF          supplier-       supplier-   supplier-
 create       geocodeAddress     contacts-       brands-bind  order-update
 (MasterList  Triple×2           create          (Transfer    (reorderByPk
  内联仅       (registered+       supplier-       穿梭框·查重)  CASE WHEN)
  fullName+    office 各一次)     certificates-               contacts/certs
  shortName)   坐标派生           create                       各自 order-update
    │                            (attachmentUrl 单图)
    └─▶ cooperationStatus/level/rating 合作管理
                                                 brand 独立线:
                                                 brand-create(手工 orderNo)
                                                 → parentBrandId 挂真父子树

① 建供应商:MasterList 内联 create 仅 fullName+shortName 两字段起建(supplier.tsx:663-684),随后 Detail 补全其余 40 业务字段。create 后 ORM 回查拿自增 supplierId(supplier-create/repo.js:65-68)。

② 双地址 geocode:admin BFF 对 registered + office 各调一次 geocodeAddressTriplesupplier.bff.ts:232-237),派生 coordinate;serializeAreaFields(['registeredRegion','officeRegion']) 序列化 region object→JSON(supplier.bff.ts:239)。server 零 geocode(supplier-update/controller.js:26-31)。

③ 联系人/证书:inline-edit 子表行内新增(NEW_ 前缀),create 时 appendNextOrderNo(db, table, {supplierId}) 按父分组初始化排序(supplier-contacts-create/repo.js:32 / supplier-certificates-create/repo.js:33);证书 attachmentUrl 单图 create 分支先建后传再 update 回填(supplier.bff.ts:134-156)。

④ 绑品牌:Transfer 穿梭框(relation-select)受控返回变更集 → BFF supplier-brands-bindqueryExistingBindings 查重再 batchBind(createMany 分片,supplier-brands-bind/service.js:6-12);unbind 走 batchUnbind(deleteMany,supplier-brands-unbind/repo.js:5-27)。

⑤ 拖拽排序:supplier/contacts/certificates 三表拖拽 → reorderByPk(db, table, pkField, orderedIds) → sys $runSQL CASE WHEN 批量重写 orderNo(supplier-order-update/repo.js:16 等);brand 无拖拽,手工 ProFormDigit 输入 orderNo(brand.tsx:518-524)。


四、CQRS 数据流(读 runReadSQL/$runSQL · 写 ORM)

总体二分:读端一律声明式 runReadSQL(STD-SERVER-0008)或裸 $runSQL(STD-SERVER-0010);写端一律 ORM(create 后回查拿自动编号主键)。describes.server glob 下共 28 个 live 云函数(supplier 21 + brand 7,全 controller/service 真实函数,非空模板):下表列举主表 / 关系 / 读导出等主 CQRS 函数,contacts / certificates 子表各 4 个写函数(create / delete / update / order-update)见 §三 生命周期 + §5.1 权限声明。

云函数读/写数据层实现(证据)
supplier-readadmin读·单表runReadSQL(mode:'list') JSON_FIELDS=[logo,registeredRegion,officeRegion],orderBy orderNo ASCsupplier-read/repo.js:44-52
supplier-info-readadmin读·单查runReadSQL(mode:'one', uniqueKey:'supplierId') findBySupplierId,JSON_FIELDS=[logo,registeredRegion,officeRegion],未命中抛 SUPPLIER_NOT_FOUND(repo.js:12-19 / service.js:9-10)——供应商详情读,即 §5.1 supplierDetailRead BFF 的服务端
supplier-create/updateadminORM create+回查(supplier-create/repo.js:65-68)/ updateMany(supplier-update/repo.js:69-72);update service resolveFileValue+x-file STD-SERVER-0007 三阶段差集清理(logo:READ diffRemovedFiles→MUTATE→CLEANUP deleteFiles,supplier-update/service.js:5-23
supplier-deleteadmin写·三阶段READ(级联读 supplier logo + 证书 attachment,collectCloudUrls(records,['logo'])/(['attachmentUrl'])repo.js:29,52)→ MUTATE deleteMany BATCH_SIZE=200 → CLEANUP deleteFiles 委托 sys(service.js:6-25
supplier-order-updateadmin写·拖拽reorderByPk(db,'supplier_base','supplierId',orderedIds)repo.js:16
supplier-importadmin写·导入service 复合实体编排:brands 字符串数组经 resolveBrandIdsByName 反查 brandId → importOne(create/update + brandIds 写关联表,service.js:7-14;返回模板归 x-api STD-SERVER-0003)
supplier-exportadmin读·导出编排exportAll → buildSupplierExport:主表 runReadSQL list + 3 子表 $runSQL IN 并行 + brandName 反查 + 内存挂载 contacts/certificates/brands(repo.js:62-104,分组按 supplierIds 全集预建空数组,取值处零兜底直取,STD-SERVER-0006)
supplier-contacts-readadmin读·单表runReadSQL(mode:'list'),buildOrderBy 返回 ORDER BY orderNo ASC(拖拽序生效,repo.js:13-14
supplier-certificates-readadmin读·单表同上,buildOrderBy ORDER BY orderNo ASC,JSON_FIELDS=[attachmentUrl](repo.js:17-18/25
brand-readadmin读·树buildBrandTree = runReadSQL(mode:'list') 全量 + 内存 normalizeRecord/filterRecords/buildTree/sortTree(消 while),JSON_FIELDS=[brandLogo,licenseImage,brandType](brand-read/repo.js:84-94
brand-create/update/deleteadminORM;update service resolveFileValue+x-file STD-SERVER-0007 三阶段差集清理双字段(brandLogo+licenseImage,brand-update/service.js:5-27);delete 三阶段(brand-delete/service.js:12-27
brand-status-toggleadmin写·状态切换service updateManyStatus(brandIds, status) 批量(service.js:4-7)——即 §5.2 toggleStatus 的服务端(brandIds 数组 + status 批量重写)
brand-importadmin写·导入service importOne(params, stats)(create/update 统计,service.js:5;返回模板归 x-api STD-SERVER-0003)
brand-exportadmin读·导出编排exportAll → buildBrandExport:runReadSQL list 全量 + normalize/filter 扁平(不建树),JSON_FIELDS=[brandLogo,licenseImage,brandType](repo.js:75-79,STD-SERVER-0006)
supplier-brands-readadmin读·JOIN$runSQL JOIN supplier_brand_relation × brand_base 投 14 列,双守卫 + length>2000 溢出 + parseJsonColumns([brandLogo,brandType])ORDER BY b.brandName ASCrepo.js:8-30
supplier-brands-bind/unbindadminbind 查重 + createMany 分片 / unbind deleteMany(supplier-brands-bind/repo.js:5-25

反向读归属声明spu-suppliers-readx-server/admin/ecommerce/spu-supplier/,多态 CASE JOIN brand_base/supplier_base 派生 supplierName/logo)归 ecommerce/spu/ARCH.md(describes.server 含 x-server/admin/ecommerce/spu-supplier/**),本域 describes 不纳入,避免双真相源。

文件生命周期二态(x-file)


五、四端消费

5.1 ADMIN 端 — supplier(MasterList + Detail 双栏)

模块位于 x-admin/src/xModules/supplier/顶层,非 ecommerce 子目录)。

a) 页面形态supplier.tsx:629-815):.x-page-layout() + flex-direction:rowsupplier.less:3-9)master-detail 双栏:

b) 嵌套子表(DetailTable inline-edit,非 DetailSteps)supplier.tsx:527-615):

c) 品牌关联(Transfer 穿梭框 relation-select)supplier.tsx:529-544):dataKey brands / idKey brandId / brandFields 11 列 / fileFields[brandLogo] / selectComponent SupplierBrandSelectAdapter / permissions{read,bind,unbind}。适配器(:317-331)→ <SupplierBrandTransfer>supplier-brand-transfer.tsx:106-128<Transfer<BrandTransferItem>> 受控,只 onChange 返回变更集不直调 bind/unbind;候选用 brandReadFlat tree→flat 修子品牌不可见 BUG,:48-60)。bind/unbind 真调延迟到 Detail 保存的 BFF(supplier.bff.ts:175-185);契约守卫 brands.mode!=='relation-select' → XSystemError(:254-258)。

d) 地址消费:register/office 双组 DetailSection fieldType:'address-triple':91-101 span3 / :226-236);coverageArea 内联 <AreaCascader mode="multiple">:241-257,getValueProps/getValueFromEvent JSON 双向,只读 areaValueToLabel);预加载 loadDistrictTree():622-624)。无 AddressWithMap/MapPreviewModal(走 address-triple 声明式 + BFF geocode,x-map Admin 端 Phase 2 迁移中)。

e) 文件:logo Detail preset(:789-790)+ resolveAdapterFiles(supplier.bff.ts:218-225);certificate attachmentUrl 单图 BFF 手工编排(:132-173);读令牌 resolveFileToken 3 处(:68-70)。全单图无图墙

f) AUTH_METAsupplier.tsx:48-75 声明供给方全操作 func(base 主操作 + contacts + certificates + brands 关联)——**逐条以 supplier.tsx:48-75 源码为准**(func 声明 X-RULE 标记 + AUTH_META.xxx.id 消费闭环由 x-auth NS-ADMIN-0004/0005 管辖)。

5.2 ADMIN 端 — brand(CURD 树形 + 手工 orderNo)

模块 x-admin/src/xModules/brand/(顶层,平级 supplier)。brand.tsx:349-1101 单栏 <Curd<BrandItem>>(x-curd STD-ADMIN-0001):

5.3 类型契约(x-type @x-data-table 标记)

类型@x-data-table出处
SupplierBasesupplier_basesupplier.types.ts:24-28
SupplierContactsupplier_contact_nodesupplier.types.ts:81-85
SupplierCertificatesupplier_certificate_nodesupplier.types.ts:108-112
BrandItembrand_basebrand.types.ts:24-29
SupplierBrand / SupplierBrandTransfer / BrandTransferItem无标记(派生 VO)supplier.types.ts:541-558 等

5.4 CLIENT 端 — 无独立供给方模块


六、上下游 Context Map(DDD)

              上游主数据(旁系 · 本域不直接依赖)
   ┌─────────────────────────────────────────────┐
   │ category_base(商品分类)—— 与供给方无直接 FK  │
   │ 两条 FK 轴(spu.categoryId 分类 · spu-supplier │
   │ .supplierId 供给)在 spu 层才汇合              │
   └──────────────────┬──────────────────────────┘
                      │(无引用箭头指向供给方)
   ╔══════════════════▼══════════════════════════════════════╗
   ║          供给方域(本域 · 真相源 · 双聚合根)              ║
   ║  supplier_base ─1:N→ contact_node / certificate_node     ║
   ║  supplier_base ─N:M(supplier_brand_relation)N:M─ brand_base ║
   ║  brand_base ─self→ parentBrandId(真父子品牌树)          ║
   ╚═══════════════════════════┬═════════════════════════════╝
        spu_supplier_relation.supplierId  ^(SUP|BRD)\d{29}$(唯一接缝)
   ┌──────────────────┬────────┴───────────┬──────────────────────┐
   ▼                  ▼                     ▼                      ▼
┌────────────────┐ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────┐
│ spu 商品核心    │ │ sku_maintenance_ │ │ order 订单域      │ │ sop 工单域    │
│ spu_supplier_  │ │ node(Offer供给   │ │ order_base.       │ │ sop_step_node │
│ relation(下游  │ │ 单元).supplierId │ │ supplierId 多态   │ │ (spuId,       │
│ 接缝·多态锚点) │ │ 多态·(spuId,     │ │ 快照冻结          │ │  supplierId)  │
│ Upstream       │ │ supplierId)UNIQUE│ │                  │ │ 联合键        │
│ Supplier       │ │                  │ │                  │ │              │
└────────────────┘ └──────────────────┘ └──────────────────┘ └──────────────┘
关系方向/模式契约(证据)
category → 供给方旁系·无直接引用category 经 spu_base.categoryId(spu_base.md:17,121)进商品树,与 supplier/brand 在 spu 层汇合,两条 FK 轴独立;供给方域不依赖 category
供给方 → spu 商品核心下游·Upstream Supplier唯一接缝 spu_supplier_relation.supplierId 多态 ^(SUP|BRD)\d{29}$spu_supplier_relation.md:14,32,137-143);大厂 SPU 绑 BRD,可经 supplier_brand_relation 反查法人 supplier(md:151);ecommerce/spu/ARCH.md §六 已把 supplier/brand 标为"上游供给方·未来独立 arch"——本域正是那个 arch,两文档边界自洽衔接
供给方 → 商品核心 Offer下游·多态sku_maintenance_node.supplierId 多态,业务键 (spuId,supplierId) UNIQUE 单条定位 Offer(sku_maintenance_node.md:15,29,38,41)
供给方 → order下游·快照冻结order_base.supplierId 多态(引用锚点之一),订单快照冻结(order_base.md:20,123,143)
供给方 → sop下游·联合键sop_step_node 经 (spuId,supplierId) 联合键关联(spu_supplier_relation.md:99)
brand ↔ supplier(域内)多对多supplier_brand_relation 唯一显式桥(纯 ID 集合,防集团控股误建父子)

未落地下游(诚实标注)spu_base.brandId 暂未实现(brand_base.md:184 明示"待业务建模",grep 证实 spu_base.md 无 brandId 字段);service_base.brandId 亦未建表(brand_base.md:185)。brand 当前只经 spu_supplier_relation 的 BRD 多态间接下游。


七、specRefs(只引不复述,逐条标管辖)

规范归类管辖(它管什么)本域证据
x-api🟢核心CQRS 读写模板(STD-SERVER-0008 单表 runReadSQL / STD-0010 $runSQL JOIN·树 / 写端 ORM)、原子服务 STD-ADMIN-0001、BFF STD-ADMIN-0004supplier-read 单表 / supplier-brands-read JOIN / brand-read 树 / supplier.bff.ts
x-data🟢核心主表/嵌套/关系表 STD-DATA-0001/0002/0004、发布闸、场景B 演化5 表族均已发布 2026-06-12(含 relation v1.1 索引层 DDL 补 uniq)
x-file🟢核心array(cloudID) 生命周期、xfile: 令牌化、resolveFileValue/diffRemovedFiles、STD-SERVER-0002/0007 三阶段supplier.logo + certificate.attachmentUrl + brand.brandLogo/licenseImage
x-address🟢核心地址三元组契约(NS-DATA-0001 标记驱动 region+detail+coordinate)、region 读链路 objectsupplier_base 双组 registered/office 各三字段成套
x-map🟢核心geocode SN 签名能力(STD-SERVER-0001 map-geocode-read)双地址组 BFF geocodeAddressTriple 派生坐标(能力归 x-map,数据归 x-address)
x-curd🟢核心列表 CURD 骨架、树形 CURDbrand.tsx CURD 树形模式
x-detail🟢核心Detail 模式(MasterList+Detail 四向绑定)、双模式子表(inline-edit / relation-select)、位置编码supplier.tsx 是 x-detail 双模式子表标杆(contacts/certificates=inline-edit,brands=relation-select),x-detail-rule 全程以 supplier 为例
x-auth🟢核心AUTH_META func 声明、L2 func 门、聚合读放行supplier / brand 两侧 AUTH_META;supplier-brands-read 聚合读
x-order🟢核心(非仅豁免拖拽模式(x-system:true + reorderByPk)+ 手工模式(非 x-system)supplier/contacts/certificates 真拖拽(supplier-order-update 等);brand_base 手工 orderNo——供给方内部两范式并存
x-exception🟢核心dto ErrorCode 枚举、ServerBusinessError、三级异常supplier/brand 两侧 *-delete errors.js + 绑定校验

明确排除(诚实,防第二真相源,对齐 spu/customer 范式)x-type(跨端通用规范,ecommerce/spu/ARCH.md/customer.md 均未列 specRefs)、x-i18n/x-structure/x-styles/x-logging/x-antd(通用规范每模块都用,不入 specRefs)。


八、诚实边界与风险

#类别描述处置
1~~delete 清理侧数组契约不符~~(已闭环)原风险:supplier_base.logo / brand_base.brandLogo/licenseImage / certificate.attachmentUrl 均 array(cloudID)(string[]),若 delete 清理侧手搓 url.startsWith('cloud://') 作用于数组会 startsWith is not a function 崩溃或恒 false 泄漏。**现网已收敛 sys 原子 collectCloudUrls**:supplier-delete/repo.js:29,52collectCloudUrls(records,['logo']) / (['attachmentUrl']))、brand-delete/repo.js:23(['brandLogo','licenseImage']))、supplier-certificates-delete/repo.js:29(['attachmentUrl'])),业务代码零裸 startsWith(sys/file-storage.js 内的 filter 有 Array.isArray 守卫,非 bug);NS-SERVER-0011 detector 强制此收敛✅ 已闭环(v0.1.1 修复,与版本日志一致,本次 prose 同步去陈旧)
2~~嵌套读排序未用 orderNo~~(已闭环)原风险:两嵌套读 buildOrderBy 返回 createdAt DESC,与拖拽写 orderNo 不一致 → 拖拽序经 read 重载不体现。现网已改supplier-contacts-read/repo.js:13-14 + supplier-certificates-read/repo.js:17-18 buildOrderBy 均 return 'ORDER BY orderNo ASC',与 supplier-read 一致,拖拽序生效✅ 已闭环(v0.1.1 修复,与版本日志一致,本次 prose 同步去陈旧)
3~~bind 查重用裸 Error~~(已闭环)原风险:supplier-brands-bind/service.js bind 查重用裸 throw new Error('…已绑定…') → controllerSystem 兜底退化 SYS_500,中文提示丢失。现网已改service.js:12 throw new ServerBusinessError(ErrorCode.BRAND_ALREADY_BOUND, '以下品牌已绑定…')(v0.1.1 修复,file:12 实证),NS-SERVER-0006 迁移后全域业务代码零裸 Error 保证✅ 已闭环(v0.1.1 + NS-SERVER-0006 双重保障)
4relation 表 uniq 索引曾线上缺失supplier_brand_relation.md 自建即声称 uniq_supplier_brand,但线上 DB 从未建(MCP 核实仅 PRIMARY)→ 防重复绑定物理闸虚设。v1.1(md:83 2026-07-05)已 MCP ALTER TABLE ADD UNIQUE INDEX 补建(索引层与模型层解耦,线上 203 行 0 重复后安全)已闭环;与「文档宣称 UNIQUE 物理闸实际不存在」审计同类
5relation 表数据源漂移json x-primary-column:"supplierId"(json:7)与 .md PK=_id(md:21)矛盾(关系表主键应 _id,误配为 FK);系统字段 x-id 用 snake_case(created_at/owner_id)而三 node 表用 sys_createdAt——命名不统一(早期存量建表遗留)演化走 x-data 场景B 时校正;非功能性阻塞
6brandId 商品下游未建模spu_base.brandId(brand_base.md:184)+ service_base.brandId(185)尚未建表,brand 当前只经 spu_supplier_relation 的 BRD 多态间接下游现状;未来业务建模后补
7反向读归属声明spu-suppliers-read(多态 CASE JOIN)物理在 x-server/admin/ecommerce/spu-supplier/,功能上读供给方,但 describes 归 ecommerce/spu/ARCH.md本域 describes 不纳入,防双真相源
8client 无独立模块供给方展示名 providerType/shopName 由 spu 商品核心 service-browse-read loadSupplierMap 派生,client 从不直接读 supplier_base/brand_basedescribes.client=null;归 ecommerce/spu/ARCH.md(边界纪律)
9发布闸约束5 表族均已发布 2026-06-12,演化必走 x-data 场景B(.md/.json 手工同步,禁 generate-json),已发布字段禁改 format(需五步破坏性迁移)常规约束
10审计边界(诚实声明)全文基于四端静态只读抽取 + file:行号 + 两次 MCP KB 核实;未执行 MCP 线上表结构核对(除 relation uniq 已核,其余 x-unique/字段类型/联合索引与线上 DB 一致性未验证),未跑 -m 规范检测归 x-data-dev verify-db-consistency 运行时核查范畴

边界纪律(arch 层铁律):供给方是纯业务领域。系统机制(认证/CQRS 模板/文件令牌/地址三元组契约/类型契约/异常码/CURD-Detail 骨架/拖拽 reorder)只经 specRefs 引用,绝不在本文复述其条款——跨切面机制归系统架构三件套(spec + rule + 内嵌 detector),本文若复述即成第二真相源,与 detector 逐字比对的真相源分岔。本文只承载:双聚合结构、供给方生命周期、数据流方向、四端消费编排、上下游边界、诚实风险。

相关规范(specRefs,只引用不复述)

规范管辖
x-api-specCQRS 读写模板 STD-SERVER-0008/0010 + 写端 ORM、原子服务 STD-ADMIN-0001、BFF STD-ADMIN-0004
x-data-spec主表/嵌套/关系表 STD-DATA-0001/0002/0004、发布闸、场景B 演化
x-file-specarray(cloudID) 生命周期、xfile: 令牌化、resolveFileValue/diffRemovedFiles、STD-SERVER-0002/0007 三阶段
x-address-specNS-DATA-0001 三元组标记驱动、NS-SERVER-0001/0002、STD-ADMIN-0001 geocodeAddressTriple
x-map-specSTD-SERVER-0001 SN 签名 map-geocode-read geocode 能力
x-curd-spec列表/树形 CURD 骨架
x-detail-specDetail 双模式子表标杆(inline-edit / relation-select)、四向绑定、位置编码
x-auth-specAUTH_META func、L2 func 门、聚合读放行
x-order-spec拖拽模式(x-system + reorderByPk)+ 手工模式(非 x-system)两范式并存
x-exception-specErrorCode 枚举、ServerBusinessError、三级异常

版本日志 → [CHANGELOG.md](./CHANGELOG.md)(本域历史条目已自 frontmatter 迁出)