中老年体检数据可视化分析系统 --- 技术文档
目录
1. 项目概述
1.1 项目背景
本系统是一个面向中老年群体体检数据的可视化分析平台,基于 Python + Flask + MySQL 构建。系统整合了来自和鲸社区(Heywhale)的公开数据集,包含 4,909 条中老年居民体检记录,涵盖高血压、心脏病、中风等慢性病指标以及血糖、BMI 等基础健康数据。
1.2 核心功能
| 功能模块 | 说明 |
|---|---|
| 数据总览 | 关键指标卡片、仪表盘、疾病分布、年龄/性别/地域多维分析 |
| 规律分析 | 相关性热力图、平行坐标、树图、疾病共现、风险分层等深度分析 |
| 趋势分析 | 性别对比、年龄趋势、城乡差异、工作类型/婚姻/吸烟影响分析 |
| 数据管理 | 管理员 CRUD、CSV 批量导入、分页浏览 |
| 重点关注 | 个人关注列表、风险评估、健康建议、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 请求处理流程
- 用户发起 HTTP 请求
- Flask 路由匹配,装饰器检查 Session 认证
- 从 MySQL 加载全量数据到 pandas DataFrame
- 根据 URL 查询参数应用过滤条件
- 对过滤后数据进行富化(风险评分、分层标注)
- 计算统计指标、生成图表数据
- 通过 Jinja2 模板引擎渲染 HTML,数据以 JSON 形式嵌入
- 浏览器加载 Plotly.js 渲染交互式图表
3. 技术栈
3.1 后端
| 技术 | 版本要求 | 用途 |
|---|---|---|
| Python | >= 3.8 | 运行环境 |
| Flask | 内置(无版本约束) | Web 框架 |
| pandas | >= 2.0 | 数据处理与分析 |
| PyMySQL | >= 1.1 | MySQL 数据库驱动 |
| SQLAlchemy | >= 2.0 | 数据库引擎(pandas SQL 操作) |
3.2 前端
| 技术 | 版本 | 用途 |
|---|---|---|
| Bootstrap | 暗色主题 | UI 组件库与响应式布局 |
| Plotly.js | 2.35.2 | 客户端交互式图表 |
| Jinja2 | Flask 内置 | 模板引擎 |
| 自定义 CSS | --- | 774 行暗色主题样式 |
3.3 数据库
| 技术 | 说明 |
|---|---|
| MySQL | 主数据库,库名 design_132_health |
| 字符集 | utf8mb4(支持完整 Unicode) |
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 表(用户表)
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
| id | BIGINT | PK, AUTO_INCREMENT | 用户 ID |
| username | VARCHAR(64) | NOT NULL, UNIQUE | 用户名 |
| password_hash | VARCHAR(255) | NOT NULL | 密码哈希值 |
| display_name | VARCHAR(80) | NULL | 显示名称 |
| VARCHAR(120) | NULL | 邮箱 | |
| phone | VARCHAR(40) | NULL | 手机号 |
| role | VARCHAR(20) | DEFAULT 'user' | 角色:user / admin |
| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | 创建时间 |
| updated_at | DATETIME | ON UPDATE CURRENT_TIMESTAMP | 更新时间 |
5.2.2 health_records 表(体检记录表)
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
| id | BIGINT | PK, AUTO_INCREMENT | 记录 ID |
| addtime | DATETIME | NULL | 录入时间 |
| xingbie | VARCHAR(20) | NULL | 性别(男性/女性) |
| nianling | INT | NULL | 年龄 |
| gxy | TINYINT | NULL | 高血压(0/1) |
| xzb | TINYINT | NULL | 心脏病(0/1) |
| hunyin | TINYINT | NULL | 婚姻状态(0=未婚/1=已婚) |
| gzxz | VARCHAR(80) | NULL | 工作性质 |
| zzxz | VARCHAR(40) | NULL | 城乡属性(城市/农村) |
| xtsp | DOUBLE | NULL | 血糖指标(mg/dL) |
| bmi | DOUBLE | NULL | BMI 指数 |
| xyzk | VARCHAR(40) | NULL | 吸烟状态 |
| sfzf | TINYINT | NULL | 是否中风(0/1) |
| yearin | INT | NULL | 调查年份 |
| monthin | INT | NULL | 调查月份 |
| dayin | INT | NULL | 调查日 |
| v_bthyr | INT | NULL | 出生年 |
| v_bthmon | INT | NULL | 出生月 |
| type | INT | NULL | 样本类型 |
| prov | INT | NULL | 省份编码 |
| residenc | INT | NULL | 居住编码(1=中心城区/2=县城乡镇/3=村庄) |
| trueage | INT | NULL | 真实年龄 |
索引:
| 索引名 | 字段 | 类型 |
|---|---|---|
| idx_age | nianling | 普通索引 |
| idx_gender | xingbie | 普通索引 |
| idx_area | zzxz | 普通索引 |
| idx_disease | gxy, xzb, sfzf | 复合索引 |
5.2.3 tracked_people 表(关注人员表)
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
| id | BIGINT | PK, AUTO_INCREMENT | 记录 ID |
| user_id | BIGINT | FK → users.id, CASCADE | 用户 ID |
| record_id | BIGINT | FK → health_records.id, CASCADE | 体检记录 ID |
| attention_level | VARCHAR(20) | DEFAULT '重点关注' | 关注级别 |
| note | TEXT | NULL | 备注 |
| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | 创建时间 |
| updated_at | DATETIME | ON UPDATE CURRENT_TIMESTAMP | 更新时间 |
唯一约束: (user_id, record_id) --- 同一用户对同一记录只能关注一次
5.3 自动初始化机制
系统首次启动时自动执行以下流程(bootstrap_database()):
- 检测 MySQL 连接,若数据库不存在则自动创建
- 执行
CREATE TABLE IF NOT EXISTS创建三张表 - 若
users表为空,插入默认管理员(admin / 123456) - 若
health_records表为空且 CSV 文件存在,自动导入全部数据
6. 核心模块详解
6.1 config.py --- 配置模块
职责: 集中管理所有静态配置项。
关键配置项:
python
# 数据库连接(支持环境变量覆盖)
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 = ["一般关注", "重点关注", "高危随访"]
环境变量支持:
| 环境变量 | 对应配置 | 默认值 |
|---|---|---|
| HEALTH_DB_HOST | 数据库主机 | localhost |
| HEALTH_DB_PORT | 数据库端口 | 3306 |
| HEALTH_DB_USER | 数据库用户 | root |
| HEALTH_DB_PASSWORD | 数据库密码 | 123456 |
| HEALTH_DB_NAME | 数据库名称 | design_132_health |
6.2 database.py --- 数据访问层
职责: 封装所有 MySQL 交互操作,提供统一的数据访问接口。
密码安全:
python
PASSWORD_ITERATIONS = 180_000 # PBKDF2 迭代次数
def hash_password(password, salt=None, iterations=180_000):
# 格式: pbkdf2_sha256$iterations$salt$digest
salt = salt or secrets.token_hex(16)
digest = hashlib.pbkdf2_hmac("sha256", password.encode(), salt.encode(), iterations).hex()
return f"pbkdf2_sha256${iterations}${salt}${digest}"
def verify_password(password, encoded):
# 使用 hmac.compare_digest 防止时序攻击
...
核心函数列表:
| 函数 | 参数 | 返回值 | 说明 |
|---|---|---|---|
init_schema() |
无 | None | 初始化数据库、表结构、默认管理员 |
ensure_database() |
无 | None | 确保数据库存在 |
seed_default_admin() |
无 | None | 插入默认管理员 |
create_user() |
username, password, display_name, email, phone | (bool, str) | 注册新用户 |
authenticate_user() |
username, password | dict or None | 验证用户凭据 |
hash_password() |
password, salt, iterations | str | 生成密码哈希 |
verify_password() |
password, encoded | bool | 验证密码 |
update_user_profile() |
user_id, display_name, email, phone | None | 更新用户资料 |
change_password() |
user_id, old_password, new_password | (bool, str) | 修改密码 |
load_health_records() |
无 | DataFrame | 加载全部体检记录 |
get_record() |
record_id | dict or None | 获取单条记录 |
health_record_count() |
无 | int | 统计记录总数 |
insert_health_record() |
data: dict | int | 插入单条记录,返回 ID |
upsert_health_dataframe() |
df: DataFrame | int | 批量插入/更新记录 |
delete_health_records() |
record_ids: list | int | 批量删除记录 |
import_csv_to_mysql() |
csv_path, replace | int | 导入 CSV 到 MySQL |
add_favorite() |
user_id, record_id, level, note | None | 添加关注 |
update_favorite() |
user_id, record_id, level, note | None | 更新关注 |
remove_favorites() |
user_id, record_ids | int | 移除关注 |
load_favorites() |
user_id | DataFrame | 加载用户关注列表 |
数据类型处理:
python
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 分的风险评分:
| 因素 | 条件 | 分值 | 最大贡献 |
|---|---|---|---|
| 年龄 | >= 80 岁 | 24 分 | 24 |
| 年龄 | 70-79 岁 | 18 分 | |
| 年龄 | 60-69 岁 | 13 分 | |
| 年龄 | 45-59 岁 | 7 分 | |
| 高血压 | gxy = 1 | 18 分 | 18 |
| 心脏病 | xzb = 1 | 16 分 | 16 |
| 中风 | sfzf = 1 | 20 分 | 20 |
| BMI | >= 28(肥胖) | 12 分 | 12 |
| BMI | 24-27.99(超重) | 6 分 | |
| 血糖 | >= 126(糖尿病风险) | 16 分 | 16 |
| 血糖 | 100-125.99(偏高) | 8 分 | |
| 吸烟 | "吸烟" | 8 分 | 8 |
| 吸烟 | "以前吸烟" | 4 分 |
风险等级划分:
| 等级 | 分数区间 | 标签 |
|---|---|---|
| 低风险 | 0 - 29.9 | 低风险 |
| 中风险 | 30 - 59.9 | 中风险 |
| 高风险 | 60 - 79.9 | 高风险 |
| 重点随访 | 80 - 100 | 重点随访 |
6.3.2 数据富化(enrich_records)
在原始数据基础上添加计算列:
| 新增列 | 计算方式 | 分层规则 |
|---|---|---|
| 年龄段 | pd.cut(nianling) | <=44 / 45-59 / 60-69 / 70-79 / >=80 |
| BMI分层 | pd.cut(bmi) | <18.5 偏瘦 / 18.5-24 正常 / 24-28 超重 / >=28 肥胖 |
| 血糖分层 | pd.cut(xtsp) | <100 正常 / 100-126 偏高 / >=126 糖尿病风险 |
| 高血压状态 | gxy.map(BOOLEAN_LABELS) | 无 / 有 |
| 心脏病状态 | xzb.map(BOOLEAN_LABELS) | 无 / 有 |
| 中风状态 | sfzf.map(BOOLEAN_LABELS) | 无 / 有 |
| 婚姻状态文本 | hunyin.map(MARRIAGE_LABELS) | 未婚 / 已婚 |
| 居住编码文本 | residenc.map(RESIDENCE_LABELS) | 中心城区 / 县城乡镇 / 村庄 |
| 风险评分 | compute_risk_score() | 0-100 连续值 |
| 风险等级 | pd.cut(风险评分) | 低/中/高/重点随访 |
6.3.3 过滤引擎(apply_filters)
支持多条件组合过滤:
| 过滤条件 | 参数类型 | 说明 |
|---|---|---|
| age_range | (min, max) | 年龄范围 |
| gender | str | 性别筛选 |
| area | str | 城乡筛选 |
| disease | str | 疾病筛选(高血压/心脏病/中风) |
| smoke | str | 吸烟状态筛选 |
| keyword | str | 关键字搜索(ID、工作、吸烟状态) |
6.3.4 统计分析函数
| 函数 | 输出 | 用途 |
|---|---|---|
disease_rate_frame() |
五种疾病的患病率 | 概览图表 |
group_disease_rates() |
按分组列统计三种疾病率 | 分组对比图表 |
disease_cooccurrence() |
3x3 疾病共现矩阵 | 相关性分析 |
age_group_stats() |
各年龄段综合统计 | 年龄分析 |
gender_comparison() |
性别对比统计 | 性别分析 |
risk_distribution() |
风险分值分布 | 风险分布图 |
generate_insights() |
自动生成的文字洞察 | 智能分析报告 |
6.3.5 洞察生成(generate_insights)
自动分析数据并生成 5 类文字洞察:
- 样本概览: 总样本量及 60 岁以上占比
- 主要风险: 患病率最高的健康风险
- 年龄分析: 高血压比例最高的年龄段
- 吸烟影响: 中风占比最高的吸烟状态
- 综合风险: 高风险及以上样本占比
- 相关性分析: 与中风关联度最高的指标
6.4 app.py --- Flask Web 应用
职责: 路由处理、请求过滤、Session 管理、模板渲染。
Flask 配置:
python
app = Flask(__name__)
app.secret_key = "health-analytics-secret-key-2024"
app.config["SESSION_TYPE"] = "filesystem"
中间件:
python
@app.after_request
def 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 页面路由
| 路径 | 方法 | 权限 | 处理函数 | 说明 |
|---|---|---|---|---|
/ |
GET | 无 | index() |
重定向到 /dashboard |
/login |
GET, POST | 无 | login() |
登录页面及表单处理 |
/register |
POST | 无 | register() |
用户注册 |
/logout |
GET | 无 | logout() |
清除 Session,退出登录 |
/dashboard |
GET | 登录 | dashboard() |
数据总览页 |
/analysis |
GET | 登录 | analysis() |
规律分析页 |
/trend |
GET | 登录 | trend() |
趋势分析页 |
/manage |
GET | 管理员 | manage() |
数据管理页 |
/focus |
GET | 登录 | focus() |
重点关注页 |
/profile |
GET | 登录 | profile() |
个人中心页 |
7.2 API 接口
7.2.1 记录管理(管理员)
| 路径 | 方法 | 说明 | 请求体 |
|---|---|---|---|
/api/records |
POST | 新增记录 | Form: addtime, xingbie, nianling, gxy, xzb, hunyin, gzxz, zzxz, xtsp, bmi, xyzk, sfzf |
/api/records/<id> |
PUT | 更新记录 | JSON: 待更新字段 |
/api/records/<id> |
DELETE | 删除记录 | 无 |
/api/import |
POST | CSV 导入 | Form: replace (checkbox) |
7.2.2 关注管理(登录用户)
| 路径 | 方法 | 说明 | 请求体 |
|---|---|---|---|
/api/favorites |
POST | 添加关注 | Form: record_id, attention_level, note |
/api/favorites/<id> |
PUT | 更新关注 | JSON: attention_level, note |
/api/favorites/<id> |
DELETE | 取消关注 | 无 |
7.2.3 用户管理
| 路径 | 方法 | 说明 | 请求体 |
|---|---|---|---|
/api/profile |
POST | 更新资料 | Form: display_name, email, phone |
/api/password |
POST | 修改密码 | Form: old_password, new_password, new_password_confirm |
7.2.4 数据导出
| 路径 | 方法 | 说明 | 响应 |
|---|---|---|---|
/api/export |
GET | 导出过滤后数据 | CSV 文件下载 |
/api/report/<id> |
GET | 导出个人健康报告 | Markdown 文件下载 |
7.3 通用查询参数
所有数据分析页面(dashboard、analysis、trend、manage)支持以下 URL 查询参数进行数据过滤:
| 参数 | 类型 | 说明 | 示例 |
|---|---|---|---|
| age_min | int | 最小年龄 | age_min=60 |
| age_max | int | 最大年龄 | age_max=80 |
| gender | str | 性别 | gender=男性 |
| area | str | 城乡 | area=城市 |
| disease | str | 疾病 | disease=高血压 |
| smoke | str | 吸烟状态 | smoke=吸烟 |
| keyword | str | 关键字 | keyword=私人企业 |
| page | int | 页码(仅 manage) | page=2 |
8. 前端页面设计
8.1 基础布局(base.html)
┌─────────────────────────────────────────────────────────────┐
│ ┌──────────┐ ┌────────────────────────────────────────────┐ │
│ │ │ │ │ │
│ │ Logo │ │ 主内容区域 │ │
│ │ │ │ │ │
│ │ 用户卡片 │ │ (各页面通过 block content 填充) │ │
│ │ │ │ │ │
│ │ 导航菜单 │ │ │ │
│ │ ·总览 │ │ │ │
│ │ ·分析 │ │ │ │
│ │ ·趋势 │ │ │ │
│ │ ·管理 │ │ │ │
│ │ ·关注 │ │ │ │
│ │ ·个人中心 │ │ │ │
│ │ │ │ │ │
│ │ 退出登录 │ │ │ │
│ │ │ │ │ │
│ │ 版本信息 │ │ │ │
│ └──────────┘ └────────────────────────────────────────────┘ │
│ 260px 固定 自适应宽度 │
└─────────────────────────────────────────────────────────────┘
全局 JavaScript:
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)
- 独立页面,不继承 base.html
- 动画粒子背景效果
- Bootstrap Tabs 切换登录/注册
- 登录表单:用户名、密码
- 注册表单:用户名、显示名、邮箱、手机号、密码、确认密码
8.3 数据总览页(dashboard.html)
布局:
┌─────────────────────────────────────────────┐
│ 顶部过滤条件栏 │
│ [年龄范围] [性别] [城乡] [疾病] [吸烟] [搜索] │
├─────────────────────────────────────────────┤
│ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │总数 │ │平均 │ │平均 │ │平均 │ │高风险│ │
│ │ │ │年龄 │ │血糖 │ │BMI │ │比例 │ │
│ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ │
├─────────────────────────────────────────────┤
│ ┌──────────────┐ ┌──────────────────────┐ │
│ │ 健康指数仪表盘 │ │ 疾病患病率柱状图 │ │
│ └──────────────┘ └──────────────────────┘ │
│ ┌──────────────┐ ┌──────────────────────┐ │
│ │ 年龄分布直方图 │ │ 风险等级饼图 │ │
│ └──────────────┘ └──────────────────────┘ │
│ ┌──────────────┐ ┌──────────────────────┐ │
│ │ BMI-血糖散点图 │ │ 血糖箱线图 │ │
│ └──────────────┘ └──────────────────────┘ │
│ ┌──────────────┐ ┌──────────────────────┐ │
│ │ BMI分层柱状图 │ │ 城乡疾病对比 │ │
│ └──────────────┘ └──────────────────────┘ │
│ ┌──────────────┐ ┌──────────────────────┐ │
│ │ 吸烟健康影响 │ │ 婚姻状态雷达图 │ │
│ └──────────────┘ └──────────────────────┘ │
│ ┌──────────────┐ ┌──────────────────────┐ │
│ │ 工作类型疾病 │ │ 血糖/BMI分布直方图 │ │
│ └──────────────┘ └──────────────────────┘ │
└─────────────────────────────────────────────┘
包含图表类型: 仪表盘、柱状图、直方图、饼图、散点图、箱线图、雷达图(共 12+ 种)
8.4 规律分析页(analysis.html)
特色功能:
- 自动生成的文字洞察卡片
- 健康雷达图(四维:年龄健康、代谢指标、生活方式、疾病风险)
- 平行坐标图(年龄、血糖、BMI、风险评分的多维关联)
- 相关性热力图(6 个数值字段的 Pearson 相关系数矩阵)
- 树图(风险等级 → 年龄段 → 性别的层级分布)
- 疾病组合分析(高血压 + 心脏病 + 中风的排列组合统计)
- BMI-血糖风险热力图(12 个象限的风险评分)
- 高风险 Top-20 排行表
- CSV 导出按钮
8.5 趋势分析页(trend.html)
分析维度:
- 性别对比(柱状图 + 雷达图)
- 年龄段疾病趋势线
- 风险分值分布
- 城乡差异对比
- 年度趋势(疾病率、指标均值变化)
- 工作类型对比
- 婚姻状态对比
- 吸烟状态详细分析
8.6 数据管理页(manage.html)
仅管理员访问,三个标签页:
- 编辑数据: 分页表格(每页 50 条),支持行内编辑和删除
- 新增记录: 表单录入
- 导入数据: CSV 同步,支持"替换模式"(清空后重新导入)
8.7 重点关注页(focus.html)
功能:
- 添加关注(输入记录 ID、选择关注级别、填写备注)
- 关注列表表格(支持行内修改级别和备注)
- 跟踪详情:风险仪表盘 + 对比柱状图
- 健康信息卡片 + 个性化健康建议
- Markdown 格式健康报告导出
8.8 个人中心页(profile.html)
- 用户信息卡片(头像、姓名、角色、邮箱)
- 资料编辑表单
- 密码修改表单
9. 数据可视化方案
9.1 技术实现
所有图表采用 Plotly.js 在客户端渲染。数据传递流程:
Python (pandas) → dict/list → Jinja2 tojson → JavaScript JSON.parse → Plotly.js 渲染
模板中的图表渲染模式:
html
<div id="chart-div" style="height:400px;"></div>
<script>
(function() {
var data = {{ chart_data | tojson }};
// 构建 Plotly traces
plotChart('chart-div', traces, layout);
})();
</script>
9.2 图表清单
| 页面 | 图表名称 | Plotly 类型 | 数据维度 |
|---|---|---|---|
| Dashboard | 健康指数仪表盘 | Indicator (gauge) | 综合风险评分 |
| Dashboard | 疾病患病率 | Bar | 5 种疾病 |
| Dashboard | 年龄分布直方图 | Histogram | 性别 × 年龄 |
| Dashboard | 风险等级饼图 | Pie | 4 个风险等级 |
| Dashboard | BMI-血糖散点图 | Scatter | BMI × 血糖 × 风险等级 |
| Dashboard | 血糖箱线图 | Box | 中风状态 × 年龄段 × 血糖 |
| Dashboard | BMI 分层柱状图 | Bar | 4 个 BMI 层级 |
| Dashboard | 城乡疾病对比 | Bar | 城乡 × 3 种疾病 |
| Dashboard | 吸烟健康影响 | Bar | 吸烟状态 × 多指标 |
| Dashboard | 婚姻状态雷达 | Scatterpolar | 婚姻 × 多维指标 |
| Dashboard | 工作类型疾病 | Bar | 工作类型 × 疾病率 |
| Dashboard | BMI/血糖分布 | Histogram | 双直方图 |
| Analysis | 健康雷达图 | Scatterpolar | 4 维综合评分 |
| Analysis | 年龄段疾病柱状图 | Bar | 年龄段 × 3 种疾病 |
| Analysis | 平行坐标 | Parcoords | 年龄/血糖/BMI/风险 |
| Analysis | 吸烟-中风柱状图 | Bar | 吸烟状态 × 中风 |
| Analysis | 相关性热力图 | Heatmap | 6×6 相关系数矩阵 |
| Analysis | 树图 | Treemap | 风险→年龄→性别 |
| Analysis | 年龄-性别-疾病 | Grouped Bar | 年龄段 × 性别 × 疾病 |
| Analysis | 疾病组合柱状图 | Bar | 疾病组合 × 人数 |
| Analysis | BMI-血糖风险热力图 | Heatmap | BMI × 血糖 → 风险评分 |
| Analysis | 年龄统计 | Bar | 均值/中位数/标准差 |
| Analysis | 风险分布 | Stacked Bar | 年龄段 × 风险区间 |
| Analysis | 城乡疾病对比 | Grouped Bar | 城乡 × 3 种疾病率 |
| Analysis | 地域指标雷达 | Scatterpolar | 城乡 × 多维指标 |
| Trend | 性别对比柱状图 | Bar | 性别 × 多指标 |
| Trend | 性别雷达图 | Scatterpolar | 性别 × 多维 |
| Trend | 年龄段趋势线 | Scatter | 年龄段 × 疾病率趋势 |
| Trend | 风险分值柱状图 | Bar | 年龄段 × 平均风险 |
| Trend | 血糖/趋势线 | Scatter | 年龄段 × 血糖趋势 |
| Trend | 城乡对比 | Bar | 城乡 × 多指标 |
| Trend | 年度趋势 | Scatter | 年份 × 指标变化 |
| Trend | 工作类型对比 | Bar | 工作类型 × 疾病率 |
| Trend | 婚姻对比 | Bar | 婚姻 × 指标 |
| Trend | 吸烟详细分析 | Bar | 吸烟状态 × 多指标 |
9.3 暗色主题
所有图表统一使用暗色主题配置:
javascript
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 / @admin_required 装饰器 |
| 角色控制 | 两级角色:user(只读 + 关注)/ admin(完整 CRUD) |
10.2 输入安全
| 机制 | 实现方式 |
|---|---|
| SQL 注入防护 | 参数化查询(PyMySQL %s 占位符) |
| 标识符验证 | _quote_identifier() 正则校验 [A-Za-z0-9_]+ |
| 用户名校验 | 正则 [A-Za-z0-9_]{3,32} |
| 密码强度 | 最少 6 位 |
| 数据类型转换 | _safe_value() 统一处理空值和类型 |
10.3 传输安全
| 机制 | 说明 |
|---|---|
| 缓存控制 | HTML 响应设置 no-store, no-cache |
| 密码哈希格式 | pbkdf2_sha256$iterations$salt$digest |
10.4 安全注意事项
生产环境部署前需处理以下问题:
app.secret_key硬编码在源码中,应改为环境变量- 数据库密码默认为
123456,应修改并使用强密码- 默认管理员密码
123456过于简单- 未启用 HTTPS
- 未配置 CSRF Token
- 未设置登录失败锁定机制
- Flask debug 模式不应在生产环境开启
11. 部署与运行
11.1 环境要求
- Python >= 3.8
- MySQL >= 5.7
- pip(Python 包管理器)
11.2 安装步骤
bash
# 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 执行后:
- Flask 启动,监听
0.0.0.0:5000 - 首次请求
/dashboard时触发bootstrap_database() - 自动创建数据库
design_132_health - 自动创建
users、health_records、tracked_people三张表 - 自动插入默认管理员(admin / 123456)
- 自动导入 CSV 数据(4,909 条记录)
11.4 手动数据导入
bash
# 使用 CLI 工具导入
python import_data.py
# 指定 CSV 文件
python import_data.py --csv path/to/data.csv
# 清空后重新导入
python import_data.py --reset
11.5 数据库配置覆盖
通过环境变量覆盖默认数据库配置:
bash
export HEALTH_DB_HOST=192.168.1.100
export HEALTH_DB_PORT=3307
export HEALTH_DB_USER=health_user
export HEALTH_DB_PASSWORD=strong_password_here
export HEALTH_DB_NAME=health_db
python app.py
11.6 默认账号
| 角色 | 用户名 | 密码 | 权限 |
|---|---|---|---|
| 管理员 | admin | 123456 | 全部功能 |
| 普通用户 | (注册) | (自定义) | 查看 + 关注 |
11.7 访问地址
启动后浏览器访问:http://localhost:5000
12. 数据字典
12.1 字段编码对照表
性别(xingbie)
| 值 | 含义 |
|---|---|
| 男性 | 男性 |
| 女性 | 女性 |
布尔字段(gxy / xzb / sfzf / hunyin)
| 值 | 含义 |
|---|---|
| 0 | 无 / 未婚 |
| 1 | 有 / 已婚 |
城乡属性(zzxz)
| 值 | 含义 |
|---|---|
| 城市 | 城市地区 |
| 农村 | 农村地区 |
居住编码(residenc)
| 值 | 含义 |
|---|---|
| 1 | 中心城区 |
| 2 | 县城/乡镇 |
| 3 | 村庄 |
工作性质(gzxz)
| 值 | 含义 |
|---|---|
| 私人企业 | 私营企业员工 |
| 自雇人士 | 自由职业/个体户 |
| 政府部门 | 政府/事业单位 |
| 儿童 | 未成年/儿童 |
吸烟状态(xyzk)
| 值 | 含义 |
|---|---|
| 从不吸烟 | 从未吸烟 |
| 以前吸烟 | 已戒烟 |
| 吸烟 | 当前吸烟 |
| 不详 | 信息缺失 |
BMI 分层
| 区间 | 层级 | 说明 |
|---|---|---|
| < 18.5 | 偏瘦 | 体重过轻 |
| 18.5 - 24 | 正常 | 健康范围 |
| 24 - 28 | 超重 | 需关注 |
| >= 28 | 肥胖 | 需干预 |
血糖分层
| 区间 (mg/dL) | 层级 | 说明 |
|---|---|---|
| < 100 | 正常 | 正常血糖 |
| 100 - 126 | 偏高 | 糖尿病前期 |
| >= 126 | 糖尿病风险 | 建议进一步检查 |
关注级别(attention_level)
| 级别 | 说明 |
|---|---|
| 一般关注 | 常规跟踪 |
| 重点关注 | 需定期复查 |
| 高危随访 | 高风险,需密切监控 |
12.2 数据集来源
- 来源: 和鲸社区(Heywhale)公开数据集
- 链接: https://www.heywhale.com/mw/dataset/6a05e303c52ca72ef9c28f3a/content
- 构成: 脑中风预测数据集 + 中国中老年健康调查数据集合并
- 样本量: 4,909 条
- 年龄范围: 3-93 岁
13. 已知问题与改进建议
13.1 已知问题
| 编号 | 问题 | 严重程度 | 说明 |
|---|---|---|---|
| 1 | README 与实际不符 | 低 | README 写的是 Streamlit,实际是 Flask |
| 2 | requirements.txt 包含未使用的 streamlit | 低 | 不影响运行,但造成困惑 |
| 3 | 默认管理员密码过弱 | 中 | admin/123456,生产环境需修改 |
| 4 | Secret Key 硬编码 | 中 | 应通过环境变量配置 |
| 5 | 无 CSRF 保护 | 中 | 表单提交缺少 Token 验证 |
| 6 | 无登录限流 | 低 | 可被暴力破解 |
| 7 | cookies.txt 残留 | 低 | 项目中遗留的无关文件 |
| 8 | 数据量小时全量加载 | 低 | 4,909 条全量加载到内存,适合中小规模 |
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
基于源码版本:当前工作目录