# 健身房管理系统数据库设计文档 > 文档编号: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 天 --- **文档结束**