护理漏测系统 — 评估检查功能需求文档
一功能概述
▼1.1 系统定位
评估检查页面是护理漏测系统的核心功能模块之一,用于检查养老院在住老人的年度评估完成情况,自动识别漏评项并支持数据同步和分析。
| 项目 | 内容 |
|---|---|
| 访问地址 | http://110.42.221.197:9090/assessment |
| 前端文件 | /opt/leakcheck/assessment_check.html |
| 后端服务 | /opt/site/server.py(Flask) |
| 分析脚本 | /opt/leakcheck/analyze_assessment.py |
| 同步脚本 | /opt/leakcheck/sync_assessment.py |
| 数据源 | 轻流平台 API + MySQL 数据库(nursing) |
| Docker 容器 | mysql-nursing |
1.2 核心业务目标
- 从轻流平台自动拉取老人评估记录到本地数据库
- 按规则计算每位老人的评估完成情况(12项必查评估类型)
- 在页面上直观展示漏评情况,支持多维筛选和排序
- 提供"同步"和"重新分析"两个操作入口,支持数据刷新
用户核心诉求:希望页面能够方便地查看哪些老人缺少哪些评估,并理解系统的判断逻辑(为什么显示12项未评?为什么改了数据库没生效?),同时需要更灵活的筛选能力。
二需求清单(全部已完成)
▼REQ-001
理解"重新分析"按钮的完整运行逻辑和处理规则 🔎
背景:用户在测试时发现,直接修改数据库
需求:需要彻底搞清楚"重新分析"按钮背后做了什么——它读取哪些表、怎么计算、为什么会覆盖手动修改。
结论:"重新分析"执行的是
已完成
assessment_analysis 表的 missing_count 值后,点击"重新分析"页面数据会还原。需求:需要彻底搞清楚"重新分析"按钮背后做了什么——它读取哪些表、怎么计算、为什么会覆盖手动修改。
结论:"重新分析"执行的是
analyze_assessment.py 脚本,从源数据表 assessment_records 重算结果写入 assessment_analysis 缓存表(INSERT ON DUPLICATE KEY UPDATE,完全覆盖)。assessment_analysis 是只读缓存,不可作为手工编辑的数据源。
REQ-002
理解"同步"按钮的完整运行逻辑 🔒
需求:了解"同步"按钮和"重新分析"的区别,以及它的两步流程(先从轻流API拉取数据,再运行分析)。
结论:同步 = 数据拉取 + 分析重算(两步合一);重新分析 = 仅分析重算(跳过拉取)。两者都是异步后台任务,通过轮询获取进度。
已完成
结论:同步 = 数据拉取 + 分析重算(两步合一);重新分析 = 仅分析重算(跳过拉取)。两者都是异步后台任务,通过轮询获取进度。
REQ-003
调整筛选项顺序 📊
原顺序:机构 → 姓名 → 区域 → 护理等级 → 完成状态
新顺序:机构 → 区域 → 姓名 → 护理等级 → 完成状态
原因:用户希望按照"从宏观到微观"的逻辑排列筛选项:先选机构、再选区域缩小范围、然后搜姓名、再按护理等级过滤、最后看完成状态。
已完成
新顺序:机构 → 区域 → 姓名 → 护理等级 → 完成状态
原因:用户希望按照"从宏观到微观"的逻辑排列筛选项:先选机构、再选区域缩小范围、然后搜姓名、再按护理等级过滤、最后看完成状态。
REQ-004
调整列表展示列顺序 📊
原顺序:机构 → 姓名 → 床位 → ...
新顺序:机构 → 区域 → 姓名 → 床位 → 护理等级 → 年龄 → 入住日期 → 其他字段
新增列:护理等级(care_level)、年龄(age)—— 从 resident_info 表实时 JOIN 查询附加。
已完成
新顺序:机构 → 区域 → 姓名 → 床位 → 护理等级 → 年龄 → 入住日期 → 其他字段
新增列:护理等级(care_level)、年龄(age)—— 从 resident_info 表实时 JOIN 查询附加。
REQ-005
删除"显示离院"筛选功能 🗑
移除工具栏中的"显示离院"复选框(showDischarged),不再提供此筛选项。
已完成
REQ-006
新增护理等级(care_level)筛选下拉框 🔒
在工具栏中新增"护理等级"下拉框筛选器,选项值来自
技术注意:care_level 不存在于 assessment_analysis 表中,SQL 筛选需使用子查询:
已完成
resident_info 表的 care_level 字段去重(当前为 '0','1','2','3','4' 五档)。技术注意:care_level 不存在于 assessment_analysis 表中,SQL 筛选需使用子查询:
id_number IN (SELECT id_number FROM resident_info WHERE care_level = %s)
REQ-007
整理评估漏测检查规则文档 📚
将代码中的所有评估相关规则和逻辑,按照护理规则的格式整理成一份完整的规则文档,包含 9 大章节:12项必查评估、核心分析逻辑、数据源与人员范围、离院处理、"当年未评估"判定、同步与分析流程、输出数据结构、真实数据示例、已知边界。每个逻辑规则附带真实数据示例。
已完成 → assessment-rules.html
REQ-008
将评估规则文档加入工作台笔记草稿 📁
在工作台(http://110.42.221.197:9090/)的"笔记草稿"区域添加评估规则卡片,与现有的护理规则卡片并列展示,便于随时查阅。
已完成
三需求探讨过程(对话时间线)
▼3.1 探讨路径图
用户提出疑问:"重新分析按钮的逻辑是什么"
│
├─► 发现 assessment_analysis 是计算缓存表
│ └─► 解释 INSERT ON DUPLICATE KEY UPDATE 会覆盖手动修改
│
├─► 用户理解后继续提 UI 改进需求
│ ├─► REQ-003: 筛选项顺序调整
│ ├─► REQ-004: 列表展示顺序调整 + 新增护理等级/年龄字段
│ ├─► REQ-005: 删除"显示离院"筛选
│ └─► REQ-006: 新增护理等级下拉筛选
│
└─► 用户要求整理规则文档
├─► REQ-007: 生成 assessment-rules.html(9大章节)
└─► REQ-008: 加入工作台笔记草稿
3.2 关键对话节点
阶段 1 — 疑惑期
用户发现一个测试现象:在数据库里把某人的 missing_count 从 12 改成 11,但刷新页面后还是显示 12。
这引发了对"重新分析"按钮的深入追问——它到底做了什么?为什么会把我的修改覆盖掉?
阶段 2 — 理解期
通过阅读
analyze_assessment.py 源代码,梳理出完整的数据流:
- assessment_records = 源数据(轻流同步过来的原始评估记录)
- resident_info = 主数据(权威人员清单)
- analyze_assessment.py = 计算引擎(读取上面两张表,算出结果)
- assessment_analysis = 结果缓存(每次分析都会完全覆写!)
阶段 3 — 改进期
理解清楚逻辑后,用户提出了 4 个 UI 改进需求:
- 筛选项顺序不符合使用习惯
- 列表缺了护理等级和年龄这两个关键字段
- "显示离院"这个筛选用不上
- 希望能按护理等级筛选
阶段 4 — 文档化
用户要求把所有这些逻辑整理成文档,方便后续维护和交接。
最终产出了 assessment-rules.html 规则文档,并加入工作台的笔记草稿区。
总结:整个需求演进遵循了 "发现问题 → 理解原理 → 提出改进 → 文档沉淀" 的自然路径。用户不是一开始就列出所有需求的,而是在逐步使用和理解过程中不断发现可以优化的点。
四UI 调整详细规格
▼4.1 工具栏(Toolbar)最终布局
| 控件 | 类型 | 说明 | 顺序 |
|---|---|---|---|
| 机构 | select 下拉 | institutions 列表 | 1 |
| 区域 | select 下拉 | areas 列表 | 2 |
| 姓名 | input 搜索框 | 模糊匹配 caregiver_name | 3 |
| 护理等级 | select 下拉 | care_levels(新增) | 4 |
| 完成状态 | select 下拉 | 全部/已完成/有漏评 | 5 |
| ── 操作按钮区 ── | |||
| 🔄 同步 | button(黄色) | 调用 /api/assessment/sync | - |
| 🔍 重新分析 | button(紫色) | 调用 /api/assessment/analyze | - |
| 📥 导出 CSV | button(绿色) | 前端生成下载 | - |
4.2 数据表格列头最终布局
| 序号 | 列名 | 数据来源 | 备注 |
|---|---|---|---|
| 1 | 机构 | institution | |
| 2 | 区域 | area | |
| 3 | 姓名 | caregiver_name | |
| 4 | 床位 | bed_number | |
| 5 | 护理等级 | ri_map.care_level | 新增 来自 resident_info JOIN |
| 6 | 年龄 | ri_map.age | 新增 来自 resident_info JOIN |
| 7 | 入住日期 | check_in_date | 原 admission_date |
| 8 | 完成数 | completed_count | |
| 9 | 缺失数 | missing_count | |
| 10 | 状态 | status_summary | |
| 11 | 操作 | - | 查看详情按钮 |
注意:护理等级和年龄字段不在 assessment_analysis 表中,是通过 server.py 中对 resident_info 的批量查询(SELECT id_number, admission_date, care_level, age FROM resident_info WHERE id_number IN (...))动态附加到返回数据的。
五按钮行为详解
▼5.1 "同步"按钮 (🔄)
| 属性 | 值 |
|---|---|
| 触发条件 | 用户点击 + 确认弹窗("确认全量同步?将清空并重新拉取全部数据,约需 2 分钟。") |
| API 入口 | POST /api/assessment/sync |
| 执行方式 | 异步后台线程(不阻塞 HTTP 响应) |
| 步骤 1 | python3 /opt/leakcheck/sync_assessment.py --full (超时 20 分钟) |
| 步骤 2 | python3 /opt/leakcheck/analyze_assessment.py (超时 10 分钟) |
| 进度查询 | GET /api/assessment/task/status (前端轮询,最多 120 次) |
| 防重复 | .assessment_task 文件标记,已有任务运行时拒绝并提示 |
| 预计耗时 | 2 ~ 3 分钟 |
| 适用场景 | 需要最新轻流数据时(如今天新录入了评估记录) |
5.2 "重新分析"按钮 (🔍)
| 属性 | 值 |
|---|---|
| 触发条件 | 用户点击(无确认弹窗) |
| API 入口 | POST /api/assessment/analyze |
| 执行方式 | 异步后台线程 |
| 执行的脚本 | python3 /opt/leakcheck/analyze_assessment.py (仅此一步) |
| 进度查询 | GET /api/assessment/task/status |
| 预计耗时 | 30 秒 ~ 1 分钟 |
| 适用场景 | 手动改了 DB 后想刷新结果;或仅想重算不改数据源 |
| 重要! | 它会完全覆写 assessment_analysis 表,手动修改会被还原 |
两者的核心区别:同步 = 拉数据 + 算结果;重新分析 = 只算结果(用现有数据)。如果只是想看看最新的分析效果,用"重新分析"更快;如果轻流那边有了新录入的数据,必须用"同步"。
六关键数据逻辑(用户最关心的部分)
▼6.1 为什么我改了数据库没生效?
因为 assessment_analysis 是计算缓存表,不是手工编辑表。
数据流向是单向的:assessment_records + resident_info → analyze_assessment.py → assessment_analysis(覆写)。任何对 analysis 表的手动修改都会在下一次"同步"或"重新分析"后被程序计算的原始值覆盖。
数据流向是单向的:assessment_records + resident_info → analyze_assessment.py → assessment_analysis(覆写)。任何对 analysis 表的手动修改都会在下一次"同步"或"重新分析"后被程序计算的原始值覆盖。
6.2 为什么某人显示 12 项全缺?(三种可能)
| 原因 | 表现 | 如何验证 |
|---|---|---|
| A: 最新评估不在今年 | 该人有历史评估记录,但最近一次是在 2025 年或更早 | 查 assessment_records 中该人最新一条记录的 assessment_date |
| B: 从未做过任何评估 | assessment_records 中完全没有该人的记录 | 查 SELECT * FROM assessment_records WHERE id_number='xxx' |
| C: 已离院 | status_summary 显示"离院",missing_count 强制为 12 | 查 resident_info 中该人的 status 字段是否为"离院" |
6.3 核心判定伪代码(用户反复确认的关键逻辑)
# 对于每位在住老人:
if 最新评估年份 == 当前分析年份:
completed = year_types # ✓ 认可今年的评估记录
else:
completed = set() # ✗ 清空!视为"当年未评估"
if 该人是离院状态:
completed = set() # ✗ 离院强制清空,不管有没有评估
missing = [t for t in REQUIRED_TYPES if t not in completed]
# missing 就是页面上看到的缺失列表
七技术实现要点(踩坑记录)
▼7.1 部署架构注意事项
| 事项 | 说明 |
|---|---|
| 实际运行的 server.py | /opt/site/server.py(不是 /opt/leakcheck/server.py!) |
| 实际的前端 HTML | /opt/leakcheck/assessment_check.html |
| server.py 中的路径变量 | SITE_DIR='/opt/site', LEAK_DIR='/opt/leakcheck' |
| 服务监听端口 | 9090(无 nginx 反代,Flask 直接跑) |
| 访问地址 | http://110.42.221.197:9090/assessment |
踩坑:最初修改了 /opt/leakcheck/server.py 但没生效,因为实际运行的是 /opt/site/server.py。两个文件都需要保持同步修改。
7.2 care_level 筛选的 SQL 实现
由于 care_level 字段不存在于 assessment_analysis 表中,筛选时使用了子查询:
# 错误写法(报 1054 Unknown column):WHERE care_level = %s
# 正确写法(子查询):WHERE id_number IN (
SELECT id_number FROM resident_info WHERE care_level = %s
)
7.3 已知限制
- expected_count 当前硬编码为 12,不支持按护理等级差异化配置
- 非必查类型的评估记录被静默忽略,不出现在任何统计中
- 身份证号为唯一匹配键,格式不一致会导致关联失败
- 大数据量时(4000+条)无 WHERE 的查询较慢(~30秒),主要瓶颈是网络传输(2.4MB JSON)而非 SQL 本身
八相关文档与资源
▼8.1 规则文档
| 文档 | 地址 | 内容 |
|---|---|---|
| 评估漏测检查规则 | /assessment-rules.html | 9大章节:评估类型、分析逻辑、数据源、离院处理、当年未评估、同步流程、输出结构、数据示例、已知边界 |
| 护理漏测检查规则 | /nursing-data-rules.html | 8个模块:生命体征判定、护理等级阈值、三周分析窗口、离院处理等 |
| 本需求文档 | /assessment-requirements.html | 本文档:需求清单、探讨过程、UI规格、按钮行为、技术要点 |
8.2 涉及的核心文件
| 文件 | 角色 |
|---|---|
| /opt/site/server.py | Flask 后端主服务(API 路由 + 业务逻辑) |
| /opt/leakcheck/assessment_check.html | 评估检查前端页面 |
| /opt/leakcheck/analyze_assessment.py | 评估分析计算引擎 |
| /opt/leakcheck/sync_assessment.py | 轻流数据同步脚本 |
| /opt/site/workbench.html | 工作台主页 |
| /opt/site/assessment-rules.html | 评估规则文档(本批次产出) |
| /opt/site/nursing-data-rules.html | 护理规则文档(之前产出) |