#!/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)