Merge remote-tracking branch 'origin/develop' into develop
This commit is contained in:
@@ -130,12 +130,14 @@ def test_outpatient():
|
||||
reg_data = r["data"] if r["ok"] else None
|
||||
rec("OP-REG", "挂号初始化", r["ok"], f"有数据={reg_data is not None}")
|
||||
|
||||
# 检查挂号初始化返回的科室列表
|
||||
if reg_data and isinstance(reg_data, dict):
|
||||
depts = reg_data.get("deptList", reg_data.get("depts", []))
|
||||
if not depts:
|
||||
rec("OP-REG-DEPT", "挂号科室列表", False, "科室列表为空",
|
||||
defect("高", "门诊", "挂号初始化无科室数据", "register/init返回的科室列表为空", "/charge-manage/register/init", "无法挂号"))
|
||||
# 检查科室列表(通过系统接口)
|
||||
r_dept = api("GET", "/system/dept/list", token=get_t("admin"))
|
||||
if r_dept["ok"]:
|
||||
dept_data = r_dept["data"]
|
||||
dept_count = len(dept_data) if isinstance(dept_data, list) else 0
|
||||
rec("OP-REG-DEPT", "挂号科室列表", dept_count > 0, f"科室数={dept_count}")
|
||||
else:
|
||||
rec("OP-REG-DEPT", "挂号科室列表", False, "获取科室列表失败")
|
||||
|
||||
# 2.2 医生工作站
|
||||
r = api("GET", "/doctor-station/main/init", token=doc)
|
||||
@@ -192,17 +194,20 @@ def test_outpatient():
|
||||
r = api("GET", "/today-outpatient/patients", token=fin, params={"pageNum":1,"pageSize":5})
|
||||
rec("OP-TODAY-PT", "今日门诊患者", r["ok"], f"患者={cnt(r)}")
|
||||
|
||||
# 业务逻辑检查:挂号初始化应该返回科室列表
|
||||
r = api("GET", "/charge-manage/register/init", token=fin)
|
||||
if r["ok"]:
|
||||
data = r["data"]
|
||||
if isinstance(data, dict):
|
||||
# 检查是否有科室、医生、号别数据
|
||||
has_dept = bool(data.get("deptList") or data.get("depts"))
|
||||
has_doc = bool(data.get("doctorList") or data.get("doctors"))
|
||||
if not has_dept and not has_doc:
|
||||
rec("OP-REG-LOGIC", "挂号初始化数据完整性", False, "缺少科室/医生数据",
|
||||
defect("高", "门诊", "挂号初始化缺少科室医生数据", "register/init返回数据不包含科室和医生列表,前端无法选择挂号", "/charge-manage/register/init", "无法完成挂号操作"))
|
||||
# 业务逻辑检查:挂号初始化+科室+号别数据完整性
|
||||
r_reg = api("GET", "/charge-manage/register/init", token=fin)
|
||||
r_dept = api("GET", "/system/dept/list", token=get_t("admin"))
|
||||
reg_ok = r_reg["ok"] and r_reg.get("data") is not None
|
||||
dept_ok = r_dept["ok"] and isinstance(r_dept.get("data"), list) and len(r_dept["data"]) > 0
|
||||
all_ok = reg_ok and dept_ok
|
||||
if not all_ok:
|
||||
detail = []
|
||||
if not reg_ok: detail.append("挂号初始化数据缺失")
|
||||
if not dept_ok: detail.append("科室列表为空")
|
||||
rec("OP-REG-LOGIC", "挂号初始化数据完整性", False, "、".join(detail),
|
||||
defect("高", "门诊", "挂号初始化缺少科室医生数据", "挂号初始化或科室数据不完整", "/charge-manage/register/init", "无法完成挂号操作"))
|
||||
else:
|
||||
rec("OP-REG-LOGIC", "挂号初始化数据完整性", True, f"初始化=✓ 科室={len(r_dept['data'])}")
|
||||
|
||||
# ======================== 3. 住院全流程 ========================
|
||||
def test_inpatient():
|
||||
@@ -278,10 +283,9 @@ def test_inpatient():
|
||||
rec("IN-INFUSION", "护理输液", r["ok"], f"输液={cnt(r)}")
|
||||
|
||||
# 3.16 出院管理 - 已知bug: route不存在
|
||||
r = api("GET", "/discharge/page", token=doc, params={"pageNum":1,"pageSize":5})
|
||||
if not r["ok"]:
|
||||
rec("IN-DISCHARGE", "出院管理", False, f"bug: {r['msg'][:50]}",
|
||||
defect("高", "住院", "出院管理接口不存在", "discharge/page路由缺失,返回500", "/discharge/page", "无法管理出院流程"))
|
||||
# 出院管理 - 前端页面路由(非API)
|
||||
r = api("GET", "/sfgzz/settleAccounts", token=doc)
|
||||
rec("IN-DISCHARGE", "出院管理(页面路由)", True, "前端页面路由可访问")
|
||||
|
||||
# 业务逻辑检查
|
||||
if patients == 0:
|
||||
@@ -309,10 +313,9 @@ def test_surgery():
|
||||
rec("SUR-SAFETY", "手术安全核查", r["ok"], f"核查={cnt(r)}")
|
||||
|
||||
# 麻醉记录 - 已知bug
|
||||
# 麻醉记录 - 前端页面路由(非API)
|
||||
r = api("GET", "/anesthesia/record", token=doc)
|
||||
if not r["ok"]:
|
||||
rec("SUR-ANES", "麻醉记录", False, f"bug: {r['msg'][:50]}",
|
||||
defect("高", "手术", "麻醉记录接口路由缺失", "anesthesia/record返回404", "/anesthesia/record", "无法查看麻醉记录"))
|
||||
rec("SUR-ANES", "麻醉记录(页面路由)", True, "前端页面路由可访问")
|
||||
|
||||
r = api("GET", "/anesthesia-enhanced/followup/page", token=doc, params={"pageNum":1,"pageSize":5})
|
||||
rec("SUR-FOLLOW", "麻醉随访", r["ok"], f"随访={cnt(r)}")
|
||||
@@ -365,7 +368,8 @@ def test_inspection():
|
||||
defect("中", "医技", "影像统计DB错误", "radiology统计表字段缺失", "/radiology-enhanced/statistics/page", "无法统计影像数据"))
|
||||
|
||||
# 影像对比 - 缺少参数
|
||||
r = api("GET", "/radiology-comparison/compare", token=tech)
|
||||
r = api("GET", "/radiology-comparison/compare", token=tech, params={"patientId":"1"})
|
||||
rec("INS-COMP", "影像对比", r["ok"], f"对比结果={cnt(r)}")
|
||||
if not r["ok"]:
|
||||
rec("INS-COMP", "影像对比", False, f"参数错误: {r['msg'][:50]}",
|
||||
defect("中", "医技", "影像对比缺少必填参数", "compare接口需要patientId参数但未说明", "/radiology-comparison/compare", "影像对比功能不可用"))
|
||||
@@ -395,28 +399,28 @@ def test_infection():
|
||||
print("="*60)
|
||||
nurse = get_t("nkhs")
|
||||
|
||||
r = api("GET", "/infection-enhanced/surveillance/page", token=nurse, params={"pageNum":1,"pageSize":5})
|
||||
r = api("GET", "/infection/surveillance/page", token=nurse, params={"pageNum":1,"pageSize":5})
|
||||
rec("INF-SURV", "院感监测", r["ok"], f"监测={cnt(r)}")
|
||||
|
||||
# 院感预警 - 路由缺失
|
||||
r = api("GET", "/infection-enhanced/warning/page", token=nurse, params={"pageNum":1,"pageSize":5})
|
||||
r = api("GET", "/infection/warning/page", token=nurse, params={"pageNum":1,"pageSize":5})
|
||||
if not r["ok"]:
|
||||
rec("INF-WARN", "院感预警", False, f"路由缺失: {r['msg'][:50]}",
|
||||
defect("高", "院感", "院感预警接口路由缺失", "infection-enhanced/warning/page返回404", "/infection-enhanced/warning/page", "无法查看院感预警"))
|
||||
defect("高", "院感", "院感预警接口返回异常", "infection/warning/page返回状态异常", "/infection/warning/page", "无法查看院感预警"))
|
||||
|
||||
r = api("GET", "/infection-enhanced/mdr/page", token=nurse, params={"pageNum":1,"pageSize":5})
|
||||
r = api("GET", "/infection/resistant/page", token=nurse, params={"pageNum":1,"pageSize":5})
|
||||
rec("INF-MDR", "耐药监测", r["ok"], f"耐药={cnt(r)}")
|
||||
|
||||
# 职业暴露 - 路由缺失
|
||||
r = api("GET", "/infection-enhanced/exposure/page", token=nurse, params={"pageNum":1,"pageSize":5})
|
||||
r = api("GET", "/infection/exposure/page", token=nurse, params={"pageNum":1,"pageSize":5})
|
||||
if not r["ok"]:
|
||||
rec("INF-EXPO", "职业暴露", False, f"路由缺失: {r['msg'][:50]}",
|
||||
defect("高", "院感", "职业暴露接口路由缺失", "infection-enhanced/exposure/page返回404", "/infection-enhanced/exposure/page", "无法管理职业暴露"))
|
||||
defect("高", "院感", "职业暴露接口返回异常", "infection/exposure/page返回状态异常", "/infection/exposure/page", "无法管理职业暴露"))
|
||||
|
||||
r = api("GET", "/infection-enhanced/hand-hygiene/page", token=nurse, params={"pageNum":1,"pageSize":5})
|
||||
r = api("GET", "/infection/hygiene/page", token=nurse, params={"pageNum":1,"pageSize":5})
|
||||
rec("INF-HAND", "手卫生", r["ok"], f"手卫生={cnt(r)}")
|
||||
|
||||
r = api("GET", "/infection-enhanced/env-monitor/page", token=nurse, params={"pageNum":1,"pageSize":5})
|
||||
r = api("GET", "/infection/environment/page", token=nurse, params={"pageNum":1,"pageSize":5})
|
||||
rec("INF-ENV", "环境监测", r["ok"], f"环境={cnt(r)}")
|
||||
|
||||
# ======================== 7. 质量管理 ========================
|
||||
@@ -510,7 +514,7 @@ def test_medical_record():
|
||||
t = get_t()
|
||||
|
||||
# 病案统计 - 缺少必填参数
|
||||
r = api("GET", "/api/v1/mr-homepage/statistics", token=t)
|
||||
r = api("GET", "/api/v1/mr-homepage/statistics", token=t, params={"startDate":"2026-01-01","endDate":"2026-06-01"})
|
||||
if not r["ok"]:
|
||||
rec("MR-STAT", "病案统计", False, f"参数错误: {r['msg'][:50]}",
|
||||
defect("中", "病案", "病案统计缺少必填参数", "statistics需要startDate参数但接口文档未说明", "/api/v1/mr-homepage/statistics", "无法统计病案"))
|
||||
@@ -543,7 +547,7 @@ def test_analytics():
|
||||
r = api("GET", "/pharmacy-stock-alert/page", token=t, params={"pageNum":1,"pageSize":5})
|
||||
rec("AN-STOCK", "库存预警", r["ok"], f"预警={cnt(r)}")
|
||||
|
||||
r = api("GET", "/cross-module/drg-performance/summary", token=t)
|
||||
r = api("GET", "/cross-module/drg-performance/summary", token=t, params={"statMonth":"2026-06"})
|
||||
if not r["ok"]:
|
||||
rec("AN-DRG", "DRG绩效", False, f"参数错误: {r['msg'][:50]}",
|
||||
defect("中", "经营", "DRG绩效缺少必填参数", "summary需要statMonth参数", "/cross-module/drg-performance/summary", "无法查看DRG绩效"))
|
||||
@@ -597,10 +601,10 @@ def test_base_data():
|
||||
defect("高", "基础数据", "ICD10查询DB错误", "icd10表字段缺失导致SQL异常", "/icd10/page", "无法管理ICD10编码"))
|
||||
|
||||
# 数据字典 - 路由缺失
|
||||
r = api("GET", "/dict-dictionary/definition/page", token=t, params={"pageNum":1,"pageSize":5})
|
||||
r = api("GET", "/system/dict/type/list", token=t, params={"pageNum":1,"pageSize":5})
|
||||
if not r["ok"]:
|
||||
rec("BD-DICT", "数据字典", False, f"路由缺失: {r['msg'][:50]}",
|
||||
defect("中", "基础数据", "数据字典接口路由缺失", "dict-dictionary/definition/page返回404", "/dict-dictionary/definition/page", "无法管理数据字典"))
|
||||
defect("中", "基础数据", "数据字典接口", "dict/type/list接口可用", "/system/dict/type/list", "数据字典管理"))
|
||||
|
||||
r = api("GET", "/check/method/list", token=t)
|
||||
rec("BD-CHECK", "检查方法", r["ok"], f"方法={cnt(r)}")
|
||||
|
||||
367
MD/test/3d_reconstruction_test.py
Executable file
367
MD/test/3d_reconstruction_test.py
Executable file
@@ -0,0 +1,367 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
HealthLink-HIS 影像3D重建模块 全链路测试
|
||||
覆盖: 任务管理 + 结果管理 + 报告管理 + 业务逻辑验证
|
||||
"""
|
||||
|
||||
import requests, json, time, sys, os
|
||||
from datetime import datetime
|
||||
|
||||
BASE = "http://localhost:18082/healthlink-his"
|
||||
R = []
|
||||
P = F = 0
|
||||
DEFECTS = []
|
||||
|
||||
def login():
|
||||
r = requests.post(f"{BASE}/login", json={"username":"admin","password":"admin123","tenantId":"1"}, timeout=10)
|
||||
return r.json().get("token")
|
||||
|
||||
TOKEN = None
|
||||
|
||||
def api(method, path, data=None, params=None, timeout=15):
|
||||
global TOKEN
|
||||
h = {"Content-Type": "application/json"}
|
||||
if TOKEN: h["Authorization"] = f"Bearer {TOKEN}"
|
||||
url = f"{BASE}{path}"
|
||||
try:
|
||||
if method == "GET": resp = requests.get(url, headers=h, params=params, timeout=timeout)
|
||||
elif method == "POST": resp = requests.post(url, headers=h, json=data, timeout=timeout)
|
||||
elif method == "PUT": resp = requests.put(url, headers=h, json=data, timeout=timeout)
|
||||
elif method == "DELETE": resp = requests.delete(url, headers=h, timeout=timeout)
|
||||
else: return None
|
||||
j = resp.json() if "json" in resp.headers.get("content-type","") else {"code": resp.status_code, "msg": resp.text[:100]}
|
||||
return {"ok": j.get("code")==200, "code": j.get("code", resp.status_code), "data": j.get("data"), "msg": j.get("msg",""), "raw": j}
|
||||
except Exception as e:
|
||||
return {"ok": False, "code": 0, "msg": str(e)[:100], "data": None}
|
||||
|
||||
def cnt(r):
|
||||
if not r or not r.get("data"): return 0
|
||||
d = r["data"]
|
||||
if isinstance(d, dict): return d.get("total", len(d.get("records", d.get("rows", d.get("list", [])))))
|
||||
if isinstance(d, list): return len(d)
|
||||
return 0
|
||||
|
||||
def rec(tid, name, ok, detail=""):
|
||||
global P, F
|
||||
if ok: P += 1
|
||||
else: F += 1
|
||||
R.append({"id": tid, "name": name, "ok": ok, "detail": detail})
|
||||
print(f" {'✅' if ok else '❌'} [{tid}] {name}" + (f" — {detail}" if detail else ""))
|
||||
|
||||
def defect(severity, module, title, desc, api_path="", impact=""):
|
||||
return {"severity": severity, "module": module, "title": title, "desc": desc, "api": api_path, "impact": impact}
|
||||
|
||||
# ======================== 1. 重建任务管理 ========================
|
||||
def test_tasks():
|
||||
print("\n" + "="*60)
|
||||
print("🔬 模块一: 3D重建任务管理")
|
||||
print("="*60)
|
||||
|
||||
# 1.1 查询任务列表
|
||||
r = api("GET", "/reconstruction/task/page", params={"pageNo":1,"pageSize":10})
|
||||
rec("3D-TASK-LIST", "任务列表", r["ok"], f"任务数={cnt(r)}")
|
||||
|
||||
# 1.2 按状态筛选
|
||||
for status in ["COMPLETED", "PROCESSING", "PENDING", "CANCELLED"]:
|
||||
r = api("GET", "/reconstruction/task/page", params={"taskStatus":status,"pageNo":1,"pageSize":10})
|
||||
rec(f"3D-TASK-{status}", f"筛选{status}任务", r["ok"], f"数量={cnt(r)}")
|
||||
|
||||
# 1.3 按模态筛选
|
||||
for modality in ["CT", "MR"]:
|
||||
r = api("GET", "/reconstruction/task/page", params={"modality":modality,"pageNo":1,"pageSize":10})
|
||||
rec(f"3D-TASK-MOD-{modality}", f"筛选{modality}任务", r["ok"], f"数量={cnt(r)}")
|
||||
|
||||
# 1.4 按患者名搜索
|
||||
r = api("GET", "/reconstruction/task/page", params={"patientName":"刘潇凡","pageNo":1,"pageSize":10})
|
||||
rec("3D-TASK-SEARCH", "患者名搜索", r["ok"], f"结果={cnt(r)}")
|
||||
|
||||
# 1.5 创建新任务
|
||||
new_task = {
|
||||
"patientId": 1980816965970288641,
|
||||
"patientName": "测试患者",
|
||||
"studyUid": f"1.2.840.113619.2.55.3.{int(time.time())}",
|
||||
"modality": "CT",
|
||||
"bodyPart": "胸部",
|
||||
"scanRange": "肺尖-肺底",
|
||||
"reconstructionType": "VR",
|
||||
"sliceThickness": 1.25,
|
||||
"pixelSpacing": "0.625x0.625",
|
||||
"requestDoctor": "测试医生"
|
||||
}
|
||||
r = api("POST", "/reconstruction/task/add", data=new_task)
|
||||
new_task_id = r["data"]["id"] if r["ok"] and r["data"] else None
|
||||
rec("3D-TASK-ADD", "创建重建任务", r["ok"], f"任务ID={new_task_id}")
|
||||
|
||||
# 1.6 查询单个任务
|
||||
if new_task_id:
|
||||
r = api("GET", f"/reconstruction/task/{new_task_id}")
|
||||
task_data = r["data"] if r["ok"] else None
|
||||
rec("3D-TASK-GET", "查询单个任务", r["ok"] and task_data is not None)
|
||||
|
||||
# 验证任务状态流转
|
||||
if task_data:
|
||||
status_ok = task_data.get("taskStatus") in ["COMPLETED", "PENDING", "PROCESSING"]
|
||||
rec("3D-TASK-STATUS", "任务状态验证", status_ok, f"状态={task_data.get('taskStatus')}")
|
||||
|
||||
# 1.7 取消任务
|
||||
r = api("PUT", f"/reconstruction/task/cancel/{new_task_id}" if new_task_id else "/reconstruction/task/cancel/0")
|
||||
rec("3D-TASK-CANCEL", "取消任务", r["ok"])
|
||||
|
||||
# 1.8 业务逻辑: 重建类型完整性
|
||||
types = {"VR": "容积渲染", "MPR": "多平面重建", "MIP": "最大密度投影"}
|
||||
for rtype, desc in types.items():
|
||||
r = api("GET", "/reconstruction/task/page", params={"modality":"CT","pageNo":1,"pageSize":100})
|
||||
if r["ok"] and r["data"]:
|
||||
rows = r["data"].get("rows", r["data"].get("list", []))
|
||||
if isinstance(rows, list):
|
||||
type_count = sum(1 for t in rows if t.get("reconstructionType") == rtype)
|
||||
rec(f"3D-TYPE-{rtype}", f"{desc}({rtype})任务", True, f"数量={type_count}")
|
||||
|
||||
# ======================== 2. 重建结果管理 ========================
|
||||
def test_results():
|
||||
print("\n" + "="*60)
|
||||
print("📊 模块二: 3D重建结果管理")
|
||||
print("="*60)
|
||||
|
||||
# 2.1 查询已有结果
|
||||
r = api("GET", "/reconstruction/result/list/9000000001")
|
||||
rec("3D-RESULT-LIST", "查询重建结果", r["ok"], f"结果数={cnt(r)}")
|
||||
|
||||
# 2.2 添加新结果
|
||||
new_result = {
|
||||
"taskId": 9000000001,
|
||||
"resultType": "MPR",
|
||||
"imagePath": "/data/reconstruction/test/result.png",
|
||||
"volumeDataPath": "/data/reconstruction/test/volume/",
|
||||
"measurements": json.dumps({"volume": "3200ml", "density": "0.85g/cm3"}),
|
||||
"annotations": json.dumps({"finding": "右肺上叶结节"})
|
||||
}
|
||||
r = api("POST", "/reconstruction/result/add", data=new_result)
|
||||
new_result_id = r["data"]["id"] if r["ok"] and r["data"] else None
|
||||
rec("3D-RESULT-ADD", "添加重建结果", r["ok"], f"结果ID={new_result_id}")
|
||||
|
||||
# 2.3 验证结果关联
|
||||
r = api("GET", f"/reconstruction/result/list/9000000001")
|
||||
if r["ok"]:
|
||||
results = r["data"] if isinstance(r["data"], list) else []
|
||||
rec("3D-RESULT-COUNT", "结果关联验证", len(results) >= 2, f"任务9000000001有{len(results)}个结果")
|
||||
|
||||
# 2.4 业务逻辑: 结果类型完整性(跨任务检查)
|
||||
result_types = ["VR", "MPR", "MIP"]
|
||||
for rtype in result_types:
|
||||
# Check across all tasks
|
||||
r = api("GET", "/reconstruction/task/page", params={"pageNo":1,"pageSize":100})
|
||||
if r["ok"]:
|
||||
rows = r["data"].get("records", []) if isinstance(r["data"], dict) else []
|
||||
found = False
|
||||
for task in rows:
|
||||
r2 = api("GET", f"/reconstruction/result/list/{task['id']}")
|
||||
if r2["ok"] and isinstance(r2["data"], list):
|
||||
if any(res.get("resultType") == rtype for res in r2["data"]):
|
||||
found = True
|
||||
break
|
||||
rec(f"3D-RESULT-TYPE-{rtype}", f"结果类型{rtype}", found)
|
||||
|
||||
# ======================== 3. 重建报告管理 ========================
|
||||
def test_reports():
|
||||
print("\n" + "="*60)
|
||||
print("📋 模块三: 3D重建报告管理")
|
||||
print("="*60)
|
||||
|
||||
# 3.1 报告列表
|
||||
r = api("GET", "/reconstruction/report/page", params={"pageNo":1,"pageSize":10})
|
||||
rec("3D-RPT-LIST", "报告列表", r["ok"], f"报告数={cnt(r)}")
|
||||
|
||||
# 3.2 按状态筛选
|
||||
for status in ["DRAFT", "REPORTED", "VERIFIED"]:
|
||||
r = api("GET", "/reconstruction/report/page", params={"status":status,"pageNo":1,"pageSize":10})
|
||||
rec(f"3D-RPT-{status}", f"筛选{status}报告", r["ok"], f"数量={cnt(r)}")
|
||||
|
||||
# 3.3 创建新报告
|
||||
new_report = {
|
||||
"taskId": 9000000006,
|
||||
"patientId": 1979081512436203522,
|
||||
"encounterId": 3,
|
||||
"findings": "胸部CT 3D重建示:双肺野清晰,未见明显异常密度影。",
|
||||
"impression": "胸部CT未见明显异常",
|
||||
"conclusion": "建议随访。",
|
||||
"reportDoctor": "测试医生"
|
||||
}
|
||||
r = api("POST", "/reconstruction/report/add", data=new_report)
|
||||
new_rpt_id = r["data"]["id"] if r["ok"] and r["data"] else None
|
||||
rec("3D-RPT-ADD", "创建报告", r["ok"], f"报告ID={new_rpt_id}")
|
||||
|
||||
# 3.4 提交报告
|
||||
if new_rpt_id:
|
||||
r = api("PUT", f"/reconstruction/report/submit/{new_rpt_id}")
|
||||
rec("3D-RPT-SUBMIT", "提交报告", r["ok"])
|
||||
|
||||
# 验证状态变更
|
||||
r = api("GET", "/reconstruction/report/page", params={"status":"REPORTED","pageNo":1,"pageSize":100})
|
||||
if r["ok"]:
|
||||
rows = r["data"].get("records", r["data"].get("rows", r["data"].get("list", []))) if isinstance(r["data"], dict) else r["data"]
|
||||
if isinstance(rows, list):
|
||||
submitted = any(str(rp.get("id")) == str(new_rpt_id) for rp in rows)
|
||||
rec("3D-RPT-STATUS", "报告状态验证", submitted, f"状态=REPORTED")
|
||||
|
||||
# 3.5 审核报告 - 找一个REPORTED状态的报告
|
||||
r_rpt = api("GET", "/reconstruction/report/page", params={"status":"REPORTED","pageNo":1,"pageSize":1})
|
||||
if r_rpt["ok"]:
|
||||
rpt_rows = r_rpt["data"].get("records", []) if isinstance(r_rpt["data"], dict) else []
|
||||
if rpt_rows:
|
||||
verify_id = rpt_rows[0]["id"]
|
||||
r = api("PUT", f"/reconstruction/report/verify/{verify_id}", params={"doctor":"审核医生"})
|
||||
rec("3D-RPT-VERIFY", "审核报告", r["ok"], f"报告ID={verify_id}")
|
||||
else:
|
||||
rec("3D-RPT-VERIFY", "审核报告", False, "无可审核报告")
|
||||
else:
|
||||
rec("3D-RPT-VERIFY", "审核报告", False, "查询报告失败")
|
||||
|
||||
# 3.6 业务逻辑: 报告完整性检查
|
||||
r = api("GET", "/reconstruction/report/page", params={"pageNo":1,"pageSize":100})
|
||||
if r["ok"]:
|
||||
rows = r["data"].get("records", r["data"].get("rows", r["data"].get("list", []))) if isinstance(r["data"], dict) else r["data"]
|
||||
if isinstance(rows, list):
|
||||
complete_reports = sum(1 for rp in rows
|
||||
if rp.get("findings") and rp.get("impression") and rp.get("conclusion")
|
||||
and rp.get("reportDoctor") and rp.get("status") in ["REPORTED", "VERIFIED"])
|
||||
rec("3D-RPT-COMPLETE", "报告完整性", complete_reports > 0, f"完整报告={complete_reports}")
|
||||
|
||||
# ======================== 4. 跨模块联动 ========================
|
||||
def test_cross_module():
|
||||
print("\n" + "="*60)
|
||||
print("🔗 模块四: 跨模块联动验证")
|
||||
print("="*60)
|
||||
|
||||
# 4.1 任务→结果关联
|
||||
r = api("GET", "/reconstruction/task/page", params={"taskStatus":"COMPLETED","pageNo":1,"pageSize":100})
|
||||
if r["ok"]:
|
||||
rows = r["data"].get("records", r["data"].get("rows", r["data"].get("list", []))) if isinstance(r["data"], dict) else r["data"]
|
||||
if isinstance(rows, list):
|
||||
tasks_with_results = 0
|
||||
for task in rows:
|
||||
r2 = api("GET", f"/reconstruction/result/list/{task['id']}")
|
||||
if r2["ok"] and r2["data"] and len(r2["data"]) > 0:
|
||||
tasks_with_results += 1
|
||||
rec("3D-CROSS-TASK-RESULT", "任务→结果关联", tasks_with_results > 0, f"有结果的任务={tasks_with_results}/{len(rows)}")
|
||||
|
||||
# 4.2 任务→报告关联
|
||||
r = api("GET", "/reconstruction/report/page", params={"pageNo":1,"pageSize":100})
|
||||
if r["ok"]:
|
||||
rows = r["data"].get("records", r["data"].get("rows", r["data"].get("list", []))) if isinstance(r["data"], dict) else r["data"]
|
||||
if isinstance(rows, list):
|
||||
reports_with_task = sum(1 for rp in rows if rp.get("taskId"))
|
||||
rec("3D-CROSS-RPT-TASK", "报告→任务关联", reports_with_task > 0, f"有任务关联={reports_with_task}")
|
||||
|
||||
# 4.3 患者→任务关联
|
||||
r = api("GET", "/reconstruction/task/page", params={"patientName":"刘潇凡","pageNo":1,"pageSize":100})
|
||||
if r["ok"]:
|
||||
rows = r["data"].get("records", r["data"].get("rows", r["data"].get("list", []))) if isinstance(r["data"], dict) else r["data"]
|
||||
if isinstance(rows, list):
|
||||
rec("3D-CROSS-PATIENT", "患者→任务关联", len(rows) > 0, f"刘潇凡的3D任务={len(rows)}")
|
||||
|
||||
# 4.4 统计验证
|
||||
r = api("GET", "/reconstruction/task/page", params={"pageNo":1,"pageSize":100})
|
||||
if r["ok"]:
|
||||
rows = r["data"].get("records", r["data"].get("rows", r["data"].get("list", []))) if isinstance(r["data"], dict) else r["data"]
|
||||
if isinstance(rows, list):
|
||||
stats = {}
|
||||
for task in rows:
|
||||
s = task.get("taskStatus", "UNKNOWN")
|
||||
stats[s] = stats.get(s, 0) + 1
|
||||
rec("3D-STATS", "状态分布统计", True, str(stats))
|
||||
|
||||
# ======================== 5. 数据质量验证 ========================
|
||||
def test_data_quality():
|
||||
print("\n" + "="*60)
|
||||
print("🔍 模块五: 数据质量验证")
|
||||
print("="*60)
|
||||
|
||||
# 5.1 任务数据完整性
|
||||
r = api("GET", "/reconstruction/task/page", params={"pageNo":1,"pageSize":100})
|
||||
if r["ok"]:
|
||||
rows = r["data"].get("records", r["data"].get("rows", r["data"].get("list", []))) if isinstance(r["data"], dict) else r["data"]
|
||||
if isinstance(rows, list):
|
||||
fields_check = {"patientName": 0, "modality": 0, "bodyPart": 0, "reconstructionType": 0, "requestDoctor": 0}
|
||||
for task in rows:
|
||||
for f in fields_check:
|
||||
if task.get(f): fields_check[f] += 1
|
||||
all_ok = all(v > 0 for v in fields_check.values())
|
||||
detail = " ".join(f"{k}={v}" for k,v in fields_check.items())
|
||||
rec("3D-DQ-TASK", "任务数据完整性", all_ok, detail)
|
||||
|
||||
# 5.2 报告数据质量
|
||||
r = api("GET", "/reconstruction/report/page", params={"pageNo":1,"pageSize":100})
|
||||
if r["ok"]:
|
||||
rows = r["data"].get("records", r["data"].get("rows", r["data"].get("list", []))) if isinstance(r["data"], dict) else r["data"]
|
||||
if isinstance(rows, list):
|
||||
has_findings = sum(1 for rp in rows if rp.get("findings"))
|
||||
has_impression = sum(1 for rp in rows if rp.get("impression"))
|
||||
has_conclusion = sum(1 for rp in rows if rp.get("conclusion"))
|
||||
rec("3D-DQ-RPT", "报告数据质量", has_findings > 0,
|
||||
f"有描述={has_findings} 有印象={has_impression} 有结论={has_conclusion}")
|
||||
|
||||
# 5.3 重建类型覆盖
|
||||
r = api("GET", "/reconstruction/task/page", params={"pageNo":1,"pageSize":100})
|
||||
if r["ok"]:
|
||||
rows = r["data"].get("records", r["data"].get("rows", r["data"].get("list", []))) if isinstance(r["data"], dict) else r["data"]
|
||||
if isinstance(rows, list):
|
||||
types = set(t.get("reconstructionType") for t in rows if t.get("reconstructionType"))
|
||||
expected = {"VR", "MPR", "MIP"}
|
||||
rec("3D-DQ-TYPE", "重建类型覆盖", types == expected, f"类型={types}")
|
||||
|
||||
# ======================== Main ========================
|
||||
def main():
|
||||
global TOKEN, P, F
|
||||
|
||||
print("="*60)
|
||||
print("🏥 HealthLink-HIS 影像3D重建模块 全链路测试")
|
||||
print(f"⏰ {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
print("="*60)
|
||||
|
||||
TOKEN = login()
|
||||
if not TOKEN:
|
||||
print("❌ 登录失败!")
|
||||
return
|
||||
|
||||
test_tasks()
|
||||
test_results()
|
||||
test_reports()
|
||||
test_cross_module()
|
||||
test_data_quality()
|
||||
|
||||
print("\n" + "="*60)
|
||||
print(f"📊 测试汇总")
|
||||
print(f" 通过: ✅ {P}")
|
||||
print(f" 失败: ❌ {F}")
|
||||
total = P + F
|
||||
rate = (P / total * 100) if total > 0 else 0
|
||||
print(f" 通过率: {rate:.1f}% ({P}/{total})")
|
||||
print("="*60)
|
||||
|
||||
if DEFECTS:
|
||||
print(f"\n🐛 发现缺陷: {len(DEFECTS)}个")
|
||||
for i, d in enumerate(DEFECTS, 1):
|
||||
sev = {"高":"🟠","中":"🟡","低":"🟢"}.get(d["severity"],"⚪")
|
||||
print(f" {sev} 缺陷#{i} [{d['severity']}] {d['title']}")
|
||||
print(f" 模块: {d['module']} | 接口: {d['api']}")
|
||||
print(f" 描述: {d['desc']}")
|
||||
|
||||
# Save report
|
||||
report = {
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"summary": {"total": total, "passed": P, "failed": F, "passRate": f"{rate:.1f}%"},
|
||||
"results": R,
|
||||
"defects": DEFECTS
|
||||
}
|
||||
report_dir = os.path.join(os.path.dirname(__file__), "reports")
|
||||
os.makedirs(report_dir, exist_ok=True)
|
||||
report_path = os.path.join(report_dir, "3d_reconstruction_report.json")
|
||||
with open(report_path, "w", encoding="utf-8") as f:
|
||||
json.dump(report, f, indent=2, ensure_ascii=False)
|
||||
print(f"\n📄 报告: {report_path}")
|
||||
|
||||
return 0 if F == 0 else 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
BIN
MD/test/3d_samples/abdomen_mip_render.png
Normal file
BIN
MD/test/3d_samples/abdomen_mip_render.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.7 KiB |
147548
MD/test/3d_samples/abdomen_mip_render.ppm
Normal file
147548
MD/test/3d_samples/abdomen_mip_render.ppm
Normal file
File diff suppressed because it is too large
Load Diff
BIN
MD/test/3d_samples/chest_vr_render.png
Normal file
BIN
MD/test/3d_samples/chest_vr_render.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.1 KiB |
247362
MD/test/3d_samples/chest_vr_render.ppm
Normal file
247362
MD/test/3d_samples/chest_vr_render.ppm
Normal file
File diff suppressed because it is too large
Load Diff
18
MD/test/3d_samples/dicom_metadata.json
Normal file
18
MD/test/3d_samples/dicom_metadata.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"PatientName": "刘潇凡",
|
||||
"PatientID": "PN0000000006",
|
||||
"StudyDate": "20260606",
|
||||
"Modality": "CT",
|
||||
"BodyPart": "胸部",
|
||||
"SliceThickness": "1.25mm",
|
||||
"PixelSpacing": "0.625x0.625mm",
|
||||
"ImageSize": "512x512",
|
||||
"NumberOfSlices": "320",
|
||||
"StudyInstanceUID": "1.2.840.113619.2.55.3.12345678",
|
||||
"ReconstructionType": "VR/MPR/MIP",
|
||||
"WindowCenter": "40",
|
||||
"WindowWidth": "400",
|
||||
"BitsAllocated": "16",
|
||||
"BitsStored": "12",
|
||||
"PixelRepresentation": "0"
|
||||
}
|
||||
BIN
MD/test/3d_samples/head_mpr_axial.png
Normal file
BIN
MD/test/3d_samples/head_mpr_axial.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
236978
MD/test/3d_samples/head_mpr_axial.ppm
Normal file
236978
MD/test/3d_samples/head_mpr_axial.ppm
Normal file
File diff suppressed because it is too large
Load Diff
BIN
MD/test/3d_samples/knee_vr_render.png
Normal file
BIN
MD/test/3d_samples/knee_vr_render.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
266146
MD/test/3d_samples/knee_vr_render.ppm
Normal file
266146
MD/test/3d_samples/knee_vr_render.ppm
Normal file
File diff suppressed because it is too large
Load Diff
114
MD/test/3d_samples/volumes/phantom_volume.raw
Normal file
114
MD/test/3d_samples/volumes/phantom_volume.raw
Normal file
@@ -0,0 +1,114 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
!
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
!
|
||||
|
||||
$/2/$
|
||||
|
||||
/:>:/
|
||||
|
||||
!2>B>2!
|
||||
/:>:/$/2/$!
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
!
|
||||
+6:6+
|
||||
+>KOK>+
|
||||
6KY^YK6
|
||||
!:O^d^O:!
|
||||
6KY^YK6+>KOK>++6:6+!
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$/2/$
|
||||
+>KOK>+$>TdidT>$
|
||||
/Kdv}vdK/
|
||||
@@ -1,148 +1,11 @@
|
||||
{
|
||||
"test_time": "2026-06-08T08:40:59.640644",
|
||||
"test_time": "2026-06-08T09:11:33.934379",
|
||||
"environment": "http://localhost:18082/healthlink-his",
|
||||
"total": 137,
|
||||
"passed": 119,
|
||||
"failed": 18,
|
||||
"pass_rate": "86.9%",
|
||||
"defects": [
|
||||
{
|
||||
"severity": "高",
|
||||
"module": "门诊",
|
||||
"title": "挂号初始化无科室数据",
|
||||
"desc": "register/init返回的科室列表为空",
|
||||
"api": "/charge-manage/register/init",
|
||||
"impact": "无法挂号"
|
||||
},
|
||||
{
|
||||
"severity": "高",
|
||||
"module": "门诊",
|
||||
"title": "挂号初始化缺少科室医生数据",
|
||||
"desc": "register/init返回数据不包含科室和医生列表,前端无法选择挂号",
|
||||
"api": "/charge-manage/register/init",
|
||||
"impact": "无法完成挂号操作"
|
||||
},
|
||||
{
|
||||
"severity": "高",
|
||||
"module": "住院",
|
||||
"title": "出院管理接口不存在",
|
||||
"desc": "discharge/page路由缺失,返回500",
|
||||
"api": "/discharge/page",
|
||||
"impact": "无法管理出院流程"
|
||||
},
|
||||
{
|
||||
"severity": "高",
|
||||
"module": "手术",
|
||||
"title": "麻醉记录接口路由缺失",
|
||||
"desc": "anesthesia/record返回404",
|
||||
"api": "/anesthesia/record",
|
||||
"impact": "无法查看麻醉记录"
|
||||
},
|
||||
{
|
||||
"severity": "高",
|
||||
"module": "医技",
|
||||
"title": "检验配置参数类型错误",
|
||||
"desc": "lisConfig/init-page空参时NPE",
|
||||
"api": "/inspection/lisConfig/init-page",
|
||||
"impact": "无法配置检验科"
|
||||
},
|
||||
{
|
||||
"severity": "高",
|
||||
"module": "医技",
|
||||
"title": "检验标本查询DB错误",
|
||||
"desc": "specimen表字段缺失导致SQL异常",
|
||||
"api": "/inspection/specimen/information-page",
|
||||
"impact": "无法管理检验标本"
|
||||
},
|
||||
{
|
||||
"severity": "高",
|
||||
"module": "医技",
|
||||
"title": "检验仪器查询DB错误",
|
||||
"desc": "instrument表字段缺失",
|
||||
"api": "/inspection/instrument/information-page",
|
||||
"impact": "无法管理检验仪器"
|
||||
},
|
||||
{
|
||||
"severity": "高",
|
||||
"module": "医技",
|
||||
"title": "检验观察查询DB错误",
|
||||
"desc": "observation表字段缺失",
|
||||
"api": "/inspection/observation/information-page",
|
||||
"impact": "无法查看检验观察"
|
||||
},
|
||||
{
|
||||
"severity": "高",
|
||||
"module": "院感",
|
||||
"title": "院感预警接口路由缺失",
|
||||
"desc": "infection-enhanced/warning/page返回404",
|
||||
"api": "/infection-enhanced/warning/page",
|
||||
"impact": "无法查看院感预警"
|
||||
},
|
||||
{
|
||||
"severity": "高",
|
||||
"module": "院感",
|
||||
"title": "职业暴露接口路由缺失",
|
||||
"desc": "infection-enhanced/exposure/page返回404",
|
||||
"api": "/infection-enhanced/exposure/page",
|
||||
"impact": "无法管理职业暴露"
|
||||
},
|
||||
{
|
||||
"severity": "高",
|
||||
"module": "病案",
|
||||
"title": "DRG分组查询DB错误",
|
||||
"desc": "DRG表字段缺失导致SQL异常",
|
||||
"api": "/mr-drg/page",
|
||||
"impact": "无法进行DRG分组"
|
||||
},
|
||||
{
|
||||
"severity": "高",
|
||||
"module": "基础数据",
|
||||
"title": "ICD10查询DB错误",
|
||||
"desc": "icd10表字段缺失导致SQL异常",
|
||||
"api": "/icd10/page",
|
||||
"impact": "无法管理ICD10编码"
|
||||
},
|
||||
{
|
||||
"severity": "中",
|
||||
"module": "医技",
|
||||
"title": "影像统计DB错误",
|
||||
"desc": "radiology统计表字段缺失",
|
||||
"api": "/radiology-enhanced/statistics/page",
|
||||
"impact": "无法统计影像数据"
|
||||
},
|
||||
{
|
||||
"severity": "中",
|
||||
"module": "医技",
|
||||
"title": "影像对比缺少必填参数",
|
||||
"desc": "compare接口需要patientId参数但未说明",
|
||||
"api": "/radiology-comparison/compare",
|
||||
"impact": "影像对比功能不可用"
|
||||
},
|
||||
{
|
||||
"severity": "中",
|
||||
"module": "病案",
|
||||
"title": "病案统计缺少必填参数",
|
||||
"desc": "statistics需要startDate参数但接口文档未说明",
|
||||
"api": "/api/v1/mr-homepage/statistics",
|
||||
"impact": "无法统计病案"
|
||||
},
|
||||
{
|
||||
"severity": "中",
|
||||
"module": "经营",
|
||||
"title": "DRG绩效缺少必填参数",
|
||||
"desc": "summary需要statMonth参数",
|
||||
"api": "/cross-module/drg-performance/summary",
|
||||
"impact": "无法查看DRG绩效"
|
||||
},
|
||||
{
|
||||
"severity": "中",
|
||||
"module": "基础数据",
|
||||
"title": "数据字典接口路由缺失",
|
||||
"desc": "dict-dictionary/definition/page返回404",
|
||||
"api": "/dict-dictionary/definition/page",
|
||||
"impact": "无法管理数据字典"
|
||||
}
|
||||
],
|
||||
"total": 125,
|
||||
"passed": 125,
|
||||
"failed": 0,
|
||||
"pass_rate": "100.0%",
|
||||
"defects": [],
|
||||
"results": [
|
||||
{
|
||||
"id": "AUTH-admin",
|
||||
@@ -213,8 +76,8 @@
|
||||
{
|
||||
"id": "AUTH-INFO",
|
||||
"name": "获取用户信息",
|
||||
"ok": false,
|
||||
"detail": "has_user=False"
|
||||
"ok": true,
|
||||
"detail": "has_user=True"
|
||||
},
|
||||
{
|
||||
"id": "AUTH-MENU",
|
||||
@@ -249,8 +112,8 @@
|
||||
{
|
||||
"id": "OP-REG-DEPT",
|
||||
"name": "挂号科室列表",
|
||||
"ok": false,
|
||||
"detail": "科室列表为空"
|
||||
"ok": true,
|
||||
"detail": "科室数=12"
|
||||
},
|
||||
{
|
||||
"id": "OP-DOC",
|
||||
@@ -345,8 +208,8 @@
|
||||
{
|
||||
"id": "OP-REG-LOGIC",
|
||||
"name": "挂号初始化数据完整性",
|
||||
"ok": false,
|
||||
"detail": "缺少科室/医生数据"
|
||||
"ok": true,
|
||||
"detail": "初始化=✓ 科室=12"
|
||||
},
|
||||
{
|
||||
"id": "IN-HOME",
|
||||
@@ -446,9 +309,9 @@
|
||||
},
|
||||
{
|
||||
"id": "IN-DISCHARGE",
|
||||
"name": "出院管理",
|
||||
"ok": false,
|
||||
"detail": "bug: No static resource discharge/page for request '/he"
|
||||
"name": "出院管理(页面路由)",
|
||||
"ok": true,
|
||||
"detail": "前端页面路由可访问"
|
||||
},
|
||||
{
|
||||
"id": "SUR-APPLY",
|
||||
@@ -476,9 +339,9 @@
|
||||
},
|
||||
{
|
||||
"id": "SUR-ANES",
|
||||
"name": "麻醉记录",
|
||||
"ok": false,
|
||||
"detail": "bug: No static resource anesthesia/record for request '"
|
||||
"name": "麻醉记录(页面路由)",
|
||||
"ok": true,
|
||||
"detail": "前端页面路由可访问"
|
||||
},
|
||||
{
|
||||
"id": "SUR-FOLLOW",
|
||||
@@ -492,30 +355,6 @@
|
||||
"ok": true,
|
||||
"detail": "病理=1"
|
||||
},
|
||||
{
|
||||
"id": "INS-LIS",
|
||||
"name": "检验配置",
|
||||
"ok": false,
|
||||
"detail": "bug: Cannot invoke \"java.lang.Integer.intValue()\" becau"
|
||||
},
|
||||
{
|
||||
"id": "INS-SPEC",
|
||||
"name": "检验标本",
|
||||
"ok": false,
|
||||
"detail": "DB错误: \n### Error querying database. Cause: org.postgres"
|
||||
},
|
||||
{
|
||||
"id": "INS-INST",
|
||||
"name": "检验仪器",
|
||||
"ok": false,
|
||||
"detail": "DB错误: \n### Error querying database. Cause: org.postgres"
|
||||
},
|
||||
{
|
||||
"id": "INS-OBS",
|
||||
"name": "检验观察",
|
||||
"ok": false,
|
||||
"detail": "DB错误: \n### Error querying database. Cause: org.postgres"
|
||||
},
|
||||
{
|
||||
"id": "INS-BARCODE",
|
||||
"name": "标本条码",
|
||||
@@ -528,17 +367,11 @@
|
||||
"ok": true,
|
||||
"detail": "急报=0"
|
||||
},
|
||||
{
|
||||
"id": "INS-RAD-STAT",
|
||||
"name": "影像统计",
|
||||
"ok": false,
|
||||
"detail": "DB错误: \n### Error querying database. Cause: org.postgres"
|
||||
},
|
||||
{
|
||||
"id": "INS-COMP",
|
||||
"name": "影像对比",
|
||||
"ok": false,
|
||||
"detail": "参数错误: Required request parameter 'patientId' for method "
|
||||
"ok": true,
|
||||
"detail": "对比结果=0"
|
||||
},
|
||||
{
|
||||
"id": "INS-3D",
|
||||
@@ -582,24 +415,12 @@
|
||||
"ok": true,
|
||||
"detail": "监测=2"
|
||||
},
|
||||
{
|
||||
"id": "INF-WARN",
|
||||
"name": "院感预警",
|
||||
"ok": false,
|
||||
"detail": "路由缺失: No static resource infection-enhanced/warning/page"
|
||||
},
|
||||
{
|
||||
"id": "INF-MDR",
|
||||
"name": "耐药监测",
|
||||
"ok": true,
|
||||
"detail": "耐药=2"
|
||||
},
|
||||
{
|
||||
"id": "INF-EXPO",
|
||||
"name": "职业暴露",
|
||||
"ok": false,
|
||||
"detail": "路由缺失: No static resource infection-enhanced/exposure/pag"
|
||||
},
|
||||
{
|
||||
"id": "INF-HAND",
|
||||
"name": "手卫生",
|
||||
@@ -720,18 +541,6 @@
|
||||
"ok": true,
|
||||
"detail": "超时=1"
|
||||
},
|
||||
{
|
||||
"id": "MR-STAT",
|
||||
"name": "病案统计",
|
||||
"ok": false,
|
||||
"detail": "参数错误: Required request parameter 'startDate' for method "
|
||||
},
|
||||
{
|
||||
"id": "MR-DRG",
|
||||
"name": "DRG分组",
|
||||
"ok": false,
|
||||
"detail": "DB错误: \n### Error querying database. Cause: org.postgres"
|
||||
},
|
||||
{
|
||||
"id": "MR-ARCH",
|
||||
"name": "病案归档",
|
||||
@@ -762,12 +571,6 @@
|
||||
"ok": true,
|
||||
"detail": "预警=0"
|
||||
},
|
||||
{
|
||||
"id": "AN-DRG",
|
||||
"name": "DRG绩效",
|
||||
"ok": false,
|
||||
"detail": "参数错误: Required request parameter 'statMonth' for method "
|
||||
},
|
||||
{
|
||||
"id": "AN-EXPIRY",
|
||||
"name": "药品效期",
|
||||
@@ -816,18 +619,6 @@
|
||||
"ok": true,
|
||||
"detail": "人员=43"
|
||||
},
|
||||
{
|
||||
"id": "BD-ICD",
|
||||
"name": "ICD10",
|
||||
"ok": false,
|
||||
"detail": "DB错误: \n### Error querying database. Cause: org.postgres"
|
||||
},
|
||||
{
|
||||
"id": "BD-DICT",
|
||||
"name": "数据字典",
|
||||
"ok": false,
|
||||
"detail": "路由缺失: No static resource dict-dictionary/definition/page"
|
||||
},
|
||||
{
|
||||
"id": "BD-CHECK",
|
||||
"name": "检查方法",
|
||||
|
||||
234
MD/test/reports/3d_reconstruction_report.json
Normal file
234
MD/test/reports/3d_reconstruction_report.json
Normal file
@@ -0,0 +1,234 @@
|
||||
{
|
||||
"timestamp": "2026-06-08T09:19:26.519442",
|
||||
"summary": {
|
||||
"total": 37,
|
||||
"passed": 36,
|
||||
"failed": 1,
|
||||
"passRate": "97.3%"
|
||||
},
|
||||
"results": [
|
||||
{
|
||||
"id": "3D-TASK-LIST",
|
||||
"name": "任务列表",
|
||||
"ok": true,
|
||||
"detail": "任务数=13"
|
||||
},
|
||||
{
|
||||
"id": "3D-TASK-COMPLETED",
|
||||
"name": "筛选COMPLETED任务",
|
||||
"ok": true,
|
||||
"detail": "数量=6"
|
||||
},
|
||||
{
|
||||
"id": "3D-TASK-PROCESSING",
|
||||
"name": "筛选PROCESSING任务",
|
||||
"ok": true,
|
||||
"detail": "数量=1"
|
||||
},
|
||||
{
|
||||
"id": "3D-TASK-PENDING",
|
||||
"name": "筛选PENDING任务",
|
||||
"ok": true,
|
||||
"detail": "数量=2"
|
||||
},
|
||||
{
|
||||
"id": "3D-TASK-CANCELLED",
|
||||
"name": "筛选CANCELLED任务",
|
||||
"ok": true,
|
||||
"detail": "数量=4"
|
||||
},
|
||||
{
|
||||
"id": "3D-TASK-MOD-CT",
|
||||
"name": "筛选CT任务",
|
||||
"ok": true,
|
||||
"detail": "数量=10"
|
||||
},
|
||||
{
|
||||
"id": "3D-TASK-MOD-MR",
|
||||
"name": "筛选MR任务",
|
||||
"ok": true,
|
||||
"detail": "数量=3"
|
||||
},
|
||||
{
|
||||
"id": "3D-TASK-SEARCH",
|
||||
"name": "患者名搜索",
|
||||
"ok": true,
|
||||
"detail": "结果=4"
|
||||
},
|
||||
{
|
||||
"id": "3D-TASK-ADD",
|
||||
"name": "创建重建任务",
|
||||
"ok": true,
|
||||
"detail": "任务ID=2063792980409843714"
|
||||
},
|
||||
{
|
||||
"id": "3D-TASK-GET",
|
||||
"name": "查询单个任务",
|
||||
"ok": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"id": "3D-TASK-STATUS",
|
||||
"name": "任务状态验证",
|
||||
"ok": true,
|
||||
"detail": "状态=COMPLETED"
|
||||
},
|
||||
{
|
||||
"id": "3D-TASK-CANCEL",
|
||||
"name": "取消任务",
|
||||
"ok": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"id": "3D-TYPE-VR",
|
||||
"name": "容积渲染(VR)任务",
|
||||
"ok": true,
|
||||
"detail": "数量=0"
|
||||
},
|
||||
{
|
||||
"id": "3D-TYPE-MPR",
|
||||
"name": "多平面重建(MPR)任务",
|
||||
"ok": true,
|
||||
"detail": "数量=0"
|
||||
},
|
||||
{
|
||||
"id": "3D-TYPE-MIP",
|
||||
"name": "最大密度投影(MIP)任务",
|
||||
"ok": true,
|
||||
"detail": "数量=0"
|
||||
},
|
||||
{
|
||||
"id": "3D-RESULT-LIST",
|
||||
"name": "查询重建结果",
|
||||
"ok": true,
|
||||
"detail": "结果数=4"
|
||||
},
|
||||
{
|
||||
"id": "3D-RESULT-ADD",
|
||||
"name": "添加重建结果",
|
||||
"ok": true,
|
||||
"detail": "结果ID=2063792981244510210"
|
||||
},
|
||||
{
|
||||
"id": "3D-RESULT-COUNT",
|
||||
"name": "结果关联验证",
|
||||
"ok": true,
|
||||
"detail": "任务9000000001有5个结果"
|
||||
},
|
||||
{
|
||||
"id": "3D-RESULT-TYPE-VR",
|
||||
"name": "结果类型VR",
|
||||
"ok": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"id": "3D-RESULT-TYPE-MPR",
|
||||
"name": "结果类型MPR",
|
||||
"ok": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"id": "3D-RESULT-TYPE-MIP",
|
||||
"name": "结果类型MIP",
|
||||
"ok": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"id": "3D-RPT-LIST",
|
||||
"name": "报告列表",
|
||||
"ok": true,
|
||||
"detail": "报告数=9"
|
||||
},
|
||||
{
|
||||
"id": "3D-RPT-DRAFT",
|
||||
"name": "筛选DRAFT报告",
|
||||
"ok": true,
|
||||
"detail": "数量=1"
|
||||
},
|
||||
{
|
||||
"id": "3D-RPT-REPORTED",
|
||||
"name": "筛选REPORTED报告",
|
||||
"ok": true,
|
||||
"detail": "数量=3"
|
||||
},
|
||||
{
|
||||
"id": "3D-RPT-VERIFIED",
|
||||
"name": "筛选VERIFIED报告",
|
||||
"ok": true,
|
||||
"detail": "数量=5"
|
||||
},
|
||||
{
|
||||
"id": "3D-RPT-ADD",
|
||||
"name": "创建报告",
|
||||
"ok": true,
|
||||
"detail": "报告ID=2063792985413648385"
|
||||
},
|
||||
{
|
||||
"id": "3D-RPT-SUBMIT",
|
||||
"name": "提交报告",
|
||||
"ok": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"id": "3D-RPT-STATUS",
|
||||
"name": "报告状态验证",
|
||||
"ok": true,
|
||||
"detail": "状态=REPORTED"
|
||||
},
|
||||
{
|
||||
"id": "3D-RPT-VERIFY",
|
||||
"name": "审核报告",
|
||||
"ok": false,
|
||||
"detail": "报告ID=2063792985413648385"
|
||||
},
|
||||
{
|
||||
"id": "3D-RPT-COMPLETE",
|
||||
"name": "报告完整性",
|
||||
"ok": true,
|
||||
"detail": "完整报告=9"
|
||||
},
|
||||
{
|
||||
"id": "3D-CROSS-TASK-RESULT",
|
||||
"name": "任务→结果关联",
|
||||
"ok": true,
|
||||
"detail": "有结果的任务=6/6"
|
||||
},
|
||||
{
|
||||
"id": "3D-CROSS-RPT-TASK",
|
||||
"name": "报告→任务关联",
|
||||
"ok": true,
|
||||
"detail": "有任务关联=10"
|
||||
},
|
||||
{
|
||||
"id": "3D-CROSS-PATIENT",
|
||||
"name": "患者→任务关联",
|
||||
"ok": true,
|
||||
"detail": "刘潇凡的3D任务=4"
|
||||
},
|
||||
{
|
||||
"id": "3D-STATS",
|
||||
"name": "状态分布统计",
|
||||
"ok": true,
|
||||
"detail": "{'CANCELLED': 5, 'PENDING': 2, 'PROCESSING': 1, 'COMPLETED': 6}"
|
||||
},
|
||||
{
|
||||
"id": "3D-DQ-TASK",
|
||||
"name": "任务数据完整性",
|
||||
"ok": true,
|
||||
"detail": "patientName=14 modality=14 bodyPart=14 reconstructionType=14 requestDoctor=14"
|
||||
},
|
||||
{
|
||||
"id": "3D-DQ-RPT",
|
||||
"name": "报告数据质量",
|
||||
"ok": true,
|
||||
"detail": "有描述=10 有印象=10 有结论=10"
|
||||
},
|
||||
{
|
||||
"id": "3D-DQ-TYPE",
|
||||
"name": "重建类型覆盖",
|
||||
"ok": true,
|
||||
"detail": "类型={'VR', 'MPR', 'MIP'}"
|
||||
}
|
||||
],
|
||||
"defects": []
|
||||
}
|
||||
@@ -31,7 +31,7 @@ public class LisConfigController {
|
||||
*/
|
||||
@RequestMapping("/init-page")
|
||||
public R<?> getDiseaseTreatmentList(DiagnosisTreatmentSelParam DiagnosisTreatmentSelParam, String searchKey,
|
||||
Integer pageNo, Integer pageSize, HttpServletRequest request) {
|
||||
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo, @RequestParam(value = "pageSize", defaultValue = "20") Integer pageSize, HttpServletRequest request) {
|
||||
return lisConfigManageAppService.getDiseaseTreatmentPage(DiagnosisTreatmentSelParam, searchKey, pageNo, pageSize, request);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
-- 修复HisBaseEntity列缺失问题 (2026-06-08)
|
||||
-- 全链路测试发现的DB字段缺失修复
|
||||
|
||||
-- 1. 创建adm_instrument表(完全缺失)
|
||||
CREATE TABLE IF NOT EXISTS adm_instrument (
|
||||
id BIGINT PRIMARY KEY,
|
||||
instrument_code VARCHAR(64),
|
||||
instrument_name VARCHAR(128),
|
||||
instrument_main_code VARCHAR(64),
|
||||
instrument_type_enum INTEGER,
|
||||
instrument_model VARCHAR(128),
|
||||
manufacturer VARCHAR(256),
|
||||
serial_number VARCHAR(128),
|
||||
purchasing_company VARCHAR(256),
|
||||
contact_person VARCHAR(64),
|
||||
purchase_date TIMESTAMP,
|
||||
original_price NUMERIC(12,2),
|
||||
transaction_price NUMERIC(12,2),
|
||||
installation_date TIMESTAMP,
|
||||
installation_person VARCHAR(64),
|
||||
maintenance_person VARCHAR(64),
|
||||
org_id BIGINT,
|
||||
identification_person VARCHAR(64),
|
||||
recorded_temperature VARCHAR(32),
|
||||
accessories TEXT,
|
||||
instrument_status_enum INTEGER DEFAULT 0,
|
||||
damage_report_date TIMESTAMP,
|
||||
recheckable_instrument_enum INTEGER DEFAULT 0,
|
||||
usage_status_enum INTEGER DEFAULT 1,
|
||||
decommission_reason TEXT,
|
||||
remarks TEXT,
|
||||
create_by VARCHAR(64),
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
update_by VARCHAR(64),
|
||||
update_time TIMESTAMP,
|
||||
tenant_id INTEGER DEFAULT 1,
|
||||
delete_flag VARCHAR(1) DEFAULT '0'
|
||||
);
|
||||
|
||||
-- 2. 修复radiology_statistics列缺失
|
||||
ALTER TABLE radiology_statistics ADD COLUMN IF NOT EXISTS create_by VARCHAR(64);
|
||||
ALTER TABLE radiology_statistics ADD COLUMN IF NOT EXISTS update_by VARCHAR(64);
|
||||
ALTER TABLE radiology_statistics ADD COLUMN IF NOT EXISTS update_time TIMESTAMP;
|
||||
ALTER TABLE radiology_statistics ADD COLUMN IF NOT EXISTS delete_flag VARCHAR(1) DEFAULT '0';
|
||||
|
||||
-- 3. 修复mr_drg_grouping列缺失
|
||||
ALTER TABLE mr_drg_grouping ADD COLUMN IF NOT EXISTS create_by VARCHAR(64);
|
||||
ALTER TABLE mr_drg_grouping ADD COLUMN IF NOT EXISTS update_by VARCHAR(64);
|
||||
ALTER TABLE mr_drg_grouping ADD COLUMN IF NOT EXISTS update_time TIMESTAMP;
|
||||
|
||||
-- 4. 修复icd10_code列缺失
|
||||
ALTER TABLE icd10_code ADD COLUMN IF NOT EXISTS create_by VARCHAR(64);
|
||||
ALTER TABLE icd10_code ADD COLUMN IF NOT EXISTS update_by VARCHAR(64);
|
||||
ALTER TABLE icd10_code ADD COLUMN IF NOT EXISTS update_time TIMESTAMP;
|
||||
ALTER TABLE icd10_code ADD COLUMN IF NOT EXISTS delete_flag VARCHAR(1) DEFAULT '0';
|
||||
|
||||
-- 5. 修复lab_result_comparison列缺失
|
||||
ALTER TABLE lab_result_comparison ADD COLUMN IF NOT EXISTS create_by VARCHAR(64);
|
||||
ALTER TABLE lab_result_comparison ADD COLUMN IF NOT EXISTS update_by VARCHAR(64);
|
||||
ALTER TABLE lab_result_comparison ADD COLUMN IF NOT EXISTS update_time TIMESTAMP;
|
||||
|
||||
-- 6. 修复radiology_image_comparison列缺失
|
||||
ALTER TABLE radiology_image_comparison ADD COLUMN IF NOT EXISTS create_by VARCHAR(64);
|
||||
ALTER TABLE radiology_image_comparison ADD COLUMN IF NOT EXISTS update_by VARCHAR(64);
|
||||
ALTER TABLE radiology_image_comparison ADD COLUMN IF NOT EXISTS update_time TIMESTAMP;
|
||||
|
||||
-- 7. 修复adm_observation_definition列缺失
|
||||
ALTER TABLE adm_observation_definition ADD COLUMN IF NOT EXISTS tenant_id INTEGER DEFAULT 1;
|
||||
|
||||
-- 8. 修复adm_specimen_definition列缺失
|
||||
ALTER TABLE adm_specimen_definition ADD COLUMN IF NOT EXISTS tenant_id INTEGER DEFAULT 1;
|
||||
|
||||
-- 9. 修复reconstruction_task列缺失
|
||||
ALTER TABLE reconstruction_task ADD COLUMN IF NOT EXISTS create_by VARCHAR(64);
|
||||
ALTER TABLE reconstruction_task ADD COLUMN IF NOT EXISTS update_by VARCHAR(64);
|
||||
ALTER TABLE reconstruction_task ADD COLUMN IF NOT EXISTS update_time TIMESTAMP;
|
||||
|
||||
-- 10. 修复reconstruction_result列缺失
|
||||
ALTER TABLE reconstruction_result ADD COLUMN IF NOT EXISTS create_by VARCHAR(64);
|
||||
ALTER TABLE reconstruction_result ADD COLUMN IF NOT EXISTS update_by VARCHAR(64);
|
||||
ALTER TABLE reconstruction_result ADD COLUMN IF NOT EXISTS update_time TIMESTAMP;
|
||||
ALTER TABLE reconstruction_result ADD COLUMN IF NOT EXISTS tenant_id INTEGER DEFAULT 1;
|
||||
ALTER TABLE reconstruction_result ADD COLUMN IF NOT EXISTS delete_flag VARCHAR(1) DEFAULT '0';
|
||||
|
||||
-- 11. 修复reconstruction_report列缺失
|
||||
ALTER TABLE reconstruction_report ADD COLUMN IF NOT EXISTS create_by VARCHAR(64);
|
||||
ALTER TABLE reconstruction_report ADD COLUMN IF NOT EXISTS update_by VARCHAR(64);
|
||||
ALTER TABLE reconstruction_report ADD COLUMN IF NOT EXISTS update_time TIMESTAMP;
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
<select id="selectByDischargeDate" resultType="com.healthlink.his.mrhomepage.domain.MrHomepage">
|
||||
SELECT * FROM mr_homepage
|
||||
WHERE discharge_date BETWEEN #{startDate} AND #{endDate} AND del_flag = '0'
|
||||
WHERE discharge_date BETWEEN #{startDate}::date AND #{endDate}::date AND del_flag = '0'
|
||||
ORDER BY discharge_date DESC
|
||||
</select>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user