From db66f158cff5fef79df809209a787b9b6c7c81cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=8E=E4=BD=97?= Date: Sun, 7 Jun 2026 22:03:41 +0800 Subject: [PATCH] =?UTF-8?q?feat(test):=20=E5=A4=9A=E8=A7=92=E8=89=B2?= =?UTF-8?q?=E5=8D=8F=E4=BD=9C=E5=85=A8=E6=B5=81=E7=A8=8B=E6=B5=8B=E8=AF=95?= =?UTF-8?q?+=E4=BF=AE=E5=A4=8D=E5=AF=86=E7=A0=81=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 05_test_multi_role.py: 12个场景88个测试用例 - 10个角色账户: admin/doctor1/jzys/jzhs/nkhs1/ssshs1/yjk1/医技员/sfy/hzzj1 - 场景覆盖: 门诊/住院/手术/检验/会诊/急诊/医保/药品/院感/权限/中医/质控 - 权限隔离测试: 验证角色只能访问其权限范围 - 测试结果: 28/88通过(31.8%), 主要问题是API路径不匹配 --- MD/test/05_test_multi_role.py | 686 ++++++++++++++++++ .../multi_role_test_20260607_220210.md | 120 +++ .../multi_role_test_20260607_220322.md | 122 ++++ 3 files changed, 928 insertions(+) create mode 100755 MD/test/05_test_multi_role.py create mode 100644 MD/test/reports/multi_role_test_20260607_220210.md create mode 100644 MD/test/reports/multi_role_test_20260607_220322.md diff --git a/MD/test/05_test_multi_role.py b/MD/test/05_test_multi_role.py new file mode 100755 index 000000000..cfb105dec --- /dev/null +++ b/MD/test/05_test_multi_role.py @@ -0,0 +1,686 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +HealthLink-HIS 三甲医院多角色协作全流程测试 +版本: v2.0 +日期: 2026-06-07 + +测试理念: +- 模拟真实三甲医院工作流:多角色按序协作 +- 每个场景由不同角色依次操作,验证数据流转 +- 验证角色权限隔离(A角色不能操作B角色的功能) +- 验证业务链路完整性(挂号→就诊→检查→发药→结算) +""" + +import requests, json, sys, os, time +from datetime import datetime +from typing import Dict, Any, Optional, Tuple + +BASE_URL = "http://localhost:18082/healthlink-his" + +# ============================ +# 角色账户配置(基于系统现有用户) +# ============================ +ACCOUNTS = { + "admin": {"user": "admin", "pwd": "admin123", "role": "超级管理员", "role_id": 1}, + "doctor1": {"user": "doctor1", "pwd": "123456", "role": "医生", "role_id": 200}, + "doctor_jz": {"user": "jzys", "pwd": "123456", "role": "急诊医生", "role_id": 200}, + "nurse_jz": {"user": "jzhs", "pwd": "123456", "role": "急诊护士", "role_id": 201}, + "nurse_nk": {"user": "nkhs1", "pwd": "123456", "role": "内科护士", "role_id": 201}, + "nurse_ss": {"user": "ssshs1", "pwd": "123456", "role": "手术室护士", "role_id": 201}, + "pharmacist":{"user": "yjk1", "pwd": "123456", "role": "药剂科", "role_id": 203}, + "tech": {"user": "医技员", "pwd": "123456", "role": "医技", "role_id": 204}, + "finance": {"user": "sfy", "pwd": "123456", "role": "收费员", "role_id": 213}, + "consult": {"user": "hzzj1", "pwd": "123456", "role": "会诊专家", "role_id": 200}, +} + +class Stats: + def __init__(self): + self.total = self.passed = self.failed = 0 + self.results = [] + def record(self, scenario, step, role, name, ok, detail=""): + self.total += 1 + if ok: self.passed += 1 + else: self.failed += 1 + s = "✅ PASS" if ok else "❌ FAIL" + self.results.append({"scenario":scenario,"step":step,"role":role,"name":name,"status":s,"detail":detail}) + print(f" {s} [{scenario}] {step} ({role}): {name}") + if detail and not ok: print(f" → {detail}") + def summary(self): + print(f"\n{'='*70}") + print(f"测试汇总: 总数={self.total}, 通过={self.passed}, 失败={self.failed}") + if self.total > 0: print(f"通过率: {self.passed*100/self.total:.1f}%") + print(f"{'='*70}") + return self.failed == 0 + +stats = Stats() + +# ============================ +# Token管理 +# ============================ +tokens = {} + +def login_as(account_key: str) -> str: + """以指定角色登录""" + if account_key in tokens and tokens[account_key]: + return tokens[account_key] + acc = ACCOUNTS[account_key] + try: + r = requests.post(f"{BASE_URL}/login", json={ + "username": acc["user"], "password": acc["pwd"], "tenantId": "1" + }).json() + token = r.get("token", "") + if token: + tokens[account_key] = token + return token + except Exception as e: + print(f" ❌ {account_key} 登录异常: {e}") + return "" + +def H(account_key: str): + return {"Authorization": f"Bearer {tokens.get(account_key,'')}", "Content-Type": "application/json"} + +def GET(account_key, path, params=None): + return requests.get(f"{BASE_URL}{path}", headers=H(account_key), params=params).json() + +def POST(account_key, path, data=None): + return requests.post(f"{BASE_URL}{path}", headers=H(account_key), json=data).json() + +def PUT(account_key, path, data=None): + return requests.put(f"{BASE_URL}{path}", headers=H(account_key), json=data).json() + +def DELETE(account_key, path): + return requests.delete(f"{BASE_URL}{path}", headers=H(account_key)).json() + +# ============================ +# 断言工具 +# ============================ +def chk(scenario, step, role, name, resp, expect_code=200, fields=None, not_empty=None): + ok = True; detail = "" + code = resp.get("code") + if code != expect_code: + ok = False + msg = resp.get("msg", "")[:150] + detail = f"code={code}, msg={msg}" + if ok and fields: + for f in fields: + if f not in resp: + ok = False; detail = f"缺少字段: {f}"; break + if ok and not_empty: + for f in not_empty: + v = resp.get(f) + if v is None or (isinstance(v, (list, dict)) and len(v) == 0): + ok = False; detail = f"字段{f}为空"; break + stats.record(scenario, step, role, name, ok, detail) + return resp + +def chk_page(scenario, step, role, name, resp, min_rows=0): + ok = True; detail = "" + code = resp.get("code") + if code != 200: + ok = False; detail = f"code={code}, msg={resp.get('msg','')[:120]}" + else: + rows = resp.get("rows", resp.get("data", [])) + if not isinstance(rows, list): + ok = False; detail = f"rows类型异常: {type(rows)}" + elif len(rows) < min_rows: + ok = False; detail = f"rows={len(rows)}, 需要>={min_rows}" + stats.record(scenario, step, role, name, ok, detail) + return resp + +# ================================================================ +# 场景1: 门诊就诊全流程(收费员→医生→医技→药师→收费员) +# ================================================================ +def scenario_outpatient(): + print(f"\n{'='*60}") + print("场景1: 门诊就诊全流程") + print("角色链: 收费员(挂号) → 医生(接诊+开方) → 医技(检查) → 药师(发药) → 收费员(结算)") + print(f"{'='*60}") + + # Step 1: 收费员初始化挂号 + r = GET("finance", "/charge-manage/register/init") + chk("门诊", "1.1", "收费员", "挂号初始化", r, 200, ["priorityLevelOptionOptions"]) + + # Step 2: 收费员查询患者 + r = GET("finance", "/charge-manage/register/patient-metadata", {"searchKey": "测试"}) + chk("门诊", "1.2", "收费员", "查询患者信息", r, 200) + + # Step 3: 收费员查询医生列表 + r = GET("finance", "/charge-manage/register/all-doctors") + chk("门诊", "1.3", "收费员", "查询医生列表", r, 200) + + # Step 4: 医生登录后查看待诊患者 + r = GET("doctor1", "/doctor-station/main/init") + chk("门诊", "1.4", "医生", "医生站初始化", r, 200) + + # Step 5: 医生查看患者信息 + r = GET("doctor1", "/doctor-station/main/patient-info") + chk("门诊", "1.5", "医生", "查看患者信息", r, 200) + + # Step 6: 医生查看接诊统计 + r = GET("doctor1", "/doctor-station/main/reception-statistics") + chk("门诊", "1.6", "医生", "接诊统计", r, 200) + + # Step 7: 医生查看医嘱基础信息 + r = GET("doctor1", "/doctor-station/advice/advice-base-info") + chk("门诊", "1.7", "医生", "医嘱基础信息", r, 200) + + # Step 8: 医生诊断初始化 + r = GET("doctor1", "/doctor-station/diagnosis/init") + chk("门诊", "1.8", "医生", "诊断初始化", r, 200) + + # Step 9: 医技查看检查申请(医技角色视角) + r = GET("tech", "/inspection/laboratory/page", {"pageNum": 1, "pageSize": 10}) + chk_page("门诊", "1.9", "医技", "查看检验结果列表", r) + + # Step 10: 医技查看影像 + r = GET("tech", "/radiology-image/page", {"pageNum": 1, "pageSize": 10}) + chk_page("门诊", "1.10", "医技", "查看影像列表", r) + + # Step 11: 药师查看药房管理 + r = GET("pharmacist", "/pharmacy-stock-alert/page", {"pageNum": 1, "pageSize": 10}) + chk_page("门诊", "1.11", "药师", "药品库存预警", r) + + # Step 12: 药师查看西药发药 + r = GET("pharmacist", "/pharmacy-manage/western-medicine-dispense/page", {"pageNum": 1, "pageSize": 10}) + chk_page("门诊", "1.12", "药师", "西药发药列表", r) + + # Step 13: 收费员收费初始化 + r = GET("finance", "/charge-manage/charge/init") + chk("门诊", "1.13", "收费员", "收费初始化", r, 200) + + # Step 14: 收费员查看收费患者列表 + r = GET("finance", "/charge-manage/charge/encounter-patient-page") + chk("门诊", "1.14", "收费员", "收费患者列表", r, 200) + + # Step 15: 收费员退费初始化 + r = GET("finance", "/charge-manage/refund/init") + chk("门诊", "1.15", "收费员", "退费初始化", r, 200) + +# ================================================================ +# 场景2: 住院入院全流程(收费员→医生→护士→药师→医生) +# ================================================================ +def scenario_inpatient(): + print(f"\n{'='*60}") + print("场景2: 住院入院全流程") + print("角色链: 收费员(入院) → 医生(医嘱) → 护士(护理) → 药师(发药) → 医生(出院)") + print(f"{'='*60}") + + # Step 1: 收费员查看入院登记 + r = GET("finance", "/charge-manage/inpatient-charge/init") + chk("住院", "2.1", "收费员", "住院收费初始化", r, 200) + + # Step 2: 收费员查看住院患者列表 + r = GET("finance", "/charge-manage/inpatient-charge/encounter-patient-page") + chk("住院", "2.2", "收费员", "住院患者列表", r, 200) + + # Step 3: 医生查看患者主页 + r = GET("doctor1", "/patient-home-manage/init") + chk("住院", "2.3", "医生", "患者主页初始化", r, 200) + + # Step 4: 医生查看空床 + r = GET("doctor1", "/patient-home-manage/empty-bed") + chk("住院", "2.4", "医生", "空床查询", r, 200) + + # Step 5: 医生查看科室统计 + r = GET("doctor1", "/patient-home-manage/caty") + chk("住院", "2.5", "医生", "科室统计", r, 200) + + # Step 6: 护士查看护理评估 + r = GET("nurse_nk", "/nursing-assessment-enhanced/page", {"pageNum": 1, "pageSize": 10}) + chk_page("住院", "2.6", "护士", "护理评估列表", r) + + # Step 7: 护士查看护理评估统计 + r = GET("nurse_nk", "/nursing-assessment-enhanced/stats") + chk("住院", "2.7", "护士", "护理评估统计", r, 200) + + # Step 8: 护士进行Braden评估 + braden = { + "patientName": "测试患者甲", + "encounterId": "6006", + "itemScores": json.dumps({"sensation":2,"moisture":2,"activity":1,"mobility":2,"nutrition":3,"friction":2}), + "detail": "压疮高危患者" + } + r = POST("nurse_nk", "/nursing-assessment-enhanced/braden/assess", braden) + chk("住院", "2.8", "护士", "Braden压疮评估", r, 200) + + # Step 9: 护士进行Morse跌倒评估 + morse = { + "patientName": "测试患者乙", + "encounterId": "6007", + "itemScores": json.dumps({"history":15,"diagnosis":0,"ambulation":15,"iv":20,"gait":0,"mental":15}), + "detail": "跌倒高危患者" + } + r = POST("nurse_nk", "/nursing-assessment-enhanced/morse/assess", morse) + chk("住院", "2.9", "护士", "Morse跌倒评估", r, 200) + + # Step 10: 护士查看体征记录 + r = GET("nurse_nk", "/vital-signs/record-search") + chk("住院", "2.10", "护士", "体征记录查询", r, 200) + + # Step 11: 护士查看护理质量 + r = GET("nurse_nk", "/nursing-quality/page", {"pageNum": 1, "pageSize": 10}) + chk_page("住院", "2.11", "护士", "护理质量指标", r) + + # Step 12: 药师查看住院发药 + r = GET("pharmacist", "/pharmacy-manage/pending-medication/page", {"pageNum": 1, "pageSize": 10}) + chk_page("住院", "2.12", "药师", "待发药品列表", r) + + # Step 13: 药师查看药品详情 + r = GET("pharmacist", "/pharmacy-manage/medication-details/page", {"pageNum": 1, "pageSize": 10}) + chk_page("住院", "2.13", "药师", "药品详情列表", r) + + # Step 14: 医生查看病历质量 + r = GET("doctor1", "/quality-enhanced/runtime/page", {"pageNum": 1, "pageSize": 10}) + chk_page("住院", "2.14", "医生", "运行质控", r) + +# ================================================================ +# 场景3: 手术全流程(医生→麻醉→手术室护士→医生) +# ================================================================ +def scenario_surgery(): + print(f"\n{'='*60}") + print("场景3: 手术全流程") + print("角色链: 医生(申请) → 专家(讨论) → 手术室护士(核查) → 医生(记录)") + print(f"{'='*60}") + + # Step 1: 医生查看手术列表 + r = GET("doctor1", "/clinical-manage/surgery/page", {"pageNum": 1, "pageSize": 10}) + chk_page("手术", "3.1", "医生", "手术列表", r) + + # Step 2: 医生查看手术排程 + r = GET("doctor1", "/clinical-manage/surgery-schedule/page", {"pageNum": 1, "pageSize": 10}) + chk_page("手术", "3.2", "医生", "手术排程", r) + + # Step 3: 专家查看术前讨论 + r = GET("consult", "/preop-discussion/page", {"pageNum": 1, "pageSize": 10}) + chk_page("手术", "3.3", "会诊专家", "术前讨论", r) + + # Step 4: 手术室护士查看安全核查 + r = GET("nurse_ss", "/surgery-safety-check/page", {"pageNum": 1, "pageSize": 10}) + chk_page("手术", "3.4", "手术室护士", "手术安全核查", r) + + # Step 5: 医生查看麻醉记录 + r = GET("doctor1", "/api/v1/anesthesia/page", {"pageNum": 1, "pageSize": 10}) + chk_page("手术", "3.5", "医生", "麻醉记录", r) + + # Step 6: 医生查看麻醉增强 + r = GET("doctor1", "/anesthesia-enhanced/page", {"pageNum": 1, "pageSize": 10}) + chk_page("手术", "3.6", "医生", "麻醉增强", r) + + # Step 7: 医生查看知情同意 + r = GET("doctor1", "/informed-consent/page", {"pageNum": 1, "pageSize": 10}) + chk_page("手术", "3.7", "医生", "知情同意", r) + + # Step 8: 医生查看电子签名 + r = GET("doctor1", "/api/v1/ca-signature/statistics") + chk("手术", "3.8", "医生", "电子签名统计", r, 200) + +# ================================================================ +# 场景4: 检验全流程(医生→护士→医技→医生) +# ================================================================ +def scenario_inspection(): + print(f"\n{'='*60}") + print("场景4: 检验全流程") + print("角色链: 医生(开单) → 护士(采样) → 医技(检验) → 医生(查看)") + print(f"{'='*60}") + + # Step 1: 医生查看检查申请 + r = GET("doctor1", "/exam/apply/page", {"pageNum": 1, "pageSize": 10}) + chk_page("检验", "4.1", "医生", "检查申请列表", r) + + # Step 2: 护士查看标本采集 + r = GET("nurse_nk", "/inspection/collection/page", {"pageNum": 1, "pageSize": 10}) + chk_page("检验", "4.2", "护士", "标本采集列表", r) + + # Step 3: 医技查看检验结果 + r = GET("tech", "/inspection/laboratory/page", {"pageNum": 1, "pageSize": 10}) + chk_page("检验", "4.3", "医技", "检验结果列表", r) + + # Step 4: 医技查看参考范围 + r = GET("tech", "/lab-ref-range/page", {"pageNum": 1, "pageSize": 10}) + chk_page("检验", "4.4", "医技", "参考范围", r) + + # Step 5: 医技查看标本定义 + r = GET("tech", "/inspection/specimen/page", {"pageNum": 1, "pageSize": 10}) + chk_page("检验", "4.5", "医技", "标本定义", r) + + # Step 6: 医技查看仪器管理 + r = GET("tech", "/inspection/instrument/page", {"pageNum": 1, "pageSize": 10}) + chk_page("检验", "4.6", "医技", "仪器管理", r) + + # Step 7: 医生查看影像对比 + r = GET("doctor1", "/radiology-comparison/page", {"pageNum": 1, "pageSize": 10}) + chk_page("检验", "4.7", "医生", "影像对比", r) + + # Step 8: 医生查看3D重建 + r = GET("doctor1", "/reconstruction/page", {"pageNum": 1, "pageSize": 10}) + chk_page("检验", "4.8", "医生", "3D重建", r) + +# ================================================================ +# 场景5: 会诊全流程(申请医生→会诊专家→申请医生) +# ================================================================ +def scenario_consultation(): + print(f"\n{'='*60}") + print("场景5: 会诊全流程") + print("角色链: 医生(申请) → 专家(会诊) → 医生(执行)") + print(f"{'='*60}") + + # Step 1: 医生查看会诊列表 + r = GET("doctor1", "/consultation/page", {"pageNum": 1, "pageSize": 10}) + chk_page("会诊", "5.1", "医生", "会诊记录", r) + + # Step 2: 专家查看会诊反馈 + r = GET("consult", "/cross-module/consult-feedback/page", {"pageNum": 1, "pageSize": 10}) + chk_page("会诊", "5.2", "会诊专家", "会诊反馈", r) + + # Step 3: 医生查看会诊超时 + r = GET("doctor1", "/cross-module/consulttimeout/page", {"pageNum": 1, "pageSize": 10}) + chk_page("会诊", "5.3", "医生", "会诊超时", r) + + # Step 4: 医生查看临床路径 + r = GET("doctor1", "/clinical-pathway/page", {"pageNum": 1, "pageSize": 10}) + chk_page("会诊", "5.4", "医生", "临床路径", r) + + # Step 5: 医生查看危急值 + r = GET("doctor1", "/api/v1/critical-value/page", {"pageNum": 1, "pageSize": 10}) + chk_page("会诊", "5.5", "医生", "危急值列表", r) + +# ================================================================ +# 场景6: 急诊全流程(急诊医生→急诊护士→医生) +# ================================================================ +def scenario_emergency(): + print(f"\n{'='*60}") + print("场景6: 急诊全流程") + print("角色链: 急诊医生(接诊) → 急诊护士(护理) → 医生(会诊)") + print(f"{'='*60}") + + # Step 1: 急诊医生查看急诊记录 + r = GET("doctor_jz", "/emergency/page", {"pageNum": 1, "pageSize": 10}) + chk_page("急诊", "6.1", "急诊医生", "急诊记录", r) + + # Step 2: 急诊护士查看分诊排队 + r = GET("nurse_jz", "/triage/queue/page", {"pageNum": 1, "pageSize": 10}) + chk_page("急诊", "6.2", "急诊护士", "分诊排队", r) + + # Step 3: 急诊护士查看护理评估 + r = GET("nurse_jz", "/nursing-assessment-enhanced/page", {"pageNum": 1, "pageSize": 10}) + chk_page("急诊", "6.3", "急诊护士", "护理评估", r) + + # Step 4: 急诊护士查看体征记录 + r = GET("nurse_jz", "/vital-signs/record-search") + chk("急诊", "6.4", "急诊护士", "体征记录", r, 200) + + # Step 5: 急诊护士查看危急值 + r = GET("nurse_jz", "/api/v1/critical-value/page", {"pageNum": 1, "pageSize": 10}) + chk_page("急诊", "6.5", "急诊护士", "危急值", r) + +# ================================================================ +# 场景7: 医保结算全流程(收费员→医保→财务) +# ================================================================ +def scenario_insurance(): + print(f"\n{'='*60}") + print("场景7: 医保结算全流程") + print("角色链: 收费员(收费) → 医保(结算) → 财务(审核)") + print(f"{'='*60}") + + # Step 1: 收费员收费 + r = GET("finance", "/charge-manage/charge/init") + chk("医保", "7.1", "收费员", "收费初始化", r, 200) + + # Step 2: 收费员退费 + r = GET("finance", "/charge-manage/refund/init") + chk("医保", "7.2", "收费员", "退费初始化", r, 200) + + # Step 3: 财务查看报表 + r = GET("finance", "/report-manage/charge/page", {"pageNum": 1, "pageSize": 10}) + chk_page("医保", "7.3", "财务", "收费报表", r) + + # Step 4: 财务查看经营分析 + r = GET("finance", "/business-analytics/page", {"pageNum": 1, "pageSize": 10}) + chk_page("医保", "7.4", "财务", "经营分析", r) + + # Step 5: 财务查看库房管理 + r = GET("finance", "/inventory-manage/product/page", {"pageNum": 1, "pageSize": 10}) + chk_page("医保", "7.5", "财务", "库存商品", r) + +# ================================================================ +# 场景8: 药品全流程(药师→医生→护士) +# ================================================================ +def scenario_pharmacy(): + print(f"\n{'='*60}") + print("场景8: 药品全流程") + print("角色链: 药师(库存管理) → 医生(合理用药) → 护士(执行)") + print(f"{'='*60}") + + # Step 1: 药师查看药品库存 + r = GET("pharmacist", "/pharmacy-stock-alert/page", {"pageNum": 1, "pageSize": 10}) + chk_page("药品", "8.1", "药师", "库存预警", r) + + # Step 2: 药师查看药房管理 + r = GET("pharmacist", "/pharmacy-manage/western-medicine-dispense/page", {"pageNum": 1, "pageSize": 10}) + chk_page("药品", "8.2", "药师", "西药发药", r) + + # Step 3: 药师查看药品追溯 + r = GET("pharmacist", "/drugtrace/page", {"pageNum": 1, "pageSize": 10}) + chk_page("药品", "8.3", "药师", "药品追溯", r) + + # Step 4: 药师查看合理用药 + r = GET("pharmacist", "/api/v1/rational-drug/page", {"pageNum": 1, "pageSize": 10}) + chk_page("药品", "8.4", "药师", "合理用药", r) + + # Step 5: 医生查看合理用药 + r = GET("doctor1", "/api/v1/rational-drug/page", {"pageNum": 1, "pageSize": 10}) + chk_page("药品", "8.5", "医生", "合理用药(医生视角)", r) + + # Step 6: 护士查看药房库存 + r = GET("nurse_nk", "/pharmacy-stock-alert/page", {"pageNum": 1, "pageSize": 10}) + chk_page("药品", "8.6", "护士", "药房库存(护士视角)", r) + +# ================================================================ +# 场景9: 院感全流程(护士→医生→医技) +# ================================================================ +def scenario_infection(): + print(f"\n{'='*60}") + print("场景9: 院感全流程") + print("角色链: 护士(监测) → 医生(诊断) → 医技(检验)") + print(f"{'='*60}") + + # Step 1: 护士查看院感监测 + r = GET("nurse_nk", "/infection-enhanced/surveillance/page", {"pageNum": 1, "pageSize": 10}) + chk_page("院感", "9.1", "护士", "院感监测", r) + + # Step 2: 医生查看院感预警 + r = GET("doctor1", "/infection-enhanced/warning/page", {"pageNum": 1, "pageSize": 10}) + chk_page("院感", "9.2", "医生", "院感预警", r) + + # Step 3: 医技查看耐药监测 + r = GET("tech", "/infection-enhanced/resistance/page", {"pageNum": 1, "pageSize": 10}) + chk_page("院感", "9.3", "医技", "耐药监测", r) + + # Step 4: 护士查看手卫生 + r = GET("nurse_nk", "/infection-enhanced/hand-hygiene/page", {"pageNum": 1, "pageSize": 10}) + chk_page("院感", "9.4", "护士", "手卫生", r) + + # Step 5: 医生查看职业暴露 + r = GET("doctor1", "/infection-enhanced/exposure/page", {"pageNum": 1, "pageSize": 10}) + chk_page("院感", "9.5", "医生", "职业暴露", r) + +# ================================================================ +# 场景10: 权限隔离测试 +# ================================================================ +def scenario_permission(): + print(f"\n{'='*60}") + print("场景10: 权限隔离测试") + print("验证: 不同角色只能访问其权限范围内的功能") + print(f"{'='*60}") + + # 10.1 医生不能访问收费功能 + r = GET("doctor1", "/charge-manage/register/init") + ok = r.get("code") != 200 + stats.record("权限", "10.1", "医生", "医生不能访问挂号初始化", ok, + "" if ok else f"意外成功: code={r.get('code')}") + + # 10.2 护士不能访问药房管理 + r = GET("nurse_nk", "/pharmacy-manage/western-medicine-dispense/page", {"pageNum": 1, "pageSize": 10}) + ok = r.get("code") != 200 + stats.record("权限", "10.2", "护士", "护士不能访问西药发药", ok, + "" if ok else f"意外成功: code={r.get('code')}") + + # 10.3 药师不能访问手术管理 + r = GET("pharmacist", "/clinical-manage/surgery/page", {"pageNum": 1, "pageSize": 10}) + ok = r.get("code") != 200 + stats.record("权限", "10.3", "药师", "药师不能访问手术管理", ok, + "" if ok else f"意外成功: code={r.get('code')}") + + # 10.4 医技不能访问护理评估 + r = GET("tech", "/nursing-assessment-enhanced/page", {"pageNum": 1, "pageSize": 10}) + ok = r.get("code") != 200 + stats.record("权限", "10.4", "医技", "医技不能访问护理评估", ok, + "" if ok else f"意外成功: code={r.get('code')}") + + # 10.5 收费员不能访问医生站 + r = GET("finance", "/doctor-station/main/init") + ok = r.get("code") != 200 + stats.record("权限", "10.5", "收费员", "收费员不能访问医生站", ok, + "" if ok else f"意外成功: code={r.get('code')}") + + # 10.6 医生可以访问手术管理 + r = GET("doctor1", "/clinical-manage/surgery/page", {"pageNum": 1, "pageSize": 10}) + stats.record("权限", "10.6", "医生", "医生可以访问手术管理", r.get("code") == 200, + "" if r.get("code") == 200 else f"被拒绝: code={r.get('code')}") + + # 10.7 护士可以访问护理评估 + r = GET("nurse_nk", "/nursing-assessment-enhanced/page", {"pageNum": 1, "pageSize": 10}) + stats.record("权限", "10.7", "护士", "护士可以访问护理评估", r.get("code") == 200, + "" if r.get("code") == 200 else f"被拒绝: code={r.get('code')}") + + # 10.8 药师可以访问药品追溯 + r = GET("pharmacist", "/drugtrace/page", {"pageNum": 1, "pageSize": 10}) + stats.record("权限", "10.8", "药师", "药师可以访问药品追溯", r.get("code") == 200, + "" if r.get("code") == 200 else f"被拒绝: code={r.get('code')}") + + # 10.9 医技可以访问影像管理 + r = GET("tech", "/radiology-image/page", {"pageNum": 1, "pageSize": 10}) + stats.record("权限", "10.9", "医技", "医技可以访问影像管理", r.get("code") == 200, + "" if r.get("code") == 200 else f"被拒绝: code={r.get('code')}") + + # 10.10 收费员可以访问收费管理 + r = GET("finance", "/charge-manage/charge/init") + stats.record("权限", "10.10", "收费员", "收费员可以访问收费管理", r.get("code") == 200, + "" if r.get("code") == 200 else f"被拒绝: code={r.get('code')}") + +# ================================================================ +# 场景11: 中医全流程(医生→护士) +# ================================================================ +def scenario_tcm(): + print(f"\n{'='*60}") + print("场景11: 中医全流程") + print("角色链: 医生(辨证论治) → 护士(护理)") + print(f"{'='*60}") + + # Step 1: 医生查看中医体质 + r = GET("doctor1", "/api/v1/tcm/constitution/page", {"pageNum": 1, "pageSize": 10}) + chk_page("中医", "11.1", "医生", "中医体质列表", r) + + # Step 2: 医生查看中药方剂 + r = GET("doctor1", "/api/v1/tcm/prescriptions", {"pageNum": 1, "pageSize": 10}) + rows = r.get("rows", r.get("data", [])) + ok = isinstance(rows, list) and len(rows) >= 2 + stats.record("中医", "11.2", "医生", "中药方剂>=2个", ok, "" if ok else f"实际={len(rows)}") + + # Step 3: 医生查看中医统计 + r = GET("doctor1", "/api/v1/tcm/statistics") + chk("中医", "11.3", "医生", "中医统计", r, 200) + +# ================================================================ +# 场景12: 质控全流程(医生→医技→护士) +# ================================================================ +def scenario_quality(): + print(f"\n{'='*60}") + print("场景12: 质控全流程") + print("角色链: 医生(病历质控) → 医技(检查质控) → 护士(护理质控)") + print(f"{'='*60}") + + # Step 1: 医生查看运行质控 + r = GET("doctor1", "/quality-enhanced/runtime/page", {"pageNum": 1, "pageSize": 10}) + chk_page("质控", "12.1", "医生", "运行质控", r) + + # Step 2: 医技查看终末质控 + r = GET("tech", "/api/v1/emr-quality/page", {"pageNum": 1, "pageSize": 10}) + chk_page("质控", "12.2", "医技", "终末质控", r) + + # Step 3: 护士查看护理质量 + r = GET("nurse_nk", "/nursing-quality/page", {"pageNum": 1, "pageSize": 10}) + chk_page("质控", "12.3", "护士", "护理质量指标", r) + + # Step 4: 医生查看质量统计 + r = GET("doctor1", "/quality-enhanced/statistics/page", {"pageNum": 1, "pageSize": 10}) + chk_page("质控", "12.4", "医生", "质量统计", r) + +# ================================================================ +# 主入口 +# ================================================================ +if __name__ == "__main__": + print(f"{'='*70}") + print("HealthLink-HIS 三甲医院多角色协作全流程测试") + print(f"测试时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") + print(f"测试环境: {BASE_URL}") + print(f"{'='*70}") + + # 登录所有角色 + print("\n>>> 登录所有角色...") + all_ok = True + for key, acc in ACCOUNTS.items(): + token = login_as(key) + if token: + print(f" ✅ {acc['role']}({acc['user']}) 登录成功") + else: + print(f" ❌ {acc['role']}({acc['user']}) 登录失败") + all_ok = False + + if not all_ok: + print("\n⚠️ 部分角色登录失败,继续测试...") + + # 执行所有场景 + scenarios = [ + scenario_outpatient, + scenario_inpatient, + scenario_surgery, + scenario_inspection, + scenario_consultation, + scenario_emergency, + scenario_insurance, + scenario_pharmacy, + scenario_infection, + scenario_permission, + scenario_tcm, + scenario_quality, + ] + + for fn in scenarios: + try: fn() + except Exception as e: + print(f" ❌ 场景异常: {fn.__name__}: {e}") + stats.record("异常", fn.__name__, "执行异常", False, str(e)) + + ok = stats.summary() + + # 生成报告 + os.makedirs("MD/test/reports", exist_ok=True) + rp = f"MD/test/reports/multi_role_test_{datetime.now().strftime('%Y%m%d_%H%M%S')}.md" + with open(rp, "w") as f: + f.write(f"# 三甲医院多角色协作测试报告\n\n") + f.write(f"**时间**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n") + f.write(f"**环境**: {BASE_URL}\n\n") + f.write(f"## 角色清单\n\n") + f.write("| 角色 | 账号 | 说明 |\n|------|------|------|\n") + for k, v in ACCOUNTS.items(): + f.write(f"| {v['role']} | {v['user']} | role_id={v['role_id']} |\n") + f.write(f"\n## 汇总\n\n- 总数: {stats.total}\n- 通过: {stats.passed}\n- 失败: {stats.failed}\n") + if stats.total > 0: f.write(f"- 通过率: {stats.passed*100/stats.total:.1f}%\n") + f.write(f"\n## 详细结果\n\n| 场景 | 步骤 | 角色 | 测试项 | 状态 | 说明 |\n|------|------|------|--------|------|------|\n") + for r in stats.results: + f.write(f"| {r['scenario']} | {r['step']} | {r['role']} | {r['name']} | {r['status']} | {r['detail']} |\n") + + print(f"\n📄 报告: {rp}") + sys.exit(0 if ok else 1) diff --git a/MD/test/reports/multi_role_test_20260607_220210.md b/MD/test/reports/multi_role_test_20260607_220210.md new file mode 100644 index 000000000..b789b65c9 --- /dev/null +++ b/MD/test/reports/multi_role_test_20260607_220210.md @@ -0,0 +1,120 @@ +# 三甲医院多角色协作测试报告 + +**时间**: 2026-06-07 22:02:10 + +**环境**: http://localhost:18082/healthlink-his + +## 角色清单 + +| 角色 | 账号 | 说明 | +|------|------|------| +| 超级管理员 | admin | role_id=1 | +| 医生 | doctor1 | role_id=200 | +| 急诊医生 | jzys | role_id=200 | +| 急诊护士 | jzhs | role_id=201 | +| 内科护士 | nkhs1 | role_id=201 | +| 手术室护士 | ssshs1 | role_id=201 | +| 药剂科 | yjk1 | role_id=203 | +| 医技 | 医技员 | role_id=204 | +| 收费员 | sfy | role_id=213 | +| 会诊专家 | hzzj1 | role_id=200 | + +## 汇总 + +- 总数: 88 +- 通过: 5 +- 失败: 83 +- 通过率: 5.7% + +## 详细结果 + +| 场景 | 步骤 | 角色 | 测试项 | 状态 | 说明 | +|------|------|------|--------|------|------| +| 门诊 | 1.1 | 收费员 | 挂号初始化 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/charge-manage/register/init,认证失败,无法访问系统资源 | +| 门诊 | 1.2 | 收费员 | 查询患者信息 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/charge-manage/register/patient-metadata,认证失败,无法访问系统资源 | +| 门诊 | 1.3 | 收费员 | 查询医生列表 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/charge-manage/register/all-doctors,认证失败,无法访问系统资源 | +| 门诊 | 1.4 | 医生 | 医生站初始化 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/doctor-station/main/init,认证失败,无法访问系统资源 | +| 门诊 | 1.5 | 医生 | 查看患者信息 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/doctor-station/main/patient-info,认证失败,无法访问系统资源 | +| 门诊 | 1.6 | 医生 | 接诊统计 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/doctor-station/main/reception-statistics,认证失败,无法访问系统资源 | +| 门诊 | 1.7 | 医生 | 医嘱基础信息 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/doctor-station/advice/advice-base-info,认证失败,无法访问系统资源 | +| 门诊 | 1.8 | 医生 | 诊断初始化 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/doctor-station/diagnosis/init,认证失败,无法访问系统资源 | +| 门诊 | 1.9 | 医技 | 查看检验结果列表 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/inspection/laboratory/page,认证失败,无法访问系统资源 | +| 门诊 | 1.10 | 医技 | 查看影像列表 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/radiology-image/page,认证失败,无法访问系统资源 | +| 门诊 | 1.11 | 药师 | 药品库存预警 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/pharmacy-stock-alert/page,认证失败,无法访问系统资源 | +| 门诊 | 1.12 | 药师 | 西药发药列表 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/pharmacy-manage/western-medicine-dispense/page,认证失败,无法访问系统资源 | +| 门诊 | 1.13 | 收费员 | 收费初始化 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/charge-manage/charge/init,认证失败,无法访问系统资源 | +| 门诊 | 1.14 | 收费员 | 收费患者列表 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/charge-manage/charge/encounter-patient-page,认证失败,无法访问系统资源 | +| 门诊 | 1.15 | 收费员 | 退费初始化 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/charge-manage/refund/init,认证失败,无法访问系统资源 | +| 住院 | 2.1 | 收费员 | 住院收费初始化 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/charge-manage/inpatient-charge/init,认证失败,无法访问系统资源 | +| 住院 | 2.2 | 收费员 | 住院患者列表 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/charge-manage/inpatient-charge/encounter-patient-page,认证失败,无法访问系统资源 | +| 住院 | 2.3 | 医生 | 患者主页初始化 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/patient-home-manage/init,认证失败,无法访问系统资源 | +| 住院 | 2.4 | 医生 | 空床查询 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/patient-home-manage/empty-bed,认证失败,无法访问系统资源 | +| 住院 | 2.5 | 医生 | 科室统计 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/patient-home-manage/caty,认证失败,无法访问系统资源 | +| 住院 | 2.6 | 护士 | 护理评估列表 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/nursing-assessment-enhanced/page,认证失败,无法访问系统资源 | +| 住院 | 2.7 | 护士 | 护理评估统计 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/nursing-assessment-enhanced/stats,认证失败,无法访问系统资源 | +| 住院 | 2.8 | 护士 | Braden压疮评估 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/nursing-assessment-enhanced/braden/assess,认证失败,无法访问系统资源 | +| 住院 | 2.9 | 护士 | Morse跌倒评估 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/nursing-assessment-enhanced/morse/assess,认证失败,无法访问系统资源 | +| 住院 | 2.10 | 护士 | 体征记录查询 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/vital-signs/record-search,认证失败,无法访问系统资源 | +| 住院 | 2.11 | 护士 | 护理质量指标 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/nursing-quality/page,认证失败,无法访问系统资源 | +| 住院 | 2.12 | 药师 | 待发药品列表 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/pharmacy-manage/pending-medication/page,认证失败,无法访问系统资源 | +| 住院 | 2.13 | 药师 | 药品详情列表 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/pharmacy-manage/medication-details/page,认证失败,无法访问系统资源 | +| 住院 | 2.14 | 医生 | 运行质控 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/quality-enhanced/runtime/page,认证失败,无法访问系统资源 | +| 手术 | 3.1 | 医生 | 手术列表 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/clinical-manage/surgery/page,认证失败,无法访问系统资源 | +| 手术 | 3.2 | 医生 | 手术排程 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/clinical-manage/surgery-schedule/page,认证失败,无法访问系统资源 | +| 手术 | 3.3 | 会诊专家 | 术前讨论 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/preop-discussion/page,认证失败,无法访问系统资源 | +| 手术 | 3.4 | 手术室护士 | 手术安全核查 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/surgery-safety-check/page,认证失败,无法访问系统资源 | +| 手术 | 3.5 | 医生 | 麻醉记录 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/api/v1/anesthesia/page,认证失败,无法访问系统资源 | +| 手术 | 3.6 | 医生 | 麻醉增强 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/anesthesia-enhanced/page,认证失败,无法访问系统资源 | +| 手术 | 3.7 | 医生 | 知情同意 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/informed-consent/page,认证失败,无法访问系统资源 | +| 手术 | 3.8 | 医生 | 电子签名统计 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/api/v1/ca-signature/statistics,认证失败,无法访问系统资源 | +| 检验 | 4.1 | 医生 | 检查申请列表 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/exam/apply/page,认证失败,无法访问系统资源 | +| 检验 | 4.2 | 护士 | 标本采集列表 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/inspection/collection/page,认证失败,无法访问系统资源 | +| 检验 | 4.3 | 医技 | 检验结果列表 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/inspection/laboratory/page,认证失败,无法访问系统资源 | +| 检验 | 4.4 | 医技 | 参考范围 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/lab-ref-range/page,认证失败,无法访问系统资源 | +| 检验 | 4.5 | 医技 | 标本定义 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/inspection/specimen/page,认证失败,无法访问系统资源 | +| 检验 | 4.6 | 医技 | 仪器管理 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/inspection/instrument/page,认证失败,无法访问系统资源 | +| 检验 | 4.7 | 医生 | 影像对比 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/radiology-comparison/page,认证失败,无法访问系统资源 | +| 检验 | 4.8 | 医生 | 3D重建 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/reconstruction/page,认证失败,无法访问系统资源 | +| 会诊 | 5.1 | 医生 | 会诊记录 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/consultation/page,认证失败,无法访问系统资源 | +| 会诊 | 5.2 | 会诊专家 | 会诊反馈 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/cross-module/consult-feedback/page,认证失败,无法访问系统资源 | +| 会诊 | 5.3 | 医生 | 会诊超时 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/cross-module/consulttimeout/page,认证失败,无法访问系统资源 | +| 会诊 | 5.4 | 医生 | 临床路径 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/clinical-pathway/page,认证失败,无法访问系统资源 | +| 会诊 | 5.5 | 医生 | 危急值列表 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/api/v1/critical-value/page,认证失败,无法访问系统资源 | +| 急诊 | 6.1 | 急诊医生 | 急诊记录 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/emergency/page,认证失败,无法访问系统资源 | +| 急诊 | 6.2 | 急诊护士 | 分诊排队 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/triage/queue/page,认证失败,无法访问系统资源 | +| 急诊 | 6.3 | 急诊护士 | 护理评估 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/nursing-assessment-enhanced/page,认证失败,无法访问系统资源 | +| 急诊 | 6.4 | 急诊护士 | 体征记录 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/vital-signs/record-search,认证失败,无法访问系统资源 | +| 急诊 | 6.5 | 急诊护士 | 危急值 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/api/v1/critical-value/page,认证失败,无法访问系统资源 | +| 医保 | 7.1 | 收费员 | 收费初始化 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/charge-manage/charge/init,认证失败,无法访问系统资源 | +| 医保 | 7.2 | 收费员 | 退费初始化 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/charge-manage/refund/init,认证失败,无法访问系统资源 | +| 医保 | 7.3 | 财务 | 收费报表 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/report-manage/charge/page,认证失败,无法访问系统资源 | +| 医保 | 7.4 | 财务 | 经营分析 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/business-analytics/page,认证失败,无法访问系统资源 | +| 医保 | 7.5 | 财务 | 库存商品 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/inventory-manage/product/page,认证失败,无法访问系统资源 | +| 药品 | 8.1 | 药师 | 库存预警 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/pharmacy-stock-alert/page,认证失败,无法访问系统资源 | +| 药品 | 8.2 | 药师 | 西药发药 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/pharmacy-manage/western-medicine-dispense/page,认证失败,无法访问系统资源 | +| 药品 | 8.3 | 药师 | 药品追溯 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/drugtrace/page,认证失败,无法访问系统资源 | +| 药品 | 8.4 | 药师 | 合理用药 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/api/v1/rational-drug/page,认证失败,无法访问系统资源 | +| 药品 | 8.5 | 医生 | 合理用药(医生视角) | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/api/v1/rational-drug/page,认证失败,无法访问系统资源 | +| 药品 | 8.6 | 护士 | 药房库存(护士视角) | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/pharmacy-stock-alert/page,认证失败,无法访问系统资源 | +| 院感 | 9.1 | 护士 | 院感监测 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/infection-enhanced/surveillance/page,认证失败,无法访问系统资源 | +| 院感 | 9.2 | 医生 | 院感预警 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/infection-enhanced/warning/page,认证失败,无法访问系统资源 | +| 院感 | 9.3 | 医技 | 耐药监测 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/infection-enhanced/resistance/page,认证失败,无法访问系统资源 | +| 院感 | 9.4 | 护士 | 手卫生 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/infection-enhanced/hand-hygiene/page,认证失败,无法访问系统资源 | +| 院感 | 9.5 | 医生 | 职业暴露 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/infection-enhanced/exposure/page,认证失败,无法访问系统资源 | +| 权限 | 10.1 | 医生 | 医生不能访问挂号初始化 | ✅ PASS | | +| 权限 | 10.2 | 护士 | 护士不能访问西药发药 | ✅ PASS | | +| 权限 | 10.3 | 药师 | 药师不能访问手术管理 | ✅ PASS | | +| 权限 | 10.4 | 医技 | 医技不能访问护理评估 | ✅ PASS | | +| 权限 | 10.5 | 收费员 | 收费员不能访问医生站 | ✅ PASS | | +| 权限 | 10.6 | 医生 | 医生可以访问手术管理 | ❌ FAIL | 被拒绝: code=401 | +| 权限 | 10.7 | 护士 | 护士可以访问护理评估 | ❌ FAIL | 被拒绝: code=401 | +| 权限 | 10.8 | 药师 | 药师可以访问药品追溯 | ❌ FAIL | 被拒绝: code=401 | +| 权限 | 10.9 | 医技 | 医技可以访问影像管理 | ❌ FAIL | 被拒绝: code=401 | +| 权限 | 10.10 | 收费员 | 收费员可以访问收费管理 | ❌ FAIL | 被拒绝: code=401 | +| 中医 | 11.1 | 医生 | 中医体质列表 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/api/v1/tcm/constitution/page,认证失败,无法访问系统资源 | +| 中医 | 11.2 | 医生 | 中药方剂>=2个 | ❌ FAIL | 实际=0 | +| 中医 | 11.3 | 医生 | 中医统计 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/api/v1/tcm/statistics,认证失败,无法访问系统资源 | +| 质控 | 12.1 | 医生 | 运行质控 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/quality-enhanced/runtime/page,认证失败,无法访问系统资源 | +| 质控 | 12.2 | 医技 | 终末质控 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/api/v1/emr-quality/page,认证失败,无法访问系统资源 | +| 质控 | 12.3 | 护士 | 护理质量指标 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/nursing-quality/page,认证失败,无法访问系统资源 | +| 质控 | 12.4 | 医生 | 质量统计 | ❌ FAIL | code=401, msg=请求访问:/healthlink-his/quality-enhanced/statistics/page,认证失败,无法访问系统资源 | diff --git a/MD/test/reports/multi_role_test_20260607_220322.md b/MD/test/reports/multi_role_test_20260607_220322.md new file mode 100644 index 000000000..8a539c7e1 --- /dev/null +++ b/MD/test/reports/multi_role_test_20260607_220322.md @@ -0,0 +1,122 @@ +# 三甲医院多角色协作测试报告 + +**时间**: 2026-06-07 22:03:22 + +**环境**: http://localhost:18082/healthlink-his + +## 角色清单 + +| 角色 | 账号 | 说明 | +|------|------|------| +| 超级管理员 | admin | role_id=1 | +| 医生 | doctor1 | role_id=200 | +| 急诊医生 | jzys | role_id=200 | +| 急诊护士 | jzhs | role_id=201 | +| 内科护士 | nkhs1 | role_id=201 | +| 手术室护士 | ssshs1 | role_id=201 | +| 药剂科 | yjk1 | role_id=203 | +| 医技 | 医技员 | role_id=204 | +| 收费员 | sfy | role_id=213 | +| 会诊专家 | hzzj1 | role_id=200 | + +## 汇总 + +- 总数: 88 +- 通过: 28 +- 失败: 60 +- 通过率: 31.8% + +## 详细结果 + +| 场景 | 步骤 | 角色 | 测试项 | 状态 | 说明 | +|------|------|------|--------|------|------| +| 门诊 | 1.1 | 收费员 | 挂号初始化 | ❌ FAIL | 缺少字段: priorityLevelOptionOptions | +| 门诊 | 1.2 | 收费员 | 查询患者信息 | ✅ PASS | | +| 门诊 | 1.3 | 收费员 | 查询医生列表 | ✅ PASS | | +| 门诊 | 1.4 | 医生 | 医生站初始化 | ✅ PASS | | +| 门诊 | 1.5 | 医生 | 查看患者信息 | ✅ PASS | | +| 门诊 | 1.6 | 医生 | 接诊统计 | ❌ FAIL | code=500, msg=Required request parameter 'startTime' for method parameter type String is not present | +| 门诊 | 1.7 | 医生 | 医嘱基础信息 | ✅ PASS | | +| 门诊 | 1.8 | 医生 | 诊断初始化 | ✅ PASS | | +| 门诊 | 1.9 | 医技 | 查看检验结果列表 | ❌ FAIL | code=500, msg=请求参数类型不匹配,参数[id]要求类型为:'java.lang.Long',但输入值为:'page' | +| 门诊 | 1.10 | 医技 | 查看影像列表 | ❌ FAIL | code=500, msg=No static resource radiology-image/page for request '/healthlink-his/radiology-image/page'. | +| 门诊 | 1.11 | 药师 | 药品库存预警 | ❌ FAIL | rows类型异常: | +| 门诊 | 1.12 | 药师 | 西药发药列表 | ❌ FAIL | code=500, msg=No static resource pharmacy-manage/western-medicine-dispense/page for request '/healthlink-his/pharmacy-manage/western-m | +| 门诊 | 1.13 | 收费员 | 收费初始化 | ✅ PASS | | +| 门诊 | 1.14 | 收费员 | 收费患者列表 | ✅ PASS | | +| 门诊 | 1.15 | 收费员 | 退费初始化 | ✅ PASS | | +| 住院 | 2.1 | 收费员 | 住院收费初始化 | ✅ PASS | | +| 住院 | 2.2 | 收费员 | 住院患者列表 | ✅ PASS | | +| 住院 | 2.3 | 医生 | 患者主页初始化 | ✅ PASS | | +| 住院 | 2.4 | 医生 | 空床查询 | ✅ PASS | | +| 住院 | 2.5 | 医生 | 科室统计 | ✅ PASS | | +| 住院 | 2.6 | 护士 | 护理评估列表 | ❌ FAIL | rows类型异常: | +| 住院 | 2.7 | 护士 | 护理评估统计 | ✅ PASS | | +| 住院 | 2.8 | 护士 | Braden压疮评估 | ✅ PASS | | +| 住院 | 2.9 | 护士 | Morse跌倒评估 | ✅ PASS | | +| 住院 | 2.10 | 护士 | 体征记录查询 | ✅ PASS | | +| 住院 | 2.11 | 护士 | 护理质量指标 | ❌ FAIL | rows类型异常: | +| 住院 | 2.12 | 药师 | 待发药品列表 | ❌ FAIL | code=500, msg=No static resource pharmacy-manage/pending-medication/page for request '/healthlink-his/pharmacy-manage/pending-medicati | +| 住院 | 2.13 | 药师 | 药品详情列表 | ❌ FAIL | code=500, msg=No static resource pharmacy-manage/medication-details/page for request '/healthlink-his/pharmacy-manage/medication-detai | +| 住院 | 2.14 | 医生 | 运行质控 | ❌ FAIL | code=500, msg=No static resource quality-enhanced/runtime/page for request '/healthlink-his/quality-enhanced/runtime/page'. | +| 手术 | 3.1 | 医生 | 手术列表 | ❌ FAIL | code=500, msg=No static resource clinical-manage/surgery/page for request '/healthlink-his/clinical-manage/surgery/page'. | +| 手术 | 3.2 | 医生 | 手术排程 | ❌ FAIL | rows类型异常: | +| 手术 | 3.3 | 会诊专家 | 术前讨论 | ❌ FAIL | code=500, msg= +### Error querying database. Cause: org.postgresql.util.PSQLException: ERROR: column "delete_flag" does not exist + Po | +| 手术 | 3.4 | 手术室护士 | 手术安全核查 | ❌ FAIL | rows类型异常: | +| 手术 | 3.5 | 医生 | 麻醉记录 | ❌ FAIL | code=500, msg=No static resource api/v1/anesthesia/page for request '/healthlink-his/api/v1/anesthesia/page'. | +| 手术 | 3.6 | 医生 | 麻醉增强 | ❌ FAIL | code=500, msg=No static resource anesthesia-enhanced/page for request '/healthlink-his/anesthesia-enhanced/page'. | +| 手术 | 3.7 | 医生 | 知情同意 | ❌ FAIL | rows类型异常: | +| 手术 | 3.8 | 医生 | 电子签名统计 | ✅ PASS | | +| 检验 | 4.1 | 医生 | 检查申请列表 | ❌ FAIL | code=500, msg=未找到申请单信息 | +| 检验 | 4.2 | 护士 | 标本采集列表 | ❌ FAIL | code=500, msg=No static resource inspection/collection/page for request '/healthlink-his/inspection/collection/page'. | +| 检验 | 4.3 | 医技 | 检验结果列表 | ❌ FAIL | code=500, msg=请求参数类型不匹配,参数[id]要求类型为:'java.lang.Long',但输入值为:'page' | +| 检验 | 4.4 | 医技 | 参考范围 | ❌ FAIL | rows类型异常: | +| 检验 | 4.5 | 医技 | 标本定义 | ❌ FAIL | code=500, msg=No static resource inspection/specimen/page for request '/healthlink-his/inspection/specimen/page'. | +| 检验 | 4.6 | 医技 | 仪器管理 | ❌ FAIL | code=500, msg=No static resource inspection/instrument/page for request '/healthlink-his/inspection/instrument/page'. | +| 检验 | 4.7 | 医生 | 影像对比 | ❌ FAIL | code=500, msg=No static resource radiology-comparison/page for request '/healthlink-his/radiology-comparison/page'. | +| 检验 | 4.8 | 医生 | 3D重建 | ❌ FAIL | code=500, msg=No static resource reconstruction/page for request '/healthlink-his/reconstruction/page'. | +| 会诊 | 5.1 | 医生 | 会诊记录 | ❌ FAIL | code=500, msg=No static resource consultation/page for request '/healthlink-his/consultation/page'. | +| 会诊 | 5.2 | 会诊专家 | 会诊反馈 | ❌ FAIL | code=500, msg=No static resource cross-module/consult-feedback/page for request '/healthlink-his/cross-module/consult-feedback/page'. | +| 会诊 | 5.3 | 医生 | 会诊超时 | ❌ FAIL | code=500, msg=No static resource cross-module/consulttimeout/page for request '/healthlink-his/cross-module/consulttimeout/page'. | +| 会诊 | 5.4 | 医生 | 临床路径 | ❌ FAIL | rows类型异常: | +| 会诊 | 5.5 | 医生 | 危急值列表 | ❌ FAIL | code=500, msg=No static resource api/v1/critical-value/page for request '/healthlink-his/api/v1/critical-value/page'. | +| 急诊 | 6.1 | 急诊医生 | 急诊记录 | ❌ FAIL | code=500, msg=No static resource emergency/page for request '/healthlink-his/emergency/page'. | +| 急诊 | 6.2 | 急诊护士 | 分诊排队 | ❌ FAIL | code=500, msg=No static resource index.html for request '/healthlink-his/index.html'. | +| 急诊 | 6.3 | 急诊护士 | 护理评估 | ❌ FAIL | rows类型异常: | +| 急诊 | 6.4 | 急诊护士 | 体征记录 | ✅ PASS | | +| 急诊 | 6.5 | 急诊护士 | 危急值 | ❌ FAIL | code=500, msg=No static resource api/v1/critical-value/page for request '/healthlink-his/api/v1/critical-value/page'. | +| 医保 | 7.1 | 收费员 | 收费初始化 | ✅ PASS | | +| 医保 | 7.2 | 收费员 | 退费初始化 | ✅ PASS | | +| 医保 | 7.3 | 财务 | 收费报表 | ❌ FAIL | code=500, msg=No static resource report-manage/charge/page for request '/healthlink-his/report-manage/charge/page'. | +| 医保 | 7.4 | 财务 | 经营分析 | ❌ FAIL | rows类型异常: | +| 医保 | 7.5 | 财务 | 库存商品 | ❌ FAIL | code=500, msg=No static resource inventory-manage/product/page for request '/healthlink-his/inventory-manage/product/page'. | +| 药品 | 8.1 | 药师 | 库存预警 | ❌ FAIL | rows类型异常: | +| 药品 | 8.2 | 药师 | 西药发药 | ❌ FAIL | code=500, msg=No static resource pharmacy-manage/western-medicine-dispense/page for request '/healthlink-his/pharmacy-manage/western-m | +| 药品 | 8.3 | 药师 | 药品追溯 | ❌ FAIL | code=500, msg=No static resource drugtrace/page for request '/healthlink-his/drugtrace/page'. | +| 药品 | 8.4 | 药师 | 合理用药 | ❌ FAIL | code=500, msg=No static resource api/v1/rational-drug/page for request '/healthlink-his/api/v1/rational-drug/page'. | +| 药品 | 8.5 | 医生 | 合理用药(医生视角) | ❌ FAIL | code=500, msg=No static resource api/v1/rational-drug/page for request '/healthlink-his/api/v1/rational-drug/page'. | +| 药品 | 8.6 | 护士 | 药房库存(护士视角) | ❌ FAIL | rows类型异常: | +| 院感 | 9.1 | 护士 | 院感监测 | ❌ FAIL | rows类型异常: | +| 院感 | 9.2 | 医生 | 院感预警 | ❌ FAIL | code=500, msg=No static resource infection-enhanced/warning/page for request '/healthlink-his/infection-enhanced/warning/page'. | +| 院感 | 9.3 | 医技 | 耐药监测 | ❌ FAIL | code=500, msg=No static resource infection-enhanced/resistance/page for request '/healthlink-his/infection-enhanced/resistance/page'. | +| 院感 | 9.4 | 护士 | 手卫生 | ❌ FAIL | rows类型异常: | +| 院感 | 9.5 | 医生 | 职业暴露 | ❌ FAIL | code=500, msg=No static resource infection-enhanced/exposure/page for request '/healthlink-his/infection-enhanced/exposure/page'. | +| 权限 | 10.1 | 医生 | 医生不能访问挂号初始化 | ❌ FAIL | 意外成功: code=200 | +| 权限 | 10.2 | 护士 | 护士不能访问西药发药 | ✅ PASS | | +| 权限 | 10.3 | 药师 | 药师不能访问手术管理 | ✅ PASS | | +| 权限 | 10.4 | 医技 | 医技不能访问护理评估 | ❌ FAIL | 意外成功: code=200 | +| 权限 | 10.5 | 收费员 | 收费员不能访问医生站 | ❌ FAIL | 意外成功: code=200 | +| 权限 | 10.6 | 医生 | 医生可以访问手术管理 | ❌ FAIL | 被拒绝: code=500 | +| 权限 | 10.7 | 护士 | 护士可以访问护理评估 | ✅ PASS | | +| 权限 | 10.8 | 药师 | 药师可以访问药品追溯 | ❌ FAIL | 被拒绝: code=500 | +| 权限 | 10.9 | 医技 | 医技可以访问影像管理 | ❌ FAIL | 被拒绝: code=500 | +| 权限 | 10.10 | 收费员 | 收费员可以访问收费管理 | ✅ PASS | | +| 中医 | 11.1 | 医生 | 中医体质列表 | ❌ FAIL | code=500, msg=No static resource api/v1/tcm/constitution/page for request '/healthlink-his/api/v1/tcm/constitution/page'. | +| 中医 | 11.2 | 医生 | 中药方剂>=2个 | ✅ PASS | | +| 中医 | 11.3 | 医生 | 中医统计 | ✅ PASS | | +| 质控 | 12.1 | 医生 | 运行质控 | ❌ FAIL | code=500, msg=No static resource quality-enhanced/runtime/page for request '/healthlink-his/quality-enhanced/runtime/page'. | +| 质控 | 12.2 | 医技 | 终末质控 | ❌ FAIL | code=500, msg=No static resource api/v1/emr-quality/page for request '/healthlink-his/api/v1/emr-quality/page'. | +| 质控 | 12.3 | 护士 | 护理质量指标 | ❌ FAIL | rows类型异常: | +| 质控 | 12.4 | 医生 | 质量统计 | ❌ FAIL | code=500, msg=No static resource quality-enhanced/statistics/page for request '/healthlink-his/quality-enhanced/statistics/page'. |