版本:v0.1(草案,待调研结论回填后定稿)
目标规模:百万级房源 / 千万级租客 / 亿级账单流水
技术栈前提:Java 生态,多租户 SaaS
落盘位置:/Volumes/Extreme Pro/Workspace/rental-saas-20261018/
绝大多数租赁系统设计失败的根因,是把四类生命周期完全不同的数据混在一张表里。本设计的
第一原则是按数据性质分层,而不是按业务模块分表。
| 类别 | 特征 | 典型数据 | 存储载体 | 变更频率 |
|---|---|---|---|---|
| A. 主数据(Master) | 有唯一身份、被引用、需强一致 | 租户、楼盘、楼栋、房间、租客、设备 | PostgreSQL(OLTP) | 低 |
| B. 事务数据(Transactional) | 有状态机、有流水、需审计与对账 | 合同、账单、收款、工单 | PostgreSQL(OLTP)+ 时间分区 | 高 |
| C. 时序/事件数据(Time-series) | 写多读少、按时间窗口聚合、几乎不改 | IoT 设备上报、门锁记录、操作审计、Agent 调用日志 | 时序库(ClickHouse 为主) | 极高 |
| D. 非结构化数据(Unstructured) | 大对象、内容寻址 | 房源图片、视频、AR 全景图/glTF 模型、合同扫描件、工单现场照片 | 对象存储(RustFS/S3)+ 元数据表 | 中 |
关键结论:
合同要留痕),分布式事务带来的复杂度远大于它买到的扩展性。百万房源 / 千万租客这个量级,
PostgreSQL 单集群配好分区完全扛得住(详见 03 文档容量测算)。
百万房源即产生 每年 10 亿+ 行写入,压垮 OLTP 主库只是时间问题。必须旁路到时序库。
media_asset 元数据 + 对象存储 key。这是图片/视频/AR 模型这类大对象的唯一正确做法。
按你给的范围(数字家庭、工单、物业、长短租、房源含 AR、Agent 能力)拆解。
每域只列「基础数据」,即需要持久化并有明确生命周期的实体,不含派生视图与缓存。
这是 SaaS 的地基,所有业务表都必须挂在租户下。
| 实体 | 说明 | 关键字段 |
|---|---|---|
tenant | 租户(一家运营公司),SaaS 隔离单元 | 租户码、名称、套餐、状态、隔离策略 |
tenant_config | 租户级配置(计费规则、账期、时区、编号规则) | 键值对 + 类型 |
user_account | 平台登录账号(与「租客」是两回事) | 手机号、密码散列、MFA |
role / permission | RBAC 权限模型 | 角色、权限码 |
user_role_binding | 账号-角色绑定 | — |
user_tenant_binding | 账号-租户绑定(一人可服务多租户) | — |
data_scope_rule | 数据权限(某账号只看某几个楼盘) | 资源类型 + 范围 |
⚠️ 必须区分
user_account(系统操作者)与tenant_person(租客自然人)。
二者可能重合(租客也登录 App),但绝不能合并 —— 系统操作者没有租赁合同,
租客也不需要后台权限。混表是租赁系统最常见的设计债。
房源是多层级结构,且长租与短租共用一套物理资产。
| 实体 | 说明 | 层级 |
|---|---|---|
property | 物业项目 / 小区 | 1 |
building | 楼栋 | 2 |
unit | 单元 / 座 | 3 |
floor | 楼层(可虚拟) | 4 |
room | 房间 / 房源(核心实体) | 5 |
room_attribute | 房间属性(面积、朝向、户型、装修、配套),支持扩展 | — |
room_status_log | 房间状态变更留痕(空置/预定/在租/维修/停用) | — |
bed | 床位(合租场景,一间房多床) | 6 |
parking_space | 车位(也是可租资产) | 附属 |
amenity | 配套设施字典(健身房、泳池) | 字典 |
room_amenity | 房间-设施关联 | 关联 |
设计要点:
room是「可出租的最小单元」,一切租赁、账单、设备都挂在room_id上。
短租与长租的差异不在物理表,而在「库存维度」:长租房间同一时刻只有 0/1 份库存,
短租需要间夜库存日历(见下)。
| 实体 | 说明 |
|---|---|
room_calendar | 间夜库存日历(短租核心),按 room_id + biz_date 唯一,记录可售/已售/价格/限制 |
rate_plan | 价格方案(长租月租价、短租日价、阶梯价、会员价) |
rate_policy | 价格策略(连住折扣、提前订优惠、最低入住天数) |
防超卖的唯一正确做法:以
room_calendar的(room_id, biz_date)唯一约束 +
行级锁(或UPDATE ... WHERE sold_count < total_count条件更新)做原子性扣减。
绝不能「先查再写」。
| 实体 | 说明 |
|---|---|
tenant_person | 租客自然人(个人),含证件、手机、实名状态 |
tenant_org | 企业租客(公司名义承租) |
tenant_person_org | 个人-企业归属(员工、联系人) |
guarantor | 担保人 |
credit_profile | 信用档案(欠租记录、黑名单、评分) |
tenant_person_identity | 证件附件与实名认证记录(走对象存储) |
contact_record | 沟通/跟进记录(为 Agent 能力准备语料) |
千万级租客的存储要点:租客表是跨租户全局实体还是租户内实体?
这是本设计最重要的一个判断 —— 同一自然人在两家运营公司都租过房,
若做成租户内实体会产生重复身份,风控与黑名单失效。**结论:租客自然人做全局表,
通过关联表挂到租户下**(详见 02 文档多租户章节的「混合隔离」)。
| 实体 | 说明 |
|---|---|
contract | 合同主表,含类型(长租/短租)、起止、状态机 |
contract_party | 合同当事人(承租方、出租方、担保人、共租人) |
contract_room | 合同-房间关联(一合同可含多间房) |
contract_term | 合同条款(版本化,JSONB 存结构化条款) |
contract_change | 变更单(续租、换房、改价、提前退租),保留原合同不动 |
contract_deposit | 押金记录(应收/实收/退还/扣除) |
checkin_record | 入住/退租交割(水电底数、物品清单、钥匙) |
contract_file | 合同电子档与电子签章记录 |
核心原则:合同只增不改。 任何变更都产生
contract_change新记录 +
生成新版本,contract上只维护current_version。否则历史对账必然出错。
| 实体 | 说明 |
|---|---|
bill | 账单(租金/押金/水电/物业费/违约金),按月/按周期生成 |
bill_item | 账单明细(一张账单可拆多个费用项) |
bill_period | 账期定义 |
fee_item | 费用项字典(租金、水费、电费、物业费、停车费) |
meter | 仪表(水/电/燃气表),关联房间 |
meter_reading | 抄表读数(时序性质,但需精确保留) |
payment | 收款流水(一笔支付) |
payment_allocation | 核销明细(一笔收款核销多张账单) |
refund | 退款单 |
invoice | 发票(申请、开具、红冲) |
dunning_record | 催缴记录(为 Agent 自动催收准备) |
late_fee_rule | 滞纳金规则 |
收租建模的关键:
payment与bill是多对多,必须用payment_allocation连接。
新人最常犯的错是给bill加一个payment_id字段 —— 一次性付清 3 个月房租就无法表达。
核销明细表还必须记录每笔核销金额,且核销过程幂等(唯一键 = 支付流水号 + 账单号)。
| 实体 | 说明 |
|---|---|
service_catalog | 服务项目录(保洁、维修、代收快递) |
work_order | 工单主表,含类型、优先级、状态机 |
work_order_item | 工单明细(材料、工时、费用) |
work_order_flow | 工单流转记录(谁在何时做了什么) |
work_order_attachment | 现场照片/视频(对象存储引用) |
staff | 服务人员(维修工、管家)与技能标签 |
dispatch_record | 派单记录 |
sla_rule | SLA 规则(响应时限、完工时限) |
inspection_task | 巡检任务与巡检点 |
工单是强状态机实体,状态流转必须有
work_order_flow全量留痕,
不允许只更新work_order.status。否则 SLA 计时与责任判定无从谈起。
| 实体 | 说明 |
|---|---|
device | 设备主数据(门锁、电表、水表、传感器、摄像头) |
device_model | 设备型号与能力定义(支持的属性/指令) |
device_binding | 设备-房间-租户绑定关系(含生效时间段) |
device_credential | 设备密钥/证书 |
device_shadow | 设备影子(最新状态快照,覆盖写) |
device_event | 设备事件(开锁、告警)→ 时序库 |
device_telemetry | 设备遥测(温度、电量)→ 时序库 |
device_command | 指令下发记录(含下发/回执状态) |
scene / scene_action | 场景联动(回家模式、离家模式) |
access_grant | 门锁临时密码/权限(租客、保洁、维修) |
device_shadow(影子)与device_telemetry(时序)必须分开:
前者是「此刻状态」,一张小表高频覆盖写,查询 O(1);
后者是「历史轨迹」,写入时序库。混在一起会让状态查询变成全表扫描。
| 实体 | 说明 |
|---|---|
media_asset | 媒体资源元数据(图片/视频/音频/模型),不存字节 |
media_variant | 多规格派生(缩略图、不同清晰度),一源多规格 |
media_binding | 媒体-业务对象绑定(房间、合同、工单、设备) |
ar_scene | AR 场景(一个房间可有多套场景/多期拍摄) |
ar_scene_node | 场景节点(全景图球面、三维模型、热点锚点) |
ar_hotspot | 热点(点击跳到某位置/展示某信息/触发某操作) |
ar_camera_path | 相机轨迹(漫游路线) |
ar_scene_version | 场景版本(重新拍摄、模型更新) |
vr_session | 看房会话(谁在何时看了哪个场景、停留时长)→ 行为数据 |
AR 看房的数据本质是三块:
① 全景图/立方体贴图(大文件,对象存储,按瓦片切分);
② glTF/GLB 三维模型(含 Draco 压缩,对象存储 + 版本号);
③ 空间锚点元数据(热点坐标、相机轨迹、房间尺寸),这部分是**小体积强结构数据,
必须进 PostgreSQL**,因为它要和房间、设备、账单关联(例如热点上挂「这台空调每月耗电」)。
千万不要把锚点数据塞进模型文件里 —— 那样就无法做「点热点→查真实设备状态」的联动。
这是最新的一块,也是最容易被设计成「一堆散字段」的一块。
| 实体 | 说明 |
|---|---|
agent_definition | Agent 定义(名称、系统提示词、可用工具集、模型配置) |
agent_tool | 工具注册表(工具名、参数 Schema、后端实现、权限要求) |
agent_session | 会话(谁、哪个渠道、关联哪个业务对象) |
agent_message | 消息(角色、内容、token 用量、耗时) |
agent_tool_call | 工具调用记录(入参、出参、是否成功、耗时)→ 审计用 |
agent_memory | 长期记忆(结构化键值 + 向量) |
knowledge_doc | 知识库文档(制度、FAQ、话术) |
knowledge_chunk | 文档切片 + 向量(pgvector) |
prompt_template | 提示词模板与版本 |
Agent 落地的三个数据要点:
1. 工具调用必须可审计:agent_tool_call要记录入参出参,否则 Agent 误操作无法追责。
2. 向量不必上专用库:本机 PostgreSQL 17 已装 pgvector 0.8.7,知识库切片量级
(十万~百万级)用 pgvector + HNSW 索引完全够用,省掉一套独立向量库的运维成本。
超过千万切片再考虑迁移。
3. Agent 的「记忆」要区分短期(会话上下文)与长期(跨会话画像),
长期记忆才是让它「越用越懂这个租客」的关键,也是租赁场景最值钱的数据资产。
tenant (租户)
├── property (小区) ── building ── unit ── floor ── room ── bed
│ ├── room_attribute
│ ├── room_calendar (短租库存)
│ ├── device_binding ── device ── device_shadow
│ ├── media_binding ── media_asset
│ └── ar_scene ── ar_scene_node ── ar_hotspot
│
├── tenant_person (租客,全局实体) ── credit_profile
│ │
│ └── contract ── contract_party / contract_room / contract_term
│ ├── bill ── bill_item
│ │ └── payment_allocation ── payment
│ └── contract_change (变更留痕)
│
├── work_order ── work_order_flow / work_order_item / dispatch_record
│ └── 关联 room / contract / tenant_person
│
└── agent_session ── agent_message ── agent_tool_call
└── 关联任意业务对象 (多态引用)
三条贯穿全局的引用原则:
room_id 是资产轴的唯一锚点 —— 设备、媒体、AR 场景、合同、工单最终都归到房间。contract_id 是履约轴的唯一锚点 —— 账单、押金、交割、催缴都归到合同。tenant_person_id 是人的唯一锚点 —— 但它是全局实体,不随租户隔离(见 02 文档)。| # | 问题 | 结论 | 依据 |
|---|---|---|---|
| 1 | 租客自然人是全局实体还是租户私有? | 全局实体,不做租户隔离 | 若隔离,跨公司黑名单失效,风控形同虚设。用哈希查重 + 证件加密平衡隐私。详见 02 文档 |
| 2 | 短租是否与长租同库同表? | 同表 room,短租靠 room_calendar 库存日历 | 物理资产是同一份,差异只在库存维度。分表会导致同一房间两套身份 |
| 4 | 时序库选 ClickHouse 还是 TimescaleDB? | ClickHouse | 遥测是纯追加+聚合场景,正是其主场;TimescaleDB 与主库同实例会抢 OLTP 资源。详见 02 文档 |
| 7 | 向量库用专用还是 pgvector? | pgvector(本机已装 0.8.7) | 百万切片以内 HNSW 足够,省一套中间件运维 |
| # | 问题 | 影响 | 建议 |
|---|---|---|---|
| 3 | 是否需要 PostGIS? | 本机未安装,需运维部署 | 若要做「地铁房」「学区房」「地图选址」,建议装。当前用 lng/lat 两列 + 应用层算距离,10 万房源内可接受 |
| 5 | 电子签章自建还是第三方? | 影响 contract_file.sign_provider 取值 | 强烈建议接第三方(法大大/e签宝),自建电子签法律效力难保证 |
| 6 | AR 看房自研还是接第三方? | 影响 ar_scene* 表的粒度 | 建议接第三方拍摄能力(如贝壳如视/众趣),但锚点数据必须存自己库——否则业务联动被厂商锁死。本设计的 ar_hotspot 已按此预留 |
| 8 | 短租房源是否与 OTA(携程/美团)打通? | 影响是否需要「渠道库存同步表」 | 若要打通,需增加 channel_inventory 表做多渠道库存映射与超卖防护。这是一个隐藏的大坑:多渠道库存同步需分布式锁 + 补偿队列 |
没有收租就没有商业模式。
建议先做单一楼盘试点。
Agent 无米下锅。但 agent_tool_call 等审计表建议早期就建,
因为工具调用日志越早积累越有价值。
本文档为基础数据梳理草案,详细表结构与 DDL 见 02-多租户与技术栈选型.md、03-数据库设计.md。