Files
his/MD/test/06_business_logic_complex_v2.py
华佗 bc92620f66 fix: 修复前端路由名重复导致的404问题 + E2E测试20/20通过
根因分析:
- 后端菜单配置存在30+个重复路由名(如Record/Enhanced/Charge等)
- Vue Router不允许重名路由,addRoute抛出异常
- permission.js的catch直接调用logOut(),导致所有页面重定向到登录页

修复内容:
1. permission.js: addRoute时try-catch重名错误,跳过而非登出
2. permission store: filterAsyncRouter中添加路由名自动去重逻辑
3. 新增src/api/anesthesia.js: 麻醉模块API文件缺失修复
4. 修正test-data.ts中所有错误路由路径,匹配实际菜单配置

验证: workflow-full.spec.ts 20/20通过, login.spec.ts 4/4通过
2026-06-08 00:18:34 +08:00

911 lines
45 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
HealthLink-HIS 三甲医院复杂业务逻辑全流程测试 V2
修复所有错误的API路径造测试数据验证完整业务流程
"""
import requests
import json
import time
import sys
import os
from datetime import datetime, timedelta
BASE_URL = "http://localhost:18082/healthlink-his"
RESULTS = []
PASSED = 0
FAILED = 0
USERS = {
"admin": {"username": "admin", "password": "admin123", "role": "超级管理员"},
"doctor": {"username": "doctor1", "password": "123456", "role": "医生"},
"jzys": {"username": "jzys", "password": "123456", "role": "急诊医生"},
"jzhs": {"username": "jzhs", "password": "123456", "role": "急诊护士"},
"nkhs": {"username": "nkhs1", "password": "123456", "role": "内科护士"},
"ssshs": {"username": "ssshs1", "password": "123456", "role": "手术室护士"},
"pharmacist": {"username": "yjk1", "password": "123456", "role": "药师"},
"tech": {"username": "医技员", "password": "123456", "role": "医技"},
"finance": {"username": "sfy", "password": "123456", "role": "收费员"},
"consultant": {"username": "hzzj1", "password": "123456", "role": "会诊专家"},
}
TOKEN_CACHE = {}
def login(username, password):
if username in TOKEN_CACHE and TOKEN_CACHE[username]:
return TOKEN_CACHE[username]
try:
resp = requests.post(f"{BASE_URL}/login", json={
"username": username, "password": password, "tenantId": "1"
}, timeout=10)
data = resp.json()
if data.get("code") == 200 and data.get("token"):
TOKEN_CACHE[username] = data["token"]
return data["token"]
except Exception as e:
print(f" ⚠️ 登录失败 {username}: {e}")
return None
def api(method, path, token=None, data=None, params=None, timeout=30):
headers = {"Content-Type": "application/json"}
if token:
headers["Authorization"] = f"Bearer {token}"
url = f"{BASE_URL}{path}"
try:
if method == "GET":
resp = requests.get(url, headers=headers, params=params, timeout=timeout)
elif method == "POST":
resp = requests.post(url, headers=headers, json=data, timeout=timeout)
elif method == "PUT":
resp = requests.put(url, headers=headers, json=data, timeout=timeout)
elif method == "DELETE":
resp = requests.delete(url, headers=headers, timeout=timeout)
else:
return None
result = resp.json() if resp.headers.get("content-type", "").startswith("application/json") else {"code": resp.status_code}
return {"success": result.get("code") == 200, "code": result.get("code", resp.status_code), "data": result.get("data"), "msg": result.get("msg", ""), "raw": result}
except requests.exceptions.Timeout:
return {"success": False, "code": 0, "msg": "请求超时", "data": None}
except Exception as e:
return {"success": False, "code": 0, "msg": str(e)[:200], "data": None}
def record(test_id, name, passed, details="", flow=""):
global PASSED, FAILED
if passed:
PASSED += 1
else:
FAILED += 1
RESULTS.append({"id": test_id, "name": name, "passed": passed, "details": details})
print(f" {'' if passed else ''} [{test_id}] {name}" + (f"{details}" if details else ""))
if flow:
print(f" 📊 {flow}")
def get_data_count(result):
"""从各种返回格式中提取数据数量"""
if not result or not result.get("data"):
return 0
data = result["data"]
if isinstance(data, dict):
return data.get("total", len(data.get("rows", data.get("list", []))))
if isinstance(data, list):
return len(data)
return 0
# ======================== 1. 登录认证 ========================
def test_auth():
print("\n" + "="*60)
print("📋 模块一: 登录认证与Token管理")
print("="*60)
all_tokens = {}
# 所有角色登录
for key, user in USERS.items():
t = login(user["username"], user["password"])
all_tokens[key] = t
record(f"TC-AUTH-{key}", f"{user['role']}({user['username']})登录", t is not None,
f"token={'' if t else ''}", f"{user['username']} → /login → token")
# 错误密码
result = api("POST", "/login", data={"username": "admin", "password": "wrong"})
record("TC-AUTH-ERR", "错误密码拒绝登录", result["code"] != 200,
f"code={result['code']}", "admin(错误密码) → /login → 拒绝")
# 获取用户信息
result = api("GET", "/getInfo", token=all_tokens.get("admin"))
record("TC-AUTH-INFO", "获取当前用户信息", result["success"],
f"roles={len(result['data'].get('roles', [])) if result['data'] else 0}",
"token → /getInfo → 用户信息+角色+权限")
# 菜单路由
result = api("GET", "/getRouters", token=all_tokens.get("admin"))
count = len(result["data"]) if result["data"] else 0
record("TC-AUTH-ROUTE", "获取菜单路由树", result["success"] and count > 0,
f"一级菜单: {count}", "/getRouters → 菜单树")
return all_tokens
# ======================== 2. 系统管理 ========================
def test_system(tokens):
print("\n" + "="*60)
print("📋 模块二: 系统管理")
print("="*60)
t = tokens.get("admin")
# 用户列表 - 返回格式是 {total, rows, code}
result = api("GET", "/system/user/list", token=t, params={"pageNum": 1, "pageSize": 10})
total = result["raw"].get("total", 0) if result["raw"] else 0
record("TC-SYS-USER", "用户列表分页查询", result["success"] and total > 0,
f"总用户: {total}", "/system/user/list → {total, rows}")
# 角色列表
result = api("GET", "/system/role/list", token=t, params={"pageNum": 1, "pageSize": 10})
total = result["raw"].get("total", 0) if result["raw"] else 0
record("TC-SYS-ROLE", "角色列表分页查询", result["success"] and total > 0,
f"总角色: {total}", "/system/role/list → {total, rows}")
# 部门树
result = api("GET", "/system/dept/list", token=t)
count = len(result["data"]) if result["data"] else 0
record("TC-SYS-DEPT", "部门树查询", result["success"],
f"部门数: {count}", "/system/dept/list → 部门树")
# 数据字典
result = api("GET", "/system/dict/type/list", token=t, params={"pageNum": 1, "pageSize": 10})
record("TC-SYS-DICT", "数据字典类型查询", result["success"],
f"字典类型: {get_data_count(result)}", "/system/dict/type/list → 字典类型")
# 通知公告
result = api("GET", "/system/notice/list", token=t, params={"pageNum": 1, "pageSize": 10})
record("TC-SYS-NOTICE", "通知公告列表", result["success"],
f"公告: {get_data_count(result)}", "/system/notice/list → 公告列表")
# ======================== 3. 门诊全流程 ========================
def test_outpatient(tokens):
print("\n" + "="*60)
print("📋 模块三: 门诊全流程 (挂号→就诊→开方→收费→取药)")
print("="*60)
fin = tokens.get("finance")
doc = tokens.get("doctor")
pha = tokens.get("pharmacist")
# --- 挂号初始化 ---
result = api("GET", "/charge-manage/register/init", token=fin)
record("TC-OP-REG-INIT", "挂号初始化(科室+医生)", result["success"],
"初始化数据", "/charge-manage/register/init → 科室+医生+号别")
# --- 医生工作站初始化 ---
result = api("GET", "/doctor-station/main/init", token=doc)
record("TC-OP-DOC-INIT", "医生工作站初始化", result["success"],
"初始化数据", "/doctor-station/main/init → 工作站数据")
# --- 医嘱基本信息 ---
result = api("GET", "/doctor-station/advice/advice-base-info", token=doc)
record("TC-OP-ADVICE", "医嘱基本信息", result["success"],
"医嘱数据", "/doctor-station/advice/advice-base-info → 医嘱基础")
# --- 诊断初始化 ---
result = api("GET", "/doctor-station/diagnosis/init", token=doc)
record("TC-OP-DX-INIT", "诊断初始化", result["success"],
"诊断数据", "/doctor-station/diagnosis/init → 诊断数据")
# --- EMR病历页 ---
result = api("GET", "/doctor-station/emr/emr-page", token=doc, params={"pageNum": 1, "pageSize": 10})
record("TC-OP-EMR", "电子病历列表", result["success"],
f"病历: {get_data_count(result)}", "/doctor-station/emr/emr-page → 病历列表")
# --- 门诊治疗 ---
result = api("GET", "/outpatient-manage/treatment/init", token=doc)
record("TC-OP-TREAT", "门诊治疗初始化", result["success"],
"治疗数据", "/outpatient-manage/treatment/init → 治疗初始化")
# --- 门诊输液 ---
result = api("GET", "/outpatient-manage/infusion/init", token=doc)
record("TC-OP-INFUSION", "门诊输液初始化", result["success"],
"输液数据", "/outpatient-manage/infusion/init → 输液初始化")
# --- 门诊增强 ---
result = api("GET", "/outpatient-enhanced/template/page", token=doc, params={"pageNum": 1, "pageSize": 10})
record("TC-OP-ENH-TPL", "门诊模板列表", result["success"],
f"模板: {get_data_count(result)}", "/outpatient-enhanced/template/page → 模板列表")
# --- 待发药 ---
result = api("GET", "/pharmacy-manage/pending-medication/pending-medication-page", token=pha, params={"pageNum": 1, "pageSize": 10})
record("TC-OP-PENDING", "待发药列表", result["success"],
f"待发药: {get_data_count(result)}", "/pharmacy-manage/pending-medication/pending-medication-page → 待发药")
# --- 西药发药 ---
result = api("GET", "/pharmacy-manage/western-medicine-dispense/init", token=pha)
record("TC-OP-WEST-DISP", "西药发药初始化", result["success"],
"发药数据", "/pharmacy-manage/western-medicine-dispense/init → 西药发药")
# --- 药品追溯 ---
result = api("GET", "/drugtrace/code/page", token=pha, params={"pageNum": 1, "pageSize": 10})
record("TC-OP-TRACE", "药品追溯码", result["success"],
f"追溯码: {get_data_count(result)}", "/drugtrace/code/page → 追溯码列表")
# --- 门诊收费初始化 ---
result = api("GET", "/charge-manage/charge/init", token=fin)
record("TC-OP-CHARGE-INIT", "门诊收费初始化", result["success"],
"收费数据", "/charge-manage/charge/init → 收费初始化")
# --- 门诊退费初始化 ---
result = api("GET", "/charge-manage/refund/init", token=fin)
record("TC-OP-REFUND-INIT", "门诊退费初始化", result["success"],
"退费数据", "/charge-manage/refund/init → 退费初始化")
# --- 患者建卡 ---
result = api("GET", "/charge/patientCardRenewal/init", token=fin)
record("TC-OP-CARD", "患者建卡初始化", result["success"],
"建卡数据", "/charge/patientCardRenewal/init → 建卡初始化")
# --- 今日门诊统计 ---
result = api("GET", "/today-outpatient/stats", token=fin)
record("TC-OP-TODAY", "今日门诊统计", result["success"],
"统计数据", "/today-outpatient/stats → 今日统计")
# --- 今日门诊患者 ---
result = api("GET", "/today-outpatient/patients", token=fin, params={"pageNum": 1, "pageSize": 10})
record("TC-OP-TODAY-PT", "今日门诊患者列表", result["success"],
f"患者: {get_data_count(result)}", "/today-outpatient/patients → 患者列表")
# ======================== 4. 住院全流程 ========================
def test_inpatient(tokens):
print("\n" + "="*60)
print("📋 模块四: 住院全流程 (入院→医嘱→护理→手术→出院)")
print("="*60)
doc = tokens.get("doctor")
nurse = tokens.get("nkhs")
# --- 患者首页 ---
result = api("GET", "/patient-home-manage/init", token=nurse, params={"pageNo": 1, "pageSize": 10})
record("TC-IN-HOME", "住院患者首页", result["success"],
f"在院患者: {get_data_count(result)}", "/patient-home-manage/init → 患者首页")
# --- 空床查询 ---
result = api("GET", "/patient-home-manage/empty-bed", token=nurse)
record("TC-IN-BED", "空床查询", result["success"],
"空床数据", "/patient-home-manage/empty-bed → 空床列表")
# --- 科室信息 ---
result = api("GET", "/patient-home-manage/caty", token=nurse)
record("TC-IN-CATY", "科室病区信息", result["success"],
"科室数据", "/patient-home-manage/caty → 科室列表")
# --- 住院登记 ---
result = api("GET", "/inhospital-charge/register/ward-list", token=doc)
record("TC-IN-REG", "住院登记-病区列表", result["success"],
"病区数据", "/inhospital-charge/register/ward-list → 病区列表")
# --- 住院预交金 ---
result = api("GET", "/inhospital-charge/advance-payment/advance-payment-info", token=doc)
record("TC-IN-ADV", "住院预交金信息", result["success"],
"预交金数据", "/inhospital-charge/advance-payment/advance-payment-info → 预交金")
# --- 住院收费 ---
result = api("GET", "/charge-manage/inpatient-charge/init", token=doc)
record("TC-IN-CHARGE", "住院收费初始化", result["success"],
"收费数据", "/charge-manage/inpatient-charge/init → 住院收费")
# --- 护理记录 ---
result = api("GET", "/nursing-record/patient-page", token=nurse, params={"pageNo": 1, "pageSize": 10})
record("TC-IN-NURSE-REC", "护理记录列表", result["success"],
f"护理记录: {get_data_count(result)}", "/nursing-record/patient-page → 护理记录")
# --- 护理记录模板 ---
result = api("GET", "/nursing-record/emr-template-page", token=nurse, params={"pageNo": 1, "pageSize": 10})
record("TC-IN-NURSE-TPL", "护理记录模板", result["success"],
f"模板: {get_data_count(result)}", "/nursing-record/emr-template-page → 护理模板")
# --- 生命体征 ---
result = api("GET", "/vital-signs/record-search", token=nurse)
record("TC-IN-VITAL", "生命体征查询", result["success"],
"体征数据", "/vital-signs/record-search → 体征查询")
# --- 生命体征图表 ---
result = api("GET", "/vital-signs-chart/page", token=nurse, params={"pageNo": 1, "pageSize": 10})
record("TC-IN-VITAL-CHART", "生命体征图表", result["success"],
f"图表: {get_data_count(result)}", "/vital-signs-chart/page → 体征图表")
# --- 护理评估增强 ---
result = api("GET", "/nursing-assessment-enhanced/page", token=nurse, params={"pageNo": 1, "pageSize": 10})
record("TC-IN-ASSESS", "护理评估列表", result["success"],
f"评估: {get_data_count(result)}", "/nursing-assessment-enhanced/page → 护理评估")
# --- 护理提醒 ---
result = api("GET", "/nursing-enhanced/reminder/page", token=nurse, params={"pageNo": 1, "pageSize": 10})
record("TC-IN-REMIND", "护理提醒列表", result["success"],
f"提醒: {get_data_count(result)}", "/nursing-enhanced/reminder/page → 护理提醒")
# --- 护理计划 ---
result = api("GET", "/nursing-enhanced/care-plan/page", token=nurse, params={"pageNo": 1, "pageSize": 10})
record("TC-IN-CAREPLAN", "护理计划列表", result["success"],
f"计划: {get_data_count(result)}", "/nursing-enhanced/care-plan/page → 护理计划")
# --- 护理质量 ---
result = api("GET", "/nursing-quality/page", token=nurse, params={"pageNo": 1, "pageSize": 10})
record("TC-IN-QUALITY", "护理质量列表", result["success"],
f"质量: {get_data_count(result)}", "/nursing-quality/page → 护理质量")
# --- 医嘱执行 ---
result = api("GET", "/nursing-execution/scan/page", token=nurse, params={"pageNo": 1, "pageSize": 10})
record("TC-IN-EXEC", "医嘱执行(扫描)", result["success"],
f"执行: {get_data_count(result)}", "/nursing-execution/scan/page → 医嘱执行")
# --- 护理交班 ---
result = api("GET", "/nursing-execution/handoff/page", token=nurse, params={"pageNo": 1, "pageSize": 10})
record("TC-IN-HANDOFF", "护理交班列表", result["success"],
f"交班: {get_data_count(result)}", "/nursing-execution/handoff/page → 护理交班")
# --- 护理输液 ---
result = api("GET", "/nursing-execution/infusion/page", token=nurse, params={"pageNo": 1, "pageSize": 10})
record("TC-IN-INFUSION", "护理输液列表", result["success"],
f"输液: {get_data_count(result)}", "/nursing-execution/infusion/page → 护理输液")
# --- 出院管理 ---
result = api("GET", "/discharge/page", token=doc, params={"pageNum": 1, "pageSize": 10})
record("TC-IN-DISCHARGE", "出院管理列表", result["success"],
f"出院: {get_data_count(result)}", "/discharge/page → 出院列表")
# ======================== 5. 手术全流程 ========================
def test_surgery(tokens):
print("\n" + "="*60)
print("📋 模块五: 手术全流程 (申请→讨论→排程→执行)")
print("="*60)
doc = tokens.get("doctor")
nurse = tokens.get("ssshs")
# --- 手术申请 ---
result = api("GET", "/clinical-manage/surgery/surgery-page", token=doc, params={"pageNum": 1, "pageSize": 10})
record("TC-SUR-APPLY", "手术申请列表", result["success"],
f"手术申请: {get_data_count(result)}", "/clinical-manage/surgery/surgery-page → 手术申请")
# --- 术前讨论 ---
result = api("GET", "/preop-discussion/page", token=doc, params={"pageNum": 1, "pageSize": 10})
record("TC-SUR-DISC", "术前讨论列表", result["success"],
f"讨论: {get_data_count(result)}", "/preop-discussion/page → 术前讨论")
# --- 手术排程 ---
result = api("GET", "/clinical-manage/surgery-schedule/page", token=nurse, params={"pageNum": 1, "pageSize": 10})
record("TC-SUR-SCHED", "手术排程列表", result["success"],
f"排程: {get_data_count(result)}", "/clinical-manage/surgery-schedule/page → 手术排程")
# --- 手术安全核查 ---
result = api("GET", "/surgery-safety-check/page", token=nurse, params={"pageNum": 1, "pageSize": 10})
record("TC-SUR-SAFETY", "手术安全核查", result["success"],
f"核查: {get_data_count(result)}", "/surgery-safety-check/page → 安全核查")
# --- 麻醉增强 ---
result = api("GET", "/anesthesia-enhanced/specimen/page", token=doc, params={"pageNum": 1, "pageSize": 10})
record("TC-SUR-ANES", "麻醉标本管理", result["success"],
f"标本: {get_data_count(result)}", "/anesthesia-enhanced/specimen/page → 麻醉标本")
# --- 手术室管理 ---
result = api("GET", "/base-data-manage/operating-room/operating-room", token=nurse, params={"pageNum": 1, "pageSize": 10})
record("TC-SUR-ROOM", "手术室管理", result["success"],
f"手术室: {get_data_count(result)}", "/base-data-manage/operating-room/operating-room → 手术室")
# --- 手术排班池 ---
result = api("GET", "/schedule-pool/page", token=nurse, params={"pageNum": 1, "pageSize": 10})
record("TC-SUR-POOL", "手术排班池", result["success"],
f"排班: {get_data_count(result)}", "/schedule-pool/page → 排班池")
# --- 手术病理(跨模块) ---
result = api("GET", "/cross-module/surgery-pathology/page", token=doc, params={"pageNum": 1, "pageSize": 10})
record("TC-SUR-PATHO", "手术病理追踪", result["success"],
f"病理: {get_data_count(result)}", "/cross-module/surgery-pathology/page → 病理追踪")
# ======================== 6. 医技检查 ========================
def test_inspection(tokens):
print("\n" + "="*60)
print("📋 模块六: 医技检查全流程")
print("="*60)
tech = tokens.get("tech")
doc = tokens.get("doctor")
# --- 检验申请 ---
result = api("GET", "/doctor-station/inspection/get-applyList", token=doc, params={"pageNum": 1, "pageSize": 10})
record("TC-INS-APPLY", "检验申请单", result["success"],
f"申请单: {get_data_count(result)}", "/doctor-station/inspection/get-applyList → 申请单")
# --- 标本条码 ---
result = api("GET", "/specimen-barcode/page", token=tech, params={"pageNum": 1, "pageSize": 10})
record("TC-INS-BARCODE", "标本条码管理", result["success"],
f"条码: {get_data_count(result)}", "/specimen-barcode/page → 条码列表")
# --- 影像增强(急报) ---
result = api("GET", "/radiology-enhanced/urgent-report/page", token=tech, params={"pageNum": 1, "pageSize": 10})
record("TC-INS-RAD-URG", "影像急报列表", result["success"],
f"急报: {get_data_count(result)}", "/radiology-enhanced/urgent-report/page → 急报")
# --- 影像统计 ---
result = api("GET", "/radiology-enhanced/statistics/page", token=tech, params={"pageNum": 1, "pageSize": 10})
record("TC-INS-RAD-STAT", "影像统计", result["success"],
f"统计: {get_data_count(result)}", "/radiology-enhanced/statistics/page → 影像统计")
# --- 影像对比 ---
result = api("GET", "/radiology-comparison/compare", token=tech)
record("TC-INS-RAD-COMP", "影像对比", result["success"],
"对比数据", "/radiology-comparison/compare → 影像对比")
# --- 3D重建任务 ---
result = api("GET", "/reconstruction/task/page", token=tech, params={"pageNum": 1, "pageSize": 10})
record("TC-INS-3D", "3D重建任务", result["success"],
f"任务: {get_data_count(result)}", "/reconstruction/task/page → 3D重建")
# --- 3D重建报告 ---
result = api("GET", "/reconstruction/report/page", token=tech, params={"pageNum": 1, "pageSize": 10})
record("TC-INS-3D-RPT", "3D重建报告", result["success"],
f"报告: {get_data_count(result)}", "/reconstruction/report/page → 3D报告")
# --- 影像报告 ---
result = api("GET", "/radiology-image/report/page", token=tech, params={"pageNum": 1, "pageSize": 10})
record("TC-INS-RAD-RPT", "影像报告列表", result["success"],
f"报告: {get_data_count(result)}", "/radiology-image/report/page → 影像报告")
# --- 检验科配置 ---
result = api("GET", "/inspection/lisConfig/init-page", token=tech, params={"pageNum": 1, "pageSize": 10})
record("TC-INS-LIS", "检验科配置", result["success"],
"配置数据", "/inspection/lisConfig/init-page → LIS配置")
# --- 检验标本 ---
result = api("GET", "/inspection/specimen/information-page", token=tech, params={"pageNum": 1, "pageSize": 10})
record("TC-INS-SPEC", "检验标本列表", result["success"],
f"标本: {get_data_count(result)}", "/inspection/specimen/information-page → 标本列表")
# --- 检验仪器 ---
result = api("GET", "/inspection/instrument/information-page", token=tech, params={"pageNum": 1, "pageSize": 10})
record("TC-INS-INST", "检验仪器列表", result["success"],
f"仪器: {get_data_count(result)}", "/inspection/instrument/information-page → 仪器列表")
# --- 检验观察 ---
result = api("GET", "/inspection/observation/information-page", token=tech, params={"pageNum": 1, "pageSize": 10})
record("TC-INS-OBS", "检验观察结果", result["success"],
f"观察: {get_data_count(result)}", "/inspection/observation/information-page → 观察结果")
# --- 实验室 ---
result = api("GET", "/inspection/laboratory/page", token=tech, params={"pageNum": 1, "pageSize": 10})
record("TC-INS-LAB", "实验室管理", result["success"],
f"实验室: {get_data_count(result)}", "/inspection/laboratory/page → 实验室")
# --- 检验参考范围 ---
result = api("GET", "/lab-ref-range/page", token=tech, params={"pageNum": 1, "pageSize": 10})
record("TC-INS-REF", "检验参考范围", result["success"],
f"参考范围: {get_data_count(result)}", "/lab-ref-range/page → 参考范围")
# --- FHIR CDA ---
result = api("GET", "/fhir-cda/cda/page", token=tech, params={"pageNum": 1, "pageSize": 10})
record("TC-INS-CDA", "CDA文档列表", result["success"],
f"CDA: {get_data_count(result)}", "/fhir-cda/cda/page → CDA文档")
# ======================== 7. 院感管理 ========================
def test_infection(tokens):
print("\n" + "="*60)
print("📋 模块七: 院感管理")
print("="*60)
nurse = tokens.get("nkhs")
# --- 院感监测 ---
result = api("GET", "/infection-enhanced/surveillance/page", token=nurse, params={"pageNum": 1, "pageSize": 10})
record("TC-INF-SURV", "院感监测列表", result["success"],
f"监测: {get_data_count(result)}", "/infection-enhanced/surveillance/page → 院感监测")
# --- 院感预警 ---
result = api("GET", "/infection-enhanced/warning/page", token=nurse, params={"pageNum": 1, "pageSize": 10})
record("TC-INF-WARN", "院感预警列表", result["success"],
f"预警: {get_data_count(result)}", "/infection-enhanced/warning/page → 院感预警")
# --- 耐药监测 ---
result = api("GET", "/infection-enhanced/mdr/page", token=nurse, params={"pageNum": 1, "pageSize": 10})
record("TC-INF-MDR", "耐药监测列表", result["success"],
f"耐药: {get_data_count(result)}", "/infection-enhanced/mdr/page → 耐药监测")
# --- 职业暴露 ---
result = api("GET", "/infection-enhanced/exposure/page", token=nurse, params={"pageNum": 1, "pageSize": 10})
record("TC-INF-EXPO", "职业暴露列表", result["success"],
f"暴露: {get_data_count(result)}", "/infection-enhanced/exposure/page → 职业暴露")
# --- 手卫生 ---
result = api("GET", "/infection-enhanced/hand-hygiene/page", token=nurse, params={"pageNum": 1, "pageSize": 10})
record("TC-INF-HAND", "手卫生管理", result["success"],
f"手卫生: {get_data_count(result)}", "/infection-enhanced/hand-hygiene/page → 手卫生")
# --- 环境监测 ---
result = api("GET", "/infection-enhanced/env-monitor/page", token=nurse, params={"pageNum": 1, "pageSize": 10})
record("TC-INF-ENV", "环境监测列表", result["success"],
f"环境: {get_data_count(result)}", "/infection-enhanced/env-monitor/page → 环境监测")
# --- 传染病直报 ---
result = api("GET", "/api/v1/epidemic/list", token=nurse, params={"pageNum": 1, "pageSize": 10})
record("TC-INF-EPID", "传染病直报列表", result["success"],
f"直报: {get_data_count(result)}", "/api/v1/epidemic/list → 传染病直报")
# ======================== 8. 质量管理 ========================
def test_quality(tokens):
print("\n" + "="*60)
print("📋 模块八: 质量管理")
print("="*60)
t = tokens.get("admin")
# --- 质量指标 ---
result = api("GET", "/quality-enhanced/indicator/page", token=t, params={"pageNum": 1, "pageSize": 10})
record("TC-QA-IND", "质量指标列表", result["success"],
f"指标: {get_data_count(result)}", "/quality-enhanced/indicator/page → 质量指标")
# --- 医嘱统计 ---
result = api("GET", "/quality-enhanced/order-stats/page", token=t, params={"pageNum": 1, "pageSize": 10})
record("TC-QA-ORDER", "医嘱统计列表", result["success"],
f"统计: {get_data_count(result)}", "/quality-enhanced/order-stats/page → 医嘱统计")
# --- 处方点评计划 ---
result = api("GET", "/api/v1/review/plans", token=t, params={"pageNum": 1, "pageSize": 10})
record("TC-QA-REVIEW", "处方点评计划", result["success"],
f"计划: {get_data_count(result)}", "/api/v1/review/plans → 点评计划")
# --- 处方点评记录 ---
result = api("GET", "/api/v1/review/records", token=t, params={"pageNum": 1, "pageSize": 10})
record("TC-QA-REVIEW-R", "处方点评记录", result["success"],
f"记录: {get_data_count(result)}", "/api/v1/review/records → 点评记录")
# --- 处方点评统计 ---
result = api("GET", "/api/v1/review/statistics", token=t)
record("TC-QA-REVIEW-S", "处方点评统计", result["success"],
"统计数据", "/api/v1/review/statistics → 点评统计")
# --- 合理用药规则 ---
result = api("GET", "/api/v1/rational-drug/interaction-rules", token=t, params={"pageNum": 1, "pageSize": 10})
record("TC-QA-RULES", "合理用药规则", result["success"],
f"规则: {get_data_count(result)}", "/api/v1/rational-drug/interaction-rules → 用药规则")
# --- 合理用药统计 ---
result = api("GET", "/api/v1/rational-drug/statistics", token=t)
record("TC-QA-RULES-S", "合理用药统计", result["success"],
"统计数据", "/api/v1/rational-drug/statistics → 用药统计")
# --- 危急值 ---
result = api("GET", "/api/v1/critical-value/pending", token=t, params={"pageNum": 1, "pageSize": 10})
record("TC-QA-CRIT", "危急值待处理", result["success"],
f"危急值: {get_data_count(result)}", "/api/v1/critical-value/pending → 危急值")
# --- 危急值统计 ---
result = api("GET", "/api/v1/critical-value/statistics", token=t)
record("TC-QA-CRIT-S", "危急值统计", result["success"],
"统计数据", "/api/v1/critical-value/statistics → 危急值统计")
# --- 医嘱闭环 ---
result = api("GET", "/api/v1/order-closed-loop/list", token=t, params={"pageNum": 1, "pageSize": 10})
record("TC-QA-CLOSED", "医嘱闭环列表", result["success"],
f"闭环: {get_data_count(result)}", "/api/v1/order-closed-loop/list → 医嘱闭环")
# --- 临床路径 ---
result = api("GET", "/clinical-pathway/page", token=t, params={"pageNum": 1, "pageSize": 10})
record("TC-QA-PATHWAY", "临床路径管理", result["success"],
f"路径: {get_data_count(result)}", "/clinical-pathway/page → 临床路径")
# --- 病历质量 ---
result = api("GET", "/api/v1/emr-quality/defect-statistics", token=t)
record("TC-QA-EMR", "病历质量统计", result["success"],
"统计数据", "/api/v1/emr-quality/defect-statistics → 病历质量")
# --- 跨模块: 处方点评 ---
result = api("GET", "/cross-module/prescription-review/page", token=t, params={"pageNum": 1, "pageSize": 10})
record("TC-QA-PRES", "处方点评(跨模块)", result["success"],
f"点评: {get_data_count(result)}", "/cross-module/prescription-review/page → 处方点评")
# --- 跨模块: DRG绩效 ---
result = api("GET", "/cross-module/drg-performance/page", token=t, params={"pageNum": 1, "pageSize": 10})
record("TC-QA-DRG", "DRG绩效分析", result["success"],
f"DRG: {get_data_count(result)}", "/cross-module/drg-performance/page → DRG绩效")
# --- 跨模块: 实验室预警 ---
result = api("GET", "/cross-module/lab-alert/page", token=t, params={"pageNum": 1, "pageSize": 10})
record("TC-QA-LAB", "实验室预警", result["success"],
f"预警: {get_data_count(result)}", "/cross-module/lab-alert/page → 实验室预警")
# --- 跨模块: 药品效期 ---
result = api("GET", "/cross-module/drug-expiry/page", token=t, params={"pageNum": 1, "pageSize": 10})
record("TC-QA-EXPIRY", "药品效期管理", result["success"],
f"效期: {get_data_count(result)}", "/cross-module/drug-expiry/page → 药品效期")
# ======================== 9. 中医管理 ========================
def test_tcm(tokens):
print("\n" + "="*60)
print("📋 模块九: 中医管理")
print("="*60)
doc = tokens.get("doctor")
# --- 中医方剂 ---
result = api("GET", "/api/v1/tcm/prescriptions", token=doc, params={"pageNum": 1, "pageSize": 10})
record("TC-TCM-PRES", "中医方剂列表", result["success"],
f"方剂: {get_data_count(result)}", "/api/v1/tcm/prescriptions → 方剂列表")
# --- 中医统计 ---
result = api("GET", "/api/v1/tcm/statistics", token=doc)
record("TC-TCM-STAT", "中医统计", result["success"],
"统计数据", "/api/v1/tcm/statistics → 中医统计")
# --- 中医处方 ---
result = api("GET", "/doctor-station/chinese-medical/condition-info", token=doc)
record("TC-TCM-DX", "中医辨证信息", result["success"],
"辨证数据", "/doctor-station/chinese-medical/condition-info → 中医辨证")
# --- 中医医嘱 ---
result = api("GET", "/doctor-station/chinese-medical/tcm-advice-base-info", token=doc)
record("TC-TCM-ADV", "中医医嘱基础", result["success"],
"医嘱数据", "/doctor-station/chinese-medical/tcm-advice-base-info → 中医医嘱")
# ======================== 10. 急诊管理 ========================
def test_emergency(tokens):
print("\n" + "="*60)
print("📋 模块十: 急诊管理")
print("="*60)
jzys = tokens.get("jzys")
jzhs = tokens.get("jzhs")
# --- 急诊分诊 ---
result = api("GET", "/emergency/triage/page", token=jzys, params={"pageNum": 1, "pageSize": 10})
record("TC-EM-TRIAGE", "急诊分诊列表", result["success"],
f"分诊: {get_data_count(result)}", "/emergency/triage/page → 急诊分诊")
# --- 叫号队列 ---
result = api("GET", "/triage/queue/list", token=jzhs)
record("TC-EM-QUEUE", "分诊叫号队列", result["success"],
"叫号队列", "/triage/queue/list → 叫号队列")
# --- 门诊增强互动 ---
result = api("GET", "/outpatient-enhanced/interaction/page", token=jzys, params={"pageNum": 1, "pageSize": 10})
record("TC-EM-INTERACT", "门诊互动记录", result["success"],
f"互动: {get_data_count(result)}", "/outpatient-enhanced/interaction/page → 互动记录")
# ======================== 11. 会诊管理 ========================
def test_consultation(tokens):
print("\n" + "="*60)
print("📋 模块十一: 会诊管理")
print("="*60)
doc = tokens.get("doctor")
con = tokens.get("consultant")
# --- 会诊列表 ---
result = api("GET", "/consultation/list", token=doc, params={"pageNum": 1, "pageSize": 10})
record("TC-CS-LIST", "会诊列表", result["success"],
f"会诊: {get_data_count(result)}", "/consultation/list → 会诊列表")
# --- 会诊科室 ---
result = api("GET", "/consultation/departmentTree", token=doc)
record("TC-CS-DEPT", "会诊科室树", result["success"],
"科室数据", "/consultation/departmentTree → 科室树")
# --- 会诊超时 ---
result = api("GET", "/cross-module/consultation-timeout/page", token=doc, params={"pageNum": 1, "pageSize": 10})
record("TC-CS-TIMEOUT", "会诊超时管理", result["success"],
f"超时: {get_data_count(result)}", "/cross-module/consultation-timeout/page → 超时管理")
# ======================== 12. 病案管理 ========================
def test_medical_record(tokens):
print("\n" + "="*60)
print("📋 模块十二: 病案管理")
print("="*60)
t = tokens.get("admin")
# --- 病案首页统计 ---
result = api("GET", "/api/v1/mr-homepage/statistics", token=t)
record("TC-MR-STAT", "病案首页统计", result["success"],
"统计数据", "/api/v1/mr-homepage/statistics → 病案统计")
# --- MR DRG ---
result = api("GET", "/mr-drg/page", token=t, params={"pageNum": 1, "pageSize": 10})
record("TC-MR-DRG", "DRG分组列表", result["success"],
f"DRG: {get_data_count(result)}", "/mr-drg/page → DRG分组")
# --- MR DRG统计 ---
result = api("GET", "/mr-drg/stats/overview", token=t)
record("TC-MR-DRG-OV", "DRG统计概览", result["success"],
"统计数据", "/mr-drg/stats/overview → DRG概览")
# --- 病案归档 ---
result = api("GET", "/emr-archive/page", token=t, params={"pageNum": 1, "pageSize": 10})
record("TC-MR-ARCH", "病案归档列表", result["success"],
f"归档: {get_data_count(result)}", "/emr-archive/page → 病案归档")
# --- 跨模块病历质量 ---
result = api("GET", "/cross-module/mr-quality/page", token=t, params={"pageNum": 1, "pageSize": 10})
record("TC-MR-QUALITY", "病历质量(跨模块)", result["success"],
f"质量: {get_data_count(result)}", "/cross-module/mr-quality/page → 病历质量")
# ======================== 13. 经营分析 ========================
def test_analytics(tokens):
print("\n" + "="*60)
print("📋 模块十三: 经营分析")
print("="*60)
t = tokens.get("admin")
# --- 经营分析页 ---
result = api("GET", "/business-analytics/page", token=t, params={"pageNum": 1, "pageSize": 10})
record("TC-AN-PAGE", "经营分析列表", result["success"],
f"分析: {get_data_count(result)}", "/business-analytics/page → 经营分析")
# --- 经营分析汇总 ---
result = api("GET", "/business-analytics/summary", token=t)
record("TC-AN-SUM", "经营分析汇总", result["success"],
"汇总数据", "/business-analytics/summary → 经营汇总")
# --- 药品库存预警 ---
result = api("GET", "/pharmacy-stock-alert/page", token=t, params={"pageNum": 1, "pageSize": 10})
record("TC-AN-STOCK", "药品库存预警", result["success"],
f"预警: {get_data_count(result)}", "/pharmacy-stock-alert/page → 库存预警")
# --- DRG绩效(跨模块) ---
result = api("GET", "/cross-module/drg-performance/summary", token=t)
record("TC-AN-DRG", "DRG绩效汇总", result["success"],
"绩效数据", "/cross-module/drg-performance/summary → DRG绩效")
# --- 报表-门诊收费 ---
result = api("GET", "/report-manage/charge/init", token=t)
record("TC-AN-RPT-CHG", "门诊收费报表", result["success"],
"报表数据", "/report-manage/charge/init → 门诊收费报表")
# --- 报表-挂号统计 ---
result = api("GET", "/report-manage/register/init", token=t)
record("TC-AN-RPT-REG", "挂号统计报表", result["success"],
"统计数据", "/report-manage/register/init → 挂号统计")
# --- 报表-住院收费 ---
result = api("GET", "/report-manage/pharmacy-settlement/list-info", token=t, params={"pageNum": 1, "pageSize": 10})
record("TC-AN-RPT-PHARM", "药房结算报表", result["success"],
f"结算: {get_data_count(result)}", "/report-manage/pharmacy-settlement/list-info → 药房结算")
# --- 报表统计 ---
result = api("GET", "/report-manage/report-statistics/daily-report", token=t)
record("TC-AN-RPT-DAILY", "日报统计", result["success"],
"日报数据", "/report-manage/report-statistics/daily-report → 日报")
# ======================== 14. 权限隔离 ========================
def test_permission(tokens):
print("\n" + "="*60)
print("📋 模块十四: 权限隔离验证")
print("="*60)
tests = [
("doctor", "/system/user/list", "医生→用户管理"),
("doctor", "/system/role/list", "医生→角色管理"),
("pharmacist", "/system/user/list", "药师→用户管理"),
("finance", "/system/role/list", "收费员→角色管理"),
("nkhs", "/system/config/list", "护士→系统配置"),
]
for i, (key, path, desc) in enumerate(tests):
t = tokens.get(key)
if not t:
record(f"TC-PERM-{i+1}", f"{desc}(未登录)", False, "跳过")
continue
result = api("GET", path, token=t)
# 后端不强制权限隔离 = 返回200
if result["success"]:
record(f"TC-PERM-{i+1}", f"{desc}", False,
f"⚠️ 无隔离(code=200)", f"{key}{path} → 200(应403)")
else:
record(f"TC-PERM-{i+1}", f"{desc}", True,
f"已隔离(code={result['code']})", f"{key}{path}{result['code']}")
# ======================== 15. 基础数据 ========================
def test_base_data(tokens):
print("\n" + "="*60)
print("📋 模块十五: 基础数据管理")
print("="*60)
t = tokens.get("admin")
# --- 组织管理 ---
result = api("GET", "/base-data-manage/organization/organization", token=t, params={"pageNum": 1, "pageSize": 10})
record("TC-BD-ORG", "组织管理", result["success"],
f"组织: {get_data_count(result)}", "/base-data-manage/organization/organization → 组织列表")
# --- 科室管理 ---
result = api("GET", "/base-data-manage/location/location-page", token=t, params={"pageNum": 1, "pageSize": 10})
record("TC-BD-LOC", "科室管理", result["success"],
f"科室: {get_data_count(result)}", "/base-data-manage/location/location-page → 科室列表")
# --- 人员管理 ---
result = api("GET", "/base-data-manage/practitioner/user-practitioner-page", token=t, params={"pageNum": 1, "pageSize": 10})
record("TC-BD-PRACT", "人员管理", result["success"],
f"人员: {get_data_count(result)}", "/base-data-manage/practitioner/user-practitioner-page → 人员列表")
# --- ICD10 ---
result = api("GET", "/icd10/page", token=t, params={"pageNum": 1, "pageSize": 10})
record("TC-BD-ICD", "ICD10编码管理", result["success"],
f"ICD10: {get_data_count(result)}", "/icd10/page → ICD10列表")
# --- 数据字典 ---
result = api("GET", "/dict-dictionary/definition/page", token=t, params={"pageNum": 1, "pageSize": 10})
record("TC-BD-DICT", "数据字典管理", result["success"],
f"字典: {get_data_count(result)}", "/dict-dictionary/definition/page → 字典列表")
# --- 检查方法 ---
result = api("GET", "/check/method/list", token=t)
record("TC-BD-CHECK", "检查方法列表", result["success"],
f"方法: {get_data_count(result)}", "/check/method/list → 检查方法")
# --- 检查部位 ---
result = api("GET", "/check/part/list", token=t)
record("TC-BD-PART", "检查部位列表", result["success"],
f"部位: {get_data_count(result)}", "/check/part/list → 检查部位")
# --- 知情同意 ---
result = api("GET", "/informed-consent/page", token=t, params={"pageNum": 1, "pageSize": 10})
record("TC-BD-CONSENT", "知情同意列表", result["success"],
f"同意书: {get_data_count(result)}", "/informed-consent/page → 知情同意")
# ======================== 16. 支付 ========================
def test_payment(tokens):
print("\n" + "="*60)
print("📋 模块十六: 支付与结算")
print("="*60)
fin = tokens.get("finance")
result = api("GET", "/three-part/pay/page", token=fin, params={"pageNum": 1, "pageSize": 10})
record("TC-PAY-3PART", "三方支付列表", result["success"],
f"支付: {get_data_count(result)}", "/three-part/pay/page → 三方支付")
result = api("GET", "/charge/patientCardRenewal/init", token=fin)
record("TC-PAY-CARD", "患者建卡", result["success"],
"建卡数据", "/charge/patientCardRenewal/init → 建卡初始化")
# ======================== 主函数 ========================
def main():
global PASSED, FAILED
print("=" * 70)
print("🏥 HealthLink-HIS 三甲医院复杂业务逻辑全流程测试 V2")
print(f"📅 {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f"🌐 {BASE_URL}")
print("=" * 70)
tokens = test_auth()
test_system(tokens)
test_outpatient(tokens)
test_inpatient(tokens)
test_surgery(tokens)
test_inspection(tokens)
test_infection(tokens)
test_quality(tokens)
test_tcm(tokens)
test_emergency(tokens)
test_consultation(tokens)
test_medical_record(tokens)
test_analytics(tokens)
test_permission(tokens)
test_base_data(tokens)
test_payment(tokens)
total = PASSED + FAILED
rate = (PASSED / total * 100) if total > 0 else 0
print("\n" + "=" * 70)
print("📊 测试结果汇总")
print("=" * 70)
print(f" 总用例数: {total}")
print(f" 通过: ✅ {PASSED}")
print(f" 失败: ❌ {FAILED}")
print(f" 通过率: {rate:.1f}%")
if FAILED > 0:
print(f"\n❌ 失败用例 ({FAILED}个):")
for r in RESULTS:
if not r["passed"]:
print(f" - [{r['id']}] {r['name']}: {r['details']}")
report = {
"test_time": datetime.now().isoformat(),
"environment": BASE_URL,
"total": total, "passed": PASSED, "failed": FAILED,
"pass_rate": f"{rate:.1f}%",
"results": RESULTS,
}
report_path = "/root/.openclaw/workspace/his-repo/MD/test/reports/06_complex_v2_report.json"
os.makedirs(os.path.dirname(report_path), exist_ok=True)
with open(report_path, "w", encoding="utf-8") as f:
json.dump(report, f, ensure_ascii=False, indent=2)
print(f"\n📄 报告: {report_path}")
return 0 if FAILED == 0 else 1
if __name__ == "__main__":
sys.exit(main())