添加变更历史 添加ARD文档

This commit is contained in:
Shen Wei
2026-04-30 20:33:51 +08:00
parent 57600598ac
commit 94d8061eb5
41 changed files with 1213 additions and 193 deletions

View File

@@ -2,13 +2,20 @@
# Fonrey 房产经纪管理系统 — DATA MODEL 设计文档
> **作者**: Backend Architect
> **版本**: v1.4
> **日期**: 2026-04-24v1.1 修复 S1/S2/S4v1.2 扩展 public schemav1.3 §三 DDL 迁至 DATA_MODEL_PUBLIC.md本文改为索引v1.4 补充 LOGIN/PERMISSION 子文档引用、领域对象、租户 Schema 章节、Redis 缓存策略)
> **版本**: v1.5
> **日期**: 2026-04-30v1.1 修复 S1/S2/S4v1.2 扩展 public schemav1.3 §三 DDL 迁至 DATA_MODEL_PUBLIC.md本文改为索引v1.4 补充 LOGIN/PERMISSION 子文档引用、领域对象、租户 Schema 章节、Redis 缓存策略v1.5 新增 ADR 导航与治理联动
> **技术栈**: Django 4.x + PostgreSQL + django-tenants + Redis
> **设计目标**: 支撑 89,000+ 房源、多租户隔离、sub-100ms 查询、合规审计
---
## 变更历史
| 日期 | 变更人 | 变更内容 |
|---|---|---|
| 2026-04-30 | Atlas | 新增 ADR 导航与治理联动规则(数据模型变更需 ADR 可追溯) |
| 2026-04-30 | Atlas | 补充“变更历史”章节(文档治理) |
## 一、架构决策总览 (Architecture Decision Records)
### 1.1 多租户策略Schema-per-Tenant
@@ -157,6 +164,7 @@ OrgUnit (组织架构)
| [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 | ✅ 完成 |
| [ADR.md](../ADR.md) | 架构与需求决策记录(按日期/按模块/历史流水append-only | ✅ 完成 |
---
@@ -833,7 +841,14 @@ app.conf.beat_schedule["partition_maintenance_task"] = {
| 跟进目的枚举 | 存 lookup_items 表,运营可维护 | 初始化数据需提前收集 |
| 手机号加密算法 | AES-256-GCM密钥存 Django settings生产用 Vault | 密钥管理需单独规划 |
## 十、文档治理与 ADR 联动规则
- 任何会影响数据模型边界的变更(新增/删除表、关键约束变更、索引策略调整、枚举口径变更)必须先在 `ADR.md` 记录为 `accepted`,再修改本文件与子文档。
- 若既有数据决策被替代,必须在 `ADR.md` 增加 `superseded` 记录并指向新 ADR禁止直接覆盖旧结论。
- 提交 PR 时,凡涉及 `DATA_MODEL/*` 结构性变更PR 描述必须包含 ADR ID格式`ADR-YYYYMMDD-XXX`)。
- `DATA_MODEL.md` 只维护全局索引与规则;表级 DDL 以各子文档为权威源,避免双写漂移。
---
*本文档为 Fonrey 系统 DATA MODEL v1.0,随 PRD 迭代同步更新。*
*本文档为 Fonrey 系统 DATA MODEL v1.5,随 PRD 与 ADR 迭代同步更新。*
*下一步建议API 接口规范URL 设计 + Request/Response Schema*

View File

@@ -8,6 +8,12 @@
---
## 变更历史
| 日期 | 变更人 | 变更内容 |
|---|---|---|
| 2026-04-30 | Atlas | 补充“变更历史”章节(文档治理) |
## 一、领域概览Domain Overview
### 核心概念

View File

@@ -8,6 +8,12 @@
---
## 变更历史
| 日期 | 变更人 | 变更内容 |
|---|---|---|
| 2026-04-30 | Atlas | 补充“变更历史”章节(文档治理) |
## 一、领域概览Domain Overview
### 核心概念

View File

@@ -9,6 +9,12 @@
---
## 变更历史
| 日期 | 变更人 | 变更内容 |
|---|---|---|
| 2026-04-30 | Atlas | 补充“变更历史”章节(文档治理) |
## 一、领域概览Domain Overview
### 核心概念

View File

@@ -8,6 +8,12 @@
---
## 变更历史
| 日期 | 变更人 | 变更内容 |
|---|---|---|
| 2026-04-30 | Atlas | 补充“变更历史”章节(文档治理) |
## 一、领域概览Domain Overview
### 核心概念

View File

@@ -10,6 +10,12 @@
---
## 变更历史
| 日期 | 变更人 | 变更内容 |
|---|---|---|
| 2026-04-30 | Atlas | 补充“变更历史”章节(文档治理) |
## 一、领域概览Domain Overview
### 1.1 核心概念

View File

@@ -1,45 +1,20 @@
> **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_PROPERTY
> **权威定义**:本文件是房源模块所有表结构的唯一权威来源。
> **所属系统**: Fonrey 房产经纪管理系统
> **版本**: v1.0
> **日期**: 2026-04-24
> **关联模块**: `apps/property/`
> **主文档引用**`DATA_MODEL.md` §3.3§3.16 为本文件的概览摘要,开发以本文件为准。
> **版本**v1.0 | **日期**2026-04-24
---
## 变更历史
## 目录
| 日期 | 变更人 | 变更内容 |
| ---------- | ----- | ---------------- |
| 2026-04-30 | Atlas | 补充“变更历史”章节(文档治理) |
1. [模块说明](#1-模块说明)
2. [表清单](#2-表清单)
3. [枚举值总览](#3-枚举值总览)
4. [DDL 定义](#4-ddl-定义)
- 4.1 [properties房源主表](#41-properties房源主表)
- 4.2 [property_contacts联系人](#42-property_contacts联系人)
- 4.3 [listing_histories挂牌历史](#43-listing_histories挂牌历史)
- 4.4 [price_changes调价记录](#44-price_changes调价记录)
- 4.5 [follow_logs跟进日志](#45-follow_logs跟进日志)
- 4.6 [follow_log_attachments跟进附件](#46-follow_log_attachments跟进附件)
- 4.7 [follow_log_recordings跟进录音](#47-follow_log_recordings跟进录音)
- 4.8 [property_keys钥匙管理](#48-property_keys钥匙管理)
- 4.9 [key_attachments钥匙附件](#49-key_attachments钥匙附件)
- 4.10 [commissions委托管理](#410-commissions委托管理)
- 4.11 [commission_attachments委托附件](#411-commission_attachments委托附件)
- 4.12 [field_surveys实勘管理](#412-field_surveys实勘管理)
- 4.13 [survey_photos实勘照片](#413-survey_photos实勘照片)
- 4.14 [property_photos房源图片](#414-property_photos房源图片)
- 4.15 [property_attachments房源附件](#415-property_attachments房源附件)
- 4.16 [property_marketing营销信息](#416-property_marketing营销信息)
- 4.17 [property_certificates产证信息](#417-property_certificates产证信息)
- 4.18 [property_completeness维护完成度](#418-property_completeness维护完成度)
- 4.19 [property_tags / property_tag_relations标签](#419-property_tags--property_tag_relations标签)
- 4.20 [property_favorites收藏](#420-property_favorites收藏)
- 4.21 [property_protections保护房](#421-property_protections保护房)
- 4.22 [number_holder_approvals号码方审批](#422-number_holder_approvals号码方审批)
5. [触发器](#5-触发器)
6. [查询模式参考](#6-查询模式参考)
7. [禁止操作](#7-禁止操作)
---
## 1 模块说明

View File

@@ -2,7 +2,7 @@
# Fonrey — Public Schema 数据模型
> **作者**: Backend Architect
> **版本**: v1.3
> **版本**: v1.5
> **日期**: 2026-04-30
> **权威源**: 本文件是 `public` schema 所有表的唯一权威定义
> **设计依据**: 系统管理模块 PRD`PRD/系统管理/系统管理模块PRD.md`);客户端发布管理模块 PRD`PRD/发布管理/客户端发布管理模块PRD.md`
@@ -10,6 +10,12 @@
---
## 变更历史
| 日期 | 变更人 | 变更内容 |
|---|---|---|
| 2026-04-30 | Atlas | 补充“变更历史”章节(文档治理) |
## 一、概览
`public` schema 存储**平台运营层**数据,与各租户的业务 schema 完全隔离。
@@ -34,6 +40,7 @@ PostgreSQL Instance
│ ├── system_versions 版本历史
│ ├── upgrade_events 升级事件A/B/C 分级 + B 类分批编排)
│ ├── client_releases 客户端版本发布
│ ├── client_heartbeats 客户端启动心跳(活跃统计 + 版本分布)
│ ├── feature_flag_definitions Feature Flag 定义C 类灰度控制平面)
│ └── feature_flag_change_log Feature Flag 变更历史append-only
@@ -59,6 +66,7 @@ PostgreSQL Instance
| `public.system_versions` | 平台版本历史,唯一 current 约束 | §2.5 |
| `public.upgrade_events` | 升级/回滚事件A/B/C 升级类型分级B 类按租户分批编排 + 健康门控 | §2.5 |
| `public.client_releases` | Windows 客户端发布版本,含安装包 URL、SHA256、强制更新标记 | §2.6 |
| `public.client_heartbeats` | 客户端启动心跳(活跃统计 + 版本分布支撑) | §2.6 |
| `public.feature_flag_definitions` | Feature Flag 全局定义C 类运行时灰度控制平面) | §2.7 |
| `public.tenants.feature_flags` | 租户级 Flag 显式覆盖(`tenants` 表新增 JSONB 列) | §2.7 |
| `public.feature_flag_change_log` | Feature Flag 变更历史append-only | §2.7 |
@@ -538,6 +546,72 @@ CREATE INDEX idx_client_releases_status ON public.client_releases(status, create
-- 按版本号快速定位(客户端更新检测时传入 current_version 查询)
CREATE INDEX idx_client_releases_version ON public.client_releases(version);
-- ────────────────────────────────────────────────────────────
-- 客户端启动心跳表(活跃统计 + 版本分布数据支撑)
-- ────────────────────────────────────────────────────────────
-- 设计依据: 客户端发布管理模块 PRD §5.5 Story 5客户端版本分布查询
-- 上报模型: 客户端 App 每次启动时 Upsert 一条记录by tenant_id + device_id
-- 归属说明: 本表属于 shared_appspublic schema。虽然 tenant_id 字段做了租户归属,
-- 但 device_id 由客户端首次安装时生成(本机 UUID跨租户全局唯一管理
-- 将本表放在 public schema 便于平台运营做全量版本分布、活跃数统计,
-- 避免跨所有 tenant schema 做 UNION ALL。
-- 隐私说明: 不收集任何业务数据,仅用于平台运营版本管理与升级决策。
CREATE TABLE public.client_heartbeats (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- 归属
tenant_id UUID NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
-- 心跳上报时所属租户(由客户端登录态决定,从 Tenant Code 解析得到);
-- 是"按租户统计安装数 / 活跃数 / 版本分布"的核心维度字段,
-- 配合 idx_client_heartbeats_tenant_lastseen 索引支撑租户维度聚合查询;
-- 租户被硬删除时心跳一并清理CASCADE
-- 同一物理设备登录不同租户视作多条独立记录Upsert 锚点为 (tenant_id, device_id)
device_id UUID NOT NULL,
-- 客户端首次安装时本机生成并持久化的设备 UUID
-- 卸载重装会变化(视作新设备);同一设备换租户登录视作不同记录
user_id UUID,
-- 上次启动时登录的用户 ID指向 tenant schema 的 staff.id
-- 不加外键约束(跨 schema 无法外键),仅作排查辅助;
-- 未登录或退出后启动允许为 NULL
-- 版本与环境
client_version VARCHAR(20) NOT NULL,
-- 上报时客户端 SemVer 版本号,如 '1.2.3'
platform VARCHAR(20) NOT NULL DEFAULT 'win32'
CHECK (platform IN ('win32')),
arch VARCHAR(10) NOT NULL DEFAULT 'x64'
CHECK (arch IN ('x64', 'arm64')),
os_version VARCHAR(80),
-- 操作系统版本字符串,如 'Windows 10 22H2';可选
-- 时间戳
first_seen_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
-- 该 (tenant_id, device_id) 组合首次心跳时间Upsert 时不更新
last_seen_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
-- 最近一次启动心跳时间Upsert 时刷新为 NOW()
launch_count INTEGER NOT NULL DEFAULT 1,
-- 累计启动次数Upsert 时 +1用于排查异常高频启动
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
-- 同一租户下同一设备唯一一条记录Upsert 锚点)
CONSTRAINT uq_client_heartbeats_tenant_device UNIQUE (tenant_id, device_id)
);
-- 活跃数统计("最近 24h 内有心跳")核心索引
CREATE INDEX idx_client_heartbeats_last_seen
ON public.client_heartbeats(last_seen_at DESC);
-- 版本分布统计:按版本聚合活跃设备数
CREATE INDEX idx_client_heartbeats_version_lastseen
ON public.client_heartbeats(client_version, last_seen_at DESC);
-- 租户维度排查:某租户下所有活跃设备
CREATE INDEX idx_client_heartbeats_tenant_lastseen
ON public.client_heartbeats(tenant_id, last_seen_at DESC);
```
### 2.7 Feature Flag 灰度体系
@@ -644,6 +718,9 @@ CREATE INDEX idx_ff_log_operator ON public.feature_flag_change_log(operator_id,
| `client_releases` 唯一 published | `idx_client_releases_published` 部分唯一索引保证同平台+架构下只有一条 `status='published'` |
| `client_releases` 不可跨租户隔离 | 本表属于 public schema所有租户共享禁止在租户 schema 中创建副本 |
| `client_releases.download_count` 原子递增 | 必须使用 `UPDATE ... SET download_count = download_count + 1`,禁止先读后写 |
| `client_heartbeats` Upsert 锚点 | 必须使用 `INSERT ... ON CONFLICT (tenant_id, device_id) DO UPDATE SET last_seen_at=NOW(), launch_count=client_heartbeats.launch_count+1, client_version=EXCLUDED.client_version`;禁止先 SELECT 再 INSERT |
| `client_heartbeats` 仅启动时上报 | 客户端 App 仅在每次启动时上报一次心跳;不做 4h 周期心跳,避免 public schema 写入压力 |
| `client_heartbeats` 不可跨租户隔离 | 本表属于 public schema所有租户共享禁止在租户 schema 中创建副本 |
| `upgrade_events.gray_tenant_ids``upgrade_type` 一致性 | A_app 类型必须为空数组CHECK 约束 `chk_app_upgrade_no_gray` 强制);只有 B_schema 类型才使用灰度名单与批次字段 |
| `upgrade_events` 状态流转单向 | 状态机详见 §4.2`succeeded` / `rolled_back` / `failed` 为终态,禁止再写回中间态 |
| `feature_flag_change_log` append-only | 禁止 UPDATE / DELETE任何 Flag 变更都必须经此表追溯,含 `reason` 强制非空 |
@@ -782,11 +859,83 @@ SELECT version, platform, arch, release_type, status,
FROM public.client_releases
ORDER BY created_at DESC;
-- 统计各版本活跃客户端数(需结合客户端上报心跳表,当前仅记录下载量
SELECT version, download_count
FROM public.client_releases
WHERE status IN ('published', 'archived')
ORDER BY published_at DESC;
-- 客户端启动心跳 Upsert每次启动调用原子更新最后活跃时间与启动次数
INSERT INTO public.client_heartbeats (
tenant_id, device_id, user_id,
client_version, platform, arch, os_version,
first_seen_at, last_seen_at, launch_count
)
VALUES ($1, $2, $3, $4, $5, $6, $7, NOW(), NOW(), 1)
ON CONFLICT (tenant_id, device_id) DO UPDATE
SET last_seen_at = NOW(),
launch_count = public.client_heartbeats.launch_count + 1,
client_version = EXCLUDED.client_version,
user_id = EXCLUDED.user_id,
os_version = EXCLUDED.os_version,
updated_at = NOW();
-- 统计各版本活跃客户端数(活跃 = 最近 24h 内有心跳)
SELECT cr.version,
cr.status,
cr.download_count,
COUNT(ch.id) FILTER (
WHERE ch.last_seen_at >= NOW() - INTERVAL '24 hours'
) AS active_devices_24h,
COUNT(ch.id) AS total_devices_ever
FROM public.client_releases cr
LEFT JOIN public.client_heartbeats ch ON ch.client_version = cr.version
WHERE cr.status IN ('published', 'archived')
GROUP BY cr.version, cr.status, cr.download_count, cr.published_at
ORDER BY cr.published_at DESC;
-- 平台总活跃数(最近 24h
SELECT COUNT(*) AS active_devices_24h
FROM public.client_heartbeats
WHERE last_seen_at >= NOW() - INTERVAL '24 hours';
-- 某租户下活跃设备列表(运营排查)
SELECT device_id, client_version, user_id, last_seen_at, launch_count
FROM public.client_heartbeats
WHERE tenant_id = $1
AND last_seen_at >= NOW() - INTERVAL '24 hours'
ORDER BY last_seen_at DESC;
-- ============================================================
-- 租户维度安装/活跃统计Platform Admin 运营看板)
-- ============================================================
-- 某租户当前活跃安装数(最近 24h 内启动过的设备)
SELECT COUNT(*) AS active_installs_24h
FROM public.client_heartbeats
WHERE tenant_id = $1
AND last_seen_at >= NOW() - INTERVAL '24 hours';
-- 某租户历史装机总数(不区分是否活跃,含已离线设备)
SELECT COUNT(*) AS total_installs_ever
FROM public.client_heartbeats
WHERE tenant_id = $1;
-- 某租户内部各版本分布(识别该租户内升级覆盖率)
SELECT client_version,
COUNT(*) FILTER (WHERE last_seen_at >= NOW() - INTERVAL '24 hours') AS active_devices_24h,
COUNT(*) AS total_devices_ever
FROM public.client_heartbeats
WHERE tenant_id = $1
GROUP BY client_version
ORDER BY active_devices_24h DESC;
-- 全平台租户活跃榜PRD §5.5 Story 5按租户维度查看版本分布识别落后租户
SELECT t.tenant_code,
t.name,
COUNT(h.id) FILTER (
WHERE h.last_seen_at >= NOW() - INTERVAL '24 hours'
) AS active_installs_24h,
COUNT(h.id) AS total_installs_ever
FROM public.tenants t
LEFT JOIN public.client_heartbeats h ON h.tenant_id = t.id
WHERE t.status = 'active'
GROUP BY t.id, t.tenant_code, t.name
ORDER BY active_installs_24h DESC;
-- ============================================================
-- 升级编排相关查询B 类 Schema 迁移分批升级)
@@ -878,3 +1027,6 @@ ORDER BY created_at DESC;
| v1.0 | 2026-04-24 | 从 `DATA_MODEL.md §三` 独立拆分;内容等价于 v1.2 DATA_MODEL.md §三 |
| v1.1 | 2026-04-24 | 新增 §2.6 `client_releases` 表(客户端发布管理);同步更新表清单、约束规则、查询模式 |
| v1.2 | 2026-04-26 | 配合系统管理技术文档 v1.2 升级分批专节§2.5 `upgrade_events` 增加 `upgrade_type``gray_tenant_ids``batch_size``batch_concurrency``batch_interval_seconds``failure_policy``current_batch_no``total_batch_count``health_gate_config``pre_backup_record_id``halted_reason` 字段,状态机扩展为 10 态;新增 §2.7 Feature Flag 体系(`feature_flag_definitions` / `tenants.feature_flags` JSONB / `feature_flag_change_log`同步更新表清单、§4.2 升级状态机、§4.3 Flag 流程、§5.1 查询模式、§5.2 禁止操作 |
| v1.3 | 2026-04-30 | 配合登录管理 PRD v2.0 / 系统管理 PRD§2.1 `tenants` 新增 `tenant_code` (CHAR(12), 全局唯一对外识别码) 与 `contact_phone` (CHAR(11)Tenant Admin 账号创建数据来源)`contact_email` 改为可 NULL同步更新约束、查询模式 |
| v1.4 | 2026-04-30 | 配合客户端发布管理 PRD v1.1 §5.5 Story 5§2.6 新增 `client_heartbeats` 表(启动时 Upsert by `tenant_id + device_id`,活跃定义为最近 24h 内心跳);同步更新表清单、§三 约束Upsert 锚点 / 启动上报 / 不可下沉到租户 schema、§5.1 查询模式Upsert 模板 + 版本活跃分布 JOIN + 平台总活跃数 + 租户维度排查MVP 不做升级趋势图v2 规划) |
| v1.5 | 2026-04-30 | 配合客户端发布管理 PRD v1.2 §5.5 Story 5 验收标准追加(按租户统计安装数):① §2.6 `client_heartbeats.tenant_id` 注释强化,明确为"按租户统计安装数 / 活跃数 / 版本分布"的核心维度字段;② §5.1 新增"租户维度安装/活跃统计"查询专区4 个查询:某租户活跃安装数 / 历史装机总数 / 租户内版本分布 / 全平台租户活跃榜) |

View File

@@ -9,6 +9,12 @@
---
## 变更历史
| 日期 | 变更人 | 变更内容 |
|---|---|---|
| 2026-04-30 | Atlas | 补充“变更历史”章节(文档治理) |
## 一、模块定位与架构边界
### 1.1 系统配置模块职责

View File

@@ -1,3 +1,4 @@
`DATA_MODEL.md` 的核心目标是:**让 AI 在触碰任何数据相关代码时,都能理解业务语义,而不只是看到一堆字段名。**
---
@@ -11,6 +12,7 @@
md
```md
## Domain Overview
This is a **multi-tenant** project management tool.
@@ -259,4 +261,4 @@ md
---
**核心原则**`DATA_MODEL.md` 写的不是给数据库看的 DDL而是给 AI 看的**业务契约**。每一个"显而易见"的业务规则,在 AI 眼里都是需要被明确告知的约束。没写的,它都会自己猜。
**核心原则**`DATA_MODEL.md` 写的不是给数据库看的 DDL而是给 AI 看的**业务契约**。每一个"显而易见"的业务规则,在 AI 眼里都是需要被明确告知的约束。没写的,它都会自己猜。

View File

@@ -13,6 +13,12 @@
---
## 变更历史
| 日期 | 变更人 | 变更内容 |
|---|---|---|
| 2026-04-30 | Atlas | 补充“变更历史”章节(文档治理) |
## 一、枚举分层标准(必须遵守)
Fonrey 采用两层枚举体系:

View File

@@ -1,111 +0,0 @@
# Schema 变更记录
> **用途**:记录在初始 Model 创建之后新增/修改的字段,供架构师逐一更新 Migration 脚本。
> **格式**:每条变更含目标表、变更类型、字段定义、变更原因、来源 PRD 依据。
> **状态流转**`待迁移` → `已迁移`(架构师完成 migration 后更新状态)
---
## 变更清单
| # | 状态 | 目标表 | 变更类型 | 字段名 | 提出日期 |
|---|------|--------|----------|--------|----------|
| 1 | 待迁移 | `public.tenants` | 新增字段 | `tenant_code` | 2026-04-30 |
| 2 | 待迁移 | `public.tenants` | 新增字段 | `contact_phone` | 2026-04-30 |
| 3 | 待迁移 | `public.tenants` | 字段修改 | `contact_email`NOT NULL → 可 NULL | 2026-04-30 |
---
## 变更详情
### #1 — `public.tenants` 新增 `tenant_code`
**状态**:待迁移
**提出日期**2026-04-30
**目标表**`public.tenants`Public Schemadjango-tenants 主租户表)
**字段定义**
```sql
tenant_code CHAR(12) UNIQUE NOT NULL
```
**注释**:对外暴露的 12 位纯数字识别码,如 `202500010001`;用户首次登录客户端时输入;由平台运营在注册租户时生成;创建后不可修改。
**推荐加在**`schema_name` 字段之后,`name` 字段之前。
**约束要求**
- `CHAR(12)`:固定 12 位
- `UNIQUE`:全局唯一
- `NOT NULL`:注册时必填
- 禁止 `UPDATE`(应用层 + DB 层双重约束,与 `schema_name` 同等级别)
**建议索引**
```sql
CREATE UNIQUE INDEX idx_tenants_tenant_code ON public.tenants(tenant_code);
```
**变更原因**
登录 PRD §5.1.3 明确用户输入 12 位 Tenant Code 完成租户识别,服务端需通过该码查找对应 `schema_name`,原始 `public.tenants` 表中缺少此字段。
**PRD 依据**
`PRD/登录管理/用户登录管理模块PRD.md` §5.1.3 — Tenant Code 格式规范:
> 格式:固定 12 位纯数字,如 `202500010001`
**登录查询示例**
```sql
SELECT schema_name, name
FROM public.tenants
WHERE tenant_code = '202500010001'
AND status = 'active';
```
---
### #2 — `public.tenants` 新增 `contact_phone`
**状态**:待迁移
**提出日期**2026-04-30
**目标表**`public.tenants`Public Schema
**字段定义**
```sql
contact_phone CHAR(11) NOT NULL
```
**注释**联系人手机号11位纯数字系统开通租户时自动以此手机号创建 Tenant Admin 登录账号;必填字段,平台运营在创建租户时录入。
**推荐加在**`contact_name` 字段之后,`contact_email` 字段之前。
**约束要求**
- `CHAR(11)`:固定 11 位手机号
- `NOT NULL`:开通租户时必填
**变更原因**
登录 PRD v1.5 决策Tenant Admin 账号统一以联系人手机号作为用户名,与普通员工账号规则对齐。需在 tenants 表增加手机号字段作为 Tenant Admin 账号创建的数据来源。
**PRD 依据**
`PRD/登录管理/用户登录管理模块PRD.md` §5.3.2 Tenant Admin 账号规格表
---
### #3 — `public.tenants.contact_email` 改为可 NULL
**状态**:待迁移
**提出日期**2026-04-30
**目标表**`public.tenants`Public Schema
**变更 SQL**
```sql
ALTER TABLE public.tenants
ALTER COLUMN contact_email DROP NOT NULL;
```
**变更原因**
`contact_email` 原为 `NOT NULL`。由于 Tenant Admin 账号创建数据来源改为 `contact_phone`(手机号,必填),邮箱降级为选填,仅用于找回密码功能。为空时 Tenant Admin 无法自助找回密码,需联系平台运营处理。
**PRD 依据**
`PRD/登录管理/用户登录管理模块PRD.md` §5.3.2
---
*后续如有新增字段,按同样格式追加到本文件。*