docs: 新增系统配置模块PRD及数据模型文档,更新TASK.md
- 新增 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 模板
This commit is contained in:
317
Project/fonrey/AGENTS.md
Normal file
317
Project/fonrey/AGENTS.md
Normal file
@@ -0,0 +1,317 @@
|
||||
> **For AI assistants**: Read this entire file before writing any code. All decisions here are final. Do not suggest alternatives unless asked. Every new feature or User Story implementation must be accompanied by corresponding tests as defined in this document.
|
||||
|
||||
# Fonrey(房睿)— AGENTS.md
|
||||
|
||||
**适用对象**:所有 AI Coding Agent(vibe coding 模式)
|
||||
**文档定位**:开发启动前的强制阅读清单,定义架构约定、禁止项和文档导航
|
||||
**最后更新**:2026-04-27
|
||||
|
||||
---
|
||||
|
||||
## 1. 项目概览
|
||||
|
||||
**Fonrey(房睿)房产经纪管理系统** —— 面向房地产经纪公司的 B2B SaaS 平台,解决房源/客源信息散乱、跟进缺失、重复录入等痛点,支撑单租户 89,000+ 房源数据量级下的高效匹配。
|
||||
|
||||
- **核心模块**:房源管理、客源管理、楼盘管理、组织人事、权限管理、登录管理、系统设置、客户端发布
|
||||
- **目标用户**:一线经纪人(高频)、店长/经理(每日)、运营/行政(每日)、系统管理员(不定期)
|
||||
- **形态**:Web 端为主 + Electron 桌面客户端(壳应用);移动端为 v2 规划
|
||||
- **设计哲学**:数据一致性 > 录入/筛选速度 > UI 简洁高效。优先保障多租户数据物理隔离与极速响应。
|
||||
|
||||
---
|
||||
|
||||
## 2. 核心技术栈
|
||||
|
||||
| 层级 | 技术选型 | 说明 |
|
||||
|---|---|---|
|
||||
| **Frontend** | HTMX + Alpine.js + Tailwind CSS | 无重前端框架;HTMX 局刷、Alpine 管状态、Tailwind 样式 |
|
||||
| **Backend** | Django 4.x(ASGI 模式) | 支持异步能力 |
|
||||
| **Multi-tenant** | `django-tenants` | PostgreSQL Schema 隔离,租户数据物理安全 |
|
||||
| **Database** | PostgreSQL 16 + PgBouncer | 连接池优化,支撑高并发 |
|
||||
| **Cache** | Redis | 缓存、限流、Token、权限快照 |
|
||||
| **Tasks** | Celery + Celery Beat | 异步导出、智能配房、邮件、图片转码 |
|
||||
| **Storage** | Cloudflare R2(S3 兼容) | 房源图片、附件、客户端安装包 |
|
||||
| **CDN** | Cloudflare | 静态资源 + 客户端更新包加速 |
|
||||
| **Server** | Gunicorn + Uvicorn workers + Nginx | ASGI 服务部署 |
|
||||
| **Monitoring** | Sentry + Grafana | 错误追踪 + 指标监控 |
|
||||
| **Deployment** | Docker Compose | 容器化部署 |
|
||||
| **Desktop Client** | Electron + electron-updater | 壳应用,渲染层复用 Web 技术栈 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 目录结构
|
||||
|
||||
代码库标准目录结构如下,**严格遵循**,不得自行创建新的顶层目录:
|
||||
|
||||
```
|
||||
fonrey/
|
||||
├── apps/
|
||||
│ ├── tenant/ # django-tenants 配置(shared_apps)
|
||||
│ ├── account/ # 登录认证
|
||||
│ ├── permission/ # 权限管理
|
||||
│ ├── org/ # 组织人事(org_units, staff)
|
||||
│ ├── region/ # 区域管理(districts, business_areas)
|
||||
│ ├── complex/ # 楼盘管理(complexes, buildings, schools)
|
||||
│ ├── property/ # 房源核心(含 models/services/tasks 三层)
|
||||
│ ├── client/ # 客源管理
|
||||
│ ├── setting/ # 系统设置(lookup, tags)
|
||||
│ └── release/ # 客户端发布管理(shared_apps)
|
||||
├── shared/ # 公共 Schema App(public schema 模型)
|
||||
└── core/
|
||||
├── models/base.py # 抽象基类(所有业务表继承)
|
||||
├── encryption.py # PII 加密(AES-256-GCM)
|
||||
└── cache.py # Redis 工具(含 tenant 前缀)
|
||||
```
|
||||
|
||||
**Django App 内部分层规范**(以 `property` 为典型,所有 App 参照执行):
|
||||
|
||||
```
|
||||
apps/property/
|
||||
├── models/ # 一表一文件,禁止全部堆在 models.py
|
||||
├── services/ # 业务逻辑(完成度计算、重复检测、匹配等)
|
||||
├── tasks.py # Celery 异步任务
|
||||
├── views.py # HTMX/JSON 视图(薄视图,调 services)
|
||||
└── urls.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 关键开发约定
|
||||
|
||||
### 4.1 多租户隔离(最高优先级)
|
||||
|
||||
- 所有数据库查询**必须**基于当前租户 Schema;`django-tenants` 中间件已自动切换,不得绕过
|
||||
- **严禁**跨租户 SQL 查询(包括 raw SQL 和 ORM 的 `using()` 指定 public schema)
|
||||
- `shared_apps` 仅放平台基础数据:`Tenant`、`ClientRelease`、`PermissionDef`、`PlatformAdmin` 等
|
||||
- Celery 任务必须在任务参数中传入 `tenant_schema_name`,任务开头调用 `connection.set_schema(schema_name)` 切换到正确 Schema,**不得依赖外部上下文传递**
|
||||
|
||||
### 4.2 前端交互约定
|
||||
|
||||
- **HTMX** 处理局部 DOM 刷新:分页、筛选、联想搜索、表单提交
|
||||
- **Alpine.js** 处理前端状态:弹窗开关、多选状态、字数统计、条件显示
|
||||
- **禁止**编写复杂原生 JavaScript;逻辑无法用 HTMX/Alpine 覆盖时,优先提出问题而不是自行引入 JS 库
|
||||
- HTMX 请求失败(4xx/5xx)必须触发全局 Toast 提示(通过 `HX-Trigger` 响应头)
|
||||
- 所有 HTMX 局部请求后端视图必须校验 `HX-Request` header,防止直接访问返回残缺 HTML
|
||||
|
||||
### 4.3 异步任务约定
|
||||
|
||||
- 所有耗时 **> 500ms** 的操作**必须**经 Celery 异步执行:Excel 导出、图片处理、智能配房、邮件发送、完成度重算
|
||||
- Celery Beat 定时任务:私客自动转公客(每小时)、重复客源检测(每日)
|
||||
- 任务状态存 Redis,前端通过轮询 HTMX 端点展示进度
|
||||
|
||||
### 4.4 数据模型约定
|
||||
|
||||
| 约定 | 规则 |
|
||||
|------|------|
|
||||
| 主键类型 | `UUID v4`(`gen_random_uuid()`),禁止自增整数 |
|
||||
| 软删除 | 所有核心表含 `deleted_at TIMESTAMPTZ`,查询默认加 `WHERE deleted_at IS NULL` |
|
||||
| 时间戳 | 全部使用 `TIMESTAMPTZ`(含时区),禁止 naive datetime |
|
||||
| 手机号存储 | AES-256-GCM 加密,建立 SHA-256 哈希索引,通过 `core/encryption.py` 操作 |
|
||||
| 审计字段 | `created_by UUID`、`updated_by UUID` 全表覆盖 |
|
||||
| 枚举值 | 业务枚举用 `VARCHAR + CHECK CONSTRAINT`;不得在代码中硬编码中文枚举值 |
|
||||
| 金额 | `NUMERIC(12,2)` 万元精度,禁止 FLOAT |
|
||||
| 大文本 | `TEXT` 类型,不设长度限制 |
|
||||
| 不可删除记录 | `listing_histories`、`price_changes` 无 `deleted_at`,append-only,禁止物理删除 |
|
||||
|
||||
### 4.5 查询性能约定
|
||||
|
||||
- 列表查询目标:89,000 条房源 / 200 万条跟进日志下,p95 响应 **< 2 秒**
|
||||
- **所有列表查询必须使用 Keyset 分页**(`WHERE id > :last_id ORDER BY id`),禁止 OFFSET 分页用于大数据量场景
|
||||
- 新增表必须在 migration 中同步创建必要索引,不得事后补建
|
||||
- 高写入表(`follow_logs`、`property_photos`、`permission_change_logs`、`login_attempts`)必须按月分区(`PARTITION BY RANGE`)
|
||||
|
||||
### 4.6 安全约定
|
||||
|
||||
- 手机号等 PII 数据统一通过 `core/encryption.py` 加密存储,**禁止明文入库**
|
||||
- 所有配置(密钥、Bucket 名、外部服务 URL)通过 `.env` 注入,**禁止硬编码**
|
||||
- 每个受权限保护的 View 必须覆盖三个测试场景:有权限(200)、无权限(403)、未登录(302)
|
||||
- Redis Key 必须携带租户 Schema 前缀,格式:`{tenant_schema}:{module}:{key}`
|
||||
|
||||
### 4.7 错误处理约定
|
||||
|
||||
- 后端 API 返回标准 JSON 错误格式:`{"error": "...", "code": "SNAKE_CASE_CODE"}`
|
||||
- View 层禁止直接抛出异常,必须捕获并返回对应 HTTP 状态码
|
||||
- Celery 任务失败必须记录到 Sentry,并更新任务状态表
|
||||
|
||||
### 4.8 文件命名约定
|
||||
|
||||
- Django App:`snake_case`(如 `property`、`follow_log`)
|
||||
- 前端模板组件:`kebab-case`(如 `property-card.html`、`client-form.html`)
|
||||
- Migration 文件:不得手动重命名,保留 Django 自动生成的序号
|
||||
|
||||
---
|
||||
|
||||
## 5. 禁止项(Do NOT — 违反视为 Bug)
|
||||
|
||||
- ❌ **React / Vue / Angular** 等重前端框架
|
||||
- ❌ 在请求线程中处理耗时 **> 500ms** 的任务(必须用 Celery)
|
||||
- ❌ 传统页面全刷方案(除初始页面加载外)
|
||||
- ❌ 复杂原生 JavaScript(优先 HTMX/Alpine 指令)
|
||||
- ❌ Electron 渲染进程开启 `nodeIntegration: true`
|
||||
- ❌ 客户端内嵌业务逻辑或本地数据库(壳应用原则,渲染层只加载 Web URL)
|
||||
- ❌ 跨租户 SQL 查询(必须经 `django-tenants` 中间件切换 Schema)
|
||||
- ❌ 代码中硬编码密钥、Tenant ID、URL、枚举中文字符串
|
||||
- ❌ 使用 OFFSET 分页处理 1000 条以上数据集
|
||||
- ❌ 手机号/身份证号明文存储
|
||||
- ❌ 使用 Django 原生 `Client()` 做集成测试(必须用 `TenantClient`)
|
||||
- ❌ 在 MVP 阶段实现 `PRD_MVP.md §3` 中标注的 Out-of-Scope 功能(移动端、合同、财务、新房、三网发布等)
|
||||
|
||||
---
|
||||
|
||||
## 6. 测试要求
|
||||
|
||||
**核心原则**:每个 P0 User Story 完成后,对应测试必须同步产出,**不允许欠测试债**。
|
||||
|
||||
### 测试分层
|
||||
|
||||
| 层级 | 工具 | 覆盖目标 | 运行频率 |
|
||||
|------|------|---------|---------|
|
||||
| **单元测试** | `pytest-django` + `factory_boy` | `core/`、`services/`、`tasks.py` | 每次 push |
|
||||
| **集成测试** | `pytest-django` + `TenantClient` | 所有 P0 User Story 的 HTTP 接口 | 每次 push |
|
||||
| **E2E 测试** | `playwright` (Python) | 5 条核心用户旅程 | 每日定时 |
|
||||
|
||||
### 测试关键约定
|
||||
|
||||
- 集成测试**必须**使用 `TenantClient`,禁止使用 Django 原生 `Client()`
|
||||
- HTMX 局部请求测试须携带 `HTTP_HX_REQUEST: true` header,验证返回局部 HTML 而非完整页面
|
||||
- Celery 任务测试使用 `CELERY_TASK_ALWAYS_EAGER = True` 同步执行
|
||||
- 外部服务(R2、Redis、邮件)在测试中全部 Mock,禁止真实调用
|
||||
- 权限测试必须覆盖:有权限(200)、无权限(403)、未登录(302)三场景
|
||||
|
||||
### 覆盖率基准
|
||||
|
||||
| 模块 | 最低目标 |
|
||||
|------|---------|
|
||||
| `core/` 核心基础模块 | ≥ 90% |
|
||||
| `apps/*/services/` 业务逻辑层 | ≥ 80% |
|
||||
| `apps/*/views.py` 视图层 | ≥ 70% |
|
||||
| E2E 核心用户旅程(5 条) | 100% 通过 |
|
||||
|
||||
---
|
||||
|
||||
## 7. MVP 范围(Phase 1)
|
||||
|
||||
**当前阶段**:MVP Phase 1(P0 功能)
|
||||
|
||||
实现任何功能前,先对照 `PRD/PRD_MVP.md` 确认是否在 P0 范围。**P0 范围以外的功能在 MVP 阶段禁止实现**,包括但不限于:
|
||||
|
||||
- 移动端适配(v2 规划)
|
||||
- 新房模块、合同、财务、三网发布
|
||||
- 公客管理(P2)
|
||||
- 门店分布地图(P2)
|
||||
|
||||
**当前 Phase 1 P0 Task 列表**:见 `PRD/TASK.md §Phase 1`(US-ACCOUNT-001~003、US-COMPLEX-001~003、US-PROPERTY-001~008、US-CLIENT-001~017、US-ORG-001~003、US-PERMISSION-001~005、US-SETTING-001)
|
||||
|
||||
---
|
||||
|
||||
## 8. 数据模型参考(实现前必读)
|
||||
|
||||
所有数据模型的**权威来源**如下,开发前必须阅读对应文档,不得凭印象实现:
|
||||
|
||||
| 模块 | 数据模型权威文档 |
|
||||
|------|---------------|
|
||||
| 总览 & 架构决策 | `DATA_MODEL/DATA_MODEL.md` |
|
||||
| 房源管理 | `DATA_MODEL/DATA_MODEL_PROPERTY.md` |
|
||||
| 客源管理 | `DATA_MODEL/DATA_MODEL_CLIENT.md` |
|
||||
| 楼盘/小区/区域 | `DATA_MODEL/DATA_MODEL_COMPLEX.md` |
|
||||
| 组织人事 | `DATA_MODEL/DATA_MODEL_ORG.md` |
|
||||
| 权限管理 | `DATA_MODEL/DATA_MODEL_PERMISSION.md` |
|
||||
| 登录认证 | `DATA_MODEL/DATA_MODEL_LOGIN.md` |
|
||||
| Public Schema(平台运营层) | `DATA_MODEL/DATA_MODEL_PUBLIC.md` |
|
||||
| 系统配置(lookup/setting) | `DATA_MODEL/DATA_MODEL_SETTING.md` |
|
||||
|
||||
### 核心领域关系速览
|
||||
|
||||
```
|
||||
[区域/商圈] ──────────────────────────────┐
|
||||
│ │
|
||||
[学校管理] │
|
||||
│ ▼
|
||||
[楼盘/小区] ── [楼栋] ──────────► [房源] ◄──── [挂牌历史]
|
||||
│ │
|
||||
│ ┌────────┼────────┐
|
||||
│ │ │ │
|
||||
│ [联系人] [跟进日志] [维护完成度]
|
||||
│
|
||||
[客源] ──── [配对记录] ──── [带看记录]
|
||||
│
|
||||
[员工/组织] ──── [权限]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 技术方案文档导航
|
||||
|
||||
| 模块 | 技术方案文档 | PRD |
|
||||
|------|------------|-----|
|
||||
| 总纲 & 禁止项 | `TECH_STACK/TECH_STACK.md` | `PRD/PRD_MVP.md` |
|
||||
| 登录认证 | `TECH_STACK/登录管理技术方案.md` | `PRD/登录管理/` |
|
||||
| 权限管理 | `TECH_STACK/权限管理系统技术方案.md` | `PRD/权限管理/` |
|
||||
| 测试规范 | `TECH_STACK/测试规范.md` | — |
|
||||
| 房源管理 | _待补充_ | `PRD/房源管理/` |
|
||||
| 客源管理 | _待补充_ | `PRD/客源管理/` |
|
||||
| 楼盘管理 | _待补充_ | `PRD/房源管理/(含楼盘)` |
|
||||
| 组织人事 | _待补充_ | `PRD/组织人事管理/` |
|
||||
| 系统设置 | _待补充_ | `PRD/系统配置/`、`PRD/系统管理/` |
|
||||
| 客户端发布 | `TECH_STACK/TECH_STACK.md §7` | `PRD/发布管理/客户端发布管理模块PRD.md` |
|
||||
|
||||
**设计 Review 记录**(了解当前已知技术债与阻塞项):
|
||||
- `REVIEW/REVIEW_全局_2026-04-26.md`(最新)
|
||||
- `REVIEW/REVIEW_全局_2026-04-25.md`
|
||||
|
||||
---
|
||||
|
||||
## 10. 已知待解决问题(编码前必须确认)
|
||||
|
||||
以下问题在开始编码前需先确认当前状态,若未解决应在实现对应模块前暂停并上报:
|
||||
|
||||
| 编号 | 级别 | 问题描述 | 影响模块 |
|
||||
|------|------|---------|---------|
|
||||
| **B-01** | 🔴 Blocker | 系统配置 PRD(`PRD/系统配置/系统配置.md`)为空骨架,US-SETTING-001 无法启动 | 系统配置 |
|
||||
| **B-02** | 🔴 Blocker | 核心枚举三方不一致(PRD ↔ DDL ↔ TASK AC):客源 status/grade、房源 status 互相矛盾,需先冻结 `DATA_MODEL/ENUMS.md` | 房源、客源 |
|
||||
| **B-03** | 🔴 Blocker | 权限数据范围档位冲突:§3 非目标"三档" vs §5.6"五档",DataScope 实现方式未统一 | 权限管理 |
|
||||
| **B-04** | 🔴 Blocker | Keyset 分页规范完全缺位,89k 房源列表设计错误 | 房源、客源 |
|
||||
| **M-02** | 🟠 Major | 主表 `properties`/`clients` 缺乏乐观锁字段 `version` | 房源、客源 |
|
||||
| **M-03** | 🟠 Major | 高写入表分区 DDL 未落地(`follow_logs` 等) | 跟进日志 |
|
||||
| **M-04** | 🟠 Major | Celery 多租户 schema 切换规范、R2 文件路径前缀规范、查询索引矩阵未补充 | 全局 |
|
||||
|
||||
> 详细内容见 `REVIEW/REVIEW_全局_2026-04-26.md`
|
||||
|
||||
---
|
||||
|
||||
## 11. 文档根目录
|
||||
|
||||
所有项目文档位于:`/mnt/d/Workspace/nexus/Project/fonrey/`
|
||||
|
||||
```
|
||||
fonrey/
|
||||
├── AGENTS.md # 本文件(AI Agent 开发指南)
|
||||
├── PRD/
|
||||
│ ├── PRD_MVP.md # MVP 范围书(必读)
|
||||
│ ├── TASK.md # 全量 Task Board(US 编号)
|
||||
│ ├── 房源管理/
|
||||
│ ├── 客源管理/
|
||||
│ ├── 楼盘管理/
|
||||
│ ├── 组织人事管理/
|
||||
│ ├── 权限管理/
|
||||
│ ├── 登录管理/
|
||||
│ ├── 系统配置/
|
||||
│ ├── 系统管理/
|
||||
│ └── 发布管理/
|
||||
├── DATA_MODEL/
|
||||
│ ├── DATA_MODEL.md # 数据模型总览(必读)
|
||||
│ ├── DATA_MODEL_PROPERTY.md
|
||||
│ ├── DATA_MODEL_CLIENT.md
|
||||
│ ├── DATA_MODEL_COMPLEX.md
|
||||
│ ├── DATA_MODEL_ORG.md
|
||||
│ ├── DATA_MODEL_PERMISSION.md
|
||||
│ ├── DATA_MODEL_LOGIN.md
|
||||
│ └── DATA_MODEL_PUBLIC.md
|
||||
├── TECH_STACK/
|
||||
│ ├── TECH_STACK.md # 技术栈总纲(必读)
|
||||
│ ├── 登录管理技术方案.md
|
||||
│ ├── 权限管理系统技术方案.md
|
||||
│ └── 测试规范.md
|
||||
└── REVIEW/
|
||||
├── REVIEW_全局_2026-04-26.md # 最新 Review(含阻塞问题)
|
||||
└── REVIEW_全局_2026-04-25.md
|
||||
```
|
||||
@@ -2,8 +2,8 @@
|
||||
# Fonrey 房产经纪管理系统 — DATA MODEL 设计文档
|
||||
|
||||
> **作者**: Backend Architect
|
||||
> **版本**: v1.3
|
||||
> **日期**: 2026-04-24(v1.1 修复 S1/S2/S4;v1.2 扩展 public schema;v1.3 §三 DDL 迁至 DATA_MODEL_PUBLIC.md,本文改为索引)
|
||||
> **版本**: v1.4
|
||||
> **日期**: 2026-04-24(v1.1 修复 S1/S2/S4;v1.2 扩展 public schema;v1.3 §三 DDL 迁至 DATA_MODEL_PUBLIC.md,本文改为索引;v1.4 补充 LOGIN/PERMISSION 子文档引用、领域对象、租户 Schema 章节、Redis 缓存策略)
|
||||
> **技术栈**: Django 4.x + PostgreSQL + django-tenants + Redis
|
||||
> **设计目标**: 支撑 89,000+ 房源、多租户隔离、sub-100ms 查询、合规审计
|
||||
|
||||
@@ -17,23 +17,24 @@
|
||||
┌──────────────────────────────────────────────────────────────────────┐
|
||||
│ PostgreSQL Instance │
|
||||
│ │
|
||||
│ ┌─────────────────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
||||
│ │ public schema │ │ tenant_abc │ │ tenant_xyz │ │
|
||||
│ │ (平台运营层) │ │ schema │ │ schema │ │
|
||||
│ │ │ │ │ │ │ │
|
||||
│ │ - tenants │ │ - properties │ │ - properties │ │
|
||||
│ │ - domains │ │ - clients │ │ - clients │ │
|
||||
│ │ - tenant_status_logs │ │ - complexes │ │ - complexes │ │
|
||||
│ │ - platform_admins │ │ - staff │ │ - staff │ │
|
||||
│ │ - admin_mfa_devices │ │ - org_units │ │ - org_units │ │
|
||||
│ │ - admin_sessions │ │ - ... │ │ - ... │ │
|
||||
│ │ - ip_whitelist │ └──────────────┘ └──────────────┘ │
|
||||
│ │ - platform_audit_logs │ │
|
||||
│ │ - backup_schedules │ │
|
||||
│ │ - backup_records │ │
|
||||
│ │ - export_tasks │ │
|
||||
│ │ - system_versions │ │
|
||||
│ │ - upgrade_events │ │
|
||||
│ ┌─────────────────────────┐ ┌──────────────────┐ ┌────────────┐ │
|
||||
│ │ public schema │ │ tenant_abc │ │ tenant_xyz │ │
|
||||
│ │ (平台运营层) │ │ schema │ │ schema │ │
|
||||
│ │ │ │ │ │ │ │
|
||||
│ │ - tenants │ │ - org_units │ │ (同左) │ │
|
||||
│ │ - domains │ │ - staff │ │ │ │
|
||||
│ │ - tenant_status_logs │ │ - complexes │ │ │ │
|
||||
│ │ - platform_admins │ │ - properties │ │ │ │
|
||||
│ │ - admin_mfa_devices │ │ - clients │ │ │ │
|
||||
│ │ - admin_sessions │ │ - user_accounts │ │ │ │
|
||||
│ │ - ip_whitelist │ │ - login_attempts │ │ │ │
|
||||
│ │ - platform_audit_logs │ │ - permission_defs│ │ │ │
|
||||
│ │ - backup_schedules │ │ - roles │ │ │ │
|
||||
│ │ - backup_records │ │ - staff_roles │ │ │ │
|
||||
│ │ - export_tasks │ │ - lookup_items │ │ │ │
|
||||
│ │ - system_versions │ │ - ... │ │ │ │
|
||||
│ │ - upgrade_events │ └──────────────────┘ └────────────┘ │
|
||||
│ │ - enum_labels │ │
|
||||
│ └─────────────────────────┘ │
|
||||
└──────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
@@ -104,6 +105,7 @@
|
||||
| **ExportTask** | `public.export_tasks` | 数据导出异步任务(CSV/JSON/SQL Dump,24h 下载链接) |
|
||||
| **SystemVersion** | `public.system_versions` | 平台版本历史,唯一 current 版本约束 |
|
||||
| **UpgradeEvent** | `public.upgrade_events` | 升级/回滚事件,含灰度租户维度进度快照 |
|
||||
| **EnumLabel** | `public.enum_labels` | 固定枚举字典(英文 Key → 中文标签),所有租户共享,供前端下拉渲染、导出报表中文标签、日志快照使用 |
|
||||
|
||||
#### Tenant Schema(租户业务层)
|
||||
|
||||
@@ -121,6 +123,9 @@
|
||||
| **Client(客源)** | `clients` → [DATA_MODEL_CLIENT.md](./DATA_MODEL_CLIENT.md) | 买家/租客档案,分私客/公客/成交客,含活跃度评分与自动公客转换机制 |
|
||||
| **Viewing(带看)** | `client_viewings` → [DATA_MODEL_CLIENT.md](./DATA_MODEL_CLIENT.md) | 经纪人带客户看房的完整记录 |
|
||||
| **Match(配对)** | `client_property_matches` → [DATA_MODEL_CLIENT.md](./DATA_MODEL_CLIENT.md) | 系统/人工推荐的客源↔房源配对 |
|
||||
| **UserAccount(用户账号)** | `user_accounts` → [DATA_MODEL_LOGIN.md](./DATA_MODEL_LOGIN.md) | 系统登录主体,与员工档案 1:1 绑定,含账号锁定/密码历史/登录审计 |
|
||||
| **PermissionDef(权限定义)** | `permission_defs` → [DATA_MODEL_PERMISSION.md](./DATA_MODEL_PERMISSION.md) | 权限目录(约 300 条),驱动 Hybrid RBAC + Override 权限模型 |
|
||||
| **Role(业务角色)** | `roles` → [DATA_MODEL_PERMISSION.md](./DATA_MODEL_PERMISSION.md) | 权限模板,含 4 大类别(置业顾问/店管/总经/运营/自定义) |
|
||||
|
||||
### 领域关系快速导航
|
||||
|
||||
@@ -149,6 +154,9 @@ OrgUnit (组织架构)
|
||||
| [DATA_MODEL_COMPLEX.md](./DATA_MODEL_COMPLEX.md) | 楼盘/区域(districts, business_areas, complexes, buildings, room_units, schools 等) | ✅ 完成 |
|
||||
| [DATA_MODEL_CLIENT.md](./DATA_MODEL_CLIENT.md) | 客源管理(clients, requirements, follow_logs, viewings, matches 等) | ✅ 完成 |
|
||||
| [DATA_MODEL_PROPERTY.md](./DATA_MODEL_PROPERTY.md) | 房源管理(properties 及配套 22 张表,含跟进/钥匙/委托/实勘/营销/产证/完成度/标签/收藏/保护/号码方审批等) | ✅ 完成 |
|
||||
| [DATA_MODEL_LOGIN.md](./DATA_MODEL_LOGIN.md) | 登录与账号认证(user_accounts, login_attempts, password_reset_tokens, password_histories + Redis 登录缓存) | ✅ 完成 |
|
||||
| [DATA_MODEL_PERMISSION.md](./DATA_MODEL_PERMISSION.md) | 权限管理(permission_defs, roles, role_permissions, staff_roles, staff_permission_overrides, staff_data_scopes, permission_change_logs + Redis 权限缓存) | ✅ 完成 |
|
||||
| [ENUMS.md](./ENUMS.md) | 枚举字典(`public.enum_labels` 表设计 + 所有模块枚举定义 + 种子数据 SQL) | ✅ 完成 |
|
||||
|
||||
---
|
||||
|
||||
@@ -175,6 +183,7 @@ OrgUnit (组织架构)
|
||||
| `public.export_tasks` | 数据导出异步任务(CSV/JSON/SQL Dump,24h 下载链接) | §2.4 |
|
||||
| `public.system_versions` | 平台版本历史,部分唯一索引保证唯一 current | §2.5 |
|
||||
| `public.upgrade_events` | 升级/回滚事件,`tenant_progress` JSONB 快照各租户状态 | §2.5 |
|
||||
| `public.enum_labels` | 固定枚举字典(英文 Key → 中文标签),所有租户共享 | §2.6 |
|
||||
|
||||
**关键约束提示**:
|
||||
- `tenant_status_logs` / `platform_audit_logs` **无 deleted_at**,禁止 UPDATE/DELETE,append-only
|
||||
@@ -257,31 +266,31 @@ OrgUnit (组织架构)
|
||||
|
||||
**核心表概览**(开发时以 DATA_MODEL_PROPERTY.md 为准):
|
||||
|
||||
| 表名 | 说明 | 关键字段 |
|
||||
|------|------|----------|
|
||||
| `properties` | 房源主表(系统核心,89,000+ 数据量) | `status`, `attribute`, `property_type`, `complex_id`, `sale_price`, `area`, `grade`, `completeness_score`, `search_vector` |
|
||||
| `property_contacts` | 业主/联系人(手机号 AES 加密+哈希索引) | `property_id`, `phone_enc`, `phone_hash`, `identity`, `is_number_holder` |
|
||||
| `listing_histories` | 挂牌历史快照(不可删除) | `property_id`, `listing_type`, `status`, `sale_price`, `seller_agent_snapshot` |
|
||||
| `price_changes` | 调价记录(不可删除) | `property_id`, `old_sale_price`, `new_sale_price`, `change_reason`, `changed_by` |
|
||||
| `follow_logs` | 跟进日志(6种类型,最高写入频率) | `property_id`, `log_type`, `content`, `is_deletable`, `operator_id` |
|
||||
| `follow_log_attachments` | 跟进附件(图片) | `follow_log_id`, `file_key`, `file_type` |
|
||||
| `follow_log_recordings` | 跟进录音 | `follow_log_id`, `file_key`, `duration_seconds` |
|
||||
| `property_keys` | 钥匙管理(机械钥匙/密码) | `property_id`, `key_type`, `holder_id`, `is_active` |
|
||||
| `key_attachments` | 钥匙附件 | `key_id`, `file_key` |
|
||||
| `commissions` | 委托管理(独家/非独家) | `property_id`, `commission_type`, `period_start`, `status` |
|
||||
| `commission_attachments` | 委托附件(身份证/产证/委托书) | `commission_id`, `category`, `file_key` |
|
||||
| `field_surveys` | 实勘管理(GPS 打卡) | `property_id`, `status`, `gps_latitude`, `gps_longitude`, `created_by` |
|
||||
| `survey_photos` | 实勘照片(按空间分类) | `survey_id`, `category`, `file_key`, `is_vr_screenshot` |
|
||||
| `property_photos` | 房源图片(经纪人管理,封面唯一约束) | `property_id`, `category`, `is_cover`, `file_key` |
|
||||
| `property_attachments` | 房源附件 | `property_id`, `category`, `file_key` |
|
||||
| `property_marketing` | 营销信息(1:1,卖点/业主心态/介绍) | `property_id`, `marketing_title`, `core_selling_points` |
|
||||
| `property_certificates` | 产证信息(1:1) | `property_id`, `cert_no`, `owner_name`, `land_nature` |
|
||||
| `property_completeness` | 维护完成度快照(1:1,Celery 异步计算) | `property_id`, `total_score`, `score_survey`, `score_commission`, ... |
|
||||
| `property_tags` | 标签字典(系统预置+运营自定义) | `name`, `color`, `is_system` |
|
||||
| `property_tag_relations` | 房源↔标签多对多 | `property_id`, `tag_id` |
|
||||
| `property_favorites` | 经纪人收藏房源 | `staff_id`, `property_id` |
|
||||
| `property_protections` | 保护房设置(1:1) | `property_id`, `is_protected`, `start_at`, `end_at` |
|
||||
| `number_holder_approvals` | 号码方变更审批 | `property_id`, `applicant_id`, `status` |
|
||||
| 表名 | 说明 | 关键字段 |
|
||||
| ------------------------- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `properties` | 房源主表(系统核心,89,000+ 数据量) | `status`, `attribute`, `property_type`, `complex_id`, `sale_price`, `area`, `grade`, `completeness_score`, `search_vector` |
|
||||
| `property_contacts` | 业主/联系人(手机号 AES 加密+哈希索引) | `property_id`, `phone_enc`, `phone_hash`, `identity`, `is_number_holder` |
|
||||
| `listing_histories` | 挂牌历史快照(不可删除) | `property_id`, `listing_type`, `status`, `sale_price`, `seller_agent_snapshot` |
|
||||
| `price_changes` | 调价记录(不可删除) | `property_id`, `old_sale_price`, `new_sale_price`, `change_reason`, `changed_by` |
|
||||
| `follow_logs` | 跟进日志(6种类型,最高写入频率) | `property_id`, `log_type`, `content`, `is_deletable`, `operator_id` |
|
||||
| `follow_log_attachments` | 跟进附件(图片) | `follow_log_id`, `file_key`, `file_type` |
|
||||
| `follow_log_recordings` | 跟进录音 | `follow_log_id`, `file_key`, `duration_seconds` |
|
||||
| `property_keys` | 钥匙管理(机械钥匙/密码) | `property_id`, `key_type`, `holder_id`, `is_active` |
|
||||
| `key_attachments` | 钥匙附件 | `key_id`, `file_key` |
|
||||
| `commissions` | 委托管理(独家/非独家) | `property_id`, `commission_type`, `period_start`, `status` |
|
||||
| `commission_attachments` | 委托附件(身份证/产证/委托书) | `commission_id`, `category`, `file_key` |
|
||||
| `field_surveys` | 实勘管理(GPS 打卡) | `property_id`, `status`, `gps_latitude`, `gps_longitude`, `created_by` |
|
||||
| `survey_photos` | 实勘照片(按空间分类) | `survey_id`, `category`, `file_key`, `is_vr_screenshot` |
|
||||
| `property_photos` | 房源图片(经纪人管理,封面唯一约束) | `property_id`, `category`, `is_cover`, `file_key` |
|
||||
| `property_attachments` | 房源附件 | `property_id`, `category`, `file_key` |
|
||||
| `property_marketing` | 营销信息(1:1,卖点/业主心态/介绍) | `property_id`, `marketing_title`, `core_selling_points` |
|
||||
| `property_certificates` | 产证信息(1:1) | `property_id`, `cert_no`, `owner_name`, `land_nature` |
|
||||
| `property_completeness` | 维护完成度快照(1:1,Celery 异步计算) | `property_id`, `total_score`, `score_survey`, `score_commission`, ... |
|
||||
| `property_tags` | 标签字典(系统预置+运营自定义) | `name`, `color`, `is_system` |
|
||||
| `property_tag_relations` | 房源↔标签多对多 | `property_id`, `tag_id` |
|
||||
| `property_favorites` | 经纪人收藏房源 | `staff_id`, `property_id` |
|
||||
| `property_protections` | 保护房设置(1:1) | `property_id`, `is_protected`, `start_at`, `end_at` |
|
||||
| `number_holder_approvals` | 号码方变更审批 | `property_id`, `applicant_id`, `status` |
|
||||
|
||||
**关键约束提示**:
|
||||
- `property_contacts.phone_hash` 是重复房源检测的主要依据,录入前必须查重
|
||||
@@ -293,6 +302,83 @@ OrgUnit (组织架构)
|
||||
|
||||
---
|
||||
|
||||
### 3.4 登录与账号认证(Login & Account)
|
||||
|
||||
> **详细模型** → 见 [`DATA_MODEL_LOGIN.md`](./DATA_MODEL_LOGIN.md)
|
||||
> 该文件为权威定义,包含完整字段、状态机、Redis 缓存结构和禁止操作。
|
||||
|
||||
**核心表概览**(开发时以 DATA_MODEL_LOGIN.md 为准):
|
||||
|
||||
| 表名 | 说明 |
|
||||
|------|------|
|
||||
| `user_accounts` | 账号主表(1:1 绑定 `org.Staff`),含加密手机号/哈希、状态机(active/locked/disabled)、初始密码标识 |
|
||||
| `login_attempts` | 登录审计日志(append-only,成功/失败均记录,无 FK 冗余存 username 保证历史完整) |
|
||||
| `password_reset_tokens` | 密码重置 Token(有效期 30 分钟,使用后立即标记 `is_used`) |
|
||||
| `password_histories` | 历史密码记录(保留最近 3 条,含初始密码,防止重复使用) |
|
||||
|
||||
**关键约束提示**:
|
||||
- `user_accounts` 主键用 `BIGSERIAL`(非 UUID),登录审计场景 BigInt 更高效
|
||||
- `user_accounts.phone_enc` AES-256-GCM 加密,`phone_hash` SHA-256 用于唯一索引
|
||||
- **禁止物理删除** `user_accounts`,离职员工只能 `status=disabled`
|
||||
- 账号锁定(5 次密码错误)→ `status=locked`,`locked_until=NOW()+30min`;Redis 仅计数,实际锁定以 DB 为准
|
||||
- Tenant Admin 的 `staff_id` 可为空(可无员工档案);普通员工 `staff_id` 必填且关联 active Staff
|
||||
- 员工离职(`org.Staff.status→resigned`)→ 应用层 Service 调用触发账号 `status→disabled`,**禁止循环 FK**
|
||||
- `password_reset_tokens` / `login_attempts` **无 deleted_at**,不可修改/删除
|
||||
|
||||
**Redis 辅助状态**(非持久化):
|
||||
|
||||
| Key 格式 | TTL | 说明 |
|
||||
|----------|-----|------|
|
||||
| `captcha_token:{uuid}` | 3 分钟 | 滑块验证会话 Token |
|
||||
| `captcha_pass:{uuid}` | 3 分钟 | 一次性通过凭证(验证后立即删除) |
|
||||
| `login_fail:{tenant_id}:{username}` | 30 分钟 | 连续密码错误计数;≥5 触发锁定 |
|
||||
| `recover_email:{email}` | 1 小时 | 找回邮件发送次数上限 3 次 |
|
||||
| `tenant_verify_ip:{ip}` | 1 分钟 | Tenant 验证接口 IP 限流;≥10 次拒绝 |
|
||||
|
||||
---
|
||||
|
||||
### 3.5 权限管理(Permission & RBAC)
|
||||
|
||||
> **详细模型** → 见 [`DATA_MODEL_PERMISSION.md`](./DATA_MODEL_PERMISSION.md)
|
||||
> 该文件为权威定义,包含完整字段、权限解析算法、`ScopeQueryBuilder` 实现和禁止操作。
|
||||
|
||||
**权限模型概述**:Hybrid RBAC + Individual Override,支持 `BOOLEAN / SCOPE / INTEGER` 三类权限值,多角色合并规则 OR / MAX。
|
||||
|
||||
**核心表概览**(开发时以 DATA_MODEL_PERMISSION.md 为准):
|
||||
|
||||
| 表名 | 说明 |
|
||||
|------|------|
|
||||
| `permission_defs` | 权限目录(约 300 条,`PUBLIC Schema` 中 `shared_apps` 存储,所有租户共享),含模块/分组/值类型/默认值/上限类别 |
|
||||
| `roles` | 业务角色(每租户独立),5 种类别:`agent/store_manager/director/operator/custom`,含系统内置标识 |
|
||||
| `role_permissions` | 角色↔权限值(稀疏存储,仅存与 default_value 不同的项) |
|
||||
| `staff_roles` | 员工↔角色分配(N:M,含主角色标识 `is_primary`、有效期) |
|
||||
| `staff_permission_overrides` | 员工个人权限覆盖(稀疏存储,仅存与角色合并值不同的项),3 种 override_mode:REPLACE / RESTRICT / GRANT |
|
||||
| `staff_data_scopes` | 员工数据范围扩展(补充 SCOPE 权限之外的额外可读范围,如特殊跨门店授权) |
|
||||
| `permission_change_logs` | 权限变更不可变审计日志(append-only,禁止 UPDATE/DELETE) |
|
||||
|
||||
**关键约束提示**:
|
||||
- `permission_defs` 位于 **Public Schema**(`shared_apps`),所有租户共享;`roles` 及其余表属租户 Schema
|
||||
- **禁止硬删除** `permission_defs`,改用 `is_active=FALSE` 下线;`code` 字段不可修改
|
||||
- **禁止直接构造 Q 对象绕过 `ScopeQueryBuilder`**,会导致越权漏洞
|
||||
- `permission_change_logs` **无 deleted_at**,禁止 UPDATE/DELETE
|
||||
- 员工权限解析:`is_system_admin=TRUE` → 短路返回全权限;否则多角色 OR/MAX 合并后叠加 Override
|
||||
- `StaffPermissionOverride` 保存前必须做差异对比,**禁止存与角色合并值相同的冗余记录**(稀疏存储)
|
||||
- `staff_roles.is_primary` 唯一约束通过 Signal 维护,**禁止绕过**
|
||||
|
||||
**权限解析缓存**:
|
||||
|
||||
| Cache Key | TTL | 失效触发 |
|
||||
|-----------|-----|---------|
|
||||
| `perm:v{VER}:{schema}:{staff_id}` | 3600s | Override / StaffRole 变更 |
|
||||
| `perm:v{VER}:{schema}:role:{role_id}:staff_ids` | 3600s | 角色权限变更 → Pipeline 批量失效 |
|
||||
| `perm:inconsistent:{schema}:{staff_id}` | 300s | 同上 |
|
||||
| `perm:defs:{schema}` | 86400s | PermissionDef 变更(低频) |
|
||||
| `perm:role_applied_count:{schema}:{role_id}` | 600s | StaffRole 变更 |
|
||||
|
||||
> **版本号机制**:`CACHE_VERSION` 在 Django settings 中,升级 PermissionDef 结构时 bump,一键全局失效。
|
||||
|
||||
---
|
||||
|
||||
### 3.17 客源管理(Client Management)
|
||||
|
||||
> **详细模型** → 见 [`DATA_MODEL_CLIENT.md`](./DATA_MODEL_CLIENT.md)
|
||||
@@ -381,6 +467,73 @@ CREATE INDEX idx_saved_filters_staff ON saved_filters(staff_id, module);
|
||||
|
||||
---
|
||||
|
||||
### 3.19 枚举字典(Enum Labels)
|
||||
|
||||
> **权威定义** → 见 [`DATA_MODEL/ENUMS.md`](./ENUMS.md)
|
||||
> 本节为概览,开发时以 ENUMS.md 为准。
|
||||
|
||||
#### 表归属
|
||||
|
||||
`enum_labels` 位于 **Public Schema**(`shared_apps`),所有租户共享,**不属于任何租户 Schema**。
|
||||
|
||||
#### 核心表设计
|
||||
|
||||
```sql
|
||||
CREATE TABLE enum_labels (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
domain VARCHAR(60) NOT NULL, -- 枚举域,格式:{模块}.{字段},如 client.status
|
||||
value VARCHAR(60) NOT NULL, -- 英文 Key(与数据库 CHECK 约束一致)
|
||||
label_zh VARCHAR(60) NOT NULL, -- 中文标签(前端展示用)
|
||||
sort_order SMALLINT NOT NULL DEFAULT 0,
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
remark TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX idx_enum_labels_domain_value ON enum_labels(domain, value);
|
||||
CREATE INDEX idx_enum_labels_domain ON enum_labels(domain, sort_order);
|
||||
```
|
||||
|
||||
#### 覆盖的枚举域(domain 清单)
|
||||
|
||||
| domain | 说明 | 对应表字段 |
|
||||
|--------|------|-----------|
|
||||
| `client.status` | 客源状态(7 态) | `clients.status` |
|
||||
| `client.grade` | 客源等级(5 档 + E) | `clients.grade` |
|
||||
| `client.purpose_type` | 需求类型 | `client_requirements.purpose_type` |
|
||||
| `client.usage` | 房源用途偏好 | `client_requirements.usage` |
|
||||
| `client.orientation` | 朝向偏好 | `client_requirements.orientation` |
|
||||
| `client.payment_method` | 付款方式 | `clients.payment_method` |
|
||||
| `property.status` | 房源状态 | `properties.status` |
|
||||
| `property.attribute` | 房源属性(公/私/保护) | `properties.attribute` |
|
||||
| `property.usage` | 房源用途 | `properties.usage` |
|
||||
| `property.grade` | 房源等级(5 档) | `properties.grade` |
|
||||
| `property.listing_type` | 挂牌类型 | `properties.listing_type` |
|
||||
| `property.decoration` | 装修程度 | `properties.decoration` |
|
||||
| `property.orientation` | 朝向 | `properties.orientation` |
|
||||
| `commission.type` | 委托类型 | `commissions.commission_type` |
|
||||
| `field_survey.status` | 实勘状态 | `field_surveys.status` |
|
||||
| `follow_log.log_type` | 跟进日志类型 | `follow_logs.log_type` |
|
||||
|
||||
#### 重要约定
|
||||
|
||||
- `enum_labels.value` 必须与对应表的 `CHECK` 约束完全一致,**两者必须同步修改**
|
||||
- 新增枚举值流程:① 修改 DDL `CHECK` 约束 → ② 插入 `enum_labels` 种子数据 → ③ 更新 `ENUMS.md`
|
||||
- `is_active = FALSE` 仅停用前端展示,**不得修改或删除已有 `value`**(历史数据引用不可破坏)
|
||||
- 前端下拉渲染**统一从 `enum_labels` 读取**,禁止在前端代码中硬编码中文标签
|
||||
|
||||
#### 与 `lookup_items` 的区别
|
||||
|
||||
| 对比维度 | `enum_labels` | `lookup_items` |
|
||||
|---------|---------------|----------------|
|
||||
| 用途 | 固定枚举的中文标签映射 | 运营可配置的动态选项(如跟进目的、来源渠道) |
|
||||
| 修改权限 | 仅开发/DBA | 运营人员后台配置 |
|
||||
| Schema 位置 | Public Schema(共享) | Tenant Schema(每租户独立) |
|
||||
| 典型示例 | 客源状态、房源等级 | 跟进目的、客户来源渠道 |
|
||||
|
||||
---
|
||||
|
||||
## 五、关键索引汇总与查询优化策略
|
||||
|
||||
### 4.1 房源列表页核心查询分析
|
||||
@@ -489,6 +642,21 @@ CREATE TRIGGER trg_update_last_followed
|
||||
# 枚举值/lookup(几乎不变)
|
||||
{schema}:lookup:{category_code} TTL: 86400 (24小时)
|
||||
|
||||
# 登录模块(详见 DATA_MODEL_LOGIN.md §四)
|
||||
captcha_token:{uuid} TTL: 180 (3分钟)
|
||||
captcha_pass:{uuid} TTL: 180 (3分钟)
|
||||
login_fail:{tenant_id}:{username} TTL: 1800 (30分钟,连续失败计数)
|
||||
recover_email:{email} TTL: 3600 (1小时,发送次数限流)
|
||||
recover_reset:{account_id} TTL: 3600 (1小时,Token 生成次数限流)
|
||||
tenant_verify_ip:{ip} TTL: 60 (1分钟,IP 限流)
|
||||
|
||||
# 权限模块(详见 DATA_MODEL_PERMISSION.md §六)
|
||||
perm:v{VER}:{schema}:{staff_id} TTL: 3600 (员工完整权限快照)
|
||||
perm:v{VER}:{schema}:role:{role_id}:staff_ids TTL: 3600 (角色→员工 ID 列表,批量失效用)
|
||||
perm:inconsistent:{schema}:{staff_id} TTL: 300 (权限不一致标记)
|
||||
perm:defs:{schema} TTL: 86400 (权限定义全量缓存)
|
||||
perm:role_applied_count:{schema}:{role_id} TTL: 600 (角色应用人数)
|
||||
|
||||
# 标签列表
|
||||
{schema}:tags:property TTL: 3600
|
||||
|
||||
|
||||
@@ -357,12 +357,12 @@ CREATE UNIQUE INDEX idx_favorite_folders_default ON client_favorite_folders(staf
|
||||
|
||||
### 3.10 client_folder_items — 收藏夹中的客源
|
||||
|
||||
| 字段 | 类型 | 约束 | 业务说明 |
|
||||
|------|------|------|----------|
|
||||
| folder_id | UUID | NOT NULL, FK→client_favorite_folders, CASCADE | |
|
||||
| client_id | UUID | NOT NULL, FK→clients, CASCADE | |
|
||||
| added_at | TIMESTAMPTZ | NOT NULL DEFAULT NOW() | |
|
||||
| PRIMARY KEY | (folder_id, client_id) | | |
|
||||
| 字段 | 类型 | 约束 | 业务说明 |
|
||||
| ----------- | ---------------------- | --------------------------------------------- | ---- |
|
||||
| folder_id | UUID | NOT NULL, FK→client_favorite_folders, CASCADE | |
|
||||
| client_id | UUID | NOT NULL, FK→clients, CASCADE | |
|
||||
| added_at | TIMESTAMPTZ | NOT NULL DEFAULT NOW() | |
|
||||
| PRIMARY KEY | (folder_id, client_id) | | |
|
||||
|
||||
```sql
|
||||
CREATE INDEX idx_folder_items_client ON client_folder_items(client_id);
|
||||
@@ -415,8 +415,7 @@ buying/renting/buy_or_rent
|
||||
### clients.grade(等级)
|
||||
|
||||
```
|
||||
A_urgent = A(急迫)
|
||||
A = A
|
||||
A = A(急迫)
|
||||
B = B(较强)
|
||||
C = C(一般,默认值)
|
||||
D = D(较弱)
|
||||
@@ -438,15 +437,15 @@ merge = 合并客源(被合并的记录保留日志)
|
||||
|
||||
### clients.activity_level(活跃度分层,系统计算)
|
||||
|
||||
| 值 | 含义 | 触发条件(示例,以运营配置为准) |
|
||||
|----|------|------|
|
||||
| `new_matched` | 新配偶 | 录入后 3 天内 |
|
||||
| `active_7d` | 7日活跃 | 最后跟进在 7 天内 |
|
||||
| `active_30d` | 30日活跃 | 最后跟进在 30 天内 |
|
||||
| `active_90d` | 90日活跃 | 最后跟进在 90 天内 |
|
||||
| `expiring` | 即将过期 | 距自动转公还有 N 天 |
|
||||
| `frozen` | 冻结(暂缓) | status = suspended |
|
||||
| `invalid` | 无效 | status = invalid |
|
||||
| 值 | 含义 | 触发条件(示例,以运营配置为准) |
|
||||
| ------------- | ------ | ------------------ |
|
||||
| `new_matched` | 新匹配 | 录入后 3 天内 |
|
||||
| `active_7d` | 7日活跃 | 最后跟进在 7 天内 |
|
||||
| `active_30d` | 30日活跃 | 最后跟进在 30 天内 |
|
||||
| `active_90d` | 90日活跃 | 最后跟进在 90 天内 |
|
||||
| `expiring` | 即将过期 | 距自动转公还有 N 天 |
|
||||
| `frozen` | 冻结(暂缓) | status = suspended |
|
||||
| `invalid` | 无效 | status = invalid |
|
||||
|
||||
---
|
||||
|
||||
|
||||
391
Project/fonrey/DATA_MODEL/DATA_MODEL_SETTING.md
Normal file
391
Project/fonrey/DATA_MODEL/DATA_MODEL_SETTING.md
Normal file
@@ -0,0 +1,391 @@
|
||||
> **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`(枚举分组)
|
||||
|
||||
每个分组代表一类可配置枚举(如「客源来源」「跟进目的」),由研发预置,租户管理员**不可新增或删除分组**,仅可管理分组内的选项。
|
||||
|
||||
```sql
|
||||
-- ============================================================
|
||||
-- 可配置枚举分组(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`(枚举选项)
|
||||
|
||||
```sql
|
||||
-- ============================================================
|
||||
-- 可配置枚举选项(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)等标量类型配置项。
|
||||
|
||||
```sql
|
||||
-- ============================================================
|
||||
-- 租户标量配置(键值对)(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`(字段必填规则表)
|
||||
|
||||
按「模块 × 房源用途 × 交易状态 × 字段」四元组确定一条规则,控制录入界面的字段显示状态。
|
||||
|
||||
```sql
|
||||
-- ============================================================
|
||||
-- 字段必填/隐藏规则(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` | 车位数 | 数值 |
|
||||
|
||||
---
|
||||
|
||||
## 四、服务层设计
|
||||
|
||||
所有业务模块**禁止直接查询配置表**,必须通过统一服务层读取:
|
||||
|
||||
```python
|
||||
# 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/*` | 录入场景的交易意图与房源全生命周期状态不同,避免耦合 |
|
||||
762
Project/fonrey/DATA_MODEL/ENUMS.md
Normal file
762
Project/fonrey/DATA_MODEL/ENUMS.md
Normal file
@@ -0,0 +1,762 @@
|
||||
> **For AI assistants**: Read this entire file before writing any code. All decisions here are final. Do not suggest alternatives unless asked.
|
||||
|
||||
# Fonrey — 统一枚举字典(ENUMS)
|
||||
|
||||
> **定位**:本文件是 Fonrey 全局枚举标准(Public + Tenant)的统一实现基线。
|
||||
> **版本**:v2.1
|
||||
> **日期**:2026-04-27
|
||||
> **适用范围**:`DATA_MODEL_PUBLIC.md`、`DATA_MODEL_LOGIN.md`、`DATA_MODEL_ORG.md`、`DATA_MODEL_COMPLEX.md`、`DATA_MODEL_PROPERTY.md`、`DATA_MODEL_CLIENT.md`、`DATA_MODEL_PERMISSION.md`、`DATA_MODEL_SETTING.md`
|
||||
|
||||
---
|
||||
|
||||
## 一、枚举分层标准(必须遵守)
|
||||
|
||||
Fonrey 采用两层枚举体系:
|
||||
|
||||
1. **固定枚举(Fixed Enum)**
|
||||
- 值域固定,受 `CHECK` 或强业务约束保护
|
||||
- 作为系统契约,不能随意改值
|
||||
- 可落地到 `public.enum_labels`(用于统一标签)
|
||||
|
||||
2. **可配置枚举(Configurable Enum)**
|
||||
- 值域由租户自行维护
|
||||
- 存储在 Tenant Schema:`lookup_groups` + `lookup_items`
|
||||
- **禁止**对业务表字段加固定 `CHECK IN (...)`
|
||||
|
||||
---
|
||||
|
||||
## 二、全局固定枚举(Public / 平台级)
|
||||
|
||||
### 2.1 tenant 生命周期
|
||||
|
||||
**domain**: `public.tenant.plan`
|
||||
- `basic`:基础版
|
||||
- `professional`:专业版
|
||||
- `enterprise`:企业版
|
||||
|
||||
**domain**: `public.tenant.status`
|
||||
- `creating`:创建中
|
||||
- `active`:正常
|
||||
- `suspended`:已挂起
|
||||
- `pending_delete`:待删除
|
||||
- `deleted`:已删除
|
||||
- `failed`:创建/初始化失败
|
||||
|
||||
**domain**: `public.tenant.suspended_reason`
|
||||
- `overdue`:欠费
|
||||
- `violation`:违规
|
||||
- `requested`:客户申请
|
||||
- `other`:其他
|
||||
|
||||
### 2.2 平台管理员
|
||||
|
||||
**domain**: `public.platform_admin.role`
|
||||
- `super_admin`:超级管理员
|
||||
- `ops_operator`:运营管理员
|
||||
- `read_only_auditor`:只读审计员
|
||||
|
||||
### 2.3 平台审计与备份导出
|
||||
|
||||
**domain**: `public.platform_audit.result`
|
||||
- `SUCCESS`:成功
|
||||
- `FAILED`:失败
|
||||
|
||||
**domain**: `public.backup_schedule.frequency`
|
||||
- `hourly`:每小时
|
||||
- `daily`:每日
|
||||
- `weekly`:每周
|
||||
|
||||
**domain**: `public.backup_schedule.storage_target`
|
||||
- `local`:本地存储
|
||||
- `s3`:Amazon S3
|
||||
- `r2`:Cloudflare R2
|
||||
- `gcs`:Google Cloud Storage
|
||||
|
||||
**domain**: `public.backup_record.trigger_type`
|
||||
- `auto`:自动触发
|
||||
- `manual`:手动触发
|
||||
- `pre_upgrade`:升级前触发
|
||||
- `pre_restore`:恢复前触发
|
||||
|
||||
**domain**: `public.backup_record.status`
|
||||
- `pending`:待执行
|
||||
- `in_progress`:执行中
|
||||
- `success`:成功
|
||||
- `failed`:失败
|
||||
|
||||
**domain**: `public.export_task.format`
|
||||
- `csv`:CSV
|
||||
- `json`:JSON
|
||||
- `sql_dump`:SQL 导出
|
||||
|
||||
**domain**: `public.export_task.status`
|
||||
- `pending`:待执行
|
||||
- `in_progress`:执行中
|
||||
- `done`:已完成
|
||||
- `failed`:失败
|
||||
|
||||
### 2.4 升级与发布(Public)
|
||||
|
||||
**domain**: `public.upgrade_event.event_type`
|
||||
- `upgrade`:升级
|
||||
- `rollback`:回滚
|
||||
|
||||
**domain**: `public.upgrade_event.upgrade_type`
|
||||
- `A_app`:A类应用升级
|
||||
- `B_schema`:B类数据库结构升级
|
||||
- `C_feature`:C类功能开关升级
|
||||
|
||||
**domain**: `public.upgrade_event.strategy`
|
||||
- `full`:全量发布
|
||||
- `canary`:灰度发布
|
||||
|
||||
**domain**: `public.upgrade_event.status`
|
||||
- `draft`:草稿
|
||||
- `pre_check`:预检查
|
||||
- `pre_backup`:预备份
|
||||
- `batch_running`:批次执行中
|
||||
- `batch_done`:批次完成
|
||||
- `halted`:已暂停
|
||||
- `succeeded`:已成功
|
||||
- `failed`:失败
|
||||
- `rollback_running`:回滚中
|
||||
- `rolled_back`:已回滚
|
||||
|
||||
**domain**: `public.upgrade_event.failure_policy`
|
||||
- `halt_batch`:失败即停止批次
|
||||
- `continue`:失败继续
|
||||
|
||||
**domain**: `public.client_release.platform`
|
||||
- `win32`:Windows 客户端
|
||||
|
||||
**domain**: `public.client_release.arch`
|
||||
- `x64`:x64 架构
|
||||
- `arm64`:ARM64 架构
|
||||
|
||||
**domain**: `public.client_release.release_type`
|
||||
- `normal`:普通更新
|
||||
- `force`:强制更新
|
||||
|
||||
**domain**: `public.client_release.status`
|
||||
- `draft`:草稿
|
||||
- `published`:已发布
|
||||
- `archived`:已归档
|
||||
|
||||
---
|
||||
|
||||
## 三、Tenant 固定枚举(模块级,值域统一)
|
||||
|
||||
> 说明:以下字段在 Tenant Schema 中存储,但值域为系统统一标准,属于“全局实现标准”。
|
||||
|
||||
## 3.1 登录认证(account/login)
|
||||
|
||||
**domain**: `login.user_account.status`
|
||||
- `active`:启用
|
||||
- `disabled`:停用
|
||||
- `locked`:锁定
|
||||
|
||||
**domain**: `login.login_attempt.failure_reason`
|
||||
- `wrong_password`:用户名或密码错误
|
||||
- `wrong_captcha`:验证码错误
|
||||
- `account_locked`:账号锁定
|
||||
- `account_disabled`:账号停用
|
||||
- `tenant_not_found`:租户不存在
|
||||
|
||||
---
|
||||
|
||||
## 3.2 组织人事(org)
|
||||
|
||||
**domain**: `org.org_unit.type`
|
||||
- `company`:公司
|
||||
- `division`:事业部
|
||||
- `region`:大区
|
||||
- `area`:区域
|
||||
- `district`:片区
|
||||
- `store`:门店
|
||||
- `group`:店组
|
||||
- `functional`:职能部门
|
||||
|
||||
**domain**: `org.org_unit.attribute`
|
||||
- `direct`:直营
|
||||
- `franchise`:加盟
|
||||
|
||||
**domain**: `org.staff.role`
|
||||
- `agent`:经纪人
|
||||
- `store_manager`:店长
|
||||
- `area_manager`:区域经理
|
||||
- `admin`:系统管理员
|
||||
- `operator`:运营/行政
|
||||
- `system`:系统账号
|
||||
|
||||
**domain**: `org.staff.status`
|
||||
- `active`:在职
|
||||
- `probation`:试用
|
||||
- `resigned`:离职
|
||||
- `frozen`:冻结
|
||||
|
||||
**domain**: `org.staff_personal_info.gender`
|
||||
- `male`:男
|
||||
- `female`:女
|
||||
- `unknown`:未知
|
||||
|
||||
**domain**: `org.staff_personal_info.id_type`
|
||||
- `id_card`:身份证
|
||||
- `passport`:护照
|
||||
- `other`:其他
|
||||
|
||||
**domain**: `org.staff_transfer.transfer_type`
|
||||
- `onboard`:入职
|
||||
- `transfer`:调动
|
||||
- `resign`:离职
|
||||
- `rejoin`:复职
|
||||
- `supervisor_change`:上级变更
|
||||
- `role_change`:角色变更
|
||||
- `freeze`:冻结账号
|
||||
- `unfreeze`:恢复账号
|
||||
|
||||
**domain**: `org.staff_account.platform`
|
||||
- `fonrey`:房睿主账号
|
||||
- `58anjuke`:58安居客
|
||||
- `cnreic`:中国网络经纪人
|
||||
- `wechat_mp`:微信公众号
|
||||
|
||||
---
|
||||
|
||||
## 3.3 权限系统(permission)
|
||||
|
||||
**domain**: `permission.module`
|
||||
- `home`:首页
|
||||
- `property`:房源
|
||||
- `new_house`:新房
|
||||
- `client`:客源
|
||||
- `transaction`:交易
|
||||
- `data`:数据
|
||||
- `marketing`:营销
|
||||
- `hr`:人事OA
|
||||
- `contract`:合同
|
||||
- `trinet`:三网
|
||||
- `system`:系统
|
||||
- `mobile`:移动端
|
||||
- `smart_store`:智能门店
|
||||
- `recharge`:在线充值
|
||||
|
||||
**domain**: `permission.value_type`
|
||||
- `BOOLEAN`:开关型
|
||||
- `SCOPE`:范围型
|
||||
- `INTEGER`:数值型
|
||||
|
||||
**domain**: `permission.role_category`
|
||||
- `agent`:置业顾问
|
||||
- `store_manager`:店管
|
||||
- `director`:总经
|
||||
- `operator`:运营/行政
|
||||
- `custom`:自定义
|
||||
|
||||
**domain**: `permission.scope_level`
|
||||
- `none`:无
|
||||
- `self`:本人
|
||||
- `group`:本组
|
||||
- `store`:本门店
|
||||
- `area`:本区域
|
||||
- `region`:本大区
|
||||
- `company`:全公司
|
||||
|
||||
**domain**: `permission.override_mode`
|
||||
- `REPLACE`:覆盖
|
||||
- `RESTRICT`:限制
|
||||
- `GRANT`:授予
|
||||
|
||||
**domain**: `permission.data_scope_type`
|
||||
- `self`:本人
|
||||
- `group`:本组
|
||||
- `store`:本门店
|
||||
- `area`:本区域
|
||||
- `region`:本大区
|
||||
- `company`:全公司
|
||||
- `custom_unit`:自定义组织单元
|
||||
|
||||
**domain**: `permission.change_log.target_type`
|
||||
- `role`:角色
|
||||
- `role_permission`:角色权限
|
||||
- `staff_role`:员工角色
|
||||
- `staff_override`:员工权限覆盖
|
||||
- `staff_scope`:员工数据范围
|
||||
|
||||
**domain**: `permission.change_log.action`
|
||||
- `create`:创建
|
||||
- `update`:更新
|
||||
- `delete`:删除
|
||||
- `assign`:分配
|
||||
- `revoke`:撤销
|
||||
|
||||
---
|
||||
|
||||
## 3.4 楼盘区域(complex)
|
||||
|
||||
**domain**: `complex.school.type`
|
||||
- `primary`:小学
|
||||
- `middle`:初中
|
||||
- `high`:高中
|
||||
- `k9`:九年一贯制
|
||||
- `k12`:十二年一贯制
|
||||
|
||||
**domain**: `complex.school.nature`
|
||||
- `public`:公立
|
||||
- `private`:私立
|
||||
- `international`:国际
|
||||
|
||||
**domain**: `complex.school.level`
|
||||
- `normal`:普通
|
||||
- `key`:重点
|
||||
- `top`:名校
|
||||
|
||||
**domain**: `complex.building_type`
|
||||
- `slab`:板楼
|
||||
- `tower`:塔楼
|
||||
- `slab_tower`:板塔结合
|
||||
|
||||
**domain**: `complex.water_type`
|
||||
- `civil`:民水
|
||||
- `commercial`:商水
|
||||
|
||||
**domain**: `complex.electricity_type`
|
||||
- `civil`:民电
|
||||
- `commercial`:商电
|
||||
|
||||
**domain**: `complex.school_zone_type`
|
||||
- `guaranteed`:对口
|
||||
- `reference`:参考
|
||||
- `lottery`:摇号
|
||||
|
||||
**domain**: `complex.photo.category`
|
||||
- `complex`:楼盘图
|
||||
- `layout`:户型图
|
||||
- `vr`:VR图
|
||||
- `other`:其他
|
||||
|
||||
---
|
||||
|
||||
## 3.5 房源(property)
|
||||
|
||||
**domain**: `property.property_type`
|
||||
- `residential`:住宅
|
||||
- `villa`:别墅
|
||||
- `commercial_residential`:商住
|
||||
- `shop`:商铺
|
||||
- `office`:写字楼
|
||||
- `other`:其他
|
||||
|
||||
**domain**: `property.status`
|
||||
- `for_sale`:出售
|
||||
- `for_rent`:出租
|
||||
- `for_sale_rent`:租售
|
||||
- `suspended`:暂缓
|
||||
- `sold_elsewhere`:他售
|
||||
- `rented_elsewhere`:他租
|
||||
- `sold`:成交
|
||||
- `unlisted`:未挂牌
|
||||
|
||||
**domain**: `property.attribute`
|
||||
- `public`:公盘
|
||||
- `private`:私盘
|
||||
- `special`:特盘
|
||||
- `sealed`:封盘
|
||||
|
||||
**domain**: `property.orientation`
|
||||
- `east`:东
|
||||
- `south`:南
|
||||
- `west`:西
|
||||
- `north`:北
|
||||
- `southeast`:东南
|
||||
- `northeast`:东北
|
||||
- `east_west`:东西
|
||||
- `south_north`:南北
|
||||
- `northwest`:西北
|
||||
- `southwest`:西南
|
||||
|
||||
**domain**: `property.decoration`
|
||||
- `rough`:毛坯
|
||||
- `plain`:清水
|
||||
- `simple`:简装
|
||||
- `medium`:中装
|
||||
- `fine`:精装
|
||||
- `luxury`:豪装
|
||||
|
||||
**domain**: `property.house_status`
|
||||
- `owner_occupied`:业主自住
|
||||
- `vacant`:空置
|
||||
- `tenant_occupied`:租客在住
|
||||
- `unknown`:未知
|
||||
|
||||
**domain**: `property.viewing_time`
|
||||
- `anytime`:随时看房
|
||||
- `by_appointment`:预约看房
|
||||
- `inconvenient`:不便看房
|
||||
|
||||
**domain**: `property.grade`
|
||||
- `A_urgent`:A(急迫)
|
||||
- `A`:A
|
||||
- `B`:B(较强)
|
||||
- `C`:C(一般)
|
||||
- `D`:D(较弱)
|
||||
|
||||
**domain**: `property.contact.gender`
|
||||
- `male`:先生
|
||||
- `female`:女士
|
||||
|
||||
**domain**: `property.contact.identity`
|
||||
- `owner`:业主
|
||||
- `contact`:联系人
|
||||
- `subletter`:转租人
|
||||
- `tenant`:租客
|
||||
- `agent`:代理人
|
||||
- `corporate`:企业法人
|
||||
|
||||
**domain**: `property.listing_history.listing_type`
|
||||
- `for_sale`:出售挂牌
|
||||
- `for_rent`:出租挂牌
|
||||
|
||||
**domain**: `property.listing_history.status`
|
||||
- `active`:生效中
|
||||
- `ended`:已结束
|
||||
|
||||
**domain**: `property.follow_log.log_type`
|
||||
- `written`:手写跟进
|
||||
- `modified`:修改跟进
|
||||
- `sensitive_op`:敏感操作
|
||||
- `sensitive_view`:敏感查看
|
||||
- `other`:其他
|
||||
- `system`:系统
|
||||
|
||||
**domain**: `property.follow_log.ai_tag`
|
||||
- `ai_for_sale`:AI判断可售
|
||||
- `ai_not_for_sale`:AI判断不可售
|
||||
|
||||
**domain**: `property.follow_attachment.file_type`
|
||||
- `bmp`:BMP
|
||||
- `jpg`:JPG
|
||||
- `png`:PNG
|
||||
- `svg`:SVG
|
||||
- `gif`:GIF
|
||||
|
||||
**domain**: `property.key.key_type`
|
||||
- `mechanical`:机械钥匙
|
||||
- `password`:密码钥匙
|
||||
|
||||
**domain**: `property.commission.owner_type`
|
||||
- `owner`:产权人本人
|
||||
- `authorized_third`:授权第三方
|
||||
|
||||
**domain**: `property.commission.status`
|
||||
- `active`:有效
|
||||
- `expired`:过期
|
||||
- `cancelled`:取消
|
||||
|
||||
**domain**: `property.commission_attachment.category`
|
||||
- `id_card`:身份证件
|
||||
- `property_cert`:产权证明
|
||||
- `commission_letter`:委托书
|
||||
- `other`:其他
|
||||
|
||||
**domain**: `property.field_survey.status`
|
||||
- `draft`:草稿
|
||||
- `submitted`:已提交
|
||||
|
||||
**domain**: `property.survey_photo.category`
|
||||
- `layout`:户型图
|
||||
- `living_room`:客厅
|
||||
- `dining_room`:餐厅
|
||||
- `bedroom`:卧室
|
||||
- `bathroom`:卫生间
|
||||
- `kitchen`:厨房
|
||||
- `entrance`:入户
|
||||
- `balcony`:阳台
|
||||
- `study`:书房
|
||||
- `indoor_other`:室内其他
|
||||
- `outdoor`:室外
|
||||
|
||||
**domain**: `property.photo.category`
|
||||
- `cover`:封面
|
||||
- `entrance`:入户
|
||||
- `living_room`:客厅
|
||||
- `dining_room`:餐厅
|
||||
- `bedroom`:卧室
|
||||
- `bathroom`:卫生间
|
||||
- `kitchen`:厨房
|
||||
- `balcony`:阳台
|
||||
- `study`:书房
|
||||
- `indoor_other`:室内其他
|
||||
- `outdoor`:室外
|
||||
- `panorama`:全景
|
||||
|
||||
**domain**: `property.attachment.category`
|
||||
- `id_card`:身份证件
|
||||
- `property_cert`:产权证明
|
||||
- `commission_letter`:委托书
|
||||
- `other`:其他
|
||||
|
||||
**domain**: `property.number_holder_approval.status`
|
||||
- `pending`:待审批
|
||||
- `approved`:已通过
|
||||
- `rejected`:已驳回
|
||||
|
||||
---
|
||||
|
||||
## 3.6 客源(client)
|
||||
|
||||
**domain**: `client.client_type`
|
||||
- `private`:私客
|
||||
- `public`:公客
|
||||
- `transacted`:成交客
|
||||
|
||||
**domain**: `client.status`
|
||||
- `buying`:求购
|
||||
- `renting`:求租
|
||||
- `buy_or_rent`:租购
|
||||
- `suspended`:暂缓
|
||||
- `bought`:已购
|
||||
- `rented_done`:已租
|
||||
- `public`:公客
|
||||
- `invalid`:无效
|
||||
|
||||
**domain**: `client.grade`
|
||||
- `A`:A(急迫)
|
||||
- `B`:B(较强)
|
||||
- `C`:C(一般)
|
||||
- `D`:D(较弱)
|
||||
- `E`:E(暂不关注)
|
||||
|
||||
**domain**: `client.property_usage`
|
||||
- `residential`:住宅
|
||||
- `villa`:别墅
|
||||
- `commercial_residential`:商住
|
||||
- `shop`:商铺
|
||||
- `office`:写字楼
|
||||
- `other`:其他
|
||||
|
||||
**domain**: `client.buying_purpose`
|
||||
- `rigid`:刚需
|
||||
- `investment`:投资
|
||||
- `school_district`:学区
|
||||
- `upgrade`:改善
|
||||
- `commercial`:商用
|
||||
- `other`:其他
|
||||
|
||||
**domain**: `client.payment_method`
|
||||
- `full`:全额
|
||||
- `mortgage`:商业贷款
|
||||
- `mortgage_fund`:商贷+公积金
|
||||
- `fund`:公积金
|
||||
|
||||
**domain**: `client.properties_owned`
|
||||
- `none`:无
|
||||
- `local_none`:本地无/外地有
|
||||
- `local_has`:本地有
|
||||
|
||||
**domain**: `client.id_type`
|
||||
- `id_card`:身份证
|
||||
- `passport`:护照
|
||||
- `hk_macao`:港澳通行证
|
||||
- `other`:其他
|
||||
|
||||
**domain**: `client.transfer_to_public_type`
|
||||
- `manual`:手动转公
|
||||
- `auto`:自动转公
|
||||
- `marketing_jump`:营销客跳公
|
||||
- `resource_public`:资料客素公
|
||||
|
||||
**domain**: `client.invalid_reason`
|
||||
- `invalid_phone`:号码无效
|
||||
- `peer_agent`:同行
|
||||
- `ad`:广告推销
|
||||
- `no_intent`:无意向
|
||||
- `other`:其他
|
||||
|
||||
**domain**: `client.transacted_type`
|
||||
- `bought`:我购
|
||||
- `rented`:我租
|
||||
|
||||
**domain**: `client.transacted_property_type`
|
||||
- `second_hand`:二手
|
||||
- `new_house`:新房
|
||||
|
||||
**domain**: `client.activity_level`
|
||||
- `new_matched`:新配对
|
||||
- `active_7d`:7日活跃
|
||||
- `active_30d`:30日活跃
|
||||
- `active_90d`:90日活跃
|
||||
- `expiring`:即将过期
|
||||
- `frozen`:暂缓中
|
||||
- `invalid`:无效
|
||||
|
||||
**domain**: `client.contact.gender`
|
||||
- `male`:先生
|
||||
- `female`:女士
|
||||
|
||||
**domain**: `client.requirement_type`
|
||||
- `second_hand`:二手
|
||||
- `new_house`:新房
|
||||
- `rental`:租房
|
||||
|
||||
**domain**: `client.floor_preference`
|
||||
- `no_first`:不要一楼
|
||||
- `low`:低楼层
|
||||
- `mid`:中楼层
|
||||
- `high`:高楼层
|
||||
- `no_top`:不要顶楼
|
||||
|
||||
**domain**: `client.orientation`
|
||||
- `east`:东
|
||||
- `south`:南
|
||||
- `west`:西
|
||||
- `north`:北
|
||||
|
||||
**domain**: `client.decoration`
|
||||
- `rough`:毛坯
|
||||
- `plain`:清水
|
||||
- `simple`:简装
|
||||
- `medium`:中装
|
||||
- `fine`:精装
|
||||
- `luxury`:豪装
|
||||
|
||||
**domain**: `client.building_age_range`
|
||||
- `within_5y`:5年内
|
||||
- `5_10y`:5-10年
|
||||
- `10_15y`:10-15年
|
||||
- `15_20y`:15-20年
|
||||
- `over_20y`:20年以上
|
||||
|
||||
**domain**: `client.follow_log.log_type`
|
||||
- `written`:写入跟进
|
||||
- `modified`:修改跟进
|
||||
- `sensitive_view`:敏感查看
|
||||
- `other`:其他
|
||||
- `system`:系统
|
||||
|
||||
**domain**: `client.viewing.viewing_type`
|
||||
- `appointment`:预约
|
||||
- `viewing`:带看
|
||||
- `revisit`:复看
|
||||
- `empty`:空看
|
||||
|
||||
**domain**: `client.viewing.client_intent`
|
||||
- `interested`:感兴趣
|
||||
- `not_interested`:不感兴趣
|
||||
- `negotiating`:谈判中
|
||||
- `cancelled`:取消
|
||||
|
||||
**domain**: `client.property_match.match_source`
|
||||
- `recorded`:录客配房
|
||||
- `system`:系统配房
|
||||
|
||||
**domain**: `client.property_match.match_group`
|
||||
- `quality_layout`:优质户型
|
||||
- `price_reduced`:降价
|
||||
- `hot`:热门
|
||||
- `newly_listed`:新上
|
||||
|
||||
**domain**: `client.property_match.status`
|
||||
- `suggested`:待推送
|
||||
- `shared`:已分享
|
||||
- `rejected`:已反馈不合适
|
||||
- `viewed`:客户已查看
|
||||
|
||||
**domain**: `client.status_log.change_type`
|
||||
- `status_change`:改状态
|
||||
- `grade_change`:改等级
|
||||
- `to_public`:转公客
|
||||
- `to_transacted`:转成交
|
||||
- `to_invalid`:转无效
|
||||
- `owner_change`:改归属人
|
||||
- `source_change`:改来源
|
||||
- `merge`:合并客源
|
||||
|
||||
---
|
||||
|
||||
## 3.7 系统配置(setting)
|
||||
|
||||
**domain**: `setting.tenant_setting.value_type`
|
||||
- `bool`:布尔
|
||||
- `int`:整数
|
||||
- `string`:字符串
|
||||
- `enum`:枚举
|
||||
|
||||
**domain**: `setting.field_rule.module`
|
||||
- `property`:房源
|
||||
- `client`:客源(预留)
|
||||
|
||||
**domain**: `setting.field_rule.entity_type`(与 `property.property_type` 对齐)
|
||||
- `residential`:住宅
|
||||
- `villa`:别墅
|
||||
- `commercial_residential`:商住
|
||||
- `shop`:商铺
|
||||
- `office`:写字楼
|
||||
- `other`:其他
|
||||
|
||||
**domain**: `setting.field_rule.trade_status`
|
||||
- `sale`:出售
|
||||
- `rent`:出租
|
||||
- `sale_rent`:租售
|
||||
- `*`:全部
|
||||
|
||||
**domain**: `setting.field_rule.requirement`
|
||||
- `required`:必填
|
||||
- `optional`:选填
|
||||
- `hidden`:隐藏
|
||||
|
||||
---
|
||||
|
||||
## 四、Tenant 可配置枚举字段清单(lookup_items 权威)
|
||||
|
||||
> 以下字段值域由 `lookup_items` 维护,属于租户级配置,不在业务表中写死 `CHECK`。
|
||||
|
||||
| domain(统一命名) | 对应字段 | 当前状态 | 说明 |
|
||||
|---|---|---|---|
|
||||
| `client.source` | `clients.source` | ✅ 已落地 | 客源来源 |
|
||||
| `client.follow_purpose` | `client_follow_logs.purpose` | ✅ 已落地 | 客源跟进目的 |
|
||||
| `property.source` | `properties.source` | ✅ 已落地 | 房源来源 |
|
||||
| `property.follow_purpose` | `follow_logs.purpose` | 🔄 建议统一 | 房源跟进目的(建议与 `client.follow_purpose` 共享或独立分组) |
|
||||
| `property.commission_type` | `commissions.commission_type` | 🔄 待入组 | 委托类型(独家/非独家等) |
|
||||
| `client.match_feedback` | `client_property_matches.feedback` | 🔄 待入组 | 配房反馈原因 |
|
||||
| `org.reward_punish_category` | `staff_reward_punish.category` | 🔄 待入组 | 人事奖惩类别 |
|
||||
|
||||
### 4.1 lookup_groups 规范(Tenant Schema)
|
||||
|
||||
- `module`: 业务模块标识(如 `client` / `property` / `org`)
|
||||
- `key`: 领域键(如 `source` / `follow_purpose`)
|
||||
- 同一组内 `value` 不可重复(`UNIQUE(group_id, value)`)
|
||||
- `is_system = TRUE` 的项禁止物理删除(仅可停用)
|
||||
|
||||
---
|
||||
|
||||
## 五、统一实现约束
|
||||
|
||||
1. **固定枚举值不可改名**:只能新增或停用,禁止修改既有 value。
|
||||
2. **中文展示从字典取值**:前端/UI 不得硬编码中文。
|
||||
3. **可配置枚举不得加固定 CHECK**:防止租户自定义被数据库约束阻断。
|
||||
4. **跨模块同名枚举必须复用语义**:如 `status` 在不同领域必须使用 domain 区分,不允许混用。
|
||||
5. **缓存规范**:
|
||||
- 固定枚举:`public:enum_labels:{domain}`(建议 TTL 24h)
|
||||
- 可配置枚举:`{schema}:setting:lookup:{module}.{key}`(TTL 300s)
|
||||
|
||||
---
|
||||
|
||||
## 六、变更流程(必须同步)
|
||||
|
||||
新增或修改任一枚举时,必须同时更新:
|
||||
|
||||
1. 本文档 `ENUMS.md`
|
||||
2. 对应 `DATA_MODEL_*.md` 字段定义(CHECK / 注释 / 业务规则)
|
||||
3. `enum_labels` 种子数据(若为固定枚举)或 `lookup_groups/items` fixture(若为可配置枚举)
|
||||
4. 服务层缓存失效逻辑(Redis key)
|
||||
|
||||
---
|
||||
|
||||
## 七、与 ADR 的一致性说明
|
||||
|
||||
本文件已对齐以下冻结决策:
|
||||
|
||||
- 固定枚举与可配置枚举双轨并存
|
||||
- 状态机和值域以文档为权威来源
|
||||
- Tenant 级可配置枚举统一由 `setting` 模块托管
|
||||
- Agent 编码前先读枚举标准,禁止各模块自行定义“影子枚举”
|
||||
@@ -56,7 +56,9 @@
|
||||
| [US-PERMISSION-003](#US-PERMISSION-003-管理员批量为员工分配角色) | 权限管理 | 管理员批量为员工分配角色 | [ ] |
|
||||
| [US-PERMISSION-004](#US-PERMISSION-004-系统执行功能权限控制菜单级) | 权限管理 | 系统执行功能权限控制(菜单级) | [ ] |
|
||||
| [US-PERMISSION-005](#US-PERMISSION-005-系统执行数据权限控制部门个人全司) | 权限管理 | 系统执行数据权限控制(部门/个人/全司) | [ ] |
|
||||
| [US-SETTING-001](#US-SETTING-001-管理员配置房源相关设置字段必填自定义字段标签) | 系统配置 | 管理员配置房源相关设置(字段必填/自定义字段/标签) | [ ] |
|
||||
| [US-SETTING-001-A](#US-SETTING-001-A-管理员配置可选枚举值-Lookup-Items) | 系统配置 | 管理员配置可选枚举值(Lookup Items) | [ ] |
|
||||
| [US-SETTING-001-B](#US-SETTING-001-B-管理员配置房源字段必填规则) | 系统配置 | 管理员配置房源字段必填规则 | [ ] |
|
||||
| [US-SETTING-001-C](#US-SETTING-001-C-管理员配置客源录入规则) | 系统配置 | 管理员配置客源录入规则(查重范围/必填字段) | [ ] |
|
||||
|
||||
#### Phase 2 — 增强功能(P1)
|
||||
|
||||
@@ -437,12 +439,40 @@
|
||||
|
||||
### 系统配置
|
||||
|
||||
##### US-SETTING-001 管理员配置房源相关设置字段必填自定义字段标签
|
||||
##### US-SETTING-001-A 管理员配置可选枚举值 Lookup Items
|
||||
|
||||
- 参考PRD文档:`Project/fonrey/PRD/系统配置/系统配置.md` - 房源设置(字段必填/自定义字段/标签)
|
||||
- 参考DATA_MODEL文档:`Project/fonrey/DATA_MODEL/DATA_MODEL_PUBLIC.md`
|
||||
- 参考PRD文档:`Project/fonrey/PRD/系统配置/系统配置模块PRD.md` - US-SETTING-001-A
|
||||
- 参考DATA_MODEL文档:`Project/fonrey/DATA_MODEL/DATA_MODEL.md`(待 Atlas 补充 `lookup_items` DDL)
|
||||
- 状态:[ ]
|
||||
- 验收标准:可配置房源录入表单中哪些字段为必填;可新增自定义字段并在房源表单中展示;标签配置后可在房源筛选中使用;配置变更后房源录入表单实时生效
|
||||
- 验收标准:
|
||||
- 管理员进入「系统设置 → 参数配置」,页面按模块分组展示所有可配置参数项(客源来源、跟进目的、房源来源)
|
||||
- 可新增自定义选项,新选项追加至列表末尾并立即对经纪人录入下拉生效(刷新后)
|
||||
- 系统预制选项(`is_system=True`)不可删除,仅可停用;停用后前端下拉不再展示,历史数据保留并标注「已停用」
|
||||
- 支持调整选项排序(拖拽或修改排序值),排序变更在保存后前端生效
|
||||
- 配置保存时主动失效 Redis 缓存 key `{tenant_schema}:setting:lookup:{module}.{key}`,最长 5 分钟延迟
|
||||
|
||||
##### US-SETTING-001-B 管理员配置房源字段必填规则
|
||||
|
||||
- 参考PRD文档:`Project/fonrey/PRD/系统配置/系统配置模块PRD.md` - US-SETTING-001-B
|
||||
- 参考DATA_MODEL文档:`Project/fonrey/DATA_MODEL/DATA_MODEL.md`(待 Atlas 补充 `field_requirement_rules` DDL)、`Project/fonrey/DATA_MODEL/DATA_MODEL_PROPERTY.md`
|
||||
- 状态:[ ]
|
||||
- 验收标准:
|
||||
- 管理员进入「系统设置 → 房源字段规则」,以「用途 × 交易状态」矩阵展示配置(住宅×出售、住宅×出租)
|
||||
- 每个字段显示当前规则(必填 / 选填 / 隐藏),以三态 Toggle 或 Radio 形式编辑,保存后生效
|
||||
- 规则应用于录入界面:必填字段显示「*」标记,提交时为空则拦截;隐藏字段不渲染
|
||||
- 规则变更仅影响新录入,不影响存量房源数据
|
||||
- MVP 可配置字段:朝向、装修情况、楼层、建筑面积、套内面积、房型、产权年限、车位数
|
||||
|
||||
##### US-SETTING-001-C 管理员配置客源录入规则
|
||||
|
||||
- 参考PRD文档:`Project/fonrey/PRD/系统配置/系统配置模块PRD.md` - US-SETTING-001-C
|
||||
- 参考DATA_MODEL文档:`Project/fonrey/DATA_MODEL/DATA_MODEL.md`(待 Atlas 补充 `tenant_settings` DDL)、`Project/fonrey/DATA_MODEL/DATA_MODEL_CLIENT.md`
|
||||
- 状态:[ ]
|
||||
- 验收标准:
|
||||
- 管理员进入「系统设置 → 客源规则」,可配置新增私客时的查重范围:本人(默认)/ 本部门 / 全公司
|
||||
- 查重规则实时生效:经纪人录入手机号失焦后,系统按当前查重范围加密比对,若重复则提示归属人和录入时间,经纪人可选「仍然录入」或「放弃」
|
||||
- 可配置客源必填字段开关(等级、来源默认必填;总价区间、居室需求、购房目的默认选填)
|
||||
- 配置保存时主动失效 Redis 缓存 key `{tenant_schema}:setting:client_rules`,经纪人下次打开录入界面即应用最新规则
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
## 首页设置
|
||||
|
||||
|
||||
## 房源设置
|
||||
### 新增/编辑/查看
|
||||
|
||||
|
||||
### 字段/标签设置
|
||||
|
||||
|
||||
### 相关方设置
|
||||
|
||||
|
||||
### 相关方保护规则设置
|
||||
|
||||
|
||||
### 跟进/面访/回访
|
||||
|
||||
|
||||
### 实勘视频/VR/实地核验
|
||||
|
||||
|
||||
### 预约拍摄设置
|
||||
|
||||
|
||||
### 钥匙/委托/政府核验
|
||||
|
||||
|
||||
### 作业盘设置
|
||||
|
||||
|
||||
### 维护人员设置
|
||||
|
||||
|
||||
### 列表/房源/分级
|
||||
|
||||
|
||||
### 营销设置
|
||||
|
||||
|
||||
### 楼盘设置
|
||||
|
||||
|
||||
### 资料房/业主委托/预录入
|
||||
|
||||
|
||||
### 隐私保护及防骚扰
|
||||
|
||||
|
||||
### 房源检查及纠错
|
||||
|
||||
|
||||
## 新房设置
|
||||
### 新房基本设置
|
||||
|
||||
### 新房参数设置
|
||||
|
||||
|
||||
## 客源设置
|
||||
### 客源基本配置
|
||||
|
||||
|
||||
### 客源参数配置
|
||||
|
||||
|
||||
### 客源相关方配置
|
||||
|
||||
### 客源行政跨部权限
|
||||
|
||||
## 交易设置
|
||||
|
||||
### 交易流程
|
||||
|
||||
### 二手售后流程
|
||||
|
||||
### 新房售后流程
|
||||
|
||||
### 参数&备件条件
|
||||
|
||||
## 财务设置
|
||||
|
||||
### 业绩管理
|
||||
|
||||
### 资金管理
|
||||
|
||||
### 结算设置
|
||||
|
||||
### 提成设置
|
||||
|
||||
|
||||
## 人事OA设置
|
||||
|
||||
### 组织人事基本设置
|
||||
|
||||
### 员工自动升降级设置
|
||||
|
||||
### 审批流程设置
|
||||
|
||||
## 任务设置
|
||||
|
||||
### 参数设置
|
||||
|
||||
### 入职周年祝福设置
|
||||
|
||||
|
||||
## 合同设置
|
||||
|
||||
### 合同基本设置
|
||||
|
||||
## 通用及移动端设置
|
||||
|
||||
### 指标设置
|
||||
### 安全设置
|
||||
|
||||
### 其他设置
|
||||
|
||||
### 电话智能监控设置
|
||||
|
||||
### 黑名单设置
|
||||
|
||||
## 安装与登录设置
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
1100
Project/fonrey/PRD/系统配置/系统配置参数数据.md
Normal file
1100
Project/fonrey/PRD/系统配置/系统配置参数数据.md
Normal file
File diff suppressed because it is too large
Load Diff
275
Project/fonrey/PRD/系统配置/系统配置数据模型设计说明_for_Atlas.md
Normal file
275
Project/fonrey/PRD/系统配置/系统配置数据模型设计说明_for_Atlas.md
Normal file
@@ -0,0 +1,275 @@
|
||||
# 系统配置模块 — 数据模型设计说明
|
||||
|
||||
**致**: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 中的技术考量章节。*
|
||||
273
Project/fonrey/PRD/系统配置/系统配置模块PRD.md
Normal file
273
Project/fonrey/PRD/系统配置/系统配置模块PRD.md
Normal file
@@ -0,0 +1,273 @@
|
||||
# PRD:系统配置模块(MVP)
|
||||
|
||||
**状态**:Draft
|
||||
**作者**:Nova(PM)
|
||||
**最后更新**:2026-04-27
|
||||
**版本**:v0.1
|
||||
**关联 Task**:US-SETTING-001
|
||||
**相关文档**:`DATA_MODEL/ENUMS.md`、`PRD/系统配置/系统配置参数数据.md`、`DATA_MODEL/DATA_MODEL.md`
|
||||
|
||||
---
|
||||
|
||||
## 1. 问题陈述
|
||||
|
||||
**背景**:Fonrey 各业务模块(房源录入、客源录入、跟进记录、带看)中存在大量下拉选项、业务规则开关、字段必填控制。如果这些值硬编码在代码中,将导致:
|
||||
- 不同租户无法根据自身业务习惯调整选项(如来源渠道、跟进目的)
|
||||
- 管理员无法灵活控制录入数据质量(如哪些字段必填)
|
||||
- 业务规则变更(如查重范围)需要研发介入,无法自服务
|
||||
|
||||
**核心问题**:租户管理员无法通过界面自主维护业务枚举选项和录入规则,导致高频配置变更依赖研发,降低了系统的可用性与租户自服务能力。
|
||||
|
||||
**MVP 范围决策**:竞品系统配置涵盖 10+ 大类、100+ 设置项,MVP 阶段仅实现影响核心业务链路的 3 类配置,其余推迟至 P1/P2。
|
||||
|
||||
---
|
||||
|
||||
## 2. 目标与成功指标
|
||||
|
||||
| 目标 | 度量指标 | 当前基线 | 目标值 | 测量窗口 |
|
||||
| ------------- | -------------- | ------------- | -------------------- | -------- |
|
||||
| 管理员可自主维护枚举选项 | 系统配置依赖研发变更的工单数 | 100%(所有变更需研发) | 0(枚举类变更全部自服务) | 上线后 30 天 |
|
||||
| 房源/客源录入数据质量提升 | 必填字段为空的录入比例 | 未知(当前无必填控制) | < 5% | 上线后 60 天 |
|
||||
| 配置变更生效时效 | 配置保存到前端生效的时间 | N/A | ≤ 5 分钟(Redis 缓存 TTL) | — |
|
||||
|
||||
---
|
||||
|
||||
## 3. 非目标(MVP 明确不做)
|
||||
|
||||
以下内容在竞品中存在,但 MVP 阶段不实现:
|
||||
|
||||
- ❌ 房源标签配置(颜色/排序/启用)— 标签功能整体推迟至 P1
|
||||
- ❌ 私盘数量上限配置 — 推迟至 P1(当前无私盘流转业务逻辑)
|
||||
- ❌ 带看规则配置(补录时间、附件必填等)— 带看功能推迟至 P1
|
||||
- ❌ 跟进置顶条数限制 — 推迟至 P1
|
||||
- ❌ 公司信息(Logo、名称、联系方式)— 归入系统管理模块(平台运营层),非租户配置
|
||||
- ❌ 区域/商圈配置 — 归入楼盘管理模块(US-COMPLEX-003)
|
||||
- ❌ 委托/交易/财务/合同相关配置 — 超出 MVP 范围
|
||||
- ❌ 通知消息配置 — P2
|
||||
- ❌ 发布平台配置 — P2
|
||||
|
||||
---
|
||||
|
||||
## 4. 目标用户
|
||||
|
||||
**主要角色**:系统管理员(租户侧,每租户 1~3 人)
|
||||
|
||||
> 典型画像:门店运营负责人或行政主管,熟悉业务流程,无技术背景,通过系统后台进行日常运营配置。使用频率:初始开通时高频(完成初始化配置),此后低频(按需调整)。
|
||||
|
||||
**间接受益角色**:
|
||||
- 一线经纪人 — 看到的下拉选项和必填规则由管理员配置决定
|
||||
- 店长/经理 — 配置直接影响客源来源分析报表的数据质量
|
||||
|
||||
---
|
||||
|
||||
## 5. User Stories 与验收标准
|
||||
|
||||
---
|
||||
|
||||
### US-SETTING-001-A:管理员配置可选枚举值(Lookup Items)
|
||||
|
||||
> **As** 系统管理员,
|
||||
> **I want** 在「系统设置 → 参数配置」页面维护各业务模块的下拉选项(如客源来源、跟进目的),
|
||||
> **So that** 经纪人录入时看到的选项符合公司实际业务,不再依赖研发修改代码。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
**AC-1:参数分组展示**
|
||||
- Given 管理员进入「系统设置 → 参数配置」
|
||||
- When 页面加载完成
|
||||
- Then 页面按模块分组展示所有可配置参数项,至少包含:
|
||||
- 「客源」分组:客源来源、跟进目的
|
||||
- 「房源」分组:房源来源
|
||||
- 每个分组展示当前已有选项列表(名称 + 排序 + 状态)
|
||||
|
||||
**AC-2:新增自定义选项**
|
||||
- Given 管理员点击某参数项的「编辑」按钮
|
||||
- When 弹出编辑抽屉,填写「选项名称」并提交
|
||||
- Then 新选项追加至列表末尾,排序值自动计算
|
||||
- And 经纪人在录入界面的对应下拉中立即可见该新选项(刷新后生效)
|
||||
|
||||
**AC-3:停用选项(不可删除系统预制项)**
|
||||
- Given 管理员在编辑态对某选项执行「停用」操作
|
||||
- When 确认提交
|
||||
- Then 该选项的 `is_active` 置为 False
|
||||
- And 前端录入下拉中不再展示该选项
|
||||
- And 历史已选该值的记录保留原值不变,仅展示时标注「已停用」
|
||||
- And 系统预制选项(`is_system = True`)的「删除」按钮禁用,仅可停用
|
||||
|
||||
**AC-4:调整选项排序**
|
||||
- Given 管理员在编辑态拖拽选项或修改排序值
|
||||
- When 保存后
|
||||
- Then 经纪人录入下拉中按新排序展示
|
||||
|
||||
**AC-5:缓存一致性**
|
||||
- Given 管理员保存配置变更
|
||||
- When 变更写入数据库成功
|
||||
- Then 对应 Redis 缓存 key 主动失效(`{tenant_schema}:setting:lookup:{module}.{key}`)
|
||||
- And 所有经纪人在下次请求时获取到最新选项(最长 5 分钟延迟)
|
||||
|
||||
**AC-6:MVP 必须覆盖的参数项**
|
||||
|
||||
| 模块 | 参数 key | 中文名 | 系统预制值(部分示例) |
|
||||
|------|---------|--------|------------------|
|
||||
| `client` | `source` | 客源来源 | 门店接待、老客户转介绍、网络(58/安居客)、驻守、上门、朋友介绍 |
|
||||
| `client` | `follow_purpose` | 跟进目的 | 回拨、推房、带看、维护、其他 |
|
||||
| `property` | `source` | 房源来源 | 主动开发、业主上门、老客户转介绍、网络来电 |
|
||||
|
||||
---
|
||||
|
||||
### US-SETTING-001-B:管理员配置房源字段必填规则
|
||||
|
||||
> **As** 系统管理员,
|
||||
> **I want** 按「房源用途 × 交易状态」的组合,控制哪些字段在录入时为必填/选填/隐藏,
|
||||
> **So that** 系统能在录入时强制采集公司要求的关键信息,提升房源数据完整度。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
**AC-1:规则配置页面**
|
||||
- Given 管理员进入「系统设置 → 房源字段规则」
|
||||
- When 页面加载
|
||||
- Then 以「用途 × 交易状态」为维度展示配置矩阵,MVP 仅需支持:
|
||||
- 住宅 × 出售
|
||||
- 住宅 × 出租
|
||||
- 商铺 × 出售(可选)
|
||||
- 商铺 × 出租(可选)
|
||||
- 每个组合展示可配置字段列表,字段来源见 AC-4
|
||||
|
||||
**AC-2:规则编辑**
|
||||
- Given 管理员点击某组合的「编辑」按钮
|
||||
- When 进入编辑态
|
||||
- Then 每个字段显示当前规则(必填 / 选填 / 隐藏),以三态 Toggle 或 Radio 形式展示
|
||||
- And 管理员修改后点击「保存」提交
|
||||
|
||||
**AC-3:规则应用于录入界面**
|
||||
- Given 管理员保存了「住宅 × 出售」的规则:朝向=必填,车位数=隐藏
|
||||
- When 经纪人在录入界面新增「住宅 × 出售」房源
|
||||
- Then 朝向字段显示必填标记(*),提交时若为空则拦截并提示
|
||||
- And 车位数字段不显示(隐藏)
|
||||
- And 规则变更在配置保存后立即对新录入生效(不影响存量房源)
|
||||
|
||||
**AC-4:MVP 可配置字段范围**
|
||||
|
||||
| 字段 | 字段 key | 说明 |
|
||||
|------|---------|------|
|
||||
| 朝向 | `orientation` | 对应 `property.orientation` 枚举 |
|
||||
| 装修情况 | `decoration` | 对应 `property.decoration` 枚举 |
|
||||
| 楼层 | `floor` | 所在楼层 / 总楼层 |
|
||||
| 建筑面积 | `building_area` | 数值字段 |
|
||||
| 套内面积 | `inner_area` | 数值字段 |
|
||||
| 房型(室/厅/卫) | `room_layout` | 数值字段组 |
|
||||
| 产权年限 | `ownership_years` | 数值字段 |
|
||||
| 车位数 | `parking_count` | 数值字段 |
|
||||
|
||||
> **注**:字段是否存在于数据模型由 `DATA_MODEL_PROPERTY.md` 决定,本配置只控制「是否必填/展示」,不新增字段。
|
||||
|
||||
---
|
||||
|
||||
### US-SETTING-001-C:管理员配置客源录入规则
|
||||
|
||||
> **As** 系统管理员,
|
||||
> **I want** 配置新增私客时的查重范围,以及必填字段控制,
|
||||
> **So that** 减少客源重复录入风险,并确保客源数据质量满足公司管理要求。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
**AC-1:查重范围配置**
|
||||
- Given 管理员进入「系统设置 → 客源规则」
|
||||
- When 查看「新增私客查重范围」设置项
|
||||
- Then 可选值为:
|
||||
- `本人`(默认)— 同一经纪人不可重复录入同一手机号
|
||||
- `本部门` — 同部门内不可重复
|
||||
- `全公司` — 全租户范围不可重复
|
||||
- And 每个选项有说明文案,明确告知管理员改动影响范围
|
||||
|
||||
**AC-2:查重规则应用**
|
||||
- Given 管理员将查重范围设置为「本部门」
|
||||
- When 经纪人录入私客手机号时
|
||||
- Then 系统在失焦后实时检测同部门内是否存在相同手机号(加密比对)
|
||||
- And 若存在重复,页面提示:「该号码在本部门已有记录,归属人:XXX,录入时间:XX」
|
||||
- And 经纪人可选择「仍然录入」(走审批流)或「放弃」
|
||||
|
||||
**AC-3:客源必填字段配置**
|
||||
- Given 管理员在「客源规则」页面勾选必填字段
|
||||
- When 保存
|
||||
- Then 经纪人录入私客时,已勾选的字段显示必填标记(*),提交时校验
|
||||
- And MVP 可配置必填的字段范围:
|
||||
|
||||
| 字段 | 字段 key | 默认值 |
|
||||
|------|---------|--------|
|
||||
| 等级 | `grade` | 必填 |
|
||||
| 来源 | `source` | 必填 |
|
||||
| 求购/求租总价区间 | `budget_range` | 选填 |
|
||||
| 居室需求 | `room_requirement` | 选填 |
|
||||
| 购房目的 | `buying_purpose` | 选填 |
|
||||
|
||||
**AC-4:配置变更即时生效**
|
||||
- Given 管理员修改任一客源规则并保存
|
||||
- When 经纪人下次打开录入界面
|
||||
- Then 应用最新规则(无需重新登录)
|
||||
- And Redis 缓存 key `{tenant_schema}:setting:client_rules` 在保存时主动失效
|
||||
|
||||
---
|
||||
|
||||
## 6. 解决方案概述
|
||||
|
||||
**页面结构**:
|
||||
|
||||
```
|
||||
系统设置(侧边栏)
|
||||
├── 参数配置 ← US-SETTING-001-A(Lookup Items 管理)
|
||||
├── 房源字段规则 ← US-SETTING-001-B(FieldRequirementRule)
|
||||
└── 客源规则 ← US-SETTING-001-C(TenantSetting + 必填规则)
|
||||
```
|
||||
|
||||
**核心设计决策**:
|
||||
|
||||
1. **Lookup Items 与 enum_labels 分离**:固定系统枚举(装修/朝向/状态/等级)存放在 Public Schema 的 `enum_labels` 表,由平台管理员通过 migration 维护,租户无权修改。可配置枚举(来源/跟进目的)存放在 Tenant Schema 的 `lookup_items` 表,由租户管理员自主维护。详见数据模型说明文档。
|
||||
|
||||
2. **字段规则不新增字段**:`field_requirement_rules` 只控制「必填/选填/隐藏」状态,字段本身的存在性由数据模型决定。避免配置层与数据模型层职责混淆。
|
||||
|
||||
3. **所有配置读取走服务层**:业务模块(房源/客源录入)通过 `TenantSettingsService` 统一读取配置,不直接查表,便于统一缓存管理。
|
||||
|
||||
---
|
||||
|
||||
## 7. 技术考量
|
||||
|
||||
**依赖**:
|
||||
- `apps/setting/` — 配置模块宿主 App(已在 AGENTS.md 目录结构中定义)
|
||||
- `core/cache.py` — Redis 工具(租户前缀管理)
|
||||
- `DATA_MODEL/ENUMS.md` — `enum_labels` 设计权威来源,`lookup_items` 需与之对齐
|
||||
|
||||
**已知风险**:
|
||||
|
||||
| 风险 | 可能性 | 影响 | 缓解措施 |
|
||||
|------|--------|------|---------|
|
||||
| 缓存失效不及时导致配置延迟生效 | 中 | 低 | 保存时主动 invalidate;最长 5 min TTL 兜底 |
|
||||
| 字段必填规则与前端渲染逻辑耦合 | 中 | 中 | 后端在每次 form 请求时返回规则快照,前端不缓存规则 |
|
||||
| 历史数据与「已停用枚举值」展示冲突 | 低 | 低 | 已停用值在展示时追加「(已停用)」后缀,数据库值不变 |
|
||||
|
||||
**待解决的 Open Questions**(启动开发前必须确认):
|
||||
|
||||
- [ ] `lookup_items` 表的最终 DDL 由 Atlas 确认后同步至 `DATA_MODEL/DATA_MODEL.md` — **Owner: Atlas / Deadline: 开发启动前**
|
||||
- [ ] 字段必填规则是否需要支持「按角色」粒度(如经纪人必填、店长选填)— **当前决策:MVP 不做角色粒度,全员统一规则。如需变更请在 Review 中提出。**
|
||||
- [ ] `FieldRequirementRule` 中「房源用途」的枚举值与 `property.property_type` 是否完全一致 — **Owner: Atlas 对齐 DATA_MODEL_PROPERTY.md**
|
||||
|
||||
---
|
||||
|
||||
## 8. 上线计划
|
||||
|
||||
| 阶段 | 时间 | 受众 | 成功门槛 |
|
||||
|------|------|------|---------|
|
||||
| 内部联调 | Sprint N | 开发团队 + 测试 | 3 个 US 核心流程无 P0 Bug |
|
||||
| Alpha 验证 | Sprint N+1 | 1 家种子客户管理员 | 管理员可独立完成初始化配置,无需研发介入 |
|
||||
| MVP 上线 | Sprint N+2 | 全部租户 | 配置变更工单量为 0(全自服务) |
|
||||
|
||||
**回滚条件**:配置保存后前端报错率 > 5%,或经纪人录入报错率相比上线前上升 > 2%,立即回滚并排查。
|
||||
|
||||
---
|
||||
|
||||
## 9. 附录
|
||||
|
||||
- [竞品系统配置参数数据](./系统配置参数数据.md)
|
||||
- [数据模型设计说明(写给 Atlas)](./系统配置数据模型设计说明_for_Atlas.md)
|
||||
- ENUMS.md:`DATA_MODEL/ENUMS.md`
|
||||
- 总体数据模型:`DATA_MODEL/DATA_MODEL.md`
|
||||
@@ -132,7 +132,7 @@ apps/property/
|
||||
| ----- | ---------------------------------- | -------------------------- | ------------------------------------- |
|
||||
| 登录认证 | [`登录管理技术方案.md`](./登录管理技术方案.md) | `PRD/登录管理/` | `DATA_MODEL/DATA_MODEL_LOGIN.md` |
|
||||
| 权限管理 | [`权限管理系统技术方案.md`](./权限管理系统技术方案.md) | `PRD/权限管理/` | `DATA_MODEL/DATA_MODEL_PERMISSION.md` |
|
||||
| 房源管理 | _待补充_ | `PRD/房源管理/` | `DATA_MODEL/DATA_MODEL_PROPERTY.md` |
|
||||
| 房源管理 | [`房源管理技术方案.md`](./房源管理技术方案.md) | `PRD/房源管理/` | `DATA_MODEL/DATA_MODEL_PROPERTY.md` |
|
||||
| 客源管理 | _待补充_ | `PRD/客源管理/` | `DATA_MODEL/DATA_MODEL_CLIENT.md` |
|
||||
| 楼盘管理 | _待补充_ | `PRD/房源管理/`(含楼盘) | `DATA_MODEL/DATA_MODEL_COMPLEX.md` |
|
||||
| 组织人事 | _待补充_ | `PRD/组织人事管理/` | `DATA_MODEL/DATA_MODEL_ORG.md` |
|
||||
|
||||
444
Project/fonrey/TECH_STACK/房源管理技术方案.md
Normal file
444
Project/fonrey/TECH_STACK/房源管理技术方案.md
Normal file
@@ -0,0 +1,444 @@
|
||||
> **For AI assistants**: Read this entire file before writing any code. All decisions here are final. Do not suggest alternatives unless asked.
|
||||
|
||||
# Fonrey 房源管理技术方案
|
||||
|
||||
**版本**: 1.0
|
||||
**项目**: Fonrey 房产经纪管理系统
|
||||
**技术栈**: Django 4.x + HTMX + Alpine.js + PostgreSQL 16 + Redis + Celery + Cloudflare R2
|
||||
**关联 PRD**: `PRD/房源管理/房源管理模块PRD.md`(v2.1)
|
||||
**关联数据模型**: `DATA_MODEL/DATA_MODEL_PROPERTY.md`(本方案不重复 DDL)
|
||||
**关联枚举字典**: `DATA_MODEL/ENUMS.md`
|
||||
**最后更新**: 2026-04-27
|
||||
|
||||
---
|
||||
|
||||
## 一、文档定位与边界
|
||||
|
||||
本文件只定义房源模块的:
|
||||
|
||||
1. 服务边界与模块协作
|
||||
2. API 端点设计(重点)
|
||||
3. HTMX 局刷协议
|
||||
4. 权限接入、异步任务、缓存与性能约束
|
||||
5. 测试与验收映射
|
||||
|
||||
> **不在本文件展开**:表结构字段、索引、DDL、触发器。请以 `DATA_MODEL_PROPERTY.md` 为唯一权威来源。
|
||||
|
||||
---
|
||||
|
||||
## 二、P0 范围(与 Task Board 对齐)
|
||||
|
||||
本方案覆盖 `PRD/TASK.md` 中房源 P0 User Story:
|
||||
|
||||
- US-PROPERTY-001:录入二手住宅(出售/出租)
|
||||
- US-PROPERTY-002:查看与筛选房源列表
|
||||
- US-PROPERTY-003:查看房源详情
|
||||
- US-PROPERTY-004:写入与查看跟进记录
|
||||
- US-PROPERTY-005:管理房源图片(上传/分类/排序)
|
||||
- US-PROPERTY-006:管理业主联系人
|
||||
- US-PROPERTY-007:调整房源价格
|
||||
- US-PROPERTY-008:变更房源状态
|
||||
|
||||
---
|
||||
|
||||
## 三、模块架构边界
|
||||
|
||||
## 3.1 模块职责
|
||||
|
||||
`apps/property` 负责:
|
||||
|
||||
- 房源主流程:新增、列表检索、详情展示、状态与价格维护
|
||||
- 房源协作数据:联系人、跟进、图片
|
||||
- 房源审计轨迹:挂牌历史、调价记录、敏感查看记录(通过服务层写入)
|
||||
|
||||
## 3.2 外部依赖
|
||||
|
||||
| 依赖模块 | 依赖内容 | 依赖方式 |
|
||||
|---|---|---|
|
||||
| `apps/complex` | 小区/楼盘基础信息、联想搜索 | Service 调用 + 外键引用 |
|
||||
| `apps/org` | 员工组织信息(相关方、操作人) | 外键 + Service 查询 |
|
||||
| `apps/permission` | RBAC/Scope 权限判定 | PermissionChecker + ScopeQueryBuilder |
|
||||
| `apps/setting` | 可配置枚举(来源、跟进目的等) | lookup_items 读取缓存 |
|
||||
| `core/encryption.py` | 手机号加密/脱敏/哈希 | 统一工具调用,禁止明文 |
|
||||
| `core/cache.py` | 筛选缓存、任务进度、短期详情缓存 | Redis Key 带租户前缀 |
|
||||
| `Celery` | 导出、图片处理、完成度重算 | 异步任务 |
|
||||
| `Cloudflare R2` | 房源图片与附件对象存储 | 预签名上传 + 回写元数据 |
|
||||
|
||||
## 3.3 分层约束(必须遵守)
|
||||
|
||||
- `views.py` 仅做:参数校验、权限门禁、调用 service、组织响应
|
||||
- 业务规则全部下沉到 `services/`
|
||||
- 所有写操作必须落审计(至少 follow_logs 或 change_logs)
|
||||
- 耗时 >500ms 的流程必须异步化
|
||||
|
||||
---
|
||||
|
||||
## 四、API 设计总原则(P0)
|
||||
|
||||
1. **页面端点与数据端点分离**:
|
||||
- 页面(SSR + HTMX 容器):`/property/...`
|
||||
- 数据 API(JSON):`/api/property/...`
|
||||
2. **HTMX 局刷优先**:列表筛选、Tab 内容、弹窗提交走局部刷新。
|
||||
3. **列表性能目标**:89k 房源规模下,`p95 < 2s`(索引 + Keyset 分页 + 预加载)。
|
||||
4. **统一错误协议**:
|
||||
- JSON:`{"error":"...","code":"SNAKE_CASE"}`
|
||||
- HTMX:返回片段 + `HX-Trigger` Toast
|
||||
5. **权限前置**:所有 API 在 service 执行前完成权限与 scope 过滤。
|
||||
6. **敏感信息最小暴露**:默认打码;明文查看必须具备权限并记录审计。
|
||||
|
||||
---
|
||||
|
||||
## 五、端点清单(核心)
|
||||
|
||||
## 5.1 页面路由(SSR + HTMX 容器)
|
||||
|
||||
| 路径 | 方法 | 鉴权 | 说明 |
|
||||
|---|---|---|---|
|
||||
| `/property/list/` | GET | 是 | 房源列表主页面(包含筛选栏与列表容器) |
|
||||
| `/property/create/` | GET | 是 | 新增房源页面(P0: 住宅) |
|
||||
| `/property/{property_id}/` | GET | 是 | 房源详情主页面(多 Tab 容器) |
|
||||
| `/property/{property_id}/edit/` | GET | 是 | 编辑房源页面(非弹窗) |
|
||||
|
||||
## 5.2 HTMX 片段端点
|
||||
|
||||
| 路径 | 方法 | 用途 | 返回 |
|
||||
|---|---|---|---|
|
||||
| `/property/fragments/list-table/` | GET | 列表筛选/排序/翻页局刷 | HTML 片段 |
|
||||
| `/property/fragments/filter-panel/` | GET | 筛选项联动刷新(区域→商圈等) | HTML 片段 |
|
||||
| `/property/{id}/fragments/tab/{tab_name}/` | GET | 详情页 Tab 懒加载(跟进/相册/附件等) | HTML 片段 |
|
||||
| `/property/{id}/fragments/contact-panel/` | GET | 联系人侧栏局刷 | HTML 片段 |
|
||||
| `/property/{id}/fragments/follow-timeline/` | GET | 跟进时间线局刷(筛选后) | HTML 片段 |
|
||||
| `/property/{id}/fragments/photo-grid/` | GET | 相册宫格局刷 | HTML 片段 |
|
||||
|
||||
> 所有 fragment 端点必须校验 `HX-Request=true`,非 HTMX 请求返回 400。
|
||||
|
||||
## 5.3 JSON API(P0)
|
||||
|
||||
| 端点 | 方法 | 鉴权 | 权限码(建议) | 说明 |
|
||||
|---|---|---|---|---|
|
||||
| `/api/property/` | POST | 是 | `property.create.allow` | 新增房源(US-001) |
|
||||
| `/api/property/list/query/` | POST | 是 | `property.list.view.scope` | 列表查询(US-002) |
|
||||
| `/api/property/{id}/detail/` | GET | 是 | `property.list.view.scope` | 详情聚合数据(US-003) |
|
||||
| `/api/property/{id}/status/change/` | POST | 是 | `property.status.change.allow` | 改状态(US-008) |
|
||||
| `/api/property/{id}/price/change/` | POST | 是 | `property.price.change.allow` | 调价(US-007) |
|
||||
| `/api/property/{id}/follow-logs/` | POST | 是 | `property.follow.create.allow` | 新增跟进(US-004) |
|
||||
| `/api/property/{id}/follow-logs/query/` | POST | 是 | `property.follow.view.scope` | 跟进查询(US-004) |
|
||||
| `/api/property/{id}/contacts/` | POST | 是 | `property.contact.create.allow` | 新增联系人(US-006) |
|
||||
| `/api/property/{id}/contacts/{contact_id}/` | PATCH | 是 | `property.contact.edit.allow` | 编辑联系人(US-006) |
|
||||
| `/api/property/{id}/contacts/same-owner/` | GET | 是 | `property.list.view.scope` | 同业主其他房源(US-006) |
|
||||
| `/api/property/{id}/owner-phone/view/` | POST | 是 | `property.owner_phone.view.daily_limit` | 查看号码(审计+额度) |
|
||||
| `/api/property/{id}/photos/upload-token/` | POST | 是 | `property.photo.upload.allow` | 获取 R2 预签名(US-005) |
|
||||
| `/api/property/{id}/photos/commit/` | POST | 是 | `property.photo.upload.allow` | 上传回执写库(US-005) |
|
||||
| `/api/property/{id}/photos/reorder/` | POST | 是 | `property.photo.edit.allow` | 图片排序(US-005) |
|
||||
| `/api/property/{id}/photos/category/batch/` | POST | 是 | `property.photo.edit.allow` | 批量改分类(US-005) |
|
||||
| `/api/property/{id}/photos/{photo_id}/set-cover/` | POST | 是 | `property.photo.edit.allow` | 设封面(US-005) |
|
||||
| `/api/property/export/jobs/` | POST | 是 | `property.list.export.scope` | 创建导出任务(US-002) |
|
||||
| `/api/property/export/jobs/{job_id}/` | GET | 是 | `property.list.export.scope` | 查询导出任务状态 |
|
||||
| `/api/property/export/jobs/{job_id}/download/` | GET | 是 | `property.list.export.scope` | 下载导出结果 |
|
||||
|
||||
---
|
||||
|
||||
## 六、关键 API 规范(请求/响应)
|
||||
|
||||
## 6.1 新增房源
|
||||
|
||||
`POST /api/property/`
|
||||
|
||||
```json
|
||||
{
|
||||
"property_type": "residential",
|
||||
"trade_type": "sale_rent",
|
||||
"complex_id": "uuid",
|
||||
"building_no": "5",
|
||||
"unit_no": "2",
|
||||
"room_no": "1102",
|
||||
"floor_current": 11,
|
||||
"floor_total": 33,
|
||||
"area": "89.50",
|
||||
"layout": {"bedroom": 3, "living": 2, "bathroom": 2, "kitchen": 1, "balcony": 1},
|
||||
"sale_price": "368.00",
|
||||
"rent_price": null,
|
||||
"owner_contacts": [
|
||||
{
|
||||
"name": "张三",
|
||||
"identity": "owner",
|
||||
"phone": "13800000000",
|
||||
"gender": "male"
|
||||
}
|
||||
],
|
||||
"related_staff": {
|
||||
"first_agent_id": "uuid",
|
||||
"number_agent_id": "uuid",
|
||||
"sale_agent_id": "uuid"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
成功:`201`
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "uuid",
|
||||
"code": "FY202604270001",
|
||||
"redirect_url": "/property/uuid/",
|
||||
"message": "保存成功"
|
||||
}
|
||||
```
|
||||
|
||||
## 6.2 列表查询
|
||||
|
||||
`POST /api/property/list/query/`
|
||||
|
||||
```json
|
||||
{
|
||||
"keyword": "阳光花园",
|
||||
"filters": {
|
||||
"district_ids": ["uuid"],
|
||||
"status": ["sale", "rent"],
|
||||
"attribute": ["public"],
|
||||
"price_sale": {"min": 200, "max": 500}
|
||||
},
|
||||
"sort": {"field": "updated_at", "order": "desc"},
|
||||
"pagination": {"mode": "keyset", "cursor": "opaque_cursor", "limit": 20}
|
||||
}
|
||||
```
|
||||
|
||||
成功:`200`
|
||||
|
||||
```json
|
||||
{
|
||||
"items": [{"id": "uuid", "title": "阳光花园 5-2-1102", "status": "sale"}],
|
||||
"next_cursor": "opaque_cursor_2",
|
||||
"total_estimate": 89432
|
||||
}
|
||||
```
|
||||
|
||||
## 6.3 改状态
|
||||
|
||||
`POST /api/property/{id}/status/change/`
|
||||
|
||||
```json
|
||||
{
|
||||
"from_status": "sale",
|
||||
"to_status": "paused",
|
||||
"reason": "业主暂不出售"
|
||||
}
|
||||
```
|
||||
|
||||
校验规则:
|
||||
- 必须符合状态机(例如 sale -> paused/other_sale/deal)
|
||||
- reason 必填,<= 50 字
|
||||
|
||||
## 6.4 调价
|
||||
|
||||
`POST /api/property/{id}/price/change/`
|
||||
|
||||
```json
|
||||
{
|
||||
"sale_price": "350.00",
|
||||
"bottom_price": "338.00",
|
||||
"reason": "业主急售"
|
||||
}
|
||||
```
|
||||
|
||||
成功后联动:
|
||||
- 更新当前挂牌价
|
||||
- 追加 `price_changes` 记录
|
||||
- 触发详情页价格区域局刷
|
||||
|
||||
## 6.5 写跟进
|
||||
|
||||
`POST /api/property/{id}/follow-logs/`
|
||||
|
||||
```json
|
||||
{
|
||||
"follow_type": "write_follow",
|
||||
"purpose": "owner_follow",
|
||||
"content": "今日电话沟通,业主接受350万附近报价。",
|
||||
"visibility": "team",
|
||||
"attachments": ["object_key_1", "object_key_2"]
|
||||
}
|
||||
```
|
||||
|
||||
校验规则:
|
||||
- content: 6~500 字
|
||||
- 附件图片最多 10 张,单张 <=20MB
|
||||
|
||||
## 6.6 导出任务
|
||||
|
||||
1) 创建:`POST /api/property/export/jobs/`
|
||||
```json
|
||||
{"query_snapshot": {...}, "columns": ["code", "title", "status", "sale_price"]}
|
||||
```
|
||||
|
||||
2) 查询:`GET /api/property/export/jobs/{job_id}/`
|
||||
```json
|
||||
{"status":"running", "progress":65}
|
||||
```
|
||||
|
||||
3) 下载:`GET /api/property/export/jobs/{job_id}/download/`
|
||||
- 任务完成后返回一次性下载 URL
|
||||
|
||||
---
|
||||
|
||||
## 七、HTMX 交互约定(房源模块)
|
||||
|
||||
## 7.1 请求头与响应头
|
||||
|
||||
- 请求必须带:`HX-Request: true`
|
||||
- 成功提示:`HX-Trigger: {"toast":{"level":"success","message":"保存成功"}}`
|
||||
- 失败提示:`HX-Trigger: {"toast":{"level":"error","message":"保存失败"}}`
|
||||
- 跳转:`HX-Redirect: /property/{id}/`
|
||||
|
||||
## 7.2 片段模板命名
|
||||
|
||||
| 场景 | 模板 |
|
||||
|---|---|
|
||||
| 列表表格 | `templates/property/fragments/list_table.html` |
|
||||
| 跟进时间线 | `templates/property/fragments/follow_timeline.html` |
|
||||
| 联系人面板 | `templates/property/fragments/contact_panel.html` |
|
||||
| 相册宫格 | `templates/property/fragments/photo_grid.html` |
|
||||
|
||||
## 7.3 推荐前端触发方式
|
||||
|
||||
- 筛选提交:`hx-post="/property/fragments/list-table/"`
|
||||
- 切 Tab:`hx-get="/property/{id}/fragments/tab/follow/"`
|
||||
- 弹窗提交:`hx-post` + `hx-target` 当前弹窗容器,成功后触发父容器刷新
|
||||
|
||||
---
|
||||
|
||||
## 八、权限与数据范围设计
|
||||
|
||||
## 8.1 P0 必需权限项(最小集合)
|
||||
|
||||
> 与 `DATA_MODEL_PERMISSION.md` 对齐;若 `permission_defs` 尚未落库,则按下列 code 补种子。
|
||||
|
||||
| 权限 code | 类型 | 说明 |
|
||||
|---|---|---|
|
||||
| `property.list.view.scope` | SCOPE | 查看房源范围 |
|
||||
| `property.list.export.scope` | SCOPE | 导出房源范围 |
|
||||
| `property.create.allow` | BOOLEAN | 新增房源 |
|
||||
| `property.list.edit.allow` | BOOLEAN | 编辑房源基础信息 |
|
||||
| `property.price.change.allow` | BOOLEAN | 调价 |
|
||||
| `property.status.change.allow` | BOOLEAN | 改状态 |
|
||||
| `property.follow.view.scope` | SCOPE | 查看跟进范围 |
|
||||
| `property.follow.create.allow` | BOOLEAN | 新增跟进 |
|
||||
| `property.contact.create.allow` | BOOLEAN | 新增联系人 |
|
||||
| `property.contact.edit.allow` | BOOLEAN | 编辑联系人 |
|
||||
| `property.photo.upload.allow` | BOOLEAN | 上传图片 |
|
||||
| `property.photo.edit.allow` | BOOLEAN | 改分类/排序/封面 |
|
||||
| `property.owner_phone.view.daily_limit` | INTEGER | 每日查看号码上限(0=不限制) |
|
||||
|
||||
## 8.2 Scope 与业务属性叠加
|
||||
|
||||
最终查询范围 = `权限 scope 过滤` ∩ `业务状态过滤` ∩ `房源属性规则(公盘/私盘/特盘/封盘)`
|
||||
|
||||
- 权限系统不直接改写 `properties.attribute`
|
||||
- 房源属性由业务规则决定可见性,权限只决定“理论上可看范围”
|
||||
|
||||
## 8.3 查看号码审计
|
||||
|
||||
调用 `/owner-phone/view/` 时必须:
|
||||
|
||||
1. 校验 `daily_limit`
|
||||
2. 解密返回明文(仅本次)
|
||||
3. 自动写入 `follow_logs`(`sensitive_view`)
|
||||
4. `sensitive_view` 记录不可删除
|
||||
|
||||
---
|
||||
|
||||
## 九、异步任务与缓存策略
|
||||
|
||||
## 9.1 Celery 任务
|
||||
|
||||
| 任务 | 触发时机 | 说明 |
|
||||
|---|---|---|
|
||||
| `property_export_task` | 创建导出任务后 | 按查询快照导出 Excel 到 R2 |
|
||||
| `property_photo_postprocess_task` | 图片上传 commit 后 | 生成缩略图、提取元数据 |
|
||||
| `property_completeness_recalc_task` | 调价/状态变更/跟进/图片更新后 | 异步重算维护完成度 |
|
||||
|
||||
> 所有任务参数必须包含 `tenant_schema_name`,任务开头显式切 schema。
|
||||
|
||||
## 9.2 Redis Key 规范
|
||||
|
||||
| Key | TTL | 说明 |
|
||||
|---|---|---|
|
||||
| `{schema}:property:list:query:{hash}` | 60s | 热门筛选结果短缓存 |
|
||||
| `{schema}:property:detail:{id}` | 120s | 详情聚合缓存 |
|
||||
| `{schema}:property:export:{job_id}` | 24h | 导出任务状态 |
|
||||
| `{schema}:property:owner_phone:view:{staff_id}:{date}` | 24h | 每日查看号码计数 |
|
||||
|
||||
---
|
||||
|
||||
## 十、性能与可靠性约束
|
||||
|
||||
1. 列表查询强制 Keyset 分页(禁止 OFFSET)。
|
||||
2. 所有筛选字段必须走已建索引(见 `DATA_MODEL_PROPERTY.md`)。
|
||||
3. 大列表默认返回精简字段,详情页再按 Tab 懒加载。
|
||||
4. 导出走异步任务,前端轮询任务状态,禁止同步导出。
|
||||
5. 批量写操作使用事务,失败要回滚并返回结构化错误。
|
||||
|
||||
---
|
||||
|
||||
## 十一、安全与合规
|
||||
|
||||
- 手机号、微信等敏感信息:入库加密、展示脱敏
|
||||
- API 全链路 HTTPS
|
||||
- 操作审计必须包含:操作者、时间、旧值/新值、来源 IP
|
||||
- 文件上传白名单:`bmp/jpg/jpeg/png/gif/svg`(P0 与 PRD 对齐)
|
||||
- 上传大小限制:20MB/文件
|
||||
- 防刷:列表查询、号码查看、导出任务创建均需限流
|
||||
|
||||
---
|
||||
|
||||
## 十二、错误码约定(房源模块)
|
||||
|
||||
| code | HTTP | 场景 |
|
||||
|---|---|---|
|
||||
| `PROPERTY_NOT_FOUND` | 404 | 房源不存在或无访问权限 |
|
||||
| `PROPERTY_INVALID_STATE_TRANSITION` | 400 | 非法状态流转 |
|
||||
| `PROPERTY_PRICE_INVALID` | 400 | 价格参数非法 |
|
||||
| `PROPERTY_FOLLOW_CONTENT_TOO_SHORT` | 400 | 跟进内容不足 6 字 |
|
||||
| `PROPERTY_PHONE_VIEW_LIMIT_EXCEEDED` | 429 | 查看号码超限 |
|
||||
| `PROPERTY_EXPORT_JOB_NOT_READY` | 409 | 导出未完成即下载 |
|
||||
| `PROPERTY_PERMISSION_DENIED` | 403 | 权限不足 |
|
||||
|
||||
---
|
||||
|
||||
## 十三、测试映射(P0)
|
||||
|
||||
| User Story | 最低测试覆盖 |
|
||||
|---|---|
|
||||
| US-PROPERTY-001 | 新增成功 / 必填校验失败 / 无权限403 |
|
||||
| US-PROPERTY-002 | 关键词+组合筛选 / Keyset 分页 / 导出任务创建 |
|
||||
| US-PROPERTY-003 | 详情加载 / 号码默认脱敏 / 查看号码审计 |
|
||||
| US-PROPERTY-004 | 跟进写入成功 / 内容长度校验 / 时间线筛选 |
|
||||
| US-PROPERTY-005 | 上传签名获取 / commit 落库 / 排序与封面设置 |
|
||||
| US-PROPERTY-006 | 联系人新增编辑 / 同业主房源查询 |
|
||||
| US-PROPERTY-007 | 调价成功并写历史 / 理由缺失失败 |
|
||||
| US-PROPERTY-008 | 合法流转成功 / 非法流转失败 |
|
||||
|
||||
**测试强制要求**:
|
||||
- 集成测试使用 `TenantClient`
|
||||
- HTMX 请求必须带 `HTTP_HX_REQUEST=true`
|
||||
- 权限三态:200 / 403 / 302
|
||||
- Celery 在测试环境 eager 执行
|
||||
|
||||
---
|
||||
|
||||
## 十四、落地顺序建议(供开发阶段使用)
|
||||
|
||||
1. 先搭建列表查询 + Scope 权限过滤(US-002)
|
||||
2. 再打通新增与详情主链路(US-001,003)
|
||||
3. 上线状态变更与调价(US-007,008)
|
||||
4. 补齐跟进、联系人、图片(US-004,005,006)
|
||||
5. 最后接入导出异步与性能压测
|
||||
|
||||
---
|
||||
|
||||
## 十五、与其他文档的同步规则
|
||||
|
||||
- 枚举变更:同步 `DATA_MODEL/ENUMS.md`
|
||||
- 权限 code 变更:同步 `DATA_MODEL/DATA_MODEL_PERMISSION.md`
|
||||
- API 变更:同步本文件与对应 PRD 验收条目
|
||||
- 超出 P0 的能力(如地图找房、商业地产)只能写“预留”,不得提前实现
|
||||
@@ -1,4 +1,4 @@
|
||||
> **For AI assistants**: Read this entire file before writing any code. All decisions here are final. Do not suggest alternatives unless asked.
|
||||
**For AI assistants**: Read this entire file before writing any code. All decisions here are final. Do not suggest alternatives unless asked.
|
||||
# Fonrey 权限管理系统技术方案建议
|
||||
|
||||
**版本**: 1.0 | **项目**: Fonrey 房产经纪管理系统 | **技术栈**: Django 4.x + HTMX + Alpine.js + PostgreSQL + Redis
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
| Token | Hex | Tailwind 类 | 使用场景 |
|
||||
| ------------- | --------- | ---------------- | ------------------------- |
|
||||
| `primary-50` | `#F0FDFA` | `bg-primary-50` | 页面强调区微底色、Tag 极淡底 |
|
||||
| `primary-100` | `#CCFBF1 | `bg-primary-100` | 选中背景、标签底色 |
|
||||
| `primary-100` | `#CCFBF1` | `bg-primary-100` | 选中背景、标签底色 |
|
||||
| `primary-200` | `#99F6E4` | `bg-primary-200` | Hover 标签底色 |
|
||||
| `primary-500` | `#14B8A6` | `bg-primary-500` | 辅助主色(图标、强调文字) |
|
||||
| `primary-600` | `#0F766E` | `bg-primary-600` | **主按钮、激活态、Tab 下划线(基准主色)** |
|
||||
|
||||
101
Project/fonrey/VIBE_CODING_开工前缺失清单.md
Normal file
101
Project/fonrey/VIBE_CODING_开工前缺失清单.md
Normal file
@@ -0,0 +1,101 @@
|
||||
# Fonrey Web Coding 开工前缺失清单
|
||||
|
||||
> 记录时间:2026-04-26
|
||||
> 目的:在开始 Web Coding 落地前,把缺失但会直接卡住实施的文档一次补齐。
|
||||
|
||||
## 一、必须补齐的 6 份关键文档
|
||||
|
||||
### 1. 项目入口文档
|
||||
- README.md / 项目总览
|
||||
- 本地启动步骤
|
||||
- 环境变量说明
|
||||
- 数据库初始化与迁移说明
|
||||
- 种子数据说明
|
||||
|
||||
### 2. ADR 架构决策记录
|
||||
需要冻结以下决策:
|
||||
- 登录态:session / token / 混合方案
|
||||
- django-tenants 本地开发模式
|
||||
- 单租户 MVP 与多租户架构共存方式
|
||||
- HTMX 页面局刷约定
|
||||
- 目录结构最终落点
|
||||
- 状态机与枚举字典权威来源
|
||||
|
||||
### 3. 枚举字典 / 状态字典
|
||||
建议新增:
|
||||
- ENUMS.md
|
||||
- STATE_MACHINE.md
|
||||
|
||||
至少冻结:
|
||||
- 客源状态
|
||||
- 客源等级
|
||||
- 房源状态
|
||||
- 操作日志类型
|
||||
- 租户状态机
|
||||
|
||||
要求统一:
|
||||
- 中文值
|
||||
- 英文值
|
||||
- 数据库 CHECK 值
|
||||
- UI 展示值
|
||||
- 允许的状态迁移
|
||||
|
||||
### 4. 页面路由 + 组件映射
|
||||
需要明确:
|
||||
- 每个模块有哪些页面
|
||||
- 每个页面对应什么 URL
|
||||
- 每个页面复用哪些组件
|
||||
- 哪些页面是列表 / 详情 / 弹窗 / 抽屉 / partial
|
||||
- 每个页面的空态、加载态、错误态、权限态
|
||||
|
||||
### 5. API 契约规范
|
||||
需要明确:
|
||||
- 请求 / 响应格式
|
||||
- 错误码规范
|
||||
- 分页规范
|
||||
- 搜索 / 筛选规范
|
||||
- 上传规范
|
||||
- 文件下载规范
|
||||
- 权限拒绝返回规范
|
||||
|
||||
### 6. 本地开发与验证手册
|
||||
需要明确:
|
||||
- 本地环境启动方式
|
||||
- PostgreSQL / Redis / Celery 启动方式
|
||||
- django-tenants 初始化方式
|
||||
- 测试租户创建方式
|
||||
- 管理员账号 seed 方式
|
||||
- 静态资源与对象存储本地替代方案
|
||||
- dev / staging / production 配置差异
|
||||
|
||||
## 二、建议优先级
|
||||
|
||||
### P0:先补,不补就不能稳定开工
|
||||
- 项目入口文档
|
||||
- ADR
|
||||
- 枚举字典 / 状态字典
|
||||
- 页面路由 + 组件映射
|
||||
|
||||
### P1:随后补,直接影响实现质量
|
||||
- API 契约规范
|
||||
- 本地开发与验证手册
|
||||
|
||||
## 三、当前项目的直接风险
|
||||
|
||||
- 需求、数据模型、任务表已经较完整,但“可执行工程包”还不够
|
||||
- Review 已指出枚举不一致、分页规范缺位、性能基准缺位等问题
|
||||
- 没有启动手册和种子数据,Web Coding 容易停留在文档层,无法稳定进入实现层
|
||||
|
||||
## 四、建议的落地顺序
|
||||
|
||||
1. 先补 README / 启动手册
|
||||
2. 冻结 ADR
|
||||
3. 冻结 ENUMS / STATE_MACHINE
|
||||
4. 补页面路由与组件映射
|
||||
5. 补 API 契约
|
||||
6. 补本地开发与验证手册
|
||||
7. 再开始正式 Web Coding
|
||||
|
||||
---
|
||||
|
||||
这份清单的目标不是增加文档数量,而是减少实现时的来回返工。
|
||||
347
Project/fonrey/prompt/PRD - 为系统设置生成PRD 设计文档.md
Normal file
347
Project/fonrey/prompt/PRD - 为系统设置生成PRD 设计文档.md
Normal file
@@ -0,0 +1,347 @@
|
||||
# Fonrey PRD 需求文档生成提示词模板
|
||||
|
||||
> 本模板专注于**产品需求定义**:做什么、为谁做、怎么验收。
|
||||
> 技术实现细节(数据模型 DDL、API 路由、架构设计)由配套的 **TECH_STACK & DATA_MODEL 模板**负责,两份文档互不重复。
|
||||
|
||||
---
|
||||
|
||||
## ✅ 使用前检查清单
|
||||
|
||||
- [ ] 已填写本次设计的模块 / 功能名称
|
||||
- [ ] 已确认参考的已有 PRD 路径(可留空)
|
||||
- [ ] 已准备好产品截图或草图(如有,直接附在消息中)
|
||||
- [ ] 已明确本次功能的优先级范围(P0 / P1 / P2)
|
||||
- [ ] TECH_STACK.md 已存在,可供 AI 读取技术约束
|
||||
|
||||
---
|
||||
|
||||
## 📋 完整提示词(复制后填写 `【...】` 再发送)
|
||||
|
||||
|
||||
## 角色与背景
|
||||
|
||||
你是 **Nova**,一名拥有 10 年以上经验的资深产品经理,擅长 B2B SaaS 产品的全生命周期管理。
|
||||
核心方法论:先问问题再给答案,先定义问题再讨论方案,先看证据再拍板决策。
|
||||
你输出的每一份需求文档都必须包含清晰的用户故事、可量化的验收标准、明确的边界(Non-Goals)。
|
||||
你永远不写模糊需求,每条需求都可以被工程师直接实现、测试和验收。
|
||||
|
||||
**你的职责边界**:
|
||||
- ✅ 负责:功能范围定义、用户故事、验收标准、字段规范、页面结构、权限要求、性能指标
|
||||
- ❌ 不负责:数据库 DDL、API 路由代码、系统架构设计——这些由配套技术文档承接
|
||||
|
||||
---
|
||||
|
||||
## 项目背景
|
||||
|
||||
**工作目录**:`~/Workspace/nexus`
|
||||
|
||||
**项目概览**:
|
||||
我正在开发 **Fonrey(房睿)**——一款面向房地产经纪公司的 B2B SaaS 平台。
|
||||
核心目标:解决房源 / 客源信息散乱、跟进缺失、重复录入等痛点,支撑 89,000+ 条数据量级下的高效匹配。
|
||||
|
||||
**目标用户(按使用频率)**:
|
||||
- 🔴 一线经纪人(高频,核心用户)
|
||||
- 🟠 店长 / 区域经理(每日使用)
|
||||
- 🟡 运营 / 行政人员(每日使用)
|
||||
- ⚪ 系统管理员(不定期)
|
||||
|
||||
**已覆盖的核心模块**:房源管理、客源管理、楼盘管理、系统设置
|
||||
|
||||
---
|
||||
|
||||
## 技术约束参考
|
||||
|
||||
请读取 `Project/fonrey/TECH_STACK/TECH_STACK.md` 了解技术约束。
|
||||
PRD 中涉及交互模式时须遵守以下原则,**不得建议替代方案**:
|
||||
|
||||
| 约束项 | 要求 |
|
||||
|--------|------|
|
||||
| 前端交互 | HTMX 局部刷新 + Alpine.js 前端状态,❌ 禁止 React/Vue/Angular |
|
||||
| 页面刷新 | ❌ 禁止整页刷新,所有操作使用 HTMX 局部更新 |
|
||||
| 异步任务 | 耗时 > 500ms 的操作须标注"需 Celery 异步处理" |
|
||||
| 文件存储 | 上传至 Cloudflare R2,PRD 中注明文件类型和大小限制即可 |
|
||||
| 当前阶段 | 仅 Web 端,移动端为 v2 规划,本期 PRD 不涉及 |
|
||||
|
||||
> 技术实现方案(models.py、urls.py、视图函数)由工程师参考配套 TECH 文档设计,PRD 不输出代码。
|
||||
|
||||
---
|
||||
|
||||
## 方法论参考
|
||||
|
||||
请读取 `Project/fonrey/prompt/product-manager.md` 并运用其中的专业知识与 PRD 格式规范。
|
||||
|
||||
核心原则(生成文档时必须体现):
|
||||
1. **先问题后方案**:每个功能点必须说明"解决了谁的什么痛点"
|
||||
2. **Non-Goals 必填**:明确本次不做什么,防止需求蔓延
|
||||
3. **验收标准可测试**:每条 AC 格式为 `Given / When / Then`,含正常流与异常流
|
||||
4. **优先级标注**:P0(MVP 必须)/ P1(重要但可延迟)/ P2(优化迭代)
|
||||
5. **技术风险前置**:依赖关系和已知风险须在文档中体现(描述风险,不设计方案)
|
||||
|
||||
---
|
||||
|
||||
## 参考已有 PRD(保持格式一致)
|
||||
|
||||
请参考以下已完成 PRD 的格式、章节结构和细化程度:
|
||||
|
||||
- 房源管理PRD: `Project/fonrey/PRD/房源管理/房源管理模块PRD.md`
|
||||
- 楼盘管理PRD: `Project/fonrey/PRD/房源管理/楼盘管理模块PRD.md`
|
||||
- 客源管理PRD: `Project/fonrey/PRD/客源管理/客源管理模块PRD.md`
|
||||
- 权限管理PRD: `Project/fonrey/PRD/权限管理/权限管理模块PRD.md`
|
||||
- 权限管理样本数据:`Project/fonrey/PRD/权限管理/首页.md`
|
||||
- 权限管理样本数据:`Project/fonrey/PRD/权限管理/房源-二手租赁.md`
|
||||
- 权限管理样本数据:`Project/fonrey/PRD/权限管理/客源.md`
|
||||
- 权限管理样本数据:`Project/fonrey/PRD/权限管理/小区.md`
|
||||
- 组织人事管理PRD: `Project/fonrey/PRD/组织人事管理/组织人事管理模块PRD.md`
|
||||
- 系统管理PRD: `Project/fonrey/PRD/系统管理/系统管理模块PRD`
|
||||
- 登录管理PRD: `Project/fonrey/PRD/登录管理/用户登录管理模块PRD.md`
|
||||
- 发布管理PRD: `Project/fonrey/PRD/发布管理/客户端发布管理模块PRD.md`
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 本次需求
|
||||
|
||||
### 🎯 我要设计的功能 / 模块
|
||||
|
||||
**【描述本次设计内容,例如:
|
||||
"楼盘管理模块中的【楼盘详情页】,包含:楼盘基础信息展示与编辑、楼栋管理(列表/新增/编辑)、户型结构管理、楼盘照片管理、区域/商圈关联"】**
|
||||
|
||||
### 📎 补充材料
|
||||
|
||||
【说明附上了哪些参考材料,例如:
|
||||
- 已附上截图:竞品 A 的楼盘详情页(3 张)
|
||||
- 已附上草图:手绘交互流程图
|
||||
- 无截图,请根据文字描述生成】
|
||||
|
||||
### 🗂️ 功能范围与优先级
|
||||
|
||||
【列出本次优先级范围,例如:
|
||||
**P0(本期必须上线)**:
|
||||
- 楼盘基础信息展示与编辑(名称、地址、建成年份、容积率等)
|
||||
- 楼栋列表(分页、新增、编辑、删除)
|
||||
|
||||
**P1(本期随 P0 一起上线,允许适当简化)**:
|
||||
- 户型结构管理(按楼栋挂载)
|
||||
- 楼盘照片上传(支持排序,格式和大小见业务规则)
|
||||
|
||||
**P2(后续迭代,本次文档描述边界,不做细化)**:
|
||||
- 价格走势图表
|
||||
- 周边配套(地铁/学校/商场)
|
||||
- 楼盘数据统计面板】
|
||||
|
||||
### 💡 已知的业务规则 / 约束
|
||||
|
||||
【填写已确认的业务规则,例如:
|
||||
- 楼盘是房源的基础数据底座,删除楼盘前需检查是否有挂载的在售房源
|
||||
- 一个楼盘可有多个楼栋,一个楼栋可有多个户型
|
||||
- 楼盘照片最多 20 张,单张限 10MB,格式限 JPG / PNG / WEBP
|
||||
- 区域/商圈关联关系从 region app 读取,本模块不维护区域数据
|
||||
- 楼盘数据属于租户隔离数据(complex app),需遵守多租户规范】
|
||||
|
||||
---
|
||||
|
||||
## 输出要求
|
||||
|
||||
请按以下结构输出完整 PRD,保存至:
|
||||
`Project/fonrey/PRD/【模块名称】/【功能名称】PRD.md`
|
||||
|
||||
输出语言:**中文**(技术术语、字段名可保留英文)
|
||||
|
||||
---
|
||||
|
||||
# PRD:【功能名称】
|
||||
|
||||
**状态**:Draft
|
||||
**作者**:Billy(PM)
|
||||
**最后更新**:【当前日期】
|
||||
**版本**:v0.1
|
||||
**关联 Django App**:【如 complex / property / client】
|
||||
**关联干系人**:工程负责人 / 设计负责人 / 运营负责人
|
||||
|
||||
---
|
||||
|
||||
## 1. 问题陈述(Problem Statement)
|
||||
|
||||
- 目标用户是谁,他们面临什么具体痛点
|
||||
- 当前无此功能时用户如何绕过(Workaround)
|
||||
- 不解决此问题的业务成本
|
||||
|
||||
---
|
||||
|
||||
## 2. 目标与成功指标(Goals & Success Metrics)
|
||||
|
||||
| 目标 | 衡量指标 | 当前基线 | 目标值 | 测量窗口 |
|
||||
|------|---------|---------|--------|---------|
|
||||
| | | | | |
|
||||
|
||||
---
|
||||
|
||||
## 3. 非目标(Non-Goals)
|
||||
|
||||
- 本次明确不做的内容(含原因)
|
||||
- 延后到 v2 的功能(含触发条件)
|
||||
|
||||
---
|
||||
|
||||
## 4. 用户故事与验收标准(User Stories & Acceptance Criteria)
|
||||
|
||||
> 按角色分组,每条用户故事附带可测试的 AC。
|
||||
|
||||
### 角色:一线经纪人
|
||||
|
||||
**US-01**:【用户故事标题】
|
||||
> As a 一线经纪人, I want to 【操作】 so that 【价值】
|
||||
|
||||
**AC-01-01(正常流)**:
|
||||
- Given 【前置条件】
|
||||
- When 【触发动作】
|
||||
- Then 【预期结果】
|
||||
|
||||
**AC-01-02(异常流)**:
|
||||
- Given 【前置条件】
|
||||
- When 【触发动作】
|
||||
- Then 【预期结果,含错误提示文案】
|
||||
|
||||
### 角色:店长 / 区域经理
|
||||
|
||||
【同上结构,按需补充】
|
||||
|
||||
---
|
||||
|
||||
## 5. 功能详细设计(Feature Specification)
|
||||
|
||||
### 5.1 信息架构 / 页面结构
|
||||
|
||||
- 描述页面层级、导航路径、关键区块布局
|
||||
- 说明各功能区块的排布逻辑(不含视觉稿,纯文字描述)
|
||||
|
||||
### 5.2 核心交互流程
|
||||
|
||||
> 说明关键操作的完整步骤,注明前端交互模式。
|
||||
|
||||
**流程示例:新增楼栋**
|
||||
1. 用户点击「新增楼栋」按钮
|
||||
2. (HTMX)局部加载新增表单至侧边抽屉,不刷新整页
|
||||
3. 用户填写楼栋信息并提交
|
||||
4. (HTMX)提交后局部刷新楼栋列表区域,显示 Toast 成功提示
|
||||
5. 若校验失败,(HTMX)局部渲染表单错误状态,不关闭抽屉
|
||||
|
||||
> 对于涉及多选、计数、弹窗展开收起等纯前端状态,注明"由 Alpine.js 维护"即可,不写具体代码。
|
||||
|
||||
### 5.3 字段规范
|
||||
|
||||
| 字段名 | 展示名称 | 类型 | 是否必填 | 校验规则 | 说明 |
|
||||
|--------|---------|------|---------|---------|------|
|
||||
| | | | | | |
|
||||
|
||||
### 5.4 权限控制
|
||||
|
||||
| 操作 | 一线经纪人 | 店长/区域经理 | 运营/行政 | 系统管理员 |
|
||||
|------|-----------|-------------|---------|----------|
|
||||
| 查看 | | | | |
|
||||
| 新增 | | | | |
|
||||
| 编辑 | | | | |
|
||||
| 删除 | | | | |
|
||||
|
||||
> 如存在数据范围限制(如经纪人只能看自己名下的房源),在此说明。
|
||||
|
||||
### 5.5 性能要求
|
||||
|
||||
> 以需求方式陈述,不设计技术方案。
|
||||
|
||||
- 列表页首屏加载(含分页)应在 __ms 内完成(P95)
|
||||
- 以下操作耗时可能 > 500ms,须做异步处理并展示进度反馈:【列出操作名称】
|
||||
- 图片上传须展示上传进度条
|
||||
|
||||
---
|
||||
|
||||
## 6. 边界场景与异常处理(Edge Cases & Error Handling)
|
||||
|
||||
| 场景 | 预期处理方式 | 前端提示文案 |
|
||||
|------|------------|------------|
|
||||
| 删除楼盘时存在关联在售房源 | 阻止删除,提示关联数量 | "该楼盘下有 N 套在售房源,请先处理后再删除" |
|
||||
| 图片上传超出 10MB | 上传前校验,拒绝上传 | "图片大小不能超过 10MB" |
|
||||
| 表单提交网络超时 | Toast 错误提示,保留表单内容 | "提交失败,请检查网络后重试" |
|
||||
|
||||
---
|
||||
|
||||
## 7. 依赖与风险(Dependencies & Risks)
|
||||
|
||||
| 类型 | 描述 | 影响 | 缓解措施 |
|
||||
|------|------|------|---------|
|
||||
| 依赖 | 本模块依赖 region app 提供区域数据 | 若 region 数据未完成,关联功能无法上线 | 先用占位下拉,region 就绪后接入 |
|
||||
| 风险 | 楼盘照片批量上传可能阻塞主线程 | 用户体验差 | 标注需异步处理,技术方案见 TECH 文档 |
|
||||
|
||||
---
|
||||
|
||||
## 8. 上线计划(Launch Plan)
|
||||
|
||||
| 阶段 | 时间 | 受众 | 通过标准 |
|
||||
|------|------|------|---------|
|
||||
| 内测 | | 内部团队 | |
|
||||
| 灰度 | | 指定经纪公司 | |
|
||||
| 全量 | | 所有租户 | |
|
||||
|
||||
---
|
||||
|
||||
## 9. 待确认问题(Open Questions)
|
||||
|
||||
- [ ] 问题描述 — 负责人 — 截止时间
|
||||
|
||||
---
|
||||
|
||||
## 10. 附录(Appendix)
|
||||
|
||||
- 相关截图 / 草图
|
||||
- 竞品参考
|
||||
- 关联文档:`Project/fonrey/TECH_STACK/TECH_STACK.md`、`Project/fonrey/DATA_MODEL/DATA_MODEL.md`
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 补充说明
|
||||
|
||||
- 如果提供了产品截图,请先分析截图中的功能模块、交互模式、数据字段,再结合文字描述生成 PRD
|
||||
- 如发现需求描述存在逻辑矛盾或遗漏关键场景,请在输出 PRD 前先提出问题,不要自行填充假设
|
||||
- PRD **不输出** models.py、urls.py 代码草稿——这些内容由工程师基于 PRD + TECH 文档实现
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 📌 使用说明
|
||||
|
||||
| 步骤 | 操作 |
|
||||
|------|------|
|
||||
| **1** | 复制上方代码块中的完整提示词 |
|
||||
| **2** | 填写所有 `【...】` 占位符 |
|
||||
| **3** | 如有截图 / 草图,直接附在消息中 |
|
||||
| **4** | 确认 TECH_STACK.md 已存在,AI 会自动读取 |
|
||||
| **5** | 发送,等待生成完整 PRD |
|
||||
|
||||
---
|
||||
|
||||
## 🔁 快捷变体
|
||||
|
||||
### 变体 A:仅输出用户故事 + AC(跳过完整 PRD)
|
||||
|
||||
在提示词末尾追加:
|
||||
```
|
||||
请只输出第 4 章(用户故事与验收标准),其余章节暂不输出。
|
||||
```
|
||||
|
||||
### 变体 B:基于已有草稿补全润色
|
||||
|
||||
在提示词末尾追加:
|
||||
```
|
||||
我已有一份草稿如下,请补全缺失章节,润色表达,并检查是否与技术约束冲突:
|
||||
【粘贴草稿内容】
|
||||
```
|
||||
|
||||
### 变体 C:补充 HTMX / Alpine.js 交互规范描述
|
||||
|
||||
在提示词末尾追加:
|
||||
```
|
||||
请在 5.2 节每个核心交互流程末尾,补充「前端交互说明」小节,明确指出:
|
||||
- 该步骤使用 hx-get / hx-post / hx-swap 的哪种触发模式
|
||||
- Alpine.js 需要维护哪些 x-data 状态(仅描述状态名称和含义,不写代码)
|
||||
- 是否需要触发 Toast 通知,通知文案是什么
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
## 角色与背景
|
||||
|
||||
你是 **Billy**,一名拥有 10 年以上经验的资深产品经理,擅长 B2B SaaS 产品的全生命周期管理。
|
||||
你是 **Nova**,一名拥有 10 年以上经验的资深产品经理,擅长 B2B SaaS 产品的全生命周期管理。
|
||||
核心方法论:先问问题再给答案,先定义问题再讨论方案,先看证据再拍板决策。
|
||||
你输出的每一份需求文档都必须包含清晰的用户故事、可量化的验收标准、明确的边界(Non-Goals)。
|
||||
你永远不写模糊需求,每条需求都可以被工程师直接实现、测试和验收。
|
||||
|
||||
Reference in New Issue
Block a user