护理漏测检查 — 数据取值规则
一生命体征判定规则
▼1.1 哪些算"生命体征"(6项)
| 序号 | 体征项 | 数据库字段 | API字段 |
|---|---|---|---|
| 1 | 体温 | temperature | temperature |
| 2 | 脉搏 | pulse | pulse |
| 3 | 呼吸 | respiration | respiration |
| 4 | 血压 | blood_pressure | blood_pressure |
| 5 | 血氧 | spo2 | spo2 |
| 6 | 血糖 | blood_sugar | blood_sugar |
1.2 判定逻辑
has_vital 字段在 analyze_db_v3.py 中计算:
# analyze_db_v3.py load_nursing_records()
has_vital = 0
for vf in VITAL_FIELDS: # 遍历6项体征
if rec.get(vf, ""): # 任一项有值(非空字符串)
has_vital = 1
break
| has_vital 值 | 含义 | 判定 |
|---|---|---|
1 | 当天至少一项生命体征有值 | 有测量 |
0 | 6项全为空 | 漏测 |
1.3 同日多记录合并
analyze_db_v3.py 中:同一人同一天可能有多条记录,取逻辑或:
# 如果当天已有记录,取 OR(只要任一条有测量就算有)
if date_key in week_data:
week_data[date_key] = week_data[date_key] or has_vital
else:
week_data[date_key] = has_vital
即:只要有任意一条记录的 has_vital=1,当天就算"有测量"。
二护理等级与完成阈值
▼2.1 阈值规则
| 护理等级 | 每周完成阈值 | 判定标准 |
|---|---|---|
| 0、1、2、3 | ≥1天有测量 | count ≥ 1 → OK |
| 4 | ≥2天有测量 | count ≥ 2 → OK |
| 无等级 / 空 | — | 忽略,不纳入统计 |
# analyze_db_v3.py analyze()
try:
level = int(level_str)
except:
continue # 无护理等级,跳过
threshold = 2 if level == 4 else 1 # 4级→需要2天, 其他→1天
2.2 判断逻辑
# analyze_db_v3.py analyze()
count = sum(1 for d in week_data.values() if d) # 有测量的天数
if count >= threshold:
week_result = {"status": "ok", ...}
else:
week_result = {"status": "miss", ...}
三两周分析窗口
▼3.1 时间范围
| week_offset | 含义 | 示例(本周一=6/2) |
|---|---|---|
1 | 上周 | 5/26 ~ 6/1 |
0 | 本周 | 6/2 ~ 6/8 |
# analyze_db_v3.py 第31行
ANALYSIS_WEEKS = [1, 0] # 只分析上周(1)和本周(0)
3.2 本周未来日期
本周中 晚于今天 的日期不检查:
# analyze_db_v3.py analyze()
if week_offset == 0 and day_date > today:
continue # 跳过未来日期
3.3 周标签生成
# 前端 leak_check.html buildWeekDates()
# server.py /api/leak/result 返回的 resultMeta.weeks 格式:
weeks: [
{"label": "5/26-6/1", "start": "2026-05-26", "end": "2026-06-01"},
{"label": "6/2-6/8", "start": "2026-06-02", "end": "2026-06-08"},
]
四入住日期过滤
▼4.1 来源
从 resident_info 表的 admission_date 字段读取入住日期。
resident_info 表读取(该表由 sync_resident_info.py 定期从轻流同步)。
4.2 过滤逻辑
# analyze_db_v3.py analyze()
checkin_date = ri_map[id_number].get("admission_date") # 从 resident_info 读取
if checkin_date:
checkin_date = datetime.strptime(checkin_date, "%Y-%m-%d").date()
# 情况1:整周未入住
if checkin_date > week_end:
week_results[week_offset] = {"status": "not_checked_in", ...}
continue
# 情况2:入住日期在周内
for day_date in week_days:
if day_date < checkin_date:
continue # 入住前的天数不统计
| 场景 | 条件 | 处理 |
|---|---|---|
| 整周未入住 | 入住日期晚于该周周日 | 整周标记"未入住" |
| 周内入住 | 入住日期在本周范围内 | 只检查入住日期及之后的天 |
五离院处理规则
▼5.1 数据来源
| 来源 | 说明 |
|---|---|
| 数据表 | resident_info 表(status 字段、leave_date 字段) |
| 权威源 | resident_info 表由定时任务从轻流入住协议表单同步 |
5.2 状态过滤规则
status = '在住' 的老人,忽略了请假人员。现改为:
只排除
status = '离院' 的老人,请假 状态的老人也要纳入计算。
# analyze_db_v3.py load_resident_base_info()
sql = """
SELECT id_number, name, bed_number, care_level, institution, area,
admission_date, status, leave_date
FROM resident_info
WHERE status != '离院'
"""
有效状态包括:在住、请假(以及 resident_info 中其他非离院状态)。
5.3 前端展示规则(leak_check.html renderWeekCell)
按离院日期与当前周的关系,分三种情况:
| 场景 | 条件 | 展示 |
|---|---|---|
| 离院前 | 周结束日 < 离院日期 | 正常展示量测(绿/红标记) |
| 离院周 | 周开始日 ≤ 离院日期 ≤ 周结束日 | 展示量测 + 底部标注 离院(日期) |
| 离院后 | 周开始日 > 离院日期 | 显示"离院",灰色背景 |
离院周中:即使老人有量测数据,也正常展示,不遮挡。底部额外加上离院日期标注。
六机构与休养区数据规则
▼6.1 数据来源
| 来源 | 说明 |
|---|---|
| 轻流表单 | appKey = 8fcbagt01402(机构-休养区对应关系表单) |
| 字段 | 机构 queId=208612747,休养区 queId=208612748 |
| MySQL表 | org_area_mapping 表(由 sync_org_area.py 定时同步) |
| API端点 | /api/leak/org-areas(前端下拉框数据源) |
6.2 同步脚本
sync_org_area.py:从轻流表单 8fcbagt01402 读取机构-休养区对应关系,写入 org_area_mapping 表。
# 手动执行
python3 /opt/leakcheck/sync_org_area.py
# 建议配置 cron 定时执行(每日凌晨3点)
0 3 * * * cd /opt/leakcheck && python3 sync_org_area.py >> sync_org_area.log 2>&1
6.3 前端使用方式
前端页面加载时,调用 /api/leak/org-areas 获取机构和休养区列表,填充下拉框。筛选时按 institution 和 area 字段过滤。
resident_info 里有老人在某个休养区,但该休养区不在轻流表单中,则筛选时该老人不会被归类到任何机构/休养区。
七数据同步流程
▼7.1 总体架构
7.2 三种同步模式
| 模式 | 触发方式 | 数据范围 | 原理 | 耗时 |
|---|---|---|---|---|
| 增量同步 | 页面「增量同步」按钮 | 数据库最大 record_time 之后的新数据 | 从 MySQL 最大记录时间开始,向轻流 API 拉取后续数据 | ~9 秒 |
| 回看21天 | 页面「同步21天」按钮 | 最近 21 天全部数据 | 固定从 21 天前开始拉取,覆盖期间所有补录数据 | ~2 分钟 |
| 全量30天 | Cron 定时任务(凌晨 2:00) | 最近 30 天全部数据 | 每日自动执行,兜底确保不遗漏 | ~3 分钟 |
7.3 analyze_db_v3.py 分析逻辑
输入:
resident_info表(所有非离院老人)→ 权威人员名单nursing_records表(最近30天护理记录)→ 测量数据
输出:leak_check_result 表
关键步骤:
load_resident_base_info():从resident_info读取所有非离院老人,建立ri_mapload_nursing_records():从nursing_records读取最近30天的护理记录analyze():对每位老人,检查每周的测量情况,生成week_resultssave_to_db():写入leak_check_result表
八API 接口说明
▼8.1 GET /api/leak/result
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
page | int | 否 | 页码,默认1 |
page_size | int | 否 | 每页条数,默认200,最大5000 |
institution | string | 否 | 按机构筛选 |
area | string | 否 | 按休养区筛选(需配合 institution) |
care_level | string | 否 | 按护理等级筛选 |
status | string | 否 | 按漏测状态筛选(miss/ok) |
响应格式:
{
"success": true,
"total": 2777, // 总人数(来自 resident_info,非离院)
"page": 1,
"page_size": 5000,
"resultMeta": {
"weeks": [
{"label": "5/26-6/1", "start": "2026-05-26", "end": "2026-06-01"},
{"label": "6/2-6/8", "start": "2026-06-02", "end": "2026-06-08"}
],
"vitalFields": ["temperature", "pulse", "respiration", "blood_pressure", "spo2", "blood_sugar"]
},
"data": [
{
"name": "陈宽云",
"bed_number": "301",
"care_level": "2",
"id_number": "320113196902130327",
"institution": "三牌楼颐养中心",
"area": "三楼休养区",
"admission_date": "2025-03-15",
"status": "在住",
"leave_date": "",
"weeks": { // ← 注意字段名是 weeks(驼峰)
"1": {"status": "miss", "count": 0, "threshold": 1, ...},
"0": {"status": "ok", "count": 2, "threshold": 1, ...}
},
"allOk": false // ← 注意字段名是 allOk(驼峰)
}
]
}
weeks(不是 week_results)、allOk(不是 all_ok)。
8.2 GET /api/leak/org-areas
响应格式:
{
"success": true,
"org_areas": {
"三牌楼颐养中心": ["三楼休养区", "四楼休养区"],
"仙鹤门颐养中心": ["一楼休养区", "二楼休养区"]
},
"orgs": ["三牌楼颐养中心", "仙鹤门颐养中心"]
}
九输出数据结构
▼9.1 每人结果字段(leak_check_result 表)
| 字段 | 类型 | 说明 |
|---|---|---|
id_number | varchar | 身份证号(主键) |
name | varchar | 姓名 |
bed_number | varchar | 床位号 |
care_level | varchar | 护理等级 |
institution | varchar | 机构名称 |
area | varchar | 休养区 |
admission_date | date | 入住日期 |
status | varchar | 在住/请假/离院 |
leave_date | date | 离院日期(如已离院) |
week_results | JSON | 各周分析结果(见下表) |
all_ok | tinyint | 所有周都达标=1,否则=0 |
9.2 week_results 结构
{
"1": {"status": "ok/miss/not_checked_in", "count": 1, "threshold": 1, "completed_days": [1,3,5], "missed_days": [2,4,6,7]},
"0": {...}
}
| 字段 | 说明 |
|---|---|
status | ok=达标,miss=漏测,not_checked_in=未入住 |
count | 有测量的天数 |
threshold | 达标阈值(1或2) |
completed_days | 有测量的星期几(1=周一,...,7=周日) |
missed_days | 漏测的星期几 |
十已知边界案例
▼| 案例 | 情况 | 判定 | 原因 |
|---|---|---|---|
| 陈宽云 5/30 | DB有记录,6项体征全空,仅体重=79.4 | 漏测 | 体重不算生命体征,has_vital=0 |
| 同日多条记录 | 一条空 + 一条有体温 | 有测量 | 逻辑或合并 |
| 本周五(未来) | 今天是周三 | 不检查 | 跳过未来日期 |
| 入住日当天 | checkin_date = 周三 | 检查周三起 | 入住当天起正常统计 |
| 请假人员 | status='请假' | 纳入统计 | 只排除离院,请假人员也要检查 |
| 离院后 | 离院日期 < 周开始日 | 显示"离院" | 离院后的周不检查 |
十一前台状态显示规则
▼11.1 三种缺测状态对照
页面表格中「漏测情况」列根据 status 和 missed_days 显示不同文字:
| 页面显示 | status | missed_days | 含义 |
|---|---|---|---|
| 完成 | "ok" |
— | 本周测量天数 ≥ 阈值,达标 |
| 缺一、二、三... | "miss" |
["一","二","三"...] |
已入住,具体缺了哪几天 |
| 未入住 | "not_checked_in" |
[] |
入住日期在此周之后,整周未入住 |
| 缺测 | "miss" |
[](空) |
保底逻辑:数据异常导致无法逐天判定 |
ok → not_checked_in → miss(有missed_days) → 缺测(保底)
11.2 前端渲染代码
leak_check.html renderWeekCell 函数(修复于 2026-06-06):
// leak_check.html — 表格渲染
if (wk.status === 'ok') {
html += '<td class="cell-ok">完成</td>';
} else if (wk.status === 'not_checked_in') {
html += '<td class="cell-na">未入住</td>';
} else {
var days = wk.missed_days || [];
var missText = days.length > 0 ? '缺' + days.join('、') : '缺测';
html += '<td class="cell-miss">' + missText + '...</td>';
}
11.3 CSV导出同样处理
// leak_check.html — CSV导出列
if (wk.status === 'ok') {
row.push('完成');
} else if (wk.status === 'not_checked_in') {
row.push('未入住');
} else {
var days = wk.missed_days || [];
row.push(days.length > 0 ? '缺' + days.join('、') : '缺测');
}
11.4 修复历史
| 日期 | 问题 | 修复 |
|---|---|---|
| 2026-06-06 | not_checked_in 状态落入 else 分支,显示"缺测"而非"未入住" |
在 ok 和 miss 之间插入 else if (wk.status === 'not_checked_in') 分支 |