生命体征判定规则

1.1 哪些算"生命体征"(6项)

序号体征项数据库字段API字段
1体温temperaturetemperature
2脉搏pulsepulse
3呼吸respirationrespiration
4血压blood_pressureblood_pressure
5血氧spo2spo2
6血糖blood_sugarblood_sugar
体重不算生命体征。即使体重有值,只要6项全空,当天仍判定为"漏测"。

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当天至少一项生命体征有值有测量
06项全为空漏测

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", ...}

两周分析窗口

变更说明(2026-06-06):原为3周(上上周、上周、本周),现改为 2周(上周 + 本周)。

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 字段读取入住日期。

变更说明(2026-06-06):原逻辑从轻流入住协议 API 读取,现改为直接从 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 状态过滤规则

变更说明(2026-06-06):原逻辑只统计 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)

按离院日期与当前周的关系,分三种情况:

场景条件展示
离院前周结束日 < 离院日期正常展示量测(绿/红标记)
离院周周开始日 ≤ 离院日期 ≤ 周结束日展示量测 + 底部标注 离院(日期)
离院后周开始日 > 离院日期显示"离院",灰色背景

离院周中:即使老人有量测数据,也正常展示,不遮挡。底部额外加上离院日期标注。

机构与休养区数据规则

新增章节(2026-06-06):机构与休养区的对应关系不再硬编码,改为从轻流表单动态获取。

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 获取机构和休养区列表,填充下拉框。筛选时按 institutionarea 字段过滤。

重要:机构和休养区筛选项 以轻流表单为准。如果 resident_info 里有老人在某个休养区,但该休养区不在轻流表单中,则筛选时该老人不会被归类到任何机构/休养区。

数据同步流程

7.1 总体架构

轻流 API (护理记录表单, appKey=8h0q8g841402) │ ▼ sync_db.py (增量/回看21天/全量30天) │ ▼ ┌─────────────────┐ │ nursing_records │ ← 护理测量记录(原始数据) └────────┬────────┘ │ ▼ analyze_db_v3.py (读取 resident_info + nursing_records → 分析) │ ▼ ┌────────────────────┐ │ leak_check_result │ ← 分析结果缓存表 └────────┬───────────┘ │ ▼ server.py → /api/leak/result → leak_check.html (前端展示) 权威人员名单来源:resident_info 表(status != '离院') 轻流入住协议表单 (appKey=8a7fonqg0o02) │ ▼ sync_resident_info.py (定时同步) │ ▼ ┌─────────────────┐ │ resident_info │ ← 老人基础信息(权威源) └─────────────────┘ 轻流机构-休养区表单 (appKey=8fcbagt01402) │ ▼ sync_org_area.py (定时同步) │ ▼ ┌────────────────────┐ │ org_area_mapping │ ← 机构-休养区对应关系 └────────────────────┘

7.2 三种同步模式

模式触发方式数据范围原理耗时
增量同步 页面「增量同步」按钮 数据库最大 record_time 之后的新数据 从 MySQL 最大记录时间开始,向轻流 API 拉取后续数据 ~9 秒
回看21天 页面「同步21天」按钮 最近 21 天全部数据 固定从 21 天前开始拉取,覆盖期间所有补录数据 ~2 分钟
全量30天 Cron 定时任务(凌晨 2:00) 最近 30 天全部数据 每日自动执行,兜底确保不遗漏 ~3 分钟
为什么需要三种模式? 护理人员经常事后补录数据(补录的 record_time 是护理发生日期,不是录入日期)。增量同步只往后看,无法捕获历史补录;回看 21 天可以覆盖大部分补录场景;全量 30 天由定时任务兜底。

7.3 analyze_db_v3.py 分析逻辑

输入

  • resident_info 表(所有非离院老人)→ 权威人员名单
  • nursing_records 表(最近30天护理记录)→ 测量数据

输出leak_check_result

关键步骤

  1. load_resident_base_info():从 resident_info 读取所有非离院老人,建立 ri_map
  2. load_nursing_records():从 nursing_records 读取最近30天的护理记录
  3. analyze():对每位老人,检查每周的测量情况,生成 week_results
  4. save_to_db():写入 leak_check_result

API 接口说明

8.1 GET /api/leak/result

请求参数

参数类型必填说明
pageint页码,默认1
page_sizeint每页条数,默认200,最大5000
institutionstring按机构筛选
areastring按休养区筛选(需配合 institution)
care_levelstring按护理等级筛选
statusstring按漏测状态筛选(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(驼峰)
    }
  ]
}
重要:JSON 字段名使用驼峰命名:weeks(不是 week_results)、allOk(不是 all_ok)。

8.2 GET /api/leak/org-areas

响应格式

{
  "success": true,
  "org_areas": {
    "三牌楼颐养中心": ["三楼休养区", "四楼休养区"],
    "仙鹤门颐养中心": ["一楼休养区", "二楼休养区"]
  },
  "orgs": ["三牌楼颐养中心", "仙鹤门颐养中心"]
}

输出数据结构

9.1 每人结果字段(leak_check_result 表)

字段类型说明
id_numbervarchar身份证号(主键)
namevarchar姓名
bed_numbervarchar床位号
care_levelvarchar护理等级
institutionvarchar机构名称
areavarchar休养区
admission_datedate入住日期
statusvarchar在住/请假/离院
leave_datedate离院日期(如已离院)
week_resultsJSON各周分析结果(见下表)
all_oktinyint所有周都达标=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": {...}
}
字段说明
statusok=达标,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 三种缺测状态对照

页面表格中「漏测情况」列根据 statusmissed_days 显示不同文字:

页面显示statusmissed_days含义
完成 "ok" 本周测量天数 ≥ 阈值,达标
缺一、二、三... "miss" ["一","二","三"...] 已入住,具体缺了哪几天
未入住 "not_checked_in" [] 入住日期在此周之后,整周未入住
缺测 "miss" [](空) 保底逻辑:数据异常导致无法逐天判定
优先级总览:oknot_checked_inmiss(有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') 分支

11.5 数据流:status 如何产生

analyze_db_v3.py analyze() │ ├─ count >= threshold ──→ status = "ok" │ ├─ checkin_date > 周日 ──→ status = "not_checked_in" │ └─ 否则遍历7天 ├─ 入住前 → 跳过 ├─ 有测量 → completed_days++ └─ 无测量 → missed_days++ 最终 count = len(completed_days) count < threshold → status = "miss" missed_days 有值