docs: 创建数据库设计文档
This commit is contained in:
@@ -0,0 +1,505 @@
|
||||
# 健身房管理系统数据库设计文档
|
||||
|
||||
> 文档编号:GYM-DB-DESIGN-001
|
||||
> 版本:v1.0
|
||||
> 创建日期:2026-03-08
|
||||
> 最后更新日期:2026-03-08
|
||||
> 作者:张翔
|
||||
> 状态:正式发布
|
||||
|
||||
## 文档修订历史
|
||||
|
||||
| 版本 | 日期 | 作者 | 修订内容 |
|
||||
| ---- | ---------- | ---- | -------- |
|
||||
| v1.0 | 2026-03-08 | 张翔 | 创建数据库设计文档 |
|
||||
|
||||
## 参考文档
|
||||
|
||||
- 《健身房管理系统基础版技术实现详细设计文档》 GYM-T-ILD-BASIC-001
|
||||
- 《健身房管理系统付费订阅版技术实现详细设计文档》 GYM-T-ILD-SUBSCRIPTION-001
|
||||
- PostgreSQL 官方文档
|
||||
- R2DBC 规范文档
|
||||
|
||||
---
|
||||
|
||||
## 一、数据库架构设计
|
||||
|
||||
### 1.1 多租户架构设计
|
||||
|
||||
本系统采用**共享数据库、共享 Schema、租户 ID 隔离**的多租户架构:
|
||||
|
||||
```
|
||||
租户层级:
|
||||
租户 (Tenant) → 门店 (Store) → 业务数据
|
||||
```
|
||||
|
||||
**租户隔离策略**:
|
||||
- 所有业务表包含 `tenant_id` 字段
|
||||
- 查询时强制添加 `tenant_id` 过滤条件
|
||||
- 通过数据库视图实现租户级数据隔离
|
||||
|
||||
**门店隔离策略**:
|
||||
- 门店级业务表包含 `store_id` 字段
|
||||
- 支持跨店约课的多门店数据关联
|
||||
- 通过配置继承实现门店级个性化
|
||||
|
||||
### 1.2 分库分表策略
|
||||
|
||||
**分库策略**(未来扩展):
|
||||
- 按租户分库:大型租户(月交易额>100 万)独立数据库
|
||||
- 按业务分库:交易库、日志库、分析库分离
|
||||
|
||||
**分表策略**:
|
||||
- 按时间分表:签到记录、预约记录按月分表
|
||||
- 按租户分表:大型租户数据独立表空间
|
||||
|
||||
### 1.3 数据库选型
|
||||
|
||||
**核心数据库**:PostgreSQL 15+
|
||||
- 完全支持 R2DBC 响应式驱动
|
||||
- JSONB 支持灵活的配置管理
|
||||
- 全文搜索支持
|
||||
- ACID 事务保证
|
||||
|
||||
**缓存数据库**:Redis 7+
|
||||
- 响应式缓存支持
|
||||
- 分布式锁
|
||||
- 过期策略
|
||||
|
||||
**搜索引擎**:Elasticsearch 8+(可选)
|
||||
- 全文搜索
|
||||
- 复杂查询
|
||||
- 数据分析
|
||||
|
||||
---
|
||||
|
||||
## 二、核心表结构设计
|
||||
|
||||
### 2.1 会员域
|
||||
|
||||
#### 2.1.1 会员基础信息表(member)
|
||||
|
||||
```sql
|
||||
CREATE TABLE member (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL,
|
||||
store_id BIGINT NOT NULL,
|
||||
phone VARCHAR(11) NOT NULL,
|
||||
name VARCHAR(50) NOT NULL,
|
||||
gender SMALLINT NOT NULL,
|
||||
birthday DATE,
|
||||
height DECIMAL(5,2),
|
||||
weight DECIMAL(5,2),
|
||||
fitness_goal VARCHAR(100),
|
||||
avatar_url VARCHAR(500),
|
||||
wechat_openid VARCHAR(100),
|
||||
status SMALLINT DEFAULT 1,
|
||||
level SMALLINT DEFAULT 1,
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
updated_at TIMESTAMP DEFAULT NOW(),
|
||||
deleted_at TIMESTAMP DEFAULT NULL,
|
||||
|
||||
CONSTRAINT uk_member_phone UNIQUE (tenant_id, phone),
|
||||
CONSTRAINT fk_member_tenant FOREIGN KEY (tenant_id) REFERENCES tenant(id),
|
||||
CONSTRAINT fk_member_store FOREIGN KEY (store_id) REFERENCES store(id)
|
||||
);
|
||||
|
||||
-- 索引设计
|
||||
CREATE INDEX idx_member_tenant_store ON member(tenant_id, store_id);
|
||||
CREATE INDEX idx_member_phone ON member(phone);
|
||||
CREATE INDEX idx_member_status ON member(status);
|
||||
CREATE INDEX idx_member_level ON member(level);
|
||||
```
|
||||
|
||||
**字段说明**:
|
||||
- `tenant_id`: 租户 ID,多租户隔离
|
||||
- `store_id`: 门店 ID,单店运营
|
||||
- `phone`: 手机号,唯一索引
|
||||
- `status`: 1-正常,2-沉默,3-流失预警,4-流失
|
||||
- `level`: 会员等级,1-普通,2-VIP,3-SVIP
|
||||
|
||||
#### 2.1.2 会员卡表(member_card)
|
||||
|
||||
```sql
|
||||
CREATE TABLE member_card (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL,
|
||||
member_id BIGINT NOT NULL,
|
||||
card_type SMALLINT NOT NULL,
|
||||
card_name VARCHAR(100) NOT NULL,
|
||||
price DECIMAL(10,2) NOT NULL,
|
||||
duration_days INT,
|
||||
duration_times INT,
|
||||
balance DECIMAL(10,2) DEFAULT 0.00,
|
||||
start_date DATE NOT NULL,
|
||||
end_date DATE NOT NULL,
|
||||
status SMALLINT DEFAULT 1,
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
updated_at TIMESTAMP DEFAULT NOW(),
|
||||
deleted_at TIMESTAMP DEFAULT NULL,
|
||||
|
||||
CONSTRAINT fk_card_tenant FOREIGN KEY (tenant_id) REFERENCES tenant(id),
|
||||
CONSTRAINT fk_card_member FOREIGN KEY (member_id) REFERENCES member(id)
|
||||
);
|
||||
|
||||
-- 索引设计
|
||||
CREATE INDEX idx_card_member ON member_card(member_id);
|
||||
CREATE INDEX idx_card_status ON member_card(status);
|
||||
CREATE INDEX idx_card_end_date ON member_card(end_date);
|
||||
```
|
||||
|
||||
**字段说明**:
|
||||
- `card_type`: 1-时长卡,2-次卡,3-储值卡,4-组合卡
|
||||
- `duration_days`: 时长卡天数
|
||||
- `duration_times`: 次卡次数
|
||||
- `balance`: 储值卡余额
|
||||
- `status`: 1-有效,2-已过期,3-已退款
|
||||
|
||||
#### 2.1.3 会员权益表(member_benefit)
|
||||
|
||||
```sql
|
||||
CREATE TABLE member_benefit (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL,
|
||||
member_id BIGINT NOT NULL,
|
||||
card_id BIGINT NOT NULL,
|
||||
benefit_type SMALLINT NOT NULL,
|
||||
benefit_value DECIMAL(10,2) NOT NULL,
|
||||
used_value DECIMAL(10,2) DEFAULT 0.00,
|
||||
remaining_value DECIMAL(10,2) NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
updated_at TIMESTAMP DEFAULT NOW(),
|
||||
|
||||
CONSTRAINT fk_benefit_tenant FOREIGN KEY (tenant_id) REFERENCES tenant(id),
|
||||
CONSTRAINT fk_benefit_member FOREIGN KEY (member_id) REFERENCES member(id),
|
||||
CONSTRAINT fk_benefit_card FOREIGN KEY (card_id) REFERENCES member_card(id)
|
||||
);
|
||||
|
||||
-- 索引设计
|
||||
CREATE INDEX idx_benefit_member ON member_benefit(member_id);
|
||||
CREATE INDEX idx_benefit_type ON member_benefit(benefit_type);
|
||||
```
|
||||
|
||||
**字段说明**:
|
||||
- `benefit_type`: 1-时长权益,2-次数权益,3-储值权益,4-等级权益
|
||||
- `benefit_value`: 权益总值
|
||||
- `used_value`: 已使用值
|
||||
- `remaining_value`: 剩余值
|
||||
|
||||
#### 2.1.4 会员生命周期表(member_lifecycle)
|
||||
|
||||
```sql
|
||||
CREATE TABLE member_lifecycle (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL,
|
||||
member_id BIGINT NOT NULL,
|
||||
stage SMALLINT NOT NULL,
|
||||
enter_time TIMESTAMP NOT NULL,
|
||||
exit_time TIMESTAMP,
|
||||
exit_reason VARCHAR(200),
|
||||
intervention VARCHAR(500),
|
||||
intervention_result SMALLINT,
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
|
||||
CONSTRAINT fk_lifecycle_tenant FOREIGN KEY (tenant_id) REFERENCES tenant(id),
|
||||
CONSTRAINT fk_lifecycle_member FOREIGN KEY (member_id) REFERENCES member(id)
|
||||
);
|
||||
|
||||
-- 索引设计
|
||||
CREATE INDEX idx_lifecycle_member ON member_lifecycle(member_id);
|
||||
CREATE INDEX idx_lifecycle_stage ON member_lifecycle(stage);
|
||||
```
|
||||
|
||||
**字段说明**:
|
||||
- `stage`: 1-新会员,2-活跃期,3-沉默期,4-流失预警,5-流失
|
||||
- `intervention`: 干预措施
|
||||
- `intervention_result`: 1-成功,2-失败
|
||||
|
||||
---
|
||||
|
||||
### 2.2 预约域
|
||||
|
||||
#### 2.2.1 可预约资源表(booking_resource)
|
||||
|
||||
```sql
|
||||
CREATE TABLE booking_resource (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL,
|
||||
store_id BIGINT NOT NULL,
|
||||
resource_type SMALLINT NOT NULL,
|
||||
resource_name VARCHAR(100) NOT NULL,
|
||||
description VARCHAR(500),
|
||||
capacity INT NOT NULL,
|
||||
duration_minutes INT NOT NULL,
|
||||
status SMALLINT DEFAULT 1,
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
updated_at TIMESTAMP DEFAULT NOW(),
|
||||
deleted_at TIMESTAMP DEFAULT NULL,
|
||||
|
||||
CONSTRAINT fk_resource_tenant FOREIGN KEY (tenant_id) REFERENCES tenant(id),
|
||||
CONSTRAINT fk_resource_store FOREIGN KEY (store_id) REFERENCES store(id)
|
||||
);
|
||||
|
||||
-- 索引设计
|
||||
CREATE INDEX idx_resource_tenant_store ON booking_resource(tenant_id, store_id);
|
||||
CREATE INDEX idx_resource_type ON booking_resource(resource_type);
|
||||
```
|
||||
|
||||
**字段说明**:
|
||||
- `resource_type`: 1-团课,2-私教,3-器械,4-场地
|
||||
- `capacity`: 容量(人数)
|
||||
- `status`: 1-启用,2-停用
|
||||
|
||||
#### 2.2.2 时段表(booking_slot)
|
||||
|
||||
```sql
|
||||
CREATE TABLE booking_slot (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL,
|
||||
resource_id BIGINT NOT NULL,
|
||||
coach_id BIGINT,
|
||||
start_time TIMESTAMP NOT NULL,
|
||||
end_time TIMESTAMP NOT NULL,
|
||||
booked_count INT DEFAULT 0,
|
||||
status SMALLINT DEFAULT 1,
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
updated_at TIMESTAMP DEFAULT NOW(),
|
||||
deleted_at TIMESTAMP DEFAULT NULL,
|
||||
|
||||
CONSTRAINT fk_slot_tenant FOREIGN KEY (tenant_id) REFERENCES tenant(id),
|
||||
CONSTRAINT fk_slot_resource FOREIGN KEY (resource_id) REFERENCES booking_resource(id)
|
||||
);
|
||||
|
||||
-- 索引设计
|
||||
CREATE INDEX idx_slot_resource ON booking_slot(resource_id);
|
||||
CREATE INDEX idx_slot_start_time ON booking_slot(start_time);
|
||||
CREATE INDEX idx_slot_status ON booking_slot(status);
|
||||
```
|
||||
|
||||
**字段说明**:
|
||||
- `coach_id`: 教练 ID(私教课必填)
|
||||
- `booked_count`: 已预约人数
|
||||
- `status`: 1-可预约,2-已满,3-已取消
|
||||
|
||||
#### 2.2.3 预约记录表(booking_record)
|
||||
|
||||
```sql
|
||||
CREATE TABLE booking_record (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL,
|
||||
member_id BIGINT NOT NULL,
|
||||
slot_id BIGINT NOT NULL,
|
||||
booking_time TIMESTAMP NOT NULL,
|
||||
status SMALLINT DEFAULT 1,
|
||||
cancel_time TIMESTAMP,
|
||||
cancel_reason VARCHAR(200),
|
||||
checkin_time TIMESTAMP,
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
updated_at TIMESTAMP DEFAULT NOW(),
|
||||
|
||||
CONSTRAINT fk_booking_tenant FOREIGN KEY (tenant_id) REFERENCES tenant(id),
|
||||
CONSTRAINT fk_booking_member FOREIGN KEY (member_id) REFERENCES member(id),
|
||||
CONSTRAINT fk_booking_slot FOREIGN KEY (slot_id) REFERENCES booking_slot(id)
|
||||
);
|
||||
|
||||
-- 索引设计
|
||||
CREATE INDEX idx_booking_member ON booking_record(member_id);
|
||||
CREATE INDEX idx_booking_slot ON booking_record(slot_id);
|
||||
CREATE INDEX idx_booking_status ON booking_record(status);
|
||||
```
|
||||
|
||||
**字段说明**:
|
||||
- `booking_time`: 预约时间
|
||||
- `status`: 1-已预约,2-已签到,3-已取消,4-已爽约
|
||||
- `checkin_time`: 签到时间
|
||||
|
||||
---
|
||||
|
||||
### 2.3 订阅域
|
||||
|
||||
#### 2.3.1 租户模块配置表(tenant_module_config)
|
||||
|
||||
```sql
|
||||
CREATE TABLE tenant_module_config (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL,
|
||||
module_code VARCHAR(32) NOT NULL,
|
||||
enabled BOOLEAN NOT NULL,
|
||||
config_data JSONB,
|
||||
version INT DEFAULT 0,
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
updated_at TIMESTAMP DEFAULT NOW(),
|
||||
created_by BIGINT,
|
||||
updated_by BIGINT,
|
||||
deleted_at TIMESTAMP DEFAULT NULL,
|
||||
|
||||
CONSTRAINT uk_tenant_module UNIQUE (tenant_id, module_code),
|
||||
CONSTRAINT fk_tenant_module_config FOREIGN KEY (tenant_id) REFERENCES tenant(id)
|
||||
);
|
||||
|
||||
-- 索引设计
|
||||
CREATE INDEX idx_tenant_module ON tenant_module_config(tenant_id, module_code);
|
||||
```
|
||||
|
||||
#### 2.3.2 门店模块配置表(store_module_config)
|
||||
|
||||
```sql
|
||||
CREATE TABLE store_module_config (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL,
|
||||
store_id BIGINT NOT NULL,
|
||||
module_code VARCHAR(32) NOT NULL,
|
||||
inherit_mode SMALLINT NOT NULL,
|
||||
enabled BOOLEAN NOT NULL,
|
||||
config_data JSONB,
|
||||
version INT DEFAULT 0,
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
updated_at TIMESTAMP DEFAULT NOW(),
|
||||
created_by BIGINT,
|
||||
updated_by BIGINT,
|
||||
deleted_at TIMESTAMP DEFAULT NULL,
|
||||
|
||||
CONSTRAINT uk_store_module UNIQUE (store_id, module_code),
|
||||
CONSTRAINT fk_store_module_config FOREIGN KEY (store_id) REFERENCES store(id)
|
||||
);
|
||||
|
||||
-- 索引设计
|
||||
CREATE INDEX idx_store_module ON store_module_config(store_id, module_code);
|
||||
```
|
||||
|
||||
#### 2.3.3 订阅记录表(subscription_record)
|
||||
|
||||
```sql
|
||||
CREATE TABLE subscription_record (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL,
|
||||
subscription_no VARCHAR(32) NOT NULL,
|
||||
module_code VARCHAR(32) NOT NULL,
|
||||
billing_cycle SMALLINT NOT NULL,
|
||||
amount DECIMAL(10,2) NOT NULL,
|
||||
discount_amount DECIMAL(10,2) DEFAULT 0.00,
|
||||
actual_amount DECIMAL(10,2) NOT NULL,
|
||||
start_date DATE NOT NULL,
|
||||
end_date DATE NOT NULL,
|
||||
status SMALLINT DEFAULT 1,
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
updated_at TIMESTAMP DEFAULT NOW(),
|
||||
created_by BIGINT,
|
||||
updated_by BIGINT,
|
||||
deleted_at TIMESTAMP DEFAULT NULL,
|
||||
|
||||
CONSTRAINT uk_subscription_no UNIQUE (subscription_no),
|
||||
CONSTRAINT fk_subscription_tenant FOREIGN KEY (tenant_id) REFERENCES tenant(id)
|
||||
);
|
||||
|
||||
-- 索引设计
|
||||
CREATE INDEX idx_subscription_tenant ON subscription_record(tenant_id);
|
||||
CREATE INDEX idx_subscription_status ON subscription_record(status);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、索引设计优化
|
||||
|
||||
### 3.1 核心索引清单
|
||||
|
||||
| 表名 | 索引字段 | 索引类型 | 说明 |
|
||||
|------|---------|---------|------|
|
||||
| member | (tenant_id, phone) | 唯一索引 | 租户级手机号唯一 |
|
||||
| member | (tenant_id, store_id) | 复合索引 | 租户 + 门店查询 |
|
||||
| member | (status) | 单列索引 | 会员状态筛选 |
|
||||
| member_card | (member_id) | 复合索引 | 会员卡片查询 |
|
||||
| member_card | (end_date) | 单列索引 | 到期提醒查询 |
|
||||
| booking_record | (member_id, status) | 复合索引 | 会员预约查询 |
|
||||
| booking_slot | (resource_id, start_time) | 复合索引 | 资源时段查询 |
|
||||
|
||||
### 3.2 索引优化建议
|
||||
|
||||
1. **避免过度索引**:单表索引数不超过 5 个
|
||||
2. **优先复合索引**:将高频查询字段组合
|
||||
3. **定期分析索引使用**:通过 pg_stat_user_indexes 监控
|
||||
4. **及时删除无用索引**:减少写入开销
|
||||
|
||||
---
|
||||
|
||||
## 四、数据迁移策略
|
||||
|
||||
### 4.1 版本化管理
|
||||
|
||||
使用 **Flyway** 进行数据库版本管理:
|
||||
|
||||
```
|
||||
db/
|
||||
├── migration/
|
||||
│ ├── V1__initial_schema.sql
|
||||
│ ├── V2__add_member_lifecycle.sql
|
||||
│ ├── V3__add_booking_resource.sql
|
||||
│ └── ...
|
||||
```
|
||||
|
||||
### 4.2 数据迁移流程
|
||||
|
||||
1. **开发环境**:创建迁移脚本 → 测试迁移 → 提交代码
|
||||
2. **测试环境**:自动执行迁移 → 验证数据 → 回归测试
|
||||
3. **生产环境**:备份数据 → 执行迁移 → 验证数据 → 监控告警
|
||||
|
||||
### 4.3 回滚策略
|
||||
|
||||
- 每个迁移脚本配备回滚脚本
|
||||
- 回滚前必须备份当前数据
|
||||
- 回滚后验证数据一致性
|
||||
|
||||
---
|
||||
|
||||
## 五、性能优化
|
||||
|
||||
### 5.1 查询优化
|
||||
|
||||
1. **避免 N+1 查询**:使用 JOIN 或批量查询
|
||||
2. **使用覆盖索引**:减少回表查询
|
||||
3. **分页优化**:使用游标分页替代 OFFSET
|
||||
|
||||
### 5.2 连接池配置
|
||||
|
||||
```yaml
|
||||
spring:
|
||||
r2dbc:
|
||||
pool:
|
||||
max-size: 20 # 基础版
|
||||
max-size: 100 # 付费订阅版
|
||||
initial-size: 10
|
||||
max-idle-time: 30m
|
||||
max-life-time: 60m
|
||||
```
|
||||
|
||||
### 5.3 监控指标
|
||||
|
||||
- 连接池使用率
|
||||
- 慢查询(>1s)
|
||||
- 锁等待时间
|
||||
- 表空间使用率
|
||||
|
||||
---
|
||||
|
||||
## 六、安全设计
|
||||
|
||||
### 6.1 数据加密
|
||||
|
||||
- 手机号:AES-256 加密
|
||||
- 身份证:AES-256 加密
|
||||
- 银行卡:AES-256 加密
|
||||
|
||||
### 6.2 数据脱敏
|
||||
|
||||
- 日志中的手机号:138****1234
|
||||
- 接口响应中的身份证:110101********1234
|
||||
|
||||
### 6.3 审计日志
|
||||
|
||||
- 关键操作记录(插入、更新、删除)
|
||||
- 操作人、操作时间、IP 地址
|
||||
- 保留 180 天
|
||||
|
||||
---
|
||||
|
||||
**文档结束**
|
||||
Reference in New Issue
Block a user