# 系统配置模块 — 数据模型设计说明 **致**:Atlas(架构师) **来自**:Nova(PM) **日期**:2026-04-27 **关联 PRD**:`PRD/系统配置/系统配置模块PRD.md` **关联文档**:`DATA_MODEL/ENUMS.md`、`DATA_MODEL/DATA_MODEL.md` --- ## 一、背景与问题 在设计系统配置模块的数据模型时,我发现当前 `DATA_MODEL/ENUMS.md` 已经明确了「固定枚举」与「可配置枚举」的分层设计,但两者的边界在文档中未完全显式化。本说明文档的目的是: 1. 厘清三类配置数据各自应存在哪张表 2. 指出需要新增的两张表及其建议 DDL 3. 说明与 ENUMS.md 现有设计的关系,以及需要 Atlas 补充/修改的内容 请 Atlas 在完成 `DATA_MODEL/DATA_MODEL.md` 和 `DATA_MODEL/ENUMS.md` 的修订后,同步通知 Nova 确认。 --- ## 二、三类配置数据的划分 系统配置涉及三类性质不同的数据,**分属不同的表和 Schema**,请严格区分: ### 类型 A:固定系统枚举(存 Public Schema / `enum_labels`) **特征**: - 值域固定,所有租户共享同一套 - 与数据库 `CHECK CONSTRAINT` 绑定(如 `decoration IN ('rough','plain','simple','medium','fine','luxury')`) - 只能由平台研发通过 migration 修改 - 租户管理员**无权**增删 **代表字段**:`property.decoration`(装修)、`property.orientation`(朝向)、`property.status`(交易状态)、`client.status`(客源状态)、`client.grade`(客源等级)、`common.gender`、`common.id_type` **现状**:ENUMS.md 已完整定义,`enum_labels` 表 DDL 已存在。**无需改动。** --- ### 类型 B:租户可配置枚举(存 Tenant Schema / `lookup_items`,**需新增**) **特征**: - 各租户选项不同(如来源渠道:A 公司有「抖音」,B 公司没有) - 租户管理员可通过界面增删排序 - 系统预制初始值(`is_system = True`),预制值不可删除但可停用 - **无** `CHECK CONSTRAINT`(值域动态) - 与 `enum_labels` 完全独立,不存在于 Public Schema **代表字段**:客源来源(`client.source`)、跟进目的(`client_follow_logs.follow_purpose`)、房源来源(`property.source`) **ENUMS.md 现状**: - `§2.14 跟进目的` 已明确标注「此枚举为可配置项,存储方式:`lookup_items` 表」,但 `lookup_items` 的 DDL 尚未在任何 DATA_MODEL 文档中定义 - `client.source`(来源)在 ENUMS.md 中未定义(因为它是可配置的),但竞品系统有 50+ 预制来源选项 **需要 Atlas 做的事**: 1. 在 `DATA_MODEL/DATA_MODEL.md` 中新增 `lookup_groups` 和 `lookup_items` 表的 DDL 2. 在 ENUMS.md 中补充一节「可配置枚举说明」,列出哪些 domain 属于 `lookup_items` 而非 `enum_labels` 3. 确认 `apps/setting/` 下新增 `lookup.py` models 文件 **建议 DDL(供参考,Atlas 可调整)**: ```sql -- ============================================================ -- 可配置枚举分组(租户 Schema) -- ============================================================ CREATE TABLE lookup_groups ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), module VARCHAR(50) NOT NULL, -- 'client' | 'property' key VARCHAR(100) NOT NULL, -- 'source' | 'follow_purpose' label_zh VARCHAR(50) NOT NULL, -- 界面显示名称,如「客源来源」 created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE (module, key) ); -- ============================================================ -- 可配置枚举选项(租户 Schema) -- ============================================================ CREATE TABLE lookup_items ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), group_id UUID NOT NULL REFERENCES lookup_groups(id) ON DELETE CASCADE, value VARCHAR(100) NOT NULL, -- 存储值(英文 key,建议 snake_case) label_zh VARCHAR(50) NOT NULL, -- 显示文本 is_system BOOLEAN NOT NULL DEFAULT FALSE, -- True=系统预制,不可删除 is_active BOOLEAN NOT NULL DEFAULT TRUE, sort_order SMALLINT NOT NULL DEFAULT 0, created_by UUID REFERENCES staff(id), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE (group_id, value) ); CREATE INDEX idx_lookup_items_group_active ON lookup_items(group_id, is_active, sort_order); ``` **预制种子数据参考**(需写入 migration fixtures,`is_system = TRUE`): | module | key | 预制选项(部分) | |--------|-----|----------------| | `client` | `source` | 门店接待、老客户转介绍、驻守派单、上门、网络-58同城、网络-安居客、微信、朋友介绍 | | `client` | `follow_purpose` | 回拨、推房、带看、维护、其他 | | `property` | `source` | 主动开发、业主上门、老客户转介绍、网络来电 | --- ### 类型 C:行为规则与开关(存 Tenant Schema,**需新增两张表**) #### C-1:键值配置表 `tenant_settings` **特征**: - 存储开关(bool)、阈值(int)、枚举选择(string)等标量类型配置 - 每个 key 全局唯一,有默认值 - 租户管理员通过界面修改 **建议 DDL**: ```sql -- ============================================================ -- 租户标量配置表(键值对) -- ============================================================ CREATE TABLE tenant_settings ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), category VARCHAR(50) NOT NULL, -- 配置分类:'client' | 'property' | 'showroom' key VARCHAR(100) NOT NULL, -- 配置 key,如 'duplicate_check_scope' value JSONB NOT NULL, -- 存储任意类型(bool/int/str/list) value_type VARCHAR(20) NOT NULL, -- 'bool' | 'int' | 'string' | 'enum'(用于前端渲染) updated_by UUID REFERENCES staff(id), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE (category, key) ); ``` **MVP 阶段需要预置的 key**: | category | key | value_type | 默认值 | 说明 | |----------|-----|-----------|--------|------| | `client` | `duplicate_check_scope` | `enum` | `"self"` | 新增私客查重范围:`self`/`dept`/`company` | #### C-2:字段必填规则表 `field_requirement_rules` **特征**: - 按「模块 × 实体用途 × 交易状态 × 字段」四元组确定一条规则 - 规则值为三态:`required` / `optional` / `hidden` - MVP 仅需支持 `property` 模块 **建议 DDL**: ```sql -- ============================================================ -- 字段必填/隐藏规则表(租户 Schema) -- ============================================================ CREATE TABLE field_requirement_rules ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), module VARCHAR(20) NOT NULL, -- 'property' | 'client' entity_type VARCHAR(50) NOT NULL, -- property_type 值,如 'residential' | 'shop' trade_status VARCHAR(50) NOT NULL, -- 'sale' | 'rent' | 'sale_rent'(or '*' 表示所有) field_key VARCHAR(50) NOT NULL, -- 字段 key,如 'orientation' | 'decoration' requirement VARCHAR(10) NOT NULL -- 'required' | 'optional' | 'hidden' CHECK (requirement IN ('required', 'optional', 'hidden')), updated_by UUID REFERENCES staff(id), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE (module, entity_type, trade_status, field_key) ); CREATE INDEX idx_field_req_lookup ON field_requirement_rules(module, entity_type, trade_status); ``` **MVP 初始规则**(建议由研发预置,管理员可覆盖): | module | entity_type | trade_status | field_key | requirement | |--------|-------------|--------------|-----------|-------------| | `property` | `residential` | `sale` | `orientation` | `optional` | | `property` | `residential` | `sale` | `decoration` | `optional` | | `property` | `residential` | `sale` | `floor` | `optional` | | `property` | `residential` | `rent` | `decoration` | `optional` | | `property` | `residential` | `rent` | `floor` | `optional` | --- ## 三、与 ENUMS.md 的冲突与修改建议 ### 冲突点 1:`lookup_items` DDL 缺失 **现状**:ENUMS.md §2.14 和 §六.4 已提到「可配置枚举通过 `lookup_items` 表管理」,但 `lookup_items` 的 DDL 既不在 ENUMS.md 中,也不在 DATA_MODEL.md 中。 **建议修改**:在 `DATA_MODEL/ENUMS.md` 末尾(或新增 §七)补充 `lookup_groups` + `lookup_items` 的 DDL 和说明,明确与 `enum_labels` 的区别: ```markdown ## 七、可配置枚举(lookup_items) 与 `enum_labels`(Public Schema,固定)不同,`lookup_items` 存储在 **Tenant Schema**, 由租户管理员自主维护。适用于各租户选项不同的枚举字段。 [此处补充 DDL 和说明] **属于 lookup_items 的 domain**: - `client.source`(客源来源) - `client.follow_purpose`(跟进目的)— 已在 §2.14 说明 - `property.source`(房源来源) ``` ### 冲突点 2:`client.source`(来源)未在 ENUMS.md 定义 **现状**:来源是可配置枚举,不应在 ENUMS.md 中定义固定值。但目前 ENUMS.md 中没有任何地方说明「哪些 domain 是可配置的、对应哪张表」,导致读者不清楚来源应该去哪里查。 **建议修改**:在 ENUMS.md §六 维护约定中新增一条: ```markdown 5. **可配置枚举对照表**:以下 domain 属于 `lookup_items`,不在本文件定义, 不建立 CHECK 约束,请查阅 `PRD/系统配置/系统配置模块PRD.md`: - `client.source` - `client.follow_purpose`(已标注) - `property.source` ``` ### 冲突点 3:`tenant_settings` 和 `field_requirement_rules` 完全缺失 **现状**:现有 DATA_MODEL 文档未涵盖这两张表。 **建议修改**:在 `DATA_MODEL/DATA_MODEL.md` 中新增「系统配置模块数据模型」章节,包含这两张表的 DDL。(本文件的建议 DDL 见第二章 C 节) --- ## 四、服务层约定(供研发参考) 所有业务模块通过统一服务层读取配置,**禁止直接查询配置表**: ```python # apps/setting/services/tenant_settings_service.py class TenantSettingsService: def get(self, key: str, default=None): """ 读取标量配置(tenant_settings 表) 缓存 key:{tenant_schema}:setting:kv:{key},TTL 5min """ def get_lookup_items(self, module: str, key: str) -> list[dict]: """ 获取可配置枚举选项(lookup_items 表) 仅返回 is_active=True 的项,按 sort_order 排序 缓存 key:{tenant_schema}:setting:lookup:{module}.{key},TTL 5min 写入时主动 invalidate """ def get_field_requirements( self, module: str, entity_type: str, trade_status: str ) -> dict[str, str]: """ 获取字段必填规则,返回 {field_key: 'required'|'optional'|'hidden'} 缓存 key:{tenant_schema}:setting:field_req:{module}.{entity_type}.{trade_status},TTL 5min """ ``` --- ## 五、需要 Atlas 完成的具体动作 | 编号 | 动作 | 修改文件 | 优先级 | |------|------|---------|--------| | A-1 | 新增 `lookup_groups` + `lookup_items` DDL | `DATA_MODEL/DATA_MODEL.md` 或单独 `DATA_MODEL_SETTING.md` | P0(开发依赖) | | A-2 | 新增 `tenant_settings` DDL | 同上 | P0 | | A-3 | 新增 `field_requirement_rules` DDL | 同上 | P0 | | A-4 | ENUMS.md §七 补充可配置枚举说明和对照表 | `DATA_MODEL/ENUMS.md` | P0 | | A-5 | ENUMS.md §六 维护约定新增第 5 条(可配置枚举对照) | `DATA_MODEL/ENUMS.md` | P0 | | A-6 | 确认 `entity_type` 字段的值域与 `property.property_type` 的 CHECK 约束完全一致 | `DATA_MODEL_PROPERTY.md` 对齐 | P0 | | A-7 | 确认 `trade_status` 字段的值域(`sale`/`rent`/`sale_rent`/`*`)是否与 `property.status` 兼容 | `DATA_MODEL_PROPERTY.md` 对齐 | P1 | 完成以上动作后,请更新 `DATA_MODEL/DATA_MODEL.md` 的版本号,并通知 Nova 做最终 PRD 对齐确认。 --- *本文档由 Nova 起草,数据模型最终决策权归 Atlas。如有架构层面的调整,请反馈给 Nova 同步更新 PRD 中的技术考量章节。*