- 新增 PRD/系统配置/系统配置模块PRD.md(v0.1 Draft) - MVP 范围:US-SETTING-001-A(Lookup Items)、B(房源字段必填规则)、C(客源录入规则) - 新增 PRD/系统配置/系统配置数据模型设计说明_for_Atlas.md - 新增 PRD/系统配置/系统配置参数数据.md(竞品参数数据) - 删除旧版 PRD/系统配置/系统配置.md(已被新PRD替代) - 新增 DATA_MODEL/DATA_MODEL_SETTING.md(系统配置数据模型) - 新增 DATA_MODEL/ENUMS.md(枚举定义与约定) - 新增 AGENTS.md(AI Agent 开发规范) - 更新 PRD/TASK.md:US-SETTING-001 拆分为 A/B/C 三个子任务,修正参考文档路径与验收标准 - 新增 VIBE_CODING_开工前缺失清单.md - 新增 TECH_STACK/房源管理技术方案.md - 更新 DATA_MODEL/DATA_MODEL.md、DATA_MODEL_CLIENT.md、DATA_MODEL_LOGIN.md - 更新 PRD/PRD_MVP.md、PRD/权限管理/权限管理模块PRD.md - 更新 TECH_STACK/TECH_STACK.md、权限管理系统技术方案.md - 更新 UI_DESIGN/preview.html、UI_SYSTEM/UI_SYSTEM.md - 新增 prompt/PRD - 为系统设置生成PRD设计文档.md、更新 prompt 模板
17 KiB
For AI assistants: Read this entire file before writing any code. All decisions here are final. Do not suggest alternatives unless asked.
Fonrey — 系统配置模块数据模型(DATA_MODEL_SETTING)
定位:本文件是
apps/setting/模块的数据模型权威来源。
版本:v1.0 | 日期:2026-04-27
关联 PRD:PRD/系统配置/系统配置模块PRD.md
关联文档:DATA_MODEL/ENUMS.md、DATA_MODEL/DATA_MODEL.md
一、模块定位与架构边界
1.1 系统配置模块职责
系统配置模块(apps/setting/)负责管理三类性质不同的配置数据:
| 类型 | 说明 | 表 | Schema |
|---|---|---|---|
| A. 固定系统枚举 | 平台级固定值域,所有租户共享 | enum_labels |
Public(shared) |
| B. 可配置枚举 | 各租户选项不同,管理员可增删排序 | lookup_groups + lookup_items |
Tenant |
| C. 行为规则与开关 | 标量配置开关 + 字段必填规则 | tenant_settings + field_requirement_rules |
Tenant |
重要区分:
- 类型 A (
enum_labels) 已在DATA_MODEL/ENUMS.md完整定义,本文件不重复- 类型 B/C 均存于 租户 Schema,由租户管理员通过界面维护
apps/setting/是tenant_apps(非shared_apps)
1.2 依赖关系
apps/setting/
├── 依赖 → core.cache(Redis,统一租户前缀)
├── 依赖 → org.Staff(created_by / updated_by FK)
└── 被依赖 ← apps/property(读取字段规则、枚举选项)
← apps/client(读取查重范围、枚举选项)
二、可配置枚举表(类型 B)
2.1 lookup_groups(枚举分组)
每个分组代表一类可配置枚举(如「客源来源」「跟进目的」),由研发预置,租户管理员不可新增或删除分组,仅可管理分组内的选项。
-- ============================================================
-- 可配置枚举分组(Tenant 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, -- 界面显示名称,如「客源来源」
description TEXT, -- 说明文案(前端 tooltip 使用)
sort_order SMALLINT NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (module, key)
);
MVP 预置分组(种子数据):
| module | key | label_zh | description |
|---|---|---|---|
client |
source |
客源来源 | 客源从何处获取,用于来源渠道分析 |
client |
follow_purpose |
跟进目的 | 客源跟进时选择的目的分类 |
property |
source |
房源来源 | 房源从何处获取 |
2.2 lookup_items(枚举选项)
-- ============================================================
-- 可配置枚举选项(Tenant Schema)
-- 租户管理员可增删排序;is_system=True 的预制项不可物理删除
-- ============================================================
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, -- 存储值,英文 snake_case(如 'door_to_door')
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) ON DELETE SET NULL, -- 系统预制时为 NULL
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);
关键约束:
is_system = TRUE的记录不允许物理删除(Service 层强制拦截)is_active = FALSE后:前端录入下拉不展示;历史已选该值的记录保留原值,展示时追加「(已停用)」后缀value一旦写入不允许修改(历史数据依赖);如需改名,停用旧项、新增新项
2.3 MVP 预置种子数据(is_system = TRUE)
以下选项在租户初始化时自动写入:
客源来源(client.source)
| value | label_zh | sort_order |
|---|---|---|
store_reception |
门店接待 | 1 |
old_client_referral |
老客户转介绍 | 2 |
stationed_dispatch |
驻守派单 | 3 |
walk_in |
上门 | 4 |
online_58 |
网络-58同城 | 5 |
online_anjuke |
网络-安居客 | 6 |
wechat |
微信 | 7 |
friend_referral |
朋友介绍 | 8 |
跟进目的(client.follow_purpose)
| value | label_zh | sort_order |
|---|---|---|
callback |
回拨 | 1 |
push_property |
推房 | 2 |
showing |
带看 | 3 |
maintain |
维护 | 4 |
other |
其他 | 5 |
房源来源(property.source)
| value | label_zh | sort_order |
|---|---|---|
proactive_development |
主动开发 | 1 |
owner_walk_in |
业主上门 | 2 |
old_client_referral |
老客户转介绍 | 3 |
online_inquiry |
网络来电 | 4 |
三、行为规则与开关(类型 C)
3.1 tenant_settings(标量配置键值表)
存储开关(bool)、阈值(int)、单选枚举(string)等标量类型配置项。
-- ============================================================
-- 租户标量配置(键值对)(Tenant Schema)
-- ============================================================
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),如 {"v": "self"}
value_type VARCHAR(20) NOT NULL -- 'bool' | 'int' | 'string' | 'enum'(用于前端渲染控件)
CHECK (value_type IN ('bool', 'int', 'string', 'enum')),
updated_by UUID REFERENCES staff(id) ON DELETE SET NULL,
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (category, key)
);
CREATE INDEX idx_tenant_settings_category ON tenant_settings(category);
存储格式约定:
bool:{"v": true}或{"v": false}int:{"v": 30}string:{"v": "some_value"}enum:{"v": "self", "choices": ["self", "dept", "company"]}—choices由代码硬编码,不存 DB
MVP 阶段预置 key:
| category | key | value_type | 默认值 | 说明 |
|---|---|---|---|---|
client |
duplicate_check_scope |
enum |
{"v": "self"} |
新增私客查重范围:self(本人)/ dept(本部门)/ company(全公司) |
未来 P1 阶段可按需追加 key,无需修改表结构。
3.2 field_requirement_rules(字段必填规则表)
按「模块 × 房源用途 × 交易状态 × 字段」四元组确定一条规则,控制录入界面的字段显示状态。
-- ============================================================
-- 字段必填/隐藏规则(Tenant Schema)
-- MVP 仅支持 module='property'
-- ============================================================
CREATE TABLE field_requirement_rules (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
module VARCHAR(20) NOT NULL, -- 'property' | 'client'(MVP 只用 'property')
entity_type VARCHAR(50) NOT NULL, -- 与 property.property_type CHECK 约束值对齐
-- 'residential'|'villa'|'commercial_residential'|'shop'|'office'|'other'
trade_status VARCHAR(20) NOT NULL, -- 交易大类:'sale'|'rent'|'sale_rent'|'*'(全部)
CHECK (trade_status IN ('sale', 'rent', 'sale_rent', '*')),
field_key VARCHAR(50) NOT NULL, -- 字段 key,如 'orientation'|'decoration'|'floor'
requirement VARCHAR(10) NOT NULL -- 规则值
CHECK (requirement IN ('required', 'optional', 'hidden')),
updated_by UUID REFERENCES staff(id) ON DELETE SET NULL,
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);
与 property.property_type 对齐说明:
entity_type 的值域与 property.property_type 的 CHECK 约束完全一致:
| entity_type | property_type label_zh |
|---|---|
residential |
住宅 |
villa |
别墅 |
commercial_residential |
商住 |
shop |
商铺 |
office |
写字楼 |
other |
其他 |
trade_status 与 property.status 的映射关系:
| trade_status | 对应 property.status 值 |
|---|---|
sale |
for_sale |
rent |
for_rent |
sale_rent |
for_sale_rent |
* |
所有状态通用规则(fallback) |
重要:
trade_status是录入场景的交易意图分类,不是property.status的完整枚举。规则匹配逻辑:先查精确匹配(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 |
sale |
building_area |
required |
property |
residential |
sale |
inner_area |
optional |
property |
residential |
sale |
room_layout |
required |
property |
residential |
rent |
decoration |
optional |
property |
residential |
rent |
floor |
optional |
property |
residential |
rent |
building_area |
required |
property |
residential |
rent |
room_layout |
required |
MVP 可配置字段范围(对应 PRD AC-4):
| field_key | 说明 | 字段类型 |
|---|---|---|
orientation |
朝向(property.orientation 枚举) |
枚举 |
decoration |
装修情况(property.decoration 枚举) |
枚举 |
floor |
所在楼层/总楼层 | 数值 |
building_area |
建筑面积(㎡) | 数值 |
inner_area |
套内面积(㎡) | 数值 |
room_layout |
房型(室/厅/卫) | 数值组 |
ownership_years |
产权年限(年) | 数值 |
parking_count |
车位数 | 数值 |
四、服务层设计
所有业务模块禁止直接查询配置表,必须通过统一服务层读取:
# apps/setting/services/tenant_settings_service.py
class TenantSettingsService:
"""
系统配置统一读取服务。
所有配置均经 Redis 缓存,TTL 5min,写入时主动 invalidate。
Redis Key 规范:{tenant_schema}:setting:{type}:{key}
"""
def get(self, category: str, key: str, default=None):
"""
读取标量配置(tenant_settings 表)
Cache Key: {tenant_schema}:setting:kv:{category}.{key}
返回 JSONB value 字段中 'v' 的值(已解包)
"""
def set(self, category: str, key: str, value, updated_by_id) -> None:
"""
写入标量配置 + 主动 invalidate 缓存
"""
def get_lookup_items(self, module: str, key: str) -> list[dict]:
"""
获取可配置枚举选项(lookup_items 表)
仅返回 is_active=True 的项,按 sort_order 排序
Cache Key: {tenant_schema}:setting:lookup:{module}.{key}
返回格式:[{"value": "walk_in", "label_zh": "上门", "is_system": True}, ...]
"""
def get_field_requirements(
self, module: str, entity_type: str, trade_status: str
) -> dict[str, str]:
"""
获取字段必填规则
匹配顺序:精确匹配(entity_type + trade_status) > 通配规则(trade_status='*')
Cache Key: {tenant_schema}:setting:field_req:{module}.{entity_type}.{trade_status}
返回格式:{"orientation": "optional", "decoration": "required", ...}
"""
五、Redis 缓存键规范
| 用途 | Cache Key | TTL | 失效触发 |
|---|---|---|---|
| 标量配置 | {schema}:setting:kv:{category}.{key} |
300s | TenantSettingsService.set() |
| 可配置枚举 | {schema}:setting:lookup:{module}.{key} |
300s | 管理员保存 lookup_items |
| 字段规则 | {schema}:setting:field_req:{module}.{entity_type}.{trade_status} |
300s | 管理员保存 field_requirement_rules |
| 客源规则(整体) | {schema}:setting:client_rules |
300s | 任意客源规则变更 |
TTL 300s(5 分钟)对应 PRD 成功指标「配置变更生效时延 ≤ 5 分钟」。
六、目录结构
apps/setting/
├── models/
│ ├── lookup.py # LookupGroup, LookupItem
│ └── setting.py # TenantSetting, FieldRequirementRule
├── services/
│ └── tenant_settings_service.py # 统一配置读取服务(禁止直接查表)
├── views/
│ ├── lookup_views.py # 参数配置页面(US-SETTING-001-A)
│ ├── field_rule_views.py # 房源字段规则(US-SETTING-001-B)
│ └── client_rule_views.py # 客源规则(US-SETTING-001-C)
├── templates/setting/
├── fixtures/
│ ├── lookup_groups.json # 分组种子数据(3 组)
│ ├── lookup_items.json # 选项种子数据(is_system=True)
│ ├── tenant_settings.json # 默认配置种子数据
│ └── field_requirement_rules.json # 默认字段规则
├── migrations/
│ ├── 0001_lookup_groups.py
│ ├── 0002_lookup_items.py
│ ├── 0003_tenant_settings.py
│ └── 0004_field_requirement_rules.py
└── urls.py
七、迁移执行顺序
0001_lookup_groups # 先建分组表(无外键依赖)
0002_lookup_items # 再建选项表(依赖 lookup_groups + staff)
0003_tenant_settings # 独立,无外键依赖
0004_field_requirement_rules # 独立,仅依赖 staff
迁移执行后,通过 call_command('loaddata', 'lookup_groups') 等方式加载 fixtures 种子数据。
八、关键约束与禁止项
| 约束 | 规则 |
|---|---|
| 不可删除系统预制项 | lookup_items.is_system = True 的记录,Service 层硬拦截物理删除请求 |
| 不可修改已有 value | lookup_items.value 写入后只读;修改请停用旧项 + 新增新项 |
| 不可直接查询配置表 | 业务模块(property/client)必须通过 TenantSettingsService 读取,禁止 ORM 直查 |
| entity_type 值域 | 必须与 property.property_type CHECK 约束完全一致(见第三章) |
| Redis Key 前缀 | 必须携带租户 schema 前缀,格式:{tenant_schema}:setting:{type}:{key} |
九、设计决策(ADR)
| 决策 | 选择 | 理由 |
|---|---|---|
| 枚举两级架构 | enum_labels(Public/固定)+ lookup_items(Tenant/可配置)分离 |
保障系统一致性的同时给租户灵活度 |
lookup_groups 由研发预置 |
是 | 防止租户随意创建不规范分组,控制可配置范围边界 |
tenant_settings 使用 JSONB |
是 | 支持 bool/int/string 等多类型,无需为每类型单独建列 |
field_requirement_rules 不新增字段 |
是 | 规则层只控制「必填/选填/隐藏」,字段存在性由 DATA_MODEL_PROPERTY 决定 |
| 服务层统一读取 | TenantSettingsService |
统一缓存管理,业务层与配置存储解耦 |
| trade_status 不复用 property.status | 独立 sale/rent/sale_rent/* |
录入场景的交易意图与房源全生命周期状态不同,避免耦合 |