Files
wyndham-Condon/backend-data-model.md
2026-08-02 13:33:13 +08:00

14 KiB
Raw Blame History

CONDO 后端数据库模型

更新日期2026-07-31
业务依据:当前前端页面、字段和运行规则

1. 设计原则

  • 当前前端是生产数据库和 API 的业务规格。
  • 生产表与本地导入预演分开;历史批次通过受控 importer 写入生产表,不把原始 Excel 作为数据库运行时依赖。
  • 所有新对象仅位于现有 booking_test 数据库的 condon schema不改动其他 schema 的对象、数据、角色或权限。
  • 一个 Usage Record 代表一间房的一次使用。
  • 每个业主房号独立管理权益余额。
  • 所有 Use 和 Balance 都由后端计算,不能信任前端传来的结果。
  • V1 只实现实时新增使用记录;取消、冲正、调整、账户状态和用户角色不预建。

2. 当前前端字段

2.1 Owner Account

  • No.
  • Transfer Date
  • Name
  • Room No.
  • Room Type
  • Unit No.
  • Member No.
  • Remaining stay privileges

2.2 Usage History

  1. Confirmation No.
  2. Check-in
  3. Check-out
  4. Night
  5. Use
  6. Balance
  7. Used Room Type
  8. Remark

关系为:

  • Night = Check-out - Check-in
  • Use = Night × Multiplier
  • Balance After = Balance Before - Use

历史导入另外保留 RoomTotal 和来源定位字段;前端新建记录仍默认 Room=1余额由后端维护。

3. 房型使用规则

3.0 酒店房型代码定义

代码 酒店定义
RM1 No balcony TWN4+4 F
RM2 Superior King6F
RM3 Superior TWN4+4 F
RM4 Superior TWN6+4 F
UG1 Deluxe King6F
UG2 Deluxe TWN6+4 F
SU1 Junior Suite King6F
SU2 Junior Suite King6FPool view
SU6 Junior Suite TWN4+4 F
SU3 Two bedroom / Family room1 room King + 1 room TWN
AC1 Handicap King
AC2 Handicap TWN

历史源文件中的泛称(如 Superior RoomDeluxe RoomJunior Suite (One Bedroom))可能缺少 King/TWN/Pool view 信息,不能仅凭文字强制映射;历史原文应保留。当前已部署模型只初始化 AC2AC1 是否加入生产代码及其扣减规则需单独确认。

3.1 房型分级

Tier 房型
1 RM1、RM2、RM3、RM4、UG1、UG2
2 SU1、SU2、SU6
3 SU3

AC2 不属于自动分级,继续采用当前前端的人工倍数规则。

3.2 自动倍数矩阵

购买房型 使用 RM/UG 使用 SU1/SU2/SU6 使用 SU3
RM/UG 1 2 3
SU1/SU2/SU6 1 1 2
SU3 1 1 1

等价计算公式:

Multiplier = max(1, Used Tier - Purchased Tier + 1)

后端必须根据 Owner Account 的 Purchased Room Type 和 Usage Record 的 Used Room Type 重新计算倍数。普通请求不能直接指定 multiplier。

购买或使用 AC2 时请求可以携带人工 multiplier后端校验范围为当前前端支持的 13。

新业务 Usage Record 保存实际使用的 applied_multiplierrule_versionlegacy 导入记录使用 applied_multiplier=NULLrule_version=legacy-source,以源 Use/Balance 为事实。

4. 推荐数据表

