健身房会员锻炼数据可视化分析系统 -- 技术文档
目录
- [1. 项目概述](#1. 项目概述)
- [2. 技术栈与依赖](#2. 技术栈与依赖)
- [3. 系统架构](#3. 系统架构)
- [4. 配置系统](#4. 配置系统)
- [5. 数据库设计](#5. 数据库设计)
- [6. 功能模块详解](#6. 功能模块详解)
- [7. 表单设计](#7. 表单设计)
- [8. 前端技术](#8. 前端技术)
- [9. 权限系统](#9. 权限系统)
- [10. API接口](#10. API接口)
- [11. 数据导入与导出](#11. 数据导入与导出)
- [12. 错误处理](#12. 错误处理)
- [13. 部署说明](#13. 部署说明)
- [附录A: 数据库ER关系图(文字描述)](#附录A: 数据库ER关系图(文字描述))
- [附录B: 模板文件清单](#附录B: 模板文件清单)
1. 项目概述
1.1 项目简介
本系统是一套基于 Flask 框架开发的健身房会员锻炼数据可视化分析系统。系统面向健身房管理者和教练团队,提供会员信息管理、锻炼记录跟踪、课程排期管理、数据分析与可视化等核心功能。系统采用经典的 MVC 架构模式,通过 Bootstrap 5 + ECharts 构建现代化的数据分析前端界面,后端使用 SQLAlchemy ORM 管理 MySQL 数据库。
1.2 核心业务场景
- 会员信息管理 -- 录入、编辑、查询、删除会员基本信息(姓名、性别、年龄、身高、体重、BMI、体脂率等)
- 锻炼记录管理 -- 记录会员每次锻炼的类型、时长、消耗卡路里、心率等数据
- 课程管理 -- 管理健身课程(瑜伽、力量训练、有氧等),安排课程时间表,处理会员报名
- 锻炼计划 -- 为会员制定个性化锻炼计划,包含多个锻炼项目
- 身体测量记录 -- 跟踪会员体围数据(胸围、腰围、臀围、上臂围、大腿围)变化
- 数据可视化分析 -- 提供仪表盘、趋势分析、分布分析、相关性分析等深度数据洞察
- 数据导入导出 -- 支持 CSV 格式的批量数据导入和导出
- 用户与权限管理 -- 基于角色的访问控制(RBAC),支持管理员和普通用户两种角色
1.3 项目目录结构
code/
├── app/ # 应用主包
│ ├── __init__.py # 应用工厂函数 create_app()
│ ├── extensions.py # Flask 扩展初始化
│ ├── decorators.py # 权限装饰器
│ ├── models/ # 数据模型层
│ │ ├── __init__.py # 模型汇总导出
│ │ ├── user.py # 用户、角色、权限模型
│ │ ├── member.py # 会员、锻炼会话模型
│ │ ├── gym_member.py # 健身房会员、身体测量模型
│ │ ├── activity.py # 活动记录模型
│ │ ├── course.py # 课程、课程安排、报名模型
│ │ └── workout.py # 锻炼计划、锻炼记录、锻炼详情模型
│ ├── main/ # 主业务蓝图
│ │ ├── __init__.py
│ │ ├── routes.py # 主业务路由(约2050行)
│ │ └── forms.py # 主业务表单
│ ├── auth/ # 认证蓝图
│ │ ├── __init__.py
│ │ ├── routes.py # 认证路由
│ │ ├── forms.py # 认证表单
│ │ └── email.py # 邮件发送功能
│ ├── api/ # API蓝图
│ │ ├── __init__.py
│ │ ├── auth.py # API认证(JWT)
│ │ ├── members.py # 会员API
│ │ ├── analytics.py # 分析API
│ │ └── errors.py # API错误处理
│ ├── errors/ # 错误处理蓝图
│ │ ├── __init__.py
│ │ └── handlers.py # 全局错误处理器
│ └── templates/ # Jinja2 模板
│ ├── base.html # 基础布局模板
│ ├── main/ # 主业务模板(30+页面)
│ ├── auth/ # 认证模板
│ ├── email/ # 邮件模板
│ └── errors/ # 错误页面
├── config.py # 多环境配置
├── run.py # 应用启动入口 + CLI命令
├── import_data.py # 数据导入脚本
├── requirements.txt # Python依赖清单
├── migrations/ # Alembic数据库迁移
├── data/ # 原始数据文件
├── uploads/ # 用户上传文件
├── logs/ # 应用日志
└── tests/ # 单元测试


































2. 技术栈与依赖
2.1 核心框架
| 包名 | 版本 | 用途 |
|---|---|---|
| Flask | 3.0.0 | Web应用框架 |
| Flask-SQLAlchemy | 3.1.1 | SQLAlchemy ORM 集成 |
| Flask-Migrate | 4.0.5 | Alembic 数据库迁移管理 |
| Flask-Login | 0.6.3 | 用户会话管理、登录状态维护 |
| Flask-WTF | 1.2.1 | WTForms 表单集成、CSRF保护 |
| Flask-Mail | 0.9.1 | 邮件发送(密码重置、邮箱确认) |
| Flask-RESTful | 0.3.10 | RESTful API 构建 |
| Flask-JWT-Extended | 4.5.3 | JWT 令牌认证 |
| Flask-CORS | 4.0.0 | 跨域资源共享 |
| Flask-Moment | 1.0.5 | 时间日期本地化显示 |
| Flask-Bootstrap5 | -- | Bootstrap5 集成(通过 Bootstrap5 类) |
| Flask-Caching | 2.1.0 | 缓存支持(simple/redis) |
2.2 数据库
| 包名 | 版本 | 用途 |
|---|---|---|
| SQLAlchemy | 2.0.21 | Python SQL工具包和ORM |
| PyMySQL | 1.1.0 | MySQL 纯Python驱动 |
| mysqlclient | 2.2.1 | MySQL C语言驱动(备选) |
2.3 表单与验证
| 包名 | 版本 | 用途 |
|---|---|---|
| WTForms | 3.0.1 | 表单渲染与验证 |
| email-validator | 2.1.0.post1 | 邮箱格式验证 |
2.4 安全与加密
| 包名 | 版本 | 用途 |
|---|---|---|
| Werkzeug | 3.0.1 | 密码哈希(generate_password_hash/check_password_hash) |
| PyJWT | 2.8.0 | JWT 令牌生成与验证 |
| bcrypt | 4.0.1 | 备用密码加密 |
| cryptography | 41.0.4 | 加密工具集 |
2.5 数据分析
| 包名 | 版本 | 用途 |
|---|---|---|
| pandas | 2.1.4 | 数据处理、CSV导入导出 |
| numpy | 1.26.3 | 数值计算、相关系数矩阵 |
| scikit-learn | 1.3.2 | 机器学习(预留) |
| scipy | 1.11.3 | 科学计算 |
2.6 数据可视化
| 包名 | 版本 | 用途 |
|---|---|---|
| matplotlib | 3.7.2 | 静态图表生成 |
| seaborn | 0.12.2 | 统计可视化 |
| plotly | 5.16.1 | 交互式图表 |
2.7 文件处理
| 包名 | 版本 | 用途 |
|---|---|---|
| openpyxl | 3.1.2 | Excel .xlsx 读写 |
| xlrd | 2.0.1 | Excel .xls 读取 |
| chardet | 5.2.0 | CSV编码自动检测 |
| Pillow | 10.2.0 | 头像图片处理(缩放、格式转换) |
| python-magic | 0.4.27 | 文件MIME类型检测 |
2.8 运维与开发
| 包名 | 版本 | 用途 |
|---|---|---|
| gunicorn | 21.2.0 | 生产环境 WSGI 服务器 |
| python-dotenv | 1.0.0 | .env 环境变量加载 |
| requests | 2.31.0 | HTTP 请求 |
| python-dateutil | 2.8.2 | 日期时间增强 |
| Faker | 19.6.2 | 虚拟测试数据生成 |
| pytest | 7.4.4 | 测试框架 |
| pytest-cov | 4.1.0 | 测试覆盖率 |
| pytest-flask | 1.2.0 | Flask测试工具 |
| flake8 | 7.0.0 | 代码规范检查 |
| black | 23.12.1 | 代码格式化 |
3. 系统架构
3.1 应用工厂模式
系统采用 Flask 推荐的应用工厂模式(Application Factory Pattern),核心函数位于 app/__init__.py:
python
def create_app(config_name='default'):
app = Flask(__name__)
app.config.from_object(config[config_name])
config[config_name].init_app(app)
# 初始化9个扩展
# 注册4个蓝图
# 注册模板过滤器、全局变量
# 健康检查端点
# 数据库表创建
return app
工厂函数执行流程:
- 创建 Flask 应用实例
- 根据
config_name加载对应配置类(development/testing/production) - 调用配置类的
init_app()方法进行环境特定初始化 - 依次初始化 9 个 Flask 扩展:db, migrate, login_manager, mail, moment, cors, cache, jwt, bootstrap
- 注册 4 个蓝图(Blueprint):main, auth, api, errors
- 注册 Shell 上下文处理器(在
flask shell中自动导入常用模型) - 注册模板全局变量(app_name, app_version, Permission)
- 注册 4 个自定义模板过滤器(datetime, number, percentage, exercise_type_cn)
- 创建上传目录
- 创建健康检查路由
/health - 在应用上下文中执行
db.create_all()创建数据库表
3.2 蓝图划分
| 蓝图名称 | URL前缀 | 模块文件 | 职责 |
|---|---|---|---|
main |
/(无前缀) |
app/main/ |
核心业务:首页、仪表盘、会员管理、锻炼记录、课程管理、数据导入导出、备份、分析 |
auth |
/auth |
app/auth/ |
用户认证:登录、注册、个人资料、密码管理、用户管理 |
api |
/api |
app/api/ |
RESTful API:JSON数据接口、JWT认证 |
errors |
无(错误处理器) | app/errors/ |
全局HTTP错误处理:400/403/404/500 |
3.3 扩展初始化
扩展在 app/extensions.py 中统一声明,在工厂函数中统一初始化:
| 扩展 | 变量名 | 配置说明 |
|---|---|---|
| SQLAlchemy | db |
ORM引擎,支持连接池(pool_size=10) |
| Migrate | migrate |
Alembic数据库迁移 |
| LoginManager | login_manager |
登录视图指向 auth.login,未登录提示"请先登录以访问此页面" |
mail |
SMTP邮件服务 | |
| Moment | moment |
前端时间格式化 |
| CORS | cors |
跨域支持 |
| Cache | cache |
开发环境simple缓存,生产环境redis |
| JWTManager | jwt |
JWT令牌管理 |
| Bootstrap5 | bootstrap |
Bootstrap5模板渲染 |
3.4 请求处理流水线
HTTP请求 → Flask路由匹配 → before_app_request (更新last_seen)
→ 蓝图路由函数 → 权限装饰器检查 → 表单验证 → 业务逻辑
→ SQLAlchemy ORM操作 → 模板渲染/JSON响应 → 返回HTTP响应
before_app_request 钩子位于 app/auth/routes.py 末尾,每次请求前自动调用 current_user.ping() 更新用户的 last_seen 时间戳。
4. 配置系统
4.1 配置类层次
配置文件 config.py 定义了 4 个配置类,形成继承层次:
Config (基类)
├── DevelopmentConfig (开发环境)
├── TestingConfig (测试环境)
└── ProductionConfig (生产环境)
4.2 基础配置(Config 类)
| 配置项 | 值 | 说明 |
|---|---|---|
SECRET_KEY |
环境变量或 'dev-key-please-change-in-production' |
Flask会话密钥 |
SQLALCHEMY_DATABASE_URI |
环境变量或 mysql://root:123456@localhost:3306/design_116_exercise |
数据库连接URI |
SQLALCHEMY_TRACK_MODIFICATIONS |
False |
关闭修改跟踪以提升性能 |
SQLALCHEMY_RECORD_QUERIES |
True |
记录SQL查询用于性能分析 |
SQLALCHEMY_ENGINE_OPTIONS |
pool_size=10, pool_recycle=3600, pool_pre_ping=True | 连接池配置 |
POSTS_PER_PAGE |
10 |
帖子/记录分页每页条数 |
USERS_PER_PAGE |
15 |
用户列表分页 |
ITEMS_PER_PAGE |
10 |
通用分页 |
MAIL_SERVER |
环境变量或 smtp.gmail.com |
SMTP服务器 |
MAIL_PORT |
环境变量或 587 |
SMTP端口 |
MAIL_USE_TLS |
true |
启用TLS |
MAX_CONTENT_LENGTH |
16 * 1024 * 1024 (16MB) |
上传文件大小限制 |
ALLOWED_EXTENSIONS |
txt, pdf, png, jpg, jpeg, gif, csv, xlsx | 允许的文件类型 |
CACHE_TYPE |
simple |
缓存类型 |
CACHE_DEFAULT_TIMEOUT |
300 (5分钟) |
默认缓存超时 |
JWT_SECRET_KEY |
环境变量或 'jwt-secret-key' |
JWT签名密钥 |
JWT_ACCESS_TOKEN_EXPIRES |
timedelta(hours=1) |
JWT访问令牌1小时过期 |
JWT_REFRESH_TOKEN_EXPIRES |
timedelta(days=30) |
JWT刷新令牌30天过期 |
LANGUAGES |
['zh', 'en'] |
支持语言 |
TIMEZONE |
'Asia/Shanghai' |
时区 |
WTF_CSRF_ENABLED |
True |
启用CSRF保护 |
WTF_CSRF_TIME_LIMIT |
3600 (1小时) |
CSRF令牌有效期 |
DATA_ANALYSIS_CACHE_TIMEOUT |
1800 (30分钟) |
数据分析缓存 |
CHART_CACHE_TIMEOUT |
600 (10分钟) |
图表缓存 |
4.3 开发环境配置
DEBUG = TrueSQLALCHEMY_ECHO = True(打印SQL语句)- 配置 RotatingFileHandler 写入
logs/gym_analysis.log(10MB轮转,保留10个备份) - 支持
LOG_TO_STDOUT环境变量切换到标准输出
4.4 测试环境配置
TESTING = True- 使用 SQLite 内存数据库
sqlite:///:memory: WTF_CSRF_ENABLED = False(禁用CSRF方便测试)CACHE_TYPE = 'null'MAIL_SUPPRESS_SEND = True(抑制邮件发送)
4.5 生产环境配置
DEBUG = False- 使用
mysql+pymysql驱动 +charset=utf8mb4 CACHE_TYPE = 'redis',连接redis://localhost:6379/0- 配置 SMTPHandler 发送错误邮件给管理员
- RotatingFileHandler 记录日志
4.6 配置选择机制
python
config_name = os.getenv('FLASK_CONFIG') or 'default'
app = create_app(config_name)
通过环境变量 FLASK_CONFIG 指定,未设置时默认使用 development 配置。
5. 数据库设计
5.1 数据库选择
默认使用 MySQL 8.x,数据库名 design_116_exercise,字符集 UTF-8。测试环境使用 SQLite 内存数据库。
5.2 模型总览
系统共定义 12 个数据库模型,分布在 6 个模型文件中:
| 模型类 | 数据库表名 | 所属文件 | 说明 |
|---|---|---|---|
Role |
roles |
user.py | 角色表 |
Permission |
-- | user.py | 权限常量类(非数据库表) |
User |
users |
user.py | 系统用户表 |
Member |
members |
member.py | 会员基础信息表 |
ExerciseSession |
exercise_sessions |
member.py | 锻炼会话记录表 |
GymMember |
gym_members |
gym_member.py | 健身房会员扩展表 |
BodyMeasurement |
body_measurements |
gym_member.py | 身体测量记录表 |
Activity |
activities |
activity.py | 活动日志表 |
Course |
courses |
course.py | 健身课程表 |
CourseSchedule |
course_schedules |
course.py | 课程安排表 |
CourseEnrollment |
course_enrollments |
course.py | 课程报名表 |
WorkoutPlan |
workout_plans |
workout.py | 锻炼计划表 |
WorkoutPlanExercise |
workout_plan_exercises |
workout.py | 计划内运动项目表 |
WorkoutRecord |
workout_records |
workout.py | 健身记录表 |
WorkoutDetail |
workout_details |
workout.py | 锻炼详情表 |
5.3 各模型详细字段说明
5.3.1 Role(角色模型)
表名: roles
| 字段名 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
Integer | PRIMARY KEY | 角色ID,自增 |
name |
String(64) | UNIQUE, NOT NULL, INDEX | 角色名称(如"普通用户"、"管理员") |
description |
Text | NULLABLE | 角色描述 |
permissions |
Integer | DEFAULT 0 | 权限位掩码(位运算存储多个权限) |
created_at |
DateTime | DEFAULT utcnow | 创建时间 |
updated_at |
DateTime | DEFAULT utcnow, ON UPDATE | 更新时间 |
关联关系:
users-> User(一对多,backref='role',lazy='dynamic')
预置角色:
普通用户:权限 = VIEW(1)管理员:权限 = VIEW(1) + WRITE(2) + MODERATE(4) + ADMIN(8) + IMPORT(16) + EXPORT(32) + BACKUP(64) = 127
关键方法:
has_permission(permission)-- 位运算检查是否有指定权限add_permission(permission)-- 添加权限位remove_permission(permission)-- 移除权限位reset_permissions()-- 重置为0insert_roles()-- 静态方法,插入默认角色
5.3.2 Permission(权限常量)
注意: Permission 不是数据库模型,而是纯Python类,定义权限常量。
| 常量 | 值 | 二进制 | 说明 |
|---|---|---|---|
VIEW |
1 | 0b0000001 | 查看权限 |
WRITE |
2 | 0b0000010 | 写入权限 |
MODERATE |
4 | 0b0000100 | 管理权限 |
ADMIN |
8 | 0b0001000 | 管理员权限 |
IMPORT |
16 | 0b0010000 | 导入权限 |
EXPORT |
32 | 0b0100000 | 导出权限 |
BACKUP |
64 | 0b1000000 | 备份权限 |
5.3.3 User(用户模型)
表名: users
| 字段名 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
Integer | PRIMARY KEY | 用户ID,自增 |
username |
String(64) | UNIQUE, INDEX | 用户名 |
email |
String(120) | UNIQUE, INDEX | 邮箱地址 |
password_hash |
String(128) | -- | 密码哈希值(Werkzeug生成) |
is_active |
Boolean | DEFAULT True | 账户是否激活 |
is_administrator |
Boolean | DEFAULT False | 是否为管理员(快捷标记) |
created_at |
DateTime | DEFAULT utcnow | 注册时间 |
last_login |
DateTime | NULLABLE | 最后登录时间(登录时自动更新) |
real_name |
String(64) | NULLABLE | 真实姓名 |
phone |
String(20) | NULLABLE | 手机号码 |
avatar |
String(128) | NULLABLE | 头像文件名 |
bio |
Text | NULLABLE | 个人简介 |
confirmed |
Boolean | DEFAULT False | 邮箱是否已确认 |
last_seen |
DateTime | DEFAULT utcnow | 最后活跃时间 |
role_id |
Integer | FOREIGN KEY -> roles.id | 角色外键 |
继承: UserMixin(Flask-Login),db.Model
关联关系:
role-> Role(多对一,backref)courses-> Course(一对多,教练关联,backref='courses')
关键方法:
| 方法 | 说明 |
|---|---|
set_password(password) |
使用 Werkzeug 的 generate_password_hash 设置密码 |
check_password(password) |
使用 check_password_hash 验证密码 |
is_admin() |
返回 is_administrator 字段 |
can(permission) |
检查用户角色是否有指定权限 |
ping() |
更新 last_seen 为当前时间 |
get_reset_password_token(expires_in=600) |
生成JWT密码重置令牌(默认10分钟有效) |
verify_reset_password_token(token) |
验证密码重置令牌 |
get_confirmation_token(expires_in=3600) |
生成JWT邮箱确认令牌(默认1小时有效) |
confirm(token) |
验证邮箱确认令牌并设置 confirmed=True |
to_dict(include_email=False) |
序列化为字典(API用) |
from_dict(data, new_user=False) |
从字典反序列化更新 |
generate_fake(count=100) |
生成虚拟用户数据(使用Faker) |
get_avatar(size) |
获取头像URL(自定义或Gravatar) |
upload_avatar(file) |
上传头像(检测MIME类型,Pillow处理缩放至200x200) |
头像处理流程:
- 使用 python-magic 检测文件MIME类型,确保是图片
- 生成唯一文件名
avatar_{user_id}_{timestamp}.{ext} - 使用 Pillow 打开图片
- RGBA/LA模式转RGB(白色背景)
- 缩放至 200x200(LANCZOS算法)
- 保存为 JPEG(quality=95, optimize=True)
- 删除旧头像文件
- 更新数据库 avatar 字段
5.3.4 Member(会员基础信息模型)
表名: members
| 字段名 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
Integer | PRIMARY KEY | 会员ID,自增 |
name |
String(100) | NOT NULL | 会员姓名 |
gender |
String(10) | NOT NULL | 性别('Male'/'Female') |
birth_date |
Date | NOT NULL | 出生日期 |
phone |
String(20) | NOT NULL | 手机号码 |
email |
String(120) | NULLABLE | 邮箱地址 |
address |
String(200) | NULLABLE | 地址 |
height |
Float | NULLABLE | 身高(米) |
weight |
Float | NULLABLE | 体重(公斤) |
bmi |
Float | NULLABLE | BMI指数(weight/height^2) |
fat_percentage |
Float | NULLABLE | 体脂率(%) |
water_intake |
Float | NULLABLE | 每日饮水量(升) |
workout_frequency |
Integer | NULLABLE | 每周锻炼次数 |
experience_level |
Integer | NULLABLE | 经验等级(1=初学者, 2=中级, 3=专家) |
created_at |
DateTime | DEFAULT utcnow | 创建时间 |
updated_at |
DateTime | DEFAULT utcnow, ON UPDATE | 更新时间 |
关联关系:
sessions-> ExerciseSession(一对多,backref='member',lazy='dynamic')gym_membership-> GymMember(一对一,backref='member',uselist=False)activities-> Activity(一对多,backref='member',lazy='dynamic')course_enrollments-> CourseEnrollment(一对多,backref='member',lazy='dynamic')
计算属性:
age-- 根据 birth_date 计算当前年龄(考虑月/日)
序列化方法: to_dict() 返回完整字段字典
5.3.5 ExerciseSession(锻炼会话记录模型)
表名: exercise_sessions
| 字段名 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
Integer | PRIMARY KEY | 记录ID,自增 |
member_id |
Integer | FOREIGN KEY -> members.id | 所属会员ID |
date |
DateTime | DEFAULT utcnow | 锻炼日期时间 |
duration |
Integer | NULLABLE | 锻炼时长(分钟) |
exercise_type |
String(50) | NULLABLE | 锻炼类型(Cardio/Strength/Yoga/HIIT/Swimming/Pilates/Other) |
calories_burned |
Float | NULLABLE | 消耗卡路里(千卡) |
heart_rate_avg |
Integer | NULLABLE | 平均心率(次/分钟) |
max_heart_rate |
Integer | NULLABLE | 最大心率(次/分钟) |
resting_heart_rate |
Integer | NULLABLE | 静息心率(次/分钟) |
notes |
Text | NULLABLE | 备注 |
created_at |
DateTime | DEFAULT utcnow | 记录创建时间 |
关联关系:
member-> Member(多对一,backref)
锻炼类型枚举值:
| 英文值 | 中文名 |
|---|---|
| Strength | 力量训练 |
| Cardio | 有氧运动 |
| Yoga | 瑜伽 |
| HIIT | HIIT高强度间歇 |
| Swimming | 游泳 |
| Pilates | 普拉提 |
| Other | 其他 |
5.3.6 GymMember(健身房会员扩展模型)
表名: gym_members
| 字段名 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
Integer | PRIMARY KEY | 记录ID,自增 |
member_id |
Integer | FOREIGN KEY -> members.id, NOT NULL | 关联基础会员ID |
membership_type |
String(50) | NOT NULL | 会员类型(普通会员/VIP会员等) |
start_date |
DateTime | NOT NULL | 会员开始日期 |
end_date |
DateTime | NOT NULL | 会员到期日期 |
status |
String(20) | NOT NULL | 状态(active/expired/suspended) |
created_at |
DateTime | DEFAULT utcnow | 创建时间 |
updated_at |
DateTime | DEFAULT utcnow, ON UPDATE | 更新时间 |
关联关系:
member-> Member(多对一,backref='gym_membership',uselist=False 一对一)measurements-> BodyMeasurement(一对多,backref='member',lazy='dynamic')workout_records-> WorkoutRecord(一对多,backref='member',lazy='dynamic')
计算属性:
bmi-- 根据 height 和 weight 计算BMI(注意:此处height单位为cm)is_active-- 检查 end_date 是否 >= 当前日期days_remaining-- 计算距离到期的剩余天数
5.3.7 BodyMeasurement(身体测量记录模型)
表名: body_measurements
| 字段名 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
Integer | PRIMARY KEY | 记录ID,自增 |
member_id |
Integer | FOREIGN KEY -> gym_members.id, NOT NULL | 所属会员ID |
measurement_date |
Date | NOT NULL | 测量日期 |
weight |
Float | NULLABLE | 体重(kg) |
body_fat |
Float | NULLABLE | 体脂率(%) |
muscle_mass |
Float | NULLABLE | 肌肉量(kg) |
chest |
Float | NULLABLE | 胸围(cm) |
waist |
Float | NULLABLE | 腰围(cm) |
hip |
Float | NULLABLE | 臀围(cm) |
biceps |
Float | NULLABLE | 上臂围(cm) |
thigh |
Float | NULLABLE | 大腿围(cm) |
notes |
Text | NULLABLE | 备注 |
created_at |
DateTime | DEFAULT utcnow | 创建时间 |
关联关系:
member-> GymMember(多对一,backref='measurements',lazy='dynamic')
5.3.8 Activity(活动记录模型)
表名: activities
| 字段名 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
Integer | PRIMARY KEY | 活动ID,自增 |
member_id |
Integer | FOREIGN KEY -> members.id, NULLABLE | 关联会员ID(系统级操作如备份可为NULL) |
activity_type |
String(50) | NOT NULL | 活动类型(workout/plan_created/backup/restore/delete_backup等) |
description |
String(200) | NULLABLE | 活动描述 |
timestamp |
DateTime | DEFAULT utcnow | 活动发生时间 |
created_at |
DateTime | DEFAULT utcnow | 记录创建时间 |
关联关系:
member-> Member(多对一,backref='activities',lazy='dynamic')
关键方法:
| 方法 | 说明 |
|---|---|
log_activity(member_id, activity_type, description) |
静态方法,记录活动日志并添加到会话 |
to_dict() |
转换为字典格式 |
5.3.9 Course(健身课程模型)
表名: courses
| 字段名 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
Integer | PRIMARY KEY | 课程ID,自增 |
name |
String(100) | NOT NULL | 课程名称 |
description |
Text | NULLABLE | 课程描述 |
course_type |
String(50) | NOT NULL | 课程类型(团课/私教/体验课/其他) |
duration |
Integer | NOT NULL | 课程时长(分钟) |
max_capacity |
Integer | NOT NULL | 最大容纳人数 |
price |
Float | NOT NULL | 课程价格(元) |
instructor_id |
Integer | FOREIGN KEY -> users.id, NOT NULL | 教练(用户)ID |
created_at |
DateTime | DEFAULT utcnow | 创建时间 |
updated_at |
DateTime | DEFAULT utcnow, ON UPDATE | 更新时间 |
关联关系:
instructor-> User(多对一,backref='courses',lazy='joined',foreign_keys=[instructor_id])schedules-> CourseSchedule(一对多,backref='course',lazy='dynamic')enrollments-> CourseEnrollment(一对多,backref='course',lazy='dynamic')
计算属性:
current_enrollment_count-- 当前活跃报名人数is_full-- 是否已满员
5.3.10 CourseSchedule(课程安排模型)
表名: course_schedules
| 字段名 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
Integer | PRIMARY KEY | 安排ID,自增 |
course_id |
Integer | FOREIGN KEY -> courses.id, NOT NULL | 所属课程ID |
start_time |
DateTime | NOT NULL | 开始时间 |
end_time |
DateTime | NOT NULL | 结束时间 |
location |
String(100) | NOT NULL | 上课地点 |
status |
String(20) | DEFAULT 'scheduled' | 状态(scheduled/ongoing/completed/cancelled) |
created_at |
DateTime | DEFAULT utcnow | 创建时间 |
关联关系:
course-> Course(多对一,backref)enrollments-> CourseEnrollment(一对多,backref='schedule',lazy='dynamic')
5.3.11 CourseEnrollment(课程报名模型)
表名: course_enrollments
| 字段名 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
Integer | PRIMARY KEY | 报名ID,自增 |
course_id |
Integer | FOREIGN KEY -> courses.id, NOT NULL | 课程ID |
member_id |
Integer | FOREIGN KEY -> members.id, NOT NULL | 会员ID |
schedule_id |
Integer | FOREIGN KEY -> course_schedules.id, NOT NULL | 课程安排ID |
status |
String(20) | DEFAULT 'active' | 状态(active/cancelled/completed) |
enrollment_date |
DateTime | DEFAULT utcnow | 报名日期 |
created_at |
DateTime | DEFAULT utcnow | 创建时间 |
关联关系:
course-> Course(多对一,backref)member-> Member(多对一,backref='course_enrollments',lazy='dynamic')schedule-> CourseSchedule(多对一,backref='enrollments',lazy='dynamic')
5.3.12 WorkoutPlan(锻炼计划模型)
表名: workout_plans
| 字段名 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
Integer | PRIMARY KEY | 计划ID,自增 |
name |
String(100) | NOT NULL | 计划名称 |
description |
Text | NULLABLE | 计划描述 |
difficulty |
String(20) | NULLABLE | 难度级别(初级/中级/高级) |
duration |
Integer | NULLABLE | 计划时长(天) |
created_at |
DateTime | DEFAULT utcnow | 创建时间 |
updated_at |
DateTime | DEFAULT utcnow, ON UPDATE | 更新时间 |
关联关系:
exercises-> WorkoutPlanExercise(一对多,backref='plan',lazy='dynamic')
序列化方法: to_dict() 返回完整字段字典
5.3.13 WorkoutPlanExercise(锻炼计划运动项目模型)
表名: workout_plan_exercises
| 字段名 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
Integer | PRIMARY KEY | 项目ID,自增 |
plan_id |
Integer | FOREIGN KEY -> workout_plans.id, NOT NULL | 所属计划ID |
exercise_name |
String(100) | NOT NULL | 运动名称 |
sets |
Integer | NULLABLE | 组数 |
reps |
Integer | NULLABLE | 每组次数 |
weight |
Float | NULLABLE | 重量(kg) |
duration |
Integer | NULLABLE | 时长(秒) |
rest_time |
Integer | NULLABLE | 休息时间(秒) |
notes |
Text | NULLABLE | 备注 |
order |
Integer | NULLABLE | 顺序号 |
created_at |
DateTime | DEFAULT utcnow | 创建时间 |
关联关系:
plan-> WorkoutPlan(多对一,backref)
5.3.14 WorkoutRecord(健身记录模型)
表名: workout_records
| 字段名 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
Integer | PRIMARY KEY | 记录ID,自增 |
member_id |
Integer | FOREIGN KEY -> gym_members.id, NOT NULL | 所属会员ID |
workout_date |
Date | NOT NULL | 锻炼日期 |
workout_type |
String(50) | NOT NULL | 锻炼类型 |
duration |
Integer | NOT NULL | 锻炼时长(分钟) |
calories_burned |
Integer | NULLABLE | 消耗卡路里 |
heart_rate_avg |
Integer | NULLABLE | 平均心率 |
heart_rate_max |
Integer | NULLABLE | 最大心率 |
notes |
Text | NULLABLE | 备注 |
created_at |
DateTime | DEFAULT utcnow | 创建时间 |
关联关系:
member-> GymMember(多对一,backref='workout_records',lazy='dynamic')details-> WorkoutDetail(一对多,backref='record',lazy='dynamic')
5.3.15 WorkoutDetail(锻炼详情模型)
表名: workout_details
| 字段名 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
Integer | PRIMARY KEY | 详情ID,自增 |
record_id |
Integer | FOREIGN KEY -> workout_records.id, NOT NULL | 所属健身记录ID |
exercise_name |
String(50) | NOT NULL | 运动名称 |
sets |
Integer | NULLABLE | 组数 |
reps |
Integer | NULLABLE | 每组次数 |
weight |
Float | NULLABLE | 重量(kg) |
duration |
Integer | NULLABLE | 时长(秒) |
distance |
Float | NULLABLE | 距离(米) |
created_at |
DateTime | DEFAULT utcnow | 创建时间 |
关联关系:
record-> WorkoutRecord(多对一,backref='details',lazy='dynamic')
5.4 模型关系图(文字描述)
Role (1) ──────< User (N)
│
├──< Course (N) [as instructor]
│ ├──< CourseSchedule (N)
│ │ └──< CourseEnrollment (N)
│ └──< CourseEnrollment (N)
│
Member (1) ──────< ExerciseSession (N)
│ \
│ ├──< Activity (N)
│ ├──< CourseEnrollment (N)
│ └── GymMember (1) [一对一]
│ ├──< BodyMeasurement (N)
│ └──< WorkoutRecord (N)
│ └──< WorkoutDetail (N)
│
WorkoutPlan (1) ──< WorkoutPlanExercise (N)
6. 功能模块详解
6.1 主业务模块(main 蓝图)
主业务模块是系统的核心,包含约 2050 行路由代码,涵盖以下功能组:
6.1.1 首页与仪表盘
| 路由 | 方法 | 函数名 | 说明 |
|---|---|---|---|
/ 或 /index |
GET | index() |
系统首页,显示基础统计(会员总数、锻炼总数、近7天数据、热门锻炼类型) |
/dashboard |
GET | dashboard() |
数据仪表盘,支持 week/month/year 时间段切换,显示:会员增长趋势、年龄分布、锻炼趋势、运动类型分布、时段分布、活跃度热力图、最新动态 |
仪表盘数据统计指标:
- 会员总数及变化率
- 总锻炼人次及变化率
- 平均锻炼时长及变化率
- 会员活跃率及变化率
仪表盘图表类型:
- 会员增长趋势折线图(新增 + 累计)
- 会员年龄分布饼图(18-25/26-35/36-45/46-55/56+)
- 锻炼趋势折线图(人次 + 卡路里)
- 运动类型分布饼图
- 时段分布柱状图(6-9/9-12/12-15/15-18/18-21/21-24点)
- 活跃度热力图(周几 x 时段矩阵)
- 最新动态列表(锻炼记录 + 新会员注册)
6.1.2 会员管理
| 路由 | 方法 | 函数名 | 说明 |
|---|---|---|---|
/members |
GET | members() |
会员列表,支持搜索(姓名/电话/邮箱)、性别筛选、分页 |
/member/<id> |
GET | member_detail(id) |
会员详情,显示基本信息 + 最近5条锻炼记录 |
/member/<id>/edit |
GET/POST | edit_member(id) |
编辑会员信息,年龄转出生日期,自动计算BMI |
/member/<id>/delete |
POST | delete_member(id) |
删除会员(级联删除锻炼记录) |
/member/new |
GET/POST | new_member() |
添加新会员 |
6.1.3 锻炼记录管理
| 路由 | 方法 | 函数名 | 说明 |
|---|---|---|---|
/sessions |
GET | sessions() |
锻炼记录列表,支持按运动类型、会员、日期范围筛选 |
/session/<id> |
GET | session_detail(id) |
锻炼记录详情 |
/session/<id>/edit |
GET/POST | edit_session(id) |
编辑锻炼记录 |
/session/<id>/delete |
POST | delete_session(id) |
删除锻炼记录 |
/session/new |
GET/POST | new_session() |
添加新锻炼记录 |
/add_session |
GET/POST | add_session() |
添加锻炼记录(需要WRITE权限) |
/member/<member_id>/workout/add |
GET/POST | add_workout_record(member_id) |
为指定会员添加锻炼记录 |
6.1.4 身体测量记录
| 路由 | 方法 | 函数名 | 说明 |
|---|---|---|---|
/member/<member_id>/measurement/add |
GET/POST | add_body_measurement(member_id) |
添加身体测量记录 |
/measurement/<id> |
GET | body_measurement_detail(id) |
测量记录详情 |
6.1.5 锻炼计划管理
| 路由 | 方法 | 函数名 | 说明 |
|---|---|---|---|
/member/<id>/plans |
GET | workout_plans(id) |
会员锻炼计划列表 |
/member/<id>/plan/add |
GET/POST | add_workout_plan(id) |
添加锻炼计划 |
/plan/<id> |
GET | workout_plan_detail(id) |
锻炼计划详情 |
/plan/<id>/edit |
GET/POST | edit_workout_plan(id) |
编辑锻炼计划 |
/plan/<id>/delete |
POST | delete_workout_plan(id) |
删除锻炼计划 |
/plan/<id>/exercise/add |
GET/POST | add_workout_plan_exercise(id) |
添加计划运动项目 |
/plan/exercise/<id>/edit |
GET/POST | edit_workout_plan_exercise(id) |
编辑计划运动项目 |
/plan/exercise/<id>/delete |
POST | delete_workout_plan_exercise(id) |
删除计划运动项目 |
6.1.6 课程管理
| 路由 | 方法 | 函数名 | 说明 |
|---|---|---|---|
/courses |
GET | courses() |
课程列表,支持按类型和教练筛选 |
/courses/add |
GET/POST | add_course() |
添加课程(需WRITE权限) |
/courses/<id> |
GET | course_detail(id) |
课程详情 |
/courses/<id>/edit |
GET/POST | edit_course(id) |
编辑课程(需WRITE权限) |
/courses/<id>/schedules |
GET | course_schedules(id) |
课程安排列表 |
/courses/<id>/schedules/add |
GET/POST | add_course_schedule(id) |
添加课程安排(需WRITE权限) |
/courses/schedules/<id>/edit |
POST | edit_course_schedule(id) |
编辑课程安排 |
/courses/schedules/<id>/delete |
POST | delete_course_schedule(id) |
删除课程安排(检查是否有活跃报名) |
/courses/<course_id>/enroll |
GET/POST | enroll_course(course_id) |
课程报名(检查容量和重复报名) |
/courses/enrollments |
GET | course_enrollments() |
报名记录列表,支持按课程/会员/状态筛选 |
/courses/enrollments/<id>/cancel |
POST | cancel_course_enrollment(id) |
取消报名 |
/courses/enrollments/<id>/edit |
POST | edit_course_enrollment(id) |
编辑报名状态 |
/courses/enrollments/<id>/delete |
POST | delete_course_enrollment(id) |
删除报名记录 |
6.1.7 数据分析
| 路由 | 方法 | 函数名 | 说明 |
|---|---|---|---|
/analytics |
GET | analytics() |
深度数据分析页面,14种分析维度 |
/member/<id>/analytics |
GET | member_analytics(id) |
单会员数据分析 |
/workout/stats |
GET | workout_stats() |
锻炼统计页面 |
/api/chart_data |
GET | chart_data() |
图表数据API(gender/workout_types/monthly_trend) |
深度分析(analytics)包含的14种分析:
- 性别分布(饼图)
- 运动类型分布(饼图)
- 运动类型按性别分组(分组柱状图)
- 各运动类型平均卡路里(柱状图)
- BMI分布(直方图,7个分桶:<15/15-18.5/18.5-25/25-30/30-35/35-40/40+)
- 体重分布(直方图,9个分桶:30-40/40-50/.../110+)
- 卡路里消耗分布(直方图,8个分桶)
- 散点图:体脂率 vs 卡路里(按运动类型着色)
- 散点图:锻炼时长 vs 卡路里(按运动类型着色)
- 散点图:体重 vs 卡路里(按经验等级着色)
- 散点图:BMI vs 卡路里
- 经验等级分布(饼图)
- 每周锻炼频率分布(饼图)
- 相关性热力图(11个数值变量的相关系数矩阵,使用numpy.corrcoef)
相关性热力图的11个变量: Age, Height, Weight, BMI, Fat%, Water, Duration, Calories, AvgHR, MaxHR, RestHR
6.1.8 数据导入导出
| 路由 | 方法 | 函数名 | 权限要求 | 说明 |
|---|---|---|---|---|
/data/import |
GET/POST | import_data() |
IMPORT + 管理员 | CSV数据导入 |
/data/export |
GET/POST | export_data() |
EXPORT + 管理员 | CSV数据导出 |
6.1.9 数据备份与恢复
| 路由 | 方法 | 函数名 | 权限要求 | 说明 |
|---|---|---|---|---|
/data/backup |
GET/POST | backup_data() |
BACKUP | 创建数据库备份(支持MySQL和SQLite) |
/data/backup/list |
GET | backup_list() |
BACKUP | 备份文件列表 |
/data/backup/download/<filename> |
GET | download_backup(filename) |
ADMIN | 下载备份文件 |
/data/backup/restore/<filename> |
POST | restore_backup(filename) |
ADMIN | 从备份恢复数据 |
/data/backup/delete/<filename> |
POST | delete_backup(filename) |
ADMIN | 删除备份文件 |
备份实现说明:
- 使用
sqlalchemy.make_url()解析数据库连接字符串,替代字符串分割 - MySQL 备份通过
subprocess.run调用mysqldump,以二进制模式捕获输出,返回码非零时抛出异常 - SQLite 备份通过
shutil.copy2复制数据库文件 - 备份操作记录到 Activity 日志(member_id 为 NULL,属系统级操作)
6.1.10 数据搜索
| 路由 | 方法 | 函数名 | 说明 |
|---|---|---|---|
/data/search |
GET | search_data() |
统一搜索入口,支持 members 和 sessions 两种类型 |
6.1.11 辅助函数
main/routes.py 底部定义了大量辅助数据计算函数:
| 函数名 | 说明 |
|---|---|
calculate_change_rate(current, previous) |
计算环比变化率(%) |
calculate_active_rate(start_date, end_date) |
计算活跃会员率 |
get_member_growth_data(start, end, fmt) |
获取会员增长趋势数据 |
get_age_distribution() |
获取年龄分布数据 |
get_workout_trend_data(start, end, fmt) |
获取锻炼趋势数据 |
get_exercise_type_distribution(start, end) |
获取运动类型分布(含中英文映射) |
get_time_distribution(start, end) |
获取时段分布数据 |
get_activity_heatmap(start, end) |
获取活跃度热力图数据 |
get_member_activity_data(start, end) |
获取会员活跃度数据 |
get_top_members(start, end, limit=10) |
获取锻炼排行榜(按次数排序) |
get_member_workout_trend(member, start, end) |
获取单会员锻炼趋势 |
get_member_exercise_type_distribution(member, start, end) |
获取单会员运动类型分布 |
get_member_calories_trend(member, start, end) |
获取单会员卡路里趋势 |
get_member_heart_rate_trend(member, start, end) |
获取单会员心率趋势 |
6.2 认证模块(auth 蓝图)
认证模块处理用户身份验证和个人信息管理。
| 路由 | 方法 | 函数名 | 说明 |
|---|---|---|---|
/auth/login |
GET/POST | login() |
用户登录(用户名+密码,支持"记住我",登录成功自动更新 last_login 时间戳) |
/auth/logout |
GET | logout() |
用户登出 |
/auth/register |
GET/POST | register() |
用户注册 |
/auth/confirm/<token> |
GET | confirm(token) |
邮箱确认 |
/auth/confirm |
GET | resend_confirmation() |
重新发送确认邮件 |
/auth/profile |
GET | profile() |
查看个人资料 |
/auth/edit_profile |
GET/POST | edit_profile() |
编辑个人资料(含头像上传) |
/auth/change_password |
GET/POST | change_password() |
修改密码 |
/auth/reset_password_request |
GET/POST | reset_password_request() |
请求重置密码(发送邮件) |
/auth/reset_password/<token> |
GET/POST | reset_password(token) |
重置密码 |
/auth/users |
GET | users() |
用户列表(管理员功能,支持搜索) |
/auth/user/<id> |
GET | user(id) |
查看用户详情(管理员或本人) |
/auth/edit_user/<id> |
GET/POST | edit_user(id) |
编辑用户(管理员功能) |
/auth/toggle_user_status/<id> |
GET | toggle_user_status(id) |
切换用户激活/禁用状态(管理员功能,不允许禁用自己) |
6.3 API模块(api 蓝图)
API模块提供JSON格式的RESTful接口,通过JWT进行认证。
6.3.1 认证接口(api/auth.py)
| 路由 | 方法 | 说明 |
|---|---|---|
/api/auth/login |
POST | API登录,返回JWT令牌和用户信息 |
/api/auth/profile |
GET | 获取当前用户信息(需登录) |
/api/auth/refresh |
POST | 刷新JWT令牌 |
JWT令牌生成逻辑:
python
payload = {
'user_id': user.id,
'username': user.username,
'exp': datetime.utcnow() + JWT_ACCESS_TOKEN_EXPIRES # 默认1小时
}
token = jwt.encode(payload, JWT_SECRET_KEY, algorithm='HS256')
6.3.2 会员接口(api/members.py)
| 路由 | 方法 | 说明 | 权限 |
|---|---|---|---|
/api/members |
GET | 获取会员列表(分页、筛选) | 登录 |
/api/members/<id> |
GET | 获取单个会员详情 | 登录 |
/api/members |
POST | 创建新会员 | WRITE |
/api/members/<id> |
PUT | 更新会员信息 | WRITE |
/api/members/<id> |
DELETE | 删除会员 | MODERATE |
/api/members/<id>/sessions |
GET | 获取会员锻炼记录 | 登录 |
/api/sessions |
GET | 获取锻炼记录列表 | 登录 |
/api/sessions |
POST | 创建锻炼记录 | WRITE |
6.3.3 分析接口(api/analytics.py)
| 路由 | 方法 | 说明 |
|---|---|---|
/api/analytics/overview |
GET | 总览数据(总会员、活跃会员、总锻炼、总卡路里、总时长) |
/api/analytics/gender_distribution |
GET | 性别分布 |
/api/analytics/age_distribution |
GET | 年龄分布(18-24/25-34/35-44/45-54/55+) |
/api/analytics/bmi_distribution |
GET | BMI分布(偏瘦/正常/超重/肥胖) |
/api/analytics/workout_types |
GET | 锻炼类型统计 |
/api/analytics/monthly_trend |
GET | 月度趋势 |
/api/analytics/member_stats/<member_id> |
GET | 指定会员统计 |
6.3.4 课程API(内嵌于main/routes.py)
| 路由 | 方法 | 说明 |
|---|---|---|
/api/courses/<id>/schedules |
GET | 获取课程未来安排(含可用名额) |
/api/courses/stats |
GET | 课程统计(类型分布、报名统计、教练课程数) |
/api/chart_data?type=xxx |
GET | 图表数据(gender/workout_types/monthly_trend) |
6.3.5 API错误处理(api/errors.py)
提供标准化的JSON错误响应格式:
python
{
"error": "Bad Request",
"message": "缺少必需字段: name"
}
支持的HTTP状态码:400(bad_request)、401(unauthorized)、403(forbidden)、404(not_found)、500(internal_server_error)
7. 表单设计
7.1 认证表单(app/auth/forms.py)
LoginForm -- 登录表单
| 字段 | 类型 | 验证规则 | 说明 |
|---|---|---|---|
username |
StringField | DataRequired, Length(1,64) | 用户名 |
password |
PasswordField | DataRequired | 密码 |
remember_me |
BooleanField | -- | 记住我 |
submit |
SubmitField | -- | 登录按钮 |
RegistrationForm -- 注册表单
| 字段 | 类型 | 验证规则 | 说明 |
|---|---|---|---|
username |
StringField | DataRequired, Length(1,64), Regexp(字母开头,允许字母数字点下划线) | 用户名 |
email |
StringField | DataRequired, Length(1,64), Email | 邮箱 |
real_name |
StringField | Length(0,64) | 真实姓名 |
password |
PasswordField | DataRequired, Length(min=6) | 密码 |
password2 |
PasswordField | DataRequired, EqualTo('password') | 确认密码 |
submit |
SubmitField | -- | 注册按钮 |
自定义验证: 验证用户名和邮箱唯一性
ChangePasswordForm -- 修改密码表单
| 字段 | 类型 | 验证规则 |
|---|---|---|
old_password |
PasswordField | DataRequired |
password |
PasswordField | DataRequired, Length(8,128) |
password2 |
PasswordField | DataRequired, EqualTo('password') |
PasswordResetRequestForm -- 密码重置请求表单
| 字段 | 类型 | 验证规则 |
|---|---|---|
email |
StringField | DataRequired, Email |
PasswordResetForm -- 密码重置表单
| 字段 | 类型 | 验证规则 |
|---|---|---|
password |
PasswordField | DataRequired, Length(8,128) |
password2 |
PasswordField | DataRequired, EqualTo('password') |
EditProfileForm -- 编辑个人资料表单
| 字段 | 类型 | 验证规则 | 说明 |
|---|---|---|---|
avatar |
FileField | FileAllowed(['jpg','jpeg','png','gif']) | 头像上传 |
real_name |
StringField | Length(0,64) | 真实姓名 |
phone |
StringField | Length(0,20) | 手机号码 |
bio |
TextAreaField | Length(0,140) | 个人简介 |
EditProfileAdminForm -- 管理员编辑用户表单
| 字段 | 类型 | 验证规则 |
|---|---|---|
username |
StringField | DataRequired, Length(1,64) |
email |
StringField | DataRequired, Email |
confirmed |
BooleanField | -- |
real_name |
StringField | Length(0,64) |
phone |
StringField | Length(0,20) |
bio |
TextAreaField | Length(0,140) |
构造函数接收 user 参数用于唯一性验证(排除自身)
7.2 主业务表单(app/main/forms.py)
GymMemberForm -- 健身房会员表单
| 字段 | 类型 | 验证规则 | 说明 |
|---|---|---|---|
name |
StringField | DataRequired, Length(1,64) | 姓名 |
email |
StringField | DataRequired, Email | 邮箱 |
phone |
StringField | Length(0,20) | 手机号码 |
age |
IntegerField | DataRequired, NumberRange(16,100) | 年龄 |
gender |
SelectField | DataRequired | 性别(Male/Female) |
weight |
FloatField | DataRequired, NumberRange(30,300) | 体重(kg) |
height |
FloatField | DataRequired, NumberRange(1.0,2.5) | 身高(m) |
fat_percentage |
FloatField | Optional, NumberRange(5,50) | 体脂率(%) |
water_intake |
FloatField | Optional, NumberRange(0.5,10) | 日饮水量(升) |
workout_frequency |
IntegerField | DataRequired, NumberRange(1,7) | 每周锻炼频率(天) |
experience_level |
SelectField | DataRequired, coerce=int | 经验等级(1/2/3) |
membership_type |
SelectField | -- | 会员类型 |
join_date |
DateField | DataRequired, default=today | 入会日期 |
is_active |
BooleanField | default=True | 是否活跃 |
构造参数: original_email -- 编辑时传入原邮箱,validate_email 仅在邮箱变更时检查唯一性,避免编辑保存时报"邮箱已被使用"。
WorkoutRecordForm -- 健身记录表单
| 字段 | 类型 | 验证规则 | 说明 |
|---|---|---|---|
member_id |
SelectField | DataRequired, coerce=int | 会员(下拉选择) |
workout_date |
DateField | DataRequired | 锻炼日期 |
workout_type |
SelectField | DataRequired | 锻炼类型(7种) |
duration |
IntegerField | DataRequired, NumberRange(1,300) | 锻炼时长(分钟) |
calories_burned |
IntegerField | Optional, NumberRange(0,2000) | 消耗卡路里 |
heart_rate_avg |
IntegerField | Optional, NumberRange(40,200) | 平均心率 |
heart_rate_max |
IntegerField | Optional, NumberRange(40,220) | 最大心率 |
notes |
TextAreaField | Optional | 备注 |
WorkoutDetailForm -- 锻炼详情表单
| 字段 | 类型 | 验证规则 |
|---|---|---|
exercise_name |
StringField | DataRequired, Length(max=50) |
sets |
IntegerField | Optional, NumberRange(1,20) |
reps |
IntegerField | Optional, NumberRange(1,100) |
weight |
FloatField | Optional, NumberRange(0,500) |
duration |
IntegerField | Optional, NumberRange(1,3600) |
distance |
FloatField | Optional, NumberRange(0,100000) |
BodyMeasurementForm -- 身体测量记录表单
| 字段 | 类型 | 验证规则 | 说明 |
|---|---|---|---|
measurement_date |
DateField | DataRequired | 测量日期 |
weight |
FloatField | Optional, NumberRange(30,200) | 体重(kg) |
body_fat |
FloatField | Optional, NumberRange(3,50) | 体脂率(%) |
muscle_mass |
FloatField | Optional, NumberRange(10,100) | 肌肉量(kg) |
chest |
FloatField | Optional, NumberRange(50,150) | 胸围(cm) |
waist |
FloatField | Optional, NumberRange(40,150) | 腰围(cm) |
hip |
FloatField | Optional, NumberRange(50,150) | 臀围(cm) |
biceps |
FloatField | Optional, NumberRange(20,50) | 上臂围(cm) |
thigh |
FloatField | Optional, NumberRange(30,80) | 大腿围(cm) |
notes |
TextAreaField | Optional | 备注 |
WorkoutPlanForm -- 锻炼计划表单
| 字段 | 类型 | 验证规则 |
|---|---|---|
title |
StringField | DataRequired, Length(max=100) |
description |
TextAreaField | -- |
start_date |
DateField | DataRequired |
end_date |
DateField | DataRequired |
status |
SelectField | active/completed/cancelled |
WorkoutPlanExerciseForm -- 锻炼计划项目表单
| 字段 | 类型 | 验证规则 |
|---|---|---|
exercise_type |
SelectField | 9种运动类型 |
target_duration |
IntegerField | DataRequired, NumberRange(min=1) |
target_calories |
FloatField | Optional, NumberRange(min=0) |
target_heart_rate |
IntegerField | Optional, NumberRange(40,220) |
notes |
TextAreaField | -- |
scheduled_date |
DateField | DataRequired |
status |
SelectField | pending/completed/skipped |
CourseForm -- 课程表单
| 字段 | 类型 | 验证规则 |
|---|---|---|
name |
StringField | DataRequired, Length(max=100) |
description |
TextAreaField | Optional, Length(max=500) |
course_type |
SelectField | DataRequired(团课/私教/体验课/其他) |
duration |
IntegerField | DataRequired, NumberRange(10,300) |
max_capacity |
IntegerField | DataRequired, NumberRange(1,100) |
price |
FloatField | DataRequired, NumberRange(min=0) |
instructor_id |
IntegerField | DataRequired |
CourseScheduleForm -- 课程安排表单
| 字段 | 类型 | 验证规则 |
|---|---|---|
course_id |
IntegerField | DataRequired |
start_time |
DateTimeField | DataRequired, format='%Y-%m-%d %H:%M' |
end_time |
DateTimeField | DataRequired, format='%Y-%m-%d %H:%M' |
location |
StringField | DataRequired, Length(max=100) |
status |
SelectField | scheduled/ongoing/completed/cancelled |
CourseEnrollmentForm -- 课程报名表单
| 字段 | 类型 | 验证规则 |
|---|---|---|
course_id |
IntegerField | DataRequired |
member_id |
IntegerField | DataRequired |
schedule_id |
IntegerField | DataRequired |
status |
SelectField | active/cancelled |
其他表单
- SearchForm -- 搜索表单(query + data_type)
- FilterForm -- 筛选表单(membership_type + status + date_from + date_to)
- DataImportForm -- 数据导入表单(file + data_type)
- DataExportForm -- 数据导出表单(data_type)
- MemberForm -- 通用会员表单(含membership_type和start_date/end_date)
- ExerciseSessionForm -- 锻炼会话表单
8. 前端技术
8.1 模板引擎
使用 Jinja2 模板引擎(Flask内置),采用模板继承机制:
base.html-- 基础布局模板,定义整体页面结构- 各子模板通过
{% extends "base.html" %}继承,使用{% block content %}和{% block scripts %}填充内容
8.2 CSS框架
Bootstrap 5.3.0 -- 通过CDN引入,提供响应式栅格系统和基础组件样式。
系统在 base.html 中定义了大量自定义CSS变量和组件样式,采用现代设计语言:
CSS变量体系:
css
:root {
--primary: #0d9488; /* 主色调(青绿色) */
--primary-dark: #0f766e;
--primary-light: #14b8a6;
--secondary: #f97316; /* 辅助色(橙色) */
--accent: #0ea5e9; /* 强调色(蓝色) */
--success: #10b981; /* 成功(绿色) */
--warning: #f59e0b; /* 警告(黄色) */
--danger: #ef4444; /* 危险(红色) */
--radius: 12px; /* 圆角 */
--shadow-md: ...; /* 阴影 */
--transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); /* 过渡动画 */
--sidebar-width: 260px; /* 侧边栏宽度 */
}
自定义组件类:
.sidebar-- 固定左侧导航栏,深绿色渐变背景.card-modern-- 现代卡片组件,hover时上浮效果.stat-card-modern-- 统计数据卡片,5种颜色主题.table-modern-- 现代表格样式.badge-modern-- 胶囊形标签.form-control-modern-- 现代输入框.alert-custom-- 自定义警告框,带左侧彩条
响应式设计:
@media (max-width: 992px)-- 侧边栏隐藏,显示汉堡菜单按钮@media (max-width: 576px)-- 缩小内容区域内边距
8.3 JavaScript图表库
ECharts 5.4.0 -- 百度开源可视化库,通过CDN引入,用于绘制所有交互式图表。
仪表盘和分析页面使用ECharts渲染的图表类型:
- 折线图(会员增长趋势、锻炼趋势、卡路里趋势、心率趋势)
- 饼图(年龄分布、运动类型分布、性别分布、经验等级分布)
- 柱状图(时段分布、BMI分布、体重分布、卡路里分布)
- 散点图(体脂率vs卡路里、时长vs卡路里、体重vs卡路里、BMIvs卡路里)
- 热力图(活跃度热力图、相关性矩阵)
- 面积图(趋势数据)
8.4 图标库
Font Awesome 6.4.0 -- 通过CDN引入,提供丰富的矢量图标。
系统使用的主要图标:
fa-dumbbell-- 品牌图标fa-home-- 首页fa-chart-pie-- 仪表盘fa-chart-bar-- 数据分析fa-users-- 会员管理fa-running-- 锻炼记录fa-calendar-check-- 课程管理fa-file-import/export-- 数据导入导出fa-user-shield-- 用户管理fa-database-- 数据备份
8.5 字体
Google Fonts -- Noto Sans SC -- 思源黑体,支持300-700多种字重,用于中文显示。
8.6 页面布局
采用左侧固定导航栏 + 右侧主内容区的经典后台布局:
-
侧边栏(Sidebar):
- 品牌区域(图标 + 系统名称)
- 导航菜单分组:主菜单 / 业务管理 / 数据工具 / 系统管理
- 用户信息区(头像、用户名、角色标签、下拉菜单)
- 移动端可折叠
- 菜单可见性: "业务管理"(会员管理、锻炼记录、课程管理)和"数据工具"仅管理员可见;"系统管理"仅超级管理员可见
-
主内容区:
- 顶部粘性Header栏(面包屑 + 用户标签 + 头像)
- Flash消息区域(自动4秒消失)
- 页面内容(各模板block)
8.7 模板文件清单
| 模板路径 | 说明 |
|---|---|
base.html |
基础布局(侧边栏 + Header + 内容区) |
main/index.html |
系统首页 |
main/dashboard.html |
数据仪表盘 |
main/analytics.html |
深度数据分析 |
main/members.html |
会员列表 |
main/member_detail.html |
会员详情 |
main/edit_member.html |
编辑/添加会员 |
main/new_member.html |
新增会员 |
main/member_analytics.html |
会员个人分析 |
main/sessions.html |
锻炼记录列表 |
main/session_detail.html |
锻炼记录详情 |
main/edit_session.html |
编辑锻炼记录 |
main/new_session.html |
新增锻炼记录 |
main/add_workout_record.html |
添加健身记录 |
main/workout_stats.html |
锻炼统计 |
main/workout_plans.html |
锻炼计划列表 |
main/workout_plan_detail.html |
锻炼计划详情 |
main/workout_plan_form.html |
锻炼计划表单 |
main/workout_plan_exercise_form.html |
计划项目表单 |
main/courses.html |
课程列表 |
main/course_detail.html |
课程详情 |
main/add_course.html |
添加课程 |
main/edit_course.html |
编辑课程 |
main/course_schedules.html |
课程安排列表 |
main/add_course_schedule.html |
添加课程安排 |
main/enroll_course.html |
课程报名 |
main/course_enrollments.html |
报名记录列表 |
main/import_data.html |
数据导入 |
main/export_data.html |
数据导出 |
main/backup_data.html |
数据备份 |
main/backup_list.html |
备份列表 |
auth/login.html |
登录页面 |
auth/register.html |
注册页面 |
auth/profile.html |
个人资料 |
auth/edit_profile.html |
编辑资料 |
auth/change_password.html |
修改密码 |
auth/reset_password_request.html |
请求重置密码 |
auth/reset_password.html |
重置密码 |
auth/users.html |
用户管理列表 |
auth/user.html |
用户详情 |
auth/edit_user.html |
编辑用户 |
email/confirm.html |
邮箱确认邮件(HTML) |
email/confirm.txt |
邮箱确认邮件(纯文本) |
errors/404.html |
404错误页面 |
9. 权限系统
9.1 RBAC模型
系统采用基于角色的访问控制(Role-Based Access Control, RBAC)模型:
用户(User) --[多对一]--> 角色(Role) --[包含]--> 权限(Permission)
9.2 权限位掩码机制
使用整数位运算存储和检查权限,每个权限占一个二进制位:
权限值 二进制 含义
1 0000001 VIEW - 查看
2 0000010 WRITE - 写入
4 0000100 MODERATE - 管理
8 0001000 ADMIN - 管理员
16 0010000 IMPORT - 导入
32 0100000 EXPORT - 导出
64 1000000 BACKUP - 备份
检查权限:(permissions & permission) == permission
添加权限:permissions += permission
9.3 角色权限矩阵
| 角色 | VIEW | WRITE | MODERATE | ADMIN | IMPORT | EXPORT | BACKUP | 总值 |
|---|---|---|---|---|---|---|---|---|
| 普通用户 | Y | - | - | - | - | - | - | 1 |
| 管理员 | Y | Y | Y | Y | Y | Y | Y | 127 |
9.4 权限装饰器
定义在 app/decorators.py 中:
| 装饰器 | 检查权限 | 说明 |
|---|---|---|
@permission_required(permission) |
自定义权限 | 通用权限检查 |
@admin_required |
Permission.ADMIN | 管理员权限 |
@import_required |
Permission.IMPORT | 导入权限 |
@export_required |
Permission.EXPORT | 导出权限 |
@backup_required |
Permission.BACKUP | 备份权限 |
装饰器工作流程:
- 检查
current_user.can(permission) - 如果无权限,flash错误消息并重定向到首页
- 如果有权限,执行被装饰的函数
9.5 页面级权限控制
在模板中通过 Jinja2 条件判断控制导航菜单和操作按钮显示:
jinja2
{% if current_user.is_admin() %}
<!-- 业务管理菜单(会员管理、锻炼记录、课程管理) -->
<!-- 数据工具菜单(数据导入、数据导出) -->
<!-- 首页"添加会员""添加记录"按钮、快速操作面板 -->
{% endif %}
{% if current_user.is_administrator %}
<!-- 系统管理菜单(用户管理、数据备份、角色管理) -->
{% endif %}
9.6 双重权限标识
系统同时使用两种管理员标识:
User.is_administrator-- Boolean字段,快捷标记User.role.name == '管理员'-- 基于角色的权限系统
在不同场景下混合使用,如 current_user.is_admin() 检查布尔字段,current_user.can(Permission.WRITE) 检查角色权限位。
10. API接口
10.1 认证机制
API采用JWT(JSON Web Token)认证:
- 客户端通过
/api/auth/login发送用户名和密码 - 服务端验证后返回JWT令牌
- 客户端在后续请求的Header中携带令牌:
Authorization: Bearer <token> - 令牌默认1小时过期
- 可通过
/api/auth/refresh刷新令牌
10.2 通用响应格式
成功响应:
json
{
"id": 1,
"username": "admin",
"email": "admin@gym.com"
}
分页响应:
json
{
"members": [...],
"total": 100,
"pages": 5,
"current_page": 1,
"per_page": 20
}
错误响应:
json
{
"error": "Bad Request",
"message": "缺少必需字段: name"
}
10.3 健康检查端点
GET /health
返回:
json
{
"status": "healthy",
"service": "健身房数据分析系统",
"version": "1.0.0"
}
11. 数据导入与导出
11.1 CSV数据导入
支持两种数据类型的CSV导入:
会员数据(members):
- 必填字段:name, gender, phone
- 可选字段:email, height, weight, bmi
- 最大10000行
- 数值字段自动验证格式
锻炼记录(sessions):
- 必填字段:member_id, date, exercise_type, duration, calories_burned
- 可选字段:avg_heart_rate, max_heart_rate, notes
- 验证member_id存在性
- 导入时自动记录Activity日志
11.2 CSV数据导出
支持导出为CSV文件(UTF-8 with BOM编码,兼容Excel中文显示):
- 会员数据导出: name, gender, age, phone, email, height, weight, bmi, fat_percentage, workout_frequency, experience_level, created_at
- 锻炼记录导出: member_id, member_name, date, exercise_type, duration, calories_burned, avg_heart_rate, max_heart_rate, notes, created_at
11.3 批量数据导入脚本
import_data.py 脚本用于从 data/gym_members_data.csv 批量导入数据:
- 自动检测并添加新字段到已有表(ALTER TABLE)
- 确保数据库表存在(db.create_all())
- 读取CSV文件
- 为每条记录生成随机中文姓名和手机号
- 每个会员生成3-8条锻炼记录(日期分布在90天内)
- 每100条提交一次
12. 错误处理
12.1 全局HTTP错误处理
定义在 app/errors/handlers.py 中:
| HTTP状态码 | 处理函数 | 模板 | 说明 |
|---|---|---|---|
| 400 | bad_request_error() |
errors/400.html |
请求错误 |
| 403 | forbidden_error() |
errors/403.html |
访问被拒绝 |
| 404 | not_found_error() |
errors/404.html |
页面未找到 |
| 500 | internal_error() |
errors/500.html |
服务器内部错误(自动回滚数据库事务) |
12.2 API错误处理
app/api/errors.py 提供JSON格式的错误响应:
bad_request(message)-- 400unauthorized(message)-- 401forbidden(message)-- 403not_found(message)-- 404internal_server_error(message)-- 500
12.3 日志系统
- 开发环境:RotatingFileHandler 写入
logs/gym_analysis.log(10MB轮转,10个备份) - 生产环境:额外配置 SMTPHandler 发送错误邮件给管理员
- 日志格式:
%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]
13. 部署说明
13.1 环境要求
- Python 3.8+
- MySQL 8.0+(或 SQLite 用于开发/测试)
- Redis(生产环境缓存,可选)
13.2 安装步骤
bash
# 1. 克隆项目
cd F:\code\116-基于Flask的健身房会员锻炼数据可视化分析系统\code
# 2. 创建虚拟环境
python -m venv venv
source venv/bin/activate # Linux/Mac
# 或 venv\Scripts\activate # Windows
# 3. 安装依赖
pip install -r requirements.txt
# 4. 配置环境变量(可选)
export FLASK_CONFIG=development
export SECRET_KEY=your-secret-key
export DATABASE_URL=mysql://user:password@localhost:3306/design_116_exercise
# 5. 初始化数据库
flask init-db
# 6. 导入测试数据(可选)
flask import-data
# 或使用独立脚本
python import_data.py
# 7. 启动开发服务器
python run.py
# 或
flask run --host=0.0.0.0 --port=5000
13.3 数据库初始化
flask init-db 命令执行以下操作:
- 创建所有数据库表
- 创建默认角色(管理员、普通用户)
- 创建默认管理员账户:admin / admin123
- 创建默认普通用户账户:user / user123
13.4 生产环境部署
bash
# 使用 Gunicorn
gunicorn -w 4 -b 0.0.0.0:5000 "app:create_app('production')"
# 设置环境变量
export FLASK_CONFIG=production
export SECRET_KEY=<强随机密钥>
export DATABASE_URL=mysql+pymysql://user:password@localhost:3306/design_116_exercise?charset=utf8mb4
export REDIS_URL=redis://localhost:6379/0
export MAIL_SERVER=smtp.example.com
export MAIL_USERNAME=your-email
export MAIL_PASSWORD=your-password
13.5 CLI命令
| 命令 | 说明 |
|---|---|
flask init-db |
初始化数据库、角色和默认用户 |
flask import-data |
从CSV导入健身房数据 |
flask create-fake-data |
创建50个虚拟会员和随机锻炼记录 |
flask test [--coverage] |
运行单元测试(可选覆盖率报告) |
13.6 数据库迁移
使用 Flask-Migrate(Alembic)管理数据库架构变更:
bash
# 生成迁移脚本
flask db migrate -m "描述信息"
# 应用迁移
flask db upgrade
# 回滚迁移
flask db downgrade
迁移历史位于 migrations/versions/ 目录。
13.7 默认账户
| 用户名 | 密码 | 角色 | 说明 |
|---|---|---|---|
| admin | admin123 | 管理员 | 系统管理员,拥有所有权限 |
| user | user123 | 普通用户 | 普通用户,仅有查看权限 |
生产环境部署时务必修改默认密码和SECRET_KEY。
附录A: 数据库ER关系图
┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐
│ roles │ │ users │ │ courses │
├──────────────┤ ├──────────────┤ ├──────────────────────┤
│ id (PK) │◄────│ role_id (FK) │ │ id (PK) │
│ name │ │ id (PK) │◄────│ instructor_id (FK) │
│ description │ │ username │ │ name │
│ permissions │ │ email │ │ description │
│ created_at │ │ password_hash│ │ course_type │
│ updated_at │ │ is_active │ │ duration │
└──────────────┘ │ is_admin │ │ max_capacity │
│ real_name │ │ price │
│ phone │ │ created_at │
│ avatar │ │ updated_at │
│ bio │ └──────────┬───────────┘
│ confirmed │ │
│ last_seen │ ┌──────────┴───────────┐
│ created_at │ │ course_schedules │
│ last_login │ ├──────────────────────┤
└──────────────┘ │ id (PK) │
│ course_id (FK) │
┌──────────────┐ ┌──────────────┐ │ start_time │
│ members │ │ gym_members │ │ end_time │
├──────────────┤ ├──────────────┤ │ location │
│ id (PK) │◄────│ member_id(FK)│ │ status │
│ name │ │ id (PK) │ └──────────┬───────────┘
│ gender │ │ membership │ │
│ birth_date │ │ start_date │ ┌──────────┴───────────┐
│ phone │ │ end_date │ │ course_enrollments │
│ email │ │ status │ ├──────────────────────┤
│ height │ └──────┬───────┘ │ id (PK) │
│ weight │ │ │ course_id (FK) │
│ bmi │ ┌──────┴───────┐ │ member_id (FK)───────┤──┐
│ fat_percentage│ │body_measure- │ │ schedule_id (FK) │ │
│ water_intake │ │ ments │ │ status │ │
│ workout_freq │ ├──────────────┤ │ enrollment_date │ │
│ experience │ │ id (PK) │ └──────────────────────┘ │
│ created_at │ │ member_id(FK)│ │
│ updated_at │ │ date │ ┌──────────────────────┐ │
└──────┬───────┘ │ weight │ │ workout_plans │ │
│ │ body_fat │ ├──────────────────────┤ │
│ │ muscle_mass │ │ id (PK) │ │
│ │ chest/waist │ │ name │ │
│ │ hip/biceps │ │ description │ │
│ │ thigh │ │ difficulty │ │
│ └──────────────┘ │ duration │ │
│ └──────────┬───────────┘ │
┌──────┴───────┐ ┌──────────────┐ │ │
│ activities │ │exercise_ │ ┌──────────┴───────────┐ │
├──────────────┤ │ sessions │ │workout_plan_exercises │ │
│ id (PK) │ ├──────────────┤ ├──────────────────────┤ │
│ member_id(FK)│ │ id (PK) │ │ id (PK) │ │
│ activity_type│ │ member_id(FK)│ │ plan_id (FK) │ │
│ description │ │ date │ │ exercise_name │ │
│ timestamp │ │ duration │ │ sets/reps/weight │ │
│ created_at │ │ exercise_type│ │ duration/rest_time │ │
└──────────────┘ │ calories │ │ notes/order │ │
│ heart_rate_* │ └──────────────────────┘ │
│ notes │ │
└──────────────┘ ┌──────────────────────┐ │
│ workout_records │ │
┌──────────────┐ ├──────────────────────┤ │
│workout_ │ │ id (PK) │ │
│ details │ │ member_id (FK)───────┤──┘
├──────────────┤ │ workout_date │
│ id (PK) │ │ workout_type │
│ record_id(FK)│◄────│ duration │
│ exercise_name│ │ calories_burned │
│ sets/reps │ │ heart_rate_avg/max │
│ weight/dur │ │ notes │
│ distance │ └──────────────────────┘
└──────────────┘
附录B: 模板文件清单
共 44 个模板文件:
templates/
├── base.html # 基础布局(侧边栏+Header)
├── auth/
│ ├── login.html # 登录页
│ ├── register.html # 注册页
│ ├── profile.html # 个人资料
│ ├── edit_profile.html # 编辑资料
│ ├── change_password.html # 修改密码
│ ├── reset_password_request.html # 请求重置密码
│ ├── reset_password.html # 重置密码
│ ├── users.html # 用户管理列表
│ ├── user.html # 用户详情
│ └── edit_user.html # 编辑用户
├── main/
│ ├── index.html # 系统首页
│ ├── dashboard.html # 数据仪表盘
│ ├── analytics.html # 深度数据分析
│ ├── members.html # 会员列表
│ ├── member_detail.html # 会员详情
│ ├── member_analytics.html # 会员个人分析
│ ├── edit_member.html # 编辑/添加会员
│ ├── new_member.html # 新增会员
│ ├── sessions.html # 锻炼记录列表
│ ├── session_detail.html # 锻炼记录详情
│ ├── edit_session.html # 编辑锻炼记录
│ ├── new_session.html # 新增锻炼记录
│ ├── add_workout_record.html # 添加健身记录
│ ├── workout_stats.html # 锻炼统计
│ ├── workout_plans.html # 锻炼计划列表
│ ├── workout_plan_detail.html # 锻炼计划详情
│ ├── workout_plan_form.html # 锻炼计划表单
│ ├── workout_plan_exercise_form.html # 计划项目表单
│ ├── courses.html # 课程列表
│ ├── course_detail.html # 课程详情
│ ├── add_course.html # 添加课程
│ ├── edit_course.html # 编辑课程
│ ├── course_schedules.html # 课程安排列表
│ ├── add_course_schedule.html # 添加课程安排
│ ├── enroll_course.html # 课程报名
│ ├── course_enrollments.html # 报名记录列表
│ ├── import_data.html # 数据导入
│ ├── export_data.html # 数据导出
│ ├── backup_data.html # 数据备份
│ └── backup_list.html # 备份列表
├── email/
│ ├── confirm.html # 邮箱确认(HTML)
│ └── confirm.txt # 邮箱确认(纯文本)
└── errors/
└── 404.html # 404错误页面
文档版本:1.0.0
生成日期:2026年5月3日
系统名称:健身房会员锻炼数据可视化分析系统