Files
ai-kefu-admin/app/platform/tenant_config.py
T
浅念 8a16a2959b test: 钉住 deep_merge 的 None 替换与列表替换语义 + 只读约定注释
- characterization:显式 None 整体替换且兄弟键保留默认(Task 9 回退契约依赖);列表整体替换不与默认拼接
- DEFAULT_CONFIG 与 TenantConfig.data/Site.brand 补只读约定注释
- platform 包依赖章程改明确:defaults/tenant_config 为叶子模块供 engine 反向消费,registry 组装层可 import app/engine

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-12 16:59:31 +08:00

38 lines
1.3 KiB
Python

"""租户与站点配置模型。
- TenantConfig.data = deep_merge(DEFAULT_CONFIG, snapshot):租户快照 ⊕ 出厂默认
- Site.brand 的合并基线是**租户级** data["brand"](而非出厂默认),
这样租户级 brand 自定义也能被各站点继承。
"""
from dataclasses import dataclass
from app.platform.defaults import DEFAULT_CONFIG, deep_merge
@dataclass(frozen=True)
class Site:
slug: str
name: str
domains: tuple[str, ...]
brand: dict # 站点门面覆盖层 ⊕ 租户默认 brand(已深合并)。
# 只读约定:构造后不得修改
@dataclass(frozen=True)
class TenantConfig:
slug: str
name: str
status: str # active | disabled
data: dict # deep_merge(DEFAULT_CONFIG, snapshot)。
# 只读约定:构造后不得修改
sites: tuple[Site, ...] = ()
@classmethod
def from_snapshot(cls, slug, name, snapshot, sites=(), status="active"):
merged = deep_merge(DEFAULT_CONFIG, snapshot)
resolved = tuple(
Site(s["slug"], s.get("name", s["slug"]), tuple(s.get("domains", ())),
deep_merge(merged["brand"], s.get("brand", {})))
for s in sites)
return cls(slug=slug, name=name, status=status, data=merged, sites=resolved)