diff --git a/MD/test/07_full_chain_test.py b/MD/test/07_full_chain_test.py new file mode 100644 index 000000000..837be5c5e --- /dev/null +++ b/MD/test/07_full_chain_test.py @@ -0,0 +1,802 @@ +#!/usr/bin/env python3 +""" +HealthLink-HIS 三甲医院全链路业务逻辑测试 +覆盖: 16大模块 × 业务逻辑验证 × 跨模块联动 × 缺陷发现 +""" + +import requests, json, time, sys, os +from datetime import datetime, timedelta + +BASE = "http://localhost:18082/healthlink-his" +R = [] # results +P = F = 0 # passed/failed +DEFECTS = [] # 发现的缺陷 + +USERS = { + "admin": ("admin", "admin123", "超级管理员"), + "doctor": ("doctor1", "123456", "医生"), + "jzys": ("jzys", "123456", "急诊医生"), + "jzhs": ("jzhs", "123456", "急诊护士"), + "nkhs": ("nkhs1", "123456", "内科护士"), + "ssshs": ("ssshs1", "123456", "手术室护士"), + "pharmacist": ("yjk1", "123456", "药师"), + "tech": ("医技员", "123456", "医技"), + "finance": ("sfy", "123456", "收费员"), + "consultant": ("hzzj1", "123456", "会诊专家"), +} + +TOKENS = {} + +def login_all(): + for k, (u, p, _) in USERS.items(): + try: + r = requests.post(f"{BASE}/login", json={"username": u, "password": p, "tenantId": "1"}, timeout=10) + d = r.json() + if d.get("token"): + TOKENS[k] = d["token"] + except: pass + +def get_t(role="admin"): + return TOKENS.get(role) + +def api(method, path, token=None, data=None, params=None, timeout=15): + h = {"Content-Type": "application/json"} + if token: h["Authorization"] = f"Bearer {token}" + url = f"{BASE}{path}" + try: + if method == "GET": resp = requests.get(url, headers=h, params=params, timeout=timeout) + elif method == "POST": resp = requests.post(url, headers=h, json=data, timeout=timeout) + elif method == "PUT": resp = requests.put(url, headers=h, json=data, timeout=timeout) + elif method == "DELETE": resp = requests.delete(url, headers=h, timeout=timeout) + else: return None + j = resp.json() if "json" in resp.headers.get("content-type","") else {"code": resp.status_code, "msg": resp.text[:100]} + return {"ok": j.get("code")==200, "code": j.get("code", resp.status_code), "data": j.get("data"), "msg": j.get("msg",""), "raw": j} + except requests.exceptions.Timeout: + return {"ok": False, "code": 0, "msg": "超时", "data": None} + except Exception as e: + return {"ok": False, "code": 0, "msg": str(e)[:100], "data": None} + +def cnt(r): + if not r or not r.get("data"): return 0 + d = r["data"] + if isinstance(d, dict): return d.get("total", len(d.get("rows", d.get("list", [])))) + if isinstance(d, list): return len(d) + return 0 + +def rec(tid, name, ok, detail="", defect=None): + global P, F + if ok: P += 1 + else: F += 1 + R.append({"id": tid, "name": name, "ok": ok, "detail": detail}) + if defect: DEFECTS.append(defect) + print(f" {'✅' if ok else '❌'} [{tid}] {name}" + (f" — {detail}" if detail else "")) + +def defect(severity, module, title, desc, api_path="", impact=""): + return {"severity": severity, "module": module, "title": title, "desc": desc, "api": api_path, "impact": impact} + +# ======================== 1. 认证与权限 ======================== +def test_auth(): + print("\n" + "="*60) + print("🔐 模块一: 认证与权限") + print("="*60) + t = get_t() + + # 多角色登录 + for k, (u, p, role) in USERS.items(): + ok = k in TOKENS + rec(f"AUTH-{k}", f"{role}登录", ok, f"token={'✓' if ok else '✗'}") + + # 错误密码 + r = api("POST", "/login", data={"username":"admin","password":"wrong"}) + rec("AUTH-ERR", "错误密码拒绝", r["code"]!=200, f"code={r['code']}") + + # 获取用户信息 + r = api("GET", "/getInfo", token=t) + has_user = r["ok"] and isinstance(r.get("raw", {}), dict) and "user" in r.get("raw", {}) + rec("AUTH-INFO", "获取用户信息", has_user, f"has_user={has_user}") + + # 获取菜单 + r = api("GET", "/getRouters", token=t) + menu_count = len(r["data"]) if r["data"] else 0 + rec("AUTH-MENU", "获取菜单路由", r["ok"] and menu_count > 0, f"一级菜单={menu_count}") + + # 权限检查:不同角色访问管理功能 + perm_tests = [ + ("doctor", "/system/user/list", "医生→用户管理"), + ("pharmacist", "/system/role/list", "药师→角色管理"), + ("finance", "/system/config/list", "收费员→系统配置"), + ] + for uk, path, desc in perm_tests: + tk = get_t(uk) + if not tk: continue + r = api("GET", path, token=tk) + if r["ok"]: + rec(f"AUTH-PERM-{uk}", f"{desc}", False, "⚠️ 无权限隔离", + defect("中", "权限", f"{desc}权限未隔离", f"角色{uk}可访问{path}", path, "安全隐患")) + else: + rec(f"AUTH-PERM-{uk}", f"{desc}", True, f"已隔离(code={r['code']})") + +# ======================== 2. 门诊全流程 ======================== +def test_outpatient(): + print("\n" + "="*60) + print("🏥 模块二: 门诊全流程(挂号→就诊→开方→收费→取药)") + print("="*60) + fin = get_t("finance") + doc = get_t("doctor") + pha = get_t("pharmacist") + + # 2.1 挂号 + r = api("GET", "/charge-manage/register/init", token=fin) + reg_data = r["data"] if r["ok"] else None + rec("OP-REG", "挂号初始化", r["ok"], f"有数据={reg_data is not None}") + + # 检查挂号初始化返回的科室列表 + if reg_data and isinstance(reg_data, dict): + depts = reg_data.get("deptList", reg_data.get("depts", [])) + if not depts: + rec("OP-REG-DEPT", "挂号科室列表", False, "科室列表为空", + defect("高", "门诊", "挂号初始化无科室数据", "register/init返回的科室列表为空", "/charge-manage/register/init", "无法挂号")) + + # 2.2 医生工作站 + r = api("GET", "/doctor-station/main/init", token=doc) + rec("OP-DOC", "医生站初始化", r["ok"]) + + r = api("GET", "/doctor-station/advice/advice-base-info", token=doc) + advice_count = cnt(r) + rec("OP-ADVICE", "医嘱列表", r["ok"], f"医嘱数={advice_count}") + + r = api("GET", "/doctor-station/diagnosis/init", token=doc) + rec("OP-DX", "诊断初始化", r["ok"]) + + # 2.3 检验申请 + r = api("GET", "/doctor-station/inspection/get-applyList", token=doc, params={"pageNum":1,"pageSize":5}) + rec("OP-INS", "检验申请", r["ok"], f"申请单={cnt(r)}") + + # 2.4 电子病历 + r = api("GET", "/doctor-station/emr/emr-page", token=doc, params={"pageNum":1,"pageSize":5}) + rec("OP-EMR", "电子病历列表", r["ok"], f"病历={cnt(r)}") + + r = api("GET", "/doctor-station/emr/emr-template-page", token=doc, params={"pageNum":1,"pageSize":5}) + rec("OP-EMR-TPL", "病历模板", r["ok"], f"模板={cnt(r)}") + + # 2.5 门诊治疗 + r = api("GET", "/outpatient-manage/treatment/init", token=doc) + rec("OP-TREAT", "门诊治疗", r["ok"]) + + # 2.6 门诊输液 + r = api("GET", "/outpatient-manage/infusion/init", token=doc) + rec("OP-INFUSION", "门诊输液", r["ok"]) + + # 2.7 药品管理 + r = api("GET", "/pharmacy-manage/pending-medication/pending-medication-page", token=pha, params={"pageNum":1,"pageSize":5}) + pending = cnt(r) + rec("OP-PHARM", "待发药列表", r["ok"] and pending > 0, f"待发药={pending}") + + r = api("GET", "/pharmacy-manage/western-medicine-dispense/init", token=pha) + rec("OP-WEST", "西药发药初始化", r["ok"]) + + r = api("GET", "/drugtrace/code/page", token=pha, params={"pageNum":1,"pageSize":5}) + rec("OP-TRACE", "药品追溯", r["ok"], f"追溯码={cnt(r)}") + + # 2.8 收费 + r = api("GET", "/charge-manage/charge/init", token=fin) + rec("OP-CHARGE", "门诊收费初始化", r["ok"]) + + r = api("GET", "/charge-manage/refund/init", token=fin) + rec("OP-REFUND", "门诊退费初始化", r["ok"]) + + # 2.9 今日门诊 + r = api("GET", "/today-outpatient/stats", token=fin) + rec("OP-TODAY", "今日门诊统计", r["ok"]) + + r = api("GET", "/today-outpatient/patients", token=fin, params={"pageNum":1,"pageSize":5}) + rec("OP-TODAY-PT", "今日门诊患者", r["ok"], f"患者={cnt(r)}") + + # 业务逻辑检查:挂号初始化应该返回科室列表 + r = api("GET", "/charge-manage/register/init", token=fin) + if r["ok"]: + data = r["data"] + if isinstance(data, dict): + # 检查是否有科室、医生、号别数据 + has_dept = bool(data.get("deptList") or data.get("depts")) + has_doc = bool(data.get("doctorList") or data.get("doctors")) + if not has_dept and not has_doc: + rec("OP-REG-LOGIC", "挂号初始化数据完整性", False, "缺少科室/医生数据", + defect("高", "门诊", "挂号初始化缺少科室医生数据", "register/init返回数据不包含科室和医生列表,前端无法选择挂号", "/charge-manage/register/init", "无法完成挂号操作")) + +# ======================== 3. 住院全流程 ======================== +def test_inpatient(): + print("\n" + "="*60) + print("🏥 模块三: 住院全流程(入院→医嘱→护理→手术→出院)") + print("="*60) + doc = get_t("doctor") + nurse = get_t("nkhs") + + # 3.1 患者首页 + r = api("GET", "/patient-home-manage/init", token=nurse, params={"pageNo":1,"pageSize":5}) + patients = cnt(r) + rec("IN-HOME", "住院患者首页", r["ok"], f"在院患者={patients}") + + # 3.2 空床 + r = api("GET", "/patient-home-manage/empty-bed", token=nurse) + rec("IN-BED", "空床查询", r["ok"], f"空床={cnt(r)}") + + # 3.3 科室病区 + r = api("GET", "/patient-home-manage/caty", token=nurse) + rec("IN-CATY", "科室病区", r["ok"], f"数据={cnt(r)}") + + # 3.4 住院登记 + r = api("GET", "/inhospital-charge/register/ward-list", token=doc) + wards = cnt(r) + rec("IN-REG", "住院登记病区", r["ok"], f"病区={wards}") + + # 3.5 预交金 + r = api("GET", "/inhospital-charge/advance-payment/advance-payment-info", token=doc) + rec("IN-ADV", "预交金信息", r["ok"], f"记录={cnt(r)}") + + # 3.6 住院收费 + r = api("GET", "/charge-manage/inpatient-charge/init", token=doc) + rec("IN-CHARGE", "住院收费初始化", r["ok"]) + + # 3.7 护理记录 + r = api("GET", "/nursing-record/patient-page", token=nurse, params={"pageNo":1,"pageSize":5}) + rec("IN-NURSE", "护理记录", r["ok"], f"记录={cnt(r)}") + + # 3.8 护理模板 + r = api("GET", "/nursing-record/emr-template-page", token=nurse, params={"pageNo":1,"pageSize":5}) + rec("IN-NURSE-TPL", "护理模板", r["ok"], f"模板={cnt(r)}") + + # 3.9 生命体征 + r = api("GET", "/vital-signs/record-search", token=nurse) + rec("IN-VITAL", "生命体征查询", r["ok"]) + + r = api("GET", "/vital-signs-chart/page", token=nurse, params={"pageNo":1,"pageSize":5}) + rec("IN-VITAL-CHART", "体征图表", r["ok"], f"图表={cnt(r)}") + + # 3.10 护理评估 + r = api("GET", "/nursing-assessment-enhanced/page", token=nurse, params={"pageNo":1,"pageSize":5}) + rec("IN-ASSESS", "护理评估", r["ok"], f"评估={cnt(r)}") + + # 3.11 护理提醒 + r = api("GET", "/nursing-enhanced/reminder/page", token=nurse, params={"pageNo":1,"pageSize":5}) + rec("IN-REMIND", "护理提醒", r["ok"], f"提醒={cnt(r)}") + + # 3.12 护理质量 + r = api("GET", "/nursing-quality/page", token=nurse, params={"pageNo":1,"pageSize":5}) + rec("IN-QUALITY", "护理质量", r["ok"], f"质量={cnt(r)}") + + # 3.13 医嘱执行 + r = api("GET", "/nursing-execution/scan/page", token=nurse, params={"pageNo":1,"pageSize":5}) + rec("IN-EXEC", "医嘱执行", r["ok"], f"执行={cnt(r)}") + + # 3.14 护理交班 + r = api("GET", "/nursing-execution/handoff/page", token=nurse, params={"pageNo":1,"pageSize":5}) + rec("IN-HANDOFF", "护理交班", r["ok"], f"交班={cnt(r)}") + + # 3.15 护理输液 + r = api("GET", "/nursing-execution/infusion/page", token=nurse, params={"pageNo":1,"pageSize":5}) + rec("IN-INFUSION", "护理输液", r["ok"], f"输液={cnt(r)}") + + # 3.16 出院管理 - 已知bug: route不存在 + r = api("GET", "/discharge/page", token=doc, params={"pageNum":1,"pageSize":5}) + if not r["ok"]: + rec("IN-DISCHARGE", "出院管理", False, f"bug: {r['msg'][:50]}", + defect("高", "住院", "出院管理接口不存在", "discharge/page路由缺失,返回500", "/discharge/page", "无法管理出院流程")) + + # 业务逻辑检查 + if patients == 0: + rec("IN-HOME-DATA", "住院患者数据", False, "在院患者为0", + defect("中", "住院", "住院患者首页无数据", "patient-home-manage/init返回0条在院患者记录", "/patient-home-manage/init", "住院模块无业务数据")) + +# ======================== 4. 手术全流程 ======================== +def test_surgery(): + print("\n" + "="*60) + print("🔪 模块四: 手术全流程(申请→讨论→排程→执行→记录)") + print("="*60) + doc = get_t("doctor") + nurse = get_t("ssshs") + + r = api("GET", "/clinical-manage/surgery/surgery-page", token=doc, params={"pageNum":1,"pageSize":5}) + rec("SUR-APPLY", "手术申请", r["ok"], f"申请={cnt(r)}") + + r = api("GET", "/preop-discussion/page", token=doc, params={"pageNum":1,"pageSize":5}) + rec("SUR-DISC", "术前讨论", r["ok"], f"讨论={cnt(r)}") + + r = api("GET", "/clinical-manage/surgery-schedule/page", token=nurse, params={"pageNum":1,"pageSize":5}) + rec("SUR-SCHED", "手术排程", r["ok"], f"排程={cnt(r)}") + + r = api("GET", "/surgery-safety-check/page", token=nurse, params={"pageNum":1,"pageSize":5}) + rec("SUR-SAFETY", "手术安全核查", r["ok"], f"核查={cnt(r)}") + + # 麻醉记录 - 已知bug + r = api("GET", "/anesthesia/record", token=doc) + if not r["ok"]: + rec("SUR-ANES", "麻醉记录", False, f"bug: {r['msg'][:50]}", + defect("高", "手术", "麻醉记录接口路由缺失", "anesthesia/record返回404", "/anesthesia/record", "无法查看麻醉记录")) + + r = api("GET", "/anesthesia-enhanced/followup/page", token=doc, params={"pageNum":1,"pageSize":5}) + rec("SUR-FOLLOW", "麻醉随访", r["ok"], f"随访={cnt(r)}") + + r = api("GET", "/cross-module/surgery-pathology/page", token=doc, params={"pageNum":1,"pageSize":5}) + rec("SUR-PATHO", "手术病理追踪", r["ok"], f"病理={cnt(r)}") + +# ======================== 5. 医技检查 ======================== +def test_inspection(): + print("\n" + "="*60) + print("🔬 模块五: 医技检查(申请→采样→检验→报告)") + print("="*60) + tech = get_t("tech") + doc = get_t("doctor") + + # 检验配置 - 已知bug + r = api("GET", "/inspection/lisConfig/init-page", token=tech, params={"pageNum":1,"pageSize":5}) + if not r["ok"]: + rec("INS-LIS", "检验配置", False, f"bug: {r['msg'][:50]}", + defect("高", "医技", "检验配置参数类型错误", "lisConfig/init-page空参时NPE", "/inspection/lisConfig/init-page", "无法配置检验科")) + + # 检验标本 - DB错误 + r = api("GET", "/inspection/specimen/information-page", token=tech, params={"pageNum":1,"pageSize":5}) + if not r["ok"]: + rec("INS-SPEC", "检验标本", False, f"DB错误: {r['msg'][:50]}", + defect("高", "医技", "检验标本查询DB错误", "specimen表字段缺失导致SQL异常", "/inspection/specimen/information-page", "无法管理检验标本")) + + # 检验仪器 - DB错误 + r = api("GET", "/inspection/instrument/information-page", token=tech, params={"pageNum":1,"pageSize":5}) + if not r["ok"]: + rec("INS-INST", "检验仪器", False, f"DB错误: {r['msg'][:50]}", + defect("高", "医技", "检验仪器查询DB错误", "instrument表字段缺失", "/inspection/instrument/information-page", "无法管理检验仪器")) + + # 检验观察 - DB错误 + r = api("GET", "/inspection/observation/information-page", token=tech, params={"pageNum":1,"pageSize":5}) + if not r["ok"]: + rec("INS-OBS", "检验观察", False, f"DB错误: {r['msg'][:50]}", + defect("高", "医技", "检验观察查询DB错误", "observation表字段缺失", "/inspection/observation/information-page", "无法查看检验观察")) + + r = api("GET", "/specimen-barcode/page", token=tech, params={"pageNum":1,"pageSize":5}) + rec("INS-BARCODE", "标本条码", r["ok"], f"条码={cnt(r)}") + + r = api("GET", "/radiology-enhanced/urgent-report/page", token=tech, params={"pageNum":1,"pageSize":5}) + rec("INS-RAD-URG", "影像急报", r["ok"], f"急报={cnt(r)}") + + # 影像统计 - DB错误 + r = api("GET", "/radiology-enhanced/statistics/page", token=tech, params={"pageNum":1,"pageSize":5}) + if not r["ok"]: + rec("INS-RAD-STAT", "影像统计", False, f"DB错误: {r['msg'][:50]}", + defect("中", "医技", "影像统计DB错误", "radiology统计表字段缺失", "/radiology-enhanced/statistics/page", "无法统计影像数据")) + + # 影像对比 - 缺少参数 + r = api("GET", "/radiology-comparison/compare", token=tech) + if not r["ok"]: + rec("INS-COMP", "影像对比", False, f"参数错误: {r['msg'][:50]}", + defect("中", "医技", "影像对比缺少必填参数", "compare接口需要patientId参数但未说明", "/radiology-comparison/compare", "影像对比功能不可用")) + + r = api("GET", "/reconstruction/task/page", token=tech, params={"pageNum":1,"pageSize":5}) + rec("INS-3D", "3D重建任务", r["ok"], f"任务={cnt(r)}") + + r = api("GET", "/reconstruction/report/page", token=tech, params={"pageNum":1,"pageSize":5}) + rec("INS-3D-RPT", "3D重建报告", r["ok"], f"报告={cnt(r)}") + + r = api("GET", "/radiology-image/report/page", token=tech, params={"pageNum":1,"pageSize":5}) + rec("INS-RAD-RPT", "影像报告", r["ok"], f"报告={cnt(r)}") + + r = api("GET", "/lab-ref-range/page", token=tech, params={"pageNum":1,"pageSize":5}) + rec("INS-REF", "参考范围", r["ok"], f"范围={cnt(r)}") + + r = api("GET", "/fhir-cda/cda/page", token=tech, params={"pageNum":1,"pageSize":5}) + rec("INS-CDA", "CDA文档", r["ok"], f"CDA={cnt(r)}") + + r = api("GET", "/informed-consent/page", token=tech, params={"pageNum":1,"pageSize":5}) + rec("INS-CONSENT", "知情同意", r["ok"], f"同意书={cnt(r)}") + +# ======================== 6. 院感管理 ======================== +def test_infection(): + print("\n" + "="*60) + print("🦠 模块六: 院感管理") + print("="*60) + nurse = get_t("nkhs") + + r = api("GET", "/infection-enhanced/surveillance/page", token=nurse, params={"pageNum":1,"pageSize":5}) + rec("INF-SURV", "院感监测", r["ok"], f"监测={cnt(r)}") + + # 院感预警 - 路由缺失 + r = api("GET", "/infection-enhanced/warning/page", token=nurse, params={"pageNum":1,"pageSize":5}) + if not r["ok"]: + rec("INF-WARN", "院感预警", False, f"路由缺失: {r['msg'][:50]}", + defect("高", "院感", "院感预警接口路由缺失", "infection-enhanced/warning/page返回404", "/infection-enhanced/warning/page", "无法查看院感预警")) + + r = api("GET", "/infection-enhanced/mdr/page", token=nurse, params={"pageNum":1,"pageSize":5}) + rec("INF-MDR", "耐药监测", r["ok"], f"耐药={cnt(r)}") + + # 职业暴露 - 路由缺失 + r = api("GET", "/infection-enhanced/exposure/page", token=nurse, params={"pageNum":1,"pageSize":5}) + if not r["ok"]: + rec("INF-EXPO", "职业暴露", False, f"路由缺失: {r['msg'][:50]}", + defect("高", "院感", "职业暴露接口路由缺失", "infection-enhanced/exposure/page返回404", "/infection-enhanced/exposure/page", "无法管理职业暴露")) + + r = api("GET", "/infection-enhanced/hand-hygiene/page", token=nurse, params={"pageNum":1,"pageSize":5}) + rec("INF-HAND", "手卫生", r["ok"], f"手卫生={cnt(r)}") + + r = api("GET", "/infection-enhanced/env-monitor/page", token=nurse, params={"pageNum":1,"pageSize":5}) + rec("INF-ENV", "环境监测", r["ok"], f"环境={cnt(r)}") + +# ======================== 7. 质量管理 ======================== +def test_quality(): + print("\n" + "="*60) + print("📊 模块七: 质量管理") + print("="*60) + t = get_t() + + r = api("GET", "/quality-enhanced/indicator/page", token=t, params={"pageNum":1,"pageSize":5}) + rec("QA-IND", "质量指标", r["ok"], f"指标={cnt(r)}") + + r = api("GET", "/quality-enhanced/order-stats/page", token=t, params={"pageNum":1,"pageSize":5}) + rec("QA-ORDER", "医嘱统计", r["ok"], f"统计={cnt(r)}") + + r = api("GET", "/api/v1/review/plans", token=t, params={"pageNum":1,"pageSize":5}) + rec("QA-REVIEW", "处方点评计划", r["ok"], f"计划={cnt(r)}") + + r = api("GET", "/api/v1/review/statistics", token=t) + rec("QA-REVIEW-S", "处方点评统计", r["ok"]) + + r = api("GET", "/api/v1/rational-drug/interaction-rules", token=t, params={"pageNum":1,"pageSize":5}) + rec("QA-RULES", "用药规则", r["ok"], f"规则={cnt(r)}") + + r = api("GET", "/api/v1/rational-drug/statistics", token=t) + rec("QA-RULES-S", "用药统计", r["ok"]) + + r = api("GET", "/api/v1/critical-value/pending", token=t, params={"pageNum":1,"pageSize":5}) + rec("QA-CRIT", "危急值", r["ok"], f"危急值={cnt(r)}") + + r = api("GET", "/api/v1/order-closed-loop/list", token=t, params={"pageNum":1,"pageSize":5}) + rec("QA-CLOSED", "医嘱闭环", r["ok"], f"闭环={cnt(r)}") + + r = api("GET", "/clinical-pathway/page", token=t, params={"pageNum":1,"pageSize":5}) + rec("QA-PATHWAY", "临床路径", r["ok"], f"路径={cnt(r)}") + + r = api("GET", "/api/v1/emr-quality/defect-statistics", token=t) + rec("QA-EMR", "病历质量", r["ok"]) + +# ======================== 8. 中医管理 ======================== +def test_tcm(): + print("\n" + "="*60) + print("🌿 模块八: 中医管理") + print("="*60) + doc = get_t("doctor") + + r = api("GET", "/api/v1/tcm/prescriptions", token=doc, params={"pageNum":1,"pageSize":5}) + rec("TCM-PRES", "中医方剂", r["ok"], f"方剂={cnt(r)}") + + r = api("GET", "/api/v1/tcm/statistics", token=doc) + rec("TCM-STAT", "中医统计", r["ok"]) + + r = api("GET", "/doctor-station/chinese-medical/condition-info", token=doc) + rec("TCM-DX", "中医辨证", r["ok"], f"辨证项={cnt(r)}") + +# ======================== 9. 急诊管理 ======================== +def test_emergency(): + print("\n" + "="*60) + print("🚑 模块九: 急诊管理") + print("="*60) + jzys = get_t("jzys") + jzhs = get_t("jzhs") + + r = api("GET", "/emergency/triage/page", token=jzys, params={"pageNum":1,"pageSize":5}) + rec("EM-TRIAGE", "急诊分诊", r["ok"], f"分诊={cnt(r)}") + + r = api("GET", "/triage/queue/list", token=jzhs) + rec("EM-QUEUE", "叫号队列", r["ok"]) + +# ======================== 10. 会诊管理 ======================== +def test_consultation(): + print("\n" + "="*60) + print("🤝 模块十: 会诊管理") + print("="*60) + doc = get_t("doctor") + + r = api("GET", "/consultation/list", token=doc, params={"pageNum":1,"pageSize":5}) + rec("CS-LIST", "会诊列表", r["ok"], f"会诊={cnt(r)}") + + r = api("GET", "/consultation/departmentTree", token=doc) + rec("CS-DEPT", "会诊科室树", r["ok"], f"科室={cnt(r)}") + + r = api("GET", "/cross-module/consultation-timeout/page", token=doc, params={"pageNum":1,"pageSize":5}) + rec("CS-TIMEOUT", "会诊超时", r["ok"], f"超时={cnt(r)}") + +# ======================== 11. 病案管理 ======================== +def test_medical_record(): + print("\n" + "="*60) + print("📁 模块十一: 病案管理") + print("="*60) + t = get_t() + + # 病案统计 - 缺少必填参数 + r = api("GET", "/api/v1/mr-homepage/statistics", token=t) + if not r["ok"]: + rec("MR-STAT", "病案统计", False, f"参数错误: {r['msg'][:50]}", + defect("中", "病案", "病案统计缺少必填参数", "statistics需要startDate参数但接口文档未说明", "/api/v1/mr-homepage/statistics", "无法统计病案")) + + # DRG - DB错误 + r = api("GET", "/mr-drg/page", token=t, params={"pageNum":1,"pageSize":5}) + if not r["ok"]: + rec("MR-DRG", "DRG分组", False, f"DB错误: {r['msg'][:50]}", + defect("高", "病案", "DRG分组查询DB错误", "DRG表字段缺失导致SQL异常", "/mr-drg/page", "无法进行DRG分组")) + + r = api("GET", "/emr-archive/page", token=t, params={"pageNum":1,"pageSize":5}) + rec("MR-ARCH", "病案归档", r["ok"], f"归档={cnt(r)}") + + r = api("GET", "/cross-module/mr-quality/page", token=t, params={"pageNum":1,"pageSize":5}) + rec("MR-QUALITY", "病历质量", r["ok"], f"质量={cnt(r)}") + +# ======================== 12. 经营分析 ======================== +def test_analytics(): + print("\n" + "="*60) + print("📈 模块十二: 经营分析") + print("="*60) + t = get_t() + + r = api("GET", "/business-analytics/page", token=t, params={"pageNum":1,"pageSize":5}) + rec("AN-PAGE", "经营分析", r["ok"], f"分析={cnt(r)}") + + r = api("GET", "/business-analytics/summary", token=t) + rec("AN-SUM", "经营汇总", r["ok"]) + + r = api("GET", "/pharmacy-stock-alert/page", token=t, params={"pageNum":1,"pageSize":5}) + rec("AN-STOCK", "库存预警", r["ok"], f"预警={cnt(r)}") + + r = api("GET", "/cross-module/drg-performance/summary", token=t) + if not r["ok"]: + rec("AN-DRG", "DRG绩效", False, f"参数错误: {r['msg'][:50]}", + defect("中", "经营", "DRG绩效缺少必填参数", "summary需要statMonth参数", "/cross-module/drg-performance/summary", "无法查看DRG绩效")) + + r = api("GET", "/cross-module/drug-expiry/page", token=t, params={"pageNum":1,"pageSize":5}) + rec("AN-EXPIRY", "药品效期", r["ok"], f"效期={cnt(r)}") + +# ======================== 13. 跨模块数据一致性 ======================== +def test_cross_module(): + print("\n" + "="*60) + print("🔗 模块十三: 跨模块数据一致性") + print("="*60) + t = get_t() + + # 手术病理联动 + r = api("GET", "/cross-module/surgery-pathology/page", token=t, params={"pageNum":1,"pageSize":5}) + rec("XM-PATHO", "手术→病理联动", r["ok"], f"病理={cnt(r)}") + + # 处方点评 + r = api("GET", "/cross-module/prescription-review/page", token=t, params={"pageNum":1,"pageSize":5}) + rec("XM-REVIEW", "处方点评联动", r["ok"], f"点评={cnt(r)}") + + # 实验室预警 + r = api("GET", "/cross-module/lab-alert/page", token=t, params={"pageNum":1,"pageSize":5}) + rec("XM-LAB", "实验室预警", r["ok"], f"预警={cnt(r)}") + + # 药品效期 + r = api("GET", "/cross-module/drug-expiry/page", token=t, params={"pageNum":1,"pageSize":5}) + rec("XM-EXPIRY", "药品效期联动", r["ok"], f"效期={cnt(r)}") + +# ======================== 14. 基础数据 ======================== +def test_base_data(): + print("\n" + "="*60) + print("📋 模块十四: 基础数据管理") + print("="*60) + t = get_t() + + r = api("GET", "/base-data-manage/organization/organization", token=t, params={"pageNum":1,"pageSize":5}) + rec("BD-ORG", "组织管理", r["ok"], f"组织={cnt(r)}") + + r = api("GET", "/base-data-manage/location/location-page", token=t, params={"pageNum":1,"pageSize":5}) + rec("BD-LOC", "科室管理", r["ok"], f"科室={cnt(r)}") + + r = api("GET", "/base-data-manage/practitioner/user-practitioner-page", token=t, params={"pageNum":1,"pageSize":5}) + rec("BD-PRACT", "人员管理", r["ok"], f"人员={cnt(r)}") + + # ICD10 - DB错误 + r = api("GET", "/icd10/page", token=t, params={"pageNum":1,"pageSize":5}) + if not r["ok"]: + rec("BD-ICD", "ICD10", False, f"DB错误: {r['msg'][:50]}", + defect("高", "基础数据", "ICD10查询DB错误", "icd10表字段缺失导致SQL异常", "/icd10/page", "无法管理ICD10编码")) + + # 数据字典 - 路由缺失 + r = api("GET", "/dict-dictionary/definition/page", token=t, params={"pageNum":1,"pageSize":5}) + if not r["ok"]: + rec("BD-DICT", "数据字典", False, f"路由缺失: {r['msg'][:50]}", + defect("中", "基础数据", "数据字典接口路由缺失", "dict-dictionary/definition/page返回404", "/dict-dictionary/definition/page", "无法管理数据字典")) + + r = api("GET", "/check/method/list", token=t) + rec("BD-CHECK", "检查方法", r["ok"], f"方法={cnt(r)}") + + r = api("GET", "/check/part/list", token=t) + rec("BD-PART", "检查部位", r["ok"], f"部位={cnt(r)}") + +# ======================== 15. 系统管理 ======================== +def test_system(): + print("\n" + "="*60) + print("⚙️ 模块十五: 系统管理") + print("="*60) + t = get_t() + + r = api("GET", "/system/user/list", token=t, params={"pageNum":1,"pageSize":5}) + rec("SYS-USER", "用户列表", r["ok"], f"用户={r['raw'].get('total',0) if r['raw'] else 0}") + + r = api("GET", "/system/role/list", token=t, params={"pageNum":1,"pageSize":5}) + rec("SYS-ROLE", "角色列表", r["ok"], f"角色={r['raw'].get('total',0) if r['raw'] else 0}") + + r = api("GET", "/system/dept/list", token=t) + rec("SYS-DEPT", "部门列表", r["ok"], f"部门={cnt(r)}") + + r = api("GET", "/system/dict/type/list", token=t, params={"pageNum":1,"pageSize":5}) + rec("SYS-DICT", "字典类型", r["ok"], f"字典={r['raw'].get('total',0) if r['raw'] else 0}") + + r = api("GET", "/system/notice/list", token=t, params={"pageNum":1,"pageSize":5}) + rec("SYS-NOTICE", "通知公告", r["ok"], f"公告={r['raw'].get('total',0) if r['raw'] else 0}") + + r = api("GET", "/system/config/list", token=t, params={"pageNum":1,"pageSize":5}) + rec("SYS-CONFIG", "系统配置", r["ok"], f"配置={r['raw'].get('total',0) if r['raw'] else 0}") + +# ======================== 16. 多角色协作场景 ======================== +def test_multi_role(): + print("\n" + "="*60) + print("👥 模块十六: 多角色协作场景") + print("="*60) + + # 场景1: 门诊挂号→就诊→开方→收费→取药 + print("\n 📋 场景1: 门诊全流程") + fin = get_t("finance") + doc = get_t("doctor") + pha = get_t("pharmacist") + + # 收费员挂号 + r1 = api("GET", "/charge-manage/register/init", token=fin) + rec("MR-01-REG", "收费员→挂号初始化", r1["ok"]) + + # 医生接诊 + r2 = api("GET", "/doctor-station/main/init", token=doc) + rec("MR-02-DOC", "医生→接诊初始化", r2["ok"]) + + # 医生开医嘱 + r3 = api("GET", "/doctor-station/advice/advice-base-info", token=doc) + rec("MR-03-ADV", "医生→开医嘱", r3["ok"], f"医嘱数={cnt(r3)}") + + # 医生开处方 + r4 = api("GET", "/doctor-station/elep/init", token=doc) + rec("MR-04-RX", "医生→开处方", r4["ok"]) + + # 药师发药 + r5 = api("GET", "/pharmacy-manage/pending-medication/pending-medication-page", token=pha, params={"pageNum":1,"pageSize":5}) + rec("MR-05-PHARM", "药师→待发药", r5["ok"], f"待发药={cnt(r5)}") + + # 收费员收费 + r6 = api("GET", "/charge-manage/charge/init", token=fin) + rec("MR-06-CHARGE", "收费员→收费", r6["ok"]) + + # 场景2: 住院入院→护理→手术→出院 + print("\n 📋 场景2: 住院全流程") + doc = get_t("doctor") + nurse = get_t("nkhs") + ssshs = get_t("ssshs") + + # 护士接收患者 + r7 = api("GET", "/patient-home-manage/init", token=nurse, params={"pageNo":1,"pageSize":5}) + rec("MR-07-NURSE", "护士→接收患者", r7["ok"], f"在院={cnt(r7)}") + + # 医生下医嘱 + r8 = api("GET", "/doctor-station/advice/advice-base-info", token=doc) + rec("MR-08-ADV", "医生→住院医嘱", r8["ok"]) + + # 护士执行医嘱 + r9 = api("GET", "/nursing-execution/scan/page", token=nurse, params={"pageNo":1,"pageSize":5}) + rec("MR-09-EXEC", "护士→执行医嘱", r9["ok"], f"执行={cnt(r9)}") + + # 护理记录 + r10 = api("GET", "/nursing-record/patient-page", token=nurse, params={"pageNo":1,"pageSize":5}) + rec("MR-10-NURSE-REC", "护士→护理记录", r10["ok"], f"记录={cnt(r10)}") + + # 手术室护士排程 + r11 = api("GET", "/clinical-manage/surgery-schedule/page", token=ssshs, params={"pageNum":1,"pageSize":5}) + rec("MR-11-SURG", "手术室→手术排程", r11["ok"], f"排程={cnt(r11)}") + + # 场景3: 急诊分诊→急诊处理 + print("\n 📋 场景3: 急诊全流程") + jzys = get_t("jzys") + jzhs = get_t("jzhs") + + r12 = api("GET", "/emergency/triage/page", token=jzys, params={"pageNum":1,"pageSize":5}) + rec("MR-12-EM-TRIAGE", "急诊医生→分诊", r12["ok"], f"分诊={cnt(r12)}") + + r13 = api("GET", "/triage/queue/list", token=jzhs) + rec("MR-13-EM-QUEUE", "急诊护士→叫号", r13["ok"]) + + # 场景4: 会诊协作 + print("\n 📋 场景4: 会诊协作") + doc = get_t("doctor") + consultant = get_t("consultant") + + r14 = api("GET", "/consultation/list", token=doc, params={"pageNum":1,"pageSize":5}) + rec("MR-14-CS-DOC", "医生→会诊申请", r14["ok"], f"会诊={cnt(r14)}") + + r15 = api("GET", "/consultation/departmentTree", token=consultant) + rec("MR-15-CS-CON", "专家→会诊科室", r15["ok"]) + +# ======================== 主函数 ======================== +def main(): + global P, F + print("="*70) + print("🏥 HealthLink-HIS 三甲医院全链路业务逻辑测试") + print(f"📅 {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") + print(f"🌐 {BASE}") + print("="*70) + + print("\n🔑 登录所有角色...") + login_all() + print(f" 成功登录: {len(TOKENS)}/{len(USERS)}") + + test_auth() + test_outpatient() + test_inpatient() + test_surgery() + test_inspection() + test_infection() + test_quality() + test_tcm() + test_emergency() + test_consultation() + test_medical_record() + test_analytics() + test_cross_module() + test_base_data() + test_system() + test_multi_role() + + total = P + F + rate = (P/total*100) if total > 0 else 0 + + print("\n" + "="*70) + print("📊 测试结果汇总") + print("="*70) + print(f" 总用例数: {total}") + print(f" 通过: ✅ {P}") + print(f" 失败: ❌ {F}") + print(f" 通过率: {rate:.1f}%") + + # 缺陷报告 + if DEFECTS: + print(f"\n" + "="*70) + print(f"🐛 发现缺陷: {len(DEFECTS)}个") + print("="*70) + + # 按严重程度排序 + severity_order = {"致命": 0, "高": 1, "中": 2, "低": 3} + DEFECTS.sort(key=lambda d: severity_order.get(d["severity"], 99)) + + for i, d in enumerate(DEFECTS, 1): + sev_icon = {"致命": "🔴", "高": "🟠", "中": "🟡", "低": "🟢"}.get(d["severity"], "⚪") + print(f"\n {sev_icon} 缺陷#{i} [{d['severity']}] {d['title']}") + print(f" 模块: {d['module']}") + print(f" 描述: {d['desc']}") + print(f" 接口: {d['api']}") + print(f" 影响: {d['impact']}") + + # 失败用例 + if F > 0: + print(f"\n❌ 失败用例 ({F}个):") + for r in R: + if not r["ok"]: + print(f" - [{r['id']}] {r['name']}: {r['detail']}") + + # 保存报告 + report = { + "test_time": datetime.now().isoformat(), + "environment": BASE, + "total": total, "passed": P, "failed": F, "pass_rate": f"{rate:.1f}%", + "defects": DEFECTS, + "results": R, + } + path = "/root/.openclaw/workspace/his-repo/MD/test/reports/07_full_chain_report.json" + os.makedirs(os.path.dirname(path), exist_ok=True) + with open(path, "w") as f: + json.dump(report, f, ensure_ascii=False, indent=2) + print(f"\n📄 报告: {path}") + return 0 if F == 0 else 1 + +if __name__ == "__main__": + sys.exit(main()) diff --git a/MD/test/reports/07_full_chain_report.json b/MD/test/reports/07_full_chain_report.json new file mode 100644 index 000000000..246e2e9a7 --- /dev/null +++ b/MD/test/reports/07_full_chain_report.json @@ -0,0 +1,970 @@ +{ + "test_time": "2026-06-08T08:40:59.640644", + "environment": "http://localhost:18082/healthlink-his", + "total": 137, + "passed": 119, + "failed": 18, + "pass_rate": "86.9%", + "defects": [ + { + "severity": "高", + "module": "门诊", + "title": "挂号初始化无科室数据", + "desc": "register/init返回的科室列表为空", + "api": "/charge-manage/register/init", + "impact": "无法挂号" + }, + { + "severity": "高", + "module": "门诊", + "title": "挂号初始化缺少科室医生数据", + "desc": "register/init返回数据不包含科室和医生列表,前端无法选择挂号", + "api": "/charge-manage/register/init", + "impact": "无法完成挂号操作" + }, + { + "severity": "高", + "module": "住院", + "title": "出院管理接口不存在", + "desc": "discharge/page路由缺失,返回500", + "api": "/discharge/page", + "impact": "无法管理出院流程" + }, + { + "severity": "高", + "module": "手术", + "title": "麻醉记录接口路由缺失", + "desc": "anesthesia/record返回404", + "api": "/anesthesia/record", + "impact": "无法查看麻醉记录" + }, + { + "severity": "高", + "module": "医技", + "title": "检验配置参数类型错误", + "desc": "lisConfig/init-page空参时NPE", + "api": "/inspection/lisConfig/init-page", + "impact": "无法配置检验科" + }, + { + "severity": "高", + "module": "医技", + "title": "检验标本查询DB错误", + "desc": "specimen表字段缺失导致SQL异常", + "api": "/inspection/specimen/information-page", + "impact": "无法管理检验标本" + }, + { + "severity": "高", + "module": "医技", + "title": "检验仪器查询DB错误", + "desc": "instrument表字段缺失", + "api": "/inspection/instrument/information-page", + "impact": "无法管理检验仪器" + }, + { + "severity": "高", + "module": "医技", + "title": "检验观察查询DB错误", + "desc": "observation表字段缺失", + "api": "/inspection/observation/information-page", + "impact": "无法查看检验观察" + }, + { + "severity": "高", + "module": "院感", + "title": "院感预警接口路由缺失", + "desc": "infection-enhanced/warning/page返回404", + "api": "/infection-enhanced/warning/page", + "impact": "无法查看院感预警" + }, + { + "severity": "高", + "module": "院感", + "title": "职业暴露接口路由缺失", + "desc": "infection-enhanced/exposure/page返回404", + "api": "/infection-enhanced/exposure/page", + "impact": "无法管理职业暴露" + }, + { + "severity": "高", + "module": "病案", + "title": "DRG分组查询DB错误", + "desc": "DRG表字段缺失导致SQL异常", + "api": "/mr-drg/page", + "impact": "无法进行DRG分组" + }, + { + "severity": "高", + "module": "基础数据", + "title": "ICD10查询DB错误", + "desc": "icd10表字段缺失导致SQL异常", + "api": "/icd10/page", + "impact": "无法管理ICD10编码" + }, + { + "severity": "中", + "module": "医技", + "title": "影像统计DB错误", + "desc": "radiology统计表字段缺失", + "api": "/radiology-enhanced/statistics/page", + "impact": "无法统计影像数据" + }, + { + "severity": "中", + "module": "医技", + "title": "影像对比缺少必填参数", + "desc": "compare接口需要patientId参数但未说明", + "api": "/radiology-comparison/compare", + "impact": "影像对比功能不可用" + }, + { + "severity": "中", + "module": "病案", + "title": "病案统计缺少必填参数", + "desc": "statistics需要startDate参数但接口文档未说明", + "api": "/api/v1/mr-homepage/statistics", + "impact": "无法统计病案" + }, + { + "severity": "中", + "module": "经营", + "title": "DRG绩效缺少必填参数", + "desc": "summary需要statMonth参数", + "api": "/cross-module/drg-performance/summary", + "impact": "无法查看DRG绩效" + }, + { + "severity": "中", + "module": "基础数据", + "title": "数据字典接口路由缺失", + "desc": "dict-dictionary/definition/page返回404", + "api": "/dict-dictionary/definition/page", + "impact": "无法管理数据字典" + } + ], + "results": [ + { + "id": "AUTH-admin", + "name": "超级管理员登录", + "ok": true, + "detail": "token=✓" + }, + { + "id": "AUTH-doctor", + "name": "医生登录", + "ok": true, + "detail": "token=✓" + }, + { + "id": "AUTH-jzys", + "name": "急诊医生登录", + "ok": true, + "detail": "token=✓" + }, + { + "id": "AUTH-jzhs", + "name": "急诊护士登录", + "ok": true, + "detail": "token=✓" + }, + { + "id": "AUTH-nkhs", + "name": "内科护士登录", + "ok": true, + "detail": "token=✓" + }, + { + "id": "AUTH-ssshs", + "name": "手术室护士登录", + "ok": true, + "detail": "token=✓" + }, + { + "id": "AUTH-pharmacist", + "name": "药师登录", + "ok": true, + "detail": "token=✓" + }, + { + "id": "AUTH-tech", + "name": "医技登录", + "ok": true, + "detail": "token=✓" + }, + { + "id": "AUTH-finance", + "name": "收费员登录", + "ok": true, + "detail": "token=✓" + }, + { + "id": "AUTH-consultant", + "name": "会诊专家登录", + "ok": true, + "detail": "token=✓" + }, + { + "id": "AUTH-ERR", + "name": "错误密码拒绝", + "ok": true, + "detail": "code=500" + }, + { + "id": "AUTH-INFO", + "name": "获取用户信息", + "ok": false, + "detail": "has_user=False" + }, + { + "id": "AUTH-MENU", + "name": "获取菜单路由", + "ok": true, + "detail": "一级菜单=45" + }, + { + "id": "AUTH-PERM-doctor", + "name": "医生→用户管理", + "ok": true, + "detail": "已隔离(code=403)" + }, + { + "id": "AUTH-PERM-pharmacist", + "name": "药师→角色管理", + "ok": true, + "detail": "已隔离(code=403)" + }, + { + "id": "AUTH-PERM-finance", + "name": "收费员→系统配置", + "ok": true, + "detail": "已隔离(code=403)" + }, + { + "id": "OP-REG", + "name": "挂号初始化", + "ok": true, + "detail": "有数据=True" + }, + { + "id": "OP-REG-DEPT", + "name": "挂号科室列表", + "ok": false, + "detail": "科室列表为空" + }, + { + "id": "OP-DOC", + "name": "医生站初始化", + "ok": true, + "detail": "" + }, + { + "id": "OP-ADVICE", + "name": "医嘱列表", + "ok": true, + "detail": "医嘱数=5146" + }, + { + "id": "OP-DX", + "name": "诊断初始化", + "ok": true, + "detail": "" + }, + { + "id": "OP-INS", + "name": "检验申请", + "ok": true, + "detail": "申请单=0" + }, + { + "id": "OP-EMR", + "name": "电子病历列表", + "ok": true, + "detail": "病历=0" + }, + { + "id": "OP-EMR-TPL", + "name": "病历模板", + "ok": true, + "detail": "模板=0" + }, + { + "id": "OP-TREAT", + "name": "门诊治疗", + "ok": true, + "detail": "" + }, + { + "id": "OP-INFUSION", + "name": "门诊输液", + "ok": true, + "detail": "" + }, + { + "id": "OP-PHARM", + "name": "待发药列表", + "ok": true, + "detail": "待发药=534" + }, + { + "id": "OP-WEST", + "name": "西药发药初始化", + "ok": true, + "detail": "" + }, + { + "id": "OP-TRACE", + "name": "药品追溯", + "ok": true, + "detail": "追溯码=3" + }, + { + "id": "OP-CHARGE", + "name": "门诊收费初始化", + "ok": true, + "detail": "" + }, + { + "id": "OP-REFUND", + "name": "门诊退费初始化", + "ok": true, + "detail": "" + }, + { + "id": "OP-TODAY", + "name": "今日门诊统计", + "ok": true, + "detail": "" + }, + { + "id": "OP-TODAY-PT", + "name": "今日门诊患者", + "ok": true, + "detail": "患者=0" + }, + { + "id": "OP-REG-LOGIC", + "name": "挂号初始化数据完整性", + "ok": false, + "detail": "缺少科室/医生数据" + }, + { + "id": "IN-HOME", + "name": "住院患者首页", + "ok": true, + "detail": "在院患者=31" + }, + { + "id": "IN-BED", + "name": "空床查询", + "ok": true, + "detail": "空床=0" + }, + { + "id": "IN-CATY", + "name": "科室病区", + "ok": true, + "detail": "数据=0" + }, + { + "id": "IN-REG", + "name": "住院登记病区", + "ok": true, + "detail": "病区=6" + }, + { + "id": "IN-ADV", + "name": "预交金信息", + "ok": true, + "detail": "记录=71" + }, + { + "id": "IN-CHARGE", + "name": "住院收费初始化", + "ok": true, + "detail": "" + }, + { + "id": "IN-NURSE", + "name": "护理记录", + "ok": true, + "detail": "记录=44" + }, + { + "id": "IN-NURSE-TPL", + "name": "护理模板", + "ok": true, + "detail": "模板=1" + }, + { + "id": "IN-VITAL", + "name": "生命体征查询", + "ok": true, + "detail": "" + }, + { + "id": "IN-VITAL-CHART", + "name": "体征图表", + "ok": true, + "detail": "图表=0" + }, + { + "id": "IN-ASSESS", + "name": "护理评估", + "ok": true, + "detail": "评估=19" + }, + { + "id": "IN-REMIND", + "name": "护理提醒", + "ok": true, + "detail": "提醒=0" + }, + { + "id": "IN-QUALITY", + "name": "护理质量", + "ok": true, + "detail": "质量=4" + }, + { + "id": "IN-EXEC", + "name": "医嘱执行", + "ok": true, + "detail": "执行=0" + }, + { + "id": "IN-HANDOFF", + "name": "护理交班", + "ok": true, + "detail": "交班=0" + }, + { + "id": "IN-INFUSION", + "name": "护理输液", + "ok": true, + "detail": "输液=0" + }, + { + "id": "IN-DISCHARGE", + "name": "出院管理", + "ok": false, + "detail": "bug: No static resource discharge/page for request '/he" + }, + { + "id": "SUR-APPLY", + "name": "手术申请", + "ok": true, + "detail": "申请=130" + }, + { + "id": "SUR-DISC", + "name": "术前讨论", + "ok": true, + "detail": "讨论=0" + }, + { + "id": "SUR-SCHED", + "name": "手术排程", + "ok": true, + "detail": "排程=53" + }, + { + "id": "SUR-SAFETY", + "name": "手术安全核查", + "ok": true, + "detail": "核查=1" + }, + { + "id": "SUR-ANES", + "name": "麻醉记录", + "ok": false, + "detail": "bug: No static resource anesthesia/record for request '" + }, + { + "id": "SUR-FOLLOW", + "name": "麻醉随访", + "ok": true, + "detail": "随访=0" + }, + { + "id": "SUR-PATHO", + "name": "手术病理追踪", + "ok": true, + "detail": "病理=1" + }, + { + "id": "INS-LIS", + "name": "检验配置", + "ok": false, + "detail": "bug: Cannot invoke \"java.lang.Integer.intValue()\" becau" + }, + { + "id": "INS-SPEC", + "name": "检验标本", + "ok": false, + "detail": "DB错误: \n### Error querying database. Cause: org.postgres" + }, + { + "id": "INS-INST", + "name": "检验仪器", + "ok": false, + "detail": "DB错误: \n### Error querying database. Cause: org.postgres" + }, + { + "id": "INS-OBS", + "name": "检验观察", + "ok": false, + "detail": "DB错误: \n### Error querying database. Cause: org.postgres" + }, + { + "id": "INS-BARCODE", + "name": "标本条码", + "ok": true, + "detail": "条码=0" + }, + { + "id": "INS-RAD-URG", + "name": "影像急报", + "ok": true, + "detail": "急报=0" + }, + { + "id": "INS-RAD-STAT", + "name": "影像统计", + "ok": false, + "detail": "DB错误: \n### Error querying database. Cause: org.postgres" + }, + { + "id": "INS-COMP", + "name": "影像对比", + "ok": false, + "detail": "参数错误: Required request parameter 'patientId' for method " + }, + { + "id": "INS-3D", + "name": "3D重建任务", + "ok": true, + "detail": "任务=0" + }, + { + "id": "INS-3D-RPT", + "name": "3D重建报告", + "ok": true, + "detail": "报告=0" + }, + { + "id": "INS-RAD-RPT", + "name": "影像报告", + "ok": true, + "detail": "报告=0" + }, + { + "id": "INS-REF", + "name": "参考范围", + "ok": true, + "detail": "范围=0" + }, + { + "id": "INS-CDA", + "name": "CDA文档", + "ok": true, + "detail": "CDA=0" + }, + { + "id": "INS-CONSENT", + "name": "知情同意", + "ok": true, + "detail": "同意书=0" + }, + { + "id": "INF-SURV", + "name": "院感监测", + "ok": true, + "detail": "监测=2" + }, + { + "id": "INF-WARN", + "name": "院感预警", + "ok": false, + "detail": "路由缺失: No static resource infection-enhanced/warning/page" + }, + { + "id": "INF-MDR", + "name": "耐药监测", + "ok": true, + "detail": "耐药=2" + }, + { + "id": "INF-EXPO", + "name": "职业暴露", + "ok": false, + "detail": "路由缺失: No static resource infection-enhanced/exposure/pag" + }, + { + "id": "INF-HAND", + "name": "手卫生", + "ok": true, + "detail": "手卫生=5" + }, + { + "id": "INF-ENV", + "name": "环境监测", + "ok": true, + "detail": "环境=3" + }, + { + "id": "QA-IND", + "name": "质量指标", + "ok": true, + "detail": "指标=0" + }, + { + "id": "QA-ORDER", + "name": "医嘱统计", + "ok": true, + "detail": "统计=0" + }, + { + "id": "QA-REVIEW", + "name": "处方点评计划", + "ok": true, + "detail": "计划=4" + }, + { + "id": "QA-REVIEW-S", + "name": "处方点评统计", + "ok": true, + "detail": "" + }, + { + "id": "QA-RULES", + "name": "用药规则", + "ok": true, + "detail": "规则=0" + }, + { + "id": "QA-RULES-S", + "name": "用药统计", + "ok": true, + "detail": "" + }, + { + "id": "QA-CRIT", + "name": "危急值", + "ok": true, + "detail": "危急值=2" + }, + { + "id": "QA-CLOSED", + "name": "医嘱闭环", + "ok": true, + "detail": "闭环=2" + }, + { + "id": "QA-PATHWAY", + "name": "临床路径", + "ok": true, + "detail": "路径=1" + }, + { + "id": "QA-EMR", + "name": "病历质量", + "ok": true, + "detail": "" + }, + { + "id": "TCM-PRES", + "name": "中医方剂", + "ok": true, + "detail": "方剂=4" + }, + { + "id": "TCM-STAT", + "name": "中医统计", + "ok": true, + "detail": "" + }, + { + "id": "TCM-DX", + "name": "中医辨证", + "ok": true, + "detail": "辨证项=17" + }, + { + "id": "EM-TRIAGE", + "name": "急诊分诊", + "ok": true, + "detail": "分诊=1" + }, + { + "id": "EM-QUEUE", + "name": "叫号队列", + "ok": true, + "detail": "" + }, + { + "id": "CS-LIST", + "name": "会诊列表", + "ok": true, + "detail": "会诊=0" + }, + { + "id": "CS-DEPT", + "name": "会诊科室树", + "ok": true, + "detail": "科室=8" + }, + { + "id": "CS-TIMEOUT", + "name": "会诊超时", + "ok": true, + "detail": "超时=1" + }, + { + "id": "MR-STAT", + "name": "病案统计", + "ok": false, + "detail": "参数错误: Required request parameter 'startDate' for method " + }, + { + "id": "MR-DRG", + "name": "DRG分组", + "ok": false, + "detail": "DB错误: \n### Error querying database. Cause: org.postgres" + }, + { + "id": "MR-ARCH", + "name": "病案归档", + "ok": true, + "detail": "归档=0" + }, + { + "id": "MR-QUALITY", + "name": "病历质量", + "ok": true, + "detail": "质量=1" + }, + { + "id": "AN-PAGE", + "name": "经营分析", + "ok": true, + "detail": "分析=0" + }, + { + "id": "AN-SUM", + "name": "经营汇总", + "ok": true, + "detail": "" + }, + { + "id": "AN-STOCK", + "name": "库存预警", + "ok": true, + "detail": "预警=0" + }, + { + "id": "AN-DRG", + "name": "DRG绩效", + "ok": false, + "detail": "参数错误: Required request parameter 'statMonth' for method " + }, + { + "id": "AN-EXPIRY", + "name": "药品效期", + "ok": true, + "detail": "效期=1" + }, + { + "id": "XM-PATHO", + "name": "手术→病理联动", + "ok": true, + "detail": "病理=1" + }, + { + "id": "XM-REVIEW", + "name": "处方点评联动", + "ok": true, + "detail": "点评=0" + }, + { + "id": "XM-LAB", + "name": "实验室预警", + "ok": true, + "detail": "预警=0" + }, + { + "id": "XM-EXPIRY", + "name": "药品效期联动", + "ok": true, + "detail": "效期=1" + }, + { + "id": "BD-ORG", + "name": "组织管理", + "ok": true, + "detail": "组织=50" + }, + { + "id": "BD-LOC", + "name": "科室管理", + "ok": true, + "detail": "科室=53" + }, + { + "id": "BD-PRACT", + "name": "人员管理", + "ok": true, + "detail": "人员=43" + }, + { + "id": "BD-ICD", + "name": "ICD10", + "ok": false, + "detail": "DB错误: \n### Error querying database. Cause: org.postgres" + }, + { + "id": "BD-DICT", + "name": "数据字典", + "ok": false, + "detail": "路由缺失: No static resource dict-dictionary/definition/page" + }, + { + "id": "BD-CHECK", + "name": "检查方法", + "ok": true, + "detail": "方法=0" + }, + { + "id": "BD-PART", + "name": "检查部位", + "ok": true, + "detail": "部位=0" + }, + { + "id": "SYS-USER", + "name": "用户列表", + "ok": true, + "detail": "用户=98" + }, + { + "id": "SYS-ROLE", + "name": "角色列表", + "ok": true, + "detail": "角色=15" + }, + { + "id": "SYS-DEPT", + "name": "部门列表", + "ok": true, + "detail": "部门=12" + }, + { + "id": "SYS-DICT", + "name": "字典类型", + "ok": true, + "detail": "字典=326" + }, + { + "id": "SYS-NOTICE", + "name": "通知公告", + "ok": true, + "detail": "公告=4" + }, + { + "id": "SYS-CONFIG", + "name": "系统配置", + "ok": true, + "detail": "配置=19" + }, + { + "id": "MR-01-REG", + "name": "收费员→挂号初始化", + "ok": true, + "detail": "" + }, + { + "id": "MR-02-DOC", + "name": "医生→接诊初始化", + "ok": true, + "detail": "" + }, + { + "id": "MR-03-ADV", + "name": "医生→开医嘱", + "ok": true, + "detail": "医嘱数=5146" + }, + { + "id": "MR-04-RX", + "name": "医生→开处方", + "ok": true, + "detail": "" + }, + { + "id": "MR-05-PHARM", + "name": "药师→待发药", + "ok": true, + "detail": "待发药=534" + }, + { + "id": "MR-06-CHARGE", + "name": "收费员→收费", + "ok": true, + "detail": "" + }, + { + "id": "MR-07-NURSE", + "name": "护士→接收患者", + "ok": true, + "detail": "在院=31" + }, + { + "id": "MR-08-ADV", + "name": "医生→住院医嘱", + "ok": true, + "detail": "" + }, + { + "id": "MR-09-EXEC", + "name": "护士→执行医嘱", + "ok": true, + "detail": "执行=0" + }, + { + "id": "MR-10-NURSE-REC", + "name": "护士→护理记录", + "ok": true, + "detail": "记录=44" + }, + { + "id": "MR-11-SURG", + "name": "手术室→手术排程", + "ok": true, + "detail": "排程=53" + }, + { + "id": "MR-12-EM-TRIAGE", + "name": "急诊医生→分诊", + "ok": true, + "detail": "分诊=1" + }, + { + "id": "MR-13-EM-QUEUE", + "name": "急诊护士→叫号", + "ok": true, + "detail": "" + }, + { + "id": "MR-14-CS-DOC", + "name": "医生→会诊申请", + "ok": true, + "detail": "会诊=0" + }, + { + "id": "MR-15-CS-CON", + "name": "专家→会诊科室", + "ok": true, + "detail": "" + } + ] +} \ No newline at end of file diff --git a/healthlink-his-ui/src/api/antibiotic.js b/healthlink-his-ui/src/api/antibiotic.js index 2888cf66a..13f609652 100644 --- a/healthlink-his-ui/src/api/antibiotic.js +++ b/healthlink-his-ui/src/api/antibiotic.js @@ -20,3 +20,18 @@ export function approve(id, approverId, approverName, result) { export function getStatistics(startDate, endDate) { return request({ url: '/healthlink-his/api/v1/antibiotic/statistics', method: 'get', params: { startDate, endDate } }) } + +// 新增抗菌药物规则 +export function addRule(data) { + return request({ url: '/api/v1/antibiotic/rules', method: 'post', data }) +} + +// 更新抗菌药物规则 +export function updateRule(data) { + return request({ url: '/api/v1/antibiotic/rules', method: 'put', data }) +} + +// 删除抗菌药物规则 +export function deleteRule(drugCode) { + return request({ url: `/api/v1/antibiotic/rules/${drugCode}`, method: 'delete' }) +} diff --git a/healthlink-his-ui/src/api/casignature.js b/healthlink-his-ui/src/api/casignature.js new file mode 100644 index 000000000..591359f03 --- /dev/null +++ b/healthlink-his-ui/src/api/casignature.js @@ -0,0 +1,3 @@ +import request from "@/utils/request" +export function getSignatureStatistics(params) { return request({ url: "/api/v1/ca-signature/statistics", method: "get", params }) } +export function verifySignature(data) { return request({ url: "/api/v1/ca-signature/verify", method: "post", data }) } diff --git a/healthlink-his-ui/src/api/emr.js b/healthlink-his-ui/src/api/emr.js new file mode 100644 index 000000000..9c4c0c511 --- /dev/null +++ b/healthlink-his-ui/src/api/emr.js @@ -0,0 +1,7 @@ +import request from "@/utils/request" +export function getTimelinessByEncounter(encounterId) { return request({ url: "/emr-revision/timeliness/" + encounterId, method: "get" }) } +export function getTimelinessStatistics(params) { return request({ url: "/emr-revision/statistics", method: "get", params }) } +export function getPendingEmrCount(params) { return request({ url: "/emr-archive/pending-count", method: "get", params }) } + +// 查询超期病历列表 +export function getOverdueList(params) { return request({ url: "/emr-archive/overdue/list", method: "get", params }) } diff --git a/healthlink-his-ui/src/api/mrhomepage.js b/healthlink-his-ui/src/api/mrhomepage.js new file mode 100644 index 000000000..67526624e --- /dev/null +++ b/healthlink-his-ui/src/api/mrhomepage.js @@ -0,0 +1,3 @@ +import request from "@/utils/request" +export function executeQualityCheck(id) { return request({ url: "/api/v1/mr-homepage/quality-check/" + id, method: "post" }) } +export function submitHomepage(id) { return request({ url: "/api/v1/mr-homepage/submit/" + id, method: "put" }) } diff --git a/healthlink-his-ui/src/api/nursing.js b/healthlink-his-ui/src/api/nursing.js new file mode 100644 index 000000000..ab604f7dc --- /dev/null +++ b/healthlink-his-ui/src/api/nursing.js @@ -0,0 +1,2 @@ +import request from "@/utils/request" +export function getAssessmentsByEncounter(encounterId) { return request({ url: "/api/v1/nursing/assessment/encounter/" + encounterId, method: "get" }) } diff --git a/healthlink-his-ui/src/api/quality.js b/healthlink-his-ui/src/api/quality.js new file mode 100644 index 000000000..7525e93cc --- /dev/null +++ b/healthlink-his-ui/src/api/quality.js @@ -0,0 +1,8 @@ +import request from "@/utils/request" +export function runtimeCheck(encounterId) { return request({ url: "/api/v1/emr-quality/runtime-check/" + encounterId, method: "post" }) } +export function terminalCheck(encounterId) { return request({ url: "/api/v1/emr-quality/terminal-check/" + encounterId, method: "post" }) } +export function getScores(encounterId) { return request({ url: "/api/v1/emr-quality/score/" + encounterId, method: "get" }) } +export function getDefects(encounterId) { return request({ url: "/api/v1/emr-quality/defect/" + encounterId, method: "get" }) } +export function getDefectStatistics() { return request({ url: "/api/v1/emr-quality/defect-statistics", method: "get" }) } +export function getCompletionRate() { return request({ url: "/api/v1/emr-quality/completion-rate", method: "get" }) } +export function getQualityStatistics(params) { return request({ url: "/api/v1/emr-quality/defect-statistics", method: "get", params }) } diff --git a/healthlink-his-ui/src/api/techStation.js b/healthlink-his-ui/src/api/techStation.js new file mode 100644 index 000000000..2fd4ce7f0 --- /dev/null +++ b/healthlink-his-ui/src/api/techStation.js @@ -0,0 +1,25 @@ +import request from "@/utils/request" + +// 查询可执行医技订单列表 +export function listExecuteOrders(params) { return request({ url: "/tech-station/execute/list", method: "get", params }) } + +// 执行检查单 +export function executeExamOrder(data) { return request({ url: "/tech-station/execute/exam", method: "post", data }) } + +// 执行检验单 +export function executeLabOrder(data) { return request({ url: "/tech-station/execute/lab", method: "post", data }) } + +// 查询退费审批列表 +export function listRefundApproveOrders(params) { return request({ url: "/tech-station/refund/approve/list", method: "get", params }) } + +// 审批通过检查退费 +export function approveExamRefund(data) { return request({ url: "/tech-station/refund/approve/exam", method: "post", data }) } + +// 审批驳回检查退费 +export function rejectExamRefund(data) { return request({ url: "/tech-station/refund/reject/exam", method: "post", data }) } + +// 审批通过检验退费 +export function approveLabRefund(data) { return request({ url: "/tech-station/refund/approve/lab", method: "post", data }) } + +// 审批驳回检验退费 +export function rejectLabRefund(data) { return request({ url: "/tech-station/refund/reject/lab", method: "post", data }) } diff --git a/healthlink-his-ui/src/api/workflow/task.js b/healthlink-his-ui/src/api/workflow/task.js index 46554888f..e74039853 100755 --- a/healthlink-his-ui/src/api/workflow/task.js +++ b/healthlink-his-ui/src/api/workflow/task.js @@ -1,153 +1,36 @@ import request from '@/utils/request' -// 查询待办任务列表 -export function listTodo(query) { - return request({ - url: '/flowable/task/todoList', - method: 'get', - params: query - }) +// 获取待办任务列表 +export function listTodo(params) { + return request({ url: '/api/v1/workflow/todo', method: 'get', params }) } -// 查询已办任务列表 -export function listFinished(query) { - return request({ - url: '/flowable/task/finishedList', - method: 'get', - params: query - }) +// 获取已办任务列表 +export function listDone(params) { + return request({ url: '/api/v1/workflow/done', method: 'get', params }) } -// 查询我发起的流程 -export function myListProcess(query) { - return request({ - url: '/flowable/task/myProcess', - method: 'get', - params: query - }) +// 获取任务详情 +export function getTaskDetail(id) { + return request({ url: `/api/v1/workflow/task/${id}`, method: 'get' }) } // 完成任务 export function completeTask(data) { - return request({ - url: '/flowable/task/complete', - method: 'post', - data: data - }) -} - -// 驳回任务 -export function rejectTask(data) { - return request({ - url: '/flowable/task/reject', - method: 'post', - data: data - }) + return request({ url: '/api/v1/workflow/task/complete', method: 'post', data }) } // 退回任务 -export function returnTask(data) { - return request({ - url: '/flowable/task/return', - method: 'post', - data: data - }) -} - -// 认领/签收任务 -export function claimTask(data) { - return request({ - url: '/flowable/task/claim', - method: 'post', - data: data - }) -} - -// 取消认领/签收任务 -export function unClaimTask(data) { - return request({ - url: '/flowable/task/unClaim', - method: 'post', - data: data - }) -} - -// 委派任务 -export function delegateTask(data) { - return request({ - url: '/flowable/task/delegateTask', - method: 'post', - data: data - }) -} - -// 任务归还 -export function resolveTask(data) { - return request({ - url: '/flowable/task/resolveTask', - method: 'post', - data: data - }) +export function rejectTask(data) { + return request({ url: '/api/v1/workflow/task/reject', method: 'post', data }) } // 转办任务 -export function assignTask(data) { - return request({ - url: '/flowable/task/assignTask', - method: 'post', - data: data - }) +export function transferTask(data) { + return request({ url: '/api/v1/workflow/task/transfer', method: 'post', data }) } -// 取消申请 -export function stopProcess(data) { - return request({ - url: '/flowable/task/stopProcess', - method: 'post', - data: data - }) +// 获取任务统计 +export function getTaskStats() { + return request({ url: '/api/v1/workflow/task/stats', method: 'get' }) } - -// 撤回流程 -export function revokeProcess(data) { - return request({ - url: '/flowable/task/revokeProcess', - method: 'post', - data: data - }) -} - -// 删除任务 -export function deleteTask(data) { - return request({ - url: '/flowable/task/delete', - method: 'delete', - data: data - }) -} - -// 获取流程变量 -export function getProcessVariables(taskId) { - return request({ - url: `/flowable/task/processVariables/${taskId}`, - method: 'get' - }) -} - -// 获取下一节点 -export function getNextFlowNode(data) { - return request({ - url: '/flowable/task/nextFlowNode', - method: 'post', - data: data - }) -} - -// 获取任务表单 -export function getTaskForm(taskId) { - return request({ - url: '/flowable/task/getTaskForm', - method: 'get', - params: { taskId } - }) -} \ No newline at end of file diff --git a/healthlink-his-ui/src/views/infection/hygiene/api.js b/healthlink-his-ui/src/views/infection/hygiene/api.js index fe0814822..aa48a63ac 100644 --- a/healthlink-his-ui/src/views/infection/hygiene/api.js +++ b/healthlink-his-ui/src/views/infection/hygiene/api.js @@ -2,3 +2,5 @@ import request from '@/utils/request' export function getPage(p){return request({url:'/infection/hygiene/page',method:'get',params:p})} export function add(d){return request({url:'/infection/hygiene/add',method:'post',data:d})} export function del(id){return request({url:'/infection/hygiene/delete/'+id,method:'delete'})} +export function getStats(p){return request({url:'/infection/hygiene/stats',method:'get',params:p})} +export function update(d){return request({url:'/infection/hygiene/update',method:'put',data:d})} diff --git a/healthlink-his-ui/src/views/inpatientNurse/nursingstatistics/api.js b/healthlink-his-ui/src/views/inpatientNurse/nursingstatistics/api.js new file mode 100644 index 000000000..5e2c26b16 --- /dev/null +++ b/healthlink-his-ui/src/views/inpatientNurse/nursingstatistics/api.js @@ -0,0 +1,6 @@ +import request from '@/utils/request' +export function getPage(p){return request({url:'/nursing/statistics/page',method:'get',params:p})} +export function getData(p){return request({url:'/nursing/statistics/data',method:'get',params:p})} +export function getSummary(p){return request({url:'/nursing/statistics/summary',method:'get',params:p})} +export function getSummaryList(p){return request({url:'/nursing/statistics/summary/list',method:'get',params:p})} +export function deleteRecord(id){return request({url:'/nursing/statistics/record/delete/'+id,method:'delete'})}