15 KiB
15 KiB
健身房管理系统数据库设计文档
文档编号: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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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 索引优化建议
- 避免过度索引:单表索引数不超过 5 个
- 优先复合索引:将高频查询字段组合
- 定期分析索引使用:通过 pg_stat_user_indexes 监控
- 及时删除无用索引:减少写入开销
四、数据迁移策略
4.1 版本化管理
使用 Flyway 进行数据库版本管理:
db/
├── migration/
│ ├── V1__initial_schema.sql
│ ├── V2__add_member_lifecycle.sql
│ ├── V3__add_booking_resource.sql
│ └── ...
4.2 数据迁移流程
- 开发环境:创建迁移脚本 → 测试迁移 → 提交代码
- 测试环境:自动执行迁移 → 验证数据 → 回归测试
- 生产环境:备份数据 → 执行迁移 → 验证数据 → 监控告警
4.3 回滚策略
- 每个迁移脚本配备回滚脚本
- 回滚前必须备份当前数据
- 回滚后验证数据一致性
五、性能优化
5.1 查询优化
- 避免 N+1 查询:使用 JOIN 或批量查询
- 使用覆盖索引:减少回表查询
- 分页优化:使用游标分页替代 OFFSET
5.2 连接池配置
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 天
文档结束