erDiagram
    ROOM_TYPES ||--o{ OWNER_ACCOUNTS : purchased_as
    OWNER_ACCOUNTS ||--o{ ENTITLEMENT_PERIODS : owns
    BOOKINGS ||--o{ USAGE_RECORDS : contains
    OWNER_ACCOUNTS ||--o{ USAGE_RECORDS : creates
    ROOM_TYPES ||--o{ USAGE_RECORDS : used_as
    ENTITLEMENT_PERIODS ||--o{ USAGE_RECORDS : applies_to
    ENTITLEMENT_PERIODS ||--o{ ENTITLEMENT_LEDGER : records
    USAGE_RECORDS ||--o| ENTITLEMENT_LEDGER : deducts

4.0 condon.bookings

一条 Confirmation 对应一条 booking同一 booking 可以关联多条 usage_records。因此 usage_records.confirmation_no 不设唯一约束,而是引用本表主键。

字段 类型 说明
confirmation_no varchar(64) PK 预订 Confirmation当前新业务要求纯数字
created_at timestamptz 首次出现时间

4.1 condon.room_types

字段 类型 说明
code varchar(16) PK RM3、SU1、AC2 等
entitlement_tier smallint nullable 自动规则为 13AC2 为空
requires_manual_multiplier boolean AC2 为 true其他房型为 false
created_at timestamptz 创建时间
updated_at timestamptz 更新时间

约束:

  • 自动房型必须 entitlement_tier IN (1, 2, 3)requires_manual_multiplier = false
  • 人工房型必须 entitlement_tier IS NULLrequires_manual_multiplier = true
  • 初始化代码仅包含 RM1、RM2、RM3、RM4、UG1、UG2、SU1、SU2、SU6、SU3、AC2。

4.2 condon.owner_accounts

字段 类型 说明
id uuid PK 内部不可变主键
account_no integer nullable 前端 No.
transfer_date date nullable Transfer Date
owner_name text 前端显示名称
room_no varchar(32) 业主房号
purchased_room_type_code FK 购买房型
unit_no varchar(32) Unit No.
member_no varchar(32) Member No.,按文本保存
created_at timestamptz 创建时间
updated_at timestamptz 更新时间

约束:

  • account_no 在非空时唯一。
  • room_no 唯一。
  • purchased_room_type_code 引用 condon.room_types(code)
  • 姓名、Unit No.、Member No. 不作为内部主键。
  • 本表不保存余额;余额的单一权威来源是 condon.entitlement_periods.current_balance

4.3 condon.entitlement_periods

每个 Owner Account 每个权益年度一条记录。

字段 类型 说明
id uuid PK 权益期间 ID
owner_account_id FK 所属业主账户
period_year smallint 权益年度
period_start date 当前规则为 1 月 1 日
period_end date 当前规则为 12 月 31 日
annual_grant integer 当前规则为 15 晚
carry_forward integer 上期结转
current_balance integer 唯一权威余额缓存
row_version integer 防止并发重复扣减
created_at timestamptz 创建时间
updated_at timestamptz 更新时间

约束:

  • (owner_account_id, period_year) 唯一。
  • period_startperiod_end 必须分别为该年的 1 月 1 日和 12 月 31 日。
  • annual_grant >= 0
  • carry_forward >= 0
  • current_balance >= 0
  • row_version >= 1

current_balance 用于快速显示;所有余额变化必须与 ledger 在同一事务内完成。

4.4 condon.usage_records

一行对应前端 Usage History 的一行。

字段 类型 说明
id uuid PK 内部记录 ID
owner_account_id FK 所属 Owner Account
entitlement_period_id FK 扣减的权益年度
confirmation_no varchar(64) Confirmation No.
check_in date Check-in
check_out date Check-out
night_count integer Night
used_room_type_code FK nullable 可映射到标准代码的 Used Room Type历史泛称可为空
raw_used_room_type text 源文件原始房型文本
applied_multiplier smallint nullable 新记录为 13legacy 历史统一为空
rule_version varchar(32) 规则版本
use_nights integer Use
balance_before integer 扣减前余额
balance_after integer Balance
room_count integer 源历史 Room当前最新批次均为 1
remark text Remark
idempotency_key uuid 防止重复请求
source_sheet varchar(128) nullable Excel 子表名
source_row integer nullable Excel 行号
source_sequence integer nullable 子表 No.,保留源顺序
import_batch varchar(128) nullable 导入批次标识
created_at timestamptz 录入时间

约束:

  • confirmation_no 引用 condon.bookings,满足当前前端的纯数字规则;同一号允许多条 usage。
  • check_out > check_in
  • night_count = check_out - check_in
  • 新记录 applied_multiplier BETWEEN 1 AND 3,且 use_nights = night_count × applied_multiplier
  • legacy 记录 applied_multiplier IS NULL,源 UseTotalBalance 原样保存。
  • balance_before >= use_nights
  • balance_after = balance_before - use_nights
  • balance_after >= 0
  • idempotency_key 唯一。
  • Check-in 和 Check-out 必须在同一个权益年度内;跨年度请求在 V1 明确拒绝。

4.5 condon.entitlement_ledger

保存每一次年度新增、结转和使用扣减。

字段 类型 说明
id uuid PK 流水 ID
entitlement_period_id FK 所属权益期间
usage_record_id FK nullable 使用扣减对应的记录
entry_type varchar(20) annual_grant、carry_forward、usage
delta_nights integer 增加为正,扣减为负
balance_before integer 变动前余额
balance_after integer 变动后余额
occurred_at timestamptz 发生时间

约束:

  • balance_after = balance_before + delta_nights
  • balance_after >= 0
  • 一条 Usage Record 只能产生一笔 usage debit。
  • annual_grant 和非零 carry_forward 为正数usage 为负数。
  • usage 必须引用 Usage Recordannual_grant 和 carry_forward 不引用 Usage Record。

4.6 condon.schema_migrations

仅记录 condon 自身的迁移版本,不借用 public

字段 类型 说明
version varchar(64) PK 迁移版本
checksum varchar(64) SQL 内容校验值
applied_at timestamptz 应用时间

导入批次和拒绝行保存在仓库内的本地审计产物中;数据库只保存已接受记录的 import_batch 与来源定位字段。

5. 后端保存事务

前端提交:

{
  "ownerAccountId": "uuid",
  "confirmationNo": "26090001",
  "checkIn": "2026-09-01",
  "checkOut": "2026-09-04",
  "usedRoomType": "SU1",
  "manualMultiplier": null,
  "remark": "",
  "idempotencyKey": "uuid"
}

后端处理:

  1. 校验 idempotency key 是否已处理。
  2. 校验 Confirmation No. 为纯数字且未使用。
  3. 读取 Owner Account、Purchased Room Type 和当前权益期间。
  4. 计算 Night。
  5. 根据购买/使用房型 tier 计算 multiplierAC2 校验人工 multiplier。
  6. 计算 Use = Night × Multiplier
  7. 开启事务并锁定对应 entitlement period同时检查 row version。
  8. 校验余额足够。
  9. 写入 Usage Record 和 ledger debitConfirmation 通过 booking 1:N 关系复用。
  10. 更新 current balance 和 row version。
  11. 提交事务并返回完整 Usage History 数据。

任一步失败都整体回滚,避免出现“余额已扣但记录未保存”或相反的情况。

前端传来的 Night、Use、Balance 和自动 multiplier 只用于即时预览,不能作为后端权威值。

数据库函数:

  • condon.calculate_multiplier(...):返回自动倍率或校验 AC2 手动倍率。
  • condon.open_entitlement_period(...):按 15 晚年度新增和上一年度余额结转建立新期间,写入年度新增 ledger并在结转大于 0 时写入结转 ledger。
  • condon.create_usage_record(...)完成计算、行锁、余额验证、usage/ledger 写入和余额更新。

三个函数均使用显式 schema 名、固定安全 search_path 和调用者权限,不修改角色或全局权限。

6. API 读取模型

Owner Accounts

返回:

  • id
  • accountNo
  • transferDate
  • name
  • roomNo
  • purchasedRoomType
  • unitNo
  • memberNo
  • remainingStayPrivileges

Usage History

返回:

  • id
  • confirmationNo
  • ownerAccountId
  • ownerName
  • ownerRoomNo
  • checkIn
  • checkOut
  • night
  • use
  • balance
  • usedRoomType
  • remark
  • createdAt

这组返回值可以直接支持全局 Usage History、账户详情、Confirmation 搜索和当前排序规则。

7. Dashboard 计算

  • Owner roomsOwner Account 数量。
  • Remaining privileges当前 entitlement period 的 current_balance 总和。
  • Used this year当前期间 Usage Record 的 use_nights 总和。
  • Room Type按 Owner Account 的 purchased room type 计数。
  • Used Room Type按 Usage Record 的 night_count 汇总实际房晚;一条记录代表一间房,所以不乘 Room 数量。
  • 月度 Use按 Check-in 月份汇总 use_nights

所有 Dashboard 指标由同一权益期间和已保存的 Usage Record 生成,不能继续使用静态图表值。

8. 推荐索引

  • owner_accounts(account_no) 唯一(允许多个 NULL
  • owner_accounts(room_no) 唯一。
  • owner_accounts(member_no)
  • owner_accounts(purchased_room_type_code)
  • entitlement_periods(owner_account_id, period_year) 唯一。
  • bookings(confirmation_no) 主键usage 通过 Confirmation 外键关联。
  • usage_records(import_batch, source_sheet, source_row) 部分唯一索引。
  • usage_records(owner_account_id, created_at, id)
  • usage_records(entitlement_period_id)
  • usage_records(used_room_type_code, check_in)
  • entitlement_ledger(entitlement_period_id, occurred_at, id)
  • entitlement_ledger(usage_record_id) 唯一(允许 annual_grant/carry_forward 为 NULL

9. V1 明确不预建的规则

  1. Usage Record 取消、冲正和人工余额调整。
  2. 房屋转让时的余额归属与历史账户处理。
  3. 真实用户登录、角色和操作人追踪。
  4. 跨权益年度的一次使用V1 返回明确错误。
  5. 新历史批次的业务纠正与房型映射;已确认的 (2) 批次已导入,后续批次继续走预演流程。

这些能力以后通过独立迁移扩展,不在基础表中预先加入未经确认的字段。

10. 推荐实施顺序

  1. 只读盘点 booking_test,确认目标 schema 和业务表状态。
  2. 本地编写版本化迁移、精确回滚和 SQL allowlist 安全检查。
  3. 应用 001_create_condon_schema002_legacy_import_and_bookings
  4. 生成最新工作簿本地导入预演批次,排除无 Confirmation 行并逐条校验。
  5. 在空业务表中单事务导入 owner、权益期间、booking、legacy usage 和 ledger。
  6. 通过只读逐条比对及 API smoke 后再开放前端读取。
  7. 新增 usage 继续只调用数据库计算倍率的 v2 写入路径。