中老年体检数据可视化分析系统 — 技术文档
目录
1. 项目概述
1.1 项目背景
本系统是一个面向中老年群体体检数据的可视化分析平台,基于 Python + Flask + MySQL 构建。系统整合了来自和鲸社区(Heywhale)的公开数据集,包含 4,909 条中老年居民体检记录,涵盖高血压、心脏病、中风等慢性病指标以及血糖、BMI 等基础健康数据。
1.2 核心功能
| |
|---|
| 关键指标卡片、仪表盘、疾病分布、年龄/性别/地域多维分析 |
| 相关性热力图、平行坐标、树图、疾病共现、风险分层等深度分析 |
| 性别对比、年龄趋势、城乡差异、工作类型/婚姻/吸烟影响分析 |
| |
| 个人关注列表、风险评估、健康建议、Markdown 报告导出 |
| |
1.3 目标用户

























2. 系统架构
2.1 整体架构
系统采用经典的四层分层架构:
┌─────────────────────────────────────────────────────────┐│ 浏览器 (Browser) ││ HTML + CSS + Bootstrap + Plotly.js │└──────────────────────────┬──────────────────────────────┘ │ HTTP┌──────────────────────────▼──────────────────────────────┐│ Web 层 (app.py — Flask) ││ 路由处理 · Session 认证 · 请求过滤 · 模板渲染 │├─────────────────────────────────────────────────────────┤│ 业务逻辑层 (analytics.py) ││ 风险评分 · 数据富化 · 统计分析 · 洞察生成 │├─────────────────────────────────────────────────────────┤│ 数据访问层 (database.py) ││ MySQL CRUD · 密码哈希 · CSV 导入 · SQLAlchemy │├─────────────────────────────────────────────────────────┤│ 配置层 (config.py) ││ 数据库连接 · 字段定义 · 选项常量 │├─────────────────────────────────────────────────────────┤│ 数据存储 ││ MySQL (design_132_health) · CSV 文件 │└─────────────────────────────────────────────────────────┘
2.2 数据流
CSV 文件 ──→ import_data.py ──→ MySQL (health_records) │ ▼ database.py (load_health_records) │ ▼ pandas DataFrame │ ┌───────────┼───────────┐ ▼ ▼ ▼ apply_filters enrich_records statistics │ │ │ └───────────┼───────────┘ ▼ JSON 数据 (tojson) │ ▼ Jinja2 模板渲染 HTML │ ▼ Plotly.js 客户端图表渲染
2.3 请求处理流程
- Flask 路由匹配,装饰器检查 Session 认证
- 从 MySQL 加载全量数据到 pandas DataFrame
- 通过 Jinja2 模板引擎渲染 HTML,数据以 JSON 形式嵌入
3. 技术栈
3.1 后端
3.2 前端
3.3 数据库
| |
|---|
| 主数据库,库名 design_132_health |
| |
4. 目录结构
health/├── app.py # Flask 主应用(1,117 行)├── analytics.py # 数据分析引擎(251 行)├── database.py # 数据库访问层(449 行)├── config.py # 全局配置(80 行)├── import_data.py # CLI 数据导入工具(39 行)├── requirements.txt # Python 依赖清单├── README.md # 项目说明│├── data/│ ├── lf44k151_lnrjk.csv # 主数据集(4,909 条)│ ├── 居民体检糖尿病及心血管健康指标数据集_readme.md # 数据集说明│ └── 参考链接.url # 数据来源链接│├── static/│ ├── css/│ │ └── bootstrap.min.css # Bootstrap 暗色主题│ ├── js/│ │ ├── bootstrap.bundle.min.js # Bootstrap JS│ │ └── plotly-2.35.2.min.js # Plotly.js 图表库│ └── style.css # 自定义样式(774 行)│└── templates/ ├── base.html # 基础布局(侧边栏 + 内容区) ├── login.html # 登录/注册页 ├── dashboard.html # 数据总览页 ├── analysis.html # 规律分析页 ├── trend.html # 趋势分析页 ├── manage.html # 数据管理页(管理员) ├── focus.html # 重点关注页 └── profile.html # 个人中心页
5. 数据库设计
5.1 ER 关系图
┌──────────────┐ ┌───────────────────┐ ┌──────────────────┐│ users │ │ tracked_people │ │ health_records │├──────────────┤ ├───────────────────┤ ├──────────────────┤│ id (PK) │──┐ │ id (PK) │ ┌──│ id (PK) ││ username │ │ │ user_id (FK) │────┘ │ addtime ││ password_hash │ └───│ record_id (FK) │───────│ xingbie ││ display_name │ │ attention_level │ │ nianling ││ email │ │ note │ │ gxy, xzb, sfzf ││ phone │ │ created_at │ │ xtsp, bmi ││ role │ │ updated_at │ │ ... (其他字段) ││ created_at │ └───────────────────┘ └──────────────────┘│ updated_at │└──────────────┘
5.2 表结构定义
5.2.1 users 表(用户表)
| | | |
|---|
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | DEFAULT CURRENT_TIMESTAMP | |
| | ON UPDATE CURRENT_TIMESTAMP | |
5.2.2 health_records 表(体检记录表)
索引:
5.2.3 tracked_people 表(关注人员表)
| | | |
|---|
| | | |
| | | |
| | FK → health_records.id, CASCADE | |
| | | |
| | | |
| | DEFAULT CURRENT_TIMESTAMP | |
| | ON UPDATE CURRENT_TIMESTAMP | |
唯一约束:(user_id, record_id) — 同一用户对同一记录只能关注一次
5.3 自动初始化机制
系统首次启动时自动执行以下流程(bootstrap_database()):
- 执行
CREATE TABLE IF NOT EXISTS 创建三张表 - 若
users 表为空,插入默认管理员(admin / 123456) - 若
health_records 表为空且 CSV 文件存在,自动导入全部数据
6. 核心模块详解
6.1 config.py — 配置模块
职责: 集中管理所有静态配置项。
关键配置项:
# 数据库连接(支持环境变量覆盖)DB_CONFIG = { "host": os.getenv("HEALTH_DB_HOST", "localhost"), "port": int(os.getenv("HEALTH_DB_PORT", "3306")), "user": os.getenv("HEALTH_DB_USER", "root"), "password": os.getenv("HEALTH_DB_PASSWORD", "123456"), "database": os.getenv("HEALTH_DB_NAME", "design_132_health"), "charset": "utf8mb4",}# 22 个标准字段名HEALTH_COLUMNS = ["id", "addtime", "xingbie", "nianling", "gxy", "xzb", "hunyin", "gzxz", "zzxz", "xtsp", "bmi", "xyzk", "sfzf", "yearin", "monthin", "dayin", "v_bthyr", "v_bthmon", "type", "prov", "residenc", "trueage"]# 中文标签映射COLUMN_LABELS = {"id": "编号", "addtime": "录入时间", "xingbie": "性别", ...}# 下拉选项GENDER_OPTIONS = ["男性", "女性"]WORK_OPTIONS = ["私人企业", "自雇人士", "政府部门", "儿童"]AREA_OPTIONS = ["城市", "农村"]SMOKE_OPTIONS = ["从不吸烟", "以前吸烟", "吸烟", "不详"]ATTENTION_LEVELS = ["一般关注", "重点关注", "高危随访"]
环境变量支持:
6.2 database.py — 数据访问层
职责: 封装所有 MySQL 交互操作,提供统一的数据访问接口。
密码安全:
PASSWORD_ITERATIONS = 180_000 # PBKDF2 迭代次数def hash_password(password, salt=None, iterations=180_000): # 格式: pbkdf2_sha256$iterations$salt{iterations}{digest}"def verify_password(password, encoded): # 使用 hmac.compare_digest 防止时序攻击 ...
核心函数列表:
| | | |
|---|
init_schema() | | | |
ensure_database() | | | |
seed_default_admin() | | | |
create_user() | username, password, display_name, email, phone | | |
authenticate_user() | | | |
hash_password() | password, salt, iterations | | |
verify_password() | | | |
update_user_profile() | user_id, display_name, email, phone | | |
change_password() | user_id, old_password, new_password | | |
load_health_records() | | | |
get_record() | | | |
health_record_count() | | | |
insert_health_record() | | | |
upsert_health_dataframe() | | | |
delete_health_records() | | | |
import_csv_to_mysql() | | | |
add_favorite() | user_id, record_id, level, note | | |
update_favorite() | user_id, record_id, level, note | | |
remove_favorites() | | | |
load_favorites() | | | |
数据类型处理:
INTEGER_COLUMNS = {"id", "nianling", "gxy", "xzb", "hunyin", "sfzf", "yearin", "monthin", "dayin", "v_bthyr", "v_bthmon", "type", "prov", "residenc", "trueage"}FLOAT_COLUMNS = {"xtsp", "bmi"}
所有数据写入前经过 _safe_value() 函数进行类型转换和空值处理。
连接管理:
- 使用 PyMySQL 直连(
_connect())执行 DDL 和简单 CRUD - 使用 SQLAlchemy Engine(
get_engine())配合 pandas 执行复杂查询 - 启用
pool_pre_ping=True 和 pool_recycle=3600 防止连接超时
6.3 analytics.py — 数据分析引擎
职责: 负责数据富化、风险评分、统计分析和洞察生成。
6.3.1 风险评分算法(compute_risk_score)
综合多维健康指标,计算 0-100 分的风险评分:
风险等级划分:
6.3.2 数据富化(enrich_records)
在原始数据基础上添加计算列:
| | |
|---|
| | <=44 / 45-59 / 60-69 / 70-79 / >=80 |
| | <18.5 偏瘦 / 18.5-24 正常 / 24-28 超重 / >=28 肥胖 |
| | <100 正常 / 100-126 偏高 / >=126 糖尿病风险 |
| | |
| | |
| | |
| hunyin.map(MARRIAGE_LABELS) | |
| residenc.map(RESIDENCE_LABELS) | |
| | |
| | |
6.3.3 过滤引擎(apply_filters)
支持多条件组合过滤:
6.3.4 统计分析函数
| | |
|---|
disease_rate_frame() | | |
group_disease_rates() | | |
disease_cooccurrence() | | |
age_group_stats() | | |
gender_comparison() | | |
risk_distribution() | | |
generate_insights() | | |
6.3.5 洞察生成(generate_insights)
自动分析数据并生成 5 类文字洞察:
- 样本概览:
- 主要风险:
- 年龄分析:
- 吸烟影响:
- 综合风险:
- 相关性分析:
6.4 app.py — Flask Web 应用
职责: 路由处理、请求过滤、Session 管理、模板渲染。
Flask 配置:
app = Flask(__name__)app.secret_key = "health-analytics-secret-key-2024"app.config["SESSION_TYPE"] = "filesystem"
中间件:
@app.after_requestdef no_cache(response): # 禁止浏览器缓存 HTML 页面 response.headers["Cache-Control"] = "no-store, no-cache, must-revalidate, max-age=0" response.headers["Pragma"] = "no-cache" return response
认证装饰器:
@login_required — 检查 session["user"] 是否存在,未登录则跳转 /login@admin_required — 在 login_required 基础上检查 role == "admin",非管理员重定向到 dashboard
7. 路由与接口设计
7.1 页面路由
| | | | |
|---|
/ | | | index() | |
/login | | | login() | |
/register | | | register() | |
/logout | | | logout() | |
/dashboard | | | dashboard() | |
/analysis | | | analysis() | |
/trend | | | trend() | |
/manage | | | manage() | |
/focus | | | focus() | |
/profile | | | profile() | |
7.2 API 接口
7.2.1 记录管理(管理员)
| | | |
|---|
/api/records | | | Form: addtime, xingbie, nianling, gxy, xzb, hunyin, gzxz, zzxz, xtsp, bmi, xyzk, sfzf |
/api/records/<id> | | | |
/api/records/<id> | | | |
/api/import | | | |
7.2.2 关注管理(登录用户)
| | | |
|---|
/api/favorites | | | Form: record_id, attention_level, note |
/api/favorites/<id> | | | JSON: attention_level, note |
/api/favorites/<id> | | | |
7.2.3 用户管理
| | | |
|---|
/api/profile | | | Form: display_name, email, phone |
/api/password | | | Form: old_password, new_password, new_password_confirm |
7.2.4 数据导出
| | | |
|---|
/api/export | | | |
/api/report/<id> | | | |
7.3 通用查询参数
所有数据分析页面(dashboard、analysis、trend、manage)支持以下 URL 查询参数进行数据过滤:
| | | |
|---|
| | | age_min=60 |
| | | age_max=80 |
| | | gender=男性 |
| | | area=城市 |
| | | disease=高血压 |
| | | smoke=吸烟 |
| | | keyword=私人企业 |
| | | page=2 |
8. 前端页面设计
8.1 基础布局(base.html)
┌─────────────────────────────────────────────────────────────┐│ ┌──────────┐ ┌────────────────────────────────────────────┐ ││ │ │ │ │ ││ │ Logo │ │ 主内容区域 │ ││ │ │ │ │ ││ │ 用户卡片 │ │ (各页面通过 block content 填充) │ ││ │ │ │ │ ││ │ 导航菜单 │ │ │ ││ │ ·总览 │ │ │ ││ │ ·分析 │ │ │ ││ │ ·趋势 │ │ │ ││ │ ·管理 │ │ │ ││ │ ·关注 │ │ │ ││ │ ·个人中心 │ │ │ ││ │ │ │ │ ││ │ 退出登录 │ │ │ ││ │ │ │ │ ││ │ 版本信息 │ │ │ ││ └──────────┘ └────────────────────────────────────────────┘ ││ 260px 固定 自适应宽度 │└─────────────────────────────────────────────────────────────┘
全局 JavaScript:
// Plotly 暗色主题配置const DARK_LAYOUT = { paper_bgcolor: 'rgba(0,0,0,0)', plot_bgcolor: 'rgba(0,0,0,0)', font: { color: '#e0e0e0' }, ...};// 通用图表渲染函数function plotChart(divId, data, layout = {}, config = {}) { Plotly.newPlot(divId, data, {...DARK_LAYOUT, ...layout}, config);}
8.2 登录页(login.html)
- 注册表单:用户名、显示名、邮箱、手机号、密码、确认密码
8.3 数据总览页(dashboard.html)
布局:
┌─────────────────────────────────────────────┐│ 顶部过滤条件栏 ││ [年龄范围] [性别] [城乡] [疾病] [吸烟] [搜索] │├─────────────────────────────────────────────┤│ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ││ │总数 │ │平均 │ │平均 │ │平均 │ │高风险│ ││ │ │ │年龄 │ │血糖 │ │BMI │ │比例 │ ││ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ │├─────────────────────────────────────────────┤│ ┌──────────────┐ ┌──────────────────────┐ ││ │ 健康指数仪表盘 │ │ 疾病患病率柱状图 │ ││ └──────────────┘ └──────────────────────┘ ││ ┌──────────────┐ ┌──────────────────────┐ ││ │ 年龄分布直方图 │ │ 风险等级饼图 │ ││ └──────────────┘ └──────────────────────┘ ││ ┌──────────────┐ ┌──────────────────────┐ ││ │ BMI-血糖散点图 │ │ 血糖箱线图 │ ││ └──────────────┘ └──────────────────────┘ ││ ┌──────────────┐ ┌──────────────────────┐ ││ │ BMI分层柱状图 │ │ 城乡疾病对比 │ ││ └──────────────┘ └──────────────────────┘ ││ ┌──────────────┐ ┌──────────────────────┐ ││ │ 吸烟健康影响 │ │ 婚姻状态雷达图 │ ││ └──────────────┘ └──────────────────────┘ ││ ┌──────────────┐ ┌──────────────────────┐ ││ │ 工作类型疾病 │ │ 血糖/BMI分布直方图 │ ││ └──────────────┘ └──────────────────────┘ │└─────────────────────────────────────────────┘
包含图表类型: 仪表盘、柱状图、直方图、饼图、散点图、箱线图、雷达图(共 12+ 种)
8.4 规律分析页(analysis.html)
特色功能:
- 健康雷达图(四维:年龄健康、代谢指标、生活方式、疾病风险)
- 平行坐标图(年龄、血糖、BMI、风险评分的多维关联)
- 相关性热力图(6 个数值字段的 Pearson 相关系数矩阵)
- 疾病组合分析(高血压 + 心脏病 + 中风的排列组合统计)
8.5 趋势分析页(trend.html)
分析维度:
8.6 数据管理页(manage.html)
仅管理员访问,三个标签页:
- 编辑数据:
- 新增记录:
- 导入数据:
8.7 重点关注页(focus.html)
功能:
- 添加关注(输入记录 ID、选择关注级别、填写备注)
8.8 个人中心页(profile.html)
9. 数据可视化方案
9.1 技术实现
所有图表采用 Plotly.js 在客户端渲染。数据传递流程:
Python (pandas) → dict/list → Jinja2 tojson → JavaScript JSON.parse → Plotly.js 渲染
模板中的图表渲染模式:
<divid="chart-div"style="height:400px;"></div><script>(function() { var data = {{ chart_data | tojson }}; // 构建 Plotly traces plotChart('chart-div', traces, layout);})();</script>
9.2 图表清单
9.3 暗色主题
所有图表统一使用暗色主题配置:
const DARK_LAYOUT = { paper_bgcolor: 'rgba(0,0,0,0)', plot_bgcolor: 'rgba(0,0,0,0)', font: { color: '#e0e0e0' }, xaxis: { gridcolor: 'rgba(255,255,255,0.1)' }, yaxis: { gridcolor: 'rgba(255,255,255,0.1)' }, margin: { t: 40, r: 20, b: 40, l: 50 },};
10. 安全机制
10.1 认证与授权
| |
|---|
| PBKDF2-SHA256,180,000 次迭代,16 字节随机盐 |
| hmac.compare_digest() |
| Flask Session(filesystem 存储) |
| @login_required |
| 两级角色:user(只读 + 关注)/ admin(完整 CRUD) |
10.2 输入安全
| |
|---|
| |
| _quote_identifier() |
| |
| |
| _safe_value() |
10.3 传输安全
| |
|---|
| HTML 响应设置 no-store, no-cache |
| pbkdf2_sha256$iterations$salt$digest |
10.4 安全注意事项
生产环境部署前需处理以下问题:
app.secret_key- 数据库密码默认为
123456,应修改并使用强密码
11. 部署与运行
11.1 环境要求
11.2 安装步骤
# 1. 克隆/解压项目cd health# 2. 安装 Python 依赖pip install -r requirements.txt# 3. 确保 MySQL 已启动# 默认连接: localhost:3306, 用户 root, 密码 123456# 4. 启动应用python app.py
11.3 启动流程
python app.py 执行后:
- 首次请求
/dashboard 时触发 bootstrap_database() - 自动创建数据库
design_132_health - 自动创建
users、health_records、tracked_people 三张表 - 自动插入默认管理员(admin / 123456)
11.4 手动数据导入
# 使用 CLI 工具导入python import_data.py# 指定 CSV 文件python import_data.py --csv path/to/data.csv# 清空后重新导入python import_data.py --reset
11.5 数据库配置覆盖
通过环境变量覆盖默认数据库配置:
export HEALTH_DB_HOST=192.168.1.100export HEALTH_DB_PORT=3307export HEALTH_DB_USER=health_userexport HEALTH_DB_PASSWORD=strong_password_hereexport HEALTH_DB_NAME=health_dbpython app.py
11.6 默认账号
11.7 访问地址
启动后浏览器访问:http://localhost:5000
12. 数据字典
12.1 字段编码对照表
性别(xingbie)
布尔字段(gxy / xzb / sfzf / hunyin)
城乡属性(zzxz)
居住编码(residenc)
工作性质(gzxz)
吸烟状态(xyzk)
BMI 分层
血糖分层
关注级别(attention_level)
12.2 数据集来源
- 来源:
- 链接: https://www.heywhale.com/mw/dataset/6a05e303c52ca72ef9c28f3a/content
- 构成: 脑中风预测数据集 + 中国中老年健康调查数据集合并
- 样本量:
- 年龄范围:
13. 已知问题与改进建议
13.1 已知问题
| | | |
|---|
| | | README 写的是 Streamlit,实际是 Flask |
| requirements.txt 包含未使用的 streamlit | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
13.2 改进建议
| |
|---|
| 添加 Flask-WTF 实现 CSRF 保护;引入 Flask-Limiter 实现登录限流;使用 bcrypt 替代 PBKDF2 |
| 引入 Redis 缓存热门查询结果;大数据量场景使用服务端分页;异步加载图表 |
| 添加数据导出为 Excel 格式;增加数据对比功能(选择两条记录对比);添加定时报告推送 |
| 使用 Gunicorn + Nginx 部署;Docker 容器化;CI/CD 自动化 |
| 拆分 app.py 为 Blueprint 模块;引入 Marshmallow 进行请求参数校验;添加单元测试 |
| 引入 Vue.js/React 实现 SPA;添加图表下载为图片功能;移动端适配优化 |
文档版本:v1.0生成日期:2026-05-22基于源码版本:当前工作目录