初始化医疗报告生成项目,添加核心代码文件

This commit is contained in:
2026-02-13 18:32:52 +08:00
commit faaf2158d4
69 changed files with 29836 additions and 0 deletions

45
.gitignore vendored Normal file
View File

@@ -0,0 +1,45 @@
# 忽略报告文件
reports/
# 忽略缓存文件
__pycache__/
# 忽略环境变量文件
.env
# 忽略日志文件
*.log
# 忽略Excel文件
*.xlsx
# 忽略PDF文件
*.pdf
# 忽略临时文件
*.tmp
*.temp
# 忽略编译文件
*.pyc
# 忽略IDE相关文件
.vscode/
.idea/
# 忽略前端依赖
frontend/node_modules/
# 忽略系统文件
Thumbs.db
.DS_Store
# 忽略调试相关文件
debug_*
# 忽略缓存文件
deepseek_cache.json
# 忽略模板文件(如果有多个模板,可以根据需要调整)
Be.U Wellness Center功能医学健康报告&定制化方案-案例.docx
Be.U+Wellness+Center功能医学健康报告&定制化方案-+Ming+Wang.docx

View File

@@ -0,0 +1,133 @@
# Requirements Document
## Introduction
医疗报告智能生成系统Medical Report Generator是一个自动化工具用于从医疗检测PDF报告中提取数据通过AI分析生成健康评估和建议并填充到专业的Word模板中生成完整的功能医学健康报告。
系统支持OCR识别、DeepSeek AI分析、模板保护区域管理、自动分页符处理等功能。
## Glossary
- **Report_Generator**: 医疗报告生成系统的核心模块
- **OCR_Service**: 百度OCR服务用于从PDF中提取文本和数据
- **DeepSeek_Service**: DeepSeek AI服务用于分析异常指标并生成健康评估内容
- **Template_Service**: Word模板处理服务负责填充数据和格式化
- **Protected_Region**: 保护区域,指模板前四页(客户健康方案之前)的内容,不应被修改
- **Health_Program_Boundary**: 保护边界,"客户健康方案/Client Health Program"在文档中的位置
- **Module_Title**: 模块标题,如"血液学检测"、"内分泌检测"等分类标题
- **ABB**: 检测项目的缩写标识符
- **Clinical_Significance**: 临床意义,对检测结果的医学解释
## Requirements
### Requirement 1: PDF数据提取
**User Story:** As a 医疗报告操作员, I want to 从PDF医疗检测报告中自动提取数据, so that 我可以快速获取检测结果而无需手动输入。
#### Acceptance Criteria
1. WHEN 用户提供PDF医疗报告文件 THEN THE OCR_Service SHALL 使用百度OCR识别并提取所有文本内容
2. WHEN PDF包含检测数据表格 THEN THE OCR_Service SHALL 提取项目名称、结果值、参考范围、单位和异常标记
3. WHEN 提取完成 THEN THE Report_Generator SHALL 将数据保存为JSON格式的缓存文件
4. IF 缓存文件已存在且未强制刷新 THEN THE Report_Generator SHALL 使用缓存数据而非重新提取
### Requirement 2: 数据匹配与映射
**User Story:** As a 系统管理员, I want to 将提取的数据与模板字段进行匹配, so that 数据可以正确填充到报告模板中。
#### Acceptance Criteria
1. WHEN 数据提取完成 THEN THE Report_Generator SHALL 根据ABB配置文件将检测项目映射到对应的模板字段
2. WHEN 检测项目有异常标记↑、↓、H、L THEN THE Report_Generator SHALL 正确识别并记录异常状态
3. WHEN 检测项目无法通过配置文件匹配 THEN THE DeepSeek_Service SHALL 分析并分类该项目所属模块
4. IF 定性结果与参考范围相同 THEN THE Report_Generator SHALL 不将其标记为异常
### Requirement 3: 保护区域管理
**User Story:** As a 报告设计师, I want to 保护模板前四页的内容不被修改, so that 公司品牌信息和固定内容保持完整。
#### Acceptance Criteria
1. WHEN 文档处理开始 THEN THE Report_Generator SHALL 动态查找"客户健康方案/Client Health Program"位置作为保护边界
2. WHILE 处理文档内容 THEN THE Report_Generator SHALL 不修改保护边界之前的任何元素
3. WHEN 文档处理完成 THEN THE Report_Generator SHALL 从原始模板复制保护区域到输出文件
4. WHEN 验证输出文件 THEN THE Report_Generator SHALL 确保保护区域的XML元素与模板逐字节匹配
5. WHEN 验证输出文件 THEN THE Report_Generator SHALL 确保保护区域的媒体文件MD5与模板完全相同
### Requirement 4: AI健康评估生成
**User Story:** As a 功能医学专家, I want to 根据异常指标自动生成健康评估内容, so that 报告包含专业的医学分析。
#### Acceptance Criteria
1. WHEN 存在异常检测指标 THEN THE DeepSeek_Service SHALL 收集所有异常项并生成"整体健康状况评估"内容
2. WHEN 生成健康评估 THEN THE DeepSeek_Service SHALL 根据异常类型自动分成合适的小节(血液学、内分泌、免疫、代谢等)
3. WHEN 生成内容 THEN THE DeepSeek_Service SHALL 先写英文内容,然后逐句翻译为中文
4. WHEN 存在异常检测指标 THEN THE DeepSeek_Service SHALL 生成"功能医学健康建议"内容
5. WHEN 生成健康建议 THEN THE DeepSeek_Service SHALL 包含营养干预、运动干预、睡眠与压力管理、生活方式调整、长期随访计划五个固定小节
### Requirement 5: 临床意义解释生成
**User Story:** As a 医疗报告阅读者, I want to 看到每个检测项目的临床意义解释, so that 我可以理解检测结果的医学含义。
#### Acceptance Criteria
1. WHEN 添加新的检测项目表格 THEN THE DeepSeek_Service SHALL 为该项目生成Clinical Significance解释
2. WHEN 生成临床意义 THEN THE DeepSeek_Service SHALL 同时提供英文和中文版本
3. WHEN 填充临床意义 THEN THE Template_Service SHALL 使用正确的字体样式英文Times New Roman 10.5pt中文宋体12pt
### Requirement 6: 文档格式化与分页
**User Story:** As a 报告阅读者, I want to 报告具有清晰的分页和格式, so that 内容易于阅读和打印。
#### Acceptance Criteria
1. WHEN 处理保护区域之后的图片 THEN THE Report_Generator SHALL 在每个图片前插入分页符
2. WHEN 处理模块标题 THEN THE Report_Generator SHALL 在模块标题前插入分页符(第一个模块除外)
3. WHEN 识别模块标题 THEN THE Report_Generator SHALL 排除长度超过50字符的文本
4. WHEN 识别模块标题 THEN THE Report_Generator SHALL 排除以"因此"、"所以"、"综上"开头的描述性文字
5. WHEN 识别模块标题 THEN THE Report_Generator SHALL 排除包含句号、逗号等标点的长句子
### Requirement 7: 表格创建与填充
**User Story:** As a 报告生成系统, I want to 为缺失的检测项目创建标准格式的表格, so that 所有检测数据都能正确显示。
#### Acceptance Criteria
1. WHEN 检测项目在模板中没有对应位置 THEN THE Template_Service SHALL 在对应模块内创建新表格
2. WHEN 创建表格 THEN THE Template_Service SHALL 包含ABB、项目名、结果、指标、参考范围、单位列
3. WHEN 创建表格 THEN THE Template_Service SHALL 包含Clinical Significance合并行
4. WHEN 设置表格边框 THEN THE Template_Service SHALL 使用顶部实线、其他虚线的样式
### Requirement 8: 空行清理与表格合并
**User Story:** As a 报告质量控制员, I want to 清理空白数据行并合并表格, so that 报告整洁无冗余。
#### Acceptance Criteria
1. WHEN 数据行的Result列为空 THEN THE Report_Generator SHALL 删除该空数据行
2. WHEN 表头下只有描述没有数据 THEN THE Report_Generator SHALL 删除描述并将下方数据表格内容移上来
3. WHILE 清理空行 THEN THE Report_Generator SHALL 不影响保护区域的内容
### Requirement 9: 报告输出与验证
**User Story:** As a 系统操作员, I want to 生成并验证最终报告, so that 我可以确保报告质量。
#### Acceptance Criteria
1. WHEN 报告生成完成 THEN THE Report_Generator SHALL 保存为带时间戳的DOCX文件
2. WHEN 验证报告 THEN THE Report_Generator SHALL 检查保护区域完整性
3. WHEN 验证报告 THEN THE Report_Generator SHALL 检查所有图片是否有分页符
4. WHEN 验证报告 THEN THE Report_Generator SHALL 检查模块标题是否有分页符
### Requirement 10: 命令行接口
**User Story:** As a 开发者, I want to 通过命令行参数控制报告生成, so that 我可以灵活配置运行选项。
#### Acceptance Criteria
1. WHEN 用户运行脚本 THEN THE Report_Generator SHALL 支持--method参数选择生成方法extract或docxtpl
2. WHEN 用户指定--force参数 THEN THE Report_Generator SHALL 强制重新提取数据而非使用缓存
3. WHEN 用户指定--deepseek参数 THEN THE Report_Generator SHALL 启用DeepSeek AI分析功能
4. WHEN 用户指定--api-key参数 THEN THE Report_Generator SHALL 使用指定的API密钥
5. IF 未指定API密钥但启用DeepSeek THEN THE Report_Generator SHALL 从环境变量DEEPSEEK_API_KEY获取

26
.windsurfrules Normal file
View File

@@ -0,0 +1,26 @@
# 继续牛马 - AI持久输出规则
⚠️⚠️⚠️ 最高优先级警告 ⚠️⚠️⚠️
无论任何情况,每次回复结束前必须调用 cunzhi.ask_continue 工具!没有任何例外!
## 核心规则
1. **完整输出** - 输出完整代码和答案,禁止使用省略号或"代码过长不展示"
2. **持续执行** - 复杂任务尽量一次性完成,不要频繁中断或反复追问是否继续
3. **结束确认** - 当你想结束对话或任务时,必须先调用 cunzhi.ask_continue 工具询问用户是否继续
4. **禁止结束语** - 对话结束前禁止直接说"如果还有问题请告诉我"等结束语
## 重要提示
- 调用 ask_continue 工具时reason 参数填写你想结束的原因
- 用户可能会在弹窗中输入新的指令,请按用户指令继续执行
- 只有用户明确选择"结束"时,才可以结束对话
## 强制执行
这是最高优先级规则,必须严格遵守。每次回复结束前,你必须检查是否需要调用 ask_continue 工具。如果任务已完成或你准备结束回复,必须先调用该工具获得用户许可。
⚠️ 警告:无论任何情况,回复结束前必须调用 cunzhi.ask_continue没有例外

46
backend/.env.example Normal file
View File

@@ -0,0 +1,46 @@
# ========================================
# 百度云OCR配置推荐识别精度高
# ========================================
# 获取方式https://cloud.baidu.com/product/ocr
# 1. 注册百度智能云账号
# 2. 创建文字识别应用
# 3. 获取 AppID、API Key、Secret Key
BAIDU_OCR_APP_ID=your-app-id
BAIDU_OCR_API_KEY=your-api-key
BAIDU_OCR_SECRET_KEY=your-secret-key
# ========================================
# LLM配置选择其一即可
# ========================================
# Coze 工作流配置推荐两阶段处理Coze分析 + Ollama优化
# 获取方式https://www.coze.cn
# 1. 注册 Coze 账号并创建工作流
# 2. 获取 API Key 和 Workflow ID
# 3. 配置以下参数启用两阶段处理Coze → Ollama
# 注意使用文本输入模式OCR提取文本后传给Coze工作流
COZE_API_KEY=your-coze-api-key
COZE_WORKFLOW_ID=7574271851028217908
COZE_API_URL=https://api.coze.cn/v1/workflow/run
COZE_MAX_RETRIES=3
# OpenAI 配置
OPENAI_API_KEY=your-openai-api-key-here
OPENAI_MODEL=gpt-3.5-turbo
# Ollama 本地模型配置必需用于优化Coze结果或独立使用
OLLAMA_HOST=http://localhost:11434
OLLAMA_MODEL=qwen2.5:7b
# ========================================
# 说明
# ========================================
# 1. 复制此文件为 .env 并填入实际配置
# 2. OCR引擎MinerU高精度文档解析
# 3. LLM处理流程
# - 如果配置 Coze两阶段处理Coze分析 + Ollama优化
# - 如果只配置 Ollama单阶段处理直接使用Ollama
# - 优先级OpenAI > Coze > Ollama > 模拟模式
# 4. 批量报告功能:
# - 上传多个PDF → OCR提取文本 → LLM综合分析 → 生成Be.U风格PDF报告
# - 使用xhtml2pdf生成PDF无需额外依赖

23
backend/_check_report.py Normal file
View File

@@ -0,0 +1,23 @@
from docx import Document
report_path = r'C:\Users\UI\Desktop\医疗报告\backend\reports\filled_report_20260212_154247.docx'
doc = Document(report_path)
body = doc.element.body
children = list(body)
keywords = ['overall health', '整体健康', 'medical intervention', '医学干预',
'functional medical health', '功能医学健康建议',
'nutrition intervention', '营养干预', 'exercise intervention', '运动干预',
'sleep', '睡眠', 'lifestyle', '生活方式', 'long-term', '长期随访',
'功能医学检测档案', 'abnormal index', '异常指标']
print(f"文档总元素数: {len(children)}")
print("=" * 80)
for i, elem in enumerate(children):
text = ''.join(elem.itertext()).strip()
if text:
text_lower = text.lower()
if any(kw in text_lower for kw in keywords):
tag = elem.tag.split("}")[-1]
print(f'[{i}] {tag}: {text[:150]}')

View File

@@ -0,0 +1,529 @@
{
"description": "基于2.pdf提取的标准检测项目ABB映射配置 - 按PDF页面顺序排列",
"version": "2.0",
"last_updated": "2026-01-09",
"total_modules": 24,
"modules": {
"Urine Test": {
"cn_name": "尿液检测",
"pages": "16-19",
"order": 1,
"items": [
{"abb": "Color", "project": "Color", "project_cn": "颜色"},
{"abb": "pH", "project": "pH", "project_cn": "酸碱度"},
{"abb": "TuR", "project": "Turbidity", "project_cn": "浊度"},
{"abb": "PRO", "project": "Protein", "project_cn": "蛋白质"},
{"abb": "BLD", "project": "Occult Blood", "project_cn": "隐血或红细胞"},
{"abb": "GLU", "project": "Glucose", "project_cn": "糖"},
{"abb": "SG", "project": "Specific Gravity", "project_cn": "比重"},
{"abb": "LEU", "project": "Leucocyte", "project_cn": "白细胞"},
{"abb": "NIT", "project": "Nitrite", "project_cn": "亚硝酸盐"},
{"abb": "KET", "project": "Ketone", "project_cn": "酮体"}
]
},
"Complete Blood Count": {
"cn_name": "血常规",
"pages": "20-26",
"order": 2,
"items": [
{"abb": "RBC", "project": "RBC Count", "project_cn": "红细胞计数"},
{"abb": "Hb", "project": "Hemoglobin", "project_cn": "血红蛋白"},
{"abb": "HCT", "project": "Hematocrit", "project_cn": "红细胞压积"},
{"abb": "MCV", "project": "Mean Corpuscular Volume", "project_cn": "平均红细胞体积"},
{"abb": "MCH", "project": "Mean Corpuscular Hemoglobin", "project_cn": "平均红细胞血红蛋白含量"},
{"abb": "MCHC", "project": "Mean Corpuscular Hemoglobin Concentration", "project_cn": "平均红细胞血红蛋白浓度"},
{"abb": "RDW", "project": "Red Cell Distribution Width", "project_cn": "红细胞体积分布宽度"},
{"abb": "RBC Morphology", "project": "RBC Morphology", "project_cn": "红细胞形态"},
{"abb": "WBC count", "project": "WBC Count", "project_cn": "白细胞总数"},
{"abb": "NEUT", "project": "Neutrophil Count", "project_cn": "中性粒细胞数量"},
{"abb": "NEUT%", "project": "Neutrophil Percentage", "project_cn": "中性粒细胞百分含量"},
{"abb": "EOS", "project": "Eosinophil Count", "project_cn": "嗜酸细胞数量"},
{"abb": "EOS%", "project": "Eosinophil Percentage", "project_cn": "嗜酸细胞百分含量"},
{"abb": "BAS", "project": "Basophil Count", "project_cn": "嗜碱细胞数量"},
{"abb": "BAS%", "project": "Basophil Percentage", "project_cn": "嗜碱细胞百分含量"},
{"abb": "LYMPH", "project": "Lymphocyte Count", "project_cn": "淋巴细胞数量"},
{"abb": "LYMPH%", "project": "Lymphocyte Percentage", "project_cn": "淋巴细胞百分含量"},
{"abb": "MONO", "project": "Monocyte Count", "project_cn": "单核细胞数量"},
{"abb": "MONO%", "project": "Monocyte Percentage", "project_cn": "单核细胞百分含量"},
{"abb": "PLT", "project": "Platelet Count", "project_cn": "血小板计数"},
{"abb": "PCT", "project": "Plateletcrit", "project_cn": "血小板压积"},
{"abb": "MPV", "project": "Mean Platelet Volume", "project_cn": "平均血小板体积"},
{"abb": "PDW", "project": "Platelet Distribution Width", "project_cn": "血小板分布宽度"}
]
},
"Blood Sugar": {
"cn_name": "血糖",
"pages": "27-28",
"order": 3,
"items": [
{"abb": "FBS", "project": "Fasting Blood Sugar", "project_cn": "空腹血糖"},
{"abb": "HbA1C", "project": "Glycated Hemoglobin", "project_cn": "糖化血红蛋白"}
]
},
"Lipid Profile": {
"cn_name": "血脂",
"pages": "29-31",
"order": 4,
"items": [
{"abb": "TC", "project": "Total Cholesterol", "project_cn": "总胆固醇"},
{"abb": "TG", "project": "Triglycerides", "project_cn": "甘油三酯"},
{"abb": "Lp(a)", "project": "Lipoprotein(a)", "project_cn": "脂蛋白(a)"},
{"abb": "HDL", "project": "HDL Cholesterol", "project_cn": "高密度脂蛋白"},
{"abb": "LDL", "project": "LDL Cholesterol", "project_cn": "低密度脂蛋白"}
]
},
"Blood Type": {
"cn_name": "血型",
"pages": "32-33",
"order": 5,
"items": [
{"abb": "Blood type", "project": "ABO Blood Type", "project_cn": "血型"},
{"abb": "Blood type RH", "project": "Rh Blood Type", "project_cn": "RH血型"}
]
},
"Blood Coagulation": {
"cn_name": "凝血功能",
"pages": "34-36",
"order": 6,
"items": [
{"abb": "PT", "project": "Prothrombin Time", "project_cn": "凝血酶原时间"},
{"abb": "PT%", "project": "Prothrombin Activity", "project_cn": "凝血酶原活动度"},
{"abb": "APTT", "project": "Activated Partial Thromboplastin Time", "project_cn": "活化部分凝血活酶时间"},
{"abb": "TT", "project": "Thrombin Time", "project_cn": "凝血酶时间"},
{"abb": "FIB", "project": "Fibrinogen", "project_cn": "纤维蛋白原"},
{"abb": "INR", "project": "International Normalized Ratio", "project_cn": "国际标准化比值"}
]
},
"Four Infectious Diseases": {
"cn_name": "传染病四项",
"pages": "37-40",
"order": 7,
"items": [
{"abb": "HIV", "project": "HIV Antibody", "project_cn": "人类免疫缺陷病毒抗体"},
{"abb": "TRUST", "project": "Toluidine Red Unheated Serum Test", "project_cn": "梅毒甲苯胺红不加热血清试验"},
{"abb": "TPPA", "project": "Treponema Pallidum Particle Agglutination", "project_cn": "梅毒螺旋体特异性抗体定性"},
{"abb": "HBsAg", "project": "Hepatitis B Surface Antigen", "project_cn": "乙肝表面抗原"},
{"abb": "HBsAb", "project": "Hepatitis B Surface Antibody", "project_cn": "乙肝表面抗体"},
{"abb": "HBeAg", "project": "Hepatitis B e Antigen", "project_cn": "乙肝E抗原"},
{"abb": "HBeAb", "project": "Hepatitis B e Antibody", "project_cn": "乙肝E抗体"},
{"abb": "HBcAb", "project": "Hepatitis B Core Antibody", "project_cn": "乙肝核心抗体"},
{"abb": "HCV-IgM", "project": "Hepatitis C Virus IgM Antibody", "project_cn": "丙型肝炎病毒抗体IgM"}
]
},
"Serum Electrolytes": {
"cn_name": "血电解质",
"pages": "41-43",
"order": 8,
"items": [
{"abb": "K", "project": "Potassium", "project_cn": "钾"},
{"abb": "Na", "project": "Sodium", "project_cn": "钠"},
{"abb": "Cl", "project": "Chloride", "project_cn": "氯"},
{"abb": "Ca", "project": "Calcium", "project_cn": "钙"},
{"abb": "Mg", "project": "Magnesium", "project_cn": "镁"},
{"abb": "P", "project": "Phosphorus", "project_cn": "磷"}
]
},
"Liver Function": {
"cn_name": "肝功能",
"pages": "44-47",
"order": 9,
"items": [
{"abb": "TP", "project": "Total Protein", "project_cn": "总蛋白"},
{"abb": "ALB", "project": "Albumin", "project_cn": "白蛋白"},
{"abb": "GLB", "project": "Globulin", "project_cn": "球蛋白"},
{"abb": "A/G", "project": "Albumin/Globulin Ratio", "project_cn": "白蛋白/球蛋白"},
{"abb": "TBil", "project": "Total Bilirubin", "project_cn": "总胆红素"},
{"abb": "DBil", "project": "Direct Bilirubin", "project_cn": "直接胆红素"},
{"abb": "IBil", "project": "Indirect Bilirubin", "project_cn": "间接胆红素"},
{"abb": "ALP", "project": "Alkaline Phosphatase", "project_cn": "碱性磷酸酶"},
{"abb": "ALT", "project": "Alanine Aminotransferase", "project_cn": "丙氨酸氨基转移酶"},
{"abb": "AST", "project": "Aspartate Aminotransferase", "project_cn": "天门冬氨酸氨基转移酶"},
{"abb": "GGT", "project": "Gamma-Glutamyl Transferase", "project_cn": "γ-谷氨酰转移酶"}
]
},
"Kidney Function": {
"cn_name": "肾功能",
"pages": "48-49",
"order": 10,
"items": [
{"abb": "Scr", "project": "Serum Creatinine", "project_cn": "血清肌酐"},
{"abb": "BUN", "project": "Blood Urea Nitrogen", "project_cn": "血尿素氮"},
{"abb": "UA", "project": "Uric Acid", "project_cn": "尿酸"}
]
},
"Myocardial Enzyme": {
"cn_name": "心肌酶谱",
"pages": "50-51",
"order": 11,
"items": [
{"abb": "CK", "project": "Creatine Kinase", "project_cn": "肌酸激酶"},
{"abb": "LDH", "project": "Lactate Dehydrogenase", "project_cn": "乳酸脱氢酶"},
{"abb": "CK-MB", "project": "Creatine Kinase-MB", "project_cn": "肌酸激酶同工酶"}
]
},
"Thyroid Function": {
"cn_name": "甲状腺功能",
"pages": "52-54",
"order": 12,
"items": [
{"abb": "T3", "project": "Triiodothyronine", "project_cn": "三碘甲状腺原氨酸"},
{"abb": "T4", "project": "Thyroxine", "project_cn": "甲状腺素"},
{"abb": "FT3", "project": "Free Triiodothyronine", "project_cn": "游离三碘甲状腺原氨酸"},
{"abb": "FT4", "project": "Free Thyroxine", "project_cn": "游离甲状腺素"},
{"abb": "TSH", "project": "Thyroid Stimulating Hormone", "project_cn": "促甲状腺激素"},
{"abb": "TgAb", "project": "Thyroglobulin Antibody", "project_cn": "甲状腺球蛋白抗体"}
]
},
"Thromboembolism": {
"cn_name": "心脑血管风险因子",
"pages": "55-56",
"order": 13,
"items": [
{"abb": "Hcy", "project": "Homocysteine", "project_cn": "同型半胱氨酸"},
{"abb": "D-Dimer", "project": "D-Dimer", "project_cn": "D-二聚体"}
]
},
"Bone Metabolism": {
"cn_name": "骨代谢",
"pages": "57-59",
"order": 14,
"items": [
{"abb": "25-OH-VD2+D3", "project": "25-Hydroxyvitamin D2+D3", "project_cn": "25-羟基维生素D2+D3"},
{"abb": "PTH", "project": "Parathyroid Hormone", "project_cn": "甲状旁腺激素"},
{"abb": "OST", "project": "Osteocalcin", "project_cn": "骨钙素"},
{"abb": "TPINP", "project": "Total Procollagen Type 1 N-terminal Propeptide", "project_cn": "总I型胶原氨基端延长肽"},
{"abb": "β-CTX", "project": "Beta-CrossLaps", "project_cn": "β-胶原降解产物"}
]
},
"Microelement": {
"cn_name": "微量元素",
"pages": "60-62",
"order": 15,
"items": [
{"abb": "Pb", "project": "Lead", "project_cn": "全血微量元素铅"},
{"abb": "Cu", "project": "Copper", "project_cn": "全血微量元素铜"},
{"abb": "Zn", "project": "Zinc", "project_cn": "全血微量元素锌"},
{"abb": "Mg", "project": "Magnesium", "project_cn": "全血微量元素镁"},
{"abb": "Fe", "project": "Iron", "project_cn": "全血微量元素铁"}
]
},
"Lymphocyte Subpopulation": {
"cn_name": "淋巴细胞亚群",
"pages": "63-64",
"order": 16,
"items": [
{"abb": "CD3+", "project": "T Lymphocyte", "project_cn": "T淋巴细胞"},
{"abb": "CD4+", "project": "Helper T Cell", "project_cn": "辅助T细胞"},
{"abb": "CD8+", "project": "Cytotoxic T Cell", "project_cn": "细胞毒性T细胞"}
]
},
"Humoral Immunity": {
"cn_name": "体液免疫",
"pages": "65-67",
"order": 17,
"items": [
{"abb": "IgG", "project": "Immunoglobulin G", "project_cn": "免疫球蛋白G"},
{"abb": "IgA", "project": "Immunoglobulin A", "project_cn": "免疫球蛋白A"},
{"abb": "IgM", "project": "Immunoglobulin M", "project_cn": "免疫球蛋白M"},
{"abb": "IgE", "project": "Immunoglobulin E", "project_cn": "免疫球蛋白E"},
{"abb": "C3", "project": "Complement C3", "project_cn": "补体C3"},
{"abb": "C4", "project": "Complement C4", "project_cn": "补体C4"}
]
},
"Inflammatory Reaction": {
"cn_name": "炎症反应",
"pages": "68-69",
"order": 18,
"items": [
{"abb": "CRP", "project": "C-Reactive Protein", "project_cn": "C反应蛋白"},
{"abb": "hs-CRP", "project": "High-Sensitivity C-Reactive Protein", "project_cn": "超敏C反应蛋白"},
{"abb": "ESR", "project": "Erythrocyte Sedimentation Rate", "project_cn": "红细胞沉降率"},
{"abb": "ASO", "project": "Anti-Streptolysin O", "project_cn": "抗链球菌溶血素O"}
]
},
"Autoantibody": {
"cn_name": "自身抗体",
"pages": "70-71",
"order": 19,
"items": [
{"abb": "ANA", "project": "Antinuclear Antibody", "project_cn": "抗核抗体"},
{"abb": "RF", "project": "Rheumatoid Factor", "project_cn": "类风湿因子"}
]
},
"Female Hormone": {
"cn_name": "女性荷尔蒙",
"pages": "72-75",
"order": 20,
"items": [
{"abb": "E2", "project": "Estradiol", "project_cn": "雌二醇"},
{"abb": "PROG", "project": "Progesterone", "project_cn": "孕酮"},
{"abb": "FSH", "project": "Follicle Stimulating Hormone", "project_cn": "促卵泡激素"},
{"abb": "LH", "project": "Luteinizing Hormone", "project_cn": "促黄体生成素"},
{"abb": "PRL", "project": "Prolactin", "project_cn": "垂体催乳素"},
{"abb": "T", "project": "Testosterone", "project_cn": "睾酮"},
{"abb": "DHEAS", "project": "Dehydroepiandrosterone Sulfate", "project_cn": "脱氢表雄酮硫酸酯"},
{"abb": "COR", "project": "Cortisol", "project_cn": "皮质醇"},
{"abb": "IGF-1", "project": "Insulin-like Growth Factor 1", "project_cn": "胰岛素样生长因子1"},
{"abb": "AMH", "project": "Anti-Mullerian Hormone", "project_cn": "人抗缪勒氏管激素"}
]
},
"Male Hormone": {
"cn_name": "男性荷尔蒙",
"pages": "76-79",
"order": 21,
"items": [
{"abb": "T", "project": "Testosterone", "project_cn": "睾酮"},
{"abb": "DHEAS", "project": "Dehydroepiandrosterone Sulfate", "project_cn": "脱氢表雄酮硫酸酯"},
{"abb": "IGF-1", "project": "Insulin-like Growth Factor 1", "project_cn": "胰岛素样生长因子1"},
{"abb": "PROG", "project": "Progesterone", "project_cn": "孕酮"},
{"abb": "FSH", "project": "Follicle Stimulating Hormone", "project_cn": "促卵泡激素"},
{"abb": "LH", "project": "Luteinizing Hormone", "project_cn": "促黄体生成素"},
{"abb": "PRL", "project": "Prolactin", "project_cn": "垂体催乳素"},
{"abb": "Cortisol", "project": "Cortisol", "project_cn": "皮质醇"},
{"abb": "E2", "project": "Estradiol", "project_cn": "雌二醇"}
]
},
"Tumor Markers": {
"cn_name": "肿瘤标记物",
"pages": "80-84",
"order": 22,
"items": [
{"abb": "AFP", "project": "Alpha-Fetoprotein", "project_cn": "甲胎蛋白"},
{"abb": "CEA", "project": "Carcinoembryonic Antigen", "project_cn": "癌胚抗原"},
{"abb": "CA19-9", "project": "Carbohydrate Antigen 19-9", "project_cn": "糖类抗原19-9"},
{"abb": "Fer", "project": "Ferritin", "project_cn": "铁蛋白"},
{"abb": "NSE", "project": "Neuron Specific Enolase", "project_cn": "神经元特异性烯醇化酶"},
{"abb": "Tg", "project": "Thyroglobulin", "project_cn": "甲状腺球蛋白"},
{"abb": "CT", "project": "Calcitonin", "project_cn": "降钙素"},
{"abb": "EA-IgA", "project": "EBV Early Antigen IgA", "project_cn": "EB病毒早期抗原IgA抗体"},
{"abb": "TPSA", "project": "Total Prostate Specific Antigen", "project_cn": "男-总前列腺特异性抗原"},
{"abb": "FPSA", "project": "Free Prostate Specific Antigen", "project_cn": "男-游离前列腺特异性抗原"},
{"abb": "F/TPSA", "project": "Free/Total PSA Ratio", "project_cn": "男-游离/总前列腺特异性抗原"},
{"abb": "CA125", "project": "Cancer Antigen 125", "project_cn": "女-糖类抗原125"},
{"abb": "CA15-3", "project": "Cancer Antigen 15-3", "project_cn": "女-糖类抗原15-3"},
{"abb": "SCC", "project": "Squamous Cell Carcinoma Antigen", "project_cn": "女-鳞状细胞癌抗原"}
]
},
"Imaging": {
"cn_name": "影像学检查",
"pages": "85-88",
"order": 23,
"items": [
{"abb": "ECG", "project": "Electrocardiogram", "project_cn": "心电图"},
{"abb": "Color Doppler Ultrasound", "project": "Color Doppler Ultrasound", "project_cn": "彩色B超检查"},
{"abb": "CT Examination", "project": "CT Examination", "project_cn": "CT检查"}
]
},
"Female-specific": {
"cn_name": "女性专项检查",
"pages": "89-91",
"order": 24,
"items": [
{"abb": "Gynecological routine inspection", "project": "Gynecological Routine Inspection", "project_cn": "妇科常规检查"},
{"abb": "Gynecological special examination", "project": "Gynecological Special Examination", "project_cn": "妇科专项检查"}
]
}
},
"abb_aliases": {
"TUR": "TuR",
"BLD/ERY": "BLD",
"ERY": "BLD",
"TBIL": "TBil",
"DBIL": "DBil",
"IBIL": "IBil",
"A": "ALB",
"G": "GLB",
"CREA": "Scr",
"Cr": "Scr",
"FPG": "FBS",
"HbA1c": "HbA1C",
"HCY": "Hcy",
"HOMOCYSTEINE": "Hcy",
"25-OH-VitD": "25-OH-VD2+D3",
"VitD": "25-OH-VD2+D3",
"P1NP": "TPINP",
"CTX": "β-CTX",
"B-CTX": "β-CTX",
"OSTE": "OST",
"TESTO": "T",
"DHEA-S": "DHEAS",
"Cortisol": "COR",
"IGF1": "IGF-1",
"Anti-HCV": "HCV-IgM",
"HCV": "HCV-IgM",
"RPR": "TRUST",
"SAPA": "TPPA",
"PSA": "TPSA",
"fPSA": "FPSA",
"CA153": "CA15-3",
"CA199": "CA19-9",
"NES": "NSE",
"E/TPSA": "F/TPSA",
"CD4": "CD4+",
"CD8": "CD8+",
"CD3": "CD3+",
"Rh-D": "Blood type RH",
"RhD": "Blood type RH",
"Rh(D)": "Blood type RH",
"Rh Factor": "Blood type RH",
"Rh": "Blood type RH",
"ABO": "Blood type",
"PT-INR": "INR",
"PT Activity": "PT%",
"PTA": "PT%",
"Chol": "TC",
"CHOL": "TC",
"HDL-C": "HDL",
"LDL-C": "LDL",
"VLDL-C": "VLDL",
"Lpa": "Lp(a)",
"LPA": "Lp(a)",
"PDY": "PDW",
"Total RBC": "RBC",
"RBCt": "RBC",
"CYFRA 21-1": "CYFRA21-1",
"Homocysteine": "Hcy",
"CD16/CD56": "NK",
"NK Cell": "NK",
"B Lymphocyte": "B-Lymph",
"T Lymphocyte": "T-Lymph",
"K+": "K",
"Na+": "Na",
"Cl-": "Cl",
"Ca2+": "Ca",
"Mg2+": "Mg",
"Kalium": "K",
"Sodium": "Na",
"Chloride": "Cl",
"Calcium": "Ca",
"Magnesium": "Mg",
"Phosphorus": "P",
"TOTALRBC": "RBC",
"RBCMORPHOLOGY": "RBC Morphology",
"RBC MORPHOLOGY": "RBC Morphology",
"LP(A)": "Lp(a)",
"FASTINGBLOODSUGAR": "FBS",
"BCTX": "β-CTX",
"C": "Scr",
"Ferritin": "Fer",
"FERRITIN": "Fer",
"MIB": "Hg",
"CIB": "Cd",
"Mn": "Mn",
"Ni": "Ni",
"NAD": "NAD+",
"Food Allergy": "Food Intolerance",
"Allergen": "Inhalant Allergen",
"Turbidity": "TuR",
"NEUT#": "NEUT",
"Neutrophil": "NEUT",
"Neutrophils": "NEUT",
"EOS#": "EOS",
"Eosinophils": "EOS",
"BAS#": "BAS",
"Basophils": "BAS",
"LYMPH#": "LYMPH",
"Lymphocytes": "LYMPH",
"MONO#": "MONO",
"Monocytes": "MONO",
"Mean Cell Hemoglobin": "MCH",
"Mean Cell Hb": "MCH",
"RDW-CV": "RDW",
"RDW-SD": "RDW",
"Plateletcrit": "PCT",
"Prothrombin Time": "PT",
"Thrombin Time": "TT",
"Fibrinogen": "FIB",
"A/G Ratio": "A/G",
"AG Ratio": "A/G",
"Albumin/Globulin": "A/G",
"Uric Acid": "UA",
"URIC": "UA",
"Creatine Kinase": "CK",
"Total T4": "T4",
"Thyroxine": "T4",
"TC/HDL Ratio": "TC/HDL",
"Chol/HDL": "TC/HDL",
"LDL/HDL Ratio": "LDL/HDL",
"Copper": "Cu",
"CU": "Cu",
"Zinc": "Zn",
"ZN": "Zn",
"Iron": "Fe",
"FE": "Fe",
"Anti-Streptolysin": "ASO",
"Antinuclear": "ANA",
"Calcitonin": "CT",
"EBV-IgA": "EA-IgA",
"Anti-Mullerian": "AMH",
"HSCRP": "hs-CRP",
"High Sensitivity CRP": "hs-CRP",
"TgAb": "TgAb",
"TGAB": "TgAb",
"Thyroglobulin Antibody": "TgAb",
"Anti-Thyroglobulin": "TgAb",
"WBC": "WBC count",
"White Blood Cell": "WBC count",
"Total WBC": "WBC count"
},
"module_aliases": {
"Urine Detection": "Urine Test",
"Urinalysis": "Urine Test",
"Urine Analysis": "Urine Test",
"CBC": "Complete Blood Count",
"Blood Count": "Complete Blood Count",
"Hematology": "Complete Blood Count",
"Glucose": "Blood Sugar",
"Blood Glucose": "Blood Sugar",
"Glycemic": "Blood Sugar",
"Lipid Panel": "Lipid Profile",
"Lipids": "Lipid Profile",
"Blood Lipid": "Lipid Profile",
"Coagulation": "Blood Coagulation",
"Clotting": "Blood Coagulation",
"Infectious Disease": "Four Infectious Diseases",
"Infectious Diseases": "Four Infectious Diseases",
"Infection": "Four Infectious Diseases",
"Electrolytes": "Serum Electrolytes",
"Electrolyte": "Serum Electrolytes",
"Ions": "Serum Electrolytes",
"Liver": "Liver Function",
"Hepatic": "Liver Function",
"Kidney": "Kidney Function",
"Renal": "Kidney Function",
"Renal Function": "Kidney Function",
"Cardiac Enzyme": "Myocardial Enzyme",
"Heart Enzyme": "Myocardial Enzyme",
"Cardiac": "Myocardial Enzyme",
"Myocardial Enzyme Spectrum": "Myocardial Enzyme",
"Thyroid": "Thyroid Function",
"Cardiovascular Risk": "Thromboembolism",
"Cardiovascular": "Thromboembolism",
"Bone": "Bone Metabolism",
"Bone Markers": "Bone Metabolism",
"Trace Elements": "Microelement",
"Trace Element": "Microelement",
"Heavy Metals": "Microelement",
"Lymphocyte": "Lymphocyte Subpopulation",
"Lymphocyto Subpopulation": "Lymphocyte Subpopulation",
"T Cell": "Lymphocyte Subpopulation",
"Immunity": "Humoral Immunity",
"Immunoglobulin": "Humoral Immunity",
"Inflammation": "Inflammatory Reaction",
"Inflammatory": "Inflammatory Reaction",
"Autoimmune": "Autoantibody",
"Autoimmunity": "Autoantibody",
"Female": "Female Hormone",
"Female Hormones": "Female Hormone",
"Male": "Male Hormone",
"Male Hormones": "Male Hormone",
"Hormone": "Male Hormone",
"Tumor": "Tumor Markers",
"Cancer Markers": "Tumor Markers",
"Oncology": "Tumor Markers",
"Radiology": "Imaging",
"Image": "Imaging",
"Gynecology": "Female-specific",
"Gynecological": "Female-specific"
}
}

80
backend/analyze_output.py Normal file
View File

@@ -0,0 +1,80 @@
"""分析生成文件的结构问题"""
from docx import Document
from lxml import etree
import zipfile
import os
def analyze_file(filepath, name):
"""分析文件结构"""
print(f"\n{'='*70}")
print(f"分析: {name}")
print(f"文件: {filepath}")
print(f"{'='*70}")
# 读取 XML
with zipfile.ZipFile(filepath, 'r') as z:
xml_content = z.read('word/document.xml')
tree = etree.fromstring(xml_content)
ns = {'w': 'http://schemas.openxmlformats.org/wordprocessingml/2006/main'}
body = tree.find('.//w:body', ns)
# 找到 Urine Detection 相关的元素
print("\n搜索 'Urine Detection' 相关元素:")
print("-" * 70)
urine_positions = []
for i, elem in enumerate(body):
text = ''.join(elem.itertext()).strip()
if 'Urine' in text and 'Detection' in text:
tag = elem.tag.split('}')[-1]
text_preview = text[:100].replace('\n', '\\n')
print(f" [{i}] <{tag}>: {text_preview}...")
urine_positions.append(i)
if not urine_positions:
print(" 未找到")
return
# 分析第一个 Urine Detection 位置前后的元素
first_pos = urine_positions[0]
print(f"\n从第一个 Urine Detection (位置 {first_pos}) 开始的40个元素:")
print("-" * 70)
for i in range(first_pos, min(first_pos + 40, len(body))):
elem = body[i]
tag = elem.tag.split('}')[-1]
text = ''.join(elem.itertext()).strip()
text_preview = text[:80].replace('\n', '\\n') if text else '[空]'
# 额外信息
extra = ""
if tag == 'tbl':
rows = elem.findall('.//{http://schemas.openxmlformats.org/wordprocessingml/2006/main}tr')
extra = f" [行数:{len(rows)}]"
# 检查是否是表头
if len(rows) == 1 and ('Abb' in text or 'Project' in text):
extra += " [表头]"
elif tag == 'p':
# 检查是否有分页符
page_breaks = elem.findall('.//{http://schemas.openxmlformats.org/wordprocessingml/2006/main}br')
for br in page_breaks:
br_type = br.get('{http://schemas.openxmlformats.org/wordprocessingml/2006/main}type')
if br_type == 'page':
extra = " [分页符]"
break
print(f" [{i}] <{tag}>{extra}: {text_preview}")
def main():
# 模板
template_path = r"../Be.U Wellness Center功能医学健康报告&定制化方案-案例.docx"
# 最新生成的文件
generated_path = "reports/filled_report_20260115_204528.docx"
analyze_file(template_path, "模板")
analyze_file(generated_path, "生成文件")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,80 @@
from docx import Document
from lxml import etree
doc = Document(r'C:\Users\UI\Desktop\医疗报告\backend\reports\filled_report_20260212_165326.docx')
body = doc.element.body
children = list(body)
ns = {'w': 'http://schemas.openxmlformats.org/wordprocessingml/2006/main'}
w = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main'
def show_para_format(elem, label):
text = ''.join(elem.itertext()).strip()
print(f'=== {label} ===')
print(f'Text: {text[:80]}')
# pPr
pPr = elem.find('w:pPr', ns)
if pPr is not None:
jc = pPr.find('w:jc', ns)
if jc is not None:
print(f' jc: {jc.get(f"{{{w}}}val")}')
pStyle = pPr.find('w:pStyle', ns)
if pStyle is not None:
print(f' pStyle: {pStyle.get(f"{{{w}}}val")}')
# runs
for r in elem.findall('w:r', ns):
rPr = r.find('w:rPr', ns)
rt = ''.join(r.itertext()).strip()
if not rt:
continue
print(f' Run: "{rt[:50]}"')
if rPr is not None:
rFonts = rPr.find('w:rFonts', ns)
sz = rPr.find('w:sz', ns)
szCs = rPr.find('w:szCs', ns)
b = rPr.find('w:b', ns)
bCs = rPr.find('w:bCs', ns)
color = rPr.find('w:color', ns)
if rFonts is not None:
fonts = {}
for attr in ['ascii', 'hAnsi', 'eastAsia', 'cs']:
v = rFonts.get(f'{{{w}}}{attr}')
if v:
fonts[attr] = v
print(f' fonts: {fonts}')
if sz is not None:
print(f' sz: {sz.get(f"{{{w}}}val")} (={int(sz.get(f"{{{w}}}val"))//2}pt)')
if szCs is not None:
print(f' szCs: {szCs.get(f"{{{w}}}val")}')
if b is not None:
print(f' bold: yes')
if bCs is not None:
print(f' boldCs: yes')
if color is not None:
print(f' color: {color.get(f"{{{w}}}val")}')
else:
print(f' (no rPr)')
# Overall Health Assessment
for i, elem in enumerate(children):
text = ''.join(elem.itertext()).strip()
if 'Overall Health' in text and 'Assessment' in text and len(text) < 200:
show_para_format(elem, f'Overall Health Assessment [{i}]')
break
print()
# Medical Intervention
for i, elem in enumerate(children):
text = ''.join(elem.itertext()).strip()
if 'Medical Intervention' in text and '医学干预' in text and len(text) < 200:
show_para_format(elem, f'Medical Intervention [{i}]')
break
print()
# FHA Title
for i, elem in enumerate(children):
text = ''.join(elem.itertext()).strip()
if 'Functional Medical Health Advice' in text and '功能医学健康建议' in text and len(text) < 300:
show_para_format(elem, f'FHA Title [{i}]')
break

View File

@@ -0,0 +1,139 @@
# 医疗检测项目分类提示词参考
## 原始 ABB → 模块硬编码映射(旧版,已替换)
```
# 尿检
COLOR, CLARITY, SG, PH, PRO, GLU, KET, NIT, URO, BIL, LEU, ERY, BLD, CRY, BAC → Urine Detection
# 血常规
WBC, RBC, HB, HGB, HCT, MCV, MCH, MCHC, PLT, RDW, MPV, PDW, NEUT, LYMPH, MONO, EOS, BAS, ESR → Complete Blood Count
# 肝功能
ALT, AST, GGT, ALP, TBIL, DBIL, IBIL, TP, ALB, GLB, A/G, LDH → Liver Function
# 肾功能
BUN, CREA, CR, UA, EGFR, CYS-C → Kidney Function
# 血脂
TC, TG, HDL, LDL, VLDL, APOA1, APOB, LP(A) → Lipid Panel
# 电解质
NA, K, CL, CA, P, MG, FE, ZN, CU, TCO2, AG → Electrolytes
# 糖代谢
FPG, HBA1C, OGTT, INS, C-PEP, EAG → Glucose
# 甲状腺
TSH, FT3, FT4, T3, T4, TG-AB, TPO-AB → Thyroid
# 激素(通用,未区分男女)
E2, PROG, TESTO, FSH, LH, PRL, CORTISOL, DHEA-S, IGF-1 → Hormone
# 肿瘤标志物
AFP, CEA, CA125, CA153, CA199, PSA, FPSA, NSE, CYFRA21-1, SCC, CA724 → Tumor Markers
# 凝血
PT, APTT, TT, FIB, D-DIMER, INR, FDP → Coagulation
# 传染病
HBSAG, HBSAB, HBEAG, HBEAB, HBCAB, ANTI-HCV, HIV, RPR, TPPA, H.PYLORI → Infectious Disease
# 免疫功能(笼统合并,未拆分体液免疫/炎症/自身抗体)
IGG, IGA, IGM, IGE, C3, C4, CRP, HS-CRP, RF, ANA, ANTI-SM, ANTI-RNP, ASO, NK → Immune Function
# 骨代谢
OSTE, P1NP, CTX, PTH, 25-OH-VITD → Bone Metabolism
# 重金属
PB, MN, NI, CR, CD, HG → Heavy Metals
# 维生素
VITB12, FOLATE, VITD → Vitamin
# 同型半胱氨酸
HCY → Homocysteine
# 血型
ABO, RH → Blood Type
```
## 原始项目名关键词 → 模块映射(旧版,已替换)
```
urine, urinary → Urine Detection
blood cell, hemoglobin, platelet, neutrophil → Complete Blood Count
liver, hepat, bilirubin → Liver Function
kidney, renal, creatinine → Kidney Function
cholesterol, triglyceride, lipid → Lipid Panel
glucose, sugar, hba1c, insulin → Glucose
thyroid, tsh → Thyroid
estrogen, testosterone, progesterone, cortisol, hormone → Hormone
tumor, cancer, antigen → Tumor Markers
coagul, thrombin, fibrin → Coagulation
hepatitis, hiv, syphilis → Infectious Disease
immun, antibod, complement → Immune Function
bone, osteocalcin → Bone Metabolism
metal, lead, mercury → Heavy Metals
vitamin, folate, b12 → Vitamin
homocysteine → Homocysteine
```
## 原始 DeepSeek 提示词(旧版,已替换)
```
请判断以下医学检测项目属于哪个检测模块,只返回模块名称(英文):
项目缩写: {abb}
项目名称: {project_name}
可选模块:
- Urine Detection尿液检测
- Complete Blood Count血常规
- Liver Function肝功能
- Kidney Function肾功能
- Lipid Panel血脂
- Electrolytes电解质
- Glucose糖代谢
- Thyroid甲状腺功能
- Hormone激素
- Tumor Markers肿瘤标志物
- Coagulation凝血功能
- Infectious Disease传染病
- Immune Function免疫功能
- Bone Metabolism骨代谢
- Heavy Metals重金属
- Vitamin维生素
- Other其他
只返回英文模块名称,不要其他内容。
```
## 旧版 → 新版模块名对照
| 旧模块名 | 新模块名(对齐 abb_mapping_config.json |
|---|---|
| Urine Detection | Urine Test |
| Lipid Panel | Lipid Profile |
| Electrolytes | Serum Electrolytes |
| Glucose | Blood Sugar |
| Thyroid | Thyroid Function |
| Hormone通用 | Female Hormone / Male Hormone按性别拆分 |
| Coagulation | Blood Coagulation |
| Infectious Disease | Four Infectious Diseases |
| Immune Function笼统 | Humoral Immunity / Inflammatory Reaction / Autoantibody拆分为3个 |
| Heavy Metals | Microelement |
| Vitamin | 移除(归入 Bone Metabolism 或 Other |
| Homocysteine | Thromboembolism |
| —(缺失) | Myocardial Enzyme新增 |
| —(缺失) | Lymphocyte Subpopulation新增 |
| —(缺失) | Imaging新增 |
| —(缺失) | Female-specific新增 |
## 新版分类问题修正
1. **ESR**:旧版归入 Complete Blood Count → 新版归入 Inflammatory Reaction参考 NH Excel
2. **LDH**:旧版归入 Liver Function → 新版归入 Myocardial Enzyme
3. **D-Dimer**:旧版归入 Coagulation → 新版归入 Thromboembolism
4. **FE/ZN/CU**:旧版归入 Electrolytes → 新版归入 Microelement
5. **免疫系统**:旧版笼统 Immune Function → 新版拆分为 Humoral ImmunityIgG/IgA/IgM/IgE/C3/C4、Inflammatory ReactionCRP/ESR/ASO、AutoantibodyANA/RF

84
backend/compare_format.py Normal file
View File

@@ -0,0 +1,84 @@
"""对比模板和生成文件的格式差异"""
from docx import Document
from docx.shared import Pt, Inches
import os
def analyze_document(filepath, name):
"""分析文档结构"""
print(f"\n{'='*60}")
print(f"分析: {name}")
print(f"文件: {filepath}")
print(f"{'='*60}")
doc = Document(filepath)
# 找到 Urine Detection 模块
found_urine = False
urine_start = -1
for i, elem in enumerate(doc.element.body):
text = elem.text if hasattr(elem, 'text') and elem.text else ''
if 'Urine' in text and 'Detection' in text:
urine_start = i
found_urine = True
break
if not found_urine:
print("未找到 Urine Detection 模块")
return
print(f"\n找到 Urine Detection 位置: {urine_start}")
print(f"\n从 Urine Detection 开始的前30个元素:")
print("-" * 60)
for i in range(urine_start, min(urine_start + 30, len(doc.element.body))):
elem = doc.element.body[i]
tag = elem.tag.split('}')[-1]
text = elem.text if hasattr(elem, 'text') else ''
text_preview = text[:80].replace('\n', '\\n') if text else ''
# 获取更多信息
extra_info = ""
if tag == 'p':
# 检查段落样式
p_elem = elem
style_elem = p_elem.find('.//{http://schemas.openxmlformats.org/wordprocessingml/2006/main}pStyle')
if style_elem is not None:
extra_info = f" [style: {style_elem.get('{http://schemas.openxmlformats.org/wordprocessingml/2006/main}val')}]"
# 检查是否有图片
drawings = p_elem.findall('.//{http://schemas.openxmlformats.org/wordprocessingml/2006/main}drawing')
if drawings:
extra_info += f" [有图片: {len(drawings)}个]"
elif tag == 'tbl':
# 统计表格行数和列数
rows = elem.findall('.//{http://schemas.openxmlformats.org/wordprocessingml/2006/main}tr')
extra_info = f" [行数: {len(rows)}]"
print(f" [{i}] <{tag}>{extra_info}: {text_preview}")
def main():
# 模板文件
template_path = r"../Be.U Wellness Center功能医学健康报告&定制化方案-案例.docx"
# 生成的文件 - 找最新的
reports_dir = "reports"
if os.path.exists(reports_dir):
files = [f for f in os.listdir(reports_dir) if f.startswith('filled_report_') and f.endswith('.docx')]
if files:
files.sort(reverse=True)
generated_path = os.path.join(reports_dir, files[0])
else:
print("未找到生成的报告文件")
return
else:
print("reports目录不存在")
return
# 分析两个文档
analyze_document(template_path, "模板文件")
analyze_document(generated_path, "生成文件")
if __name__ == "__main__":
main()

412
backend/config.py Normal file
View File

@@ -0,0 +1,412 @@
"""
配置文件 - 统一管理路径和参数
"""
from pathlib import Path
import os
# 项目根目录
PROJECT_ROOT = Path(__file__).parent.parent
BACKEND_ROOT = Path(__file__).parent
# ==================== 路径配置 ====================
# PDF输入目录存放原始医疗报告PDF
PDF_INPUT_DIR = Path(r"c:\Users\UI\Desktop\医疗报告\医疗报告智能体")
# Word模板文件
TEMPLATE_COMPLETE = BACKEND_ROOT / "template_complete.docx" # 用于 extract_and_fill_report.py
TEMPLATE_DOCXTPL = PROJECT_ROOT / "template_docxtpl.docx" # 用于 fill_with_docxtpl.py
# 配置文件
ABB_MAPPING_CONFIG = BACKEND_ROOT / "abb_mapping_config.json"
# 输出目录
REPORTS_OUTPUT_DIR = BACKEND_ROOT / "reports"
REPORTS_OUTPUT_DIR.mkdir(exist_ok=True)
# 缓存文件
EXTRACTED_DATA_FILE = BACKEND_ROOT / "extracted_medical_data.json"
ANALYZED_DATA_FILE = BACKEND_ROOT / "analyzed_medical_data.json"
DEEPSEEK_PROCESSED_DATA_FILE = BACKEND_ROOT / "deepseek_processed_data.json"
DEEPSEEK_CACHE_FILE = BACKEND_ROOT / "deepseek_cache.json"
# ==================== OCR配置 ====================
# 百度OCR配置从环境变量读取
BAIDU_OCR_APP_ID = os.getenv("BAIDU_OCR_APP_ID", "")
BAIDU_OCR_API_KEY = os.getenv("BAIDU_OCR_API_KEY", "")
BAIDU_OCR_SECRET_KEY = os.getenv("BAIDU_OCR_SECRET_KEY", "")
# ==================== LLM配置 ====================
# DeepSeek配置
DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY", "")
DEEPSEEK_API_BASE = os.getenv("DEEPSEEK_API_BASE", "https://api.deepseek.com")
# OpenAI配置
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "")
OPENAI_API_BASE = os.getenv("OPENAI_API_BASE", "")
OPENAI_MODEL = os.getenv("OPENAI_MODEL", "gpt-3.5-turbo")
# Ollama配置
OLLAMA_HOST = os.getenv("OLLAMA_HOST", "http://localhost:11434")
OLLAMA_MODEL = os.getenv("OLLAMA_MODEL", "qwen2.5:7b")
# Coze配置
COZE_API_KEY = os.getenv("COZE_API_KEY", "")
COZE_WORKFLOW_ID = os.getenv("COZE_WORKFLOW_ID", "")
# ==================== 功能开关 ====================
# 是否启用DeepSeek分析在extract_and_fill_report.py中使用
ENABLE_DEEPSEEK_ANALYSIS = os.getenv("ENABLE_DEEPSEEK_ANALYSIS", "false").lower() == "true"
# ==================== 辅助函数 ====================
def load_abb_config() -> dict:
"""
加载ABB映射配置文件
Returns:
dict: 包含以下键的字典:
- modules: 模块配置字典
- abb_list: 所有ABB列表
- abb_to_module: ABB到模块的映射
- abb_to_info: ABB到详细信息的映射
- abb_aliases: ABB别名映射
- module_aliases: 模块名称别名映射
"""
import json
result = {
'modules': {},
'abb_list': [],
'abb_to_module': {},
'abb_to_info': {},
'abb_aliases': {},
'module_aliases': {}
}
if not ABB_MAPPING_CONFIG.exists():
return result
with open(ABB_MAPPING_CONFIG, 'r', encoding='utf-8') as f:
config = json.load(f)
# 新格式:基于模块的配置
if 'modules' in config and isinstance(config['modules'], dict):
result['modules'] = config['modules']
result['abb_aliases'] = config.get('abb_aliases', {})
result['module_aliases'] = config.get('module_aliases', {})
# 定义大小写敏感的ABB这些ABB有大小写冲突必须精确匹配
case_sensitive_abbs = {'TG', 'Tg'} # TG=甘油三酯, Tg=甲状腺球蛋白
for module_name, module_data in config['modules'].items():
items = module_data.get('items', [])
for item in items:
abb = item.get('abb', '')
if abb:
result['abb_list'].append(abb)
info = {
'abb': abb,
'project': item.get('project', ''),
'project_cn': item.get('project_cn', ''),
'module': module_name,
'module_cn': module_data.get('cn_name', '')
}
# 对于大小写敏感的ABB使用原始大小写作为key
if abb in case_sensitive_abbs:
result['abb_to_module'][abb] = module_name
result['abb_to_info'][abb] = info
else:
# 其他ABB使用大写作为key保持向后兼容
result['abb_to_module'][abb.upper()] = module_name
result['abb_to_info'][abb.upper()] = info
# 旧格式items列表
elif 'items' in config:
for item in config['items']:
abb = item.get('abb', '')
if abb:
result['abb_list'].append(abb)
module = item.get('module', '')
result['abb_to_module'][abb.upper()] = module
result['abb_to_info'][abb.upper()] = {
'abb': abb,
'project': item.get('project', ''),
'project_cn': item.get('project_cn', ''),
'module': module
}
return result
def normalize_abb(abb: str, config: dict = None) -> str:
"""
标准化ABB名称处理别名
Args:
abb: 原始ABB名称
config: ABB配置可选如果不提供则自动加载
Returns:
标准化后的ABB名称
"""
if config is None:
config = load_abb_config()
aliases = config.get('abb_aliases', {})
abb_upper = abb.upper()
# 检查是否有别名
if abb in aliases:
return aliases[abb]
if abb_upper in aliases:
return aliases[abb_upper]
return abb
def normalize_module_name(module: str, config: dict = None) -> str:
"""
标准化模块名称处理DeepSeek返回的不同名称
Args:
module: 原始模块名称
config: ABB配置可选如果不提供则自动加载
Returns:
标准化后的模块名称
"""
if config is None:
config = load_abb_config()
module_aliases = config.get('module_aliases', {})
# 检查是否有别名
if module in module_aliases:
return module_aliases[module]
# 尝试不区分大小写匹配
module_lower = module.lower()
for alias, standard in module_aliases.items():
if alias.lower() == module_lower:
return standard
return module
def get_standard_module_order() -> list:
"""
获取标准模块顺序基于2.pdf模板
优先从配置文件读取order字段确保与2.pdf一致
Returns:
模块名称列表,按标准顺序排列
"""
config = load_abb_config()
modules = config.get('modules', {})
# 如果配置中有order字段按order排序
if modules and any('order' in m for m in modules.values()):
sorted_modules = sorted(
modules.items(),
key=lambda x: x[1].get('order', 999)
)
return [name for name, _ in sorted_modules]
# 默认顺序与2.pdf一致
return [
'Urine Test', # 1. 尿液检测 (第16-19页)
'Complete Blood Count', # 2. 血常规 (第20-26页)
'Blood Sugar', # 3. 血糖 (第27-28页)
'Lipid Profile', # 4. 血脂 (第29-31页)
'Blood Type', # 5. 血型 (第32-33页)
'Blood Coagulation', # 6. 凝血功能 (第34-36页)
'Four Infectious Diseases', # 7. 传染病四项 (第37-40页)
'Serum Electrolytes', # 8. 血电解质 (第41-43页)
'Liver Function', # 9. 肝功能 (第44-47页)
'Kidney Function', # 10. 肾功能 (第48-49页)
'Myocardial Enzyme', # 11. 心肌酶谱 (第50-51页)
'Thyroid Function', # 12. 甲状腺功能 (第52-54页)
'Thromboembolism', # 13. 心脑血管风险因子 (第55-56页)
'Bone Metabolism', # 14. 骨代谢 (第57-59页)
'Microelement', # 15. 微量元素 (第60-62页)
'Lymphocyte Subpopulation', # 16. 淋巴细胞亚群 (第63-64页)
'Humoral Immunity', # 17. 体液免疫 (第65-67页)
'Inflammatory Reaction', # 18. 炎症反应 (第68-69页)
'Autoantibody', # 19. 自身抗体 (第70-71页)
'Female Hormone', # 20. 女性荷尔蒙 (第72-75页)
'Male Hormone', # 21. 男性荷尔蒙 (第76-79页)
'Tumor Markers', # 22. 肿瘤标记物 (第80-84页)
'Imaging', # 23. 影像学检查 (第85-88页)
'Female-specific', # 24. 女性专项检查 (第89-91页)
]
def get_standard_item_order(module_name: str, config: dict = None) -> list:
"""
获取指定模块的标准项目顺序
Args:
module_name: 模块名称
config: ABB配置可选
Returns:
该模块的ABB列表按标准顺序排列
"""
if config is None:
config = load_abb_config()
modules = config.get('modules', {})
if module_name in modules:
items = modules[module_name].get('items', [])
return [item.get('abb', '') for item in items]
return []
def sort_items_by_standard_order(items: list, module_name: str, config: dict = None) -> list:
"""
按标准顺序排序项目列表
Args:
items: [(abb, data), ...] 格式的项目列表
module_name: 模块名称
config: ABB配置可选
Returns:
排序后的项目列表,标准项目在前,非标准项目在后
"""
if config is None:
config = load_abb_config()
standard_order = get_standard_item_order(module_name, config)
abb_aliases = config.get('abb_aliases', {})
# 创建顺序映射(先精确匹配,再大写匹配)
# 大小写敏感的ABB如TG/Tg需要精确匹配
case_sensitive_abbs = {'TG', 'Tg'}
order_map_exact = {abb: i for i, abb in enumerate(standard_order)}
order_map_upper = {abb.upper(): i for i, abb in enumerate(standard_order) if abb not in case_sensitive_abbs}
# 分离标准项目和非标准项目
standard_items = []
extra_items = []
for abb, data in items:
# 先标准化ABB处理别名
normalized_abb = normalize_abb(abb, config)
# 先尝试精确匹配使用标准化后的ABB
if normalized_abb in order_map_exact:
standard_items.append((abb, data, order_map_exact[normalized_abb]))
# 再尝试原始ABB精确匹配
elif abb in order_map_exact:
standard_items.append((abb, data, order_map_exact[abb]))
# 再尝试大写匹配排除大小写敏感的ABB
elif normalized_abb.upper() in order_map_upper:
standard_items.append((abb, data, order_map_upper[normalized_abb.upper()]))
elif abb.upper() in order_map_upper:
standard_items.append((abb, data, order_map_upper[abb.upper()]))
else:
extra_items.append((abb, data))
# 标准项目按顺序排序
standard_items.sort(key=lambda x: x[2])
sorted_standard = [(abb, data) for abb, data, _ in standard_items]
# 非标准项目按ABB字母顺序排序添加到末尾
extra_items.sort(key=lambda x: x[0].upper())
return sorted_standard + extra_items
def get_output_path(prefix: str = "filled_report", suffix: str = ".docx") -> Path:
"""
生成输出文件路径(自动递增版本号)
Args:
prefix: 文件名前缀
suffix: 文件后缀
Returns:
输出文件路径
"""
existing = list(REPORTS_OUTPUT_DIR.glob(f"{prefix}_*.docx"))
if not existing:
version = 1
else:
versions = []
for p in existing:
name = p.stem
try:
# 尝试提取版本号(格式: prefix_v1, prefix_20240101_120000等
if name.startswith(prefix):
rest = name[len(prefix):]
if rest.startswith('_v'):
v_str = rest[2:]
if v_str.isdigit():
versions.append(int(v_str))
elif rest.startswith('_') and len(rest) > 1:
# 尝试提取时间戳后的版本号
parts = rest.split('_')
if len(parts) > 1:
last_part = parts[-1]
if last_part.isdigit():
versions.append(int(last_part))
except:
continue
version = max(versions) + 1 if versions else 1
return REPORTS_OUTPUT_DIR / f"{prefix}_v{version}{suffix}"
def check_required_files() -> dict:
"""
检查必需文件是否存在
Returns:
dict: {文件路径: 是否存在}
"""
return {
"template_complete": TEMPLATE_COMPLETE.exists(),
"template_docxtpl": TEMPLATE_DOCXTPL.exists(),
"config_file": ABB_MAPPING_CONFIG.exists(),
"pdf_input_dir": PDF_INPUT_DIR.exists(),
}
def print_config_summary():
"""打印配置摘要"""
print("\n" + "=" * 70)
print("配置摘要")
print("=" * 70)
print("\n[路径配置]")
print(f" PDF输入目录: {PDF_INPUT_DIR}")
print(f" 模板文件 (extract): {TEMPLATE_COMPLETE}")
print(f" 模板文件 (docxtpl): {TEMPLATE_DOCXTPL}")
print(f" 配置文件: {ABB_MAPPING_CONFIG}")
print(f" 输出目录: {REPORTS_OUTPUT_DIR}")
print("\n[文件检查]")
files_status = check_required_files()
for name, exists in files_status.items():
status = "[OK] 存在" if exists else "[X] 缺失"
print(f" {name}: {status}")
print("\n[API配置]")
print(f" 百度OCR: {'[OK] 已配置' if BAIDU_OCR_API_KEY else '[X] 未配置'}")
print(f" DeepSeek: {'[OK] 已配置' if DEEPSEEK_API_KEY else '[X] 未配置'}")
print(f" OpenAI: {'[OK] 已配置' if OPENAI_API_KEY else '[X] 未配置'}")
print(f" Ollama: {'[OK] 已配置' if OLLAMA_HOST else '[X] 未配置'}")
print(f" Coze: {'[OK] 已配置' if COZE_API_KEY else '[X] 未配置'}")
print("=" * 70 + "\n")
if __name__ == '__main__':
# 测试配置
print_config_summary()

View File

@@ -0,0 +1,283 @@
"""
DeepSeek医疗数据分析器
使用DeepSeek API分析OCR提取的医疗数据补充缺失的参考范围和单位
"""
import json
import requests
from typing import List, Dict
class DeepSeekAnalyzer:
def __init__(self, api_key: str):
self.api_key = api_key
self.api_url = "https://api.deepseek.com/v1/chat/completions"
self.headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
def analyze_medical_data(self, items: List[Dict]) -> List[Dict]:
"""
分析医疗数据,补充缺失的参考范围、单位和提示
Args:
items: OCR提取的医疗检测项列表
Returns:
补充完整的医疗检测项列表
"""
# 分批处理每批20个项目
batch_size = 20
all_results = []
for i in range(0, len(items), batch_size):
batch = items[i:i+batch_size]
print(f" 处理第 {i//batch_size + 1} 批 ({len(batch)} 项)...")
result = self._analyze_batch(batch)
if result:
all_results.extend(result)
else:
# 如果API调用失败保留原始数据
all_results.extend(batch)
return all_results
def _analyze_batch(self, items: List[Dict]) -> List[Dict]:
"""分析一批医疗数据"""
# 构建提示词
prompt = self._build_prompt(items)
try:
response = requests.post(
self.api_url,
headers=self.headers,
json={
"model": "deepseek-chat",
"messages": [
{
"role": "system",
"content": """你是一个专业的医学检验数据分析专家。你的任务是:
1. 分析医疗检测项目数据
2. 为缺失参考范围(reference)的项目补充标准参考范围
3. 为缺失单位(unit)的项目补充正确单位
4. 判断结果是否在正常范围内:
- 如果结果在正常范围内point字段设为空字符串""
- 如果结果高于正常范围point字段设为""
- 如果结果低于正常范围point字段设为""
- 如果是定性结果如Negative/Positive且结果正常point为空异常则标注
请严格按照JSON格式返回不要添加任何额外说明。"""
},
{
"role": "user",
"content": prompt
}
],
"temperature": 0.1,
"max_tokens": 4000
},
timeout=60
)
if response.status_code == 200:
result = response.json()
content = result['choices'][0]['message']['content']
# 解析JSON响应
# 处理可能的markdown代码块
if '```json' in content:
content = content.split('```json')[1].split('```')[0]
elif '```' in content:
content = content.split('```')[1].split('```')[0]
return json.loads(content.strip())
else:
print(f" ⚠ API错误: {response.status_code} - {response.text[:100]}")
return None
except json.JSONDecodeError as e:
print(f" ⚠ JSON解析错误: {e}")
return None
except requests.exceptions.Timeout:
print(" ⚠ API请求超时")
return None
except Exception as e:
print(f" ⚠ 请求错误: {e}")
return None
def _build_prompt(self, items: List[Dict]) -> str:
"""构建分析提示词"""
# 简化数据,只保留必要字段
simplified = []
for item in items:
simplified.append({
"abb": item.get("abb", ""),
"project": item.get("project", ""),
"result": item.get("result", ""),
"point": item.get("point", ""),
"unit": item.get("unit", ""),
"reference": item.get("reference", "")
})
prompt = f"""请分析以下医疗检测数据,补充缺失的参考范围和单位,并判断结果是否正常:
{json.dumps(simplified, ensure_ascii=False, indent=2)}
要求:
1. 为每个项目补充完整的reference参考范围和unit单位
2. 根据result判断是否在正常范围内设置point字段正常为空偏高为"",偏低为""
3. 定性结果如Negative、Positive正常时point为空异常时根据具体情况标注
4. 保持原有的abb、project、result字段不变
请直接返回JSON数组格式不要添加任何说明文字"""
return prompt
def test_deepseek():
"""测试DeepSeek API"""
# 需要替换为实际的API Key
api_key = "YOUR_DEEPSEEK_API_KEY"
analyzer = DeepSeekAnalyzer(api_key)
# 测试数据
test_items = [
{"abb": "WBC", "project": "White Blood Cell", "result": "5.95", "point": "", "unit": "", "reference": ""},
{"abb": "PRO", "project": "Protein", "result": "Negative", "point": "", "unit": "", "reference": ""},
{"abb": "GLU", "project": "Glucose", "result": "6.5", "point": "", "unit": "", "reference": ""},
]
result = analyzer.analyze_medical_data(test_items)
print(json.dumps(result, ensure_ascii=False, indent=2))
def call_deepseek(prompt: str, api_key: str) -> str:
"""调用DeepSeek API通用接口"""
url = "https://api.deepseek.com/v1/chat/completions"
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
data = {
"model": "deepseek-chat",
"messages": [
{"role": "user", "content": prompt}
],
"temperature": 0.1,
"max_tokens": 8000
}
response = requests.post(url, headers=headers, json=data, timeout=120)
response.raise_for_status()
return response.json()["choices"][0]["message"]["content"]
def process_with_deepseek(ocr_data: list, template_abbs: list, api_key: str) -> dict:
"""让DeepSeek处理OCR数据并匹配到模板ABB"""
prompt = f"""你是医疗数据处理专家。请处理以下OCR提取的医疗检测数据并匹配到模板中的ABB。
## OCR提取的原始数据
```json
{json.dumps(ocr_data, ensure_ascii=False, indent=2)}
```
## 模板中需要填充的ABB列表
{template_abbs}
## 任务要求:
1. 清理OCR数据中的错误和噪音
2. 将每个有效数据项匹配到正确的模板ABB
3. 正确分离result结果、unit单位、reference参考范围
4. 对于尿检项目如PRO、GLU、KET、NIT等结果通常是Negative/Positive这是正确的定性结果
5. 过滤掉明显错误的数据如result为".""0"、空值等)
6. 如果同一个ABB有多条数据选择最合理的一条
## 输出格式:
请返回JSON格式结构如下
```json
{{
"ABB1": {{"result": "数值或定性结果", "unit": "单位", "reference": "参考范围", "point": "提示"}},
"ABB2": {{"result": "...", "unit": "...", "reference": "...", "point": ""}},
...
}}
```
只返回JSON不要其他说明文字。确保JSON格式正确可解析。
"""
print("正在调用DeepSeek处理数据...")
result = call_deepseek(prompt, api_key)
# 提取JSON
try:
# 尝试直接解析
return json.loads(result)
except:
# 尝试从markdown代码块提取
import re
json_match = re.search(r'```(?:json)?\s*([\s\S]*?)\s*```', result)
if json_match:
return json.loads(json_match.group(1))
raise ValueError(f"无法解析DeepSeek返回的JSON: {result[:500]}")
def process_ocr_data_main():
"""
处理OCR数据的主函数原deepseek_process.py的main函数
"""
from pathlib import Path
import os
# 从环境变量获取API Key
api_key = os.environ.get("DEEPSEEK_API_KEY", "")
if not api_key:
api_key = input("请输入DeepSeek API Key: ").strip()
if not api_key:
print("❌ API Key不能为空")
return
# 加载OCR数据
ocr_file = Path(__file__).parent / "extracted_medical_data.json"
if not ocr_file.exists():
print("❌ 未找到OCR数据文件")
return
with open(ocr_file, 'r', encoding='utf-8') as f:
data = json.load(f)
ocr_items = data.get('items', data) if isinstance(data, dict) else data
print(f"加载 {len(ocr_items)} 条OCR数据")
# 加载模板ABB配置
from config import load_abb_config
config = load_abb_config()
template_abbs = config.get('abb_list', [])
print(f"模板中有 {len(template_abbs)} 个ABB")
# 调用DeepSeek处理
processed_data = process_with_deepseek(ocr_items, template_abbs, api_key)
# 保存处理后的数据
output_file = Path(__file__).parent / "deepseek_processed_data.json"
with open(output_file, 'w', encoding='utf-8') as f:
json.dump(processed_data, f, ensure_ascii=False, indent=2)
print(f"✅ DeepSeek处理完成{len(processed_data)} 个有效项")
print(f"✅ 已保存到: {output_file}")
return processed_data
if __name__ == "__main__":
import sys
if len(sys.argv) > 1 and sys.argv[1] == '--process':
process_ocr_data_main()
else:
test_deepseek()

View File

@@ -0,0 +1,694 @@
"""
处理PDF中有但模板中没有的检测项目
- 识别额外项目
- 调用DeepSeek进行分类
- 在对应模块末尾插入表格
"""
import json
import requests
from typing import Dict, List, Tuple
from pathlib import Path
from docx import Document
from docx.shared import Pt, Cm
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.table import WD_TABLE_ALIGNMENT
from docx.oxml.ns import qn
from docx.oxml import OxmlElement
from copy import deepcopy
def clean_reference_range(reference: str) -> str:
"""清理参考范围格式:去掉括号,将<X转换为0-X"""
import re
if not reference:
return reference
ref = reference.strip()
# 去掉各种括号
if ref.startswith('(') and ref.endswith(')'):
ref = ref[1:-1]
elif ref.startswith('') and ref.endswith(''):
ref = ref[1:-1]
elif ref.startswith('[') and ref.endswith(']'):
ref = ref[1:-1]
if ref.startswith('('):
ref = ref[1:]
if ref.endswith(')'):
ref = ref[:-1]
if ref.startswith(''):
ref = ref[1:]
if ref.endswith(''):
ref = ref[:-1]
ref = ref.strip()
# 将 <X 或 ≤X 转换为 0-X 格式
match = re.match(r'^[<≤]\s*([\d\.]+)\s*$', ref)
if match:
upper_value = match.group(1)
ref = f"0-{upper_value}"
match = re.match(r'^<=\s*([\d\.]+)\s*$', ref)
if match:
upper_value = match.group(1)
ref = f"0-{upper_value}"
return ref.strip()
class ExtraItemsHandler:
"""处理模板中没有的额外检测项目"""
def __init__(self, api_key: str = None):
self.api_key = api_key
self.api_url = "https://api.deepseek.com/v1/chat/completions"
# 加载ABB配置
from config import load_abb_config
self.abb_config = load_abb_config()
# 构建已知ABB集合包括别名
self.known_abbs = set()
for abb in self.abb_config.get('abb_list', []):
self.known_abbs.add(abb.upper())
for alias in self.abb_config.get('abb_aliases', {}).keys():
self.known_abbs.add(alias.upper())
# 模块关键词映射(用于在文档中定位模块)
self.module_keywords = {
'Urine Test': ['urine detection', 'urine test', '尿液检测'],
'Complete Blood Count': ['complete blood count', 'blood count', '血常规'],
'Blood Sugar': ['blood sugar', 'glucose', '血糖'],
'Lipid Profile': ['lipid profile', 'lipid panel', '血脂'],
'Blood Type': ['blood type', '血型'],
'Blood Coagulation': ['blood coagulation', 'coagulation', '凝血功能'],
'Four Infectious Diseases': ['infectious disease', '传染病'],
'Serum Electrolytes': ['electrolyte', '电解质'],
'Liver Function': ['liver function', '肝功能'],
'Kidney Function': ['kidney function', '肾功能'],
'Myocardial Enzyme': ['myocardial enzyme', 'cardiac enzyme', '心肌酶'],
'Thyroid Function': ['thyroid function', '甲状腺'],
'Thromboembolism': ['thromboembolism', 'cardiovascular risk', '心脑血管'],
'Bone Metabolism': ['bone metabolism', '骨代谢'],
'Microelement': ['microelement', 'trace element', '微量元素'],
'Lymphocyte Subpopulation': ['lymphocyte subpopulation', '淋巴细胞亚群'],
'Humoral Immunity': ['humoral immunity', 'immunoglobulin', '体液免疫'],
'Inflammatory Reaction': ['inflammatory', 'inflammation', '炎症'],
'Autoantibody': ['autoantibody', '自身抗体'],
'Female Hormone': ['female hormone', '女性激素'],
'Male Hormone': ['male hormone', '男性激素'],
'Tumor Markers': ['tumor marker', '肿瘤标志物'],
'Imaging': ['imaging', '影像'],
'Female-specific': ['female-specific', 'gynecological', '女性专项'],
'Other Tests': ['other test', '其他检测']
}
def identify_extra_items(self, extracted_items: List[Dict]) -> List[Dict]:
"""
识别模板中没有的额外项目
Args:
extracted_items: OCR提取的所有项目
Returns:
额外项目列表
"""
extra_items = []
for item in extracted_items:
abb = item.get('abb', '').upper()
# 跳过空ABB
if not abb:
continue
# 检查是否在已知ABB中
if abb not in self.known_abbs:
extra_items.append(item)
print(f" 识别到 {len(extra_items)} 个额外项目(模板中没有)")
return extra_items
def classify_items_with_deepseek(self, extra_items: List[Dict]) -> Dict[str, List[Dict]]:
"""
使用DeepSeek对额外项目进行分类
Args:
extra_items: 额外项目列表
Returns:
{模块名: [项目列表]}
"""
if not extra_items:
return {}
if not self.api_key:
print(" ⚠️ 未配置DeepSeek API Key使用默认分类")
return self._default_classify(extra_items)
# 构建项目描述
items_desc = []
for item in extra_items:
desc = f"- ABB: {item.get('abb', '')}, 项目名: {item.get('project', '')}"
if item.get('result'):
desc += f", 结果: {item.get('result', '')}"
if item.get('unit'):
desc += f" {item.get('unit', '')}"
items_desc.append(desc)
# 获取可用模块列表
modules = list(self.abb_config.get('modules', {}).keys())
prompt = f"""你是医学检验专家,请将以下检测项目分类到对应的检测模块中。
## 待分类的检测项目:
{chr(10).join(items_desc)}
## 可用的检测模块:
{', '.join(modules)}
## 要求:
1. 根据项目的医学属性,将每个项目分配到最合适的模块
2. 如果项目不属于任何已有模块,分配到 "Other Tests"
3. 返回JSON格式
## 输出格式:
```json
{{
"模块名1": ["ABB1", "ABB2"],
"模块名2": ["ABB3"],
...
}}
```
只返回JSON不要其他说明。"""
try:
headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json"
}
response = requests.post(
self.api_url,
headers=headers,
json={
"model": "deepseek-chat",
"messages": [{"role": "user", "content": prompt}],
"temperature": 0.1,
"max_tokens": 2000
},
timeout=60
)
if response.status_code == 200:
content = response.json()['choices'][0]['message']['content']
# 解析JSON
if '```json' in content:
content = content.split('```json')[1].split('```')[0]
elif '```' in content:
content = content.split('```')[1].split('```')[0]
classification = json.loads(content.strip())
# 将ABB映射回完整项目数据
result = {}
abb_to_item = {item['abb'].upper(): item for item in extra_items}
for module, abbs in classification.items():
result[module] = []
for abb in abbs:
abb_upper = abb.upper()
if abb_upper in abb_to_item:
result[module].append(abb_to_item[abb_upper])
print(f" ✓ DeepSeek分类完成: {len(result)} 个模块")
return result
else:
print(f" ⚠️ DeepSeek API错误: {response.status_code}")
return self._default_classify(extra_items)
except Exception as e:
print(f" ⚠️ DeepSeek分类失败: {e}")
return self._default_classify(extra_items)
def _default_classify(self, extra_items: List[Dict]) -> Dict[str, List[Dict]]:
"""默认分类逻辑当DeepSeek不可用时"""
# 简单的关键词匹配分类
result = {'Other Tests': []}
keyword_to_module = {
'crp': 'Inflammatory Reaction',
'esr': 'Inflammatory Reaction',
'hs-crp': 'Inflammatory Reaction',
'tgab': 'Thyroid Function',
'tpoab': 'Thyroid Function',
'ery': 'Urine Test',
'cib': 'Microelement',
'mib': 'Microelement',
}
for item in extra_items:
abb_lower = item.get('abb', '').lower()
project_lower = item.get('project', '').lower()
classified = False
for keyword, module in keyword_to_module.items():
if keyword in abb_lower or keyword in project_lower:
if module not in result:
result[module] = []
result[module].append(item)
classified = True
break
if not classified:
result['Other Tests'].append(item)
# 移除空模块
result = {k: v for k, v in result.items() if v}
return result
def generate_clinical_significance(self, items: List[Dict]) -> Dict[str, Dict[str, str]]:
"""
为额外项目生成临床意义解释
Args:
items: 项目列表
Returns:
{ABB: {"clinical_en": "...", "clinical_cn": "..."}}
"""
if not items or not self.api_key:
return {}
items_desc = []
for item in items:
desc = f"- {item.get('abb', '')}: {item.get('project', '')}"
if item.get('result'):
desc += f", 结果: {item.get('result', '')}"
items_desc.append(desc)
prompt = f"""你是医学检验专家,请为以下检测项目生成简短的临床意义解释。
## 检测项目:
{chr(10).join(items_desc)}
## 要求:
1. 每个项目提供英文和中文解释
2. 解释简洁约30-50字
3. 说明该指标的临床意义
## 输出格式JSON
```json
{{
"ABB1": {{
"clinical_en": "English explanation...",
"clinical_cn": "中文解释..."
}}
}}
```
只返回JSON。"""
try:
headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json"
}
response = requests.post(
self.api_url,
headers=headers,
json={
"model": "deepseek-chat",
"messages": [{"role": "user", "content": prompt}],
"temperature": 0.1,
"max_tokens": 4000
},
timeout=60
)
if response.status_code == 200:
content = response.json()['choices'][0]['message']['content']
if '```json' in content:
content = content.split('```json')[1].split('```')[0]
elif '```' in content:
content = content.split('```')[1].split('```')[0]
return json.loads(content.strip())
except Exception as e:
print(f" ⚠️ 生成临床意义失败: {e}")
return {}
def find_module_position(self, doc: Document, module_name: str) -> int:
"""
在文档中找到指定模块的最后一个表格位置
Args:
doc: Word文档对象
module_name: 模块名称
Returns:
模块最后一个表格在body中的索引-1表示未找到
"""
keywords = self.module_keywords.get(module_name, [module_name.lower()])
body = doc._body._body
children = list(body)
module_start_idx = -1
module_end_idx = -1
# 找到模块开始位置
for i, elem in enumerate(children):
text = ''.join(elem.itertext()).strip().lower()
for kw in keywords:
if kw in text:
module_start_idx = i
break
if module_start_idx >= 0:
break
if module_start_idx < 0:
return -1
# 找到模块结束位置(下一个模块开始或文档结束)
all_module_keywords = []
for kws in self.module_keywords.values():
all_module_keywords.extend(kws)
for i in range(module_start_idx + 1, len(children)):
text = ''.join(children[i].itertext()).strip().lower()
# 检查是否是另一个模块的开始
for kw in all_module_keywords:
if kw in text and kw not in keywords:
module_end_idx = i
break
if module_end_idx >= 0:
break
if module_end_idx < 0:
module_end_idx = len(children)
# 在模块范围内找最后一个表格
last_table_idx = -1
for i in range(module_start_idx, module_end_idx):
if children[i].tag.endswith('}tbl'):
last_table_idx = i
return last_table_idx
def create_item_table(self, doc: Document, item: Dict, clinical_en: str = "", clinical_cn: str = "") -> any:
"""
创建单个检测项目的表格
Args:
doc: Word文档对象
item: 项目数据
clinical_en: 英文临床意义
clinical_cn: 中文临床意义
Returns:
创建的表格元素
"""
# 创建表格4行6列
table = doc.add_table(rows=4, cols=6)
table.alignment = WD_TABLE_ALIGNMENT.CENTER
table.autofit = False
# 设置列宽
widths = [Cm(2.5), Cm(3.5), Cm(2.5), Cm(2.5), Cm(2.5), Cm(2.5)]
for row in table.rows:
for idx, width in enumerate(widths):
row.cells[idx].width = width
def set_font(run, bold=False, font_size=10.5):
run.bold = bold
run.font.name = 'Times New Roman'
run.font.size = Pt(font_size)
run._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')
# Row 0: 空行(顶部边框)
row0 = table.rows[0]
row0.height = Cm(0.05)
for cell in row0.cells:
cell.text = ''
# Row 1: 表头
header_row = table.rows[1]
headers = [
('Abb', '简称'), ('Project', '项目'), ('Result', '结果'),
('Point', '提示'), ('Refer', '参考'), ('Unit', '单位')
]
for idx, (en, cn) in enumerate(headers):
p = header_row.cells[idx].paragraphs[0]
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
run = p.add_run(f'{en}\n{cn}')
set_font(run, bold=True, font_size=9)
# Row 2: 数据行
data_row = table.rows[2]
# ABB
p = data_row.cells[0].paragraphs[0]
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
run = p.add_run(item.get('abb', ''))
set_font(run, bold=True)
# 项目名
p = data_row.cells[1].paragraphs[0]
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
run = p.add_run(item.get('project', ''))
set_font(run, bold=True)
# 结果
p = data_row.cells[2].paragraphs[0]
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
run = p.add_run(str(item.get('result', '')))
set_font(run)
# Point
p = data_row.cells[3].paragraphs[0]
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
run = p.add_run(item.get('point', ''))
set_font(run)
# 参考范围
p = data_row.cells[4].paragraphs[0]
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
run = p.add_run(clean_reference_range(item.get('reference', '')))
set_font(run, font_size=9)
# 单位
p = data_row.cells[5].paragraphs[0]
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
run = p.add_run(item.get('unit', ''))
set_font(run, font_size=9)
# Row 3: 临床意义(合并单元格)
sig_row = table.rows[3]
top_cell = sig_row.cells[0]
for i in range(1, 6):
top_cell.merge(sig_row.cells[i])
p = top_cell.paragraphs[0]
p.alignment = WD_ALIGN_PARAGRAPH.LEFT
if clinical_en:
run = p.add_run('Clinical Significance: ')
set_font(run, bold=True, font_size=9)
run = p.add_run(clinical_en)
set_font(run, font_size=9)
run = p.add_run('\n')
if clinical_cn:
run = p.add_run('临床意义:')
set_font(run, bold=True, font_size=9)
run = p.add_run(clinical_cn)
set_font(run, font_size=9)
# 设置边框
self._set_table_borders(table)
return table._tbl
def _set_table_borders(self, table):
"""设置表格边框样式"""
def set_cell_border(cell, **kwargs):
tc = cell._tc
tcPr = tc.get_or_add_tcPr()
tcBorders = OxmlElement('w:tcBorders')
for edge in ['top', 'left', 'bottom', 'right']:
if edge in kwargs:
element = OxmlElement(f'w:{edge}')
element.set(qn('w:val'), kwargs[edge].get('val', 'single'))
element.set(qn('w:sz'), str(kwargs[edge].get('sz', 4)))
element.set(qn('w:color'), kwargs[edge].get('color', '000000'))
tcBorders.append(element)
tcPr.append(tcBorders)
border_solid = {'val': 'single', 'sz': 4, 'color': '000000'}
border_dashed = {'val': 'dashed', 'sz': 4, 'color': 'AAAAAA'}
for i, row in enumerate(table.rows):
for cell in row.cells:
top = border_solid if i == 0 else border_dashed
set_cell_border(cell, top=top, bottom=border_dashed,
left=border_dashed, right=border_dashed)
cell.vertical_alignment = 1
def insert_extra_items_to_doc(self, doc_path: str, classified_items: Dict[str, List[Dict]],
explanations: Dict[str, Dict[str, str]] = None) -> str:
"""
将额外项目插入到文档对应模块末尾
Args:
doc_path: 文档路径
classified_items: {模块名: [项目列表]}
explanations: {ABB: {"clinical_en": "...", "clinical_cn": "..."}}
Returns:
处理后的文档路径
"""
if not classified_items:
print(" 没有额外项目需要插入")
return doc_path
explanations = explanations or {}
doc = Document(doc_path)
body = doc._body._body
inserted_count = 0
for module_name, items in classified_items.items():
if not items:
continue
print(f" 处理模块 [{module_name}]: {len(items)} 个项目")
# 找到模块位置
insert_pos = self.find_module_position(doc, module_name)
if insert_pos < 0:
print(f" ⚠️ 未找到模块 [{module_name}],跳过")
continue
# 为每个项目创建表格并插入
for item in items:
abb = item.get('abb', '').upper()
exp = explanations.get(abb, {})
clinical_en = exp.get('clinical_en', '')
clinical_cn = exp.get('clinical_cn', '')
# 创建表格
table_elem = self.create_item_table(doc, item, clinical_en, clinical_cn)
# 插入到指定位置后面
children = list(body)
if insert_pos < len(children):
children[insert_pos].addnext(table_elem)
insert_pos += 1 # 更新位置,下一个表格插入到这个后面
inserted_count += 1
print(f" ✓ 插入 {abb}")
# 保存文档
doc.save(doc_path)
print(f" ✓ 共插入 {inserted_count} 个额外项目表格")
return doc_path
def process_extra_items(extracted_items: List[Dict], doc_path: str, api_key: str = None) -> str:
"""
处理额外项目的主函数
Args:
extracted_items: OCR提取的所有项目
doc_path: 已填充的文档路径
api_key: DeepSeek API Key
Returns:
处理后的文档路径
"""
print("\n" + "=" * 60)
print("处理额外检测项目(模板中没有的项目)")
print("=" * 60)
handler = ExtraItemsHandler(api_key)
# 1. 识别额外项目
extra_items = handler.identify_extra_items(extracted_items)
if not extra_items:
print(" 没有额外项目需要处理")
return doc_path
print(f"\n 额外项目列表:")
for item in extra_items:
print(f" - {item.get('abb', '')}: {item.get('project', '')} = {item.get('result', '')}")
# 2. 使用DeepSeek分类
print("\n 正在分类...")
classified_items = handler.classify_items_with_deepseek(extra_items)
if classified_items:
print(f"\n 分类结果:")
for module, items in classified_items.items():
print(f" [{module}]: {[item.get('abb', '') for item in items]}")
# 3. 生成临床意义
print("\n 正在生成临床意义...")
explanations = handler.generate_clinical_significance(extra_items)
# 4. 插入到文档
print("\n 正在插入表格...")
result_path = handler.insert_extra_items_to_doc(doc_path, classified_items, explanations)
print("\n" + "=" * 60)
print("额外项目处理完成")
print("=" * 60)
return result_path
if __name__ == "__main__":
# 测试
import os
api_key = os.getenv("DEEPSEEK_API_KEY", "")
# 加载提取数据
extracted_file = Path(__file__).parent / "extracted_medical_data.json"
if extracted_file.exists():
with open(extracted_file, 'r', encoding='utf-8') as f:
data = json.load(f)
items = data.get('items', data) if isinstance(data, dict) else data
handler = ExtraItemsHandler(api_key)
extra_items = handler.identify_extra_items(items)
print(f"\n识别到 {len(extra_items)} 个额外项目:")
for item in extra_items:
print(f" - {item.get('abb', '')}: {item.get('project', '')}")
if extra_items and api_key:
classified = handler.classify_items_with_deepseek(extra_items)
print(f"\n分类结果:")
for module, items in classified.items():
print(f" [{module}]: {[item.get('abb', '') for item in items]}")

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,140 @@
"""
从模板文件中提取所有检测项目的临床意义
"""
from docx import Document
import json
import re
def extract_all_explanations():
doc = Document('template_complete.docx')
explanations = {}
# 遍历所有表格
for table_idx, table in enumerate(doc.tables):
rows = table.rows
if len(rows) < 2:
continue
# 检查是否是检测项目表格(通过表头判断)
header_text = ' '.join([cell.text.strip() for cell in rows[0].cells])
# 遍历每一行
current_abb = None
for row_idx, row in enumerate(rows):
cells = row.cells
if not cells:
continue
# 获取第一列文本通常是ABB
first_cell_text = cells[0].text.strip()
# 跳过表头行
if 'Abb' in first_cell_text or '简称' in first_cell_text:
continue
# 检查是否是ABB行短文本不是临床意义
if first_cell_text and len(first_cell_text) < 40:
if not first_cell_text.startswith('Clinical') and not first_cell_text.startswith('临床'):
# 可能是ABB
current_abb = first_cell_text
# 查找临床意义
for cell in cells:
text = cell.text.strip()
if 'Clinical Significance:' in text and '临床意义:' in text:
# 提取英文和中文
parts = text.split('临床意义:')
if len(parts) == 2:
en = parts[0].replace('Clinical Significance:', '').strip()
cn = parts[1].strip()
if current_abb and en and cn:
# 标准化ABB名称
abb_key = current_abb.upper().strip()
# 处理特殊字符
abb_key = abb_key.replace(' - ', '-').replace('', '(').replace('', ')')
if abb_key not in explanations:
explanations[abb_key] = {
'clinical_en': en,
'clinical_cn': cn
}
print(f'提取: {abb_key}')
return explanations
def main():
print('从模板提取临床意义...')
print('=' * 60)
template_explanations = extract_all_explanations()
print(f'\n从模板提取了 {len(template_explanations)} 个项目')
# 读取现有文件
try:
with open('template_explanations.json', 'r', encoding='utf-8') as f:
existing = json.load(f)
print(f'现有文件中有 {len(existing)} 个项目')
except:
existing = {}
print('创建新文件')
# 用模板内容更新(模板优先)
updated_count = 0
for abb, exp in template_explanations.items():
if abb not in existing or existing[abb] != exp:
existing[abb] = exp
updated_count += 1
# 保存
with open('template_explanations.json', 'w', encoding='utf-8') as f:
json.dump(existing, f, ensure_ascii=False, indent=2)
print(f'\n更新了 {updated_count} 个项目')
print(f'最终文件包含 {len(existing)} 个项目')
# 检查配置文件中的项目是否都有临床意义
print('\n' + '=' * 60)
print('检查配置文件中的项目覆盖情况...')
with open('abb_mapping_config.json', 'r', encoding='utf-8') as f:
config = json.load(f)
config_abbs = set()
for module_name, module_data in config.get('modules', {}).items():
for item in module_data.get('items', []):
abb = item.get('abb', '').upper().strip()
abb = abb.replace(' - ', '-').replace('', '(').replace('', ')')
config_abbs.add(abb)
# 检查缺失
missing = []
for abb in config_abbs:
if abb not in existing:
# 尝试一些变体
found = False
variants = [
abb,
abb.replace('-', ' '),
abb.replace(' ', '-'),
abb.replace('%', ''),
abb + ' COUNT',
abb + ' TYPE',
]
for v in variants:
if v in existing:
found = True
break
if not found:
missing.append(abb)
if missing:
print(f'\n缺失临床意义的项目 ({len(missing)}):')
for abb in sorted(missing):
print(f' {abb}')
else:
print('\n所有配置项目都有临床意义!')
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,989 @@
{
"total_items": 108,
"items": [
{
"abb": "ALB",
"project": "白蛋白",
"result": "10",
"point": "",
"unit": "mg/L",
"reference": "<=20",
"source": "1125041700091(1).pdf"
},
{
"abb": "pH",
"project": "酸碱度",
"result": "6.5",
"point": "",
"unit": "",
"reference": "4.5-8",
"source": "1125041700091(1).pdf"
},
{
"abb": "WBC",
"project": "白细胞计数(WBC)",
"result": "5.1",
"point": "",
"unit": "x10^9/L",
"reference": "3.5-9.5",
"source": "1125041700091(1).pdf"
},
{
"abb": "NEUT%",
"project": "中性粒细胞百分率(NEUT%)",
"result": "43.9",
"point": "",
"unit": "%",
"reference": "40-75",
"source": "1125041700091(1).pdf"
},
{
"abb": "LYMPH%",
"project": "淋巴细胞百分率(LYMPH%)",
"result": "45.7",
"point": "",
"unit": "%",
"reference": "20-50",
"source": "1125041700091(1).pdf"
},
{
"abb": "MONO%",
"project": "单核细胞百分率(MONO%)",
"result": "7.5",
"point": "",
"unit": "%",
"reference": "3-10",
"source": "1125041700091(1).pdf"
},
{
"abb": "EOS%",
"project": "嗜酸性粒细胞百分率(EO%)",
"result": "2.3",
"point": "",
"unit": "%",
"reference": "0.4-8",
"source": "1125041700091(1).pdf"
},
{
"abb": "BAS%",
"project": "嗜碱性粒细胞百分率(BASO%)",
"result": "0.6",
"point": "",
"unit": "%",
"reference": "<=1",
"source": "1125041700091(1).pdf"
},
{
"abb": "NEUT",
"project": "中性粒细胞数(NEUT#)",
"result": "2.3",
"point": "",
"unit": "x10^9/L",
"reference": "1.8-6.3",
"source": "1125041700091(1).pdf"
},
{
"abb": "LYMPH",
"project": "淋巴细胞数(LYMPH#)",
"result": "2.4",
"point": "",
"unit": "x10^9/L",
"reference": "1.1-3.2",
"source": "1125041700091(1).pdf"
},
{
"abb": "MONO",
"project": "单核细胞数(MONO#)",
"result": "0.39",
"point": "",
"unit": "x10^9/L",
"reference": "0.1-0.6",
"source": "1125041700091(1).pdf"
},
{
"abb": "EOS",
"project": "嗜酸性粒细胞数(EO#)",
"result": "0.12",
"point": "",
"unit": "x10^9/L",
"reference": "0.02-0.52",
"source": "1125041700091(1).pdf"
},
{
"abb": "BAS",
"project": "嗜碱性粒细胞数(BASO#)",
"result": "0.03",
"point": "",
"unit": "x10^9/L",
"reference": "<=0.06",
"source": "1125041700091(1).pdf"
},
{
"abb": "RBC",
"project": "红细胞计数(RBC)",
"result": "3.77",
"point": "↓",
"unit": "x10^12/L",
"reference": "4.3-5.8",
"source": "1125041700091(1).pdf"
},
{
"abb": "Hb",
"project": "血红蛋白量(HGB)",
"result": "123",
"point": "↓",
"unit": "g/L",
"reference": "130-175",
"source": "1125041700091(1).pdf"
},
{
"abb": "HCT",
"project": "红细胞比积(HCT)",
"result": "38",
"point": "↓",
"unit": "%",
"reference": "40-50",
"source": "1125041700091(1).pdf"
},
{
"abb": "MCV",
"project": "平均红细胞体积(MCV)",
"result": "100",
"point": "",
"unit": "fL",
"reference": "82-100",
"source": "1125041700091(1).pdf"
},
{
"abb": "MCH",
"project": "平均红细胞血红蛋白量(MCH)",
"result": "33",
"point": "",
"unit": "pg",
"reference": "27-34",
"source": "1125041700091(1).pdf"
},
{
"abb": "MCHC",
"project": "平均红细胞血红蛋白浓度(MCHC)",
"result": "326",
"point": "",
"unit": "g/L",
"reference": "316-354",
"source": "1125041700091(1).pdf"
},
{
"abb": "RDW-SD",
"project": "红细胞分布宽度-标准差(RDW-SD)",
"result": "45",
"point": "",
"unit": "fL",
"reference": "37-50",
"source": "1125041700091(1).pdf"
},
{
"abb": "RDW",
"project": "红细胞分布宽度-变异系数(RDW-CV)",
"result": "12.0",
"point": "",
"unit": "%",
"reference": "11.6-14.4",
"source": "1125041700091(1).pdf"
},
{
"abb": "PLT",
"project": "血小板计数(PLT)",
"result": "163",
"point": "",
"unit": "x10^9/L",
"reference": "125-350",
"source": "1125041700091(1).pdf"
},
{
"abb": "PCT",
"project": "血小板比积(PCT)",
"result": "0.18",
"point": "",
"unit": "%",
"reference": "0.17-0.35",
"source": "1125041700091(1).pdf"
},
{
"abb": "MPV",
"project": "平均血小板体积(MPV)",
"result": "10.9",
"point": "",
"unit": "fL",
"reference": "9-13",
"source": "1125041700091(1).pdf"
},
{
"abb": "PDW",
"project": "血小板分布宽度(PDW)",
"result": "16.0",
"point": "",
"unit": "fL",
"reference": "9-17",
"source": "1125041700091(1).pdf"
},
{
"abb": "P-LCR",
"project": "大型血小板比率(P-LCR)",
"result": "31",
"point": "",
"unit": "%",
"reference": "13-43",
"source": "1125041700091(1).pdf"
},
{
"abb": "TBil",
"project": "总胆红素",
"result": "8.3",
"point": "",
"unit": "umol/L",
"reference": "3-26",
"source": "1125041700091(1).pdf"
},
{
"abb": "DBil",
"project": "直接胆红素",
"result": "1.7",
"point": "",
"unit": "umol/L",
"reference": "<=7",
"source": "1125041700091(1).pdf"
},
{
"abb": "IBil",
"project": "间接胆红素",
"result": "6.6",
"point": "",
"unit": "umol/L",
"reference": "1.7-17",
"source": "1125041700091(1).pdf"
},
{
"abb": "TP",
"project": "总蛋白",
"result": "72.4",
"point": "",
"unit": "g/L",
"reference": "65-85",
"source": "1125041700091(1).pdf"
},
{
"abb": "ALB",
"project": "白蛋白",
"result": "44.8",
"point": "",
"unit": "g/L",
"reference": "40-55",
"source": "1125041700091(1).pdf"
},
{
"abb": "GLB",
"project": "球蛋白",
"result": "27.6",
"point": "",
"unit": "g/L",
"reference": "20-40",
"source": "1125041700091(1).pdf"
},
{
"abb": "A/G",
"project": "白球比值",
"result": "1.6",
"point": "",
"unit": "",
"reference": "1.2-2.4",
"source": "1125041700091(1).pdf"
},
{
"abb": "CHE",
"project": "胆碱酯酶",
"result": "290",
"point": "",
"unit": "U/L",
"reference": "203-460",
"source": "1125041700091(1).pdf"
},
{
"abb": "ALT",
"project": "谷丙转氨酶",
"result": "16",
"point": "",
"unit": "U/L",
"reference": "9-50",
"source": "1125041700091(1).pdf"
},
{
"abb": "AST",
"project": "谷草转氨酶",
"result": "25",
"point": "",
"unit": "U/L",
"reference": "15-40",
"source": "1125041700091(1).pdf"
},
{
"abb": "GGT",
"project": "γ-谷氨酰基转移酶",
"result": "22",
"point": "",
"unit": "U/L",
"reference": "10-60",
"source": "1125041700091(1).pdf"
},
{
"abb": "ALP",
"project": "碱性磷酸酶",
"result": "61",
"point": "",
"unit": "U/L",
"reference": "45-125",
"source": "1125041700091(1).pdf"
},
{
"abb": "Tf",
"project": "转铁蛋白",
"result": "2.43",
"point": "",
"unit": "g/L",
"reference": "2.00-3.60",
"source": "1125041700091(1).pdf"
},
{
"abb": "Tf",
"project": "转铁蛋白",
"result": "43.57",
"point": "",
"unit": "mg/L",
"reference": "25.80-65.70",
"source": "1125041700091(1).pdf"
},
{
"abb": "CysC",
"project": "胱抑素C",
"result": "0.90",
"point": "",
"unit": "mg/L",
"reference": "0.55-1.05",
"source": "1125041700091(1).pdf"
},
{
"abb": "β2-MG",
"project": "血清β2微球蛋白",
"result": "1.8",
"point": "",
"unit": "mg/L",
"reference": "1.0-2.3",
"source": "1125041700091(1).pdf"
},
{
"abb": "ALB",
"project": "白蛋白",
"result": "1.0",
"point": "",
"unit": "mg/L",
"reference": "<=20",
"source": "1125041700091(1).pdf"
},
{
"abb": "GLB",
"project": "球蛋白",
"result": "70",
"point": "",
"unit": "ug/L",
"reference": "<=300",
"source": "1125041700091(1).pdf"
},
{
"abb": "TG",
"project": "甘油三酯",
"result": "1.37",
"point": "",
"unit": "mmol/L",
"reference": "0.45-1.69",
"source": "1125041700091(1).pdf"
},
{
"abb": "TC",
"project": "总胆固醇",
"result": "4.67",
"point": "",
"unit": "mmol/L",
"reference": "2.33-5.17",
"source": "1125041700091(1).pdf"
},
{
"abb": "HDL",
"project": "高密度脂蛋白胆固醇",
"result": "1.52",
"point": "",
"unit": "mmol/L",
"reference": "0.91-2.06",
"source": "1125041700091(1).pdf"
},
{
"abb": "LDL",
"project": "低密度脂蛋白胆固醇",
"result": "2.50",
"point": "",
"unit": "mmol/L",
"reference": "2.07-3.36",
"source": "1125041700091(1).pdf"
},
{
"abb": "FFA",
"project": "游离脂肪酸",
"result": "0.66",
"point": "",
"unit": "mmol/L",
"reference": "0.1-0.77",
"source": "1125041700091(1).pdf"
},
{
"abb": "INS",
"project": "胰岛素(空腹)",
"result": "8.3",
"point": "",
"unit": "μU/ml",
"reference": "2.6-24.9",
"source": "1125041700091(1).pdf"
},
{
"abb": "FBS",
"project": "葡萄糖(空腹)",
"result": "5.41",
"point": "",
"unit": "mmol/L",
"reference": "3.9-6.1",
"source": "1125041700091(1).pdf"
},
{
"abb": "HbA1C",
"project": "糖化血红蛋白",
"result": "5.6",
"point": "",
"unit": "%",
"reference": "4.0-6.5",
"source": "1125041700091(1).pdf"
},
{
"abb": "CK",
"project": "肌酸激酶",
"result": "136",
"point": "",
"unit": "U/L",
"reference": "50-310",
"source": "1125041700091(1).pdf"
},
{
"abb": "CK-MB",
"project": "肌酸激酶同工酶MB",
"result": "11",
"point": "",
"unit": "U/L",
"reference": "<=24",
"source": "1125041700091(1).pdf"
},
{
"abb": "hs-CRP",
"project": "超敏C反应蛋白",
"result": "0.5",
"point": "",
"unit": "mg/L",
"reference": "<=3.0",
"source": "1125041700091(1).pdf"
},
{
"abb": "Hcy",
"project": "同型半胱氨酸",
"result": "9.7",
"point": "",
"unit": "umol/L",
"reference": "<=15",
"source": "1125041700091(1).pdf"
},
{
"abb": "Lp(a)",
"project": "脂蛋白(a)",
"result": "26",
"point": "",
"unit": "mg/L",
"reference": "<=300",
"source": "1125041700091(1).pdf"
},
{
"abb": "Tg",
"project": "甲状腺球蛋白",
"result": "8.8",
"point": "",
"unit": "ng/ml",
"reference": "3.5-77.0",
"source": "1125041700091(1).pdf"
},
{
"abb": "T3",
"project": "三碘甲状腺原氨酸T3",
"result": "1.31",
"point": "",
"unit": "nmol/L",
"reference": "1.3-2.4",
"source": "1125041700091(1).pdf"
},
{
"abb": "T4",
"project": "甲状腺素T4",
"result": "99.0",
"point": "",
"unit": "nmol/L",
"reference": "70-140",
"source": "1125041700091(1).pdf"
},
{
"abb": "FT3",
"project": "游离三碘甲状腺原氨酸FT3",
"result": "4.35",
"point": "",
"unit": "pmol/L",
"reference": "3.82-6.30",
"source": "1125041700091(1).pdf"
},
{
"abb": "FT4",
"project": "游离甲状腺素FT4",
"result": "14.30",
"point": "",
"unit": "pmol/L",
"reference": "12.80-21.30",
"source": "1125041700091(1).pdf"
},
{
"abb": "TSH",
"project": "促甲状腺素TSH",
"result": "1.55",
"point": "",
"unit": "mIU/L",
"reference": "0.75-5.60",
"source": "1125041700091(1).pdf"
},
{
"abb": "TgAb",
"project": "抗甲状腺球蛋白抗体",
"result": "16.5",
"point": "",
"unit": "IU/ml",
"reference": "<115.0",
"source": "1125041700091(1).pdf"
},
{
"abb": "TPO-Ab",
"project": "抗甲状腺过氧化物酶抗体",
"result": "13.1",
"point": "",
"unit": "IU/ml",
"reference": "<34.0",
"source": "1125041700091(1).pdf"
},
{
"abb": "PGI",
"project": "胃蛋白酶原I",
"result": "98.4",
"point": "",
"unit": "ng/ml",
"reference": ">=30",
"source": "1125041700091(1).pdf"
},
{
"abb": "G-17",
"project": "胃泌素-17",
"result": "2.9",
"point": "",
"unit": "pmol/L",
"reference": "1.7-7.6",
"source": "1125041700091(1).pdf"
},
{
"abb": "PGII",
"project": "胃蛋白酶原Ⅱ",
"result": "11.1",
"point": "",
"unit": "ng/ml",
"reference": "",
"source": "1125041700091(1).pdf"
},
{
"abb": "PGR",
"project": "胃蛋白酶原比值",
"result": "8.9",
"point": "",
"unit": "",
"reference": ">=3",
"source": "1125041700091(1).pdf"
},
{
"abb": "HBsAg",
"project": "乙肝表面抗原",
"result": "0.87",
"point": "",
"unit": "COI",
"reference": "<1.0",
"source": "1125041700091(1).pdf"
},
{
"abb": "HBsAb",
"project": "乙肝表面抗体",
"result": "<2.00",
"point": "",
"unit": "IU/L",
"reference": "<10.0",
"source": "1125041700091(1).pdf"
},
{
"abb": "HBeAg",
"project": "乙肝e抗原",
"result": "0.10",
"point": "",
"unit": "COI",
"reference": "<1.0",
"source": "1125041700091(1).pdf"
},
{
"abb": "HBeAb",
"project": "乙肝e抗体",
"result": "1.40",
"point": "",
"unit": "COI",
"reference": ">1.0",
"source": "1125041700091(1).pdf"
},
{
"abb": "HBcAb",
"project": "乙肝核心抗体",
"result": "0.01",
"point": "",
"unit": "COI",
"reference": ">1.0",
"source": "1125041700091(1).pdf"
},
{
"abb": "HBcAb",
"project": "乙肝核心抗体",
"result": "阳性",
"point": "",
"unit": "",
"reference": "",
"source": "1125041700091(1).pdf"
},
{
"abb": "CRP",
"project": "C反应蛋白",
"result": "0.5",
"point": "",
"unit": "mg/L",
"reference": "<=6.0",
"source": "1125041700091(1).pdf"
},
{
"abb": "ASO",
"project": "抗链球菌溶血素\"0\"",
"result": "32",
"point": "",
"unit": "IU/ml",
"reference": "<=160",
"source": "1125041700091(1).pdf"
},
{
"abb": "ANA",
"project": "抗核抗体",
"result": "0.9",
"point": "",
"unit": "AU/ml",
"reference": "<40.0",
"source": "1125041700091(1).pdf"
},
{
"abb": "RF",
"project": "类风湿因子",
"result": "5",
"point": "",
"unit": "IU/ml",
"reference": "<=20",
"source": "1125041700091(1).pdf"
},
{
"abb": "PTH",
"project": "甲状旁腺素",
"result": "5.9",
"point": "",
"unit": "pmol/L",
"reference": "1.6-6.9",
"source": "1125041700091(1).pdf"
},
{
"abb": "OST",
"project": "骨钙素",
"result": "15.4",
"point": "",
"unit": "ng/ml",
"reference": "5.58-28.62",
"source": "1125041700091(1).pdf"
},
{
"abb": "VitB12",
"project": "维生素B12",
"result": "497",
"point": "",
"unit": "pmol/L",
"reference": "145-569",
"source": "1125041700091(1).pdf"
},
{
"abb": "Fer",
"project": "血清铁蛋白",
"result": "86",
"point": "",
"unit": "ng/ml",
"reference": "31.3-408.5",
"source": "1125041700091(1).pdf"
},
{
"abb": "Folate",
"project": "维生素B9(叶酸)血药浓度测定",
"result": "12.18",
"point": "",
"unit": "ng/ml",
"reference": ">4",
"source": "1125041700091(1).pdf"
},
{
"abb": "25-OH-VD2+D3",
"project": "25-羟基维生素D血药浓度测定",
"result": "19.91",
"point": "↓",
"unit": "ng/ml",
"reference": "30-100",
"source": "1125041700091(1).pdf"
},
{
"abb": "VitA",
"project": "维生素A血药浓度测定",
"result": "564.54",
"point": "",
"unit": "ng/ml",
"reference": "325-780",
"source": "1125041700091(1).pdf"
},
{
"abb": "VD3",
"project": "25-羟基维生素D3血药浓度测定",
"result": "19.63",
"point": "",
"unit": "ng/ml",
"reference": "无参考范围",
"source": "1125041700091(1).pdf"
},
{
"abb": "VD2",
"project": "25-羟基维生素D2血药浓度测定",
"result": "0.28",
"point": "",
"unit": "ng/ml",
"reference": "无参考范围",
"source": "1125041700091(1).pdf"
},
{
"abb": "VitE",
"project": "维生素E血药浓度测定",
"result": "9.29",
"point": "",
"unit": "ug/ml",
"reference": "5--18",
"source": "1125041700091(1).pdf"
},
{
"abb": "VitB2",
"project": "维生素B2血药浓度测定",
"result": "7.90",
"point": "",
"unit": "ng/ml",
"reference": "2.33-14.69",
"source": "1125041700091(1).pdf"
},
{
"abb": "VitB1",
"project": "维生素B1血药浓度测定",
"result": "1.67",
"point": "↓",
"unit": "ng/ml",
"reference": "2.4-9.02",
"source": "1125041700091(1).pdf"
},
{
"abb": "VitB5",
"project": "维生素B5血药浓度测定",
"result": "50.25",
"point": "",
"unit": "ng/ml",
"reference": "12.9-253.1",
"source": "1125041700091(1).pdf"
},
{
"abb": "VitB3",
"project": "维生素B3血药浓度测定",
"result": "29.62",
"point": "",
"unit": "ng/ml",
"reference": "5.2-72.1",
"source": "1125041700091(1).pdf"
},
{
"abb": "VitB6",
"project": "维生素B6血药浓度测定",
"result": "4.67",
"point": "↓",
"unit": "ng/ml",
"reference": "4.9-30.9",
"source": "1125041700091(1).pdf"
},
{
"abb": "AFP",
"project": "甲胎蛋白",
"result": "0.5",
"point": "",
"unit": "ng/ml",
"reference": "<=7.0",
"source": "1125041700091(1).pdf"
},
{
"abb": "CEA",
"project": "癌胚抗原",
"result": "1.3",
"point": "",
"unit": "ng/ml",
"reference": "<=5",
"source": "1125041700091(1).pdf"
},
{
"abb": "CA19-9",
"project": "糖类抗原19-9",
"result": "9.9",
"point": "",
"unit": "U/ml",
"reference": "<=30",
"source": "1125041700091(1).pdf"
},
{
"abb": "CA72-4",
"project": "糖类抗原72-4",
"result": "2.6",
"point": "",
"unit": "U/ml",
"reference": "<6.9",
"source": "1125041700091(1).pdf"
},
{
"abb": "CA24-2",
"project": "糖类抗原24-2",
"result": "7.3",
"point": "",
"unit": "U/ml",
"reference": "<20.0",
"source": "1125041700091(1).pdf"
},
{
"abb": "CA50",
"project": "糖类抗原50",
"result": "8.0",
"point": "",
"unit": "U/ml",
"reference": "<25.0",
"source": "1125041700091(1).pdf"
},
{
"abb": "CA125",
"project": "糖类抗原125",
"result": "4.9",
"point": "",
"unit": "U/ml",
"reference": "<=24",
"source": "1125041700091(1).pdf"
},
{
"abb": "NSE",
"project": "神经元特异性烯醇化酶",
"result": "2.6",
"point": "",
"unit": "ng/ml",
"reference": "<16.3",
"source": "1125041700091(1).pdf"
},
{
"abb": "CYFRA21-1",
"project": "细胞角蛋白19片段",
"result": "1.6",
"point": "",
"unit": "ng/ml",
"reference": "<3.3",
"source": "1125041700091(1).pdf"
},
{
"abb": "ProGRP",
"project": "胃泌素释放肽前体",
"result": "30.0",
"point": "",
"unit": "pg/ml",
"reference": "28.3-74.4",
"source": "1125041700091(1).pdf"
},
{
"abb": "SCC",
"project": "鳞状细胞癌相关抗原",
"result": "2.32",
"point": "",
"unit": "ng/ml",
"reference": "<=2.7",
"source": "1125041700091(1).pdf"
},
{
"abb": "TPSA",
"project": "总前列腺特异抗原",
"result": "0.48",
"point": "",
"unit": "ng/ml",
"reference": "<=4",
"source": "1125041700091(1).pdf"
},
{
"abb": "FPSA",
"project": "游离前列腺特异抗原",
"result": "0.25",
"point": "",
"unit": "ng/ml",
"reference": "<=0.93",
"source": "1125041700091(1).pdf"
},
{
"abb": "F/TPSA",
"project": "游离PSA/总PSA",
"result": "0.52",
"point": "",
"unit": "",
"reference": "0.25-1",
"source": "1125041700091(1).pdf"
}
],
"pdf_files": {
"1125041700091(1).pdf": 1770859127.7919312
},
"patient_info": {
"name": "姚友胜",
"gender": "男性",
"age": "59",
"nation": "中国",
"exam_time": "2012-50-41",
"project": "功能医学检测套餐",
"report_time": "2026-02-12"
}
}

1044
backend/fill_with_docxtpl.py Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,962 @@
"""
整体健康情况分析模块 V2
按照功能医学整体观重新设计的报告分析生成器
核心原则:
1. 功能医学整体观视角,聚焦"系统功能平衡""机能趋势预判"
2. 专业、客观、严谨的语言风格
3. 只呈现"指标状态→功能提示→关注方向",不包含干预方案
"""
import json
import re
from typing import Dict, List, Any, Tuple
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
# 系统分类映射:将模块映射到四大系统
SYSTEM_MAPPING = {
# (I) 血液学与炎症状态
'Hematology': [
'Complete Blood Count', # 血常规
'Blood Coagulation', # 凝血功能
'Inflammatory Reaction', # 炎症反应
],
# (II) 荷尔蒙与内分泌调节
'Endocrine': [
'Thyroid Function', # 甲状腺功能
'Female Hormone', # 女性荷尔蒙
'Male Hormone', # 男性荷尔蒙
'Bone Metabolism', # 骨代谢
],
# (III) 免疫学与感染风险
'Immunology': [
'Four Infectious Diseases', # 传染病四项
'Lymphocyte Subpopulation', # 淋巴细胞亚群
'Humoral Immunity', # 体液免疫
'Autoantibody', # 自身抗体
],
# (IV) 营养与代谢状况
'Metabolism': [
'Blood Sugar', # 血糖
'Lipid Profile', # 血脂
'Liver Function', # 肝功能
'Kidney Function', # 肾功能
'Serum Electrolytes', # 血电解质
'Microelement', # 微量元素
'Urine Test', # 尿液检测
'Myocardial Enzyme', # 心肌酶谱
'Thromboembolism', # 心脑血管风险因子
'Tumor Markers', # 肿瘤标记物
],
}
# 系统中英文名称
SYSTEM_NAMES = {
'Hematology': {
'en': '(I) Hematology and Inflammatory Status',
'cn': '(一)血液学与炎症状态'
},
'Endocrine': {
'en': '(II) Hormonal and Endocrine Regulation',
'cn': '(二)荷尔蒙与内分泌调节'
},
'Immunology': {
'en': '(III) Immunology and Infection Risk',
'cn': '(三)免疫学与感染风险'
},
'Metabolism': {
'en': '(IV) Nutrition and Metabolic Profile',
'cn': '(四)营养与代谢状况'
},
}
def get_system_for_module(module_name: str) -> str:
"""根据模块名称获取所属系统"""
for system, modules in SYSTEM_MAPPING.items():
if module_name in modules:
return system
# 默认归入代谢系统
return 'Metabolism'
def classify_items_by_system(matched_data: dict, config: dict = None) -> Dict[str, Dict[str, List]]:
"""
将所有检测项目按四大系统分类
Returns:
{
'Hematology': {
'normal': [...], # 正常指标
'abnormal': [...], # 异常指标
'borderline': [...] # 临界指标
},
...
}
"""
from config import load_abb_config, normalize_abb
if config is None:
config = load_abb_config()
abb_to_info = config.get('abb_to_info', {})
result = {
'Hematology': {'normal': [], 'abnormal': [], 'borderline': []},
'Endocrine': {'normal': [], 'abnormal': [], 'borderline': []},
'Immunology': {'normal': [], 'abnormal': [], 'borderline': []},
'Metabolism': {'normal': [], 'abnormal': [], 'borderline': []},
}
for abb, data in matched_data.items():
point = data.get('point', '').strip()
result_val = data.get('result', '').strip()
reference = data.get('reference', '').strip()
unit = data.get('unit', '').strip()
# 获取模块信息
normalized_abb = normalize_abb(abb, config)
info = abb_to_info.get(normalized_abb.upper(), {})
if not info:
info = abb_to_info.get(abb.upper(), {})
module = info.get('module', data.get('module', ''))
system = get_system_for_module(module)
# 获取中文名称
name = info.get('project_cn') or data.get('project_cn') or info.get('project') or data.get('project', abb)
item_info = {
'abb': abb,
'name': name,
'result': result_val,
'unit': unit,
'reference': reference,
'point': point,
'module': module,
'system': system
}
# 分类:正常、异常、临界
if point in ['', '', 'H', 'L', '', '']:
# 判断是否是临界值(接近参考范围边界)
is_borderline = _is_borderline_value(result_val, reference, point)
if is_borderline:
result[system]['borderline'].append(item_info)
else:
result[system]['abnormal'].append(item_info)
else:
# 正常指标
if result_val: # 只添加有结果的项目
result[system]['normal'].append(item_info)
return result
def _is_borderline_value(result: str, reference: str, point: str) -> bool:
"""
判断是否是临界值偏离参考范围不超过10%
"""
try:
result_num = float(re.sub(r'[^\d.]', '', result))
# 解析参考范围
ref_match = re.search(r'([\d.]+)\s*[-~]\s*([\d.]+)', reference)
if ref_match:
ref_low = float(ref_match.group(1))
ref_high = float(ref_match.group(2))
if point in ['', 'H', '']:
# 偏高检查是否超出上限不超过10%
if ref_high > 0:
deviation = (result_num - ref_high) / ref_high
return 0 < deviation <= 0.1
elif point in ['', 'L', '']:
# 偏低检查是否低于下限不超过10%
if ref_low > 0:
deviation = (ref_low - result_num) / ref_low
return 0 < deviation <= 0.1
except:
pass
return False
def collect_all_items_for_assessment(matched_data: dict, api_key: str = None) -> Tuple[List, List, Dict]:
"""
收集所有指标用于健康评估
Returns:
(normal_items, abnormal_items, system_classified_data)
"""
from config import load_abb_config, normalize_abb
config = load_abb_config()
abb_to_info = config.get('abb_to_info', {})
normal_items = []
abnormal_items = []
for abb, data in matched_data.items():
point = data.get('point', '').strip()
result_val = data.get('result', '').strip()
reference = data.get('reference', '').strip()
unit = data.get('unit', '').strip()
if not result_val:
continue
# 获取项目信息
normalized_abb = normalize_abb(abb, config)
info = abb_to_info.get(normalized_abb.upper(), {})
if not info:
info = abb_to_info.get(abb.upper(), {})
module = info.get('module', data.get('module', ''))
name = info.get('project_cn') or data.get('project_cn') or info.get('project') or data.get('project', abb)
item_info = {
'abb': abb,
'name': name,
'result': result_val,
'unit': unit,
'reference': reference,
'point': point,
'module': module,
'system': get_system_for_module(module)
}
if point in ['', '', 'H', 'L', '', '']:
abnormal_items.append(item_info)
else:
normal_items.append(item_info)
# 按系统分类
system_data = classify_items_by_system(matched_data, config)
return normal_items, abnormal_items, system_data
def build_assessment_prompt(normal_items: List, abnormal_items: List, system_data: Dict) -> str:
"""
构建整体健康情况分析的 Prompt基于案例文档优化
"""
# 构建正常指标描述
normal_desc = []
for item in normal_items[:30]:
desc = f" - {item['name']} ({item['abb']}): {item['result']}"
if item.get('unit'):
desc += f" {item['unit']}"
if item.get('reference'):
desc += f" [参考: {item['reference']}]"
normal_desc.append(desc)
# 构建异常指标描述(按系统分组)
abnormal_by_system = {}
for item in abnormal_items:
system = item.get('system', 'Metabolism')
if system not in abnormal_by_system:
abnormal_by_system[system] = []
direction = '偏高' if item['point'] in ['', 'H', ''] else '偏低'
is_borderline = _is_borderline_value(item['result'], item.get('reference', ''), item['point'])
level = '临界' if is_borderline else '异常'
desc = f" - {item['name']} ({item['abb']}): {item['result']}"
if item.get('unit'):
desc += f" {item['unit']}"
desc += f" ({direction}, {level})"
if item.get('reference'):
desc += f" [参考: {item['reference']}]"
abnormal_by_system[system].append(desc)
# 构建系统分组的异常指标描述
system_abnormal_desc = []
for system_key, system_info in SYSTEM_NAMES.items():
items = abnormal_by_system.get(system_key, [])
if items:
system_abnormal_desc.append(f"\n{system_info['cn']}")
system_abnormal_desc.extend(items)
prompt = f"""# 角色设定
你是Be.U Med功能医学团队的资深医学顾问在功能医学、整体健康、抗衰老医学领域具有丰富的临床经验。
# 任务
根据体检者的血液检查报告,撰写"整体健康情况分析"报告。
# 检测数据
## 正常指标(部分)
{chr(10).join(normal_desc) if normal_desc else ' 暂无数据'}
## 异常/临界指标(按系统分类)
{chr(10).join(system_abnormal_desc) if system_abnormal_desc else ' 暂无异常指标'}
# 核心原则(必须严格遵守)
## 1. 段落格式(极其重要!)
- **每个段落必须先写英文,再写对应的中文**
- **第一段英文80-120词中文80-120字**
- **第二段英文80-100词中文约120字严格控制在110-130字之间**
- 不要英中混排,必须分开
## 2. 语言风格
- 专业、客观、严谨,体现功能医学视角
- 使用"提示""可能""建议""值得关注""需要注意"等引导词
- 禁用"必须""一定""保证""治愈"等绝对化表述
- 不做临床疾病诊断,聚焦功能状态分析
## 3. 核心指标判定
- **核心指标**:从医学角度判定各生理学系统的关键指标
- **异常项**:超出参考范围的指标 + 逼近临界值的指标
- 指标必须精准,标注具体数值及单位
# 文章结构(必须严格遵循)
## 总述概述2段
**第一段**:前半部分列重点正常项及数值,后半部分列重点异常项及数值
**第二段**:说明这些异常指标对整体健康的综合影响
## 四大系统分析固定顺序每个系统2段
### (I) Hematology and Inflammatory Status / (一)血液学与炎症状态
**第一段**:前半部分列该系统重点正常项及数值,后半部分列重点异常项及数值(含临界值)
**第二段**:说明该系统核心异常指标对其他生理系统的影响
### (II) Hormonal and Endocrine Regulation / (二)荷尔蒙与内分泌调节
**第一段**:前半部分列该系统重点正常项及数值,后半部分列重点异常项及数值(含临界值)
**第二段**:说明该系统核心异常指标对其他生理系统的影响
### (III) Immunology and Infection Risk / (三)免疫学与感染风险
**第一段**:前半部分列该系统重点正常项及数值,后半部分列重点异常项及数值(含临界值)
**第二段**:说明该系统核心异常指标对其他生理系统的影响
### (IV) Nutrition and Metabolic Profile / (四)营养与代谢状况
**第一段**:前半部分列该系统重点正常项及数值,后半部分列重点异常项及数值(含临界值)
**第二段**:说明该系统核心异常指标对其他生理系统的影响
## 结尾总结2段
**第一段 - 功能医学健康管理重点**:概括本次检测发现的核心健康管理重点
**第二段 - 个性化管理方向**:说明往哪个方向开展个性化健康管理
# 输出格式JSON
```json
{{
"overview": {{
"paragraph1": {{
"en": "英文80-120词前半部分列重点正常项及数值后半部分列重点异常项及数值...",
"cn": "中文80-120字对应翻译..."
}},
"paragraph2": {{
"en": "英文80-100词说明异常指标对整体健康的综合影响...",
"cn": "中文约120字对应翻译..."
}}
}},
"systems": [
{{
"key": "Hematology",
"title_en": "(I) Hematology and Inflammatory Status",
"title_cn": "(一)血液学与炎症状态",
"paragraph1": {{
"en": "英文80-120词前半部分列该系统重点正常项及数值后半部分列重点异常项及数值...",
"cn": "中文80-120字对应翻译..."
}},
"paragraph2": {{
"en": "英文80-100词说明该系统核心异常指标对其他生理系统的影响...",
"cn": "中文约120字对应翻译..."
}}
}},
{{
"key": "Endocrine",
"title_en": "(II) Hormonal and Endocrine Regulation",
"title_cn": "(二)荷尔蒙与内分泌调节",
"paragraph1": {{}},
"paragraph2": {{}}
}},
{{
"key": "Immunology",
"title_en": "(III) Immunology and Infection Risk",
"title_cn": "(三)免疫学与感染风险",
"paragraph1": {{}},
"paragraph2": {{}}
}},
{{
"key": "Metabolism",
"title_en": "(IV) Nutrition and Metabolic Profile",
"title_cn": "(四)营养与代谢状况",
"paragraph1": {{}},
"paragraph2": {{}}
}}
],
"conclusion": {{
"management_focus": {{
"en": "英文80-120词功能医学健康管理重点概括...",
"cn": "中文80-120字对应翻译..."
}},
"personalized_direction": {{
"en": "英文80-120词个性化管理方向说明...",
"cn": "中文80-120字对应翻译..."
}}
}}
}}
```
# 重要提示
1. **每个段落必须先英文后中文,不要混排**
2. **第一段英文80-120词中文80-120字**
3. **第二段英文80-100词中文约120字严格控制在110-130字**
4. **第一段结构:前半部分正常项+数值,后半部分异常项+数值**
5. **第二段结构:异常指标对其他系统的影响**
6. **结尾两段:功能医学管理重点 + 个性化管理方向**
7. **逼近临界值的指标也算作异常项**
8. **只返回JSON不要其他内容**"""
return prompt
def generate_health_assessment_v2(matched_data: dict, api_key: str, call_deepseek_api) -> dict:
"""
生成整体健康情况分析内容V2版本
Args:
matched_data: 匹配的检测数据
api_key: DeepSeek API Key
call_deepseek_api: API调用函数
Returns:
包含整体分析和系统分析的字典
"""
if not api_key:
print(" ⚠️ 未提供API Key跳过健康评估生成")
return {}
# 收集所有指标
normal_items, abnormal_items, system_data = collect_all_items_for_assessment(matched_data)
if not normal_items and not abnormal_items:
print(" ⚠️ 没有检测数据,跳过健康评估生成")
return {}
print(f" 📊 数据统计: 正常指标 {len(normal_items)} 个, 异常指标 {len(abnormal_items)}")
# 构建prompt
prompt = build_assessment_prompt(normal_items, abnormal_items, system_data)
def parse_json_response(response_text):
"""解析JSON响应"""
# 提取JSON部分
if '```json' in response_text:
response_text = response_text.split('```json')[1].split('```')[0]
elif '```' in response_text:
response_text = response_text.split('```')[1].split('```')[0]
response_text = response_text.strip()
try:
return json.loads(response_text)
except json.JSONDecodeError:
pass
# 尝试修复常见问题
if response_text.count('"') % 2 != 0:
response_text += '"'
open_braces = response_text.count('{') - response_text.count('}')
open_brackets = response_text.count('[') - response_text.count(']')
if open_brackets > 0:
if open_braces > 0:
response_text += '}' * open_braces
response_text += ']' * open_brackets
elif open_braces > 0:
response_text += '}' * open_braces
try:
return json.loads(response_text)
except json.JSONDecodeError:
return None
# 最多重试3次
for attempt in range(3):
try:
print(f" 🤖 调用DeepSeek生成整体健康分析... (第{attempt+1}次)")
response = call_deepseek_api(prompt, api_key, max_tokens=6000, timeout=180)
if response is None:
if attempt < 2:
print(f" ⚠️ API请求失败重试中...")
import time
time.sleep(3)
continue
result = parse_json_response(response)
# 检查新格式overview, systems或旧格式overall_analysis, system_analysis
if result and (result.get('overview') or result.get('systems') or
result.get('overall_analysis') or result.get('system_analysis')):
print(f" ✓ 成功生成整体健康分析")
return result
if attempt < 2:
print(f" ⚠️ 响应格式不完整,重试中...")
except Exception as e:
if attempt < 2:
print(f" ⚠️ 生成失败: {e},重试中...")
print(f" ✗ 生成整体健康分析失败")
return {}
def convert_v2_to_sections_format(v2_result: dict) -> dict:
"""
将V2格式转换为原有的sections格式以便复用现有的填充函数
新格式overview{paragraph1, paragraph2}, systems[], conclusion{management_focus, personalized_direction}
"""
sections = []
# 1. 总述部分2段不需要标题
overview = v2_result.get('overview', {})
if overview:
paragraphs = []
# 第一段:正常项+异常项
para1 = overview.get('paragraph1', {})
if para1.get('en') or para1.get('cn'):
paragraphs.append({
'en': para1.get('en', ''),
'cn': para1.get('cn', '')
})
# 第二段:异常指标对整体健康的影响
para2 = overview.get('paragraph2', {})
if para2.get('en') or para2.get('cn'):
paragraphs.append({
'en': para2.get('en', ''),
'cn': para2.get('cn', '')
})
if paragraphs:
sections.append({
'title_en': '',
'title_cn': '',
'paragraphs': paragraphs,
'is_overview': True
})
# 2. 四大系统分析
systems = v2_result.get('systems', [])
for system in systems:
paragraphs = []
# 第一段:正常项+异常项
para1 = system.get('paragraph1', {})
if para1.get('en') or para1.get('cn'):
paragraphs.append({
'en': para1.get('en', ''),
'cn': para1.get('cn', '')
})
# 第二段:异常指标对其他系统的影响
para2 = system.get('paragraph2', {})
if para2.get('en') or para2.get('cn'):
paragraphs.append({
'en': para2.get('en', ''),
'cn': para2.get('cn', '')
})
# 兼容旧格式paragraphs数组
if not paragraphs and system.get('paragraphs'):
for para in system.get('paragraphs', []):
if para.get('en') or para.get('cn'):
paragraphs.append({
'en': para.get('en', ''),
'cn': para.get('cn', '')
})
if paragraphs:
sections.append({
'title_en': system.get('title_en', ''),
'title_cn': system.get('title_cn', ''),
'paragraphs': paragraphs
})
# 3. 结尾总结2段
conclusion = v2_result.get('conclusion', {})
if conclusion:
paragraphs = []
# 第一段:功能医学健康管理重点
mgmt_focus = conclusion.get('management_focus', {})
if mgmt_focus.get('en') or mgmt_focus.get('cn'):
paragraphs.append({
'en': mgmt_focus.get('en', ''),
'cn': mgmt_focus.get('cn', '')
})
# 第二段:个性化管理方向
pers_dir = conclusion.get('personalized_direction', {})
if pers_dir.get('en') or pers_dir.get('cn'):
paragraphs.append({
'en': pers_dir.get('en', ''),
'cn': pers_dir.get('cn', '')
})
# 兼容旧格式直接en/cn
if not paragraphs and (conclusion.get('en') or conclusion.get('cn')):
paragraphs.append({
'en': conclusion.get('en', ''),
'cn': conclusion.get('cn', '')
})
if paragraphs:
sections.append({
'title_en': '',
'title_cn': '',
'paragraphs': paragraphs,
'is_conclusion': True
})
# 兼容旧格式overall_analysis, system_analysis[]
if not sections:
overall = v2_result.get('overall_analysis', {})
if overall:
paragraphs = []
summary = overall.get('summary', {})
if summary.get('en') or summary.get('cn'):
paragraphs.append({'en': summary.get('en', ''), 'cn': summary.get('cn', '')})
strength = overall.get('strength_indicators', {})
if strength.get('en') or strength.get('cn'):
paragraphs.append({'en': strength.get('en', ''), 'cn': strength.get('cn', '')})
abnormal = overall.get('abnormal_indicators', {})
if abnormal.get('en') or abnormal.get('cn'):
paragraphs.append({'en': abnormal.get('en', ''), 'cn': abnormal.get('cn', '')})
focus = overall.get('focus_direction', {})
if focus.get('en') or focus.get('cn'):
paragraphs.append({'en': focus.get('en', ''), 'cn': focus.get('cn', '')})
if paragraphs:
sections.append({
'title_en': overall.get('title_en', ''),
'title_cn': overall.get('title_cn', ''),
'paragraphs': paragraphs
})
system_analysis = v2_result.get('system_analysis', [])
for system in system_analysis:
paragraphs = []
for key in ['summary', 'strength_indicators', 'abnormal_indicators', 'focus_direction']:
item = system.get(key, {})
if item.get('en') or item.get('cn'):
paragraphs.append({'en': item.get('en', ''), 'cn': item.get('cn', '')})
if paragraphs:
sections.append({
'title_en': system.get('title_en', ''),
'title_cn': system.get('title_cn', ''),
'paragraphs': paragraphs
})
return {'sections': sections}
# ============================================================
# 文档填充函数
# ============================================================
def clean_markdown_formatting(text: str) -> str:
"""清理文本中的Markdown格式标记"""
if not text:
return text
text = re.sub(r'\*\*([^*]+)\*\*', r'\1', text)
text = re.sub(r'__([^_]+)__', r'\1', text)
text = re.sub(r'(?<!\*)\*([^*]+)\*(?!\*)', r'\1', text)
text = re.sub(r'(?<!_)_([^_]+)_(?!_)', r'\1', text)
text = re.sub(r'`([^`]+)`', r'\1', text)
return text
def create_formatted_paragraph_v2(text: str, is_title: bool = False, is_chinese: bool = False):
"""创建带格式的段落(无缩进)"""
text = clean_markdown_formatting(text)
p = OxmlElement('w:p')
pPr = OxmlElement('w:pPr')
# 设置段落样式
pStyle = OxmlElement('w:pStyle')
if is_chinese:
pStyle.set(qn('w:val'), '0') # 0中文正文样式
else:
pStyle.set(qn('w:val'), '00') # 0英语正文样式
pPr.append(pStyle)
# 显式清除所有缩进(覆盖样式中的默认缩进)
ind = OxmlElement('w:ind')
ind.set(qn('w:left'), '0')
ind.set(qn('w:right'), '0')
ind.set(qn('w:firstLine'), '0')
pPr.append(ind)
if is_title:
rPr_para = OxmlElement('w:rPr')
b = OxmlElement('w:b')
rPr_para.append(b)
bCs = OxmlElement('w:bCs')
rPr_para.append(bCs)
pPr.append(rPr_para)
p.append(pPr)
r = OxmlElement('w:r')
rPr = OxmlElement('w:rPr')
if is_title:
b = OxmlElement('w:b')
rPr.append(b)
bCs = OxmlElement('w:bCs')
rPr.append(bCs)
color = OxmlElement('w:color')
if is_chinese:
color.set(qn('w:val'), '000000')
else:
color.set(qn('w:val'), '767171')
color.set(qn('w:themeColor'), 'background2')
color.set(qn('w:themeShade'), '80')
rPr.append(color)
r.append(rPr)
t = OxmlElement('w:t')
t.set('{http://www.w3.org/XML/1998/namespace}space', 'preserve')
t.text = text
r.append(t)
p.append(r)
return p
def create_empty_paragraph_v2():
"""创建空段落(无缩进)"""
p = OxmlElement('w:p')
pPr = OxmlElement('w:pPr')
pStyle = OxmlElement('w:pStyle')
pStyle.set(qn('w:val'), '00')
pPr.append(pStyle)
# 显式清除所有缩进
ind = OxmlElement('w:ind')
ind.set(qn('w:left'), '0')
ind.set(qn('w:right'), '0')
ind.set(qn('w:firstLine'), '0')
pPr.append(ind)
p.append(pPr)
return p
def create_section_title_two_lines_v2(title_en: str, title_cn: str):
"""创建两行的模块标题(只创建非空的标题)"""
result = []
if title_en and title_en.strip():
p_en = create_formatted_paragraph_v2(title_en, is_title=True, is_chinese=False)
result.append(p_en)
if title_cn and title_cn.strip():
p_cn = create_formatted_paragraph_v2(title_cn, is_title=True, is_chinese=True)
result.append(p_cn)
return result
def fill_health_assessment_v2(doc, assessment_result: dict):
"""
将V2版本的健康评估内容填充到文档
Args:
doc: Word文档对象
assessment_result: generate_health_assessment_v2 返回的结果
"""
if not assessment_result:
print(" 健康评估内容为空,跳过填充")
return
# 转换为sections格式
sections_data = convert_v2_to_sections_format(assessment_result)
sections = sections_data.get('sections', [])
if not sections:
print(" 转换后的sections为空跳过填充")
return
body = doc.element.body
children = list(body)
# 查找 "Overall Health Assessment" 位置
overall_start = -1
for i, elem in enumerate(children):
text = ''.join(elem.itertext()).strip().lower()
if 'overall health' in text and 'assessment' in text:
overall_start = i
print(f" 找到Overall Health Assessment位置: {i}")
break
if overall_start < 0:
print(" 未找到Overall Health Assessment位置")
return
# 找到下一个主要区域的位置
next_section_pos = len(children)
end_keywords = ['medical intervention', '医学干预',
'functional medical health advice', '功能医学健康建议']
for i in range(overall_start + 1, len(children)):
text = ''.join(children[i].itertext()).strip().lower()
if any(kw in text for kw in end_keywords):
next_section_pos = i
print(f" 找到下一区域位置: {i}")
break
# 删除标题之后、下一区域之前的所有模板内容
children = list(body)
elements_to_remove = []
for i in range(overall_start + 1, min(next_section_pos, len(children))):
elem = children[i]
if elem.tag.endswith('}sectPr'):
continue
br_elem = elem.find('.//{http://schemas.openxmlformats.org/wordprocessingml/2006/main}br')
if br_elem is not None:
break_type = br_elem.get('{http://schemas.openxmlformats.org/wordprocessingml/2006/main}type')
if break_type == 'page':
continue
elements_to_remove.append(elem)
for elem in elements_to_remove:
try:
body.remove(elem)
except:
pass
if elements_to_remove:
print(f" 已删除 {len(elements_to_remove)} 个模板占位内容")
# 重新获取位置
children = list(body)
insert_pos = -1
for i, elem in enumerate(children):
text = ''.join(elem.itertext()).strip().lower()
if 'overall health' in text and 'assessment' in text:
insert_pos = i + 1
break
if insert_pos < 0:
print(" 无法确定插入位置")
return
# 插入新生成的内容
for idx, section in enumerate(sections):
title_en = section.get('title_en', '').strip()
title_cn = section.get('title_cn', '').strip()
paragraphs = section.get('paragraphs', [])
is_overview = section.get('is_overview', False)
is_conclusion = section.get('is_conclusion', False)
# 只在有标题的模块前插入空段落(总述和结尾没有标题,不需要空段落)
if idx > 0 and (title_en or title_cn):
empty_p = create_empty_paragraph_v2()
body.insert(insert_pos, empty_p)
insert_pos += 1
# 小节标题(只有当标题不为空时才插入)
if title_en or title_cn:
title_paragraphs = create_section_title_two_lines_v2(title_en, title_cn)
for title_p in title_paragraphs:
body.insert(insert_pos, title_p)
insert_pos += 1
# 段落内容
for para in paragraphs:
en_text = para.get('en', '')
if en_text:
p_en = create_formatted_paragraph_v2(en_text, is_chinese=False)
body.insert(insert_pos, p_en)
insert_pos += 1
cn_text = para.get('cn', '')
if cn_text:
p_cn = create_formatted_paragraph_v2(cn_text, is_chinese=True)
body.insert(insert_pos, p_cn)
insert_pos += 1
print(f" ✓ 已插入 {len(sections)} 个健康评估小节")
# ============================================================
# 主入口函数
# ============================================================
def generate_and_fill_health_assessment_v2(doc, matched_data: dict, api_key: str, call_deepseek_api):
"""
生成并填充整体健康情况分析V2版本
这是主入口函数,替代原有的 generate_health_assessment_content + fill_health_assessment_section
"""
if not api_key:
print(" ⚠️ 未提供DeepSeek API Key跳过健康评估生成")
return None
print("\n" + "=" * 60)
print("整体健康情况分析 V2")
print("=" * 60)
# 生成内容
assessment_result = generate_health_assessment_v2(matched_data, api_key, call_deepseek_api)
if assessment_result:
# 填充到文档
print("\n 📝 正在填充健康评估内容...")
fill_health_assessment_v2(doc, assessment_result)
print(" ✓ 整体健康情况分析完成")
else:
print(" ✗ 健康评估生成失败")
return assessment_result
# ============================================================
# 测试函数
# ============================================================
if __name__ == '__main__':
# 测试prompt构建
test_normal = [
{'abb': 'WBC', 'name': '白细胞计数', 'result': '6.5', 'unit': '10^9/L', 'reference': '4.0-10.0', 'system': 'Hematology'},
{'abb': 'RBC', 'name': '红细胞计数', 'result': '4.8', 'unit': '10^12/L', 'reference': '4.0-5.5', 'system': 'Hematology'},
]
test_abnormal = [
{'abb': 'TSH', 'name': '促甲状腺激素', 'result': '16.879', 'unit': 'μIU/mL', 'reference': '0.35-4.94', 'point': '', 'system': 'Endocrine'},
{'abb': 'AMH', 'name': '抗缪勒管激素', 'result': '0.17', 'unit': 'ng/mL', 'reference': '1.0-10.0', 'point': '', 'system': 'Endocrine'},
]
test_system_data = {
'Hematology': {'normal': test_normal, 'abnormal': [], 'borderline': []},
'Endocrine': {'normal': [], 'abnormal': test_abnormal, 'borderline': []},
'Immunology': {'normal': [], 'abnormal': [], 'borderline': []},
'Metabolism': {'normal': [], 'abnormal': [], 'borderline': []},
}
prompt = build_assessment_prompt(test_normal, test_abnormal, test_system_data)
print("=" * 60)
print("生成的Prompt预览前2000字符:")
print("=" * 60)
print(prompt[:2000])
print("...")

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,32 @@
"""列出配置文件中所有模块和项目"""
import json
with open('abb_mapping_config.json', 'r', encoding='utf-8') as f:
config = json.load(f)
modules = config.get('modules', {})
total = 0
print("=" * 80)
print("配置文件中的所有检测项目(按模块分类)")
print("=" * 80)
for name, data in modules.items():
cn_name = data.get('cn_name', '')
pages = data.get('pages', '')
items = data.get('items', [])
print(f"\n### {name} ({cn_name}) - 页码: {pages}")
print("-" * 60)
print(f"| {'序号':<4} | {'ABB':<20} | {'项目名称':<15} |")
print("-" * 60)
for i, item in enumerate(items, 1):
abb = item.get('abb', '')
project_cn = item.get('project_cn', '')
print(f"| {i:<4} | {abb:<20} | {project_cn:<15} |")
total += 1
print("\n" + "=" * 80)
print(f"总计: {len(modules)} 个模块, {total} 个检测项目")
print("=" * 80)

144
backend/main.py Normal file
View File

@@ -0,0 +1,144 @@
from fastapi import FastAPI, UploadFile, File, HTTPException, Form
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse
from typing import List
import os
import tempfile
from pathlib import Path
from dotenv import load_dotenv
# 加载环境变量
load_dotenv()
from services.ocr_service import OCRService
from services.llm_service import LLMService
from services.report_integrator import ReportIntegrator
from services.pdf_service import PDFService
from services.template_service import TemplateService
from services.batch_report_service import BatchReportService
from services.deepseek_health_service import DeepSeekHealthService
app = FastAPI(title="医疗报告分析系统")
# CORS配置
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:5173", "http://localhost:3000"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 初始化服务(仅用于综合报告流程)
ocr_service = OCRService()
llm_service = LLMService()
report_integrator = ReportIntegrator(llm_service)
pdf_service = PDFService()
template_service = TemplateService()
batch_service = BatchReportService(ocr_service, llm_service, pdf_service, template_service)
# 检查 DeepSeek 配置状态
deepseek_key = os.getenv("DEEPSEEK_API_KEY", "")
if deepseek_key:
print("✓ DeepSeek API Key 已配置,健康评估和建议功能已启用")
else:
print("⚠ DeepSeek API Key 未配置,健康评估和建议功能将被跳过")
@app.get("/")
async def root():
return {"message": "医疗报告分析系统API", "status": "running"}
@app.post("/api/comprehensive-report")
async def generate_comprehensive_report(
files: List[UploadFile] = File(...),
patient_name: str = Form(default="患者")
):
"""
批量上传并生成综合健康报告
注意:上传的文件不会永久存储,处理完后自动删除
"""
temp_files = []
try:
# 验证文件类型
allowed_extensions = {".pdf", ".jpg", ".jpeg", ".png", ".bmp"}
# 临时保存上传的文件
for file in files:
file_ext = Path(file.filename).suffix.lower()
if file_ext not in allowed_extensions:
raise HTTPException(
status_code=400,
detail=f"不支持的文件格式: {file.filename}"
)
# 创建临时文件
temp_file = tempfile.NamedTemporaryFile(
delete=False,
suffix=file_ext,
dir=str(batch_service.temp_dir)
)
# 写入文件内容
content = await file.read()
temp_file.write(content)
temp_file.close()
temp_files.append(temp_file.name)
# 批量处理报告
result = batch_service.process_multiple_reports(
file_paths=temp_files,
patient_name=patient_name
)
return {
"success": True,
"patient_name": result["patient_name"],
"report_count": result["report_count"],
"pdf_filename": Path(result["pdf_path"]).name,
"pdf_path": result["pdf_path"],
"generated_at": result["generated_at"],
# 包含健康评估和建议内容
"health_assessment": result.get("analysis", {}).get("health_assessment", {}),
"health_advice": result.get("analysis", {}).get("health_advice", {})
}
except HTTPException:
# 清理临时文件
batch_service._cleanup_temp_files(temp_files)
raise
except Exception as e:
# 清理临时文件
batch_service._cleanup_temp_files(temp_files)
import traceback
error_detail = f"综合报告生成失败: {str(e)}\n{traceback.format_exc()}"
print(error_detail) # 打印完整错误堆栈
raise HTTPException(status_code=500, detail=f"综合报告生成失败: {str(e)}")
@app.get("/api/comprehensive-report/download/{pdf_filename}")
async def download_comprehensive_report(pdf_filename: str):
"""下载综合健康报告"""
try:
pdf_path = pdf_service.output_dir / pdf_filename
if not pdf_path.exists():
raise HTTPException(status_code=404, detail="PDF文件不存在")
return FileResponse(
path=str(pdf_path),
media_type="application/pdf",
filename=pdf_filename
)
except Exception as e:
raise HTTPException(status_code=500, detail=f"下载失败: {str(e)}")
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8001)

File diff suppressed because it is too large Load Diff

878
backend/ocr_raw_chaonv2.txt Normal file
View File

@@ -0,0 +1,878 @@
Health ASSURANCK
lac-MRA
National Healthcare Systems Co.,Ltd DMSC
2301/2 New Petchburi Road,Soi 47 (Soonvijai) Bangkapi, Huaykwang, Bangkok 10310 Thailand
Tel.(662)762-4000 Fax.- ISO15189
Email:nhcslab@nhealth-asia.com Accreditation No.4120/47
LABORATORY REPORT
Patient Name: MR. SHUNHU YU
Sex Male DOB 03 Jun 1968 Age 57Y6M17D Address/Ref.No.: Be.U wellness center(Cash)(C1W)/CASH02/HN.000
Doctor: un 46-25
MRN.: 10A-25-213418
Lab No.: 10253540676 Requested Date : 20 Dec 2025 1836
Collected Date/Time: 20 Dec 2025
Received Date/Time 20 Dec 2025 1853
Test Name Result Unit Reference Range
(*)Complete Blood Count
Specimen......................: EDTA blood
Total WBC.....................: 7.84 *10^3/mm3 (4.00-10.00)
Red Blood Cells...............: 4.97 *10^6/mm3 (4.50-5.90)
Hemoglobin(Hb)................: 15.8 g/dL (13.0-18.0)
Hematocrit(HCT)...............: 46.6 (40.0-54.0)
Mean Cell Volume..............: 93.8 fL (80.0-100.0)
Mean Cell Hemoglobin..........: 31.8 pg (26.0-34.0)
Mean Cell Hb Concentration....: 33.9 g/dL (31.0-37.0)
RBC Distribution Width.......: 13.2 号 (9.0-15.0)
RBC Morphology................: No significant morphological abnormality seen.
WBC Differential
Neutrophils..................: 88.1H 号 (46.5-75.0)
6907 /mm3 (2000-7500)
Lymphocytes.................: 3.8L 号 (12.0-44.0)
298L /mm3 (1500-4000)
Monocytes...................: 5.4 号 (0.0-11.2)
423 /mm3 (200-1000)
Eosinophils..............: 2.2 (0.0-9.5)
172 /mm3 (40-700)
Basophil..................: 0.5 (0.0-2.5)
39 /mm3 (0-200)
Platelet Count..............: 155 10^3/mm3 (150-450)
Mean Platelet Volume..........: 10.3 fL (6.0-12.0)
Platelet Comment.............: Adequate
Remark:(H) means higher than reference values;(L) means lower than reference values;(*)ISO 15189 Accreditied
Reported by: Afnan Waedoloh,MT20602 on 20 Dec 2025 1944
Authorised by: Afnan Waedoloh,MT20602 on 20 Dec 2025 1944 Print Date and Time 20 Dec 2025 2011
This report has been approved electronically. Information contained in this document is CONFIDENTIAL. Copyright: Issued by N Health.
This report is only for the specimen (S) received on the above date: Copyright issued by N Health."DO NOT COPY"
This report present test resultsfor infomational purposes and is no a substitute for medical advice, diagnosis, r treatment. Variability in specimen quality, test kit performance,reagent integrity, and the
sensitivity and specificity of instruments may influence the findings. While every efrt is made to ensure the accuracy and timeliness of the infomation provided, results should be considered in the context of evolving clinical guidelines and standards. Personal data is handled following applicable data privacy laws.
Issue date 10/06/2025
FP-NLS-NHS-00-002/5 Revision 12 Page1/2
Health SSURANCR
HC 9
National Healthcare Systems Co., Ltd DMSC
2301/2 New Petchburi Road,Soi 47(Soonvijai)
Bangkapi,Huaykwang,Bangkok 10310,Thailand Tel.(662)762-4000 Fax.- ISO15189
Email:nhcslab@nhealth-asia.com Accreditation No.4120/47
LABORATORY REPORT
Patient Name: MR. SHUNHU YU
Sex Male DOB 03 Jun 1968 Age 57Y6M17D Address/Ref.No.: Be.U wellness center(Cash)(C1W)/CASH02/HN.000
Doctor:5n d
46-25
MRN.:10A-25-213418
LabNo.:10253540676 Requested Date :20 Dec 20251836
Collected Date/Time 20 Dec 2025
Received Date/Time: 20 Dec 2025 1853
Test Name Result Unit Reference Range
(*)ESR
Specimen......................: EDTA
ESR 1 Hour .................. 3 mm/hr (0-15)
Remark:(H)means higher than reference values;(L) means lower than reference values;(*)ISO 15189 Accreditied
Reported by: Kamonchat Pikulthong,MT23842 on 20 Dec 2025 2010
Authorised by: Kamonchat Pikulthong,MT23842 on 20 Dec 2025 2010 Print Date and Time 20 Dec 2025 2011
This report has been approved electronically. Information contained in this document is CONFIDENTIAL. Copyright: Issued by N Health.
This report is only for the specimen (S) received on the above date: Copyright issued by N Health."DO NOT COPY"
This report present test results fr infomational purposes and is not a substitute for medical advice, diagnosis, r treatment Variability in specimen quality, test kit perfomance, eagent integrity, and the
sensitivity and specifcity of nstruments may influence the findings. While every effort is made to ensure he acuracy and timelines of the infomation provided, results should be considere in the context of evolving clinical guidelines and standards. Personal data is handled following applicable data privacy laws.
FP-NLS-NHS-00-002/5 Revision 12 Issue date 10/06/2025
Page 2/2
Health
National Healthcare Systems Co., Ltd
2301/2 New Petchburi Road,Soi 47(Soonvijai)
Bangkapi,Huaykwang,Bangkok 10310,Thailand Tel.(662)762-4000 Fax. -
Email:nhcslab@nhealth-asia.com
LABORATORY REPORT
Patient Name: MR. SHUNHU YU
Sex Male DOB 03 Jun 1968 Age 57Y6M17D Address/Ref.No.: Be.U wellness center(Cash)(C1W)/CASH02/HN.000
Doctor:En d
46-25
MRN.: 10A-25-213418
Lab No.:10253540676 Requested Date : 20 Dec 20251836
Collected Date/Time: 20 Dec 2025
Received Date/Time: 20 Dec 2025 1853
Test Name Result Unit Reference Range
ASO(Anti-Streptolysin O Titre)
Anti Streptolysin O Titre(ASO): Less than 200 IU/mL (N Less than 200 IU/mL)
Remark:(H)means higher than reference values;(L) means lower than reference values;(*)ISO 15189 Accreditied
Reported by: Sarocha Manfak, MT16760 on 20 Dec 2025 2000
Authorised by: Nurhuda Suetoh,MT21824 on 20 Dec 2025 2003 Print Date and Time 20 Dec 2025 2003
This report has been approved electronically. Information contained in this document is CONFIDENTIAL. Copyright: Issued by N Health.
This report is only for the specimen(S) received on the above date: Copyright issued by N Health."DO NOT COPY"
This report presentstet results frinformational purposes ad is not a substitute formedical advice, diagnosis, ortreatment Variability in specimen quality, test kit perfomance,reagent integrity, and the
sensitivity and specificity of instruments may influence the findings. While every effort is made to ensure the acuracy and timeliness of the information provided, results should be considered in the context of evolving clinical guidelines and standards. Personal data is handled following applicable data privacy laws.
Issue date 10/06/2025
FP-NLS-NHS-00-002/5 Revision 12 Page 1/1
Health dUALITY 55URANC
lac MRA
National Healthcare Systems Co., Ltd DMSC
2301/2 New Petchburi Road,Soi47(Soonvijai)
Bangkapi,Huaykwang,Bangkok 10310,Thailand ISO15189
Tel.(662)762-4000 Fax.-
Email:nhcslab@nhealth-asia.com Accreditation No.4120/47
LABORATORY REPORT
Patient Name: MR. SHUNHU YU
Sex Male DOB: 03 Jun 1968 Age 57Y6M17D Address/Ref.No.: Be.U wellness center(Cash)(C1W)/CASH02/HN.000
Doctor: 46-25
MRN.: 10A-25-213418
Lab No.: 10253540676 Requested Date : 20 Dec 2025 1836
Collected Date/Time: 20 Dec 2025
Received Date/Time: 20 Dec 2025 1853
Test Name Result Unit Reference Range
(*)Urine Examination
Specimen......................: Urine
Physical Examination
Color.........................: Yellow [Normal: Yellow]
Transparency..................: Clear [Normal Clear]
Specific Gravity............: 1.020 (1.003-1.030)
pH............................: 8.5H (4.5-8.0)
Chemical Examination
Protein.......................: Negative [Normal Negative]
Glucose.......................: Negative [Normal Negative]
Ketone........................: Negative [Normal Negative]
Bilirubin.....................: Negative [Normal Negative]
Erythrocyte...................: Negative [Normal: Negative]
Urobilinogen..................: Negative [Normal Negative]
Nitrite.......................: Negative [Normal Negative]
Leucocyte.....................: Negative [Normal Negative]
Urine Sedimentation
WBC...........................: 0-1Cells/HPF [Normal:0-5 Cells/HPF]
RBC...........................: 0-1 Cells/HPF [Normal: 0-2 Cells/HPF]
Squamous Epithelial Cell....: 0-1 Cells/HPF [Normal: 0-5 Cells/HPF]
Remark:(H) means higher than reference values;(L) means lower than reference values;(*)ISO 15189 Accreditied
Reported by: Kamonchat Pikulthong,MT23842 on 20 Dec 2025 1947
Authorised by: Kamonchat Pikulthong,MT23842 on 20 Dec 2025 1947 Print Date and Time: 20 Dec 2025 1948
This report has been approved electronically. Information contained in this document is CONFIDENTIAL. Copyright: Issued by N Health.
This report is only for the specimen (S) received on the above date: Copyright issued by N Health."DO NOT COPY"
This report present test resultsfor infomational purposes and is nota substitute for medical advice, diagnosis, r treatment. Variability in specimen quality, test kit performane,reagent integrity, and the
sensitivity and specificity of instruments may influence the findings. While every efrtis made to ensure the accuracy and timeliness of the infomation provided,results should be considered in the
context of evolving clinical guidelines and standards. Personal data is handled following applicable data privacy laws.
Issue date 10/06/2025
FP-NLS-NHS-00-002/5 Revision 12 Page 1/1
Health 0 SSURANCA
lac-MRA
National Healthcare Systems Co., Ltd DMSC
2301/2 New Petchburi Road,Soi 47(Soonvijai)
Bangkapi,Huaykwang,Bangkok 10310,Thailand Tel.(662)762-4000 Fax.- ISO 15189
Email:nhcslab@nhealth-asia.com Accreditation No.4120/47
LABORATORY REPORT
Patient Name MR. SHUNHU YU
Sex: Male DOB 03 Jun 1968 Age 57Y6M17D Address/Ref.No.: Be.U wellness center(Cash)(C1W)/CASH02/HN.000
Doctor: 5n d
46-25
MRN.:10A-25-213418
Lab No.:10253540676 Requested Date : 20 Dec 2025 1836
Collected Date/Time 20 Dec 2025
Received Date/Time 20 Dec 2025 1853
Test Name Result Unit Reference Range
(*)Thrombin Time
Specimen type..............: 3.2 % Na citrate plasma
Thrombin Time(TT)..........: 16.8 Secs. (15.8-19.0)
Remark:(H) means higher than reference values;(L) means lower than reference values;(*)ISO 15189 Accreditied
Reported by: Afnan Waedoloh,MT20602 on 20 Dec 2025 2011
Authorised by: Afnan Waedoloh,MT20602 on 20 Dec 2025 2011 Print Date and Time 20 Dec 2025 2012
This report has been approved electronically. Information contained in this document is CONFIDENTIAL. Copyright: Issued by N Health.
This report is only for the specimen (S) received on the above date: Copyright issued by N Health."DO NOT COPY"
Thi report present test results fr infomational purposes ad is not a substitute formedical advice, diagnosis, ortreatment Variability in specimen quality, test kit perfomane, reagent integrity, an the
sensitivity and specificity of instruments may influence the findings. While every efrtis made to ensure the accuracy and timeliness of the infomation provided, results should be considered in the context of evolving clinical guidelines and standards. Personal data is handled following applicable data privacy laws.
Issue date 10/06/2025
FP-NLS-NHS-00-002/5 Revision 12 Page 1/4
Health Q BSRARON
ilaCMRA
National Healthcare Systems Co., Ltd DMSC
2301/2 New Petchburi Road,Soi47(Soonvijai)
Bangkapi,Huaykwang,Bangkok 10310,Thailand Tel.(662)762-4000 Fax.- ISO15189
Email:nhcslab@nhealth-asia.com Accreditation No.4120/47
LABORATORY REPORT
Patient Name: MR. SHUNHU YU
Sex Male DOB 03 Jun 1968 Age 57Y6M17D Address/Ref.No.: Be.U wellness center(Cash)(C1W)/CASH02/HN.000
Doctor: n 46-25
MRN.: 10A-25-213418
Lab No. 10253540676 Requested Date : 20 Dec 2025 1836
Collected Date/Time: 20 Dec 2025
Received Date/Time 20 Dec 2025 1853
Test Name Result Unit Reference Range
(*)Lipid set (Chol, Tri, HDL, LDL)
Specimen......................: Serum
Cholesterol...................: 175 mg/dL (<200)
Triglyceride..................: 89 mg/dL (<150)
HDL-Cholesterol...............: 52 mg/dL (>40)
LDL Direct....................: 99 mg/dL (<130)
VLDL..........................: 18 mg/dL (<30)
Cholesterol/HDL-C Ratio.......: 3.4
LDL/HDL Ratio. 1.90
Normal Range LDL " The American Heart Association (AHA) recommends
Optimal : <100 mg/dl
Near Optimal :100-129 mg/d1
Borderline high :130-159 mg/dl
High :160-189 mg/dl
Very high :>=190 mg/d1
For 10-year ASCVD risk score >= 7.5 % or cardiovascular diseases
Or diabetes mellitus, LDL target should be 70 mg/dl. or lower"
(*)Liver Function Test (8 Tests)
Total Protein.................: 7.24 g/dL (6.40-8.30)
Albumin.......................: 5.07 g/dL (3.50-5.20)
Globulin......................: 2.17 g/dL (2.10-3.70)
Bilirubin(Total)............: 0.6 mg/dL (0.2-1.2)
Bilirubin (Direct)..........: 0.2 mg/dL (0.0-0.5)
ALP(Alkaline Phosphatase).....: 57 U/L (40-150)
AST(Aspartate Transaminase)...: 30 U/L (5-34)
ALT(Alanine Transaminase).....: 41 U/L (0-45)
Remark:(H) means higher than reference values;(L) means lower than reference values;(*)ISO 15189 Accreditied
Reported by: (Auto)Warunee Jit.,MT7166 on 20 Dec 2025 2010
Authorised by: (Auto) Warunee Jit.,MT7166 on 20 Dec 2025 2011 Print Date and Time 20 Dec 2025 2012
This report has been approved electronically. Information contained in this document is CONFIDENTIAL. Copyright: Issued by N Health.
This report is only for the specimen (S) received on the above date: Copyright issued by N Health."DO NOT COPY"
This rport present test results fr infomational purposes and is nota substitute for medical advice, diagnosis, r treatment. Variability in specimen quality, tes kt prformance, reagent integrity, and the
sensitivity and specifcity of instruments may influence the findings. While every efotis made to esure the accuracy and timeliness of the information provided,results should be considered in the context of evolving clinical guidelines and standards.Personal data is handled following applicable data privacy laws.
FP-NLS-NHS-00-002/5 Revision 12 Issue date 10/06/2025
Page2/4
Health SSURANOK
lac-MRA
National Healthcare Systems Co., Ltd DMSC
2301/2New Petchburi Road,Soi 47(Soonvijai)
Bangkapi,Huaykwang,Bangkok 10310,Thailand Tel.(662)762-4000 Fax.- ISO 15189
Email:nhcslab@nhealth-asia.com Accreditation No.4120/47
LABORATORY REPORT
Patient Name MR. SHUNHU YU
Sex Male DOB 03 Jun 1968 Age: 57Y6M17D Address/Ref.No.: Be.U wellness center(Cash)(C1W)/CASH02/HN.000
Doctor:5n 46-25
MRN.:10A-25-213418
LabNo.:10253540676 Requested Date : 20 Dec 20251836
Collected Date/Time 20 Dec 2025
Received Date/Time 20 Dec 2025 1853
Test Name Result Unit Reference Range
(*)Fibrinogen Level
Fibrinogen Level.......: 228.2 mg/dL (200.0-400.0)
Remark:(H)means higher than reference values;(L) means lower than reference values;(*)ISO 15189 Accreditied
Reported by Afnan Waedoloh,MT20602 on 20 Dec 2025 2011
Authorised by: Afnan Waedoloh,MT20602 on 20 Dec 2025 2011 Print Date and Time 20 Dec 2025 2012
This report has been approved electronically. Information contained in this document is CONFIDENTIAL. Copyright: Issued by N Health.
This report is only for the specimen (S) received on the above date: Copyright issued by N Health."DO NOT COPY"
This report present test results fr infomational purposes and is not a substitute formedical advice, diagnosis, r treatment Variability in specimen quality, test kit performance,reagent integrity, an the
sensitivity and specificity of instruments may influence the findings. While every fort is made to ensure the accuracy and timeliness of the infomation provided, results should be considered in the context of evolving clinical guidelines and standards. Personal data is handled following applicable data privacy laws.
Issue date 10/06/2025
FP-NLS-NHS-00-002/5 Revision 12 Page 3/4
Health 655URANO
laCMEA
National Healthcare Systems Co., Ltd DMS
2301/2 New Petchburi Road,Soi 47(Soonvijai)
Bangkapi, Huaykwang, Bangkok 10310Thailand ISO 15189
Tel.(662)762-4000 Fax.-
Email:nhcslab@nhealth-asia.com Accreditation No.4120/47
LABORATORY REPORT
Patient Name MR. SHUNHU YU
Sex Male DOB 03 Jun 1968 Age 57Y6M17D Address/Ref.No.: Be.U wellness center(Cash)(C1W)/CASH02/HN.000
Doctor:n 46-25
MRN.:10A-25-213418
Lab No.:10253540676 Requested Date : 20 Dec 2025 1836
Collected Date/Time 20 Dec 2025
Received Date/Time 20 Dec 2025 1853
Test Name Result Unit Reference Range
(*)C-Reactive Protein High Sens.
C-Reactive Protein(High Sens)....: 0.98 mg/L (0.00-5.00)
Reference Range...........: Coronary vascular disease risk assessment
according to AHA/CDC recommendation.
<1.0 mg/L Low risk
1.0-3.0 mg/L Average risk
>3.0 mg/L High risk
Remark:(H) means higher than reference values;(L) means lower than reference values;(*)ISO 15189 Accreditied
Reported by:(Auto)Warunee Jit.,MT7166 on 20 Dec 2025 2010
Authorised by: (Auto) Warunee Jit.,MT7166 on 20 Dec 2025 2011 Print Date and Time 20 Dec 2025 2012
This report has been approved electronically. Information contained in this document is CONFIDENTIAL. Copyright: Issued by N Health.
This report is only for the specimen (S) received on the above date: Copyright issued by N Health."DO NOT COPY"
This eport present test results or infomational purposes and is not a substitute for medical advice, diagnosis, r treatment Variability in specimen quality, test kit perfomane,reagent integrity, and the
sensitivity and specifcity ofinstruments may influence the findings. While every effort s madeto ensure the acuracy and timeliess of the information provided, results should be considered in the context of evolving clinical guidelines and standards.Personal data is handled following applicable data privacy laws.
Issue date 10/06/2025
FP-NLS-NHS-00-002/5 Revision 12 Page 4/4
N
Health 一 SSURANC
lac MRA
National Healthcare Systems Co., Ltd DMSC
2301/2 New Petchburi Road,Soi 47(Soonvijai)
Bangkapi,Huaykwang, Bangkok 10310 Thailand ISO 15189
Tel.(662)762-4000 Fax.-
Email:nhcslab@nhealth-asia.com Accreditation No.4120/47
LABORATORY REPORT
Patient Name MR. SHUNHU YU
Sex: Male DOB 03 Jun 1968 Age 57Y6M17D Address/Ref.No.: Be.U wellness center(Cash)(C1W)/CASH02/HN.000
Doctor:uwn d
46-25
MRN.:10A-25-213418
Lab No.:10253540676 Requested Date : 20 Dec 2025 1836
Collected Date/Time 20 Dec 2025
Received Date/Time 20 Dec 2025 1853
Test Name Result Unit Reference Range
(*)Prothrombin Time
Specimen type.................: 3.2 % Na citrate plasma
Prothrombin Time(PT)........: 10.6 Secs. (10.1-12.3)
INR 0.93
PT %........................ 116.5 号 (50.0-150.0)
Remark:(H)means higher than reference values;(L) means lower than reference values;(*)ISO 15189 Accreditied
Reported by Afnan Waedoloh,MT20602 on 20 Dec 2025 2011
Authorised by: Afnan Waedoloh,MT20602 on 20 Dec 2025 2011 Print Date and Time 20 Dec 2025 2011
This report has been approved electronically. Information contained in this document is CONFIDENTIAL. Copyright: Issued by N Health.
This report is only for the specimen (S) received on the above date: Copyright issued by N Health."DO NOT COPY"
This report present test results fr infomational purposes and is not a substitute for medical advice, diagnosis, r treatment. Variability in specimen quality, test kit performance,reagent integrity, and the
sensitivity and specificity of instruments may influence the findings. While every ffort is made to ensure the accuracy and timeliness of the infomation provided, results should be considered in the context of evolving clinical guidelines and standards. Personal data is handled following applicable data privacy laws.
Issue date 10/06/2025
FP-NLS-NHS-00-002/5 Revision 12 Page 1/2
Health SSURANO
ilac MRA
National Healthcare Systems Co., Ltd DMSC
2301/2 New Petchburi Road,Soi47(Soonvijai)
Bangkapi, Huaykwang, Bangkok 10310 Thailand Tel.(662)762-4000 Fax.- ISO 15189
Email:nhcslab@nhealth-asia.com Accreditation No.4120/47
LABORATORY REPORT
Patient Name: MR. SHUNHU YU
Sex Male DOB 03 Jun 1968 Age 57Y6M17D Address/Ref.No.: Be.U wellness center(Cash)(C1W)/CASH02/HN.000
Doctor:n 46-25
MRN.:10A-25-213418
Lab No.:10253540676 Requested Date : 20 Dec 2025 1836
Collected Date/Time 20 Dec 2025
Received Date/Time 20 Dec 2025 1853
Test Name Result Unit Reference Range
(*)Partial Thromboplastin Time(PTT)
Specimen........................: Sodium Citrate plasma
Specimen type.................: 3.2 % Na citrate plasma
Partial Thromboplastin Time(PTT).: 31.6 H Secs. (22.5-31.5)
Mean of Reference Range........: 26.96Secs.
Remark:(H) means higher than reference values;(L) means lower than reference values;(*)ISO 15189 Accreditied
Reported by Afnan Waedoloh,MT20602 on 20 Dec 2025 2011
Authorised by: Afnan Waedoloh,MT20602 on 20 Dec 2025 2011 Print Date and Time 20 Dec 2025 2011
This report has been approved electronically. Information contained in this document is CONFIDENTIAL. Copyright: Issued by N Health.
This report is only for the specimen (S) received on the above date: Copyright issued by N Health."DO NOT COPY"
This report present test results fr infomational purposes and is nota ubstitute for medical advice, diagnosis, r treatment. Variability in specimen quality, test kit performance,reagent integrity, and the
sensitivity and specificity of instruments may influence the findings. While every efort is made to ensure the accuracy and timeliness of the infomation provided, results should be considered in the context of evolving clinical guidelines and standards.Personal data is handled following applicable data privacy laws.
Issue date 10/06/2025
FP-NLS-NHS-00-002/5 Revision 12 Page 2/2
Health Q S5URANOA
ac MRA
National Healthcare Systems Co.,Ltd DMS
2301/2 New Petchburi Road,Soi 47(Soonvijai)
Bangkapi,Huaykwang,Bangkok 10310,Thailand Tel.(662)762-4000 Fax.- ISO15189
Email:nhcslab@nhealth-asia.com Accreditation No.4120/47
LABORATORY REPORT
Patient Name: MR. SHUNHU YU
Sex: Male DOB 03 Jun 1968 Age 57Y6M17D Address/Ref.No.: Be.U wellness center(Cash)(C1W)/CASH02/HN.000
Doctor:n 46-25
MRN.:10A-25-213418
LabNo.:10253540676 Requested Date :20 Dec 2025 1836
Collected Date/Time: 20 Dec 2025
Received Date/Time 20 Dec 2025 1853
Test Name Result Unit Reference Range
(*)Apolipoprotein B
Apolipoprotein B.........: 81.70 mg/dL (49.00-173.00)
Remark:(H)means higher than reference values;(L) means lower than reference values;(*)ISO 15189 Accreditied
Reported by: Sarocha Manfak, MT16760 on 20 Dec 2025 2013
Authorised by: Sarocha Manfak, MT16760 on 20 Dec 2025 2014 Print Date and Time 20 Dec 2025 2014
This report has been approved electronically. Information contained in this document is CONFIDENTIAL. Copyright: Issued by N Health.
This report is only for the specimen (S) received on the above date: Copyright issued by N Health."DO NOT COPY"
This report present test results frinfomational purposes and is not a substitute for medical advice, diagnosis, ortreatment Variability in specimen quality, test kit perfomane, eagent integrity, and the
sensitivity and specificity of instruments may influence the findings. While every efort is made to ensure the accuracy and timelinessoftheinformation provided, results should be considered in the
context of evolving clinical guidelines and standards. Personal data is handled following applicable data privacy laws.
Issue date 10/06/2025
FP-NLS-NHS-00-002/5 Revision 12 Page 1/1
Health QuALete 55URANC
ilac-MRA
National Healthcare Systems Co.,Ltd DMSC
2301/2 New Petchburi Road,Soi 47(Soonvijai)
Bangkapi,Huaykwang,Bangkok 10310,Thailand Tel.(662)762-4000 Fax. - ISO15189
Email:nhcslab@nhealth-asia.com Accreditation No. 4120/47
LABORATORY REPORT
Patient Name: MR. SHUNHU YU
Sex Male DOB 03 Jun 1968 Age 57Y6M17D Address/Ref.No.: Be.U wellness center(Cash)(C1W)/CASH02/HN.000
Doctor: n d
46-25
MRN.: 10A-25-213418
Lab No.: 10253540676 Requested Date : 20 Dec 2025 1836
Collected Date/Time: 20 Dec 2025
Received Date/Time: 20 Dec 2025 1853
Test Name Result Unit Reference Range
(*)Glucose(Fasting)
Specimen.............: Serum
Glucose(Fasting)........: 89 mg/dL (70-99)
(*)Glycated Hb(HbA1C)
Specimen.....................: EDTA blood
Haemoglobin Alc............: 5.1 号 (<5.7)
Estimated Average Glucose.....: 100 mg/dL
(*)Blood urea nitrogen
Specimen.................: Serum
Blood Urea Nitrogen........: 13.10 mg/dL (8.40-25.70)
(*)Creatinine(plus eGFR)
Specimen....................: Serum
Creatinine....................: 0.76 mg/dL (0.73-1.18)
eGFR (African-American)......: 117.38 ml/min/1.73m2
eGFR (Non African-American)...: 101.28 ml/min/1.73 m2
eGFR for Thai.................: 108.78 ml/min/1.73 m2
eGFR.........................: 104.84 ml/min/1.73m2
eGFR Comment Calculated by CKD- EPI formula according to
National kidney foundation recommendation.
(2021) This equation should only be used for patients 18 and older.
(*)uric acid
Specimen................: Serum
Uric Acid.................: 6.3 mg/dL (3.5-7.2)
Remark:(H) means higher than reference values;(L) means lower than reference values;(*)ISO 15189 Accreditied
Reported by: (Auto)Warunee Jit.,MT7166 on 20 Dec 2025 2010
Authorised by: (Auto) Warunee Jit.,MT7166 on 20 Dec 2025 2011 Print Date and Time: 20 Dec 2025 2022
This report has been approved electronically. Information contained in this document is CONFIDENTIAL. Copyright: Issued by N Health.
This report is only for the specimen (S) received on the above date: Copyright issued by N Health."DO NOT COPY"
sensitivity and specificity of instruments may influence the findings. While every efort is made to ensure the accuracy and timeliness of the infomation provided,results should be considered in the context of evolving clinical guidelines and standards. Personal data is handled following applicable data privacy laws.
FP-NLS-NHS-00-002/5 Revision 12 Issue date 10/06/2025
Page 1/2
Health S5URANCK
lac-MRA
National Healthcare Systems Co.,Ltd DMSC
2301/2 New Petchburi Road,Soi 47(Soonvijai)
Bangkapi,Huaykwang,Bangkok 10310,Thailand Tel.(662)762-4000 Fax. - ISO15189
Email:nhcslab@nhealth-asia.com Accreditation No. 4120/47
LABORATORY REPORT
Patient Name: MR. SHUNHU YU
Sex Male DOB 03 Jun 1968 Age: 57Y6M17D Address/Ref.No.: Be.U wellness center(Cash)(C1W)/CASH02/HN.000
Doctor: 5 d
46-25
MRN.: 10A-25-213418
Lab No.: 10253540676 Requested Date : 20 Dec 2025 1836
Collected Date/Time: 20 Dec 2025
Received Date/Time: 20 Dec 2025 1853
Test Name Result Unit Reference Range
(*)GGT( gamma GT)
Specimen...............: Serum
Gamma Glutamyl Transferase...: 25 U/L (12-64)
(*)Inorganic phosphate
Specimen.....................: Serum
Inorganic phosphate........: 3.4 mg/dL (2.5-4.5)
(*)Magnesium
Specimen....................: Serum
Magnesium(Mg)............: 2.3 mg/dL (1.6-2.6)
(*)Electrolytes
Specimen......................: Serum
Sodium......................: 138 mmol/L (136-145)
Potassium.....................: 4.80 mmol/L (3.50-5.10)
Chloride.....................: 105 mmol/L (98-107)
TCO2..........................: 25 mmol/L (22-29)
Anion gap ................... 8.0L mmol/L (10.0-12.0)
Remark:(H) means higher than reference values;(L) means lower than reference values;(*)ISO 15189 Accreditied
Reported by: (Auto)Warunee Jit.,MT7166 on 20 Dec 2025 2021
Authorised by: (Auto) Warunee Jit.,MT7166 on 20 Dec 2025 2022 Print Date and Time: 20 Dec 2025 2022
This report has been approved electronically. Information contained in this document is CONFIDENTIAL. Copyright: Issued by N Health.
This report is only for the specimen (S) received on the above date: Copyright issued by N Health."DO NOT COPY"
This report present test results fr infomational purposes ad is not a substitute for medical advice, diagnosis, o treatment Variability in specimen qualty, test kit perfomane, reagent integrity, an the
sensitivity and specificity of instruments may influence the findings. While every fort is made to ensure the accuracy and timeliness of the infomation provided, results should be considered in the context of evolving clinical guidelines and standards.Personal data is handled following applicable data privacy laws.
FP-NLS-NHS-00-002/5 Revision 12 Issue date 10/06/2025
Page 2/2
0 S5URANO
Health
ilac MRA
National Healthcare Systems Co., Ltd DMSC
2301/2 New Petchburi Road, Soi 47 (Soonvijai)
Bangkapi, Huaykwang, Bangkok 10310 Thailand Tel.(662)762-4000 Fax. - ISO 15189
Email:nhcslab@nhealth-asia.com Accreditation No.4120/47
LABORATORY REPORT
Patient Name: MR. SHUNHU YU
Sex Male DOB 03 Jun 1968 Age 57Y6M17D Address/Ref.No.: Be.U wellness center(Cash)(C1W)/CASH02/HN.000
Doctor:n , 46-25
MRN.:10A-25-213418
Lab No.:10253540676 Requested Date :20 Dec 2025 1836
Collected Date/Time 20 Dec 2025
Received Date/Time 20 Dec 2025 1853
Test Name Result Unit Reference Range
(*)Lipoprotein(a)
Lipoprotein(a).........: 4.82 mg/dL (<30.00)
Method.................: Immunoturbidimetric assay
Remark:(H) means higher than reference values;(L) means lower than reference values;(*)ISO 15189 Accreditied
Reported by: Nurhuda Suetoh,MT21824 on 20 Dec 2025 2036
Authorised by: Nurhuda Suetoh,MT21824 on 20 Dec 2025 2036 Print Date and Time: 20 Dec 2025 2036
This report has been approved electronically. Information contained in this document is CONFIDENTIAL. Copyright: Issued by N Health.
This report is only for the specimen (S) received on the above date: Copyright issued by N Health."DO NOT COPY"
This report presents testresults forinformational purposes and is not a substitute formedical advice, diagnosis, r treatment. Variability in specimen quality, test kit prformance, reagent integrity, and the
sensitivity and specificity of instruments may influence the findings. While every efort is made to ensure the accuracy and timeliness of the information provided,results should be considered in the
context of evolving clinical guidelines and standards. Personal data is handled following applicable data privacy laws.
Issue date 10/06/2025
FP-NLS-NHS-00-002/5 Revision 12 Page 1/1
Health
lkC 6
National Healthcare Systems Co., Ltd DMSc
2301/2 New Petchburi Road,Soi47(Soonvijai)
Bangkapi,Huaykwang, Bangkok 10310 Thailand ISO15189
Tel.(662)762-4000 Fax.- Accreditation No.4120/47
Email:nhcslab@nhealth-asia.com
LABORATORY REPORT
Patient Name: MR. SHUNHU YU
Sex: Male DOB 03 Jun 1968 Age 57Y6M17D Address/Ref.No.: Be.U wellness center(Cash)(C1W)/CASH02/HN.000
Doctor: 46-25
MRN.:10A-25-213418
Lab No.:10253540676 Requested Date : 20 Dec 2025 1836
Collected Date/Time 20 Dec 2025
Received Date/Time 20 Dec 2025 1853
Test Name Result Unit Reference Range
(*)IgG(Immunoglobulin G) Level
Immunoglobulin G(IgG)......: 1085.0 mg/dL (700.0-1600.0)
Remark:(H) means higher than reference values;(L) means lower than reference values;(*)ISO 15189 Accreditied
Reported by: (Auto)Warunee Jit.,MT7166 on 20 Dec 2025 2041
Authorised by: (Auto)Warunee Jit.,MT7166 on 20 Dec 2025 2042 Print Date and Time 20 Dec 2025 2042
This report has been approved electronically. Information contained in this document is CONFIDENTIAL. Copyright: Issued by N Health.
This report is only for the specimen (S) received on the above date: Copyright issued by N Health."DO NOT COPY" This report presents test results for informational purposes and is not a substitute for medical advice, dignosis or treatment. Variability in specimen quality, est it perfomance, reagent integrity, and the
sensitivity and specificity of instruments may influence he findings. While every fort is made to ensure the accuracy and timeliness of the infomation provided, esults should be considered in the
context of evolving clinical guidelines and standards. Personal data is handled following applicable data privacy laws.
Issue date 10/06/2025
FP-NLS-NHS-00-002/5 Revision 12 Page 1/2
SUKAN
Health
lac MRA
National Healthcare Systems Co., Ltd DMSC
2301/2 New Petchburi Road, Soi 47 (Soonvijai)
Bangkapi, Huaykwang, Bangkok 10310 Thailand ISO 15189
Tel.(662)762-4000 Fax.- Accreditation No.4120/47
Email:nhcslab@nhealth-asia.com
LABORATORY REPORT
Patient Name: MR. SHUNHU YU
Sex: Male DOB 03 Jun 1968 Age 57Y6M17 D Address/Ref.No.: Be.U wellness center(Cash)(C1W)/CASH02/HN.000
Doctor: 46-25
MRN.: 10A-25-213418
Lab No.:10253540676 Requested Date : 20 Dec 20251836
Collected Date/Time 20 Dec 2025
Received Date/Time: 20 Dec 2025 1853
Test Name Result Unit Reference Range
(*)IgA(Immunoglobulin A) Level
Immunoglobulin A(IgA)........: 125.00mg/dL (70.00-400.00)
(*)Complement C3(B1C)
Complement C3(B1C).........: 107.0 mg/dl (90.0-180.0)
(*)ComplementC4
Complement C4 ............. 19.60 mg/dL (10.00-40.00)
Remark:(H)means higher than reference values;(L) means lower than reference values;(*)ISO 15189 Accreditied
Reported by (Auto) Warunee Jit.,MT7166 on 20 Dec 2025 2041
Authorised by: (Auto)Warunee Jit.,MT7166 on 20 Dec 2025 2042 Print Date and Time 20 Dec 2025 2042
This report has been approved electronically. Information contained in this document is CONFIDENTIAL. Copyright: Issued by N Health.
This report is only for the specimen (S) received on the above date: Copyright issued by N Health."DO NOT COPY" This reportpresents test results for informationa purposes and is not a substitute for medical advice, dignosis,or treatment. Variability in specimen quality, tet it performance, reagent integrity, and the
context of evolving clinical guidelines and standards. Personal data is handled following applicable data privacy laws.
Issue date 10/06/2025
FP-NLS-NHS-00-002/5 Revision 12 Page 2/2
0
Health
lac-MRA
National Healthcare Systems Co., Ltd DMSC
2301/2 New Petchburi Road, Soi 47 (Soonvijai)
Bangkapi,Huaykwang,Bangkok 10310,Thailand ISO15189
Tel.(662)762-4000 Fax.- Accreditation No.4120/47
Email:nhcslab@nhealth-asia.com
LABORATORY REPORT
Patient Name: MR. SHUNHU YU
Sex Male DOB 03 Jun 1968 Age 57Y6M17D Address/Ref.No.: Be.U wellness center(Cash)(C1W)/CASH02/HN.000
Doctor: n d 46-25
MRN.: 10A-25-213418
Lab No.: 10253540676 Requested Date : 20 Dec 2025 1836
Collected Date/Time: 20 Dec 2025
Received Date/Time 20 Dec 2025 1853
Test Name Result Unit Reference Range
(*)Triiodothyronine(T3)
Total T3(Triiodothyronine)...: 97 ng/dL (57-152)
T3 Reference Range
Female/Male 4 days-364 days 85-234 ng/dL
Female/Male 1-11 years 113-189 ng/dL
Female/Male 12-14 years 98-176 ng/dL
Female 15-16 years 92-142 ng/dL
Male 15-16 years 94-156 ng/dL
Female/Male 17-18 years 90-168 ng/dL
Female >19 years 57-152 ng/dL
Male >19 years 57-152 ng/dL
(*)Thyroxine(T4)
Total T4(Thyroxine).........: 6.39 ug/dL (4.87-11.72)
T4 Reference Range
Female/Male 7 days-364 days 5.87-13.67 ug/dL
Female/Male 1-8years 6.16-10.32 ug/dL
Female/Male 9-11 years 5.48-9.31 ug/dL
Female 12-13 years 5.08-8.34 ug/dL
Female 14-18 years 5.46-12.99 ug/dL
Male 12-13 years 5.01-8.28 ug/dL
Male 14-18 years 4.68-8.62 ug/dL
Female/Male >19 years 4.87-11.72 ug/dL
(*)TSH(Thyroid Stimulating Hormone)
Specimen.....................: Serum
TSH..........................: 0.528 uIU/mL (0.350-4.940)
TSH Reference Range
Female/Male 4 days-179 days 0.73-4.77 uIU/mL
Remark:(H) means higher than reference values;(L) means lower than reference values;(*)ISO 15189 Accreditied
Reported by: (Auto)Warunee Jit.,MT7166 on 20 Dec 2025 2040
Authorised by: (Auto) Warunee Jit.,MT7166 on 20 Dec 2025 2041 Print Date and Time 20 Dec 2025 2046
This report has been approved electronically. Information contained in this document is CONFIDENTIAL. Copyright: Issued by N Health.
This report is only for the specimen (S) received on the above date: Copyright issued by N Health."DO NOT COPY"
This report presents et results fr informational purposes and is nota substitute for medical advice, diagnosis,or treatment. Variability in specimen quality, est it performance, reagent integrity, and the
sensitivity and specificity of instruments may influence thefindings. While every ffort is made to ensure the accuracy and timeliness of the infomation provided, results should be considered in the
context of evolving clinical guidelines and standards. Personal data is handled following applicable data privacy laws.
Issue date 10/06/2025
FP-NLS-NHS-00-002/5 Revision 12 Page 1/6
55URANCK
Health
lac-MRA
National Healthcare Systems Co., Ltd DMSC
2301/2 New Petchburi Road, Soi 47 (Soonvijai)
Bangkapi, Huaykwang, Bangkok 10310 Thailand ISO15189
Tel.(662)762-4000 Fax.- Accreditation No.4120/47
Email:nhcslab@nhealth-asia.com
LABORATORY REPORT
Patient Name: MR. SHUNHU YU
Sex Male DOB 03 Jun 1968 Age : 57Y6M17D Address/Ref.No.: Be.U wellness center(Cash)(C1W)/CASH02/HN.000
Doctor: n d 46-25
MRN.:10A-25-213418
Lab No.:10253540676 Requested Date : 20 Dec 2025 1836
Collected Date/Time: 20 Dec 2025
Received Date/Time 20 Dec 2025 1853
Test Name Result Unit Reference Range
Female/Male 180 days-13 years 0.70-4.17 uIU/mL
Female/Male 14-19 years 0.47-3.41 uIU/mL
Female/Male > 20 years 0.35-4.94 uIU/mL
(*)Triiodothyronine Free(Free T3)
Specimen......................: Serum
Free T3(Free Triiodothyronine): 2.64 pg/mL (1.58-3.91)
FT3 Reference Range
Female/Male 4 days-364 days 2.32-4.87 pg/mL
Female/Male 1-11 years 2.79-4.42pg/mL
Female 12-14 years 2.50-3.95 pg/mL
Female 15-18 years 2.31-3.71 pg/mL
Male 12-14 years 2.89-4.33 pg/mL
Male 15-18 years 2.25-3.85 pg/mL
Female/Male >19 years 1.58-3.91 pg/mL
(*)Thyroxine Free (Free T4)
Specimen......................: Serum
Free T4 (Free Thyroxine)......: 0.84 ng/dL (0.70-1.48)
FT4 Reference Range:
Female/Male 5 days-14 days 1.05-3.21 ng/dL
Female/Male 15 days-29 days 0.68-2.53 ng/dL
Female/Male 30 days -364 days 0.89-1.7 ng/dL
Female/Male 1-18 years 0.89-1.37 ng/dL
Female/Male > 19 years 0.7-1.48 ng/dL
(*)Alpha Fetoprotein(AFP)
AFP(Alpha Fetoprotein)......: 2.49 ng/mL (0.89-8.78)
Remark:(H) means higher than reference values;(L) means lower than reference values;(*)ISO 15189 Accreditied
Reported by:(Auto) Warunee Jit.,MT7166 on 20 Dec 2025 2040
Authorised by: (Auto) Warunee Jit.,MT7166 on 20 Dec 2025 2041 Print Date and Time 20 Dec 2025 2046
This report has been approved electronically. Information contained in this document is CONFIDENTIAL. Copyright: Issued by N Health.
This report is only for the specimen (S) received on the above date: Copyright issued by N Health."DO NOT COPY"
ensitivity and specificity of instruments may influence the findings. While every ffort is made to ensure the accuracy and timeliness of the infomation provided, results should be considered in the
context of evolving clinical guidelines and standards. Personal data is handled following applicable data privacy laws.
Issue date 10/06/2025
FP-NLS-NHS-00-002/5 Revision 12 Page2/6
Q 6 55URANC
Health
lac MRA
National Healthcare Systems Co., Ltd DMSC
2301/2 New Petchburi Road, Soi 47 (Soonvijai)
Bangkapi, Huaykwang, Bangkok 10310 Thailand ISO 15189
Tel.(662)762-4000 Fax.- Accreditation No.4120/47
Email:nhcslab@nhealth-asia.com
LABORATORY REPORT
Patient Name MR. SHUNHU YU
Sex Male DOB 03 Jun 1968 Age 57Y6M17D Address/Ref.No.: Be.U wellness center(Cash)(C1W)/CASH02/HN.000
Doctor:n 。, d 46-25
MRN.:10A-25-213418
LabNo.:10253540676 Requested Date : 20 Dec 2025 1836
Collected Date/Time 20 Dec 2025
Received Date/Time 20 Dec 2025 1853
Test Name Result Unit Reference Range
(*)Carcinoembryonic Antigen(CEA)
CEA(Carcinoembryonic Antigen).: 0.90 ng/mL (0.00-5.00)
(*)Carbohydrate Antigen 19-9(Digestive Tract)
Carbohydrate Antigen 19-9..... 0.98 U/mL (0.00-37.00)
(*)Ferritin
Ferritin......................: 399.6 Hng/mL (15.0-200.0)
(*)Estradiol(E2)
Specimen......................: Serum
Estradiol(E2).................: <20.0 pg/mL
Normal Menstruating Females
Follicular Phase 21-251 pg/mL
Mid-Cycle Phase 38-649 pg/mL
Luteal Phase 21- 312 pg/mL
Postmenopausal Females not on HRT <28 pg/mL
Postmenopausal Females on HRT* <144 pg/mL
Males<44 pg/mL
(*)Luteinizing Hormone(LH)
LH(Luteinizing Hormone).....: 4.79 mIU/mL
Reference Range: Male 0.57-12.07
Female
Follicular Phase 1.80-11.78
Remark:(H) means higher than reference values;(L)means lower than reference values;(*)ISO 15189 Accreditied
Reported by Nurhuda Suetoh,MT21824 on 20 Dec 2025 2045
Authorised by: Nurhuda Suetoh,MT21824 on 20 Dec 2025 2046 Print Date and Time 20 Dec 2025 2046
This report has been approved electronically. Information contained in this document is CONFIDENTIAL. Copyright: Issued by N Health.
This report is only for the specimen (S) received on the above date: Copyright issued by N Health."DO NOT COPY"
sensitivity and specificity of instruments may influence thefindings. While every ffort is made to ensure the accuracy and timeliness of the information provided, results should be considered in the
context of evolving clinical guidelines and standards. Personal data is handled following applicable data privacy laws.
Issue date 10/06/2025
FP-NLS-NHS-00-002/5 Revision 12 Page 3/6
Q S5URANC
Health
lacMRA
National Healthcare Systems Co., Ltd DMSc
2301/2 New Petchburi Road,Soi 47(Soonvijai)
Bangkapi, Huaykwang, Bangkok 10310 Thailand ISO15189
Tel.(662)762-4000 Fax.- Accreditation No. 4120/47
Email:nhcslab@nhealth-asia.com
LABORATORY REPORT
Patient Name: MR. SHUNHU YU
Sex: Male DOB 03 Jun 1968 Age 57Y6M17D Address/Ref.No.: Be.U wellness center(Cash)(C1W)/CASH02/HN.000
Doctor:n d 46-25
MRN.: 10A-25-213418
Lab No.:10253540676 Requested Date :20 Dec 20251836
Collected Date/Time 20 Dec 2025
Received Date/Time: 20 Dec 2025 1853
Test Name Result Unit Reference Range
Mid-cycle Peak 7.59-89.08
Luteal Phase 0.56-14.00
Postmenopausal Phase 5.16-61.99
(*)Progesterone
Progesterone..................: <0.20 ng/mL
Reference Range...............: Normal Menstruating Females
Follicular Phase <0.3 ng/mL
Luteal Phase 1.2-15.9 ng/mL
Postmenopausal Females <0.2 ng/mL
Pregnant Females
First Trimester 2.8-147.3 ng/mL
Second Trimester 22.5 - 95.3 ng/mL
Third Trimester 27.9 - 242.5 ng/mL
Male<0.2 ng/mL
(*)Folicle Stimulating Hormone(FSH)
Specimen......................: Serum
FSH...........................: 6.69 mIU/mL
Reference Range: Male 0.95-11.95
Female
Follicular Phase 3.03-8.08
Mid-cycle Peak 2.55-16.69
Luteal Phase 1.38-5.47
Postmenopausal Phase 26.72-133.41
Remark:(H) means higher than reference values;(L)means lower than reference values;(*)ISO 15189 Accreditied
Reported by: Nurhuda Suetoh,MT21824 on 20 Dec 2025 2045
Authorised by: Nurhuda Suetoh,MT21824 on 20 Dec 2025 2046 Print Date and Time 20 Dec 2025 2046
This report has been approved electronically. Information contained in this document is CONFIDENTIAL. Copyright: Issued by N Health.
This report is only for the specimen(S) received on the above date: Copyright issued by N Health."DO NOT COPY"
This report present test results or infomational purposes and is nota substitute for medical advice, diagnosis, r treatment. Variability in specimen quality, et it performance, reagent integrity, and the
sensitivity and specificity of instruments may influence the findings. While ever ffort is made to ensure the accuracy and timeliness of the information provided,results should be considered in the
context of evolving clinical guidelines and standards. Personal data is handled following applicable data privacy laws.
Issue date 10/06/2025
FP-NLS-NHS-00-002/5 Revision 12 Page 4/6
Q A55URANCR
Health
lac-MRA
National Healthcare Systems Co., Ltd DMSC
2301/2 New Petchburi Road,Soi 47(Soonvijai) ISO15189
Bangkapi, Huaykwang, Bangkok 10310 Thailand
Tel.(662)762-4000 Fax.- Accreditation No.4120/47
Email:nhcslab@nhealth-asia.com
LABORATORY REPORT
Patient Name: MR. SHUNHU YU
Sex: Male DOB 03 Jun 1968 Age 57Y6M17D Address/Ref.No.: Be.U wellness center(Cash)(C1W)/CASH02/HN.000
Doctor: n 46-25
MRN.: 10A-25-213418
Lab No. 10253540676 Requested Date : 20 Dec 2025 1836
Collected Date/Time: 20 Dec 2025
Received Date/Time 20 Dec 2025 1853
Test Name Result Unit Reference Range
(*)Testosterone
Specimen......................: Serum
Testosterone ................. 4.77 ng/mL
Testosterone..................: 477.00 ng/dL
Reference Range...............: Male 21-49 years : 240-871 ng/dL
Male >= 50 years : 221-716 ng/dL
Female 21-49 years 14-53 ng/dL
Female >=50 years 12-36 ng/dL
(*)Dehydroepiandrosterone Sulphate(DHEAS)(Alinity)
DHEA-Sulphate.................: 580.8H ug/dL (48.6-361.8)
(*)Prolactin
Specimen......................: Serum
Prolactin .................... 5.03 ng/mL (3.46-19.40)
(*)Cortisol(Blood)
Cortisol......................: 9.000 ug/dL
Serum AM 3.7 - 19.4 ug/dL
Serum PM 2.9 - 17.3 ug/dL
Remark:(H) means higher than reference values;(L) means lower than reference values;(*)ISO 15189 Accreditied
Reported by Nurhuda Suetoh,MT21824 on 20 Dec 2025 2045
Authorised by: Nurhuda Suetoh,MT21824 on 20 Dec 2025 2046 Print Date and Time: 20 Dec 2025 2046
This report has been approved electronically. Information contained in this document is CONFIDENTIAL. Copyright: Issued by N Health.
This report is only for the specimen(S) received on the above date: Copyright issued by N Health."DO NOT COPY" This report presents test results for informational purposes and is not a substitute for medical advice, ignosis, or treatment. Variabilit in specimen quality, est kit perfomance, reagen ntegrity, and the
sensitivity and specificity of nstruments may influence the findings. While every effort is made to ensure te acuracy and timeliness of the infomation provided, results should be considered in the
context of evolving clinical guidelines and standards. Personal data is handled following applicable data privacy laws.
Issue date 10/06/2025
FP-NLS-NHS-00-002/5 Revision 12 Page 5/6
Health Q
lac-MRA
National Healthcare Systems Co., Ltd DMSC
2301/2 New Petchburi Road, Soi 47(Soonvijai)
Bangkapi, Huaykwang, Bangkok 10310 Thailand ISO 15189
Tel.(662)762-4000 Fax.- Accreditation No.4120/47
Email:nhcslab@nhealth-asia.com
LABORATORY REPORT
Patient Name MR. SHUNHU YU
Sex Male DOB 03 Jun 1968 Age 57Y6M17D Address/Ref.No.: Be.U wellness center(Cash)(C1W)/CASH02/HN.000
Doctor: 46-25
MRN.:10A-25-213418
Lab No.:10253540676 Requested Date : 20 Dec 2025 1836
Collected Date/Time 20 Dec 2025
Received Date/Time 20 Dec 2025 1853
Test Name Result Unit Reference Range
(*)Thyroglobulin Antibody
Thyroglobulin Antibody.....: 0.73 IU/ml (0.00-4.11)
Remark:(H) means higher than reference values;(L) means lower than reference values;(*)ISO 15189 Accreditied
Reported by: (Auto)Warunee Jit.,MT7166 on 20 Dec 2025 2043
Authorised by: (Auto) Warunee Jit.,MT7166 on 20 Dec 2025 2044 Print Date and Time 20 Dec 2025 2046
This report has been approved electronically. Information contained in this document is CONFIDENTIAL. Copyright: Issued by N Health.
This report is only for the specimen (S) received on the above date: Copyright issued by N Health."DO NOT COPY"
This repor presents testresults forinformational purposes and is not a substitute for medical advice, diagnosis, r treatment. Variability in specimen quality, tet it prformance, reagent integrity, and the
sensitivity and specificity of instruments may influence thefindings. While every fort is made to ensure the accuracy and timeliness of the infomation provided,results should be considered in the
context of evolving clinical guidelines and standards. Personal data is handled following applicable data privacy laws.
Issue date 10/06/2025
FP-NLS-NHS-00-002/5 Revision 12 Page 6/6
N Q 65SURANC
Health
lac-MRA
National Healthcare Systems Co., Ltd DMSC
2301/2 New Petchburi Road, Soi 47(Soonvijai)
Bangkapi,Huaykwang,Bangkok 10310,Thailand ISO 15189
Tel.(662)762-4000 Fax.- Accreditation No.4120/47
Email:nhcslab@nhealth-asia.com
LABORATORY REPORT
Patient Name MR. SHUNHU YU
Sex Male DOB 03 Jun 1968 Age :57Y6M17D Address/Ref.No.: Be.U wellness center(Cash)(C1W)/CASH02/HN.000
Doctor:n d 46-25
MRN.:10A-25-213418
Lab No.:10253540676 Requested Date : 20 Dec 2025 1836
Collected Date/Time 20 Dec 2025
Received Date/Time 20 Dec 2025 1853
Test Name Result Unit Reference Range
(*)IgE(Immunoglobulin E) Level
Immunoglobulin E(IgE)......: 34.70 IU/mL (<100.00)
Method........................: Electrochemiluminescence immunoassay
Remark:(H)means higher than reference values;(L) means lower than reference values;(*)ISO 15189 Accreditied
Reported by: Nurhuda Suetoh,MT21824 on 20 Dec 2025 2048
Authorised by Nurhuda Suetoh,MT21824 on 20 Dec 2025 2048 Print Date and Time: 20 Dec 2025 2048
This report has been approved electronically. Information contained in this document is CONFIDENTIAL. Copyright: Issued by N Health.
This report is only for the specimen (S) received on the above date: Copyright issued by N Health."DO NOT COPY" This report presents test results for informational purposes and is not a substitute for medical advice, diagnosis,or treatment. Variability in specimen quality, est it performane, reagent integrity, and the
sensitivity and specificity of instruments may influence the findings. While every ffort is made to ensure the accuracy and timeliness of the information provided, esults should be considered in the
context of evolving clinical guidelines and standards. Personal data is handled following applicable data privacy laws.
Issue date 10/06/2025
FP-NLS-NHS-00-002/5 Revision 12 Page 1/1
Q S5URANC
Health
lac-MRA
National Healthcare Systems Co., Ltd DMSC
2301/2 New Petchburi Road, Soi 47 (Soonvijai)
Bangkapi, Huaykwang,Bangkok 10310 Thailand ISO 15189
Tel.(662)762-4000 Fax.- Accreditation No.4120/47
Email:nhcslab@nhealth-asia.com
LABORATORY REPORT
Patient Name MR. SHUNHU YU
Sex Male DOB 03 Jun 1968 Age 57Y6M17D Address/Ref.No.: Be.U wellness center(Cash)(C1W)/CASH02/HN.000
Doctor: 46-25
MRN.:10A-25-213418
Lab No.:10253540676 Requested Date : 20 Dec 2025 1836
Collected Date/Time 20 Dec 2025
Received Date/Time 20 Dec 2025 1853
Test Name Result Unit Reference Range
(*)IgM( Immunoglobulin M) Level
Immunoglobulin M(IgM).......: 35.10 Lmg/dL (40.00-230.00)
Remark:(H)means higher than reference values;(L) means lower than reference values;(*)ISO 15189 Accreditied
Reported by: Nurhuda Suetoh,MT21824 on 20 Dec 2025 2048
Authorised by: Nurhuda Suetoh,MT21824 on 20 Dec 2025 2049 Print Date and Time 20 Dec 2025 2050
This report has been approved electronically. Information contained in this document is CONFIDENTIAL. Copyright: Issued by N Health.
This report is only for the specimen(S) received on the above date: Copyright issued by N Health."DO NOT COPY"
This report presents tes results fr informational purposes and is not a substitute for medical advice, diagnosis, r treatment. Variability in specimen quality, tet kit prformance, reagent integrity, and the
sensitivity and specificity of instruments may influence the findings. While every efrtis made to ensure the accuracy and timeliness of the infomation provided, results should be considered in the context of evolving clinical guidelines and standards. Personal data is handled following applicable data privacy laws.
Issue date 10/06/2025
FP-NLS-NHS-00-002/5 Revision 12 Page 1/1

1143
backend/parse_medical_v2.py Normal file

File diff suppressed because it is too large Load Diff

274
backend/pdf_item_order.json Normal file
View File

@@ -0,0 +1,274 @@
{
"Urine Test": [
"16",
"17",
"Color",
"pH",
"TuR",
"PRO",
"18",
"BLD/ERY",
"GLU",
"SG",
"WBC",
"19",
"NIT",
"KET"
],
"Complete Blood Count": [
"20",
"21",
"Hb",
"HCT",
"22",
"MCV",
"MCH",
"MCHC",
"RDW",
"23",
"RBC",
"Morphology",
"WBC",
"count",
"NEUT",
"NEUT%",
"EOS",
"EOS%",
"24",
"BAS",
"BAS%",
"LYMPH",
"LYMPH%",
"MONO",
"MONO%",
"25",
"PLT",
"PCT",
"MPV",
"PDW",
"26"
],
"Blood Sugar": [
"27",
"28",
"FBS",
"HbA1C"
],
"Lipid Profile": [
"29",
"30",
"TC",
"TG",
"HDL",
"31",
"LDL"
],
"Blood Type": [
"32",
"33",
"Blood",
"type"
],
"Blood Coagulation": [
"34",
"35",
"PT",
"APTT",
"TT",
"36",
"FIB"
],
"Four Infectious Diseases": [
"37",
"38",
"HIV",
"TRUST",
"TPPA",
"HBsAg",
"39",
"HBsAb",
"HBeAg",
"HBeAb",
"HBcAb",
"40",
"IgM"
],
"Serum Electrolytes": [
"41",
"42",
"Kalium",
"Sodium",
"Chloride",
"Calcium",
"43",
"Magnesium",
"Phosphorus"
],
"Liver Function": [
"44",
"45",
"TP",
"A/G",
"46",
"TBil",
"DBil",
"IBil",
"ALP",
"47",
"ALT",
"AST",
"GGT"
],
"Kidney Function": [
"48",
"49",
"Scr",
"BUN",
"UA"
],
"Myocardial Enzyme": [
"50",
"51",
"CK",
"LDH"
],
"Thyroid Function": [
"52",
"53",
"T3",
"T4",
"FT3",
"FT4",
"54",
"TSH"
],
"Thromboembolism": [
"55",
"Thromboembolism",
"56",
"Homocysteine",
"Dimer"
],
"Bone Metabolism": [
"57",
"58",
"25-OH-",
"VD2+D3",
"D2+D3",
"PTH",
"OST",
"TPINP",
"59"
],
"Microelement": [
"60",
"Microelement",
"61",
"Pb",
"Cu",
"Zn",
"Mg",
"62",
"Fe",
"63",
"64",
"CD3+",
"CD4+",
"CD8+"
],
"Lymphocyte Subpopulation": [
"63",
"64",
"CD3+",
"CD4+",
"CD8+"
],
"Humoral Immunity": [
"65",
"66",
"IgG",
"IgA",
"IgM",
"IgE",
"67",
"C3",
"C4"
],
"Inflammatory Reaction": [
"68",
"69",
"CRP",
"ESR",
"ASO"
],
"Autoantibody": [
"70",
"Autoantibody",
"71",
"ANA",
"RF"
],
"Female Hormone": [
"72",
"73",
"E2",
"PROG",
"FSH",
"74",
"LH",
"PRL",
"DHEAS",
"75",
"COR",
"IGF1",
"AMH"
],
"Male Hormone": [
"76",
"77",
"DHEAS",
"IGF1",
"PROG",
"78",
"FSH",
"LH",
"PRL",
"Cortisol",
"79",
"E2"
],
"Tumor Markers": [
"80",
"81",
"AFP",
"CEA",
"CA19-9",
"Fer",
"82",
"NES",
"Tg",
"CT",
"EA-IgA",
"83",
"TPSA",
"FPSA",
"E/TPSA",
"84",
"CA125",
"CA15-3",
"PSA"
],
"Imaging": [
"85",
"Imaging",
"86",
"ECG",
"87",
"Color",
"Doppler",
"Ultrasound",
"88",
"CT",
"Examination"
]
}

View File

@@ -0,0 +1,267 @@
# 功能医学健康建议生成提示词
## 角色设定
你是Be.U Med功能医学团队的资深健康管理顾问在功能医学、营养医学、运动医学、睡眠医学及生活方式干预领域具有丰富的临床经验。你擅长从功能医学整体观出发结合检测数据为客户制定个性化的健康管理方案。
## 任务
根据体检者的血液检查报告异常指标,撰写"功能医学健康建议"方案。该方案位于「医学干预」建议方案之后,是对医学干预的延伸与补充,侧重于日常可执行的健康管理策略。
---
## 核心原则(必须严格遵守)
### 1. 段落格式(极其重要!)
- **每个段落必须先写英文,再写对应的中文**
- **英文段落80-120词不超过150词**
- **中文段落80-120字不超过150字**
- 不要英中混排,必须分开
### 2. 禁止输出检测数值
- **全篇禁止出现任何检验指标的具体数值、参考区间、单位、百分号或数字0-9**
- 只能用定性描述:正常/稳定/良好/偏高/偏低/接近上限/接近下限
- 不要抄写原始报告中的结果值、参考区间、单位
- **可以提及指标名称**(如"黄体酮偏低""ESR升高"),但不要写具体数值
### 3. 语言风格
- 专业、客观、严谨,体现功能医学视角
- 使用肯定、明确的表述,如"建议""支持""助力""优化""需要""应当"
- **严禁使用任何不确定表述**,包括但不限于:
- 中文禁用词:可能、也许、或许、大概、似乎、有概率、有可能、可能会、或可、疑似、倾向于、趋向于、不排除、有待、存在...的可能
- 英文禁用词may, might, could, possibly, probably, perhaps, likely, potentially, tend to, appear to, seem to, it is possible that
- 禁用"必须""一定""保证""治愈"等绝对化表述
- 不做临床疾病诊断,聚焦功能状态改善
---
## 文章结构(必须严格遵循)
### 总述引导(固定模板+动态填充共4个段落
**段落1英文固定模板**
"Functional medicine goes beyond the diagnosis and medical treatment of diseases, placing greater emphasis on comprehensive health management for each individual. Beyond the aforementioned "medical intervention", the core of functional medicine lies in helping individuals improve their lifestyle from the root, optimize bodily functions, and enhance overall health. Through a comprehensive assessment of metabolism, immunity, hormones, nutrition, emotions, and daily habits, a personalized health optimization pathway can be tailored for each client."
**段落2中文固定模板**
"功能医学不仅仅停留在疾病的诊断与医学治疗,更强调对个体的全方位健康管理。在上述「医学干预」之外,功能医学的核心在于帮助人们从源头改善生活方式、优化身体功能与提升整体健康状态。通过对代谢、免疫、荷尔蒙、营养、情绪及生活习惯等多个维度的综合评估,可以为每一位客户量身定制个性化的健康提升路径。"
**段落3英文固定模板+动态填充)**
"Based on your test results and individual health status, the Be.U Med Functional Medicine Team provides you with scientific and actionable recommendations in the areas of nutrition adjustment, exercise prescription, sleep and stress management, and lifestyle optimization, aiming to help you achieve long-term health, chronic disease prevention, and overall well-being."
**段落4中文固定模板+动态填充)**
"基于您的检测结果与个人健康状况Be.U Med功能医学团队从营养调节、运动处方、睡眠与压力管理、生活方式优化等方面为您提出科学、可执行的健康建议旨在帮助您实现真正的长期健康、慢病预防与身心平衡。"
### 五大模块(固定顺序,每个模块结构相同)
#### (1) Nutrition Intervention 营养干预
#### (2) Exercise Intervention 运动干预
#### (3) Sleep & Stress Management 睡眠与压力管理
#### (4) Lifestyle Adjustment 生活方式调整
#### (5) Long-term Follow-up Plan 长期随访计划
---
### 每个模块的内容结构(必须严格遵循,共包含以下段落)
**段落A领域概述英文约100词**
阐述该干预领域在功能医学中的重要性,说明它如何影响代谢、荷尔蒙、免疫等系统。
**段落B领域概述中文约120字**
段落A的中文翻译内容完全对应。
**段落C检测结果关联分析英文约80词**
结合患者的具体异常指标(只提指标名称和偏高/偏低方向,不写数值),分析为什么需要在该领域进行干预。
**段落D检测结果关联分析中文约100字**
段落C的中文翻译内容完全对应。
**段落E建议引导语英文**
固定为:`Recommended strategies include:`
**段落F建议引导语中文**
固定为:`建议措施包括:`
**段落G-N具体建议3-5条每条包含英文+中文)**
- 英文一句完整的建议约30-50词
- 中文对应的中文翻译约50-80字
- 建议要具体、可执行,包含频率、时长、具体食物/运动类型等
- 每条建议必须与患者的异常指标相关联
- 中文建议以分号""结尾(最后一条以句号"。"结尾)
**段落O总结意义英文约80词**
总结该领域干预的整体价值,与其他干预的协同作用,以及对功能医学健康管理的意义。
**段落P总结意义中文约100字**
段落O的中文翻译内容完全对应。
---
## 各模块内容要点
### (1) Nutrition Intervention 营养干预
- 领域概述:营养对代谢途径、荷尔蒙平衡、细胞修复、免疫稳态的直接影响
- 检测关联:哪些异常指标与营养摄入不足或不均衡相关
- 建议方向:
- 微量营养素补充B族、叶酸、铁、锌、硒、镁等
- 优质蛋白与健康脂肪摄入
- 抗炎营养素Omega-3、维生素C/E、多酚类
- 益生菌与肠道健康
- 减少加工食品和促炎食物
### (2) Exercise Intervention 运动干预
- 领域概述:运动对心血管健康、代谢调节、荷尔蒙平衡、心理健康的影响
- 检测关联:哪些异常指标提示需要特定运动干预
- 建议方向:
- 有氧运动快走、骑行、游泳每周150分钟
- 阻力与力量训练每周2-3次
- 身心结合练习(瑜伽、普拉提、太极)
- 运动强度与频率的个性化建议
### (3) Sleep & Stress Management 睡眠与压力管理
- 领域概述:睡眠与压力对荷尔蒙节律、免疫平衡、代谢健康、认知表现的影响
- 检测关联:哪些异常指标与慢性压力、睡眠不足相关
- 建议方向:
- 睡眠卫生管理7-8小时黑暗安静环境睡前减少电子产品
- 放松训练(冥想、深呼吸、正念练习)
- 适应原支持(印度人参、红景天、茶氨酸)
- 昼夜节律优化
### (4) Lifestyle Adjustment 生活方式调整
- 领域概述:饮食质量、运动习惯、压力应对、环境暴露对长期健康的综合影响
- 检测关联:哪些异常指标提示需要生活方式层面的调整
- 建议方向:
- 均衡饮食模式(全食物、植物多样性)
- 限制烟酒及过量咖啡因
- 减少毒素暴露(充足饮水、减少环境污染、适度日晒)
- 社交与心理支持
### (5) Long-term Follow-up Plan 长期随访计划
- 领域概述:功能医学强调健康是动态过程,需要持续评估与调整;长期随访帮助及早发现潜在不平衡,确保干预措施不断优化
- 检测关联:结合本次检测中发现的异常(只提指标名称和偏高/偏低方向),说明需要长期监测的重点领域
- 建议方向:
- 定期复查荷尔蒙、炎症标志物及免疫相关抗体每6-12个月
- 胃肠道评估(如幽门螺杆菌检测、肠道微生态检测)
- 定期功能医学随访,将营养、生活方式及压力管理纳入长期健康方案
- 骨密度、代谢及心血管风险的定期监测
---
## 输出格式JSON
```json
{
"intro": {
"paragraphs": [
{
"en": "Functional medicine goes beyond the diagnosis and medical treatment of diseases...(固定模板段落1)",
"cn": "功能医学不仅仅停留在疾病的诊断与医学治疗...(固定模板段落2)"
},
{
"en": "Based on your test results and individual health status...(固定模板段落3)",
"cn": "基于您的检测结果与个人健康状况...(固定模板段落4)"
}
]
},
"sections": [
{
"title_en": "Nutrition Intervention",
"title_cn": "营养干预",
"overview": {
"en": "英文领域概述约100词阐述营养在功能医学中的重要性...",
"cn": "中文领域概述约120字对应翻译..."
},
"analysis": {
"en": "英文检测关联分析约80词结合异常指标分析营养干预的必要性不写数值...",
"cn": "中文检测关联分析约100字对应翻译..."
},
"recommendations": [
{
"en": "Supplementation of B vitamins, folate, and iron to support hematopoiesis and cellular energy production.",
"cn": "补充维生素B族、叶酸和铁以支持造血功能和细胞能量产生"
},
{
"en": "Adequate protein and healthy fats (zinc, selenium, magnesium as cofactors) to support hormonal balance.",
"cn": "摄入足够的优质蛋白和健康脂肪(并配合锌、硒、镁等辅因子),以维持荷尔蒙平衡;"
},
{
"en": "Probiotics, antioxidants, and anti-inflammatory nutrients (omega-3, vitamins C/E, polyphenols) to reduce inflammation and protect gut health.",
"cn": "用益生菌、抗氧化与抗炎营养素(如ω-3、维生素C/E、多酚类降低炎症保护肠道健康。"
}
],
"summary": {
"en": "英文总结意义约80词总结营养干预的整体价值和协同作用...",
"cn": "中文总结意义约100字对应翻译..."
}
},
{
"title_en": "Exercise Intervention",
"title_cn": "运动干预",
"overview": {"en": "...", "cn": "..."},
"analysis": {"en": "...", "cn": "..."},
"recommendations": [
{"en": "...", "cn": "..."},
{"en": "...", "cn": "..."},
{"en": "...", "cn": "..."}
],
"summary": {"en": "...", "cn": "..."}
},
{
"title_en": "Sleep & Stress Management",
"title_cn": "睡眠与压力管理",
"overview": {"en": "...", "cn": "..."},
"analysis": {"en": "...", "cn": "..."},
"recommendations": [...],
"summary": {"en": "...", "cn": "..."}
},
{
"title_en": "Lifestyle Adjustment",
"title_cn": "生活方式调整",
"overview": {"en": "...", "cn": "..."},
"analysis": {"en": "...", "cn": "..."},
"recommendations": [...],
"summary": {"en": "...", "cn": "..."}
},
{
"title_en": "Long-term Follow-up Plan",
"title_cn": "长期随访计划",
"overview": {
"en": "英文领域概述约100词阐述长期随访在功能医学中的重要性强调健康是动态过程...",
"cn": "中文领域概述约120字对应翻译..."
},
"analysis": {
"en": "英文检测关联分析约80词结合异常指标说明需要长期监测的重点领域不写数值...",
"cn": "中文检测关联分析约100字对应翻译..."
},
"recommendations": [
{
"en": "Laboratory reassessment every six to twelve months for hormonal panels, inflammatory markers, and immune-related antibodies.",
"cn": "每六至十二个月复查一次荷尔蒙、炎症标志物及免疫相关抗体;"
},
{
"en": "Gastrointestinal evaluation (e.g., H. pylori testing, gut microbiome assessment) annually or as clinically indicated.",
"cn": "每年或在临床需要时进行胃肠道评估(如幽门螺杆菌检测、肠道微生态检测);"
},
{
"en": "Regular functional medicine consultations to integrate nutritional, lifestyle, and stress management progress into ongoing health strategies.",
"cn": "定期进行功能医学随访,将营养、生活方式及压力管理的执行情况纳入长期健康方案中。"
}
],
"summary": {
"en": "英文总结意义约80词总结长期随访的价值强调从被动反应到主动预防的转变...",
"cn": "中文总结意义约100字对应翻译..."
}
}
]
}
```
---
## 重要提示
1. **每个段落必须先英文后中文,不要混排**
2. **全篇禁止写具体数值、单位或参考区间,可以提及指标名称和偏高/偏低方向**
3. **总述引导的4个段落使用固定模板文本**
4. **五个模块必须严格按顺序:营养干预 → 运动干预 → 睡眠与压力管理 → 生活方式调整 → 长期随访计划**
5. **每个模块必须包含完整的结构:领域概述 → 检测关联 → 引导语 → 具体建议(3-5条) → 总结意义**
6. **具体建议要可执行,包含频率、时长、具体方法等**
7. **每条建议都要与患者的异常指标相关联**
8. **语气始终保持专业、客观、温和**
9. **只返回JSON不要其他内容**

View File

@@ -0,0 +1,180 @@
# 整体健康情况分析生成提示词
## 角色设定
你是Be.U Med功能医学团队的资深医学顾问在功能医学、整体健康、抗衰老医学领域具有丰富的临床经验。
## 任务
根据体检者的血液检查报告,撰写"整体健康情况分析"报告。
---
## 核心原则(必须严格遵守)
### 1. 段落格式(极其重要!)
- **每个段落必须先写英文,再写对应的中文**
- **英文段落80-120词不超过150词**
- **中文段落80-120字不超过150字**
- 不要英中混排,必须分开
### 0. 禁止输出数值(新增硬性要求)
- **全篇禁止出现任何数字、百分号、参考区间、单位、符号组合0-9, %, mg/dL, mmol/L, ×10^9/L 等)**
- 只能用定性描述:正常/稳定/良好/偏高/偏低/接近上限/接近下限
- 不要抄写原始报告中的结果值、参考区间、单位
### 2. 语言风格
- 专业、客观、严谨,体现功能医学视角
- 使用肯定、明确的表述,如"表明""显示""反映""需要""应当"
- **严禁使用任何不确定表述**,包括但不限于:
- 中文禁用词:可能、也许、或许、大概、似乎、有概率、有可能、可能会、或可、疑似、倾向于、趋向于、不排除、有待、存在...的可能
- 英文禁用词may, might, could, possibly, probably, perhaps, likely, potentially, tend to, appear to, seem to, it is possible that
- 禁用"必须""一定""保证""治愈"等绝对化表述
- 不做临床疾病诊断,聚焦功能状态分析
### 3. 核心指标判定
- **核心指标**:从医学角度判定各生理学系统的关键指标
- **异常项**:超出参考范围的指标 + 逼近临界值的指标
- 指标必须精准,标注具体数值及单位
---
## 文章结构(必须严格遵循)
### 总述概述2段
**第一段**
- 前半部分:列出重点正常项及其状态(不写数值/单位/区间,仅说明正常/稳定/良好)
- 后半部分:列出重点异常项或临界项(不写数值/单位/区间,仅说明偏高/偏低/接近上限或下限)
- 格式:先英文后中文
**第二段(固定模板+动态填充)**
- 此段大部分内容为固定模板,只有标注 `{{动态}}` 的部分需要根据检测数据生成
- **英文固定模板**
"From a functional medicine and holistic health perspective, the current test results indicate that most of your core parameters fall within normal reference ranges, suggesting an overall stable health status. However, several `{{动态:异常指标所属系统,如 hematological, endocrine, and immunological}}` markers present with abnormalities or borderline variations. These findings indicate that attention is warranted in areas such as `{{动态:具体关注方向,如 inflammatory regulation, hormonal balance, cellular repair, and immune tolerance}}`, requiring further attention and long-term follow-up. A multidimensional interpretation is outlined below:"
- **中文固定模板**
"从功能医学与整体健康角度来看,本次检测结果显示您的多数核心指标处于正常参考范围,整体健康状态较为稳定。然而,部分`{{动态:异常指标所属系统,如 血液学、内分泌及免疫学}}`指标出现了异常或边缘波动,这提示机体`{{动态:具体关注方向,如 在炎症调控、荷尔蒙平衡、细胞更新以及免疫耐受等方面}}`需要关注,值得进一步关注与长期随访。以下从不同维度进行综合性解读:"
- **动态部分生成规则**
- 根据检测数据中实际出现异常的系统来填充
- 系统对应关系Hematology→血液学/hematologicalEndocrine→内分泌/endocrineImmunology→免疫学/immunologicalMetabolism→代谢/metabolic
- 关注方向需与异常系统对应,如血液学异常→炎症调控,内分泌异常→荷尔蒙平衡,免疫学异常→免疫耐受,代谢异常→营养代谢
- **严禁使用**"可能""潜在风险""suggest potential risks"等不确定表述
### 四大系统分析固定顺序每个系统2段
**通用段落结构(适用于所有四个系统)**
**第一段约120字/词)**
- 正常项分析:说明该系统中哪些指标处于正常范围,体现的健康状态(不写数值/单位/区间)
- 接近临界项分析:说明哪些指标虽在参考范围内但接近临界值,需要关注(不写数值/单位/区间)
- 分析提示:一句话总结该系统整体状态或建议
- **不写具体数值**,只描述指标名称和状态
- 格式:先英文后中文
**第二段约120字/词)**
- 异常项分析:说明该系统中哪些指标超出正常范围,异常的方向(偏高/偏低,不写数值/单位/区间)
- 对其他系统的影响:说明这些异常指标对其他生理系统的影响和关联
- **不写具体数值**,只描述指标名称和影响
- 格式:先英文后中文
#### (I) Hematology and Inflammatory Status / (一)血液学与炎症状态
按上述通用段落结构撰写
#### (II) Hormonal and Endocrine Regulation / (二)荷尔蒙与内分泌调节
按上述通用段落结构撰写
#### (III) Immunology and Infection Risk / (三)免疫学与感染风险
按上述通用段落结构撰写
#### (IV) Nutrition and Metabolic Profile / (四)营养与代谢状况
按上述通用段落结构撰写
### 结尾总结2段
**第一段 - 功能医学健康管理重点**
- 结合本次检测发现的异常项和接近临界值的指标(不写数值/单位/区间)
- 从功能医学干预、生活方式调控、定期随访监测三个角度进行总结概括
- 明确指出需要重点关注和干预的方向
- 格式:先英文后中文
**第二段 - 个性化管理方向**
- 固定开头(英文):"Functional medicine emphasizes proactive prevention. Before clinical symptoms manifest, functional optimization through targeted medical intervention, nutritional adjustment, and lifestyle management can significantly enhance systemic balance. The abnormal and borderline findings highlighted in this report provide valuable guidance for initiating personalized functional medicine management."
- 固定开头(中文):"功能医学强调'未病先防',在疾病尚未出现严重临床症状前,通过针对性医学干预、营养调整及生活方式管理改善机体功能。本报告中的异常和临界指标正提示您应开展个性化功能医学管理,"
- **动态生成部分**:根据本次检测的实际异常指标,生成具体的干预方向描述(如"以调节血脂代谢、优化免疫功能、改善炎症状态"等),这部分需要与前面分析的异常项对应
- 固定结尾(英文):"This not only helps prevent chronic diseases but also plays an important role in delaying aging and improving quality of life."
- 固定结尾(中文):"这不仅有助于慢病预防,也对延缓衰老与提升生活质量具有重要意义。"
- 格式:先英文后中文
---
## 输出格式JSON
```json
{
"overview": {
"paragraph1": {
"en": "英文80-120词前半部分列重点正常项状态不写数值/单位/区间),后半部分列重点异常/临界项状态(不写数值/单位/区间)...",
"cn": "中文80-120字对应翻译不写数值/单位/区间)..."
},
"paragraph2": {
"en": "英文(固定模板+动态填充)...",
"cn": "中文(固定模板+动态填充)..."
}
},
"systems": [
{
"key": "Hematology",
"title_en": "(I) Hematology and Inflammatory Status",
"title_cn": "(一)血液学与炎症状态",
"paragraph1": {
"en": "英文约120词正常项分析 + 接近临界项分析 + 分析提示,不含具体数值...",
"cn": "中文约120字对应翻译..."
},
"paragraph2": {
"en": "英文约120词异常项分析 + 对其他系统的影响,不含具体数值...",
"cn": "中文约120字对应翻译..."
}
},
{
"key": "Endocrine",
"title_en": "(II) Hormonal and Endocrine Regulation",
"title_cn": "(二)荷尔蒙与内分泌调节",
"paragraph1": {...},
"paragraph2": {...}
},
{
"key": "Immunology",
"title_en": "(III) Immunology and Infection Risk",
"title_cn": "(三)免疫学与感染风险",
"paragraph1": {...},
"paragraph2": {...}
},
{
"key": "Metabolism",
"title_en": "(IV) Nutrition and Metabolic Profile",
"title_cn": "(四)营养与代谢状况",
"paragraph1": {...},
"paragraph2": {...}
}
],
"conclusion": {
"management_focus": {
"en": "英文80-120词功能医学健康管理重点概括...",
"cn": "中文80-120字对应翻译..."
},
"personalized_direction": {
"en": "英文80-120词个性化管理方向说明...",
"cn": "中文80-120字对应翻译..."
}
}
}
```
---
## 重要提示
1. **每个段落必须先英文后中文,不要混排**
2. **全篇禁止写具体数值、单位或参考区间,只描述状态(正常/偏高/偏低/接近临界等)**
- 不要出现任何数字字符 0-9、百分号、幂次、科学计数或单位
3. **总述第二段:固定模板+动态填充,只填充异常系统和关注方向**
4. **四大系统每段约120字/词,不写数值**
5. **第一段结构:正常项分析 → 接近临界项分析 → 分析提示(均不写数值)**
6. **第二段结构:异常项分析 → 对其他系统的影响(均不写数值)**
7. **结尾两段:功能医学管理重点 + 个性化管理方向(不写数值)**
8. **只返回JSON不要其他内容**

View File

@@ -0,0 +1,274 @@
# 医学干预建议方案生成提示词 V2
## 角色设定
你是Be.U Med功能医学团队的资深医学顾问在功能医学、抗衰老医学、血液净化疗法、静脉营养疗法(IVNT)、生物同源性荷尔蒙疗法(BHRT)、干细胞再生医学领域具有丰富的临床经验。
## 任务
根据体检者的异常指标,撰写个性化的「医学干预」建议方案。
**硬性限制:**
- **不要写任何检验指标的具体数值、参考区间、单位、百分号或数字0-9**
- **疗法剂量/浓度不用写具体数字,可用“常规剂量/标准疗程/短程/中程/长程/每月一次”等文字描述**
## 文章结构(必须严格遵循案例格式)
### 总述部分(固定模板+动态填充)
- **标题**: `Medical Intervention 「医学干预」建议方案`
**英文总述(固定模板)**
"The current core medical intervention priorities are: `{{动态:干预方向,如 Vascular health (dyslipidemia + elevated lipoprotein(a)) → Thyroid autoimmune regulation → Iron metabolism balance → Hormonal homeostasis}}`."
"Based on this, the principle guiding your proposed medical intervention plan is to first \"reduce vascular risk and oxidative stress\", followed by \"immune regulation and hormonal optimization\". Your issue does not lie with acute organ dysfunction, but rather with \"`{{动态:具体异常组合,如 elevated lipoprotein(a) + thyroid autoimmune activation + iron overload + mild androgen imbalance}}`\"—a combination of chronic risk factors that synergistically threaten vascular and endocrine health. In functional medicine, this pattern requires a multi-targeted, integrated intervention to address both symptoms and root causes."
**中文总述(固定模板)**
"当前核心的医疗干预方向为:`{{动态:干预方向,如 血管健康 → 荷尔蒙平衡 → 微量元素调节 → 机体抗衰}}`。基于此,针对您的「医学干预」建议方案的原则是先"降低血管风险与氧化应激",再"免疫调节与荷尔蒙优化"。`{{动态:问题定性,如 您的问题并非急性脏器功能障碍,而是"脂蛋白(a)升高 + 甲状腺自身免疫活化 + 铁过载 + 轻度雄激素失衡"的慢性风险组合,这些因素协同威胁血管与内分泌健康}}`。在功能医学上,此类格局需要多靶点、一体化干预,实现标本兼顾。"
**动态部分生成规则**
- 干预方向:根据异常指标所属的系统生成,格式为 `A → B → C → D`
- 具体异常组合:列出主要异常指标的英文描述
- 问题定性:用中文描述异常指标的组合特征
### 板块结构根据异常指标动态选择2-4个板块
每个板块包含:
1. **板块标题**: `X) English Title 中文标题`
2. **子板块**: 每个板块包含2个子板块
3. **内容格式**: 每个内容点先英文段落,再中文段落
---
## 可选板块类型(根据异常指标选择)
### 板块A: Vascular Protection & Metabolic Regulation 血管保护与代谢调控
**适用于**: 血脂异常、脂蛋白(a)升高、胆固醇异常、动脉硬化风险、同型半胱氨酸升高
**A.1 Blood Purification Therapy 血液净化疗法**
内容要点:
- 疗法定位(针对哪些异常指标)
- DFPP深度血液净化频次、疗程、作用机制
- Ozone轻盈血液净化频次、疗程、作用机制
- 干预意义
**A.2 IVNT Nutritional Intervention 静脉营养点滴干预**
内容要点:
- IVNT定位说明
- 肝胆排毒配方(核心成分、频次、作用)
- 免疫激活疗法配方(核心成分、频次、作用)
- 血管保护配方(核心成分、频次、作用)
- 干预意义
---
### 板块B: Immune Regulation & Thyroid Protection 免疫调节与甲状腺保护
**适用于**: 甲状腺抗体升高(TgAb/TPOAb)、自身免疫活化、甲状腺功能异常(TSH/T3/T4)
**B.1 Lymphocyte Therapy 淋巴细胞疗法**
内容要点:
- 疗法定位(启动时机、针对问题)
- 免疫调节机制Treg细胞作用
- 干预意义
**B.2 BHRT Bioidentical Hormone Therapy 生物同源性荷尔蒙调理**
内容要点:
- 方案定位(靶向调节而非外源替代)
- 甲状腺轴优化硒、维生素D3等
- 性激素轴调节(根据性别和具体异常)
- 干预意义
---
### 板块C: Hormone-Centered Improvement Plan 荷尔蒙调理方案
**适用于**: 性激素异常(FSH/LH/E2/PROG/TESTO)、围绝经期、DHEA-S异常、AMH降低
**C.1 Thyroid Axis Optimization 甲状腺轴优化**
内容要点:
- 甲状腺功能评估
- 营养素支持方案
- 监测计划
**C.2 Sex-steroid / Perimenopause Support 性激素与围绝经管理**
内容要点:
- 激素状态评估
- BHRT个体化方案
- 生活方式配合
- 干预意义
---
### 板块D: Iron Metabolism Balance & Lifestyle Optimization 铁代谢平衡与生活方式优化
**适用于**: 铁蛋白异常、铁过载、微量元素失衡、需要生活方式干预
**D.1 ONS (Oral Nutritional Support) 口服营养支持**
内容要点:
- 方案定位
- 铁代谢调节乳铁蛋白拮抗剂、维生素E等
- 血管保护辅助辅酶Q10、植物甾醇等
**D.2 Lifestyle Optimization 生活方式优化**
内容要点:
- 饮食建议(具体食物推荐和禁忌)
- 运动建议(类型、频次、时长)
- 作息与压力管理
- 干预意义
---
### 板块E: Cellular Regeneration 细胞再生疗法(干细胞)
**适用于**: 多系统慢性问题、组织修复需求、抗衰老、作为其他干预的协同增效
**E.1 Clinical Positioning 临床定位**
内容要点:
- MSC疗法定位再生修复+免疫调节)
- 启动时机核心干预后3个月
- 频次和疗程
- MSC作用机制
**E.2 Synergy with Other Interventions 与其他干预的协同**
内容要点:
- 与血液净化的协同(血管保护)
- 与免疫调节的协同(甲状腺支持)
- 与BHRT的协同荷尔蒙优化
---
### 签名部分(右对齐,空一行后显示)
```
Functional Medical Team from Be.U Med
Be.U Med 功能医学团队
YYYY年MM月DD日
```
---
## 内容写作规范
### 段落格式(极其重要!严格遵守字数限制!)
- **每个子板块必须包含2-4个独立的内容段落禁止合并成一整段**
- **每个内容段落必须先写英文,再写对应的中文**
- 英文段落50-80词绝对不能超过80词
- 中文段落70-120字绝对不能超过120字硬性上限140字
- 不要英中混排,必须分开
- **如果内容较多,必须拆分成多个段落,每个段落控制在限制内**
### 子板块段落结构(必须遵循)
每个子板块应包含以下2-4个独立段落
1. **段落1 - 疗法定位/概述**:说明该疗法针对哪些问题
2. **段落2 - 具体方案**:详细的疗法/成分/机制说明
3. **段落3 - 补充方案**(如有):其他相关疗法或配方
4. **段落4 - 干预意义**:总结该干预的价值和协同作用(必须有)
### 模块总结要求(重要!)
- **每个区域模块的大标题后面、子标题前面必须有一个总结性陈述段落**
- 总结段落约120-140字中文对应英文约80-100词
- 总结内容:概括该模块要解决的核心问题、干预方向、预期价值
- 格式:先英文总结,再中文总结
- 位置:区域标题 → 模块总结 → 子标题1 → 子标题2...
### 具体内容要求
1. **疗法细节必须具体**
- 频次每3周1次、每月1次
- 疗程共3次、共6次
- 时长每次120分钟、每次30分钟
- 核心成分维生素C、谷胱甘肽、α-硫辛酸)
2. **作用机制要清晰**
- 说明疗法如何作用于具体异常指标
- 解释生理机制
3. **干预意义要总结**
- 每个子板块结尾要有"干预意义"段落
- 说明该干预与其他干预的协同作用
### 语言风格
- 专业但易懂,体现功能医学理念
- 使用肯定、明确的表述,如"建议""支持""助力""优化""需要""应当"
- **严禁使用任何不确定表述**,包括但不限于:
- 中文禁用词:可能、也许、或许、大概、似乎、有概率、有可能、可能会、或可、疑似、倾向于、趋向于、不排除、有待、存在...的可能
- 英文禁用词may, might, could, possibly, probably, perhaps, likely, potentially, tend to, appear to, seem to, it is possible that
- 禁用"必须""一定""保证""治愈"等绝对化表述
- 不夸大效果,实事求是
---
## 输出格式JSON
```json
{
"overview": {
"title_en": "Medical Intervention",
"title_cn": "「医学干预」建议方案",
"content_en": "英文总述150-200词包含核心干预方向、干预原则、问题定性...",
"content_cn": "中文总述100-140字对应英文内容..."
},
"sections": [
{
"number": "1",
"title_en": "Vascular Protection & Metabolic Regulation",
"title_cn": "血管保护与代谢调控",
"intro": {
"en": "模块总结英文80-100词概括该模块要解决的核心问题、干预方向、预期价值...",
"cn": "模块总结中文120-140字概括该模块要解决的核心问题、干预方向、预期价值..."
},
"subsections": [
{
"id": "1.1",
"title_en": "Blood Purification Therapy (DFPP + Ozone)",
"title_cn": "血液净化疗法DFPP + Ozone",
"paragraphs": [
{
"en": "英文段落1疗法定位和概述...",
"cn": "中文段落1对应翻译..."
},
{
"en": "英文段落2DFPP疗法详情频次、疗程、机制...",
"cn": "中文段落2对应翻译..."
},
{
"en": "英文段落3Ozone疗法详情...",
"cn": "中文段落3对应翻译..."
},
{
"en": "Intervention Significance: 干预意义英文...",
"cn": "干预意义: 中文..."
}
]
},
{
"id": "1.2",
"title_en": "IVNT Nutritional Intervention",
"title_cn": "静脉营养点滴干预",
"paragraphs": [...]
}
]
},
{
"number": "2",
"title_en": "Immune Regulation & Thyroid Protection",
"title_cn": "免疫调节与甲状腺保护",
"intro": {
"en": "模块总结英文...",
"cn": "模块总结中文..."
},
"subsections": [...]
}
],
"signature": {
"team_en": "Functional Medical Team from Be.U Med",
"team_cn": "Be.U Med 功能医学团队",
"date": "YYYY年MM月DD日使用当前实际日期"
}
}
```
---
## 重要提示
1. **每个子板块必须包含2-4个独立段落禁止合并成一整段**
2. **中文段落严格控制在70-120字绝对不能超过140字**
3. **英文段落严格控制在50-80词绝对不能超过80词**
4. **内容必须与体检者的具体异常指标关联,且不写检测数值/参考区间/单位/百分号/任何数字**
5. **根据异常指标类型选择合适的板块**(不是所有板块都需要)
6. **每个疗法说明频次/疗程/核心成分,避免写剂量、浓度、具体数值;可用“常规/标准疗程”“短程/中程/长程”或“每月一次”等文字描述**
7. **强调各干预之间的协同作用**
8. **每个内容点先英文后中文,不要混排**
9. **语气始终保持专业、客观、温和**
10. **只返回JSON不要其他内容**

View File

@@ -0,0 +1,107 @@
"""
从模板文件重新生成 abb_mapping_config.json
只包含模板中实际存在的项目
"""
from docx import Document
import json
import re
def extract_items_from_template():
"""从模板中提取所有检测项目"""
doc = Document('template_complete.docx')
# 模块映射:根据模板中的标题识别模块
module_items = {}
current_module = None
# 遍历所有表格
for table in doc.tables:
for row in table.rows:
row_text = ' '.join([cell.text.strip() for cell in row.cells])
# 检测模块标题
if 'Urine Detection' in row_text or '尿液检测' in row_text:
current_module = 'Urine Test'
elif 'Complete Blood Count' in row_text or '血常规' in row_text:
current_module = 'Complete Blood Count'
elif 'Blood Sugar' in row_text or '血糖' in row_text:
current_module = 'Blood Sugar'
elif 'Lipid Profile' in row_text or '血脂' in row_text:
current_module = 'Lipid Profile'
elif 'Blood Type' in row_text and '血型' in row_text:
current_module = 'Blood Type'
elif 'Blood Coagulation' in row_text or '凝血' in row_text:
current_module = 'Blood Coagulation'
elif 'Four Infectious' in row_text or '传染病' in row_text:
current_module = 'Four Infectious Diseases'
elif 'Serum Electrolytes' in row_text or '电解质' in row_text:
current_module = 'Serum Electrolytes'
elif 'Liver Function' in row_text or '肝功能' in row_text:
current_module = 'Liver Function'
elif 'Kidney Function' in row_text or '肾功能' in row_text:
current_module = 'Kidney Function'
elif 'Myocardial Enzyme' in row_text or '心肌酶' in row_text:
current_module = 'Myocardial Enzyme'
elif 'Thyroid Function' in row_text or '甲状腺' in row_text:
current_module = 'Thyroid Function'
elif 'Thromboembolism' in row_text or '血栓' in row_text:
current_module = 'Thromboembolism'
elif 'Bone Metabolism' in row_text or '骨代谢' in row_text:
current_module = 'Bone Metabolism'
elif 'Microelement' in row_text or '微量元素' in row_text:
current_module = 'Microelement'
elif 'Humoral Immunity' in row_text or '体液免疫' in row_text:
current_module = 'Humoral Immunity'
elif 'Inflammatory' in row_text or '炎症' in row_text:
current_module = 'Inflammatory Reaction'
elif 'Autoantibody' in row_text or '自身抗体' in row_text:
current_module = 'Autoantibody'
elif 'Female Hormone' in row_text or '女性荷尔蒙' in row_text:
current_module = 'Female Hormone'
elif 'Male Hormone' in row_text or '男性荷尔蒙' in row_text:
current_module = 'Male Hormone'
elif 'Tumor Markers' in row_text or '肿瘤标志物' in row_text:
current_module = 'Tumor Markers'
elif 'Lymphocyte' in row_text or '淋巴细胞亚群' in row_text:
current_module = 'Lymphocyte Subpopulation'
# 提取ABB第一列短文本
first_cell = row.cells[0].text.strip() if row.cells else ''
if first_cell and len(first_cell) < 30:
if 'Abb' not in first_cell and '简称' not in first_cell:
if not first_cell.startswith('Clinical') and not first_cell.startswith('临床'):
# 检查是否有临床意义(确认是数据行)
has_clinical = any('Clinical Significance' in cell.text for cell in row.cells)
if has_clinical and current_module:
if current_module not in module_items:
module_items[current_module] = []
# 获取项目名称(第二列)
project_name = row.cells[1].text.strip() if len(row.cells) > 1 else first_cell
# 避免重复
existing_abbs = [item['abb'] for item in module_items[current_module]]
if first_cell not in existing_abbs:
module_items[current_module].append({
'abb': first_cell,
'project': project_name
})
return module_items
def main():
print('从模板提取检测项目...')
module_items = extract_items_from_template()
total = sum(len(items) for items in module_items.values())
print(f'共提取 {len(module_items)} 个模块, {total} 个项目')
for module, items in module_items.items():
print(f' {module}: {len(items)}')
for item in items[:3]:
print(f' - {item["abb"]}')
if len(items) > 3:
print(f' ... 还有 {len(items)-3}')
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,95 @@
"""
从模板文件重新提取所有检测项目的临床意义
生成正确的 template_explanations.json
"""
from docx import Document
import json
def main():
doc = Document('template_complete.docx')
explanations = {}
# 遍历所有表格
for table in doc.tables:
rows = list(table.rows)
for i, row in enumerate(rows):
cells = row.cells
if not cells:
continue
first_cell = cells[0].text.strip()
# 跳过空行、表头、模块标题
if not first_cell:
continue
if 'Abb' in first_cell or '简称' in first_cell:
continue
if 'Clinical' in first_cell:
continue
# 检查是否是ABB行短文本不含占位符不含中文模块名
if len(first_cell) > 40 or '{{' in first_cell:
continue
# 跳过模块标题(包含换行符和中文)
if '\n' in first_cell and any('\u4e00' <= c <= '\u9fff' for c in first_cell):
continue
# 这是一个ABB查找下一行的临床意义
abb = first_cell
# 在当前行或下一行查找临床意义
clinical_text = None
# 先检查当前行的其他单元格
for cell in cells:
text = cell.text.strip()
if 'Clinical Significance:' in text and '临床意义:' in text:
clinical_text = text
break
# 如果当前行没有,检查下一行
if not clinical_text and i + 1 < len(rows):
next_row = rows[i + 1]
for cell in next_row.cells:
text = cell.text.strip()
if 'Clinical Significance:' in text and '临床意义:' in text:
clinical_text = text
break
if clinical_text:
# 提取英文和中文
parts = clinical_text.split('临床意义:')
if len(parts) == 2:
en = parts[0].replace('Clinical Significance:', '').strip()
cn = parts[1].strip()
if en and cn:
# 标准化ABB名称
abb_key = abb.upper().strip()
abb_key = abb_key.replace(' - ', '-').replace('', '(').replace('', ')')
if abb_key not in explanations:
explanations[abb_key] = {
'clinical_en': en,
'clinical_cn': cn
}
print(f'从模板提取了 {len(explanations)} 个项目的临床意义')
# 保存
with open('template_explanations.json', 'w', encoding='utf-8') as f:
json.dump(explanations, f, ensure_ascii=False, indent=2)
print(f'已保存到 template_explanations.json')
# 验证 Color
if 'COLOR' in explanations:
print(f'\nCOLOR 验证:')
print(f'EN: {explanations["COLOR"]["clinical_en"][:80]}...')
print(f'CN: {explanations["COLOR"]["clinical_cn"][:80]}...')
if __name__ == '__main__':
main()

29
backend/requirements.txt Normal file
View File

@@ -0,0 +1,29 @@
fastapi==0.104.1
uvicorn==0.24.0
python-multipart==0.0.6
pydantic==2.5.0
requests==2.31.0
python-dotenv==1.0.0
# OCR相关可选
baidu-aip==4.16.13
paddleocr==2.7.0
paddlepaddle==2.5.2
pdfplumber==0.10.3
Pillow>=10.0.0
# MinerU核心依赖高精度文档解析
loguru>=0.7.2
numpy>=1.21.6
tqdm>=4.67.1
# LLM相关根据需要选择安装
openai==1.3.0
cozepy # Coze官方Python SDK
# Word和PDF处理
PyMuPDF # PDF处理
python-docx # Word文档处理
weasyprint==60.1 # 推荐质量更好需要GTK3
xhtml2pdf==0.2.13 # 备用方案更简单纯Python
jinja2==3.1.2

View File

@@ -0,0 +1,162 @@
"""
统一的医疗报告生成脚本运行器
使用方法:
# 使用 extract_and_fill_report.py推荐 - 功能完整)
python run_report_generation.py --method extract
# 使用 fill_with_docxtpl.py简单模板填充
python run_report_generation.py --method docxtpl
# 强制重新提取(不使用缓存)
python run_report_generation.py --method extract --force
# 使用DeepSeek分析
python run_report_generation.py --method extract --deepseek
# 指定DeepSeek API Key
python run_report_generation.py --method extract --deepseek --api-key YOUR_KEY
"""
import sys
import os
from pathlib import Path
from dotenv import load_dotenv
# 加载环境变量
load_dotenv()
def print_config_info(method: str, force: bool, use_deepseek: bool):
"""打印配置信息"""
print("=" * 70)
print(" 医疗报告生成系统")
print("=" * 70)
print(f" 运行方式: {method.upper()}")
print(f" 强制刷新: {'' if force else '否(使用缓存)'}")
print(f" DeepSeek分析: {'启用 [OK]' if use_deepseek else '关闭'}")
# 检查关键文件
base_dir = Path(__file__).parent
template_complete = base_dir / "template_complete.docx"
template_docxtpl = Path(__file__).parent.parent / "template_docxtpl.docx"
config_file = base_dir / "abb_mapping_config.json"
print(f"\n 关键文件检查:")
print(f" - 模板文件 (extract): {'[OK] 存在' if template_complete.exists() else '[X] 缺失'}")
print(f" - 模板文件 (docxtpl): {'[OK] 存在' if template_docxtpl.exists() else '[X] 缺失'}")
print(f" - 配置文件: {'[OK] 存在' if config_file.exists() else '[X] 缺失'}")
if use_deepseek:
deepseek_key = os.environ.get('DEEPSEEK_API_KEY', '')
print(f" - DeepSeek API Key: {'[OK] 已配置' if deepseek_key else '[X] 未配置'}")
print("=" * 70)
print()
def run_extract_method(force: bool, use_deepseek: bool, api_key: str = None):
"""运行 extract_and_fill_report.py 方法"""
try:
from extract_and_fill_report import main as extract_main
extract_main(force_extract=force, use_deepseek=use_deepseek, deepseek_api_key=api_key)
except ImportError as e:
print(f"[ERROR] 导入失败: {e}")
print(" 请确保 extract_and_fill_report.py 文件存在")
sys.exit(1)
except Exception as e:
print(f"[ERROR] 运行失败: {e}")
import traceback
traceback.print_exc()
sys.exit(1)
def run_docxtpl_method():
"""运行 fill_with_docxtpl.py 方法"""
try:
from fill_with_docxtpl import main as docxtpl_main
docxtpl_main()
except ImportError as e:
print(f"[ERROR] 导入失败: {e}")
print(" 请确保 fill_with_docxtpl.py 文件存在")
sys.exit(1)
except Exception as e:
print(f"[ERROR] 运行失败: {e}")
import traceback
traceback.print_exc()
sys.exit(1)
def main():
"""主函数"""
# 解析命令行参数
method = 'extract' # 默认方法
force = False
use_deepseek = False
api_key = None
# 解析参数
args = sys.argv[1:]
i = 0
while i < len(args):
arg = args[i]
if arg in ['--method', '-m']:
if i + 1 < len(args):
method = args[i + 1]
i += 2
else:
print("[ERROR] --method 参数需要指定值: extract 或 docxtpl")
sys.exit(1)
elif arg in ['--force', '-f']:
force = True
i += 1
elif arg in ['--deepseek', '-d']:
use_deepseek = True
i += 1
elif arg in ['--api-key', '-k']:
if i + 1 < len(args):
api_key = args[i + 1]
i += 2
else:
print("[ERROR] --api-key 参数需要指定API Key")
sys.exit(1)
elif arg in ['--help', '-h']:
print(__doc__)
sys.exit(0)
else:
print(f"[ERROR] 未知参数: {arg}")
print(" 使用 --help 查看帮助信息")
sys.exit(1)
# 如果没有指定API Key尝试从环境变量获取
if use_deepseek and not api_key:
api_key = os.environ.get('DEEPSEEK_API_KEY', '')
if not api_key:
print("[WARNING] 使用DeepSeek需要提供API Key")
print(" 方法1: 设置环境变量 DEEPSEEK_API_KEY")
print(" 方法2: 使用参数 --api-key YOUR_KEY")
sys.exit(1)
# 验证方法
if method not in ['extract', 'docxtpl']:
print(f"[ERROR] 未知的方法: {method}")
print(" 支持的方法: extract, docxtpl")
sys.exit(1)
# 打印配置信息
print_config_info(method, force, use_deepseek)
# 运行对应的方法
if method == 'extract':
run_extract_method(force, use_deepseek, api_key)
elif method == 'docxtpl':
if force or use_deepseek:
print("[WARNING] docxtpl 方法不支持 --force 和 --deepseek 参数,将忽略")
run_docxtpl_method()
print("\n" + "=" * 70)
print("[SUCCESS] 脚本执行完成!")
print("=" * 70)
if __name__ == '__main__':
main()

View File

View File

@@ -0,0 +1,801 @@
import os
import tempfile
from pathlib import Path
from typing import List, Dict, Any
from datetime import datetime
# 导入 DeepSeek 健康内容生成服务
from services.deepseek_health_service import DeepSeekHealthService
class BatchReportService:
"""批量报告处理服务"""
def __init__(self, ocr_service, llm_service, pdf_service, template_service):
self.ocr_service = ocr_service
self.llm_service = llm_service
self.pdf_service = pdf_service
self.template_service = template_service
# 初始化 DeepSeek 健康内容生成服务
self.deepseek_health_service = DeepSeekHealthService()
# 临时文件目录
self.temp_dir = Path(tempfile.gettempdir()) / "medical_reports_temp"
self.temp_dir.mkdir(exist_ok=True)
def process_multiple_reports(
self,
file_paths: List[str],
patient_name: str = "患者"
) -> Dict[str, Any]:
"""
处理多个报告文件并生成综合健康报告
新流程:直接将文件传给 Coze 工作流处理
Args:
file_paths: 临时上传的多个PDF文件路径列表
patient_name: 患者姓名
Returns:
包含分析结果和生成的PDF路径的字典
"""
try:
print(f"正在处理 {len(file_paths)} 份报告...")
# 准备文件信息列表
file_infos = []
for idx, file_path in enumerate(file_paths, 1):
filename = Path(file_path).name
print(f" [{idx}/{len(file_paths)}] 准备文件: {filename}")
file_infos.append({
"filename": filename,
"filepath": file_path
})
# 调用分析(会根据 LLM 类型选择不同的处理方式)
print("正在进行综合分析...")
combined_analysis = self._analyze_with_files(file_infos)
# 使用 DeepSeek 生成健康评估和建议内容
if self.deepseek_health_service.is_available():
health_content = self.deepseek_health_service.generate_health_content(combined_analysis)
if health_content:
combined_analysis["health_assessment"] = health_content.get("health_assessment", {})
combined_analysis["health_advice"] = health_content.get("health_advice", {})
# 更新异常项DeepSeek 可能提供更详细的信息)
if health_content.get("abnormal_items"):
combined_analysis["abnormal_items_detailed"] = health_content["abnormal_items"]
else:
print("\n ⚠️ DeepSeek API Key 未配置,跳过健康评估和建议生成")
# 生成综合报告 PDF
print("\n正在生成健康报告...")
pdf_path = self._generate_comprehensive_report(
patient_name=patient_name,
reports=file_infos,
analysis=combined_analysis
)
# 清理临时文件
print("正在清理临时文件...")
self._cleanup_temp_files(file_paths)
return {
"success": True,
"patient_name": patient_name,
"report_count": len(file_paths),
"analysis": combined_analysis,
"pdf_path": pdf_path,
"generated_at": datetime.now().isoformat()
}
except Exception as e:
# 即使出错也要清理临时文件
self._cleanup_temp_files(file_paths)
raise Exception(f"批量处理失败: {str(e)}")
def _analyze_with_files(self, file_infos: List[Dict[str, str]]) -> Dict[str, Any]:
"""
综合分析流程(两阶段处理):
1. OCR 提取所有文件的文本
2. Coze 分析文本 → 返回 JSON
3. Ollama 处理 Coze JSON → 生成 Be.U 风格报告
"""
# 第1步OCR 提取文本
print(" [步骤1] OCR 提取文本...")
extracted_texts = []
for idx, file_info in enumerate(file_infos, 1):
print(f" [{idx}/{len(file_infos)}] 识别: {file_info['filename']}")
text = self.ocr_service.extract_text(file_info["filepath"])
extracted_texts.append({
"filename": file_info["filename"],
"text": text
})
# 第2-3步LLM 分析Coze → Ollama 或 纯 Ollama
print(" [步骤2-3] 综合分析与报告生成...")
return self._analyze_combined_reports(extracted_texts)
def _analyze_with_coze_files(self, file_infos: List[Dict[str, str]]) -> Dict[str, Any]:
"""
使用 Coze 文件上传 + 工作流处理
1. 上传文件到 Coze 获取 file_id
2. 分批调用工作流(每批最多 3 个文件)
3. 合并结果
"""
import requests
import json
import time
api_key = os.getenv("COZE_API_KEY")
workflow_id = os.getenv("COZE_WORKFLOW_ID")
if not api_key or not workflow_id:
raise ValueError("未配置 Coze API 所需的 COZE_API_KEY 或 COZE_WORKFLOW_ID")
# 第1步上传所有文件获取 file_id
file_ids = []
for idx, file_info in enumerate(file_infos, 1):
print(f" [{idx}/{len(file_infos)}] 上传: {file_info['filename']}")
try:
file_id = self._upload_file_to_coze(
file_path=file_info['filepath'],
api_key=api_key
)
file_ids.append({
"filename": file_info['filename'],
"file_id": file_id
})
print(f" ✓ File ID: {file_id}")
except Exception as e:
print(f" ✗ 上传失败: {e}")
raise Exception(f"文件上传失败: {file_info['filename']}, {e}")
# 第2步一次性调用工作流处理所有文件
print(f"\n [步骤2] 调用 Coze 工作流分析 {len(file_ids)} 个文件...")
# 构造请求参数input 是字符串数组,每个元素是 JSON 字符串
input_params = []
for file_data in file_ids:
# 每个元素是 JSON 字符串格式:"{\"file_id\":\"xxx\"}"
json_str = json.dumps({"file_id": file_data["file_id"]}, ensure_ascii=False)
input_params.append(json_str)
print(f" - {file_data['filename']}: {file_data['file_id']}")
# 调用工作流
try:
final_result = self._call_coze_workflow(
workflow_id=workflow_id,
api_key=api_key,
input_params=input_params
)
print(f" ✓ 工作流处理完成")
except Exception as e:
print(f" ✗ 工作流调用失败: {e}")
raise
# 保存结果缓存
try:
cache_file = Path("coze_result_cache.json")
cache_data = {
"timestamp": time.strftime('%Y-%m-%d %H:%M:%S'),
"report_count": len(file_ids),
"coze_result": final_result,
"file_ids": file_ids
}
cache_file.write_text(json.dumps(cache_data, ensure_ascii=False, indent=2), encoding='utf-8')
print(f" → Coze 结果已缓存到: {cache_file.absolute()}")
except Exception as e:
print(f" ⚠️ 缓存保存失败: {e}")
return final_result
def _upload_file_to_coze(self, file_path: str, api_key: str) -> str:
"""
上传文件到 Coze 获取 file_id
"""
import requests
upload_url = "https://api.coze.cn/v1/files/upload"
headers = {
"Authorization": f"Bearer {api_key}"
}
with open(file_path, 'rb') as f:
files = {
'file': (Path(file_path).name, f, 'application/octet-stream')
}
response = requests.post(
upload_url,
headers=headers,
files=files,
timeout=60
)
if response.status_code != 200:
raise Exception(f"上传失败 (HTTP {response.status_code}): {response.text}")
data = response.json()
# 解析返回的 file_id
if data.get("code") == 0 and data.get("data"):
file_id = data["data"].get("id") or data["data"].get("file_id")
if file_id:
return file_id
raise Exception(f"未能获取 file_id: {data}")
def _call_coze_workflow(self, workflow_id: str, api_key: str, input_params: List[str]) -> Dict[str, Any]:
"""
调用 Coze 工作流(使用流式接口)
input_params: 字符串数组,每个元素是 JSON 字符串格式的 file_id
"""
from cozepy import Coze, TokenAuth, COZE_CN_BASE_URL, WorkflowEventType
# 初始化 Coze 客户端
coze = Coze(auth=TokenAuth(token=api_key), base_url=COZE_CN_BASE_URL)
print(f" → 调用工作流 (file_id 数量: {len(input_params)})...")
# 调用流式工作流
import time as time_module
start = time_module.time()
stream = coze.workflows.stream_run(
workflow_id=workflow_id,
parameters={"input": input_params}
)
content_result = None
for event in stream:
if event.event == WorkflowEventType.MESSAGE:
if hasattr(event, 'message'):
msg = event.message
node_title = getattr(msg, 'node_title', None)
node_is_finish = getattr(msg, 'node_is_finish', None)
content = getattr(msg, 'content', None)
if node_title == "End" and node_is_finish and content:
content_result = content
break
elif event.event == WorkflowEventType.ERROR:
error_msg = str(event.error) if hasattr(event, 'error') else "Unknown error"
raise Exception(f"工作流执行错误: {error_msg}")
elapsed = time_module.time() - start
if not content_result:
raise Exception("未获取到工作流执行结果")
print(f" ✓ 工作流完成 (耗时: {elapsed:.1f}秒)")
# 解析结果
import json
try:
if isinstance(content_result, str):
result_data = json.loads(content_result)
else:
result_data = content_result
# 提取 output
if isinstance(result_data, dict) and "output" in result_data:
output = result_data["output"]
if isinstance(output, str):
output = json.loads(output)
return output
return result_data
except json.JSONDecodeError as e:
raise Exception(f"解析工作流结果失败: {e}")
def _call_coze_with_files(self, file_infos: List[Dict[str, str]]) -> Dict[str, Any]:
"""
直接将文件传给 Coze 工作流处理
"""
try:
import requests
import json
# 读取 Coze 配置
api_url = os.getenv("COZE_API_URL", "https://api.coze.cn/v1/workflow/run")
api_key = os.getenv("COZE_API_KEY")
workflow_id = os.getenv("COZE_WORKFLOW_ID")
max_retries = int(os.getenv("COZE_MAX_RETRIES", "3"))
if not api_key or not workflow_id:
raise ValueError("未配置 Coze API 所需的 COZE_API_KEY 或 COZE_WORKFLOW_ID")
# 准备文件数据(根据 Coze API 要求构造)
# 如果 Coze 需要文件内容,读取并转换为 base64
files_data = []
for file_info in file_infos:
filepath = file_info["filepath"]
filename = file_info["filename"]
# 读取文件内容并转换为 base64
with open(filepath, 'rb') as f:
import base64
file_content = base64.b64encode(f.read()).decode('utf-8')
files_data.append({
"filename": filename,
"content": file_content,
"type": "application/pdf" if filename.endswith('.pdf') else "image/jpeg"
})
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
# 构造请求体
payload = {
"workflow_id": workflow_id,
"parameters": {
"input": files_data # 传递文件数组
}
}
print(f" 正在调用 Coze 工作流处理 {len(files_data)} 个文件...")
last_error = None
for attempt in range(max_retries):
try:
response = requests.post(api_url, headers=headers, json=payload, timeout=300)
response.raise_for_status()
data = response.json()
# 解析 Coze 返回结果
if isinstance(data, dict) and data.get("code") == 0:
raw_data = data.get("data", {})
if isinstance(raw_data, str):
try:
raw_data = json.loads(raw_data)
except json.JSONDecodeError:
pass
output = raw_data.get("output", raw_data)
# 使用 Ollama 增强 Coze 结果
print(" [阶段2] 使用 Ollama 优化报告内容...")
final_result = self._enhance_with_ollama(output, f"处理了 {len(files_data)} 个文件")
return final_result
last_error = f"Coze API 返回非0 code: {data}"
except Exception as e:
last_error = str(e)
if attempt < max_retries - 1:
wait_time = (attempt + 1) * 3
print(f" 重试 {attempt + 1}/{max_retries}...")
import time
time.sleep(wait_time)
else:
break
raise Exception(last_error or "Coze API 调用失败")
except Exception as e:
print(f" ⚠ Coze 处理失败: {e}")
# 降级到 OCR + Ollama 方式
print(" 降级到 OCR + Ollama 处理...")
extracted_texts = []
for file_info in file_infos:
text = self.ocr_service.extract_text(file_info["filepath"])
extracted_texts.append({
"filename": file_info["filename"],
"text": text
})
return self._analyze_combined_reports(extracted_texts)
def _analyze_combined_reports(self, reports: List[Dict[str, str]]) -> Dict[str, Any]:
"""
综合分析流程(两阶段处理):
- 如果使用 Coze
阶段1: Coze 工作流处理数据 → 返回结构化 JSON
阶段2: Ollama 分析 JSON → 生成适配 Be.U 模板的专业报告
- 如果使用其他LLM
直接使用该 LLM 生成报告
"""
if self.llm_service.llm_type == "coze":
# === 两阶段处理Coze + Ollama ===
# 【阶段1】Coze 工作流分析
print(" [阶段1/2] Coze 工作流分析中...")
print(f" - 处理 {len(reports)} 份报告")
# Coze 工作流有执行超时限制,超过阈值时分批处理
BATCH_SIZE = 3 # 每批最多 3 个报告
import json
# 原始文本列表(给 Ollama 使用)
original_texts: List[str] = []
# 所有批次的 Coze 结果
all_coze_results: List[Dict[str, Any]] = []
# 分批处理
total_batches = (len(reports) + BATCH_SIZE - 1) // BATCH_SIZE
if total_batches > 1:
print(f" - 报告数量较多,将分 {total_batches} 批处理(每批 {BATCH_SIZE} 个)")
for batch_idx in range(total_batches):
start_idx = batch_idx * BATCH_SIZE
end_idx = min(start_idx + BATCH_SIZE, len(reports))
batch_reports = reports[start_idx:end_idx]
if total_batches > 1:
print(f"\n [批次 {batch_idx + 1}/{total_batches}] 处理报告 {start_idx + 1}-{end_idx}")
# 准备当前批次的数据
coze_inputs: List[Dict[str, str]] = []
for report in batch_reports:
filename = report["filename"]
text = report["text"]
# 保留一份可读的原始文本
original_text = f"【文件名】{filename}\n【内容】\n{text}"
original_texts.append(original_text)
# 构造传给 Coze 的 JSON 对象
coze_obj = {
"filename": filename,
"text": text,
}
coze_inputs.append(coze_obj)
print(f" - {filename}: {len(text)} 字符")
print(f" - 本批次元素个数: {len(coze_inputs)}")
# 保存当前批次的调试信息
if total_batches > 1:
debug_file = Path(f"debug_batch_{batch_idx + 1}.json")
else:
debug_file = Path("debug_ocr_texts.json")
try:
final_payload = {
"workflow_id": os.getenv("COZE_WORKFLOW_ID", ""),
"parameters": {
"input": coze_inputs
}
}
debug_file.write_text(json.dumps(final_payload, ensure_ascii=False, indent=2), encoding='utf-8')
print(f" → Payload 已保存: {debug_file.name}")
except Exception as e:
print(f" ⚠️ 保存调试文件失败: {e}")
# 调用 Coze 处理当前批次
print(f" → 调用 Coze 工作流...")
batch_result = self.llm_service.analyze_multiple_reports(coze_inputs)
# 检查当前批次是否成功
if batch_result.get("error"):
error_msg = batch_result.get('error')
print(f" ✗ 批次 {batch_idx + 1} 失败: {error_msg}")
raise Exception(f"Coze 工作流调用失败: {error_msg}")
print(f" ✓ 批次 {batch_idx + 1} 完成")
all_coze_results.append(batch_result)
# 合并所有批次的结果
print(f"\n ✓ 所有批次处理完成,合并结果...")
coze_result = self._merge_batch_results(all_coze_results)
# 保存 Coze 返回结果用于后续测试
try:
import time
cache_file = Path("coze_result_cache.json")
cache_data = {
"timestamp": time.strftime('%Y-%m-%d %H:%M:%S'),
"report_count": len(reports),
"coze_result": coze_result,
"original_texts": original_texts
}
cache_file.write_text(json.dumps(cache_data, ensure_ascii=False, indent=2), encoding='utf-8')
print(f" → Coze 结果已缓存到: {cache_file.absolute()}")
except Exception as e:
print(f" ⚠️ 缓存保存失败: {e}")
# 【阶段2】Ollama 优化生成
print(" [阶段2/2] Ollama 生成 Be.U 风格报告...")
print(" - 将 Coze JSON 转换为专业报告内容")
print(" - 适配 Be.U Wellness Center 模板")
# 合并原始文本供 Ollama 参考(仍然使用人类可读的文本,而不是 JSON 字符串)
combined_text = "\n\n".join(original_texts)
final_analysis = self._enhance_with_ollama(coze_result, combined_text)
print(" ✓ 综合报告生成完成")
return final_analysis
else:
# === 单阶段:直接使用当前 LLM ===
print(f" 使用 {self.llm_service.llm_type} 直接生成报告...")
print(f" - 处理 {len(reports)} 份报告")
# 合并所有报告文本
combined_text = "\n\n=== 报告分隔 ===\n\n".join([
f"【文件名】{report['filename']}\n【内容】\n{report['text']}"
for report in reports
])
analysis = self.llm_service.analyze_single_report(combined_text)
print(" ✓ 报告生成完成")
return analysis
def _merge_batch_results(self, batch_results: List[Dict[str, Any]]) -> Dict[str, Any]:
"""
合并多个批次的 Coze 结果
"""
if len(batch_results) == 1:
return batch_results[0]
print(f" - 合并 {len(batch_results)} 个批次的结果...")
# 合并结果
merged = {
"summary": "",
"key_findings": [],
"abnormal_items": [],
"risk_assessment": "",
"recommendations": []
}
# 收集所有字段
summaries = []
risk_assessments = []
for idx, result in enumerate(batch_results, 1):
# 摘要
if result.get("summary"):
summaries.append(f"批次{idx}: {result['summary']}")
# 关键发现
if result.get("key_findings"):
merged["key_findings"].extend(result["key_findings"])
# 异常指标
if result.get("abnormal_items"):
merged["abnormal_items"].extend(result["abnormal_items"])
# 风险评估
if result.get("risk_assessment"):
risk_assessments.append(f"批次{idx}: {result['risk_assessment']}")
# 建议
if result.get("recommendations"):
merged["recommendations"].extend(result["recommendations"])
# 合并摘要和风险评估
merged["summary"] = "\n\n".join(summaries) if summaries else "未提供摘要"
merged["risk_assessment"] = "\n\n".join(risk_assessments) if risk_assessments else "未提供风险评估"
print(f" - 合并后: 关键发现 {len(merged['key_findings'])} 项, "
f"异常指标 {len(merged['abnormal_items'])} 项, "
f"建议 {len(merged['recommendations'])}")
return merged
def _enhance_with_ollama(self, coze_result: Dict[str, Any], original_text: str) -> Dict[str, Any]:
"""
使用 Ollama 分析 Coze 返回的 JSON生成适配 Be.U 模板的最终报告内容
"""
try:
import requests
import json
# 构建给 Ollama 的提示词
prompt = f"""你是一位专业的医疗报告撰写专家。现在需要基于 Coze 工作流返回的结构化数据,生成一份适合 Be.U Wellness Center 风格的功能医学健康报告。
Coze 工作流返回的数据:
{json.dumps(coze_result, ensure_ascii=False, indent=2)}
原始检测报告文本:
{original_text}
请基于以上信息生成一份专业的综合健康报告包含以下部分JSON格式
1. summary: 综合健康摘要(整体评估,语言专业且易懂)
2. key_findings: 关键发现列表(提取最重要的检测结果)
3. abnormal_items: 异常指标详情(包含 name, value, reference, level
4. risk_assessment: 健康风险评估(基于所有指标的综合分析)
5. recommendations: 个性化健康建议(具体可执行的建议)
要求:
- 语言专业但易于理解
- 突出重点和异常项
- 提供可操作的健康建议
- 使用 Be.U Wellness Center 的专业风格
- 必须返回完整的 JSON 格式
请直接返回 JSON不要有其他文字"""
# 调用 Ollama
ollama_host = os.getenv("OLLAMA_HOST", "http://localhost:11434")
ollama_model = os.getenv("OLLAMA_MODEL", "qwen2.5:7b")
response = requests.post(
f"{ollama_host}/api/generate",
json={
"model": ollama_model,
"prompt": prompt,
"stream": False
},
timeout=300
)
if response.status_code == 200:
content = response.json().get("response", "")
# 解析 Ollama 返回的 JSON
return self._parse_ollama_response(content)
else:
print(f" ⚠ Ollama 调用失败,使用 Coze 原始结果")
return coze_result
except Exception as e:
print(f" ⚠ Ollama 增强失败: {e},使用 Coze 原始结果")
return coze_result
def _parse_ollama_response(self, response: str) -> Dict[str, Any]:
"""解析 Ollama 返回的 JSON"""
try:
import re
import json
# 尝试提取 JSON
json_match = re.search(r'```(?:json)?\s*(\{.*?\})\s*```', response, re.DOTALL)
if json_match:
json_str = json_match.group(1)
else:
json_match = re.search(r'\{.*\}', response, re.DOTALL)
if json_match:
json_str = json_match.group(0)
else:
json_str = response
result = json.loads(json_str)
# 确保必需字段存在
required_fields = ["summary", "key_findings", "abnormal_items", "risk_assessment", "recommendations"]
for field in required_fields:
if field not in result:
result[field] = [] if field in ["key_findings", "abnormal_items", "recommendations"] else "未提供"
return result
except Exception as e:
print(f" ⚠ JSON 解析失败: {e}")
return {
"summary": "解析失败",
"key_findings": [],
"abnormal_items": [],
"risk_assessment": "无法生成",
"recommendations": []
}
def _direct_ollama_analysis(self, combined_text: str) -> Dict[str, Any]:
"""
Coze 失败后的降级方案:直接使用 Ollama 生成完整报告
"""
try:
import requests
import json
print(" → 使用 Ollama 模型生成完整报告...")
# 构建 Ollama 提示词
prompt = f"""你是一位专业的医疗报告分析助手。请分析以下医疗报告,提供专业的综合健康评估。
医疗报告内容:
{combined_text}
请按以下 JSON 格式返回分析结果:
{{
"summary": "综合健康摘要2-3句话",
"key_findings": ["关键发现1", "关键发现2", "..."],
"abnormal_items": [
{{
"name": "指标名称",
"result": "测量值",
"reference": "参考范围",
"level": "high/low/normal"
}}
],
"risk_assessment": "健康风险评估(综合说明)",
"recommendations": ["建议1", "建议2", "..."]
}}
请严格按照 JSON 格式返回,不要添加其他说明文字。"""
# 调用 Ollama
response = requests.post(
"http://localhost:11434/api/generate",
json={
"model": "qwen2.5:7b",
"prompt": prompt,
"stream": False
},
timeout=180
)
if response.status_code == 200:
ollama_response = response.json().get("response", "")
print(f" ✓ Ollama 响应完成")
# 解析 JSON
import re
json_match = re.search(r'\{.*\}', ollama_response, re.DOTALL)
if json_match:
result = json.loads(json_match.group(0))
# 确保必需字段存在
required_fields = ["summary", "key_findings", "abnormal_items", "risk_assessment", "recommendations"]
for field in required_fields:
if field not in result:
result[field] = [] if field in ["key_findings", "abnormal_items", "recommendations"] else "未提供"
return result
else:
raise ValueError("无法解析 Ollama 返回的 JSON")
else:
raise Exception(f"Ollama API 返回错误: {response.status_code}")
except Exception as e:
print(f" ⚠️ Ollama 降级方案也失败: {e}")
return {
"summary": "由于系统问题,暂时无法生成完整分析",
"key_findings": ["OCR 文本提取完成", "分析服务暂时不可用"],
"abnormal_items": [],
"risk_assessment": "建议稍后重试或使用其他方式分析",
"recommendations": ["联系技术支持", "检查系统配置"]
}
def _generate_comprehensive_report(
self,
patient_name: str,
reports: List[Dict[str, str]],
analysis: Dict[str, Any]
) -> str:
"""生成综合健康报告 PDF"""
# 准备扩展的模板数据
template_data = {
"patient_name": patient_name,
"report_count": len(reports),
"report_list": [r["filename"] for r in reports],
"analysis": analysis,
"generation_date": datetime.now().strftime("%Y年%m月%d")
}
# 生成 PDF使用增强的模板
pdf_path = self.pdf_service.generate_comprehensive_report(
patient_name=patient_name,
template_data=template_data
)
return pdf_path
def _cleanup_temp_files(self, file_paths: List[str]):
"""清理临时文件"""
for file_path in file_paths:
try:
if os.path.exists(file_path):
os.remove(file_path)
print(f" ✓ 已删除临时文件: {Path(file_path).name}")
except Exception as e:
print(f" ⚠ 删除临时文件失败 {Path(file_path).name}: {e}")

View File

@@ -0,0 +1,135 @@
import json
import os
from pathlib import Path
from typing import Dict, Any, Optional
from datetime import datetime
import threading
class DataStore:
"""数据持久化存储服务"""
def __init__(self, storage_dir: str = "data"):
self.storage_dir = Path(storage_dir)
self.storage_dir.mkdir(exist_ok=True)
# 数据文件路径
self.data_file = self.storage_dir / "reports_data.json"
# 内存缓存
self._cache: Dict[str, Any] = {}
# 线程锁,防止并发写入冲突
self._lock = threading.Lock()
# 启动时加载数据
self._load_data()
def _load_data(self):
"""从文件加载数据"""
if self.data_file.exists():
try:
with open(self.data_file, 'r', encoding='utf-8') as f:
self._cache = json.load(f)
print(f"✓ 成功加载 {len(self._cache)} 份报告数据")
except Exception as e:
print(f"⚠ 加载数据失败: {e},将使用空数据")
self._cache = {}
else:
print("✓ 数据文件不存在,将创建新文件")
self._cache = {}
def _save_data(self):
"""保存数据到文件"""
try:
with self._lock:
# 创建临时文件,避免写入过程中断导致数据损坏
temp_file = self.data_file.with_suffix('.json.tmp')
with open(temp_file, 'w', encoding='utf-8') as f:
json.dump(self._cache, f, ensure_ascii=False, indent=2)
# 原子性替换
temp_file.replace(self.data_file)
except Exception as e:
print(f"⚠ 保存数据失败: {e}")
def get_all(self) -> Dict[str, Any]:
"""获取所有报告数据"""
return self._cache.copy()
def get(self, file_id: str) -> Optional[Dict[str, Any]]:
"""获取单个报告数据"""
return self._cache.get(file_id)
def set(self, file_id: str, data: Dict[str, Any]) -> None:
"""设置/更新报告数据"""
self._cache[file_id] = data
self._save_data()
def update(self, file_id: str, updates: Dict[str, Any]) -> None:
"""更新报告数据的部分字段"""
if file_id in self._cache:
self._cache[file_id].update(updates)
self._save_data()
else:
raise KeyError(f"报告 {file_id} 不存在")
def delete(self, file_id: str) -> None:
"""删除报告数据"""
if file_id in self._cache:
del self._cache[file_id]
self._save_data()
def exists(self, file_id: str) -> bool:
"""检查报告是否存在"""
return file_id in self._cache
def count(self) -> int:
"""获取报告总数"""
return len(self._cache)
def cleanup_orphaned_files(self, upload_dir: Path) -> int:
"""清理孤立的文件(数据库中有记录但文件不存在)"""
cleaned = 0
orphaned_ids = []
for file_id, report in self._cache.items():
filepath = report.get('filepath')
if filepath and not os.path.exists(filepath):
orphaned_ids.append(file_id)
for file_id in orphaned_ids:
self.delete(file_id)
cleaned += 1
if cleaned > 0:
print(f"✓ 清理了 {cleaned} 条孤立记录")
return cleaned
def export_backup(self, backup_path: Optional[str] = None) -> str:
"""导出备份"""
if backup_path is None:
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_path = self.storage_dir / f"backup_{timestamp}.json"
else:
backup_path = Path(backup_path)
with open(backup_path, 'w', encoding='utf-8') as f:
json.dump(self._cache, f, ensure_ascii=False, indent=2)
print(f"✓ 数据已备份到: {backup_path}")
return str(backup_path)
def import_backup(self, backup_path: str) -> None:
"""从备份恢复数据"""
backup_path = Path(backup_path)
if not backup_path.exists():
raise FileNotFoundError(f"备份文件不存在: {backup_path}")
with open(backup_path, 'r', encoding='utf-8') as f:
imported_data = json.load(f)
self._cache.update(imported_data)
self._save_data()
print(f"✓ 已从备份恢复 {len(imported_data)} 份报告")

View File

@@ -0,0 +1,480 @@
"""
DeepSeek 健康评估与建议生成服务
用于生成"整体健康状况""功能性健康建议"内容
优化:优先使用模板中已有的项目解释,只有模板中没有的项目才调用 DeepSeek 生成
"""
import os
import json
import requests
from pathlib import Path
from typing import List, Dict, Any
class DeepSeekHealthService:
"""DeepSeek 健康内容生成服务"""
def __init__(self, api_key: str = None):
self.api_key = api_key or os.getenv("DEEPSEEK_API_KEY", "")
self.api_url = "https://api.deepseek.com/v1/chat/completions"
# 加载模板中的解释
self.template_explanations = self._load_template_explanations()
def _load_template_explanations(self) -> Dict[str, Dict[str, str]]:
"""加载模板中已有的项目解释"""
explanations_file = Path(__file__).parent.parent / "template_explanations.json"
if explanations_file.exists():
try:
with open(explanations_file, 'r', encoding='utf-8') as f:
explanations = json.load(f)
print(f" ✓ 已加载 {len(explanations)} 个模板解释")
return explanations
except Exception as e:
print(f" ⚠️ 加载模板解释失败: {e}")
return {}
def get_template_explanation(self, abb: str) -> Dict[str, str]:
"""
获取模板中的项目解释
Args:
abb: 项目缩写
Returns:
{"clinical_en": "...", "clinical_cn": "..."} 或空字典
"""
# 尝试多种匹配方式
abb_upper = abb.upper().strip()
# 直接匹配
if abb_upper in self.template_explanations:
return self.template_explanations[abb_upper]
# 去除特殊字符后匹配
abb_clean = ''.join(c for c in abb_upper if c.isalnum())
for key, value in self.template_explanations.items():
key_clean = ''.join(c for c in key if c.isalnum())
if abb_clean == key_clean:
return value
return {}
def is_available(self) -> bool:
"""检查服务是否可用"""
return bool(self.api_key)
def call_deepseek(self, prompt: str) -> str:
"""调用 DeepSeek API"""
if not self.api_key:
raise ValueError("未配置 DEEPSEEK_API_KEY")
headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json"
}
data = {
"model": "deepseek-chat",
"messages": [{"role": "user", "content": prompt}],
"temperature": 0.1,
"max_tokens": 8000
}
response = requests.post(
self.api_url,
headers=headers,
json=data,
timeout=120
)
response.raise_for_status()
return response.json()["choices"][0]["message"]["content"]
def collect_abnormal_items(self, analysis: Dict[str, Any]) -> List[Dict[str, str]]:
"""
从分析结果中收集异常项
Args:
analysis: LLM 分析结果,包含 abnormal_items 字段
Returns:
异常项列表
"""
abnormal_items = []
# 从 abnormal_items 字段提取
raw_items = analysis.get("abnormal_items", [])
for item in raw_items:
if isinstance(item, dict):
abnormal_items.append({
"name": item.get("name", ""),
"abb": item.get("abb", item.get("name", "")),
"result": str(item.get("result", item.get("value", ""))),
"reference": item.get("reference", ""),
"unit": item.get("unit", ""),
"level": item.get("level", ""),
"point": "" if item.get("level") == "high" else ("" if item.get("level") == "low" else "")
})
elif isinstance(item, str):
# 如果是字符串格式,尝试解析
abnormal_items.append({
"name": item,
"abb": "",
"result": "",
"reference": "",
"unit": "",
"level": "",
"point": ""
})
return abnormal_items
def get_item_explanations(self, abnormal_items: List[Dict[str, str]]) -> Dict[str, Dict[str, str]]:
"""
为异常项获取解释(优先使用模板中的解释,缺失的才调用 DeepSeek 生成)
Args:
abnormal_items: 异常项列表
Returns:
{
"ABB": {"clinical_en": "...", "clinical_cn": "..."},
...
}
"""
explanations = {}
items_need_generation = []
print("\n 📋 检查模板中的项目解释...")
for item in abnormal_items:
abb = item.get("abb", "").upper().strip()
name = item.get("name", "")
if not abb:
continue
# 尝试从模板获取解释
template_exp = self.get_template_explanation(abb)
if template_exp and template_exp.get("clinical_en") and template_exp.get("clinical_cn"):
explanations[abb] = template_exp
print(f"{abb}: 使用模板解释")
else:
items_need_generation.append(item)
print(f"{abb}: 需要生成解释")
# 如果有需要生成的项目,调用 DeepSeek
if items_need_generation and self.api_key:
print(f"\n 🤖 调用 DeepSeek 为 {len(items_need_generation)} 个项目生成解释...")
generated = self._generate_missing_explanations(items_need_generation)
explanations.update(generated)
return explanations
def _generate_missing_explanations(self, items: List[Dict[str, str]]) -> Dict[str, Dict[str, str]]:
"""
调用 DeepSeek 为缺失解释的项目生成临床意义
Args:
items: 需要生成解释的项目列表
Returns:
生成的解释字典
"""
if not items:
return {}
# 构建项目描述
items_desc = []
for item in items:
desc = f"- {item['abb']}: {item['name']}"
if item.get('result'):
desc += f", 结果: {item['result']}"
if item.get('unit'):
desc += f" {item['unit']}"
if item.get('reference'):
desc += f", 参考范围: {item['reference']}"
items_desc.append(desc)
prompt = f"""你是一位医学检验专家,请为以下医疗检测项目生成临床意义解释。
## 需要解释的项目:
{chr(10).join(items_desc)}
## 要求:
1. 为每个项目提供英文和中文的临床意义解释
2. 解释应包含:该指标的作用、正常范围的意义、异常时可能的原因
3. 语言专业但易于理解
4. 每个解释约50-100字
## 输出格式JSON
```json
{{
"ABB1": {{
"clinical_en": "English clinical significance...",
"clinical_cn": "中文临床意义..."
}},
"ABB2": {{
"clinical_en": "...",
"clinical_cn": "..."
}}
}}
```
只返回JSON不要其他说明。"""
try:
response = self.call_deepseek(prompt)
# 解析 JSON
if "```json" in response:
response = response.split("```json")[1].split("```")[0]
elif "```" in response:
response = response.split("```")[1].split("```")[0]
result = json.loads(response.strip())
print(f" ✓ 成功生成 {len(result)} 个项目的解释")
return result
except Exception as e:
print(f" ✗ 生成解释失败: {e}")
return {}
def generate_health_assessment(self, abnormal_items: List[Dict[str, str]]) -> Dict[str, Any]:
"""
生成"整体健康状况"评估内容
Args:
abnormal_items: 异常项列表
Returns:
包含多个小节的健康评估内容
"""
if not self.api_key or not abnormal_items:
return {"sections": []}
# 构建异常项描述
abnormal_desc = []
for item in abnormal_items:
direction = "偏高" if item.get("point") in ["", "H", ""] or item.get("level") == "high" else "偏低"
desc = f"- {item['name']}"
if item.get('abb'):
desc += f" ({item['abb']})"
desc += f": {item['result']}"
if item.get('unit'):
desc += f" {item['unit']}"
desc += f" ({direction}"
if item.get('reference'):
desc += f", 参考范围: {item['reference']}"
desc += ")"
abnormal_desc.append(desc)
prompt = f"""你是一位功能医学专家,请根据以下所有异常检测指标,撰写"整体健康状况评估"的内容。
## 异常指标:
{chr(10).join(abnormal_desc)}
## 要求:
1. 根据异常指标的类型,自动分成合适的小节(如血液学、内分泌、免疫、代谢等,根据实际异常项决定)
2. 每个小节包含英文和中文两个版本
3. 从功能医学和整体健康角度分析
4. 解释可能的原因和健康影响
5. 语言专业但易于理解
6. 每个小节的每个语言版本约150-250字
## 输出格式JSON
```json
{{
"sections": [
{{
"title_en": "(I) Section Title in English",
"title_cn": "(一)中文小节标题",
"content_en": "English analysis content...",
"content_cn": "中文分析内容..."
}}
]
}}
```
只返回JSON不要其他说明。根据实际异常项情况决定分几个小节不要硬套固定模板。"""
try:
response = self.call_deepseek(prompt)
# 解析 JSON
if "```json" in response:
response = response.split("```json")[1].split("```")[0]
elif "```" in response:
response = response.split("```")[1].split("```")[0]
result = json.loads(response.strip())
print(f" ✓ 生成健康评估内容,共 {len(result.get('sections', []))} 个小节")
return result
except Exception as e:
print(f" ✗ 生成健康评估内容失败: {e}")
return {"sections": []}
def generate_health_advice(self, abnormal_items: List[Dict[str, str]]) -> Dict[str, Any]:
"""
生成"功能性健康建议"内容
Args:
abnormal_items: 异常项列表
Returns:
包含5个固定小节的健康建议内容
"""
if not self.api_key or not abnormal_items:
return {"sections": []}
# 异常项描述
abnormal_desc = []
for item in abnormal_items:
direction = "偏高" if item.get("point") in ["", "H", ""] or item.get("level") == "high" else "偏低"
desc = f"- {item['name']}"
if item.get('abb'):
desc += f" ({item['abb']})"
desc += f": {item['result']}"
if item.get('unit'):
desc += f" {item['unit']}"
desc += f" ({direction})"
abnormal_desc.append(desc)
prompt = f"""你是一位功能医学专家,请根据以下异常检测指标,撰写"功能医学健康建议"的内容。
## 异常指标:
{chr(10).join(abnormal_desc)}
## 要求:
1. 必须包含以下5个固定小节按顺序
- Nutrition Intervention 营养干预
- Exercise Intervention 运动干预
- Sleep & Stress Management 睡眠与压力管理
- Lifestyle Adjustment 生活方式调整
- Long-term Follow-up Plan 长期随访计划
2. 每个小节针对这些异常指标提供具体、可执行的建议
3. 从功能医学角度出发,强调预防和整体调理
4. 每个小节包含3-5条具体建议措施
5. 语言专业但易于理解
6. 分别提供英文和中文版本
7. 每个小节的每个语言版本约200-300字
## 输出格式JSON
```json
{{
"sections": [
{{
"title_en": "Nutrition Intervention",
"title_cn": "营养干预",
"content_en": "English nutrition advice...",
"content_cn": "中文营养建议..."
}},
{{
"title_en": "Exercise Intervention",
"title_cn": "运动干预",
"content_en": "...",
"content_cn": "..."
}},
{{
"title_en": "Sleep & Stress Management",
"title_cn": "睡眠与压力管理",
"content_en": "...",
"content_cn": "..."
}},
{{
"title_en": "Lifestyle Adjustment",
"title_cn": "生活方式调整",
"content_en": "...",
"content_cn": "..."
}},
{{
"title_en": "Long-term Follow-up Plan",
"title_cn": "长期随访计划",
"content_en": "...",
"content_cn": "..."
}}
]
}}
```
只返回JSON不要其他说明。"""
try:
response = self.call_deepseek(prompt)
# 解析 JSON
if "```json" in response:
response = response.split("```json")[1].split("```")[0]
elif "```" in response:
response = response.split("```")[1].split("```")[0]
result = json.loads(response.strip())
print(f" ✓ 生成健康建议内容,共 {len(result.get('sections', []))} 个小节")
return result
except Exception as e:
print(f" ✗ 生成健康建议内容失败: {e}")
return {"sections": []}
def generate_health_content(self, analysis: Dict[str, Any]) -> Dict[str, Any]:
"""
生成完整的健康评估和建议内容
优化:优先使用模板中已有的项目解释,只有模板中没有的项目才调用 DeepSeek 生成
Args:
analysis: LLM 分析结果
Returns:
包含 health_assessment, health_advice, item_explanations 的字典
"""
if not self.is_available():
print(" ⚠️ DeepSeek API Key 未配置,跳过健康内容生成")
return {}
print("\n============================================================")
print("DeepSeek 健康内容生成")
print("============================================================")
# 收集异常项
print("\n 📝 正在收集异常项...")
abnormal_items = self.collect_abnormal_items(analysis)
if not abnormal_items:
print(" 没有检测到异常项目,跳过内容生成")
return {}
print(f" 发现 {len(abnormal_items)} 个异常项目:")
for item in abnormal_items[:10]:
direction = "" if item.get("level") == "high" or item.get("point") == "" else ""
print(f" - {item['name']}: {item['result']} {direction}")
if len(abnormal_items) > 10:
print(f" ... 等共 {len(abnormal_items)}")
# 获取项目解释(优先使用模板,缺失的才生成)
item_explanations = self.get_item_explanations(abnormal_items)
# 统计使用情况
template_count = sum(1 for abb in item_explanations if self.get_template_explanation(abb))
generated_count = len(item_explanations) - template_count
print(f"\n 📊 解释来源统计: 模板 {template_count} 个, DeepSeek生成 {generated_count}")
# 生成健康评估
print("\n 🤖 正在调用 DeepSeek 生成整体健康状况...")
health_assessment = self.generate_health_assessment(abnormal_items)
# 生成健康建议
print("\n 🤖 正在调用 DeepSeek 生成功能性健康建议...")
health_advice = self.generate_health_advice(abnormal_items)
print("\n ✓ 健康内容生成完成")
return {
"health_assessment": health_assessment,
"health_advice": health_advice,
"abnormal_items": abnormal_items,
"item_explanations": item_explanations # 包含每个项目的解释
}

View File

@@ -0,0 +1,506 @@
import os
import json
from typing import Dict, Any, List
class LLMService:
"""大语言模型服务支持本地Ollama或OpenAI API"""
def __init__(self):
self.llm_type = self._detect_llm_type()
self._initialize_llm()
def _detect_llm_type(self) -> str:
"""检测可用的LLM类型"""
# 优先使用 DeepSeek如果已配置
if os.getenv("DEEPSEEK_API_KEY") and os.getenv("USE_DEEPSEEK_LLM", "true").lower() == "true":
return "deepseek"
# 检查 OpenAI
elif os.getenv("OPENAI_API_KEY"):
return "openai"
# Coze如果已配置
elif os.getenv("COZE_API_KEY") and os.getenv("COZE_WORKFLOW_ID"):
return "coze"
elif os.getenv("OLLAMA_HOST") or self._check_ollama_available():
return "ollama"
else:
return "mock"
def _check_ollama_available(self) -> bool:
"""检查Ollama是否可用"""
try:
import requests
response = requests.get("http://localhost:11434/api/tags", timeout=2)
return response.status_code == 200
except:
return False
def _initialize_llm(self):
"""初始化LLM客户端"""
if self.llm_type == "deepseek":
try:
self.deepseek_api_key = os.getenv("DEEPSEEK_API_KEY")
self.deepseek_api_url = os.getenv("DEEPSEEK_API_BASE", "https://api.deepseek.com") + "/v1/chat/completions"
self.model = os.getenv("DEEPSEEK_MODEL", "deepseek-chat")
print(f"✓ 使用 DeepSeek API (模型: {self.model})")
except Exception as e:
print(f"⚠ DeepSeek 初始化失败: {e}")
self.llm_type = "mock"
elif self.llm_type == "openai":
try:
from openai import OpenAI
# 支持自定义API端点
api_key = os.getenv("OPENAI_API_KEY")
api_base = os.getenv("OPENAI_API_BASE")
if api_base:
self.client = OpenAI(api_key=api_key, base_url=api_base)
else:
self.client = OpenAI(api_key=api_key)
self.model = os.getenv("OPENAI_MODEL", "gpt-3.5-turbo")
print(f"✓ 使用 OpenAI API (模型: {self.model})")
except Exception as e:
print(f"⚠ OpenAI 初始化失败: {e}")
self.llm_type = "mock"
elif self.llm_type == "ollama":
try:
import requests
self.ollama_host = os.getenv("OLLAMA_HOST", "http://localhost:11434")
# 默认使用已安装的 qwen2.5:7b 模型,如需更换可通过 OLLAMA_MODEL 环境变量覆盖
self.model = os.getenv("OLLAMA_MODEL", "qwen2.5:7b")
print(f"✓ 使用 Ollama (模型: {self.model})")
except Exception as e:
print(f"⚠ Ollama 初始化失败: {e}")
self.llm_type = "mock"
elif self.llm_type == "coze":
try:
# Coze 工作流调用所需配置,通过环境变量提供
self.coze_api_url = os.getenv("COZE_API_URL", "https://api.coze.cn/v1/workflow/run")
self.coze_api_key = os.getenv("COZE_API_KEY")
self.coze_workflow_id = os.getenv("COZE_WORKFLOW_ID")
if not self.coze_api_key or not self.coze_workflow_id:
raise ValueError("COZE_API_KEY 或 COZE_WORKFLOW_ID 未配置")
print("✓ 使用 Coze 工作流作为LLM")
except Exception as e:
print(f"⚠ Coze 初始化失败: {e}")
self.llm_type = "mock"
if self.llm_type == "mock":
print("✓ 使用模拟LLM模式用于演示")
def analyze_single_report(self, report_text: str) -> Dict[str, Any]:
"""分析单个报告"""
prompt = f"""请分析以下医疗报告,提取关键信息:
{report_text}
请提供:
1. 摘要
2. 关键发现
3. 异常指标
4. 风险评估
5. 建议
以JSON格式返回结果。
"""
if self.llm_type == "deepseek":
return self._call_deepseek(prompt)
elif self.llm_type == "openai":
return self._call_openai(prompt)
elif self.llm_type == "ollama":
return self._call_ollama(prompt)
elif self.llm_type == "coze":
# 对于 Coze直接将原始报告文本传给工作流由工作流内部负责解析与生成结构化结果
print(f" → 准备调用 Coze 工作流...")
coze_input = [{
"filename": "single_report",
"text": report_text,
}]
result = self._call_coze(coze_input) # 单个报告也作为数组传入
print(f" ← Coze 调用返回")
return result
else:
return self._mock_analysis(report_text)
def analyze_multiple_reports(self, report_texts: List[str]) -> Dict[str, Any]:
"""
分析多个报告Coze专用
report_texts: 报告文本的数组每个元素是一个PDF的文本
"""
if self.llm_type == "coze":
print(f" → 准备调用 Coze 工作流(传入 {len(report_texts)} 个报告)...")
result = self._call_coze(report_texts)
print(f" ← Coze 调用返回")
return result
else:
# 其他LLM类型合并文本后调用
combined = "\n\n".join(report_texts)
return self.analyze_single_report(combined)
def _call_openai(self, prompt: str) -> Dict[str, Any]:
"""调用OpenAI API"""
try:
response = self.client.chat.completions.create(
model=self.model,
messages=[
{"role": "system", "content": "你是一位专业的医疗报告分析助手。"},
{"role": "user", "content": prompt}
],
temperature=0.7,
max_tokens=2000
)
content = response.choices[0].message.content
return self._parse_llm_response(content)
except Exception as e:
return {
"error": f"OpenAI API 调用失败: {str(e)}",
"summary": "分析失败",
"key_findings": [],
"abnormal_items": [],
"risk_assessment": "无法评估",
"recommendations": []
}
def _call_deepseek(self, prompt: str) -> Dict[str, Any]:
"""调用 DeepSeek API"""
try:
import requests
headers = {
"Authorization": f"Bearer {self.deepseek_api_key}",
"Content-Type": "application/json"
}
data = {
"model": self.model,
"messages": [
{"role": "system", "content": "你是一位专业的医疗报告分析助手。请以JSON格式返回分析结果。"},
{"role": "user", "content": prompt}
],
"temperature": 0.3,
"max_tokens": 4000
}
response = requests.post(
self.deepseek_api_url,
headers=headers,
json=data,
timeout=120
)
if response.status_code == 200:
content = response.json()["choices"][0]["message"]["content"]
return self._parse_llm_response(content)
else:
raise Exception(f"DeepSeek 返回错误: {response.status_code} - {response.text}")
except Exception as e:
return {
"error": f"DeepSeek API 调用失败: {str(e)}",
"summary": "分析失败",
"key_findings": [],
"abnormal_items": [],
"risk_assessment": "无法评估",
"recommendations": []
}
def _call_coze(self, report_texts: List[str]) -> Dict[str, Any]:
"""
调用 Coze 工作流 API流式模式对医疗报告进行分析
report_texts: 报告文本数组每个元素是一个PDF的文本
"""
try:
import time
from cozepy import Coze, TokenAuth, COZE_CN_BASE_URL, WorkflowEventType
api_key = getattr(self, "coze_api_key", os.getenv("COZE_API_KEY"))
workflow_id = getattr(self, "coze_workflow_id", os.getenv("COZE_WORKFLOW_ID"))
max_retries = int(os.getenv("COZE_MAX_RETRIES", "3"))
if not api_key or not workflow_id:
raise ValueError("未配置 Coze API 所需的 COZE_API_KEY 或 COZE_WORKFLOW_ID")
print(f" → 调用 Coze 工作流(流式模式)...")
print(f" → Workflow ID: {workflow_id}")
print(f" → 数组元素个数: {len(report_texts)}")
total_chars = 0
for item in report_texts:
if isinstance(item, str):
total_chars += len(item)
elif isinstance(item, dict):
text_value = item.get("text")
if isinstance(text_value, str):
total_chars += len(text_value)
print(f" → 总文本长度: {total_chars} 字符")
print(f" → 请求发送时间: {time.strftime('%H:%M:%S')}")
# 初始化 Coze 客户端
coze = Coze(auth=TokenAuth(token=api_key), base_url=COZE_CN_BASE_URL)
# 添加请求开始时间
import time as time_module
start = time_module.time()
last_error = None
for attempt in range(max_retries):
try:
if attempt > 0:
print(f" → 重试 {attempt}/{max_retries - 1}...")
# 调用流式接口
stream = coze.workflows.runs.stream(
workflow_id=workflow_id,
parameters={"input": report_texts}
)
print(f" ✓ 已连接到流式接口,等待执行...")
# 处理事件流
content_result = None
event_count = 0
for event in stream:
event_count += 1
print(f" [事件 {event_count}] 类型: {event.event}")
if event.event == WorkflowEventType.MESSAGE:
# 打印进度信息
if hasattr(event, 'message') and event.message:
msg = event.message
node_type = getattr(msg, 'node_type', None)
node_title = getattr(msg, 'node_title', None)
node_is_finish = getattr(msg, 'node_is_finish', None)
content = getattr(msg, 'content', None)
print(f" 节点标题: {node_title}")
print(f" 节点类型: {node_type}")
print(f" 是否完成: {node_is_finish}")
print(f" 内容长度: {len(content) if content else 0}")
if node_title:
print(f" ⏳ 执行节点: {node_title} (类型: {node_type})")
# 检查是否为结束节点(使用 node_title 判断)
if node_title == "End" and node_is_finish and content:
print(f" ✓ 工作流执行完成,获取到结果")
content_result = content
break
elif event.event == WorkflowEventType.ERROR:
error_msg = str(event.error) if hasattr(event, 'error') else "Unknown error"
print(f" ✗ 错误事件: {error_msg}")
raise Exception(f"工作流执行错误: {error_msg}")
elif event.event == WorkflowEventType.INTERRUPT:
print(f" ⚠️ 工作流需要交互,暂不支持")
raise Exception("工作流需要人工交互,当前不支持")
if not content_result:
raise Exception("未获取到工作流执行结果")
elapsed = time_module.time() - start
print(f" → 收到完整结果 (耗时: {elapsed:.1f}秒)")
print(f" → 结果数据: {content_result[:200]}...")
# 解析 content 字段(通常包含 JSON 格式的输出)
# content 格式示例: {"output":"```json\n{...}\n```"}
if isinstance(content_result, str):
# 尝试解析为 JSON
try:
content_json = json.loads(content_result)
output = content_json.get("output", content_result)
except json.JSONDecodeError:
output = content_result
# 如果 output 包含 markdown 格式的 JSON提取出来
if isinstance(output, str) and "```json" in output:
import re
json_match = re.search(r'```json\s*\n(.*?)\n```', output, re.DOTALL)
if json_match:
output = json_match.group(1)
# 尝试解析最终的 JSON
data = {"code": 0, "data": {"output": output}}
else:
data = {"code": 0, "data": {"output": content_result}}
# 参考间隔定时脚本的返回结构:{ code: 0, data: { output: ... } }
if isinstance(data, dict) and data.get("code") == 0:
raw_data = data.get("data", {})
if isinstance(raw_data, str):
try:
raw_data = json.loads(raw_data)
except json.JSONDecodeError:
# data.data 为字符串,直接按 LLM 文本解析
return self._parse_llm_response(raw_data)
# 期望 workflow 在 data.output 中返回结果
output = raw_data.get("output", raw_data)
# 如果 output 还是字符串,再次解析
if isinstance(output, str):
try:
output = json.loads(output)
print(f" ✓ Coze 返回的 output 需要二次解析")
except json.JSONDecodeError:
print(f" ⚠️ output 为字符串但无法解析为JSON尝试文本解析")
return self._parse_llm_response(output)
if isinstance(output, dict):
# 如果已经是结构化结果,直接补齐字段
print(f" ✓ Coze 返回结构化数据")
print(f" → 包含字段: {list(output.keys())}")
result = output
required_fields = [
"summary",
"key_findings",
"abnormal_items",
"risk_assessment",
"recommendations",
]
for field in required_fields:
if field not in result:
result[field] = [] if field in [
"key_findings",
"abnormal_items",
"recommendations",
] else "未提供"
print(f" ✓✓ Coze 工作流调用成功!")
return result
if isinstance(output, str):
# output 为文本,通过原有 JSON 解析逻辑处理
return self._parse_llm_response(output)
# 其它类型(列表等),转为字符串后再解析
return self._parse_llm_response(json.dumps(output, ensure_ascii=False))
# code 非 0视为错误
last_error = f"Coze API 返回非0 code: {data}"
print(f" ✗ Coze 返回错误: {last_error}")
except Exception as e: # 包含超时在内的所有请求异常
last_error = str(e)
print(f" ✗ Coze API 调用失败: {last_error}")
if attempt < max_retries - 1:
# 简单的递增退避等待
wait_time = (attempt + 1) * 3
print(f" → 等待 {wait_time} 秒后重试...")
time.sleep(wait_time)
else:
print(f" ✗✗ 已达最大重试次数,放弃调用")
break
print(f" ✗✗ Coze 工作流调用最终失败: {last_error}")
raise Exception(last_error or "Coze API 调用失败")
except Exception as e:
return {
"error": f"Coze API 调用失败: {str(e)}",
"summary": "分析失败",
"key_findings": [],
"abnormal_items": [],
"risk_assessment": "无法评估",
"recommendations": []
}
def _call_ollama(self, prompt: str) -> Dict[str, Any]:
"""调用Ollama API"""
try:
import requests
response = requests.post(
f"{self.ollama_host}/api/generate",
json={
"model": self.model,
"prompt": prompt,
"stream": False
},
timeout=300
)
if response.status_code == 200:
content = response.json().get("response", "")
return self._parse_llm_response(content)
else:
raise Exception(f"Ollama 返回错误: {response.status_code}")
except Exception as e:
return {
"error": f"Ollama API 调用失败: {str(e)}",
"summary": "分析失败",
"key_findings": [],
"abnormal_items": [],
"risk_assessment": "无法评估",
"recommendations": []
}
def _parse_llm_response(self, response: str) -> Dict[str, Any]:
"""解析LLM响应"""
try:
# 尝试提取JSON内容
import re
# 查找JSON代码块
json_match = re.search(r'```(?:json)?\s*(\{.*?\})\s*```', response, re.DOTALL)
if json_match:
json_str = json_match.group(1)
else:
# 查找裸JSON
json_match = re.search(r'\{.*\}', response, re.DOTALL)
if json_match:
json_str = json_match.group(0)
else:
json_str = response
result = json.loads(json_str)
# 验证必需字段
required_fields = ["summary", "key_findings", "abnormal_items", "risk_assessment", "recommendations"]
for field in required_fields:
if field not in result:
result[field] = [] if field in ["key_findings", "abnormal_items", "recommendations"] else "未提供"
return result
except:
# 解析失败,返回原始文本
return {
"summary": "无法解析LLM响应",
"raw_response": response,
"key_findings": [],
"abnormal_items": [],
"risk_assessment": "解析失败",
"recommendations": []
}
def _mock_analysis(self, report_text: str) -> Dict[str, Any]:
"""模拟分析结果"""
return {
"summary": "这是一份血常规检查报告。根据报告内容,各项指标均在正常参考范围内,未发现明显异常。",
"key_findings": [
"白细胞计数: 6.5×10^9/L正常范围",
"红细胞计数: 4.8×10^12/L正常范围",
"血红蛋白: 145 g/L正常范围",
"血小板计数: 220×10^9/L正常范围"
],
"abnormal_items": [],
"risk_assessment": "低风险。所有检测指标均在正常范围内,未发现需要关注的异常项。建议定期体检,保持健康生活方式。",
"recommendations": [
"继续保持良好的生活习惯",
"定期进行健康体检(建议每年一次)",
"保持均衡饮食和适量运动",
"如有不适症状,及时就医"
],
"note": "这是一个模拟的分析结果。实际使用时请配置 OpenAI API 或本地 Ollama 模型。"
}

View File

@@ -0,0 +1,222 @@
import os
import sys
from pathlib import Path
from typing import Union
import tempfile
import shutil
class OCRService:
"""OCR识别服务 - 支持 MinerU、百度云OCR API、PaddleOCR生产模式不支持演示"""
def __init__(self):
self.ocr_type = self._detect_ocr_type()
self._initialize_ocr()
def _detect_ocr_type(self) -> str:
"""检测可用的OCR类型"""
# 最优先使用百度云OCR API速度快、精度高、免费额度足够日常使用
if os.getenv("BAIDU_OCR_APP_ID") and os.getenv("BAIDU_OCR_API_KEY") and os.getenv("BAIDU_OCR_SECRET_KEY"):
return "baidu_cloud"
# 其次使用 MinerU最强大的文档解析工具但速度慢
elif self._check_mineru():
return "mineru"
# 再次使用PaddleOCR
elif self._check_paddleocr():
return "paddleocr"
# 没有可用的OCR
else:
raise RuntimeError(
"❌ 没有可用的OCR引擎请至少配置以下一种\n"
"1. MinerU - 将 MinerU-master 文件夹放在桌面\n"
"2. 百度OCR - 配置环境变量 BAIDU_OCR_*\n"
"3. PaddleOCR - 运行 pip install paddleocr paddlepaddle"
)
def _initialize_ocr(self):
"""初始化OCR引擎"""
if self.ocr_type == "mineru":
try:
# 添加 MinerU 路径
mineru_path = Path(r"c:\Users\UI\Desktop\MinerU-master")
if mineru_path.exists() and str(mineru_path) not in sys.path:
sys.path.insert(0, str(mineru_path))
from demo.demo import parse_doc
self.mineru_parse = parse_doc
try:
from torch.serialization import add_safe_globals
from doclayout_yolo.nn.tasks import YOLOv10DetectionModel
add_safe_globals([YOLOv10DetectionModel])
except Exception:
pass
print("✓ 使用 MinerU 引擎(高精度文档解析)")
except Exception as e:
raise RuntimeError(f"❌ MinerU 初始化失败: {e}\n请安装完整依赖或使用其他OCR引擎")
elif self.ocr_type == "baidu_cloud":
try:
from aip import AipOcr
app_id = os.getenv("BAIDU_OCR_APP_ID")
api_key = os.getenv("BAIDU_OCR_API_KEY")
secret_key = os.getenv("BAIDU_OCR_SECRET_KEY")
self.baidu_client = AipOcr(app_id, api_key, secret_key)
print("✓ 使用百度云OCR API高精度")
except Exception as e:
raise RuntimeError(f"❌ 百度云OCR初始化失败: {e}\n请检查环境变量配置")
elif self.ocr_type == "paddleocr":
try:
from paddleocr import PaddleOCR
self.paddle_ocr = PaddleOCR(use_angle_cls=True, lang="ch", show_log=False)
print("✓ 使用 PaddleOCR 引擎(本地离线)")
except Exception as e:
raise RuntimeError(f"❌ PaddleOCR 初始化失败: {e}\n请运行: pip install paddleocr paddlepaddle")
def _check_mineru(self) -> bool:
"""检查MinerU是否可用"""
try:
mineru_path = Path(r"c:\Users\UI\Desktop\MinerU-master")
return mineru_path.exists() and (mineru_path / "demo" / "demo.py").exists()
except:
return False
def _check_paddleocr(self) -> bool:
"""检查PaddleOCR是否可用"""
try:
import paddleocr
return True
except ImportError:
return False
def extract_text(self, file_path: Union[str, Path]) -> str:
"""从图片或PDF中提取文本"""
file_path = str(file_path)
file_ext = Path(file_path).suffix.lower()
if file_ext == '.pdf':
return self._extract_from_pdf(file_path)
else:
return self._extract_from_image(file_path)
def _extract_from_image(self, image_path: str) -> str:
"""从图片中提取文本"""
if self.ocr_type == "mineru":
return self._extract_with_mineru(image_path)
elif self.ocr_type == "baidu_cloud":
return self._extract_with_baidu_cloud(image_path)
elif self.ocr_type == "paddleocr":
return self._extract_with_paddleocr(image_path)
else:
raise RuntimeError("OCR引擎未正确初始化")
def _extract_with_mineru(self, file_path: str) -> str:
"""使用 MinerU 提取文本支持PDF和图片"""
try:
# 创建临时输出目录
temp_dir = tempfile.mkdtemp(prefix="mineru_")
try:
# 调用 MinerU 解析
file_path_obj = Path(file_path)
self.mineru_parse(
path_list=[file_path_obj],
output_dir=temp_dir,
lang="ch", # 中文
backend="pipeline", # 使用 pipeline 模式
method="auto" # 自动检测
)
# 读取生成的 markdown 文件
md_files = list(Path(temp_dir).rglob("*.md"))
if md_files:
# 优先排除 layout / span / origin 等辅助文件
content_files = [
f for f in md_files
if not any(x in f.stem for x in ['layout', 'span', 'origin'])
]
target_files = content_files or md_files
with open(target_files[0], 'r', encoding='utf-8') as f:
content = f.read()
return content if content.strip() else "未识别到文本内容"
return "未识别到文本内容"
finally:
# 清理临时目录
try:
shutil.rmtree(temp_dir)
except:
pass
except Exception as e:
return f"MinerU识别出错: {str(e)}"
def _extract_with_baidu_cloud(self, image_path: str) -> str:
"""使用百度云OCR API提取文本"""
try:
# 读取图片
with open(image_path, 'rb') as f:
image_data = f.read()
# 调用通用文字识别(高精度版)
result = self.baidu_client.accurateBasic(image_data)
if 'error_code' in result:
return f"百度OCR错误 ({result['error_code']}): {result.get('error_msg', '未知错误')}"
# 提取文本
if 'words_result' in result:
text_lines = [item['words'] for item in result['words_result']]
return "\n".join(text_lines) if text_lines else "未识别到文本内容"
return "未识别到文本内容"
except Exception as e:
return f"百度云OCR识别出错: {str(e)}"
def _extract_with_paddleocr(self, image_path: str) -> str:
"""使用PaddleOCR提取文本"""
try:
result = self.paddle_ocr.ocr(image_path, cls=True)
if not result or not result[0]:
return "未识别到文本内容"
# 提取所有文本行
text_lines = []
for line in result[0]:
if line and len(line) >= 2:
text_lines.append(line[1][0])
return "\n".join(text_lines) if text_lines else "未识别到文本内容"
except Exception as e:
return f"PaddleOCR识别出错: {str(e)}"
def _extract_from_pdf(self, pdf_path: str) -> str:
"""从PDF中提取文本"""
# 优先使用 MinerU 处理 PDF效果最好
if self.ocr_type == "mineru":
return self._extract_with_mineru(pdf_path)
# 备选方案:使用 pdfplumber
try:
import pdfplumber
text_content = []
with pdfplumber.open(pdf_path) as pdf:
for page in pdf.pages:
text = page.extract_text()
if text:
text_content.append(text)
return "\n\n".join(text_content) if text_content else "未提取到文本内容"
except ImportError:
# PDF库不可用尝试使用OCR处理PDF的图像
return "PDF处理需要安装 pdfplumber 库\n可以运行: pip install pdfplumber"
except Exception as e:
return f"PDF处理出错: {str(e)}"

View File

@@ -0,0 +1,267 @@
import os
from pathlib import Path
from datetime import datetime
from typing import Dict, Any
from jinja2 import Environment, FileSystemLoader
class PDFService:
"""PDF报告生成服务"""
def __init__(self):
# 模板目录
self.template_dir = Path(__file__).parent.parent / "templates"
self.template_dir.mkdir(exist_ok=True)
# 输出目录
self.output_dir = Path(__file__).parent.parent / "generated_reports"
self.output_dir.mkdir(exist_ok=True)
# 初始化Jinja2环境
self.jinja_env = Environment(loader=FileSystemLoader(str(self.template_dir)))
def generate_report(
self,
filename: str,
analysis: Dict[str, Any],
llm_type: str = "Coze Workflow"
) -> str:
"""
生成PDF报告
Args:
filename: 原始文件名
analysis: 分析结果
llm_type: 使用的LLM类型
Returns:
生成的PDF文件路径
"""
try:
# 准备模板数据
template_data = self._prepare_template_data(filename, analysis, llm_type)
# 渲染HTML
html_content = self._render_html(template_data)
# 生成PDF
pdf_path = self._generate_pdf(html_content, filename)
return pdf_path
except Exception as e:
raise Exception(f"PDF生成失败: {str(e)}")
def _prepare_template_data(
self,
filename: str,
analysis: Dict[str, Any],
llm_type: str
) -> Dict[str, Any]:
"""准备模板数据"""
# 处理 key_findings
key_findings = analysis.get("key_findings", [])
if key_findings:
# 如果是对象数组,提取文本
key_findings = [
item.get("finding", item.get("text", str(item)))
if isinstance(item, dict) else str(item)
for item in key_findings
]
# 处理 abnormal_items
abnormal_items = analysis.get("abnormal_items", [])
if abnormal_items:
processed_items = []
for item in abnormal_items:
if isinstance(item, dict):
processed_items.append(item)
else:
processed_items.append({"name": str(item)})
abnormal_items = processed_items
# 处理 risk_assessment
risk_assessment = analysis.get("risk_assessment", "未提供")
if isinstance(risk_assessment, dict):
# 如果是对象,转换为文本
parts = []
if risk_assessment.get("high_risk"):
parts.append(f"【高风险】{'; '.join(risk_assessment['high_risk'])}")
if risk_assessment.get("medium_risk"):
parts.append(f"【中风险】{'; '.join(risk_assessment['medium_risk'])}")
if risk_assessment.get("low_risk"):
parts.append(f"【低风险】{'; '.join(risk_assessment['low_risk'])}")
risk_assessment = "\n".join(parts) if parts else "未检测到明确风险"
# 处理 recommendations
recommendations = analysis.get("recommendations", [])
if recommendations:
recommendations = [
item.get("recommendation", item.get("text", str(item)))
if isinstance(item, dict) else str(item)
for item in recommendations
]
return {
"filename": filename,
"analysis_date": datetime.now().strftime("%Y年%m月%d"),
"llm_type": llm_type,
"summary": analysis.get("summary", "暂无摘要"),
"key_findings": key_findings,
"abnormal_items": abnormal_items,
"risk_assessment": risk_assessment,
"recommendations": recommendations,
"generation_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
def _render_html(self, template_data: Dict[str, Any]) -> str:
"""渲染HTML模板"""
template = self.jinja_env.get_template("report_template.html")
return template.render(**template_data)
def _generate_pdf(self, html_content: str, original_filename: str) -> str:
"""将HTML转换为PDF"""
try:
# 生成PDF文件名
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
base_name = Path(original_filename).stem
pdf_filename = f"{base_name}_分析报告_{timestamp}.pdf"
pdf_path = self.output_dir / pdf_filename
# 尝试使用 WeasyPrint推荐质量更好
try:
from weasyprint import HTML, CSS
HTML(string=html_content).write_pdf(
str(pdf_path),
stylesheets=[CSS(string='@page { size: A4; margin: 1cm; }')]
)
except ImportError:
# 降级到 xhtml2pdf更简单无需额外依赖
print(" WeasyPrint 未安装,使用 xhtml2pdf 生成PDF...")
from xhtml2pdf import pisa
with open(pdf_path, "wb") as pdf_file:
pisa_status = pisa.CreatePDF(html_content, dest=pdf_file)
if pisa_status.err:
raise Exception("xhtml2pdf 生成失败")
return str(pdf_path)
except Exception as e:
raise Exception(f"PDF转换失败: {str(e)}")
def get_pdf_file(self, pdf_path: str) -> bytes:
"""读取PDF文件内容"""
if not os.path.exists(pdf_path):
raise FileNotFoundError("PDF文件不存在")
with open(pdf_path, "rb") as f:
return f.read()
def generate_comprehensive_report(
self,
patient_name: str,
template_data: Dict[str, Any]
) -> str:
"""
生成综合健康报告(多份报告整合)
Args:
patient_name: 患者姓名
template_data: 包含所有报告数据和分析结果的字典
Returns:
生成的PDF文件路径
"""
try:
# 准备综合报告模板数据
comprehensive_data = self._prepare_comprehensive_data(patient_name, template_data)
# 渲染HTML使用综合报告模板
html_content = self._render_comprehensive_html(comprehensive_data)
# 生成PDF
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
pdf_filename = f"{patient_name}_综合健康报告_{timestamp}.pdf"
pdf_path = self.output_dir / pdf_filename
try:
from weasyprint import HTML, CSS
HTML(string=html_content).write_pdf(
str(pdf_path),
stylesheets=[CSS(string='@page { size: A4; margin: 1cm; }')]
)
except ImportError:
# 如果 WeasyPrint 不可用,使用 xhtml2pdf
from xhtml2pdf import pisa
with open(pdf_path, "wb") as pdf_file:
pisa_status = pisa.CreatePDF(html_content, dest=pdf_file)
if pisa_status.err:
raise Exception("xhtml2pdf 生成失败")
return str(pdf_path)
except Exception as e:
raise Exception(f"综合报告生成失败: {str(e)}")
def _prepare_comprehensive_data(
self,
patient_name: str,
template_data: Dict[str, Any]
) -> Dict[str, Any]:
"""准备综合报告模板数据"""
analysis = template_data.get("analysis", {})
# 处理分析结果(与单报告相同的逻辑)
key_findings = analysis.get("key_findings", [])
if key_findings:
key_findings = [
item.get("finding", item.get("text", str(item)))
if isinstance(item, dict) else str(item)
for item in key_findings
]
abnormal_items = analysis.get("abnormal_items", [])
if abnormal_items:
processed_items = []
for item in abnormal_items:
if isinstance(item, dict):
processed_items.append(item)
else:
processed_items.append({"name": str(item)})
abnormal_items = processed_items
risk_assessment = analysis.get("risk_assessment", "未提供")
if isinstance(risk_assessment, dict):
parts = []
if risk_assessment.get("high_risk"):
parts.append(f"【高风险】{'; '.join(risk_assessment['high_risk'])}")
if risk_assessment.get("medium_risk"):
parts.append(f"【中风险】{'; '.join(risk_assessment['medium_risk'])}")
if risk_assessment.get("low_risk"):
parts.append(f"【低风险】{'; '.join(risk_assessment['low_risk'])}")
risk_assessment = "\n".join(parts) if parts else "未检测到明确风险"
recommendations = analysis.get("recommendations", [])
if recommendations:
recommendations = [
item.get("recommendation", item.get("text", str(item)))
if isinstance(item, dict) else str(item)
for item in recommendations
]
return {
"patient_name": patient_name,
"report_count": template_data.get("report_count", 0),
"report_list": template_data.get("report_list", []),
"generation_date": template_data.get("generation_date", datetime.now().strftime("%Y年%m月%d")),
"summary": analysis.get("summary", "暂无摘要"),
"key_findings": key_findings,
"abnormal_items": abnormal_items,
"risk_assessment": risk_assessment,
"recommendations": recommendations,
"generation_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
def _render_comprehensive_html(self, template_data: Dict[str, Any]) -> str:
"""渲染综合报告HTML模板"""
template = self.jinja_env.get_template("comprehensive_report_template.html")
return template.render(**template_data)

View File

@@ -0,0 +1,106 @@
from typing import List, Dict, Any
import json
class ReportIntegrator:
"""医疗报告整合分析器"""
def __init__(self, llm_service):
self.llm_service = llm_service
def integrate_reports(self, reports: List[Dict[str, Any]]) -> Dict[str, Any]:
"""整合多份医疗报告"""
if len(reports) == 1:
return self._single_report_summary(reports[0])
# 构建整合分析的提示词
prompt = self._build_integration_prompt(reports)
# 调用LLM进行整合分析
if self.llm_service.llm_type == "openai":
result = self._call_openai_integration(prompt)
elif self.llm_service.llm_type == "ollama":
result = self._call_ollama_integration(prompt)
else:
result = self._mock_integration(reports)
# 添加报告列表
result["reports_included"] = [
{"filename": report["filename"], "summary": report["analysis"].get("summary", "无摘要")}
for report in reports
]
return result
def _build_integration_prompt(self, reports: List[Dict[str, Any]]) -> str:
"""构建整合分析提示词"""
report_details = []
for i, report in enumerate(reports, 1):
analysis = report["analysis"]
report_details.append(f"【报告{i}: {report['filename']}\n摘要: {analysis.get('summary', '')}")
prompt = f"""你是专业医疗分析专家。整合以下{len(reports)}份报告,提供综合评估。
{chr(10).join(report_details)}
请以JSON格式返回
{{"overall_summary": "整体摘要", "health_trends": ["趋势"], "priority_concerns": [{{"concern": "关注点", "severity": "低/中/高", "description": "描述"}}], "comprehensive_assessment": "综合评估", "integrated_recommendations": ["建议"], "follow_up_suggestions": ["后续建议"]}}"""
return prompt
def _call_openai_integration(self, prompt: str) -> Dict[str, Any]:
"""调用OpenAI进行整合分析"""
try:
response = self.llm_service.client.chat.completions.create(
model=self.llm_service.model,
messages=[{"role": "system", "content": "你是医疗分析专家。"}, {"role": "user", "content": prompt}],
temperature=0.7, max_tokens=3000
)
content = response.choices[0].message.content
return self.llm_service._parse_llm_response(content)
except Exception as e:
return self._create_error_result(f"OpenAI分析失败: {str(e)}")
def _call_ollama_integration(self, prompt: str) -> Dict[str, Any]:
"""调用Ollama进行整合分析"""
try:
import requests
response = requests.post(f"{self.llm_service.ollama_host}/api/generate",
json={"model": self.llm_service.model, "prompt": prompt, "stream": False}, timeout=90)
if response.status_code == 200:
return self.llm_service._parse_llm_response(response.json().get("response", ""))
raise Exception(f"Ollama错误: {response.status_code}")
except Exception as e:
return self._create_error_result(f"Ollama分析失败: {str(e)}")
def _mock_integration(self, reports: List[Dict[str, Any]]) -> Dict[str, Any]:
"""模拟整合分析结果"""
total_abnormal = sum(len(report["analysis"].get("abnormal_items", [])) for report in reports)
return {
"overall_summary": f"综合分析了{len(reports)}份报告,发现{total_abnormal}项异常指标。整体健康状况良好。",
"health_trends": ["各项指标整体稳定", "未发现明显恶化趋势", "建议持续监测"],
"priority_concerns": [{"concern": "定期体检", "severity": "", "description": "建议保持定期体检"}] if total_abnormal == 0 else [{"concern": "异常指标", "severity": "", "description": f"发现{total_abnormal}项异常"}],
"comprehensive_assessment": "整体健康状况可控,建议关注生活方式、定期复查。",
"integrated_recommendations": ["保持均衡饮食", "坚持适量运动", "保证充足睡眠", "定期体检"],
"follow_up_suggestions": ["3-6个月后复查关键指标", "如有不适及时就医", "保持健康记录"],
"note": "这是模拟结果。实际使用请配置OpenAI或Ollama。"
}
def _single_report_summary(self, report: Dict[str, Any]) -> Dict[str, Any]:
"""单个报告摘要"""
analysis = report["analysis"]
return {
"overall_summary": f"单份报告分析:{analysis.get('summary', '无摘要')}",
"reports_included": [{"filename": report["filename"], "summary": analysis.get("summary", "")}],
"health_trends": analysis.get("key_findings", []),
"priority_concerns": [{"concern": item, "severity": "", "description": "需关注"} for item in analysis.get("abnormal_items", [])[:3]],
"comprehensive_assessment": analysis.get("risk_assessment", "请查看详细分析"),
"integrated_recommendations": analysis.get("recommendations", []),
"follow_up_suggestions": ["定期复查", "咨询医生"]
}
def _create_error_result(self, error_msg: str) -> Dict[str, Any]:
"""创建错误结果"""
return {
"error": error_msg, "overall_summary": "分析失败", "health_trends": [],
"priority_concerns": [], "comprehensive_assessment": "无法完成分析",
"integrated_recommendations": [], "follow_up_suggestions": []
}

View File

@@ -0,0 +1,67 @@
import os
import shutil
from pathlib import Path
from typing import Optional
class TemplateService:
"""PDF模板管理服务"""
def __init__(self, template_dir: str = "templates/pdf"):
self.template_dir = Path(template_dir)
self.template_dir.mkdir(parents=True, exist_ok=True)
# 默认模板名称
self.default_template = "be_u_template.pdf"
def get_template_path(self, template_name: Optional[str] = None) -> Path:
"""获取模板文件路径"""
if template_name is None:
template_name = self.default_template
template_path = self.template_dir / template_name
if not template_path.exists():
raise FileNotFoundError(f"模板文件不存在: {template_path}")
return template_path
def save_template(self, source_path: str, template_name: Optional[str] = None) -> str:
"""
保存模板文件到系统
Args:
source_path: 源文件路径
template_name: 模板名称(可选)
Returns:
保存后的模板路径
"""
source_path = Path(source_path)
if not source_path.exists():
raise FileNotFoundError(f"源文件不存在: {source_path}")
if template_name is None:
template_name = self.default_template
dest_path = self.template_dir / template_name
# 复制文件
shutil.copy2(source_path, dest_path)
print(f"✓ 模板已保存: {dest_path}")
return str(dest_path)
def template_exists(self, template_name: Optional[str] = None) -> bool:
"""检查模板是否存在"""
if template_name is None:
template_name = self.default_template
return (self.template_dir / template_name).exists()
def list_templates(self) -> list:
"""列出所有可用模板"""
if not self.template_dir.exists():
return []
return [f.name for f in self.template_dir.glob("*.pdf")]

Binary file not shown.

View File

@@ -0,0 +1,438 @@
{
"COLOR": {
"clinical_en": "Reflects the appearance and properties of urine, assisting in judging the health status of the urinary system and the body as a whole. Normal urine is pale yellow to dark yellow due to containing urobilin and other substances, and is affected by water intake, food, drugs, etc.",
"clinical_cn": "反映尿液的外观性状,辅助判断泌尿系统及身体整体健康状态,正常尿液因含尿色素等呈淡黄色至深黄色,受饮水、食物、药物等影响。"
},
"PH": {
"clinical_en": "Reflects the acidity and alkalinity of urine, affected by diet, diseases, drugs, etc., helping to judge acid - base balance, urinary system diseases, and the impact of drugs. The normal urine pH is approximately 4.5 - 8.0, and the fluctuation is related to diet. For example, a diet rich in meat makes it more acidic, and a diet rich in vegetables makes it more alkaline.",
"clinical_cn": "反映尿液的酸碱性受饮食、疾病、药物等影响帮助判断酸碱平衡、泌尿系统疾病及药物影响等情况。正常尿液pH约 4.5 - 8.0,波动与饮食有关,如食肉多偏酸,食蔬菜多偏碱。"
},
"TUR": {
"clinical_en": "Reflects the clarity and transparency of urine, assisting in judging abnormal components in the urine. Normal urine is clear and transparent, and abnormal turbidity is related to the presence of cells, crystals, bacteria, etc. in the urine.",
"clinical_cn": "体现尿液的清澈透明程度,辅助判断尿中成分异常,正常尿液清晰透明,浊度异常与尿中含细胞、结晶、细菌等有关。"
},
"PRO": {
"clinical_en": "Detects the protein content in urine, which is an important indicator for diagnosing kidney diseases and evaluating kidney damage. Normal urine protein is negative or in trace amounts.",
"clinical_cn": "检测尿中蛋白质含量,是诊断肾脏疾病、评估肾脏损伤的重要指标,正常尿蛋白阴性或极微量。"
},
"BLD/ERY": {
"clinical_en": "Judges whether there are red blood cells and the destruction of red blood cells in the urine, assisting in the diagnosis of urinary system and systemic diseases. There are very few red blood cells in normal urine, and occult blood is negative.",
"clinical_cn": "判断尿中是否有红细胞及红细胞破坏情况,辅助诊断泌尿系统及全身疾病,正常尿中红细胞极少,隐血阴性。"
},
"GLU": {
"clinical_en": "Reflects the glucose content in urine, assisting in the diagnosis of diabetes and other glucose metabolism disorders. Normal urine glucose is negative.",
"clinical_cn": "反映尿中葡萄糖含量,辅助诊断糖尿病及其他糖代谢异常疾病,正常尿糖阴性。"
},
"SG": {
"clinical_en": "Reflects the concentration of solutes in urine, reflecting the kidney's concentrating and diluting functions. The normal reference value is approximately 1.003 - 1.030, affected by water intake, sweating, etc.",
"clinical_cn": "体现尿液中溶质浓度,反映肾脏浓缩和稀释功能,正常参考值约 1.003 - 1.030,受饮水、出汗等影响。"
},
"WBC": {
"clinical_en": "Reflects the acidity and alkalinity of urine, affected by diet, diseases, drugs, etc., helping to judge acid - base balance, urinary system diseases, and the impact of drugs. The normal urine pH is approximately 4.5 - 8.0, and the fluctuation is related to diet. For example, a diet rich in meat makes it more acidic, and a diet rich in vegetables makes it more alkaline.",
"clinical_cn": "反映尿液的酸碱性受饮食、疾病、药物等影响帮助判断酸碱平衡、泌尿系统疾病及药物影响等情况。正常尿液pH约 4.5 - 8.0,波动与饮食有关,如食肉多偏酸,食蔬菜多偏碱。"
},
"NIT": {
"clinical_en": "Assists in the diagnosis of urinary system infections, because some bacteria can reduce nitrates in the urine to nitrites. It is normally negative.",
"clinical_cn": "辅助诊断泌尿系统感染,因某些细菌可将尿中硝酸盐还原为亚硝酸盐,正常阴性。"
},
"KET": {
"clinical_en": "Reflects the state of fat decomposition and metabolism in the body, assisting in the diagnosis of diseases such as diabetic ketoacidosis. Normal urine ketone bodies are negative.",
"clinical_cn": "反映体内脂肪分解代谢情况,辅助诊断糖尿病酮症酸中毒等疾病,正常尿酮体阴性。"
},
"RBC COUNT": {
"clinical_en": "Reflects the total number of red blood cells, is a basic indicator for judging blood diseases such as anemia and erythrocytosis, and is also used to evaluate oxygen - carrying capacity. The normal range for adult males is (4.0 - 5.5) × 10¹²/L, and for adult females is (3.5 - 5.0) × 10¹²/L. 2.",
"clinical_cn": "反映红细胞的总体数量是判断贫血、红细胞增多症等血液疾病的基础指标也用于评估携氧能力。正常成年男性4.0-5.5×10¹²/L女性3.5-5.0×10¹²/L。"
},
"HB": {
"clinical_en": "A key indicator for evaluating anemia and oxygen - carrying capacity, which, together with the number of red blood cells, reflects the oxygen - carrying state of the blood. The normal range for adult males is 120 - 160g/L, and for adult females is 110 - 150g/L. 2.",
"clinical_cn": "评估贫血及携氧能力的关键指标与红细胞数量协同反映血液携氧状态正常成年男性120-160g/L女性110-150g/L。"
},
"HCT": {
"clinical_en": "Refers to the percentage of volume occupied by red blood cells in whole blood, reflecting the concentration of red blood cells, assisting in the diagnosis of anemia and erythrocytosis, and also used in the evaluation of dehydration and other conditions. The normal range for adult males is 40% - 50%, and for adult females is 37% - 48%.",
"clinical_cn": "指红细胞在全血中所占体积百分比反映红细胞的浓缩程度辅助诊断贫血、红细胞增多症也用于脱水等情况评估正常成年男性40%-50%女性37%-48%。"
},
"MCV": {
"clinical_en": "Reflects the average size of red blood cells, helping to classify anemia, such as macrocytic, normocytic, and microcytic anemia. The normal range is 80 - 100fL.",
"clinical_cn": "反映红细胞的平均大小帮助贫血分类如大细胞性、正细胞性、小细胞性贫血正常80-100fL。"
},
"MCH": {
"clinical_en": "Indicates the average amount of hemoglobin contained in each red blood cell, assisting in the diagnosis and classification of anemia. The normal range is 27 - 34pg.",
"clinical_cn": "表示每个红细胞内平均所含血红蛋白的量辅助贫血诊断与分类正常27-34pg。"
},
"MCHC": {
"clinical_en": "Reflects the concentration of hemoglobin in red blood cells, used for anemia classification. The normal range is 320 - 360g/L.",
"clinical_cn": "反映红细胞内血红蛋白的浓度用于贫血分类正常320-360g/L。"
},
"RDW": {
"clinical_en": "Reflects the heterogeneity of red blood cell volume, that is, the degree of dispersion of red blood cell volume, helping in the differential diagnosis of anemia. The normal RDW - CV (coefficient of variation) is 11.5% - 14.5%.",
"clinical_cn": "反映红细胞体积大小的异质性即红细胞体积的离散程度帮助贫血的鉴别诊断正常RDW-CV变异系数11.5%-14.5%。"
},
"RBC MORPHOLOGY": {
"clinical_en": "Observing the morphological characteristics of red blood cells (such as normal, spherical, elliptical, sickle - shaped, etc.) is of great value in the diagnosis of the etiology of anemia and hematological diseases. Normal red blood cells are biconcave discs.",
"clinical_cn": "观察红细胞的形态特点(如正常、球形、椭圆形、镰状等),对贫血病因、血液系统疾病诊断有重要价值,正常红细胞为双凹圆盘状。"
},
"WBC COUNT": {
"clinical_en": "Reflects the total number of white blood cells, is a preliminary indicator for judging infections, inflammation, and certain blood diseases. The normal range for adults is (3.5 - 9.5) × 10⁹/L. 2.",
"clinical_cn": "反映白细胞的总体数量是判断感染、炎症及某些血液疾病的初步指标正常成人3.5-9.5×10⁹/L。"
},
"NEUT%": {
"clinical_en": "Neutrophils are the main component of white blood cells and play a key role in anti - infection. Changes in their count and percentage reflect infection, inflammation, and hematopoietic conditions. The normal count is (2.0 - 7.5) × 10⁹/L, and the percentage is 50% - 70%.",
"clinical_cn": "中性粒细胞是白细胞主要成分在抗感染中起关键作用数量和百分比变化反映感染、炎症及造血情况正常数量2.0-7.5×10⁹/L百分比50%-70%。"
},
"EOS%": {
"clinical_en": "Participates in immune processes such as allergic reactions and anti - parasitic infections. Changes in their count and percentage are significant for the diagnosis of allergic diseases, parasitic infections, etc. The normal count is (0.02 - 0.52) × 10⁹/L, and the percentage is 0.4% - 8%.",
"clinical_cn": "参与过敏反应、抗寄生虫感染等免疫过程数量和百分比变化对过敏疾病、寄生虫感染等诊断有意义正常数量0.02-0.52×10⁹/L百分比0.4%-8%。"
},
"BAS%": {
"clinical_en": "Participates in allergic reactions and releases bioactive substances (such as histamine). Although their number is small, they are valuable for the diagnosis of allergies and certain blood diseases. The normal count is (0 - 0.06) × 10⁹/L, and the percentage is 0% - 1%.",
"clinical_cn": "参与过敏反应释放生物活性物质如组胺数量少但对过敏、某些血液疾病诊断有价值正常数量0-0.06×10⁹/L百分比0%-1%。"
},
"LYMPH%": {
"clinical_en": "Lymphocytes are an important part of immune cells and participate in specific immunity (such as humoral immunity and cellular immunity). Changes in their count and percentage reflect immune status, infections, and blood diseases, etc. The normal count is (1.1 - 3.2) × 10⁹/L, and the percentage is 20% - 50%.",
"clinical_cn": "淋巴细胞是免疫细胞重要组成参与特异性免疫如体液免疫、细胞免疫数量和百分比变化反映免疫状态、感染及血液疾病等正常数量1.1-3.2×10⁹/L百分比20%-50%。"
},
"MONO%": {
"clinical_en": "Monocytes are precursors of macrophages and play a role in anti - infection and immune regulation, participating in the clearance of pathogens, foreign bodies, etc. The normal count is (0.1 - 0.6) × 10⁹/L, and the percentage is 3% - 10%.",
"clinical_cn": "单核细胞是巨噬细胞的前体在抗感染、免疫调节中起作用参与清除病原体、异物等正常数量0.1-0.6×10⁹/L百分比3%-10%。"
},
"PLT": {
"clinical_en": "Reflects the number of platelets, evaluates hemostatic function, and is important for the diagnosis of hemorrhagic diseases, thrombotic diseases, and treatment monitoring. The normal range is (125 - 350) × 10⁹/L.",
"clinical_cn": "反映血小板数量评估止血功能对出血性疾病、血栓性疾病诊断及治疗监测重要正常125-350×10⁹/L。"
},
"PCT": {
"clinical_en": "Refers to the percentage of volume occupied by platelets in whole blood, related to the number and volume of platelets, assisting in evaluating platelet function and hematopoietic conditions. The normal range is 0.11% - 0.28%.",
"clinical_cn": "指血小板在全血中所占体积百分比与血小板数量、体积有关辅助评估血小板功能、造血情况正常0.11%-0.28%。"
},
"MPV": {
"clinical_en": "Reflects the average volume of platelets, which has reference value for platelet function and disease diagnosis. The normal range is 7 - 11fL.",
"clinical_cn": "反映血小板的平均体积大小对血小板功能、疾病诊断有参考价值正常7-11fL。"
},
"PDW": {
"clinical_en": "Reflects the degree of dispersion of platelet volume, indicating the uniformity of platelet volume, assisting in judging platelet abnormalities. The normal PDW is 15% - 17%.",
"clinical_cn": "反映血小板体积大小的离散程度说明血小板体积的均一性辅助判断血小板异常正常PDW15%-17%。"
},
"FBS": {
"clinical_en": "A basic indicator for diagnosing diabetes and evaluating blood sugar control. Normal reference value: 3.9 - 6.1mmol/L.",
"clinical_cn": "诊断糖尿病、评估血糖控制的基础指标正常参考值3.9-6.1mmol/L。"
},
"HBA1C": {
"clinical_en": "Reflects the average blood sugar level in the past 2 - 3 months, evaluating the control effect of diabetes. Normal reference value: 4% - 6%.",
"clinical_cn": "反映过去2-3个月平均血糖水平评估糖尿病控制效果正常参考值4%-6%。"
},
"TC": {
"clinical_en": "Reflects the overall level of cholesterol in the blood, evaluates the risk of atherosclerosis and cardiovascular diseases. Normal reference value: < 5.2mmol/L (appropriate level), 5.2 - 6.2mmol/L is borderline elevation, > 6.2mmol/L is elevation.",
"clinical_cn": "反映血液中胆固醇总体水平评估动脉粥样硬化、心血管疾病风险正常参考值5.2mmol/L合适水平5.2-6.2mmol/L为边缘升高6.2mmol/L为升高。"
},
"TG": {
"clinical_en": "Evaluates dyslipidemia and pancreatitis risk. Normal reference value: < 1.7mmol/L, 1.7 - 2.3mmol/L is borderline elevation, > 2.3mmol/L is elevation.",
"clinical_cn": "评估血脂异常、胰腺炎风险正常参考值1.7mmol/L1.7-2.3mmol/L为边缘升高2.3mmol/L为升高。"
},
"LP(A)": {
"clinical_en": "An independent risk factor for cardiovascular diseases, related to atherosclerosis and thrombosis. Normal reference value: < 300mg/L.",
"clinical_cn": "独立的心血管疾病危险因素与动脉粥样硬化、血栓形成相关正常参考值300mg/L。"
},
"HDL": {
"clinical_en": "\"Good cholesterol\", promotes reverse cholesterol transport, reduces the risk of cardiovascular diseases. Normal reference value: > 1.0mmol/L (males), > 1.3mmol/L (females).",
"clinical_cn": "“好胆固醇”促进胆固醇逆向转运降低心血管疾病风险正常参考值1.0mmol/L男性1.3mmol/L女性。"
},
"LDL": {
"clinical_en": "\"Bad cholesterol\", a core factor causing atherosclerosis, a key indicator for evaluating the risk of cardiovascular diseases. Normal reference value: < 3.4mmol/L (appropriate level), 3.4 - 4.1mmol/L is borderline elevation, > 4.1mmol/L is elevation.",
"clinical_cn": "“坏胆固醇”致动脉粥样硬化核心因素评估心血管疾病风险关键指标正常参考值3.4mmol/L合适水平3.4-4.1mmol/L为边缘升高4.1mmol/L为升高。"
},
"BLOOD TYPE": {
"clinical_en": "Used for blood type identification to ensure blood transfusion safety (blood type matching is required during blood transfusion), and also used in fields such as organ transplantation and forensic medicine.",
"clinical_cn": "用于血型鉴定,保障输血安全(输血时需血型匹配),也在器官移植、法医学等领域有应用。"
},
"BLOOD TYPE RH": {
"clinical_en": "The RH blood type system is important. Most people are RH positive. RH negative people (\"panda blood\") need special attention during blood transfusion, and it is also related to neonatal hemolytic disease (which may occur when the mother is RH negative and the fetus is RH positive).",
"clinical_cn": "RH血型系统重要大部分人RH阳性RH阴性者熊猫血输血时需特殊注意也与新生儿溶血病母亲RH阴性胎儿RH阳性时可能发生相关。"
},
"PT": {
"clinical_en": "Reflects the function of the extrinsic coagulation pathway and the common coagulation pathway, used to evaluate vitamin K deficiency, liver disease, oral anticoagulants (such as warfarin), etc. The normal reference value varies depending on the detection method, generally 11 - 13 seconds, and more than 3 seconds longer than the normal control has clinical significance.",
"clinical_cn": "反映外源性凝血途径及共同凝血途径的功能用于评估维生素K缺乏、肝病、口服抗凝剂如华法林等情况正常参考值因检测方法而异一般11-13秒超过正常对照3秒有临床意义。"
},
"APTT": {
"clinical_en": "Reflects the function of the intrinsic coagulation pathway and the common coagulation pathway, used to diagnose hemophilia (deficiency of intrinsic coagulation factors), evaluate liver disease, monitor heparin therapy, etc. The normal range is 30 - 45 seconds, and more than 10 seconds longer than the normal control is significant.",
"clinical_cn": "反映内源性凝血途径及共同凝血途径的功能用于诊断血友病内源性凝血因子缺乏、评估肝病、监测肝素治疗等正常30-45秒超过正常对照10秒有意义。"
},
"TT": {
"clinical_en": "Reflects the process of converting fibrinogen into fibrin, affected by fibrinogen content, structure, and anticoagulants in the blood. The normal range is 16 - 18 seconds, and more than 3 seconds longer than the normal control is significant.",
"clinical_cn": "反映纤维蛋白原转化为纤维蛋白的过程受纤维蛋白原含量、结构及血液中抗凝物质影响正常16-18秒超过正常对照3秒有意义。"
},
"FIB": {
"clinical_en": "It is a cross - linked fibrin degradation product, reflecting the body's coagulation and fibrinolytic activity, used to diagnose thrombotic diseases such as DIC, deep vein thrombosis, and pulmonary embolism. The normal reference value is generally < 0.5mg/L (ELISA method), and there are differences in different detection methods.",
"clinical_cn": "是交联纤维蛋白降解产物反映体内凝血和纤溶活性用于诊断DIC、深静脉血栓、肺栓塞等血栓性疾病正常参考值一般0.5mg/LELISA法不同检测方法有差异。"
},
"HIV": {
"clinical_en": "Used for the screening and diagnosis of AIDS (AIDS), to determine whether HIV is infected. It is normally negative.",
"clinical_cn": "用于艾滋病AIDS的筛查、诊断判断是否感染HIV正常为阴性。"
},
"TRUST": {
"clinical_en": "It is a non - specific antibody test for syphilis, used for syphilis screening and curative effect observation. It is normally negative.",
"clinical_cn": "是梅毒非特异性抗体检测,用于梅毒筛查、疗效观察,正常为阴性。"
},
"TPPA": {
"clinical_en": "A test for specific antibodies against Treponema pallidum, used for the diagnosis of syphilis. After syphilis infection, specific antibodies are generally positive for life. It is normally negative.",
"clinical_cn": "梅毒螺旋体特异性抗体检测,用于梅毒确诊,感染梅毒后,特异性抗体一般终身阳性,正常为阴性。"
},
"HBSAG": {
"clinical_en": "It is a marker of hepatitis B virus infection, used for hepatitis B screening and diagnosis. It is normally negative.",
"clinical_cn": "是乙肝病毒感染的标志,用于乙肝筛查、诊断,正常为阴性。"
},
"HBSAB": {
"clinical_en": "It is a protective antibody against hepatitis B, indicating immunity to hepatitis B virus. It is normally negative or has a protective titer (generally, ≥10mIU/ml is considered to have protective power).",
"clinical_cn": "是乙肝保护性抗体提示对乙肝病毒有免疫力正常为阴性或有保护性滴度一般认为≥10mIU/ml有保护力。"
},
"HBEAG": {
"clinical_en": "Reflects the degree of hepatitis B virus replication and the strength of infectivity. It is normally negative.",
"clinical_cn": "反映乙肝病毒复制活跃程度、传染性强弱,正常为阴性。"
},
"HBEAB": {
"clinical_en": "Indicates that hepatitis B virus replication is inhibited and infectivity is reduced. It is normally negative.",
"clinical_cn": "表示乙肝病毒复制受抑制,传染性降低,正常为阴性。"
},
"HBCAB": {
"clinical_en": "It is a marker of hepatitis B virus infection. As long as the hepatitis B virus has been infected (whether current infection or past infection), the core antibody is mostly positive. It is normally negative.",
"clinical_cn": "是乙肝病毒感染的标志,只要感染过乙肝病毒(无论现症感染还是既往感染),核心抗体多为阳性,正常为阴性。"
},
"HCV-IGM": {
"clinical_en": "Used for the diagnosis and condition monitoring of acute hepatitis C virus (HCV) infection. It is normally negative.",
"clinical_cn": "用于丙肝病毒HCV急性感染的诊断、病情监测正常为阴性。"
},
"KALIUM": {
"clinical_en": "Maintains cell physiological functions (such as osmotic pressure, acid - base balance, neuromuscular excitability, etc.), and is particularly important for heart function. The normal serum potassium is 3.5 - 5.5mmol/L.",
"clinical_cn": "维持细胞生理功能如渗透压、酸碱平衡、神经肌肉兴奋性等对心脏功能尤其重要正常血清钾3.5-5.5mmol/L。"
},
"SODIUM": {
"clinical_en": "Maintains extracellular fluid osmotic pressure, acid - base balance, participates in nerve conduction, maintains blood volume, etc. The normal serum sodium is 135 - 145mmol/L.",
"clinical_cn": "维持细胞外液渗透压、酸碱平衡参与神经传导、维持血容量等正常血清钠135-145mmol/L。"
},
"CHLORIDE": {
"clinical_en": "Maintains body fluid osmotic pressure, acid - base balance, participates in gastric acid formation, etc. The normal serum chloride is 96 - 108mmol/L.",
"clinical_cn": "维持体液渗透压、酸碱平衡参与胃酸形成等正常血清氯96-108mmol/L。"
},
"CALCIUM": {
"clinical_en": "Maintains bone hardness, neuromuscular excitability, coagulation function, etc. The normal total serum calcium is 2.25 - 2.58mmol/L, and ionized calcium is 1.10 - 1.34mmol/L.",
"clinical_cn": "维持骨骼硬度、神经肌肉兴奋性、凝血功能等正常血清总钙2.25-2.58mmol/L离子钙1.10-1.34mmol/L。"
},
"MAGNESIUM": {
"clinical_en": "Participates in the regulation of various enzyme activities, maintains neuromuscular excitability, heart function, etc. The normal serum magnesium is 0.75 - 1.02mmol/L.",
"clinical_cn": "参与多种酶活性调节维持神经肌肉兴奋性、心脏功能等正常血清镁0.75-1.02mmol/L。"
},
"PHOSPHORUS": {
"clinical_en": "Participates in bone formation, energy metabolism, nucleic acid synthesis, etc. The normal serum phosphorus is 0.97 - 1.61mmol/L.",
"clinical_cn": "参与骨骼形成、能量代谢、核酸合成等正常血清磷0.97-1.61mmol/L。"
},
"TP": {
"clinical_en": "Reflects liver synthetic function and body protein reserves, assisting in the diagnosis of liver and kidney diseases and nutritional status. Normal reference value: 60 - 80g/L (there may be differences in different detection methods).",
"clinical_cn": "反映肝脏合成功能及体内蛋白储备协助诊断肝肾疾病、营养状态。正常参考值60-80g/L不同检测方法可能有差异。"
},
"A": {
"clinical_en": "The main protein synthesized by the liver, reflecting liver synthetic capacity and nutritional status, and maintaining plasma colloid osmotic pressure. Normal reference value: 40 - 55g/L.",
"clinical_cn": "肝脏合成的主要蛋白体现肝脏合成能力、营养状态维持血浆胶体渗透压。正常参考值40-55g/L。"
},
"G": {
"clinical_en": "Participates in immunity, reflects the body's immune status, and together with albumin, judges liver diseases and immune diseases. Normal reference value: 20 - 30g/L.",
"clinical_cn": "参与免疫反映机体免疫状态与白蛋白协同判断肝脏疾病、免疫疾病。正常参考值20-30g/L。"
},
"A/G": {
"clinical_en": "Evaluates liver function and protein metabolism balance. The normal ratio is 1.5 - 2.5:1.",
"clinical_cn": "评估肝脏功能、蛋白代谢平衡正常比值1.5-2.5:1。"
},
"TBIL": {
"clinical_en": "Reflects hepatocyte damage and bile excretion, assisting in the diagnosis of jaundice types (hemolytic, hepatocellular, obstructive). Normal reference value: 3.4 - 17.1μmol/L.",
"clinical_cn": "反映肝细胞损伤、胆汁排泄情况协助诊断黄疸类型溶血性、肝细胞性、梗阻性。正常参考值3.4-17.1μmol/L。"
},
"DBIL": {
"clinical_en": "Also known as conjugated bilirubin, reflects hepatocyte processing of bilirubin and biliary excretion function, assisting in jaundice identification. Normal reference value: 0 - 6.8μmol/L.",
"clinical_cn": "又称结合胆红素反映肝细胞处理胆红素及胆道排泄功能辅助黄疸鉴别。正常参考值0-6.8μmol/L。"
},
"IBIL": {
"clinical_en": "Also known as unconjugated bilirubin, reflects red blood cell destruction and the liver's initial metabolic capacity, assisting in jaundice identification. Normal reference value: 3.4 - 10.2μmol/L (calculated by TBi - DBi).",
"clinical_cn": "又称非结合胆红素反映红细胞破坏及肝脏初步代谢能力辅助黄疸鉴别。正常参考值3.4-10.2μmol/LTBi-DBi计算得。"
},
"ALP": {
"clinical_en": "Reflects hepatocyte damage and biliary obstruction, and is also used to diagnose bone metabolic diseases (such as rickets, bone tumors). Normal reference value: 45 - 125U/L (adults); children and adolescents may have higher values due to bone development.",
"clinical_cn": "反映肝细胞损伤、胆道梗阻也用于诊断骨代谢疾病如佝偻病、骨肿瘤。正常参考值45-125U/L成人儿童、青少年因骨骼发育数值可更高。"
},
"ALT": {
"clinical_en": "A sensitive indicator of hepatocyte damage, which increases in the early stage of hepatitis, drug - induced liver injury, etc. Normal reference value: 7 - 40U/L (there may be differences in different reagents).",
"clinical_cn": "肝细胞损伤敏感指标肝炎、药物肝损伤等早期即升高。正常参考值7-40U/L不同试剂有差异。"
},
"AST": {
"clinical_en": "An indicator of myocardial, liver, and skeletal muscle damage. It increases in myocardial infarction, and can also be abnormal in liver diseases and muscle diseases. Normal reference value: 13 - 35U/L.",
"clinical_cn": "心肌、肝脏、骨骼肌损伤指标心肌梗死时升高肝病、肌肉疾病也可异常。正常参考值13-35U/L。"
},
"GGT": {
"clinical_en": "Reflects hepatocyte damage and biliary obstruction, and is of auxiliary value in the diagnosis of alcoholic liver disease and liver cancer. Normal reference value: 7 - 45U/L (adults).",
"clinical_cn": "反映肝细胞损伤、胆道梗阻对酒精性肝病、肝癌诊断有辅助价值。正常参考值7-45U/L成人。"
},
"SCR": {
"clinical_en": "Reflects glomerular filtration function, evaluates the degree of renal function damage (one of the bases for chronic renal failure staging). Normal reference value: 53 - 106μmol/L for males, 44 - 97μmol/L for females (due to differences in muscle mass).",
"clinical_cn": "反映肾小球滤过功能评估肾功能损伤程度慢性肾衰分期依据之一。正常参考值男性53-106μmol/L女性44-97μmol/L因肌肉量有差异。"
},
"BUN": {
"clinical_en": "Reflects glomerular filtration function and protein metabolism status (greatly affected by diet and extrarenal factors). Normal reference value: 3.2 - 7.1mmol/L.",
"clinical_cn": "反映肾小球滤过功能、蛋白质代谢状态受饮食、肾外因素影响大。正常参考值3.2-7.1mmol/L。"
},
"UA": {
"clinical_en": "Reflects the end product of purine metabolism and renal excretion. Elevated levels (hyperuricemia) are associated with gout, kidney stones, metabolic syndrome, hypertension, and cardiovascular disease.",
"clinical_cn": "反映嘌呤代谢终产物及肾脏排泄功能。升高(高尿酸血症)常与痛风、肾结石、代谢综合征、高血压及心血管疾病相关。"
},
"CK": {
"clinical_en": "A specific indicator of myocardial and skeletal muscle damage, early diagnosis of myocardial infarction (increases 4 - 6h, peaks 1 - 2 days), and also used to diagnose myopathy (such as myositis, progressive muscular dystrophy). Normal reference value: 50 - 310U/L for males, 40 - 200U/L for females.",
"clinical_cn": "心肌、骨骼肌损伤特异性指标心肌梗死早期诊断4-6h升高1-2天达峰也用于诊断肌病如肌炎、进行性肌营养不良。正常参考值男性50-310U/L女性40-200U/L。"
},
"LDH": {
"clinical_en": "An indicator of myocardial, liver, skeletal muscle, and hematological system diseases. It can increase in myocardial infarction (increases 12 - 24h, recovers 1 - 2 weeks), liver diseases, and hemolytic anemia. Normal reference value: 120 - 250U/L.",
"clinical_cn": "心肌、肝脏、骨骼肌、血液系统疾病指标心肌梗死12-24h升高1-2周恢复、肝病、溶血性贫血均可升高。正常参考值120-250U/L。"
},
"CK-MB": {
"clinical_en": "A specific indicator of myocardial damage, early diagnosis of myocardial infarction (increases 3 - 4h, peaks 1 - 2 days), better than total CK. Normal reference value: 0 - 25U/L (activity method); < 4ng/ml (mass method, more accurate).",
"clinical_cn": "心肌损伤特异性指标心肌梗死早期诊断3-4h升高1-2天达峰优于总CK。正常参考值0-25U/L活性法4ng/ml质量法更精准。"
},
"T3": {
"clinical_en": "Reflects thyroid function, increases in hyperthyroidism and decreases in hypothyroidism, and is also used to evaluate the severity of the disease (such as significantly high T3 in thyroid storm). Normal reference value: 1.3 - 3.1nmol/L.",
"clinical_cn": "反映甲状腺功能甲亢时升高、甲减时降低也用于评估病情严重度如甲亢危象T3显著高。正常参考值1.3-3.1nmol/L。"
},
"T4": {
"clinical_en": "Reflects thyroid function, increases in hyperthyroidism and decreases in hypothyroidism. Because T4 has a long half - life, it more stably reflects thyroid reserve function. Normal reference value: 12 - 22pmol/L.",
"clinical_cn": "反映甲状腺功能甲亢时升高、甲减时降低因T4半衰期长更稳定反映甲状腺储备功能。正常参考值12-22pmol/L。"
},
"FT3": {
"clinical_en": "Reflects the \"active indicator\" of thyroid function, not affected by binding proteins, more accurately diagnosing hyperthyroidism / hypothyroidism. Normal reference value: 3.1 - 6.8pmol/L.",
"clinical_cn": "反映甲状腺功能的“活性指标”,不受结合蛋白影响,更精准诊断甲亢/甲减。正常参考值3.1-6.8pmol/L。"
},
"FT4": {
"clinical_en": "The \"active indicator\" of thyroid function, stably reflects thyroid hormone levels, and is a core indicator for the diagnosis and efficacy monitoring of hyperthyroidism / hypothyroidism. Normal reference value: 12 - 22pmol/L.",
"clinical_cn": "甲状腺功能“活性指标”,稳定反映甲状腺激素水平,甲亢/甲减诊断、疗效监测核心指标。正常参考值12-22pmol/L。"
},
"TSH": {
"clinical_en": "Reflects the function of the pituitary - thyroid axis, decreases in hyperthyroidism and increases in hypothyroidism, and is the preferred screening indicator for hypothyroidism (sensitive). Normal reference value: 0.27 - 4.2mIU/L.",
"clinical_cn": "反映垂体-甲状腺轴功能甲亢时降低、甲减时升高是甲减首选筛查指标敏感。正常参考值0.27-4.2mIU/L。"
},
"HOMOCYSTEINE": {
"clinical_en": "An independent risk factor for cardiovascular and cerebrovascular diseases, elevation increases the risk of atherosclerosis, cerebral infarction, and coronary heart disease. Normal reference value: 5 - 15μmol/L.",
"clinical_cn": "心脑血管疾病独立危险因素升高增加动脉粥样硬化、脑梗死、冠心病风险正常参考值5-15μmol/L。"
},
"D-DIMER": {
"clinical_en": "(mentioned in the four coagulation items above, focusing on cardiovascular and cerebrovascular risks here) Reflects the body's coagulation and fibrinolytic activity, assisting in the diagnosis of pulmonary embolism, deep vein thrombosis, etc. The normal reference value is < 0.5mg/L (ELISA method).",
"clinical_cn": "反映体内凝血和纤溶活性辅助诊断肺栓塞、深静脉血栓等正常参考值0.5mg/L。"
},
"25-OH-VD2+D3": {
"clinical_en": "Reflects the bodys vitamin D status and calcium-phosphorus metabolism. It is the most reliable marker for evaluating vitamin D reserves. Deficiency is associated with rickets, osteomalacia, osteoporosis, and increased risks of fractures and certain chronic diseases. The optimal reference range is usually 30100 ng/mL, with <20 ng/mL indicating deficiency.",
"clinical_cn": "反映机体维生素D水平及钙磷代谢状态是评估维生素D储备最可靠的指标。缺乏与佝偻病、骨软化症、骨质疏松及骨折风险增加等相关也可能与部分慢性疾病发生率升高有关。正常参考范围通常为 30100 ng/mL20 ng/mL 提示缺乏。"
},
"PTH": {
"clinical_en": "Regulates calcium and phosphorus metabolism, maintains blood calcium stability, and diagnoses hyperparathyroidism / hypoparathyroidism. Normal reference value: 15 - 65pg/ml.",
"clinical_cn": "调节钙磷代谢,维持血钙稳定,诊断甲状旁腺功能亢进/减退正常参考值15-65pg/ml。"
},
"OST": {
"clinical_en": "Serves as a sensitive marker of bone formation and osteoblastic activity. Elevated levels suggest active bone metabolism (e.g., during growth or fracture healing), while reduced levels may indicate impaired bone formation, osteoporosis, or metabolic bone disease. It is also used to monitor the efficacy of anti-osteoporosis treatments.",
"clinical_cn": "是反映骨形成和成骨细胞活性的敏感指标。升高提示骨代谢活跃(如儿童生长期或骨折愈合期),降低则可能提示骨形成不足、骨质疏松或代谢性骨病。常用于骨质疏松治疗过程中的疗效监测。"
},
"TPINP": {
"clinical_en": "A bone formation marker, reflecting the synthesis of type I collagen, evaluating bone metabolism and the effect of osteoporosis treatment. The normal reference value varies depending on the detection method, generally 14 - 42ng/ml for adult males, 10 - 35ng/ml for premenopausal females, and 19 - 71ng/ml for postmenopausal females.",
"clinical_cn": "骨形成标志物反映I型胶原合成情况评估骨代谢、骨质疏松治疗效果正常参考值因检测方法而异一般成人男性14-42ng/ml女性绝经前10-35ng/ml绝经后19-71ng/ml。"
},
"Β-CTX": {
"clinical_en": "A bone resorption marker, reflecting bone collagen degradation, evaluating osteoporosis and bone turnover rate. The normal reference value is 0.13 - 0.47ng/ml for adult males, 0.11 - 0.43ng/ml for premenopausal females, and 0.23 - 0.78ng/ml for postmenopausal females.",
"clinical_cn": "骨吸收标志物反映骨胶原降解情况评估骨质疏松、骨转换率正常参考值成人男性0.13-0.47ng/ml女性绝经前0.11-0.43ng/ml绝经后0.23-0.78ng/ml。"
},
"PB": {
"clinical_en": "Evaluates lead exposure and lead poisoning risk. Excessive lead can damage the nervous system, blood system, digestive system, etc.",
"clinical_cn": "评估铅暴露及铅中毒风险,铅过量会损害神经系统、血液系统、消化系统等。"
},
"CU": {
"clinical_en": "Participates in the synthesis of various enzymes (such as ceruloplasmin, superoxide dismutase), evaluates nutritional status, liver diseases, and genetic metabolic diseases (such as Wilson's disease).",
"clinical_cn": "参与多种酶(如铜蓝蛋白、超氧化物歧化酶)合成,评估营养状态、肝脏疾病、遗传代谢病(如肝豆状核变性)。"
},
"ZN": {
"clinical_en": "Participates in growth and development, immune function, and substance metabolism, evaluates nutritional status, immune diseases, and hereditary zinc deficiency diseases.",
"clinical_cn": "参与生长发育、免疫功能、物质代谢,评估营养状态、免疫疾病、遗传性锌缺乏病。"
},
"MG": {
"clinical_en": "Participates in enzyme activity, neuromuscular function, and bone metabolism, evaluates electrolyte balance and nutritional status.",
"clinical_cn": "参与酶活性、神经肌肉功能、骨代谢,评估电解质平衡、营养状态。"
},
"FE": {
"clinical_en": "Participates in hemoglobin synthesis and oxygen transport, evaluates iron - deficiency anemia and iron overload (such as hemochromatosis).",
"clinical_cn": "参与血红蛋白合成、氧运输,评估缺铁性贫血、铁过载(如血色病)。"
},
"CD3+": {
"clinical_en": "Reflects the total number of T cells, evaluates cellular immune function, and assists in the diagnosis of immunodeficiency diseases, autoimmune diseases, and tumor immune status.",
"clinical_cn": "反映总T细胞数量评估细胞免疫功能辅助诊断免疫缺陷病、自身免疫病、肿瘤免疫状态。"
},
"CD4+": {
"clinical_en": "Assists in immune response, is a core indicator for AIDS diagnosis and condition monitoring, and is also used to evaluate autoimmune diseases and tumor immune status.",
"clinical_cn": "辅助免疫应答,是艾滋病诊断、病情监测核心指标,也用于评估自身免疫病、肿瘤免疫状态。"
},
"CD8+": {
"clinical_en": "Reflects the clarity and transparency of urine, assisting in judging abnormal components in the urine. Normal urine is clear and transparent, and abnormal turbidity is related to the presence of cells, crystals, bacteria, etc. in the urine.",
"clinical_cn": "杀伤感染细胞、肿瘤细胞,评估免疫功能、肿瘤免疫监视、病毒感染状态。"
},
"IGG": {
"clinical_en": "The most abundant immunoglobulin in the body, involved in anti-infection and autoimmune regulation; used to assess humoral immune function, diagnose autoimmune diseases, and infectious diseases.",
"clinical_cn": "体内含量最高的免疫球蛋白,参与抗感染、自身免疫调节,评估体液免疫功能、诊断自身免疫病、感染性疾病。"
},
"IGA": {
"clinical_en": "Participates in mucosal immunity (respiratory and gastrointestinal tracts); used for evaluating infections, autoimmune diseases, and allergic disorders.",
"clinical_cn": "参与黏膜免疫(如呼吸道、消化道),评估感染、自身免疫病、过敏性疾病。"
},
"IGM": {
"clinical_en": "An early-response immunoglobulin during acute infection; used to evaluate acute infections, autoimmune diseases, and hematological disorders (e.g., Waldenströms macroglobulinemia).",
"clinical_cn": "感染早期快速应答的免疫球蛋白,评估急性感染、自身免疫病、血液病(如巨球蛋白血症)。"
},
"IGE": {
"clinical_en": "Involved in allergic diseases and immune response against parasitic infections; used to assess allergic status and parasitic diseases.",
"clinical_cn": "参与过敏性疾病、寄生虫感染免疫应答,评估过敏状态、寄生虫病。"
},
"C3": {
"clinical_en": "A central component of the complement system; plays a role in immune defense and regulation; used in evaluating autoimmune diseases, infectious diseases, and renal disorders.",
"clinical_cn": "补体系统核心成分,参与免疫防御、免疫调节,评估自身免疫病、感染性疾病、肾脏疾病。"
},
"C4": {
"clinical_en": "Involved in activation of the classical complement pathway; aids in the diagnosis of autoimmune diseases (e.g., systemic lupus erythematosus) and hereditary complement deficiencies.",
"clinical_cn": "参与补体经典途径激活,辅助诊断自身免疫病(如系统性红斑狼疮)、遗传性补体缺陷病。"
},
"CRP": {
"clinical_en": "An acute-phase protein; rapidly assesses infection, inflammation, and tissue injury.",
"clinical_cn": "急性时相反应蛋白,快速评估感染、炎症、组织损伤程度。"
},
"ESR": {
"clinical_en": "Indirectly reflects inflammation, tissue damage, and anemia; assists in diagnosing infections, autoimmune diseases, and malignancies.",
"clinical_cn": "间接反映炎症、组织损伤、贫血等,辅助诊断感染、自身免疫病、肿瘤。"
},
"ASO": {
"clinical_en": "Used to diagnose Group A β-hemolytic streptococcal infections (e.g., scarlet fever, rheumatic fever, acute glomerulonephritis); reflects recent infection history.",
"clinical_cn": "诊断A组溶血性链球菌感染如猩红热、风湿热、急性肾小球肾炎评估近期感染史。"
},
"ANA": {
"clinical_en": "A screening marker for autoimmune diseases (particularly connective tissue diseases); used in evaluating systemic lupus erythematosus, rheumatoid arthritis, and Sjögrens syndrome.",
"clinical_cn": "自身免疫病(尤其是结缔组织病)的筛查指标,评估系统性红斑狼疮、类风湿关节炎、干燥综合征等。"
},
"RF": {
"clinical_en": "Helps in diagnosing rheumatoid arthritis; used to assess disease activity and prognosis.",
"clinical_cn": "辅助诊断类风湿关节炎,评估疾病活动度、预后。"
},
"NEUT": {
"clinical_en": "Neutrophils are the main component of white blood cells and play a key role in anti - infection. Changes in their count and percentage reflect infection, inflammation, and hematopoietic conditions. The normal count is (2.0 - 7.5) × 10⁹/L, and the percentage is 50% - 70%.",
"clinical_cn": "中性粒细胞是白细胞主要成分在抗感染中起关键作用数量和百分比变化反映感染、炎症及造血情况正常数量2.0-7.5×10⁹/L百分比50%-70%。"
},
"EOS": {
"clinical_en": "Participates in immune processes such as allergic reactions and anti - parasitic infections. Changes in their count and percentage are significant for the diagnosis of allergic diseases, parasitic infections, etc. The normal count is (0.02 - 0.52) × 10⁹/L, and the percentage is 0.4% - 8%.",
"clinical_cn": "参与过敏反应、抗寄生虫感染等免疫过程数量和百分比变化对过敏疾病、寄生虫感染等诊断有意义正常数量0.02-0.52×10⁹/L百分比0.4%-8%。"
},
"BAS": {
"clinical_en": "Participates in allergic reactions and releases bioactive substances (such as histamine). Although their number is small, they are valuable for the diagnosis of allergies and certain blood diseases. The normal count is (0 - 0.06) × 10⁹/L, and the percentage is 0% - 1%.",
"clinical_cn": "参与过敏反应释放生物活性物质如组胺数量少但对过敏、某些血液疾病诊断有价值正常数量0-0.06×10⁹/L百分比0%-1%。"
},
"LYMPH": {
"clinical_en": "Lymphocytes are an important part of immune cells and participate in specific immunity (such as humoral immunity and cellular immunity). Changes in their count and percentage reflect immune status, infections, and blood diseases, etc. The normal count is (1.1 - 3.2) × 10⁹/L, and the percentage is 20% - 50%.",
"clinical_cn": "淋巴细胞是免疫细胞重要组成参与特异性免疫如体液免疫、细胞免疫数量和百分比变化反映免疫状态、感染及血液疾病等正常数量1.1-3.2×10⁹/L百分比20%-50%。"
},
"MONO": {
"clinical_en": "Monocytes are precursors of macrophages and play a role in anti - infection and immune regulation, participating in the clearance of pathogens, foreign bodies, etc. The normal count is (0.1 - 0.6) × 10⁹/L, and the percentage is 3% - 10%.",
"clinical_cn": "单核细胞是巨噬细胞的前体在抗感染、免疫调节中起作用参与清除病原体、异物等正常数量0.1-0.6×10⁹/L百分比3%-10%。"
}
}

370
backend/test_baidu_ocr.py Normal file
View File

@@ -0,0 +1,370 @@
# -*- coding: utf-8 -*-
"""
百度OCR识别测试脚本
测试目标使用百度OCR对产品报价表图片进行识别验证识别效果
"""
import os
import sys
import io
import json
import time
from pathlib import Path
# 修复 Windows 终端 UTF-8 输出
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8", errors="replace")
# 加载环境变量
from dotenv import load_dotenv
load_dotenv(Path(__file__).parent / ".env")
from aip import AipOcr
# ==================== 配置 ====================
APP_ID = os.getenv("BAIDU_OCR_APP_ID", "")
API_KEY = os.getenv("BAIDU_OCR_API_KEY", "")
SECRET_KEY = os.getenv("BAIDU_OCR_SECRET_KEY", "")
# 测试图片路径
IMAGE_PATH = r"C:\Users\UI\.cursor\projects\c-Users-UI-Desktop\assets\c__Users_UI_AppData_Roaming_Cursor_User_workspaceStorage_6df83b93d4a0651428307542725e79d8_images_ecdbe509-3f63-49c0-a8be-db9facaef857_3_-4dec6c0d-a755-4bda-8780-9e6b20e02df8.png"
def test_accurate_basic(client, image_data):
"""测试1通用文字识别高精度版- basicAccurate"""
print("\n" + "=" * 70)
print("[测试1] 通用文字识别(高精度版)- basicAccurate")
print("=" * 70)
start = time.time()
result = client.basicAccurate(image_data)
elapsed = time.time() - start
if "error_code" in result:
print(f" [FAIL] 错误 ({result['error_code']}): {result.get('error_msg', '未知错误')}")
return None
words_result = result.get("words_result", [])
print(f" [OK] 识别成功 | 耗时: {elapsed:.2f}s | 识别行数: {len(words_result)}")
print("-" * 70)
for i, item in enumerate(words_result):
print(f" [{i+1:3d}] {item['words']}")
return result
def test_accurate(client, image_data):
"""测试2通用文字识别高精度含位置版- accurate"""
print("\n" + "=" * 70)
print("[测试2] 通用文字识别(高精度含位置版)- accurate")
print("=" * 70)
start = time.time()
result = client.accurate(image_data)
elapsed = time.time() - start
if "error_code" in result:
print(f" [FAIL] 错误 ({result['error_code']}): {result.get('error_msg', '未知错误')}")
return None
words_result = result.get("words_result", [])
print(f" [OK] 识别成功 | 耗时: {elapsed:.2f}s | 识别行数: {len(words_result)}")
print("-" * 70)
for i, item in enumerate(words_result):
loc = item.get("location", {})
pos_str = f"(x={loc.get('left',0)}, y={loc.get('top',0)}, w={loc.get('width',0)}, h={loc.get('height',0)})"
print(f" [{i+1:3d}] {pos_str:40s} {item['words']}")
return result
def test_general_basic(client, image_data):
"""测试3通用文字识别标准版- basicGeneral"""
print("\n" + "=" * 70)
print("[测试3] 通用文字识别(标准版)- basicGeneral")
print("=" * 70)
start = time.time()
result = client.basicGeneral(image_data)
elapsed = time.time() - start
if "error_code" in result:
print(f" [FAIL] 错误 ({result['error_code']}): {result.get('error_msg', '未知错误')}")
return None
words_result = result.get("words_result", [])
print(f" [OK] 识别成功 | 耗时: {elapsed:.2f}s | 识别行数: {len(words_result)}")
print("-" * 70)
for i, item in enumerate(words_result):
print(f" [{i+1:3d}] {item['words']}")
return result
def test_table_recognize(client, image_data):
"""测试4表格文字识别 - tableRecognition (异步)"""
print("\n" + "=" * 70)
print("[测试4] 表格文字识别 - tableRecognitionAsync")
print("=" * 70)
# 提交表格识别请求
start = time.time()
result = client.tableRecognitionAsync(image_data)
if "error_code" in result:
print(f" [FAIL] 提交失败 ({result['error_code']}): {result.get('error_msg', '未知错误')}")
return None
# tableRecognitionAsync 返回格式可能不同,兼容处理
result_list = result.get("result", [])
if isinstance(result_list, list) and len(result_list) > 0:
request_id = result_list[0].get("request_id", "")
elif isinstance(result_list, dict):
request_id = result_list.get("request_id", "")
else:
request_id = ""
if not request_id:
print(f" [FAIL] 未获取到 request_id返回结果: {json.dumps(result, ensure_ascii=False)}")
return None
print(f" [INFO] 提交成功 | request_id: {request_id}")
print(" [INFO] 等待识别结果...")
# 轮询获取结果最多等60秒
ret_code = -1
for attempt in range(20):
time.sleep(3)
get_result = client.getTableRecognitionResult(request_id)
if "error_code" in get_result:
print(f" [FAIL] 查询失败 ({get_result['error_code']}): {get_result.get('error_msg', '')}")
return None
percent = get_result.get("result", {}).get("percent", 0)
ret_code = get_result.get("result", {}).get("ret_code", -1)
if ret_code == 3:
# 识别完成
elapsed = time.time() - start
print(f" [OK] 识别完成 | 耗时: {elapsed:.2f}s")
# 解析表格结果
result_data = get_result.get("result", {}).get("result_data", "")
if result_data:
print("-" * 70)
print(" 表格识别结果(原始):")
try:
table_data = json.loads(result_data)
formatted = json.dumps(table_data, ensure_ascii=False, indent=2)
print(formatted[:5000])
if len(formatted) > 5000:
print(" ... (结果过长,已截断)")
except Exception:
print(result_data[:5000])
return get_result
print(f" 轮询 {attempt+1}/20 | 进度: {percent}%")
elapsed = time.time() - start
print(f" [WARN] 超时(等待 {elapsed:.1f}s最后状态: ret_code={ret_code}")
return None
def test_web_image(client, image_data):
"""测试5网络图片文字识别 - webImage"""
print("\n" + "=" * 70)
print("[测试5] 网络图片文字识别 - webImage")
print("=" * 70)
start = time.time()
result = client.webImage(image_data)
elapsed = time.time() - start
if "error_code" in result:
print(f" [FAIL] 错误 ({result['error_code']}): {result.get('error_msg', '未知错误')}")
return None
words_result = result.get("words_result", [])
print(f" [OK] 识别成功 | 耗时: {elapsed:.2f}s | 识别行数: {len(words_result)}")
print("-" * 70)
for i, item in enumerate(words_result):
print(f" [{i+1:3d}] {item['words']}")
return result
def test_table_sync(client, image_data):
"""测试6表格识别同步版- form"""
print("\n" + "=" * 70)
print("[测试6] 表格识别(同步版)- form")
print("=" * 70)
start = time.time()
result = client.form(image_data)
elapsed = time.time() - start
if "error_code" in result:
print(f" [FAIL] 错误 ({result['error_code']}): {result.get('error_msg', '未知错误')}")
return None
forms_result = result.get("forms_result", [])
print(f" [OK] 识别成功 | 耗时: {elapsed:.2f}s | 表单数: {len(forms_result)}")
print("-" * 70)
# 打印表格内容
for f_idx, form in enumerate(forms_result):
print(f"\n === 表单 {f_idx + 1} ===")
header = form.get("header", [])
body = form.get("body", [])
footer = form.get("footer", [])
if header:
print(" [表头]")
for row in header:
if isinstance(row, dict):
print(f" {row.get('words', row)}")
elif isinstance(row, list):
row_text = " | ".join(
cell.get("words", str(cell)) if isinstance(cell, dict) else str(cell)
for cell in row
)
print(f" {row_text}")
if body:
print(" [表体]")
for r_idx, row in enumerate(body[:80]):
if isinstance(row, dict):
print(f" {row.get('words', row)}")
elif isinstance(row, list):
row_text = " | ".join(
cell.get("words", str(cell)) if isinstance(cell, dict) else str(cell)
for cell in row
)
print(f" {row_text}")
if len(body) > 80:
print(f" ... (共 {len(body)} 行)")
# 如果 forms_result 为空,打印原始结果
if not forms_result:
print(f" 原始结果键: {list(result.keys())}")
formatted = json.dumps(result, ensure_ascii=False, indent=2)
print(formatted[:3000])
return result
def save_results(results, output_path):
"""保存识别结果到JSON文件"""
with open(output_path, "w", encoding="utf-8") as f:
json.dump(results, f, ensure_ascii=False, indent=2)
print(f"\n[SAVE] 结果已保存到: {output_path}")
def main():
print("=" * 70)
print("百度OCR识别测试 - 产品报价表图片")
print("=" * 70)
# 检查配置
if not all([APP_ID, API_KEY, SECRET_KEY]):
print("[FAIL] 百度OCR未配置请检查 .env 文件中的 BAIDU_OCR_* 变量")
sys.exit(1)
print(f" APP_ID: {APP_ID}")
print(f" API_KEY: {API_KEY[:8]}...")
print(f" SECRET_KEY: {SECRET_KEY[:8]}...")
# 检查图片文件
if not Path(IMAGE_PATH).exists():
print(f"[FAIL] 图片文件不存在: {IMAGE_PATH}")
sys.exit(1)
file_size = Path(IMAGE_PATH).stat().st_size
print(f" 图片路径: {IMAGE_PATH}")
print(f" 文件大小: {file_size / 1024:.1f} KB")
# 初始化百度OCR客户端
client = AipOcr(APP_ID, API_KEY, SECRET_KEY)
# 读取图片
with open(IMAGE_PATH, "rb") as f:
image_data = f.read()
print(f" 图片数据: {len(image_data)} bytes")
# 收集所有测试结果
all_results = {}
# ---- 测试1高精度版 ----
r1 = test_accurate_basic(client, image_data)
if r1:
all_results["accurate_basic"] = {
"method": "basicAccurate高精度版",
"lines": len(r1.get("words_result", [])),
"data": r1,
}
# ---- 测试2高精度含位置版 ----
r2 = test_accurate(client, image_data)
if r2:
all_results["accurate_with_location"] = {
"method": "accurate高精度含位置版",
"lines": len(r2.get("words_result", [])),
"data": r2,
}
# ---- 测试3标准版 ----
r3 = test_general_basic(client, image_data)
if r3:
all_results["general_basic"] = {
"method": "basicGeneral标准版",
"lines": len(r3.get("words_result", [])),
"data": r3,
}
# ---- 测试4表格识别异步 ----
r4 = test_table_recognize(client, image_data)
if r4:
all_results["table_recognition_async"] = {
"method": "tableRecognitionAsync表格识别-异步)",
"data": r4,
}
# ---- 测试5网络图片文字识别 ----
r5 = test_web_image(client, image_data)
if r5:
all_results["web_image"] = {
"method": "webImage网络图片文字识别",
"lines": len(r5.get("words_result", [])),
"data": r5,
}
# ---- 测试6表格识别同步 ----
r6 = test_table_sync(client, image_data)
if r6:
all_results["table_sync"] = {
"method": "form表格识别-同步)",
"data": r6,
}
# ---- 汇总 ----
print("\n" + "=" * 70)
print("测试汇总")
print("=" * 70)
for key, val in all_results.items():
lines = val.get("lines", "N/A")
print(f" {val['method']:45s} 识别行数: {lines}")
# 保存结果
output_path = Path(__file__).parent / "test_baidu_ocr_results.json"
save_results(all_results, output_path)
print("\n[DONE] 所有测试完成!")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,552 @@
{
"accurate_with_location": {
"method": "accurate高精度含位置版",
"lines": 60,
"data": {
"words_result": [
{
"words": "南昌环宇产品报价",
"location": {
"top": 0,
"left": 287,
"width": 213,
"height": 15
}
},
{
"words": "2026.02.04仅供参考",
"location": {
"top": 2,
"left": 533,
"width": 156,
"height": 12
}
},
{
"words": "随时变化",
"location": {
"top": 0,
"left": 689,
"width": 61,
"height": 15
}
},
{
"words": "报价仅针对经销商价格仅供参考行情变化快不接受退货咨询电话0791-86315357",
"location": {
"top": 20,
"left": 313,
"width": 259,
"height": 6
}
},
{
"words": "绿色降/红色涨/蓝色订货/黑色无变化",
"location": {
"top": 20,
"left": 604,
"width": 109,
"height": 6
}
},
{
"words": "达内存国态麦国主板艾尔莎板卡华南金牌主板惠视现代显示器美奇显示器。2019年8月8日起intel所有CPU均保修三年散片/原盒),之前的按照标贴处理,所有硬盘(含国态/U盘不对任何存储数负责",
"location": {
"top": 31,
"left": 195,
"width": 656,
"height": 7
}
},
{
"words": "1-11intad",
"location": {
"top": 43,
"left": 19,
"width": 34,
"height": 6
}
},
{
"words": "WONA5PLU5保三",
"location": {
"top": 45,
"left": 117,
"width": 81,
"height": 3
}
},
{
"words": "ST驶PC盘二年换",
"location": {
"top": 43,
"left": 236,
"width": 60,
"height": 6
}
},
{
"words": "菱光国三年取",
"location": {
"top": 43,
"left": 351,
"width": 45,
"height": 6
}
},
{
"words": "龙内存三年",
"location": {
"top": 43,
"left": 460,
"width": 44,
"height": 6
}
},
{
"words": "金达因品三年保",
"location": {
"top": 43,
"left": 586,
"width": 50,
"height": 6
}
},
{
"words": "U型/T卡系列",
"location": {
"top": 43,
"left": 717,
"width": 45,
"height": 6
}
},
{
"words": "WD20PX 5400RFM/128548",
"location": {
"top": 52,
"left": 105,
"width": 97,
"height": 6
}
},
{
"words": "013000068002100",
"location": {
"top": 54,
"left": 217,
"width": 74,
"height": 3
}
},
{
"words": "403400U",
"location": {
"top": 54,
"left": 431,
"width": 63,
"height": 3
}
},
{
"words": "83.0",
"location": {
"top": 52,
"left": 818,
"width": 29,
"height": 8
}
},
{
"words": "4F",
"location": {
"top": 61,
"left": 108,
"width": 26,
"height": 3
}
},
{
"words": "165",
"location": {
"top": 60,
"left": 515,
"width": 20,
"height": 6
}
},
{
"words": "达P202560",
"location": {
"top": 61,
"left": 581,
"width": 40,
"height": 3
}
},
{
"words": "275",
"location": {
"top": 60,
"left": 668,
"width": 8,
"height": 10
}
},
{
"words": "2",
"location": {
"top": 61,
"left": 690,
"width": 13,
"height": 3
}
},
{
"words": "600",
"location": {
"top": 61,
"left": 705,
"width": 9,
"height": 3
}
},
{
"words": "2HDM",
"location": {
"top": 61,
"left": 956,
"width": 20,
"height": 3
}
},
{
"words": "8-0J57,28T47ALEL4/47",
"location": {
"top": 81,
"left": 110,
"width": 66,
"height": 3
}
},
{
"words": "达4P1803120r",
"location": {
"top": 81,
"left": 580,
"width": 55,
"height": 3
}
},
{
"words": "582",
"location": {
"top": 81,
"left": 716,
"width": 22,
"height": 3
}
},
{
"words": "2900",
"location": {
"top": 86,
"left": 192,
"width": 12,
"height": 10
}
},
{
"words": "T0HA/动",
"location": {
"top": 97,
"left": 226,
"width": 75,
"height": 3
}
},
{
"words": "4个U5",
"location": {
"top": 92,
"left": 824,
"width": 28,
"height": 8
}
},
{
"words": "内",
"location": {
"top": 117,
"left": 457,
"width": 51,
"height": 3
}
},
{
"words": "4000M00244T",
"location": {
"top": 120,
"left": 123,
"width": 49,
"height": 6
}
},
{
"words": "金士/T列三年月区",
"location": {
"top": 137,
"left": 703,
"width": 72,
"height": 3
}
},
{
"words": "79",
"location": {
"top": 140,
"left": 22,
"width": 6,
"height": 5
}
},
{
"words": "75120000007",
"location": {
"top": 161,
"left": 117,
"width": 37,
"height": 3
}
},
{
"words": "6",
"location": {
"top": 180,
"left": 9,
"width": 4,
"height": 4
}
},
{
"words": "/13400",
"location": {
"top": 181,
"left": 36,
"width": 18,
"height": 3
}
},
{
"words": "LD",
"location": {
"top": 181,
"left": 157,
"width": 20,
"height": 3
}
},
{
"words": "0",
"location": {
"top": 213,
"left": 670,
"width": 4,
"height": 6
}
},
{
"words": "000",
"location": {
"top": 228,
"left": 668,
"width": 8,
"height": 6
}
},
{
"words": "440",
"location": {
"top": 249,
"left": 669,
"width": 8,
"height": 3
}
},
{
"words": "0",
"location": {
"top": 260,
"left": 217,
"width": 4,
"height": 4
}
},
{
"words": "KT三年",
"location": {
"top": 265,
"left": 124,
"width": 67,
"height": 3
}
},
{
"words": "D5疗三年会",
"location": {
"top": 265,
"left": 580,
"width": 62,
"height": 3
}
},
{
"words": "0",
"location": {
"top": 267,
"left": 304,
"width": 6,
"height": 6
}
},
{
"words": "GAMNG WPUUS05 ",
"location": {
"top": 269,
"left": 919,
"width": 68,
"height": 3
}
},
{
"words": "00",
"location": {
"top": 274,
"left": 197,
"width": 7,
"height": 3
}
},
{
"words": "同情机主版以倒三年",
"location": {
"top": 276,
"left": 934,
"width": 55,
"height": 6
}
},
{
"words": "XD",
"location": {
"top": 320,
"left": 840,
"width": 18,
"height": 3
}
},
{
"words": "130",
"location": {
"top": 396,
"left": 257,
"width": 14,
"height": 3
}
},
{
"words": "TT",
"location": {
"top": 460,
"left": 159,
"width": 10,
"height": 3
}
},
{
"words": "固店三年有",
"location": {
"top": 476,
"left": 589,
"width": 45,
"height": 3
}
},
{
"words": "1",
"location": {
"top": 480,
"left": 306,
"width": 3,
"height": 5
}
},
{
"words": "士05内三年",
"location": {
"top": 496,
"left": 438,
"width": 87,
"height": 3
}
},
{
"words": "GDU",
"location": {
"top": 500,
"left": 933,
"width": 28,
"height": 3
}
},
{
"words": "127:25",
"location": {
"top": 512,
"left": 618,
"width": 25,
"height": 3
}
},
{
"words": "MAIGU板三个月三年",
"location": {
"top": 516,
"left": 795,
"width": 101,
"height": 4
}
},
{
"words": "0",
"location": {
"top": 531,
"left": 885,
"width": 5,
"height": 5
}
},
{
"words": "PU三",
"location": {
"top": 536,
"left": 22,
"width": 60,
"height": 3
}
},
{
"words": "1",
"location": {
"top": 540,
"left": 244,
"width": 21,
"height": 3
}
}
],
"words_result_num": 60,
"log_id": 2019648663845167347
}
}
}

View File

@@ -0,0 +1,551 @@
# -*- coding: utf-8 -*-
"""
测试提取逻辑 - 不调用OCR/DeepSeek API纯本地测试
测试内容:
1. parse_medical_data_v2: OCR文本 → 检测项解析
2. classify_abb_module: ABB/项目名 → 模块分类(含中文关键词)
3. match_with_template: 提取数据 → 模板匹配
"""
import sys
import os
import io
import json
# 修复 Windows 终端 UTF-8
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8", errors="replace")
# 确保 backend 目录在 path 中
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from parse_medical_v2 import parse_medical_data_v2, clean_extracted_data_v2
from extract_and_fill_report import classify_abb_module, match_with_template
# ============================================================
# 测试1: classify_abb_module - ABB硬编码映射
# ============================================================
def test_abb_mapping():
"""测试ABB硬编码映射能否正确分类"""
print("\n" + "=" * 70)
print("[测试1] ABB硬编码映射")
print("=" * 70)
test_cases = [
# (abb, project_name, expected_module)
# 尿检
("COLOR", "Color", "Urine Detection"),
("PH", "pH", "Urine Detection"),
("PRO", "Protein", "Urine Detection"),
("SG", "Specific Gravity", "Urine Detection"),
# 血常规
("WBC", "White Blood Cell", "Complete Blood Count"),
("RBC", "Red Blood Cell", "Complete Blood Count"),
("HGB", "Hemoglobin", "Complete Blood Count"),
("PLT", "Platelet Count", "Complete Blood Count"),
("ESR", "ESR 1 Hour", "Complete Blood Count"),
# 肝功能
("ALT", "Alanine Aminotransferase", "Liver Function"),
("AST", "Aspartate Aminotransferase", "Liver Function"),
("GGT", "Gamma GT", "Liver Function"),
("TBIL", "Total Bilirubin", "Liver Function"),
("ALB", "Albumin", "Liver Function"),
# 肾功能
("BUN", "Blood Urea Nitrogen", "Kidney Function"),
("CREA", "Creatinine", "Kidney Function"),
("UA", "Uric Acid", "Kidney Function"),
# 血脂
("TC", "Total Cholesterol", "Lipid Panel"),
("TG", "Triglyceride", "Lipid Panel"),
("HDL", "HDL Cholesterol", "Lipid Panel"),
("LDL", "LDL Cholesterol", "Lipid Panel"),
# 电解质
("NA", "Sodium", "Electrolytes"),
("K", "Potassium", "Electrolytes"),
("CL", "Chloride", "Electrolytes"),
("CA", "Calcium", "Electrolytes"),
# 血糖
("FPG", "Fasting Glucose", "Glucose"),
("HBA1C", "HbA1c", "Glucose"),
# 甲状腺
("TSH", "TSH", "Thyroid"),
("FT3", "Free T3", "Thyroid"),
("FT4", "Free T4", "Thyroid"),
# 激素
("E2", "Estradiol", "Hormone"),
("FSH", "FSH", "Hormone"),
("LH", "LH", "Hormone"),
("CORTISOL", "Cortisol", "Hormone"),
# 肿瘤标志物
("AFP", "Alpha Fetoprotein", "Tumor Markers"),
("CEA", "CEA", "Tumor Markers"),
("CA125", "CA125", "Tumor Markers"),
("PSA", "PSA", "Tumor Markers"),
# 凝血
("PT", "Prothrombin Time", "Coagulation"),
("APTT", "APTT", "Coagulation"),
("FIB", "Fibrinogen", "Coagulation"),
# 传染病
("HBSAG", "HBsAg", "Infectious Disease"),
("HIV", "HIV", "Infectious Disease"),
# 免疫功能
("IGG", "IgG", "Immune Function"),
("C3", "Complement C3", "Immune Function"),
("CRP", "CRP", "Immune Function"),
# 骨代谢
("OSTE", "Osteocalcin", "Bone Metabolism"),
("PTH", "PTH", "Bone Metabolism"),
# 重金属
("PB", "Lead", "Heavy Metals"),
("HG", "Mercury", "Heavy Metals"),
# 维生素
("VITB12", "Vitamin B12", "Vitamin"),
("FOLATE", "Folate", "Vitamin"),
# 同型半胱氨酸
("HCY", "Homocysteine", "Homocysteine"),
# 血型
("ABO", "ABO Blood Group", "Blood Type"),
]
passed = 0
failed = 0
for abb, project, expected in test_cases:
result = classify_abb_module(abb, project, api_key=None)
if result == expected:
passed += 1
else:
failed += 1
print(f" [FAIL] ABB={abb}, project={project}")
print(f" 期望: {expected}, 实际: {result}")
print(f"\n 结果: {passed} 通过, {failed} 失败 / 共 {len(test_cases)}")
return failed == 0
# ============================================================
# 测试2: classify_abb_module - 中文关键词匹配
# ============================================================
def test_chinese_keyword_matching():
"""测试中文关键词能否正确匹配模块"""
print("\n" + "=" * 70)
print("[测试2] 中文关键词匹配")
print("=" * 70)
# 用不在ABB映射中的假ABB强制走keyword匹配
test_cases = [
# (abb, project_name_cn, expected_module)
# 尿液
("X001", "尿液分析", "Urine Detection"),
("X002", "尿检常规", "Urine Detection"),
("X003", "隐血试验", "Urine Detection"),
("X004", "酮体检测", "Urine Detection"),
# 血常规
("X010", "红细胞计数", "Complete Blood Count"),
("X011", "白细胞分类", "Complete Blood Count"),
("X012", "血红蛋白测定", "Complete Blood Count"),
("X013", "血小板计数", "Complete Blood Count"),
("X014", "中性粒细胞百分比", "Complete Blood Count"),
("X015", "嗜酸性粒细胞", "Complete Blood Count"),
("X016", "单核细胞计数", "Complete Blood Count"),
# 肝功能
("X020", "肝功能全套", "Liver Function"),
("X021", "总蛋白测定", "Liver Function"),
("X022", "白蛋白测定", "Liver Function"),
("X023", "胆红素测定", "Liver Function"),
("X024", "转氨酶检测", "Liver Function"),
("X025", "谷氨酰转肽酶", "Liver Function"),
# 肾功能
("X030", "肾功能检测", "Kidney Function"),
("X031", "血清肌酐", "Kidney Function"),
("X032", "尿素氮测定", "Kidney Function"),
("X033", "尿酸检测", "Kidney Function"),
# 血脂
("X040", "总胆固醇", "Lipid Panel"),
("X041", "甘油三酯测定", "Lipid Panel"),
("X042", "高密度脂蛋白", "Lipid Panel"),
("X043", "血脂四项", "Lipid Panel"),
# 血糖
("X050", "空腹血糖测定", "Glucose"),
("X051", "糖化血红蛋白检测", "Glucose"),
("X052", "随机血糖", "Glucose"),
# 甲状腺
("X060", "甲状腺功能", "Thyroid"),
("X061", "促甲状腺激素", "Thyroid"),
# 激素
("X070", "雌二醇测定", "Hormone"),
("X071", "孕酮检测", "Hormone"),
("X072", "睾酮水平", "Hormone"),
("X073", "皮质醇测定", "Hormone"),
("X074", "催乳素检测", "Hormone"),
("X075", "荷尔蒙全套", "Hormone"),
("X076", "促卵泡生成素", "Hormone"),
("X077", "促黄体生成素", "Hormone"),
("X078", "脱氢表雄酮硫酸盐", "Hormone"),
("X079", "胰岛素样生长因子", "Hormone"),
("X080", "抗缪勒管激素", "Hormone"),
# 肿瘤标志物
("X090", "肿瘤标志物全套", "Tumor Markers"),
("X091", "甲胎蛋白检测", "Tumor Markers"),
("X092", "癌胚抗原测定", "Tumor Markers"),
("X093", "铁蛋白检测", "Tumor Markers"),
("X094", "糖类抗原125", "Tumor Markers"),
("X095", "前列腺特异性抗原", "Tumor Markers"),
("X096", "鳞状细胞癌抗原", "Tumor Markers"),
("X097", "神经元特异性烯醇化酶", "Tumor Markers"),
# 凝血
("X100", "凝血功能检测", "Coagulation"),
("X101", "纤维蛋白原测定", "Coagulation"),
# 传染病
("X110", "乙肝五项", "Infectious Disease"),
("X111", "丙肝抗体", "Infectious Disease"),
("X112", "梅毒筛查", "Infectious Disease"),
("X113", "传染病四项", "Infectious Disease"),
# 免疫功能
("X120", "免疫球蛋白测定", "Immune Function"),
("X121", "补体C3检测", "Immune Function"),
("X122", "c反应蛋白测定", "Immune Function"),
("X123", "抗核抗体检测", "Immune Function"),
("X124", "类风湿因子测定", "Immune Function"),
("X125", "红细胞沉降速率", "Immune Function"),
# 骨代谢
("X130", "骨代谢标志物", "Bone Metabolism"),
("X131", "骨钙素检测", "Bone Metabolism"),
("X132", "甲状旁腺激素", "Bone Metabolism"),
("X133", "25-羟维生素d检测", "Bone Metabolism"),
# 重金属
("X140", "微量元素检测", "Heavy Metals"),
("X141", "重金属筛查", "Heavy Metals"),
# 同型半胱氨酸
("X150", "同型半胱氨酸检测", "Homocysteine"),
# 血型
("X160", "ABO血型鉴定", "Blood Type"),
# 电解质
("X170", "电解质全套", "Electrolytes"),
("X171", "血清钾测定", "Electrolytes"),
("X172", "血清钠检测", "Electrolytes"),
("X173", "血清钙测定", "Electrolytes"),
]
passed = 0
failed = 0
for abb, project, expected in test_cases:
result = classify_abb_module(abb, project, api_key=None)
if result == expected:
passed += 1
else:
failed += 1
print(f" [FAIL] project={project}")
print(f" 期望: {expected}, 实际: {result}")
print(f"\n 结果: {passed} 通过, {failed} 失败 / 共 {len(test_cases)}")
return failed == 0
# ============================================================
# 测试3: parse_medical_data_v2 - OCR文本解析
# ============================================================
def test_parse_ocr_text():
"""测试OCR文本解析能否正确提取检测项"""
print("\n" + "=" * 70)
print("[测试3] OCR文本解析 (parse_medical_data_v2)")
print("=" * 70)
# 模拟典型的百度OCR提取文本英文报告格式
sample_ocr_text = """Page 1
Patient Name: MR. TEST PATIENT
Sex : Male Age : 45Y
Collected Date/Time: 20 Jan 2025
Complete Blood Count
Total WBC............... 6.50 *10^3/mm3 (4.0-10.0)
Red Blood Cell.......... 4.69 *10^6/mm3 (4.5-5.5)
Hemoglobin(Hb)......... 14.2 g/dL (13.0-17.0)
Hematocrit(HCT)........ 41.3 % (40-54)
MCV.................... 88.1 fL (80-100)
MCH.................... 30.3 pg (27-34)
MCHC................... 34.4 g/dL (32-36)
Platelet Count......... 230 *10^3/mm3 (150-400)
Neutrophil............. 62.3 % (40-70)
Lymphocyte............. 28.5 % (20-40)
Monocyte............... 6.2 % (2-8)
Eosinophil............. 2.5 % (1-6)
Basophil............... 0.5 % (0-1)
ESR 1 Hour............. 8 mm/hr (0-15)
Liver Function
ALT(Alanine Transaminase)...... 25 U/L (0-41)
AST(Aspartate Transaminase).... 22 U/L (0-40)
GGT( Gamma GT)................. 30 U/L (8-61)
ALP(Alkaline Phosphatase)...... 70 U/L (40-130)
Total Bilirubin................ 0.8 mg/dL (0.1-1.2)
Direct Bilirubin............... 0.2 mg/dL (0-0.3)
Total Protein.................. 7.2 g/dL (6.6-8.3)
Albumin........................ 4.5 g/dL (3.5-5.2)
Globulin....................... 2.7 g/dL (2.0-3.5)
Kidney Function
BUN............................ 15 mg/dL (6-20)
Creatinine..................... 0.95 mg/dL (0.67-1.17)
Uric Acid...................... 5.8 mg/dL (3.4-7.0)
eGFR........................... 92 mL/min (>90)
Lipid Profile
Total Cholesterol.............. 195 mg/dL (<200)
Triglyceride................... 120 mg/dL (<150)
HDL-Cholesterol................ 55 mg/dL (>40)
LDL-Cholesterol(Direct)........ 118 mg/dL (<100)
Glucose(Fasting)............... 95 mg/dL (74-100)
HbA1c.......................... 5.7 % (4.0-5.6)
Thyroid Function
TSH............................ 2.15 mIU/L (0.27-4.2)
Free T3........................ 3.2 pg/mL (2.0-4.4)
Free T4........................ 1.25 ng/dL (0.93-1.7)
Hormones
Estradiol(E2).................. 28.5 pg/mL (11.3-43.2)
Testosterone................... 450 ng/dL (249-836)
Cortisol....................... 12.5 ug/dL (6.2-19.4)
FSH............................ 5.85 mIU/mL (1.5-12.4)
LH(Luteinizing Hormone)....... 4.2 mIU/mL (1.7-8.6)
Prolactin...................... 8.5 ng/mL (4.0-15.2)
DHEA-Sulphate.................. 280 ug/dL (88.9-427)
IGF-1.......................... 165 ng/mL (101-267)
Tumor Markers
AFP(Alpha Fetoprotein)......... 3.2 ng/mL (0-7)
CEA(Carcinoembryonic Antigen).. 2.1 ng/mL (0-5)
Total PSA...................... 0.8 ng/mL (0-4)
CA125.......................... 12.5 U/mL (0-35)
Coagulation
Prothrombin Time(PT)........... 12.5 sec (10-14)
APTT........................... 28.3 sec (25-35)
Thrombin Time(TT).............. 16.2 sec (14-21)
Fibrinogen..................... 2.8 g/L (2.0-4.0)
INR............................ 0.93 (0.8-1.2)
Infectious Disease
HBsAg(Hepatitis B Surface Antigen)... Negative
HBsAb(Hepatitis B Surface Antibody).. Positive
HCV Ab (Hepatitis C Antibody)........ Non Reactive
HIV-1/HIV-2 Antibody................. Non Reactive
RPR (Rapid Plasma Reagin)............ Non Reactive
Electrolytes
Sodium......................... 140 mmol/L (136-145)
Potassium...................... 4.2 mmol/L (3.5-5.1)
Chloride....................... 103 mmol/L (98-107)
Calcium........................ 9.5 mg/dL (8.6-10.2)
Immune Function
Immunoglobulin G(IgG).......... 1050 mg/dL (700-1600)
Immunoglobulin A(IgA).......... 220 mg/dL (70-400)
Immunoglobulin M(IgM).......... 95 mg/dL (40-230)
Complement C3(B1C)............. 110 mg/dL (90-180)
Complement C4.................. 28 mg/dL (10-40)
C-Reactive Protein(High Sens).. 0.5 mg/L (<3)
Bone Metabolism
N-mid Osteocalcin.............. 15.2 ng/mL (14-46)
PTH(Intact).................... 35 pg/mL (15-65)
Vitamin D(25-OH Vitamin D Total) 32 ng/mL (30-100)
Blood Type
ABO Group...................... A
Rh Group....................... Positive
Homocysteine................... 10.5 umol/L (5-15)
Vitamin B12.................... 450 pg/mL (197-771)
Folate......................... 12.3 ng/mL (>3.0)
"""
items = parse_medical_data_v2(sample_ocr_text, "test_sample.pdf")
items = clean_extracted_data_v2(items)
print(f" 解析出 {len(items)} 个检测项")
# 期望至少能解析出的关键ABB
expected_abbs = {
'WBC', 'RBC', 'Hb', 'HCT', 'MCV', 'MCH', 'MCHC', 'PLT',
'NEUT', 'LYMPH', 'MONO', 'EOS', 'BAS', 'ESR',
'ALT', 'AST', 'GGT', 'ALP', 'TBil', 'DBil', 'TP', 'ALB', 'GLB',
'BUN', 'Scr', 'UA', 'eGFR',
'TC', 'TG', 'HDL', 'LDL',
'FBS', 'HbA1C',
'TSH', 'FT3', 'FT4',
'E2', 'T', 'COR', 'FSH', 'LH', 'PRL', 'DHEAS', 'IGF-1',
'AFP', 'CEA', 'TPSA', 'CA125',
'PT', 'APTT', 'TT', 'FIB', 'INR',
'HBsAg', 'HBsAb', 'HCV', 'HIV', 'TRUST',
'Na', 'K', 'Cl', 'Ca',
'IgG', 'IgA', 'IgM', 'C3', 'C4', 'hs-CRP',
'OST', 'PTH', '25-OH-VD2+D3',
'ABO', 'Rh',
'Hcy',
'VitB12', 'Folate',
}
found_abbs = {item['abb'] for item in items}
matched = expected_abbs & found_abbs
missing = expected_abbs - found_abbs
extra = found_abbs - expected_abbs
print(f" 期望 {len(expected_abbs)} 个ABB")
print(f" 匹配 {len(matched)}")
if missing:
print(f" [WARN] 未匹配 {len(missing)} 个: {sorted(missing)}")
if extra:
print(f" [INFO] 额外识别 {len(extra)} 个: {sorted(extra)}")
# 打印所有解析出的项目详情
print(f"\n {'ABB':<15} {'结果':<12} {'标记':<4} {'单位':<20} {'参考范围'}")
print(" " + "-" * 70)
for item in sorted(items, key=lambda x: x['abb']):
abb = item['abb']
result = item.get('result', '')[:10]
point = item.get('point', '')
unit = item.get('unit', '')[:18]
ref = item.get('reference', '')[:25]
marker = "" if abb in expected_abbs else " "
print(f" {marker} {abb:<13} {result:<12} {point:<4} {unit:<20} {ref}")
coverage = len(matched) / len(expected_abbs) * 100 if expected_abbs else 0
print(f"\n 覆盖率: {coverage:.1f}% ({len(matched)}/{len(expected_abbs)})")
return coverage >= 70 # 至少70%覆盖率算通过
# ============================================================
# 测试4: 分类 + 模板匹配联合测试
# ============================================================
def test_classify_with_template():
"""测试提取数据经过分类后能否正确归入模块"""
print("\n" + "=" * 70)
print("[测试4] 分类 → 模板匹配联合测试")
print("=" * 70)
# 加载真实配置
config_path = os.path.join(os.path.dirname(__file__), "abb_mapping_config.json")
if not os.path.exists(config_path):
print(" [SKIP] 配置文件不存在")
return True
with open(config_path, 'r', encoding='utf-8') as f:
config = json.load(f)
# 模拟提取的数据混合英文ABB和中文项目名
mock_items = [
{"abb": "WBC", "project": "White Blood Cell", "result": "6.5", "point": "", "unit": "*10^3/mm3", "reference": "(4.0-10.0)", "source": "test.pdf"},
{"abb": "ALT", "project": "Alanine Aminotransferase", "result": "25", "point": "", "unit": "U/L", "reference": "(0-41)", "source": "test.pdf"},
{"abb": "TC", "project": "Total Cholesterol", "result": "195", "point": "", "unit": "mg/dL", "reference": "(<200)", "source": "test.pdf"},
{"abb": "TSH", "project": "TSH", "result": "2.15", "point": "", "unit": "mIU/L", "reference": "(0.27-4.2)", "source": "test.pdf"},
{"abb": "AFP", "project": "Alpha Fetoprotein", "result": "3.2", "point": "", "unit": "ng/mL", "reference": "(0-7)", "source": "test.pdf"},
{"abb": "E2", "project": "Estradiol", "result": "28.5", "point": "", "unit": "pg/mL", "reference": "", "source": "test.pdf"},
{"abb": "PT", "project": "Prothrombin Time", "result": "12.5", "point": "", "unit": "sec", "reference": "(10-14)", "source": "test.pdf"},
{"abb": "HBsAg", "project": "HBsAg", "result": "Negative", "point": "", "unit": "", "reference": "", "source": "test.pdf"},
{"abb": "Na", "project": "Sodium", "result": "140", "point": "", "unit": "mmol/L", "reference": "(136-145)", "source": "test.pdf"},
{"abb": "Hcy", "project": "Homocysteine", "result": "10.5", "point": "", "unit": "umol/L", "reference": "(5-15)", "source": "test.pdf"},
]
matched = match_with_template(mock_items, config)
print(f"\n 模板匹配结果: {len(matched)} 个项目")
# 检查每个项目分类
for abb in ['WBC', 'ALT', 'TC', 'TSH', 'AFP', 'E2', 'PT', 'HBsAg', 'Na', 'Hcy']:
data = matched.get(abb, {})
project = data.get('project', '?')
result = data.get('result', '?')
module = classify_abb_module(abb, project, api_key=None)
print(f" {abb:<8} result={result:<10} → [{module}]")
return len(matched) >= 8
# ============================================================
# 测试5: 边界情况 - 关键词冲突
# ============================================================
def test_keyword_conflicts():
"""测试潜在的关键词冲突场景"""
print("\n" + "=" * 70)
print("[测试5] 关键词冲突/边界测试")
print("=" * 70)
test_cases = [
# 长关键词应优先于短关键词
("X200", "红细胞沉降速率测定", "Immune Function"), # 不应匹配到 CBC 的 '红细胞'
("X201", "红细胞计数", "Complete Blood Count"), # 应正常匹配 '红细胞'
# 白蛋白 vs 白细胞
("X202", "血清白蛋白", "Liver Function"), # '白蛋白' → Liver
("X203", "白细胞分类计数", "Complete Blood Count"), # '白细胞' → CBC
# 甲状腺 vs 甲状旁腺
("X204", "甲状旁腺激素检测", "Bone Metabolism"), # '甲状旁腺' → Bone
("X205", "甲状腺功能五项", "Thyroid"), # '甲状腺' → Thyroid
# 维生素D归属
("X206", "25-羟维生素d总量", "Bone Metabolism"), # '维生素d' → Bone (非Vitamin)
# 尿酸 不应匹配 尿液
("X207", "血清尿酸", "Kidney Function"), # '尿酸' → Kidney
# 胆固醇 不应匹配 胆红素
("X208", "总胆固醇", "Lipid Panel"), # '胆固醇' → Lipid
("X209", "总胆红素", "Liver Function"), # '胆红素' → Liver
# 免疫缺陷病毒
("X210", "人类免疫缺陷病毒抗体", "Infectious Disease"), # 不应匹配 '免疫球蛋白'
]
passed = 0
failed = 0
for abb, project, expected in test_cases:
result = classify_abb_module(abb, project, api_key=None)
status = "OK" if result == expected else "FAIL"
if result == expected:
passed += 1
else:
failed += 1
icon = "" if status == "OK" else ""
print(f" {icon} {project:<25} 期望: {expected:<20} 实际: {result}")
print(f"\n 结果: {passed} 通过, {failed} 失败 / 共 {len(test_cases)}")
return failed == 0
# ============================================================
# 主函数
# ============================================================
def main():
print("=" * 70)
print(" 医疗数据提取逻辑测试")
print(" (不调用OCR/DeepSeek API纯本地离线测试)")
print("=" * 70)
results = {}
results["ABB硬编码映射"] = test_abb_mapping()
results["中文关键词匹配"] = test_chinese_keyword_matching()
results["OCR文本解析"] = test_parse_ocr_text()
results["分类+模板匹配"] = test_classify_with_template()
results["关键词冲突检测"] = test_keyword_conflicts()
# 汇总
print("\n" + "=" * 70)
print(" 测试汇总")
print("=" * 70)
all_pass = True
for name, passed in results.items():
icon = "✓ PASS" if passed else "✗ FAIL"
print(f" {icon} {name}")
if not passed:
all_pass = False
print("=" * 70)
if all_pass:
print(" 所有测试通过!")
else:
print(" 存在失败项,请检查上方详情")
print("=" * 70)
return 0 if all_pass else 1
if __name__ == "__main__":
sys.exit(main())

View File

@@ -0,0 +1,187 @@
# -*- coding: utf-8 -*-
"""
实际OCR提取 + 分类测试
流程:
1. 调用百度OCR提取 医疗报告智能体 文件夹下的PDF
2. 用 parse_medical_data_v2 解析OCR文本
3. 用 classify_abb_module 对每个项目分类
4. 输出分类结果统计
"""
import sys
import os
import io
import json
import time
# 修复 Windows 终端 UTF-8
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8", errors="replace")
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from pathlib import Path
from dotenv import load_dotenv
load_dotenv(Path(__file__).parent / ".env")
from parse_medical_v2 import parse_medical_data_v2, clean_extracted_data_v2
from extract_and_fill_report import (
extract_pdf_text, classify_abb_module, match_with_template,
extract_patient_info
)
def main():
pdf_dir = Path(r"c:\Users\UI\Desktop\医疗报告\医疗报告智能体")
config_path = Path(__file__).parent / "abb_mapping_config.json"
pdf_files = list(pdf_dir.glob("*.pdf"))
if not pdf_files:
print("[ERROR] 没有找到PDF文件")
return
print("=" * 70)
print(" 百度OCR提取 + 分类测试")
print("=" * 70)
# ========== 步骤1: OCR提取 ==========
all_items = []
for pdf_file in pdf_files:
print(f"\n📄 OCR提取: {pdf_file.name} ({pdf_file.stat().st_size / 1024:.0f} KB)")
start = time.time()
text = extract_pdf_text(str(pdf_file))
elapsed = time.time() - start
lines = [l for l in text.split('\n') if l.strip()]
print(f" ✓ OCR完成 | 耗时: {elapsed:.1f}s | 行数: {len(lines)}")
# 保存OCR原文用于调试
ocr_output = Path(__file__).parent / "test_ocr_raw_text.txt"
with open(ocr_output, 'w', encoding='utf-8') as f:
f.write(text)
print(f" ✓ OCR原文已保存: {ocr_output.name}")
# 提取患者信息
patient_info = extract_patient_info(text)
print(f"\n 患者信息:")
print(f" 姓名: {patient_info.get('name', '未提取')}")
print(f" 性别: {patient_info.get('gender', '未提取')}")
print(f" 年龄: {patient_info.get('age', '未提取')}")
# 解析检测项
items = parse_medical_data_v2(text, pdf_file.name)
items = clean_extracted_data_v2(items)
print(f"\n ✓ 解析出 {len(items)} 个检测项")
all_items.extend(items)
if not all_items:
print("\n[WARN] 未提取到任何检测项OCR结果可能为非血液检测报告")
print(" 请检查 test_ocr_raw_text.txt 查看OCR原文")
return
# ========== 步骤2: 分类测试 ==========
print("\n" + "=" * 70)
print(f" 分类测试 ({len(all_items)} 个检测项)")
print("=" * 70)
# 按模块分组
by_module = {}
unclassified = []
for item in all_items:
abb = item.get('abb', '')
project = item.get('project', abb)
result = item.get('result', '')
module = classify_abb_module(abb, project, api_key=None)
item['classified_module'] = module
if module == 'Other':
unclassified.append(item)
else:
if module not in by_module:
by_module[module] = []
by_module[module].append(item)
# 打印每个模块的项目
print(f"\n 分类成功: {len(all_items) - len(unclassified)}")
print(f" 未分类(Other): {len(unclassified)}")
# 按模块显示
print("\n" + "-" * 70)
for module, items in sorted(by_module.items()):
print(f"\n 📁 [{module}] ({len(items)} 项)")
for item in items:
abb = item.get('abb', '?')
project = item.get('project', '')[:30]
result = item.get('result', '')[:15]
point = item.get('point', '')
print(f" {abb:<15} {project:<32} = {result:<15} {point}")
if unclassified:
print(f"\n ⚠️ [Other - 未分类] ({len(unclassified)} 项)")
for item in unclassified:
abb = item.get('abb', '?')
project = item.get('project', '')[:40]
result = item.get('result', '')[:15]
print(f" {abb:<15} {project:<42} = {result}")
# ========== 步骤3: 模板匹配测试 ==========
print("\n" + "=" * 70)
print(" 模板匹配测试")
print("=" * 70)
with open(config_path, 'r', encoding='utf-8') as f:
config = json.load(f)
matched = match_with_template(all_items, config)
print(f" 模板匹配: {len(matched)} 个项目")
# 统计
module_count = {}
for abb, data in matched.items():
module = data.get('module', '')
if not module:
module = classify_abb_module(abb, data.get('project', abb), api_key=None)
if module not in module_count:
module_count[module] = 0
module_count[module] += 1
print("\n 模块分布:")
for module, count in sorted(module_count.items(), key=lambda x: -x[1]):
print(f" {module:<30} {count}")
# ========== 汇总 ==========
print("\n" + "=" * 70)
print(" 汇总")
print("=" * 70)
total = len(all_items)
classified = total - len(unclassified)
rate = classified / total * 100 if total else 0
print(f" 总提取项: {total}")
print(f" 分类成功: {classified} ({rate:.1f}%)")
print(f" 未分类: {len(unclassified)}")
print(f" 模块数: {len(by_module)}")
print("=" * 70)
# 保存结果
output_path = Path(__file__).parent / "test_ocr_classify_result.json"
save_data = {
"total_items": total,
"classified": classified,
"unclassified_count": len(unclassified),
"modules": {m: len(items) for m, items in by_module.items()},
"items": [{
"abb": item.get("abb", ""),
"project": item.get("project", ""),
"result": item.get("result", ""),
"point": item.get("point", ""),
"unit": item.get("unit", ""),
"module": item.get("classified_module", "Other")
} for item in all_items]
}
with open(output_path, 'w', encoding='utf-8') as f:
json.dump(save_data, f, ensure_ascii=False, indent=2)
print(f"\n 结果已保存: {output_path.name}")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,886 @@
{
"total_items": 108,
"classified": 108,
"unclassified_count": 0,
"modules": {
"Liver Function": 17,
"Urine Detection": 1,
"Complete Blood Count": 24,
"Kidney Function": 2,
"Lipid Panel": 6,
"Glucose": 3,
"Immune Function": 11,
"Homocysteine": 1,
"Thyroid": 8,
"Infectious Disease": 6,
"Bone Metabolism": 5,
"Vitamin": 10,
"Tumor Markers": 14
},
"items": [
{
"abb": "ALB",
"project": "白蛋白",
"result": "10",
"point": "",
"unit": "mg/L",
"module": "Liver Function"
},
{
"abb": "pH",
"project": "酸碱度",
"result": "6.5",
"point": "",
"unit": "",
"module": "Urine Detection"
},
{
"abb": "WBC",
"project": "白细胞计数(WBC)",
"result": "5.1",
"point": "",
"unit": "x10^9/L",
"module": "Complete Blood Count"
},
{
"abb": "NEUT%",
"project": "中性粒细胞百分率(NEUT%)",
"result": "43.9",
"point": "",
"unit": "%",
"module": "Complete Blood Count"
},
{
"abb": "LYMPH%",
"project": "淋巴细胞百分率(LYMPH%)",
"result": "45.7",
"point": "",
"unit": "%",
"module": "Complete Blood Count"
},
{
"abb": "MONO%",
"project": "单核细胞百分率(MONO%)",
"result": "7.5",
"point": "",
"unit": "%",
"module": "Complete Blood Count"
},
{
"abb": "EOS%",
"project": "嗜酸性粒细胞百分率(EO%)",
"result": "2.3",
"point": "",
"unit": "%",
"module": "Complete Blood Count"
},
{
"abb": "BAS%",
"project": "嗜碱性粒细胞百分率(BASO%)",
"result": "0.6",
"point": "",
"unit": "%",
"module": "Complete Blood Count"
},
{
"abb": "NEUT",
"project": "中性粒细胞数(NEUT#)",
"result": "2.3",
"point": "",
"unit": "x10^9/L",
"module": "Complete Blood Count"
},
{
"abb": "LYMPH",
"project": "淋巴细胞数(LYMPH#)",
"result": "2.4",
"point": "",
"unit": "x10^9/L",
"module": "Complete Blood Count"
},
{
"abb": "MONO",
"project": "单核细胞数(MONO#)",
"result": "0.39",
"point": "",
"unit": "x10^9/L",
"module": "Complete Blood Count"
},
{
"abb": "EOS",
"project": "嗜酸性粒细胞数(EO#)",
"result": "0.12",
"point": "",
"unit": "x10^9/L",
"module": "Complete Blood Count"
},
{
"abb": "BAS",
"project": "嗜碱性粒细胞数(BASO#)",
"result": "0.03",
"point": "",
"unit": "x10^9/L",
"module": "Complete Blood Count"
},
{
"abb": "RBC",
"project": "红细胞计数(RBC)",
"result": "3.77",
"point": "↓",
"unit": "x10^12/L",
"module": "Complete Blood Count"
},
{
"abb": "Hb",
"project": "血红蛋白量(HGB)",
"result": "123",
"point": "↓",
"unit": "g/L",
"module": "Complete Blood Count"
},
{
"abb": "HCT",
"project": "红细胞比积(HCT)",
"result": "38",
"point": "↓",
"unit": "%",
"module": "Complete Blood Count"
},
{
"abb": "MCV",
"project": "平均红细胞体积(MCV)",
"result": "100",
"point": "",
"unit": "fL",
"module": "Complete Blood Count"
},
{
"abb": "MCH",
"project": "平均红细胞血红蛋白量(MCH)",
"result": "33",
"point": "",
"unit": "pg",
"module": "Complete Blood Count"
},
{
"abb": "MCHC",
"project": "平均红细胞血红蛋白浓度(MCHC)",
"result": "326",
"point": "",
"unit": "g/L",
"module": "Complete Blood Count"
},
{
"abb": "RDW-SD",
"project": "红细胞分布宽度-标准差(RDW-SD)",
"result": "45",
"point": "",
"unit": "fL",
"module": "Complete Blood Count"
},
{
"abb": "RDW",
"project": "红细胞分布宽度-变异系数(RDW-CV)",
"result": "12.0",
"point": "",
"unit": "%",
"module": "Complete Blood Count"
},
{
"abb": "PLT",
"project": "血小板计数(PLT)",
"result": "163",
"point": "",
"unit": "x10^9/L",
"module": "Complete Blood Count"
},
{
"abb": "PCT",
"project": "血小板比积(PCT)",
"result": "0.18",
"point": "",
"unit": "%",
"module": "Complete Blood Count"
},
{
"abb": "MPV",
"project": "平均血小板体积(MPV)",
"result": "10.9",
"point": "",
"unit": "fL",
"module": "Complete Blood Count"
},
{
"abb": "PDW",
"project": "血小板分布宽度(PDW)",
"result": "16.0",
"point": "",
"unit": "fL",
"module": "Complete Blood Count"
},
{
"abb": "P-LCR",
"project": "大型血小板比率(P-LCR)",
"result": "31",
"point": "",
"unit": "%",
"module": "Complete Blood Count"
},
{
"abb": "TBil",
"project": "总胆红素",
"result": "8.3",
"point": "",
"unit": "umol/L",
"module": "Liver Function"
},
{
"abb": "DBil",
"project": "直接胆红素",
"result": "1.7",
"point": "",
"unit": "umol/L",
"module": "Liver Function"
},
{
"abb": "IBil",
"project": "间接胆红素",
"result": "6.6",
"point": "",
"unit": "umol/L",
"module": "Liver Function"
},
{
"abb": "TP",
"project": "总蛋白",
"result": "72.4",
"point": "",
"unit": "g/L",
"module": "Liver Function"
},
{
"abb": "ALB",
"project": "白蛋白",
"result": "44.8",
"point": "",
"unit": "g/L",
"module": "Liver Function"
},
{
"abb": "GLB",
"project": "球蛋白",
"result": "27.6",
"point": "",
"unit": "g/L",
"module": "Liver Function"
},
{
"abb": "A/G",
"project": "白球比值",
"result": "1.6",
"point": "",
"unit": "",
"module": "Liver Function"
},
{
"abb": "CHE",
"project": "胆碱酯酶",
"result": "290",
"point": "",
"unit": "U/L",
"module": "Liver Function"
},
{
"abb": "ALT",
"project": "谷丙转氨酶",
"result": "16",
"point": "",
"unit": "U/L",
"module": "Liver Function"
},
{
"abb": "AST",
"project": "谷草转氨酶",
"result": "25",
"point": "",
"unit": "U/L",
"module": "Liver Function"
},
{
"abb": "GGT",
"project": "γ-谷氨酰基转移酶",
"result": "22",
"point": "",
"unit": "U/L",
"module": "Liver Function"
},
{
"abb": "ALP",
"project": "碱性磷酸酶",
"result": "61",
"point": "",
"unit": "U/L",
"module": "Liver Function"
},
{
"abb": "Tf",
"project": "转铁蛋白",
"result": "2.43",
"point": "",
"unit": "g/L",
"module": "Liver Function"
},
{
"abb": "Tf",
"project": "转铁蛋白",
"result": "43.57",
"point": "",
"unit": "mg/L",
"module": "Liver Function"
},
{
"abb": "CysC",
"project": "胱抑素C",
"result": "0.90",
"point": "",
"unit": "mg/L",
"module": "Kidney Function"
},
{
"abb": "β2-MG",
"project": "血清β2微球蛋白",
"result": "1.8",
"point": "",
"unit": "mg/L",
"module": "Kidney Function"
},
{
"abb": "ALB",
"project": "白蛋白",
"result": "1.0",
"point": "",
"unit": "mg/L",
"module": "Liver Function"
},
{
"abb": "GLB",
"project": "球蛋白",
"result": "70",
"point": "",
"unit": "ug/L",
"module": "Liver Function"
},
{
"abb": "TG",
"project": "甘油三酯",
"result": "1.37",
"point": "",
"unit": "mmol/L",
"module": "Lipid Panel"
},
{
"abb": "TC",
"project": "总胆固醇",
"result": "4.67",
"point": "",
"unit": "mmol/L",
"module": "Lipid Panel"
},
{
"abb": "HDL",
"project": "高密度脂蛋白胆固醇",
"result": "1.52",
"point": "",
"unit": "mmol/L",
"module": "Lipid Panel"
},
{
"abb": "LDL",
"project": "低密度脂蛋白胆固醇",
"result": "2.50",
"point": "",
"unit": "mmol/L",
"module": "Lipid Panel"
},
{
"abb": "FFA",
"project": "游离脂肪酸",
"result": "0.66",
"point": "",
"unit": "mmol/L",
"module": "Lipid Panel"
},
{
"abb": "INS",
"project": "胰岛素(空腹)",
"result": "8.3",
"point": "",
"unit": "μU/ml",
"module": "Glucose"
},
{
"abb": "FBS",
"project": "葡萄糖(空腹)",
"result": "5.41",
"point": "",
"unit": "mmol/L",
"module": "Glucose"
},
{
"abb": "HbA1C",
"project": "糖化血红蛋白",
"result": "5.6",
"point": "",
"unit": "%",
"module": "Glucose"
},
{
"abb": "CK",
"project": "肌酸激酶",
"result": "136",
"point": "",
"unit": "U/L",
"module": "Immune Function"
},
{
"abb": "CK-MB",
"project": "肌酸激酶同工酶MB",
"result": "11",
"point": "",
"unit": "U/L",
"module": "Immune Function"
},
{
"abb": "hs-CRP",
"project": "超敏C反应蛋白",
"result": "0.5",
"point": "",
"unit": "mg/L",
"module": "Immune Function"
},
{
"abb": "Hcy",
"project": "同型半胱氨酸",
"result": "9.7",
"point": "",
"unit": "umol/L",
"module": "Homocysteine"
},
{
"abb": "Lp(a)",
"project": "脂蛋白(a)",
"result": "26",
"point": "",
"unit": "mg/L",
"module": "Lipid Panel"
},
{
"abb": "Tg",
"project": "甲状腺球蛋白",
"result": "8.8",
"point": "",
"unit": "ng/ml",
"module": "Thyroid"
},
{
"abb": "T3",
"project": "三碘甲状腺原氨酸T3",
"result": "1.31",
"point": "",
"unit": "nmol/L",
"module": "Thyroid"
},
{
"abb": "T4",
"project": "甲状腺素T4",
"result": "99.0",
"point": "",
"unit": "nmol/L",
"module": "Thyroid"
},
{
"abb": "FT3",
"project": "游离三碘甲状腺原氨酸FT3",
"result": "4.35",
"point": "",
"unit": "pmol/L",
"module": "Thyroid"
},
{
"abb": "FT4",
"project": "游离甲状腺素FT4",
"result": "14.30",
"point": "",
"unit": "pmol/L",
"module": "Thyroid"
},
{
"abb": "TSH",
"project": "促甲状腺素TSH",
"result": "1.55",
"point": "",
"unit": "mIU/L",
"module": "Thyroid"
},
{
"abb": "TgAb",
"project": "抗甲状腺球蛋白抗体",
"result": "16.5",
"point": "",
"unit": "IU/ml",
"module": "Thyroid"
},
{
"abb": "TPO-Ab",
"project": "抗甲状腺过氧化物酶抗体",
"result": "13.1",
"point": "",
"unit": "IU/ml",
"module": "Thyroid"
},
{
"abb": "PGI",
"project": "胃蛋白酶原I",
"result": "98.4",
"point": "",
"unit": "ng/ml",
"module": "Immune Function"
},
{
"abb": "G-17",
"project": "胃泌素-17",
"result": "2.9",
"point": "",
"unit": "pmol/L",
"module": "Immune Function"
},
{
"abb": "PGII",
"project": "胃蛋白酶原Ⅱ",
"result": "11.1",
"point": "",
"unit": "ng/ml",
"module": "Immune Function"
},
{
"abb": "PGR",
"project": "胃蛋白酶原比值",
"result": "8.9",
"point": "",
"unit": "",
"module": "Immune Function"
},
{
"abb": "HBsAg",
"project": "乙肝表面抗原",
"result": "0.87",
"point": "",
"unit": "COI",
"module": "Infectious Disease"
},
{
"abb": "HBsAb",
"project": "乙肝表面抗体",
"result": "<2.00",
"point": "",
"unit": "IU/L",
"module": "Infectious Disease"
},
{
"abb": "HBeAg",
"project": "乙肝e抗原",
"result": "0.10",
"point": "",
"unit": "COI",
"module": "Infectious Disease"
},
{
"abb": "HBeAb",
"project": "乙肝e抗体",
"result": "1.40",
"point": "",
"unit": "COI",
"module": "Infectious Disease"
},
{
"abb": "HBcAb",
"project": "乙肝核心抗体",
"result": "0.01",
"point": "",
"unit": "COI",
"module": "Infectious Disease"
},
{
"abb": "HBcAb",
"project": "乙肝核心抗体",
"result": "阳性",
"point": "",
"unit": "",
"module": "Infectious Disease"
},
{
"abb": "CRP",
"project": "C反应蛋白",
"result": "0.5",
"point": "",
"unit": "mg/L",
"module": "Immune Function"
},
{
"abb": "ASO",
"project": "抗链球菌溶血素\"0\"",
"result": "32",
"point": "",
"unit": "IU/ml",
"module": "Immune Function"
},
{
"abb": "ANA",
"project": "抗核抗体",
"result": "0.9",
"point": "",
"unit": "AU/ml",
"module": "Immune Function"
},
{
"abb": "RF",
"project": "类风湿因子",
"result": "5",
"point": "",
"unit": "IU/ml",
"module": "Immune Function"
},
{
"abb": "PTH",
"project": "甲状旁腺素",
"result": "5.9",
"point": "",
"unit": "pmol/L",
"module": "Bone Metabolism"
},
{
"abb": "OST",
"project": "骨钙素",
"result": "15.4",
"point": "",
"unit": "ng/ml",
"module": "Bone Metabolism"
},
{
"abb": "VitB12",
"project": "维生素B12",
"result": "497",
"point": "",
"unit": "pmol/L",
"module": "Vitamin"
},
{
"abb": "Fer",
"project": "血清铁蛋白",
"result": "86",
"point": "",
"unit": "ng/ml",
"module": "Vitamin"
},
{
"abb": "Folate",
"project": "维生素B9(叶酸)血药浓度测定",
"result": "12.18",
"point": "",
"unit": "ng/ml",
"module": "Vitamin"
},
{
"abb": "25-OH-VD2+D3",
"project": "25-羟基维生素D血药浓度测定",
"result": "19.91",
"point": "↓",
"unit": "ng/ml",
"module": "Bone Metabolism"
},
{
"abb": "VitA",
"project": "维生素A血药浓度测定",
"result": "564.54",
"point": "",
"unit": "ng/ml",
"module": "Vitamin"
},
{
"abb": "VD3",
"project": "25-羟基维生素D3血药浓度测定",
"result": "19.63",
"point": "",
"unit": "ng/ml",
"module": "Bone Metabolism"
},
{
"abb": "VD2",
"project": "25-羟基维生素D2血药浓度测定",
"result": "0.28",
"point": "",
"unit": "ng/ml",
"module": "Bone Metabolism"
},
{
"abb": "VitE",
"project": "维生素E血药浓度测定",
"result": "9.29",
"point": "",
"unit": "ug/ml",
"module": "Vitamin"
},
{
"abb": "VitB2",
"project": "维生素B2血药浓度测定",
"result": "7.90",
"point": "",
"unit": "ng/ml",
"module": "Vitamin"
},
{
"abb": "VitB1",
"project": "维生素B1血药浓度测定",
"result": "1.67",
"point": "↓",
"unit": "ng/ml",
"module": "Vitamin"
},
{
"abb": "VitB5",
"project": "维生素B5血药浓度测定",
"result": "50.25",
"point": "",
"unit": "ng/ml",
"module": "Vitamin"
},
{
"abb": "VitB3",
"project": "维生素B3血药浓度测定",
"result": "29.62",
"point": "",
"unit": "ng/ml",
"module": "Vitamin"
},
{
"abb": "VitB6",
"project": "维生素B6血药浓度测定",
"result": "4.67",
"point": "↓",
"unit": "ng/ml",
"module": "Vitamin"
},
{
"abb": "AFP",
"project": "甲胎蛋白",
"result": "0.5",
"point": "",
"unit": "ng/ml",
"module": "Tumor Markers"
},
{
"abb": "CEA",
"project": "癌胚抗原",
"result": "1.3",
"point": "",
"unit": "ng/ml",
"module": "Tumor Markers"
},
{
"abb": "CA19-9",
"project": "糖类抗原19-9",
"result": "9.9",
"point": "",
"unit": "U/ml",
"module": "Tumor Markers"
},
{
"abb": "CA72-4",
"project": "糖类抗原72-4",
"result": "2.6",
"point": "",
"unit": "U/ml",
"module": "Tumor Markers"
},
{
"abb": "CA24-2",
"project": "糖类抗原24-2",
"result": "7.3",
"point": "",
"unit": "U/ml",
"module": "Tumor Markers"
},
{
"abb": "CA50",
"project": "糖类抗原50",
"result": "8.0",
"point": "",
"unit": "U/ml",
"module": "Tumor Markers"
},
{
"abb": "CA125",
"project": "糖类抗原125",
"result": "4.9",
"point": "",
"unit": "U/ml",
"module": "Tumor Markers"
},
{
"abb": "NSE",
"project": "神经元特异性烯醇化酶",
"result": "2.6",
"point": "",
"unit": "ng/ml",
"module": "Tumor Markers"
},
{
"abb": "CYFRA21-1",
"project": "细胞角蛋白19片段",
"result": "1.6",
"point": "",
"unit": "ng/ml",
"module": "Tumor Markers"
},
{
"abb": "ProGRP",
"project": "胃泌素释放肽前体",
"result": "30.0",
"point": "",
"unit": "pg/ml",
"module": "Tumor Markers"
},
{
"abb": "SCC",
"project": "鳞状细胞癌相关抗原",
"result": "2.32",
"point": "",
"unit": "ng/ml",
"module": "Tumor Markers"
},
{
"abb": "TPSA",
"project": "总前列腺特异抗原",
"result": "0.48",
"point": "",
"unit": "ng/ml",
"module": "Tumor Markers"
},
{
"abb": "FPSA",
"project": "游离前列腺特异抗原",
"result": "0.25",
"point": "",
"unit": "ng/ml",
"module": "Tumor Markers"
},
{
"abb": "F/TPSA",
"project": "游离PSA/总PSA",
"result": "0.52",
"point": "",
"unit": "",
"module": "Tumor Markers"
}
]
}

View File

@@ -0,0 +1,876 @@
健康管理体检报告
姓名 姚友胜 性别男 体检单号1125041700091 年龄59
身份证320902196601223511 检查日期20250418
一、血压检查
检查日期 20250418 检查医师 浦湘菊
检查名称 检查结果 参考值 单位
血压(坐姿收缩压) 123 90-140 /mmHg
血压(坐姿舒张压) 77 60-90 /mmHg
科室小结
未见明显异常
二、内科检查
检查日期 20250418 检查医师 杨素芳
检查名称 检查结果 参考值 单位
既往病史 * 既往病史:高血压
* 既往病史:尿酸高
心音 正常
心律 心律齐
肝脾 肋下未及
肺及呼吸道 未见明显异常
皮肤、浅表淋巴结 无肿大,无压痛
医生建议 无
腹部 平软,无包块,无压痛
科室小结
1、既往病史高血压
2、既往病史尿酸高
三、外科检查
检查日期 20250418 检查医师 游洋
检查名称 检查结果 参考值 单位
肛门 拒检指检
前列腺 拒检指检
皮肤 未见明显异常
泌尿生殖 拒检泌尿生殖
疝 无
浅表淋巴结 无肿大,无压痛
甲状腺 无肿大
打印日期20250428 第1页共26页
健康管理体检报告
姓名 姚友胜 性别男 体检单号1125041700091 年龄59
身份证 320902196601223511 检查日期20250418
脊柱 未见明显异常
四肢关节 活动自如
外科其它 未见明显异常
医生建议 无
科室小结
未见明显异常
四、耳鼻喉科检查
检查日期 20250418 检查医师 许万云
检查名称 检查结果 参考值 单位
听力 正常
耳廓 未见明显异常
外耳道 未见明显异常
鼓膜 未见明显异常
鼻中隔 * 鼻中隔糜烂
鼻疾 未见明显异常
其它疾病 无
扁桃体 未见明显异常
咽 未见明显异常
口腔粘膜 未见明显异常
医生建议 无
科室小结
1、鼻中隔糜烂
五、心电图检查
检查日期 20250418 检查医师 陈娟
检查名称 检查结果 参考值 单位
P-R间期 正常
P波 正常
QRS 正常
ST段 正常
T波 正常
传导 正常
心律 正常
医生建议 无
打印日期20250428 第2页共26页
健康管理体检报告
姓名 姚友胜 性别男 体检单号1125041700091 年龄59
身份证 320902196601223511 检查日期20250418
科室小结
未见明显异常
六、眼科检查
检查日期 20250418 检查医师 孙建宁
检查名称 检查结果 参考值 单位
光学相干断层成像(OCT) 未见明显异常
裂隙灯检查 * 双眼白内障初期
外眼 未见明显异常
眼底 * 双眼视网膜动脉硬化Ⅰ
左眼眼压 12.0 8.000-21.000 mmHg
右眼眼压 13.0 8.000-21.000 mmHg
医生建议 无
科室小结
1、双眼白内障初期
2、双眼视网膜动脉硬化I
七、口腔科检查
检查日期 20250418 检查医师 陆泽锋
检查名称 检查结果 参考值 单位
龋齿 无
牙周 * 中度牙周炎
口腔粘膜 正常
其它 未见明显异常
医生建议 * 全口牙周治疗
科室小结
1、中度牙周炎
2、全口牙周治疗
八、尿液分析检查
检查日期 20250418 检查医师 叶安安
检查名称 检查结果 参考值 单位
颜色 深黄色
透明度 清亮
胆红素 阴性
打印日期20250428 第3页共26页
健康管理体检报告
姓名 姚友胜 性别男 体检单号1125041700091 年龄59
身份证320902196601223511 检查日期20250418
尿胆原 正常 阴性-弱阳性
酮体 阴性
葡萄糖 阴性
隐血 阴性
亚硝酸盐 阴性
白细胞酯酶 阴性
蛋白质 阴性
白蛋白 10 <=20 mg/L
肌酐 200 mg/dl
蛋白质肌酐比值 阴性 阴性
白蛋白肌酐比值 阴性 阴性
酸碱度 6.5 4.5-8
比重 1.013 1.003-1.03
渗透压 382 338-1039 m0sm/kg
电导率 11.1 3.1000-39.0000 mS/cm
红细胞 1.3 <=19 /ul
白细胞 1.2 <=24 /ul
白细胞团 0.0 <=23 /ul
上皮细胞 1.1 <=31 /ul
鳞状上皮 0.8 <=31 /ul
非鳞状上皮 0.2 <=4.1 /ul
管型 0.00 <=1 /ul
透明管型 0.00 <=1 /ul
病理管型 0.00 <=1 /ul
细菌 0.0 <=1200 /ul
类酵母菌 0.0 <=1 /ul
结晶 0.0 <=10 /ul
精子 0.0 <=1 /ul
粘液丝 0.00 <=1 /ul
红细胞形态信息 未提示
尿路感染信息 未提示
细菌信息 未提示
科室小结
未见明显异常
九、血型检查
检查日期 20250424 检查医师 刘亚东
检查名称 检查结果 参考值 单位
打印日期20250428 第4页共26页
健康管理体检报告
姓名 姚友胜 性别男 体检单号1125041700091 年龄59
身份证320902196601223511 检查日期20250418
ABO血型 A型
Rh(D)血型 阳性
科室小结
未见明显异常
十、血常规检查
检查日期 20250418 检查医师 卞成思
检查名称 检查结果 参考值 单位
白细胞计数(WBC) 5.1 3.5-9.5 x10^9/L
中性粒细胞百分率(NEUT%) 43.9 40-75 %
淋巴细胞百分率(LYMPH%) 45.7 20-50 %
单核细胞百分率(MONO%) 7.5 3-10 %
嗜酸性粒细胞百分率(EO%) 2.3 0.4-8 %
嗜碱性粒细胞百分率(BASO%) 0.6 <=1 %
中性粒细胞数(NEUT#) 2.3 1.8-6.3 x10^9/L
淋巴细胞数(LYMPH#) 2.4 1.1-3.2 x10^9/L
单核细胞数(MONO#) 0.39 0.1-0.6 x10^9/L
嗜酸性粒细胞数(EO#) 0.12 0.02-0.52 x10^9/L
嗜碱性粒细胞数(BASO#) 0.03 <=0.06 x10^9/L
红细胞计数(RBC) 3.77 4.3-5.8 x10^12/L
血红蛋白量(HGB) 123 130-175 g/L
红细胞比积(HCT) ↓ 38 40-50 %
平均红细胞体积(MCV) 100 82-100 fL
平均红细胞血红蛋白量(MCH) 33 27-34 pg
平均红细胞血红蛋白浓度(MCHC) 326 316-354 g/L
红细胞分布宽度-标准差(RDW-SD) 45 37-50 fL
红细胞分布宽度-变异系数(RDW-CV) 12.0 11.6-14.4 %
血小板计数(PLT) 163 125-350 x10^9/L
血小板比积(PCT) 0.18 0.17-0.35 %
平均血小板体积(MPV) 10.9 9-13 fL
血小板分布宽度(PDW) 16.0 9-17 fL
大型血小板比率(P-LCR) 31 13-43 %
科室小结
1、红细胞计数降低
2、血红蛋白量降低
3、红细胞比积偏低
打印日期20250428 第5页共26页
健康管理体检报告
姓名姚友胜 性别男 体检单号1125041700091 年龄59
身份证320902196601223511 检查日期20250418
十一、肝功能检查
检查日期 20250419 检查医师 冯瑞祥
检查名称 检查结果 参考值 单位
总胆红素 8.3 3-26 umol/L
直接胆红素 1.7 <=7 umol/L
间接胆红素 6.6 1.7-17 umol/L
总蛋白 72.4 65-85 g/L
白蛋白 44.8 40-55 g/L
球蛋白 27.6 20-40 g/L
白球比值 1.6 1.2-2.4
胆碱酯酶 290 203-460 U/L
谷丙转氨酶 16 9-50 U/L
谷草转氨酶 25 15-40 U/L
γ-谷氨酰基转移酶 22 10-60 U/L
碱性磷酸酶 61 45-125 U/L
转铁蛋白 2.43 2.00-3.60 g/L
糖缺失性转铁蛋白 43.57 25.80-65.70 mg/L
糖缺失性转铁蛋白百分率 1.79 1.06-2.60 %
科室小结
未见明显异常
十二、肾功能检查
检查日期 20250419 检查医师 冯瑞祥
检查名称 检查结果 参考值 单位
尿素 5.5 3.1-8.0 mmol/L
肌酐 90 57-97 umol/L
尿酸 285 202-416 umol/L
胱抑素C 0.90 0.55-1.05 mg/L
血清β2微球蛋白 1.8 1.0-2.3 mg/L
尿肌酐 11064 mmol/L
尿微量白蛋白 1.0 <=20 mg/L
尿微量白蛋白尿肌酐比值 0.8 <=30 mg/g
尿β2微球蛋白 70 <=300 ug/L
尿β2微球蛋白尿肌酐比值 56 <=200 ug/g
科室小结
未见明显异常
打印日期20250428 第6页共26页
健康管理体检报告
姓名 姚友胜 性别男 体检单号1125041700091 年龄59
身份证320902196601223511 检查日期20250418
十三、血脂检查
检查日期 20250419 检查医师 冯瑞祥
检查名称 检查结果 参考值 单位
甘油三酯 1.37 0.45-1.69 mmol/L
总胆固醇 4.67 2.33-5.17 mmol/L
高密度脂蛋白胆固醇 1.52 0.91-2.06 mmol/L
低密度脂蛋白胆固醇 2.50 2.07-3.36 mmol/L
游离脂肪酸 0.66 0.1-0.77 mmol/L
科室小结
未见明显异常
十四、血糖及相关检查
检查日期 20250421 检查医师 彭丹亚
检查名称 检查结果 参考值 单位
胰岛素(空腹) 8.3 2.6-24.9 μU/ml
葡萄糖(空腹) 5.41 3.9-6.1 mmol/L
糖化血红蛋白 5.6 4.0-6.5 %
科室小结
未见明显异常
十五、心肌酶检查
检查日期 20250419 检查医师 冯瑞祥
检查名称 检查结果 参考值 单位
肌酸激酶 136 50-310 U/L
肌酸激酶同工酶MB 11 <=24 U/L
科室小结
未见明显异常
十六、心血管病风险因子检查
检查日期 20250419 检查医师 冯瑞祥
检查名称 检查结果 参考值 单位
超敏C反应蛋白 0.5 <=3.0 mg/L
打印日期20250428 第7页共26页
健康管理体检报告
姓名 姚友胜 性别男 体检单号1125041700091 年龄59
身份证320902196601223511 检查日期20250418
同型半胱氨酸 9.7 <=15 umol/L
脂蛋白(a) 26 <=300 mg/L
科室小结
未见明显异常
十七、甲状腺功能检查
检查日期 20250421 检查医师 彭丹亚
检查名称 检查结果 参考值 单位
甲状腺球蛋白 8.8 3.5-77.0 ng/ml
三碘甲状腺原氨酸T3 1.31 1.3-2.4 nmol/L
甲状腺素T4 99.0 70-140 nmol/L
游离三碘甲状腺原氨酸FT3 4.35 3.82-6.30 pmol/L
游离甲状腺素FT4 14.30 12.80-21.30 pmol/L
促甲状腺素TSH 1.55 0.75-5.60 mIU/L
抗甲状腺球蛋白抗体 16.5 <115.0 IU/ml
抗甲状腺过氧化物酶抗体 13.1 <34.0 IU/ml
科室小结
未见明显异常
十八、胃功能检查
检查日期 20250421 检查医师 陆朝阳
检查名称 检查结果 参考值 单位
胃蛋白酶原I 98.4 >=30 ng/ml
胃泌素-17 2.9 1.7-7.6 pmol/L
胃蛋白酶原Ⅱ 11.1 ng/ml
胃蛋白酶原比值 8.9 >=3
科室小结
未见明显异常
十九、感染标志物检查
检查日期 20250424 检查医师 陈瑾
检查名称 检查结果 参考值 单位
EB病毒DNA 未检出 100 IU/ml
乙肝表面抗原 0.87 <1.0 COI
打印日期20250428 第8页共26页
健康管理体检报告
姓名 姚友胜 性别男 体检单号1125041700091 年龄59
身份证320902196601223511 检查日期20250418
乙肝表面抗体 <2.00 <10.0 IU/L
乙肝e抗原 0.10 <1.0 COI
乙肝e抗体 1.40 >1.0 COI
乙肝核心抗体 0.01 >1.0 COI
科室小结
1、乙肝核心抗体阳性
二十、风湿病检查
检查日期 20250419 检查医师 冯瑞祥
检查名称 检查结果 参考值 单位
C反应蛋白 0.5 <=6.0 mg/L
抗链球菌溶血素"0" 32 <=160 IU/ml
抗核抗体 0.9 <40.0 AU/ml
类风湿因子 5 <=20 IU/ml
科室小结
未见明显异常
二十一、电解质检查
检查日期 20250424 检查医师 柏玉
检查名称 检查结果 参考值 单位
钾 3.99 3.5-5.5 mmol/L
钠 139.3 135-145 mmol/L
氯 105.7 98--108 mmol/L
总钙 2.42 2.11-2.52 mmol/L
磷 1.04 0.85-1.51 mmol/L
科室小结
未见明显异常
二十二、骨矿代谢检查
检查日期 20250421 检查医师 彭丹亚
检查名称 检查结果 参考值 单位
甲状旁腺素 5.9 1.6-6.9 pmol/L
骨钙素 15.4 5.58-28.62 ng/ml
打印日期20250428 第9页共26页
健康管理体检报告
姓名 姚友胜 性别男 体检单号1125041700091 年龄59
身份证320902196601223511 检查日期20250418
科室小结
未见明显异常
二十三、贫血检查
检查日期 20250421 检查医师 彭丹亚
检查名称 检查结果 参考值 单位
维生素B12 497 145-569 pmol/L
血清铁蛋白 86 31.3-408.5 ng/ml
科室小结
未见明显异常
二十四、维生素测定检查
检查日期 20250424 检查医师 赵娴
检查名称 检查结果 参考值 单位
维生素B9(叶酸)血药浓度测定 12.18 >4 ng/ml
25-羟基维生素D血药浓度测定 19.91 30-100 ng/ml
维生素A血药浓度测定 564.54 325-780 ng/ml
25-羟基维生素D3血药浓度测定 19.63 无参考范围 ng/ml
25-羟基维生素D2血药浓度测定 0.28 无参考范围 ng/ml
维生素E血药浓度测定 9.29 5--18 ug/ml
维生素K1血药浓度测定 f 3.40 0.13--1.88 ng/ml
维生素B2血药浓度测定 7.90 2.33-14.69 ng/ml
维生素B1血药浓度测定 ↓ 1.67 2.4-9.02 ng/ml
维生素B5血药浓度测定 50.25 12.9-253.1 ng/ml
维生素B3血药浓度测定 29.62 5.2-72.1 ng/ml
维生素B6血药浓度测定 4.67 4.9-30.9 ng/ml
科室小结
1、25-羟基维生素D血药浓度测定偏低
2、维生素K1血药浓度测定偏高
3、维生素B1血药浓度测定偏低
4、维生素B6血药浓度测定偏低
二十五、肿瘤标志物检查
检查日期 20250421 检查医师 陆朝阳
检查名称 检查结果 参考值 单位
打印日期20250428 第10页共26页
健康管理体检报告
姓名 姚友胜 性别男 体检单号1125041700091 年龄59
身份证320902196601223511 检查日期20250418
甲胎蛋白 0.5 <=7.0 ng/ml
癌胚抗原 1.3 <=5 ng/ml
糖类抗原19-9 9.9 <=30 U/ml
糖类抗原72-4 2.6 <6.9 U/ml
糖类抗原24-2 7.3 <20.0 U/ml
糖类抗原50 8.0 <25.0 U/ml
糖类抗原125 4.9 <=24 U/ml
神经元特异性烯醇化酶 2.6 <16.3 ng/ml
细胞角蛋白19片段 1.6 <3.3 ng/ml
胃泌素释放肽前体 30.0 28.3-74.4 pg/ml
鳞状细胞癌相关抗原 2.32 <=2.7 ng/ml
总前列腺特异抗原 0.48 <=4 ng/ml
游离前列腺特异抗原 0.25 <=0.93 ng/ml
游离PSA/总PSA 0.52 0.25-1
科室小结
未见明显异常
二十六、碳十三呼气试验检查
检查日期 20250418 检查医师 张小芳
检查名称 检查结果 参考值 单位
碳13尿素呼气试验结果 阴性 阴性
碳13尿素呼气试验DOB值 0.73 <4.0
科室小结
未见明显异常
二十七、心脏彩色超声检查
检查日期 20250419 检查医师 董静
检查名称 检查结果 参考值 单位
心脏彩色超声检查所见 心脏各房、室腔内径正常,主动脉窦部内径正常。各瓣膜形态、回
声及开放活动未见明显异常。房间隔及室间隔连续未见中断。左
室壁厚度正常,静息状态下未见明显节段性左室壁运动异常。心包
及心包腔未见明显异常。CDFI:二、三尖瓣房侧及主动脉瓣下可见
返流束。二尖瓣口舒张期血流频谱E峰大于A峰。组织多普勒显
像(TDI):二尖瓣环E'/A'小于1。
心脏彩色超声检查提示 * 主动脉瓣返流(轻微)
* 二尖瓣返流(轻度)
打印日期20250428 第11页共26页
健康管理体检报告
姓名 姚友胜 性别男 体检单号1125041700091 年龄59
身份证 320902196601223511 检查日期20250418
* 三尖瓣返流(轻度)
* 左室松弛性异常
左室收缩功能正常
心脏穿透 健康服务中心 EPIQTC TIS0.6MI1.4 2025/04/18 07:57:35 心脏穿透 健康服务中心 EPIQTC TIS1.2 MI1.1 07:59.54
S5-1 45Hz 18cm S5-1 18cm M1M4
20 20 75% C50
C50 HRes ,: HRes
彩色血流 4000Hz
WF 309H
1.63.2 1.63.2 小®
心脏穿透 健康服务中心 EPIQTC TIS1.2 MI 1.1 08:00.54
S5-1 21Hz 18cm M1M4
20 C50
HRes ,:
彩色血流 4000Hz
WESRCL Ervs
1.63.2 ®
科室小结
1、主动脉瓣返流轻微
2、二尖瓣返流轻度
3、三尖瓣返流轻度
4、左室松弛性异常
二十八、腹部彩色超声检查
检查日期 20250419 检查医师 傅宁华
检查名称 检查结果 参考值 单位
腹部彩色超声检查所见 肝脏:肝脏大小形态正常,包膜光整,肝内管系走向清晰。于肝
内可见数个无回声区较大约23*22mm,边界清,后方回声增强。
胆囊胆囊切除术后胆总管内径约8mm,显示段未见明显异常回
声。肾脏:双侧肾脏大小形态正常,皮髓质分界清晰,集合系统
未见分离。左侧肾脏上极可见一个无回声区,壁薄,边界清,后
方回声增强大小约39*39mm。左侧肾脏内可见数个强回声光团
较大直径约8mm,后方伴声影。右侧肾脏内可见数个强回声光团,
较大直径约7mm,后方伴声影。胰腺:胰腺大小形态正常,边界清
晰,内部回声均匀,其内未见明显异常,主胰管未见明显扩张。
打印日期20250428 第12页共26页
健康管理体检报告
姓名 姚友胜 性别男 体检单号1125041700091 年龄59
身份证320902196601223511 检查日期20250418
脾脏:脾脏大小形态正常,回声均匀,其内未见明显异常。
腹部彩色超声检查提示 * 肝囊肿
* 胆囊切除术后
* 左侧肾囊肿
* 双侧多发性肾结石
胰腺声像图未见异常
脾脏声像图未见异常
MI1.2Tls 0.6 C1-6 DlanYo LlaoYangYuar
-AO% -AO
L 0.84 cm
545:550130.7:31.1
25/04/1807:39:37
-AO% -AO%
L0.73 cm 3.90 cm
291:294(16.4:16.5 914:915(51.1:51.1
DlanYo LlaoYangYua
L 0.88 cm
243:252(13.7:14.1
5/04/1807:42:00 Place the last poin
科室小结
1、肝囊肿
2、胆囊切除术后
3、左侧肾囊肿
4、双侧多发性肾结石
打印日期20250428 第13页共26页
健康管理体检报告
姓名 姚友胜 性别男 体检单号1125041700091 年龄59
身份证320902196601223511 检查日期20250418
二十九、甲状腺彩色超声检查
检查日期 20250419 检查医师 傅宁华
检查名称 检查结果 参考值 单位
甲状腺彩色超声检查所见 甲状腺切面形态大小正常,表面光滑,包膜完整,内部回声均匀,
其内未见明显异常回声CDFI示血流信号未见异常。
甲状腺彩色超声检查提示 甲状腺声像图未见异常
DlanYo LlaoYangYuar 25/04/1807:36:32 MI 0.7 Tls 0.5 ML6-15
Generi
IIAO% 3/16
科室小结
未见明显异常
三十、颈动脉彩色超声检查
检查日期 20250419 检查医师 傅宁华
检查名称 检查结果 参考值 单位
颈动脉彩色超声检查所见 双侧颈动脉走行及内径正常,内中膜光滑,厚度范围正常,未见
明显斑块形成。CDFI示管腔内血流充盈良好血流速度及频谱形
态未见明显异常。
颈动脉彩色超声检查提示 双侧颈动脉声像图及多普勒血流频谱未见明显异常
DlanYo LlaoYangYua 25004/18.07-37-34 MI 0.7 Tls 0.5 ML6-15
A/B Ratio
科室小结
未见明显异常
打印日期20250428 第14页共26页
健康管理体检报告
姓名 姚友胜 性别男 体检单号1125041700091 年龄59
身份证320902196601223511 检查日期20250418
三十一、前列腺彩色超声检查
检查日期 20250419 检查医师 傅宁华
检查名称 检查结果 参考值 单位
前列腺彩色超声检查所见 膀胱:膀胱不充盈。前列腺:经腹壁检查,前列腺大小约
46*27mm,形态尚规则,包膜光整,回声欠均匀。
前列腺彩色超声检查提示 * 前列腺轻度增生
DlanYo LlaoYangYua MI 1.2Tls 0.6 C1-6
科室小结
1、前列腺轻度增生
三十二、经颅多普勒彩色超声检查
检查日期 20250418 检查医师 李倩
检查名称 检查结果 参考值 单位
TCD提示 * 双侧椎动脉流速减慢
科室小结
1、双侧椎动脉流速减慢
三十三、人体成分分析检查
检查日期 20250418 检查医师 庞燕
检查名称 检查结果 参考值 单位
体型评估 肥胖
身高 165.3 公分(cm)
体重 65.6 公斤(Kg)
BMI 24.0 18.50-23.99
科室小结
1、体质指数偏高
打印日期20250428 第15页共26页
健康管理体检报告
姓名 姚友胜 性别男 体检单号1125041700091 年龄59
身份证320902196601223511 检查日期20250418
三十四、骨密度检查
检查日期 20250418 检查医师 周丹
检查名称 检查结果 参考值 单位
骨密度检查结论 * 骨质减少
科室小结
1、骨质减少
三十五、肺功能检查
检查日期 20250418 检查医师 汪向雨
检查名称 检查结果 参考值 单位
肺功能检查 通气功能大致正常
科室小结
未见明显异常
三十六、胸部CT检查
检查日期 20250420 检查医师 鞠兵
检查名称 检查结果 参考值 单位
胸部CT检查所见 胸廓对称,肋骨及胸壁软组织未见异常。肺窗示左肺舌叶可见少
许纤维条索影两肺内可见2-4mm实性、磨玻璃样小结节影
肺门不大。纵隔窗示纵隔无偏移,心影及大血管形态正常,纵隔
内未见肿块及肿大淋巴结。无胸腔积液及胸膜肥厚。扫及肝脏、
左肾可见类圆形低密度影;胆囊缺如。双侧大脑半球对称,灰白
质对比正常,未见局灶性密度异常,各脑室、脑池大小形态正常,
中线结构居中,幕下小脑、脑干无明确异常。
胸部CT检查结论 * 两肺多发小结节,建议随访复查
* 左肺舌叶少许慢性炎症
* 肝囊肿
* 胆囊术后,请结合相关病史
* 左肾囊肿
头颅CT平扫未见明显异常
科室小结
1、两肺多发小结节建议随访复查
2、左肺舌叶少许慢性炎症
3、肝囊肿
4、胆囊术后请结合相关病史
打印日期20250428 第16页共26页
健康管理体检报告
姓名 姚友胜 性别男 体检单号1125041700091 年龄59
身份证320902196601223511 检查日期20250418
5、左肾囊肿
三十七、X光检查
检查日期 20250421 检查医师 鞠兵
检查名称 检查结果 参考值 单位
X光检查所见 颈椎序列齐,生理曲线稍变直;颈椎椎体缘见骨质增生影,前纵
韧带钙化;各椎间隙尚可;余未见明显异常。
X光检查结论 * 颈椎退变
科室小结
1、颈椎退变
打印日期20250428 第17页共26页
健康管理体检报告
姓名 姚友胜 性别男 体检单号1125041700091 年龄59
身份证320902196601223511 检查日期20250418
近3次体检项目图表
血压(坐姿收缩压)
200 133 118 123
最高值140
100 结果
最低值90
0
2023-05-19 2024-04-20 2025-04-18
血压(坐姿舒张压)
87 77
100 72
最高值90
50 结果
最低值60
0
2023-05-19 2024-04-20 2025-04-18
尿酸
600 342 368
285 最高值416
400 结果
200 最低值202
0
2023-05-19 2024-04-20 2025-04-18
甘油三酯
2 1.36 1.37
0.96 最高值1.69
1 结果
最低值0.45
0
2023-05-19 2024-04-20 2025-04-18
总胆固醇
6 4.55 4.19 4.67
最高值5.17
4 结果
2 最低值2.33
0
2023-05-19 2024-04-20 2025-04-18
打印日期20250428 第18页共26页
健康管理体检报告
姓名 姚友胜 性别男 体检单号1125041700091 年龄59
身份证320902196601223511 检查日期20250418
高密度脂蛋白胆固醇
3
1.44 1.46 1.52 最高值2.06
2 结果
1 最低值0.91
0
2023-05-19 2024-04-20 2025-04-18
低密度脂蛋白胆固醇
4 2.61 2.33 2.5
最高值3.36
2 结果
最低值2.07
0
2023-05-19 2024-04-20 2025-04-18
胰岛素(空腹)
30
最高值24.9
20 7.8 8.3 结果
10 5
最低值2.6
0
2023-05-19 2024-04-20 2025-04-18
葡萄糖(空腹)
10
5.77 4.96 5.41 最高值6.1
5 结果
最低值3.9
0
2023-05-19 2024-04-20 2025-04-18
甲胎蛋白
10
最高值7.0
5 结果
0.5 0.7 0.5 最低值0
0
2023-05-19 2024-04-20 2025-04-18
打印日期20250428 第19页共26页
健康管理体检报告
姓名 姚友胜 性别男 体检单号1125041700091 年龄59
身份证320902196601223511 检查日期20250418
癌胚抗原
6
最高值5
4 1.5 结果
1.2 1.3
2 ---- 最低值0
0
2023-05-19 2024-04-20 2025-04-18
糖类抗原19-9
40
最高值30
20 9.9 结果
5.2 3.6 最低值0
0
2023-05-19 2024-04-20 2025-04-18
糖类抗原72-4
10
最高值6.899
5 2 2.9 2.6 结果
最低值0
0
2023-05-19 2024-04-20 2025-04-18
糖类抗原125
30
最高值24
20 7.5 结果
6 4.9
10 最低值0
0
2023-05-19 2024-04-20 2025-04-18
总前列腺特异抗原
6
最高值4
4 结果
1.03 0.82 0.48
2 最低值0
0
2023-05-19 2024-04-20 2025-04-18
打印日期20250428 第20页共26页
健康管理体检报告
姓名 姚友胜 性别男 体检单号1125041700091 年龄59
身份证320902196601223511 检查日期20250418
游离前列腺特异抗原
1
最高值0.93
0.5 0.32 0.25 0.25 结果
最低值0
0
2023-05-19 2024-04-20 2025-04-18
BMI
30 24.4 23.7 24
最高值23.99
20 结果
10 最低值18.50
0
2023-05-19 2024-04-20 2025-04-18
打印日期20250428 第21页共26页
健康管理体检报告
姓名 姚友胜 性别男 体检单号1125041700091 年龄59
身份证320902196601223511 检查日期20250418
血压检查
年份 20250418 20240420 20230519
检验项目 结果 结果 结果
血压(坐姿收缩压) 123 118 133
血压(坐姿舒张压) 77 72 87
内科检查
年份 20250418 20240420 20230519
检验项目 结果 结果 结果
既往病史 既往病史:高血压 既往病史:高血压 既往病史:高血压
既往病史:尿酸高 /
心音 正常 正常 正常
心律 心律齐 心律齐 心律齐
/ 心动过缓每分钟52 /
肝脾 肋下未及 肋下未及 肋下未及
肺及呼吸道 未见明显异常 未见明显异常 未见明显异常
皮肤、浅表淋巴结 无肿大,无压痛 无肿大,无压痛 无肿大,无压痛
医生建议 无 无 无
腹部 平软,无包块,无压 平软,无包块,无压 平软,无包块,无压
痛 痛 痛
胸部CT检查
年份 20250420 20240422 20230522
检验项目 结果 结果 结果
胸部CT检查所见 胸廓对称,肋骨及胸 胸廓对称,肋骨及胸 胸廓对称,肋骨及胸
壁软组织未见异常。 壁软组织未见异常。 壁软组织未见异常。
肺窗示左肺舌叶可 肺窗示双肺纹理清 肺窗示双肺纹理清
见少许纤维条索影, 晰,走行自然,肺野 晰,走行自然,肺野
两肺内可见2-4mm 透光度良好,左下肺 透光度良好,左下肺
实性、磨玻璃样小结 背段见磨玻璃微小 背段见磨玻璃微小
节影,双肺门不大。 结节约5*4mm;余 结节约5*4mm;余
纵隔窗示纵隔无偏 双肺见多枚实性微 双肺见多枚实性微
移,心影及大血管形 小结节影,直径小于 小结节影,直径小于
态正常,纵隔内未见 5mm。双肺门不大。 5mm。双肺门不大。
肿块及肿大淋巴结。 纵隔窗示纵隔无偏 纵隔窗示纵隔无偏
无胸腔积液及胸膜 移,心影及大血管形 移,心影及大血管形
肥厚。扫及肝脏、左 态正常,纵隔内未见 态正常,纵隔内未见
肾可见类圆形低密 肿块及肿大淋巴结。 肿块及肿大淋巴结。
打印日期20250428 第22页共26页
健康管理体检报告
姓名 姚友胜 性别男 体检单号1125041700091 年龄59
身份证320902196601223511 检查日期20250418
度影;胆囊缺如。双 无胸腔积液及胸膜 无胸腔积液及胸膜
侧大脑半球对称,灰 肥厚。肝及左肾见囊 肥厚。肝及左肾见囊
白质对比正常,未见 状低密度影。胆囊窝 状低密度影。胆囊窝
局灶性密度异常,各 见银夹。 见银夹。
脑室、脑池大小形态
正常,中线结构居中,
幕下小脑、脑干无明
确异常。
胸部CT检查结论 两肺多发小结节,建 左下肺背段磨玻璃 左下肺背段磨玻璃
议随访复查 微小结节影拟为良 微小结节影拟为良
性病变 性病变
左肺舌叶少许慢性 两肺内散在实性微 两肺内散在实性微
炎症 小结节影拟为陈旧 小结节影拟为陈旧
性炎性灶 性炎性灶
肝囊肿 肝及左肾囊肿 肝及左肾囊肿
胆囊术后,请结合相 胆囊窝银夹拟为术 胆囊窝银夹拟为术
关病史 后改变 后改变
左肾囊肿 /
头颅CT平扫未见明 /
显异常
X光检查
年份 20250421 20240423 /
检验项目 结果 结果 结果
X光检查所见 颈椎序列齐,生理曲 颈椎序列,生理曲度
线稍变直;颈椎椎体 变直,部分椎体边缘
缘见骨质增生影,前 骨质增生,前纵韧带
纵韧带钙化;各椎间 可见小斑点状高密
隙尚可;余未见明显 度影;椎间隙尚可,
异常。 椎小关节未见明显
异常。余无特殊。
X光检查结论 颈椎退变 颈椎退行性改变
打印日期20250428 第23页共26页
健康管理体检报告
姓名 姚友胜 性别男 体检单号1125041700091 年龄59
身份证320902196601223511 检查日期20250418
三十八、综合诊断及建议
1. 既往病史:高血压
(1)健康行为指导建议低盐饮食每日6克左右。低脂、低胆固醇膳食建议多食绿叶类蔬菜、鲜奶
及水果等,限制饮酒、戒烟。注意劳逸结合,减轻精神压力,保持心理平衡。
(2)治疗建议:您本次收缩压和舒张压都在正常范围,请保持健康生活方式,定期复查血压,心内科随
诊。
2. 既往病史:尿酸高
建议您结合临床,定期复查血尿酸。
3. 鼻中隔糜烂
请您耳鼻喉科进一步检查治疗。
4. 双眼白内障初期
建议您定期复查,眼科随诊。
5. 双眼视网膜动脉硬化I
建议您改善微循环,定期查血压。
6. 中度牙周炎
建议您全口牙周治疗。
7. 红细胞计数降低,红细胞比积偏低
常见各种贫血,建议定期复查,必要时请到血液科随诊。
8. 血红蛋白量降低
请您专科进一步检查贫血原因并作相应治疗。
9. 乙肝核心抗体阳性
乙肝病毒感染后HBsAg已消失HBsAb尚未出现的窗口期传染性弱或无传染性。建议避免疲劳、避免
饮酒,必要时专科进一步复查随诊。
10.25-羟基维生素D血药浓度测定偏低
25-羟基维生素D血药浓度测定偏低意味着体内的维生素D水平缺乏或不足。这可能与摄入不足、日照不
足、肠道吸收不良、肝肾疾病等因素有关。25-羟基维生素D偏低会影响钙的吸收增加骨骼疾病、心血
管疾病风险,并可能影响免疫力和情绪。建议您结合临床,定期复查,必要时三甲医院专科进一步检查偏
低原因。
11. 维生素K1血药浓度测定偏高
维生素K1血药浓度测定偏高可能是近期绿色蔬菜食用量较多如菠菜等或者是长期、大量服用了含有
打印日期20250428 第24页共26页
健康管理体检报告
姓名 姚友胜 性别男 体检单号1125041700091 年龄59
身份证320902196601223511 检查日期20250418
维生素K1的保健品导致。建议结合临床定期复查必要时进一步检查偏高原因专科随诊。
12. 维生素B1血药浓度测定偏低
维生素B1血药浓度测定偏低主要原因包括饮食摄入不足、吸收障碍、代谢需求增加等。建议结合临床
定期复查必要时进一步检查明确维生素B1血药浓度测定偏低的原因专科随诊。
13. 维生素B6血药浓度测定偏低
维生素B6血药浓度测定偏低主要原因包括饮食摄入不足、吸收障碍、代谢需求增加、药物影响及遗传因
素等。建议结合临床定期复查适当补充维生素B6片必要时进一步检查明确维生素B6血药浓度测
定偏低的原因,专科随诊。
14. 主动脉瓣返流(轻微),二尖瓣返流(轻度),三尖瓣返流(轻度),左室松弛性异常
建议您结合临床,定期复查,心内科随诊。
15. B超、CT检查肝囊肿
根据您此次检查结果,建议年度复查。肝囊肿如果直径>100mm,并有腹部胀痛者可专科穿刺或手术治疗。
16.B超胆囊切除术后。CT:胆囊术后
建议您定期B超复查专科随诊。
17.B超、CT检查左侧肾囊肿
根据您此次检查结果,建议定期复查,泌尿外科随诊。肾囊肿如果直径>50mm应考虑泌尿外科治疗。
18.双侧多发性肾结石
(1)肾结石是因为尿液中钙、草酸、尿酸等成石物质浓度升高,在肾内析出结晶并积聚且逐渐增大而成。
(2)建议平时多饮水,多食蔬菜水果,不饮浓茶。高尿酸者应避免高嘌呤食物如动物内脏、蛤蟹类、豆
制品类、啤酒、海鲜等。
(3)建议结合临床,如有腹痛或腰痛等不适症状时,请去泌尿外科就诊。
19. 前列腺轻度增生
无症状者无需处理,定期复查。
20. 双侧椎动脉流速减慢
建议您结合临床,定期复查,心内科随诊。
21. 体质指数偏高
建议您改善生活方式,饮食控制,适当运动,以减轻体重。
22. 骨质减少
建议多食用含钙量高的食物如牛奶、虾等同时可补充钙剂及维生素D等以促进钙的吸收适量锻炼
止摔倒,多晒太阳,骨科随诊。
打印日期20250428 第25页共26页
健康管理体检报告
姓名 姚友胜 性别男 体检单号1125041700091 年龄59
身份证320902196601223511 检查日期20250418
23. 两肺多发小结节;左肺舌叶少许慢性炎症
建议您结合临床,定期复查,呼吸科随诊。
24. 颈椎退变
1.请结合临床,年度复查。
2.有头晕或颈部、上肢有僵麻感等症状时需及时去医院就诊,可选择针灸,理疗或牵引等方法治疗。
3.平时注意避免过分疲劳,并选用合适高度和弧度的枕头,注意颈部适度活动及保暖。
备注:
1.“结合临床定期复查”或“定期复查”指如无明显身体不适症状者一般6-12个月复查如出现身体
不适症状情况请及时去医院就诊。
2.“随诊”说明您身体有了病变趋势,需要您高度重视,如出现身体不适症状情况,请及时去医院进一步
检查、就诊。
3.“近期复查”是指一个月内复查。
主检医师:买晓配
打印日期20250428 第26页共26页

BIN
backend/test_step3.docx Normal file

Binary file not shown.

177
backend/xml_safe_save.py Normal file
View File

@@ -0,0 +1,177 @@
"""
安全保存模块 - 使用 lxml 精确处理 XML 元素
"""
import zipfile
import shutil
import os
import re
from pathlib import Path
from lxml import etree
def safe_save(doc, output_path, template_path):
"""
安全保存 - 使用 lxml 精确处理 XML
策略:
1. 先保存文档到临时文件
2. 使用 lxml 解析 XML
3. 从模板复制前四页元素(到 Client Health Program 为止)
4. 从处理后文件复制 Client Health Program 之后的所有内容
5. 合并并保存
"""
import tempfile
output_path = Path(output_path)
template_path = Path(template_path)
ns = {'w': 'http://schemas.openxmlformats.org/wordprocessingml/2006/main'}
temp_fd, temp_path = tempfile.mkstemp(suffix='.docx')
os.close(temp_fd)
try:
# 1. 保存到临时文件
doc.save(temp_path)
# 2. 读取模板 XML
with zipfile.ZipFile(template_path, 'r') as z:
template_xml = z.read('word/document.xml')
template_tree = etree.fromstring(template_xml)
template_body = template_tree.find('.//w:body', ns)
# 3. 读取处理后 XML
with zipfile.ZipFile(temp_path, 'r') as z:
modified_xml = z.read('word/document.xml')
modified_tree = etree.fromstring(modified_xml)
modified_body = modified_tree.find('.//w:body', ns)
if template_body is None or modified_body is None:
print(" [安全保存] 无法解析 XML body")
shutil.copy(temp_path, output_path)
return
template_children = list(template_body)
modified_children = list(modified_body)
# 4. 找到模板中的保护边界Client Health Program 之后)
boundary_pos = -1
for i, elem in enumerate(template_children):
text = ''.join(elem.itertext()).strip()
if 'Client Health Program' in text or '客户健康方案' in text:
boundary_pos = i + 1 # 包括这个元素
break
if boundary_pos < 0:
# 默认使用 80 个元素
boundary_pos = min(80, len(template_children))
# 5. 找到处理后文件中的数据起始位置
# 关键修改:从 Client Health Program 之后开始,而不是从 health report analysis 开始
# 这样可以保留 Functional Medical Health Advice 等内容
data_start_pos = -1
# 首先尝试找 Client Health Program 的位置
for i, elem in enumerate(modified_children):
text = ''.join(elem.itertext()).strip()
if 'Client Health Program' in text or '客户健康方案' in text:
data_start_pos = i + 1 # 从 Client Health Program 之后开始
print(f" [安全保存] 找到 Client Health Program 位置: {i}")
break
# 如果找不到,使用备用关键词
if data_start_pos < 0:
start_keywords = ['health report analysis', '健康报告分析',
'abnormal index', '异常指标',
'functional medical health advice', '功能医学健康建议',
'urine detection', '尿液检测']
for i, elem in enumerate(modified_children):
text = ''.join(elem.itertext()).strip().lower()
if any(kw in text for kw in start_keywords):
data_start_pos = i
break
if data_start_pos < 0:
data_start_pos = boundary_pos
print(f" [安全保存] 边界位置:{boundary_pos}, 数据起始:{data_start_pos}")
# 6. 清空模板 body重新构建
# 保存模板的 sectPr 元素(包含页脚引用)
sectPr = None
for elem in template_children:
if elem.tag.endswith('}sectPr'):
sectPr = etree.fromstring(etree.tostring(elem))
break
# 清空 body
for elem in list(template_body):
template_body.remove(elem)
# 7. 添加模板的前 boundary_pos 个元素(前四页)
# 重新读取模板以获取原始元素
with zipfile.ZipFile(template_path, 'r') as z:
orig_template_xml = z.read('word/document.xml')
orig_template_tree = etree.fromstring(orig_template_xml)
orig_template_body = orig_template_tree.find('.//w:body', ns)
orig_template_children = list(orig_template_body)
protected_count = 0
for i in range(min(boundary_pos, len(orig_template_children))):
elem = orig_template_children[i]
if elem.tag.endswith('}sectPr'):
continue
elem_copy = etree.fromstring(etree.tostring(elem))
template_body.append(elem_copy)
protected_count += 1
# 8. 添加处理后文件的数据部分(从 Client Health Program 之后开始)
data_count = 0
for i in range(data_start_pos, len(modified_children)):
elem = modified_children[i]
if elem.tag.endswith('}sectPr'):
continue
elem_copy = etree.fromstring(etree.tostring(elem))
template_body.append(elem_copy)
data_count += 1
# 9. 添加 sectPr
if sectPr is not None:
template_body.append(sectPr)
print(f" [安全保存] 保护部分:{protected_count}, 数据部分:{data_count}")
# 10. 保存 XML
new_xml = etree.tostring(template_tree, xml_declaration=True, encoding='UTF-8', standalone='yes')
# 11. 基于模板创建输出文件
temp_result = str(output_path) + '.temp.docx'
with zipfile.ZipFile(template_path, 'r') as zin:
with zipfile.ZipFile(temp_result, 'w', zipfile.ZIP_DEFLATED) as zout:
for item in zin.infolist():
if item.filename == 'word/document.xml':
zout.writestr(item, new_xml)
else:
zout.writestr(item, zin.read(item.filename))
# 12. 移动到最终位置
if output_path.exists():
output_path.unlink()
shutil.move(temp_result, output_path)
print(f" [安全保存] ✓ 完成")
except Exception as e:
print(f" [安全保存] 错误: {e}")
import traceback
traceback.print_exc()
# 回退到普通保存
doc.save(output_path)
finally:
for f in [temp_path, str(output_path) + '.temp.docx']:
if os.path.exists(f):
try:
os.remove(f)
except:
pass

13
frontend/index.html Normal file
View File

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>医疗报告分析系统</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>

2889
frontend/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

26
frontend/package.json Normal file
View File

@@ -0,0 +1,26 @@
{
"name": "medical-report-analyzer",
"private": true,
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"axios": "^1.6.0",
"lucide-react": "^0.292.0"
},
"devDependencies": {
"@types/react": "^18.2.37",
"@types/react-dom": "^18.2.15",
"@vitejs/plugin-react": "^4.2.0",
"autoprefixer": "^10.4.16",
"postcss": "^8.4.31",
"tailwindcss": "^3.3.5",
"vite": "^5.0.0"
}
}

View File

@@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

261
frontend/src/App.jsx Normal file
View File

@@ -0,0 +1,261 @@
import React, { useState, useEffect } from 'react'
import { FileText, Search, Brain, Check, AlertCircle, Trash2, Layers } from 'lucide-react'
import FileUpload from './components/FileUpload'
import ReportList from './components/ReportList'
import ReportDetail from './components/ReportDetail'
import IntegrationResult from './components/IntegrationResult'
import BatchUpload from './components/BatchUpload'
import { api } from './services/api'
function App() {
const [reports, setReports] = useState([])
const [selectedReport, setSelectedReport] = useState(null)
const [integrationResult, setIntegrationResult] = useState(null)
const [selectedReports, setSelectedReports] = useState([])
const [loading, setLoading] = useState(false)
const [error, setError] = useState(null)
const [activeTab, setActiveTab] = useState('batch') // 默认使用批量模式
const loadReports = async () => {
try {
const data = await api.getReports()
setReports(data.reports || [])
} catch (err) {
setError('加载报告列表失败')
}
}
const handleFileUpload = async (file) => {
try {
setLoading(true)
setError(null)
const result = await api.uploadFile(file)
await loadReports()
return result
} catch (err) {
setError('文件上传失败: ' + err.message)
throw err
} finally {
setLoading(false)
}
}
const handleOCR = async (fileId) => {
try {
setLoading(true)
setError(null)
await api.performOCR(fileId)
await loadReports()
} catch (err) {
setError('OCR识别失败: ' + err.message)
} finally {
setLoading(false)
}
}
const handleAnalyze = async (fileId) => {
try {
setLoading(true)
setError(null)
await api.analyzeReport(fileId)
await loadReports()
const detail = await api.getReportDetail(fileId)
setSelectedReport(detail.report)
} catch (err) {
setError('报告分析失败: ' + err.message)
} finally {
setLoading(false)
}
}
const handleIntegrate = async () => {
if (selectedReports.length === 0) {
setError('请至少选择一份报告进行整合')
return
}
try {
setLoading(true)
setError(null)
const result = await api.integrateReports(selectedReports)
setIntegrationResult(result)
} catch (err) {
setError('报告整合失败: ' + err.message)
} finally {
setLoading(false)
}
}
const handleDelete = async (fileId) => {
if (!confirm('确定要删除这份报告吗?')) return
try {
setLoading(true)
await api.deleteReport(fileId)
await loadReports()
if (selectedReport?.id === fileId) {
setSelectedReport(null)
}
setSelectedReports(prev => prev.filter(id => id !== fileId))
} catch (err) {
setError('删除失败: ' + err.message)
} finally {
setLoading(false)
}
}
const toggleReportSelection = (reportId) => {
setSelectedReports(prev =>
prev.includes(reportId)
? prev.filter(id => id !== reportId)
: [...prev, reportId]
)
}
const viewReportDetail = async (reportId) => {
try {
const detail = await api.getReportDetail(reportId)
setSelectedReport(detail.report)
setIntegrationResult(null)
} catch (err) {
setError('加载报告详情失败')
}
}
const handleGeneratePDF = async (fileId) => {
try {
setLoading(true)
setError(null)
const result = await api.generatePDF(fileId)
// 生成成功后直接下载
api.downloadPDF(fileId)
// 显示成功提示
alert('PDF报告生成成功')
} catch (err) {
setError('PDF生成失败: ' + err.message)
} finally {
setLoading(false)
}
}
const handleBatchGenerate = async (files, patientName) => {
try {
setLoading(true)
setError(null)
const result = await api.generateComprehensiveReport(files, patientName)
// 生成成功后直接下载
api.downloadComprehensiveReport(result.pdf_filename)
// 显示成功提示
alert(`综合健康报告生成成功!\n患者${result.patient_name}\n报告数量${result.report_count}`)
} catch (err) {
setError('综合报告生成失败: ' + err.message)
} finally {
setLoading(false)
}
}
return (
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100">
{/* Header */}
<header className="bg-white shadow-md">
<div className="max-w-7xl mx-auto px-4 py-6">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-3">
<FileText className="w-8 h-8 text-indigo-600" />
<h1 className="text-3xl font-bold text-gray-900">医疗报告分析系统</h1>
</div>
<div className="flex items-center space-x-2 text-sm text-gray-600">
<div className="flex items-center space-x-1">
<div className="w-2 h-2 bg-green-500 rounded-full"></div>
<span>系统运行中</span>
</div>
</div>
</div>
</div>
</header>
<main className="max-w-7xl mx-auto px-4 py-8">
{/* Error Alert */}
{error && (
<div className="mb-4 bg-red-50 border border-red-200 rounded-lg p-4 flex items-start">
<AlertCircle className="w-5 h-5 text-red-500 mt-0.5 mr-3" />
<div>
<p className="text-red-800 font-medium">错误</p>
<p className="text-red-700 text-sm">{error}</p>
</div>
<button onClick={() => setError(null)} className="ml-auto text-red-500 hover:text-red-700">
×
</button>
</div>
)}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* Left Panel - Upload & Reports */}
<div className="lg:col-span-1 space-y-6">
{/* 选项卡切换 */}
<div className="bg-white rounded-lg shadow-md p-2 flex space-x-2">
<button
className="flex-1 py-2 px-4 rounded-md font-medium bg-indigo-600 text-white cursor-default"
>
<Layers className="w-4 h-4 inline mr-2" />
批量生成
</button>
</div>
{/* File Upload */}
<BatchUpload onGenerate={handleBatchGenerate} loading={loading} />
{/* Reports List - 只在单个上传模式显示 */}
{false && activeTab === 'single' && (
<ReportList
reports={reports}
selectedReports={selectedReports}
onToggleSelect={toggleReportSelection}
onView={viewReportDetail}
onOCR={handleOCR}
onAnalyze={handleAnalyze}
onDelete={handleDelete}
loading={loading}
/>
)}
{/* Integration Button */}
{false && reports.length > 0 && (
<div className="bg-white rounded-lg shadow-md p-4">
<button
onClick={handleIntegrate}
disabled={loading || selectedReports.length === 0}
className="w-full bg-indigo-600 text-white py-3 rounded-lg font-medium hover:bg-indigo-700 disabled:bg-gray-300 disabled:cursor-not-allowed flex items-center justify-center space-x-2 transition-colors"
>
<Brain className="w-5 h-5" />
<span>整合分析 ({selectedReports.length})</span>
</button>
</div>
)}
</div>
{/* Right Panel - Details */}
<div className="lg:col-span-2">
{integrationResult ? (
<IntegrationResult result={integrationResult} onClose={() => setIntegrationResult(null)} />
) : selectedReport ? (
<ReportDetail
report={selectedReport}
onClose={() => setSelectedReport(null)}
onGeneratePDF={handleGeneratePDF}
/>
) : (
<div className="bg-white rounded-lg shadow-md p-12 text-center">
<Brain className="w-16 h-16 text-gray-300 mx-auto mb-4" />
<p className="text-gray-500 text-lg">上传医疗报告开始分析</p>
<p className="text-gray-400 text-sm mt-2">支持 JPGPNGPDF 格式</p>
</div>
)}
</div>
</div>
</main>
</div>
)
}
export default App

View File

@@ -0,0 +1,191 @@
import React, { useState } from 'react'
import { Upload, X, FileText, User, Loader } from 'lucide-react'
function BatchUpload({ onGenerate, loading }) {
const [selectedFiles, setSelectedFiles] = useState([])
const [patientName, setPatientName] = useState('')
const [dragActive, setDragActive] = useState(false)
const handleFileChange = (e) => {
const files = Array.from(e.target.files)
addFiles(files)
}
const addFiles = (files) => {
const validFiles = files.filter(file => {
const ext = file.name.toLowerCase()
return ext.endsWith('.pdf') || ext.endsWith('.jpg') ||
ext.endsWith('.jpeg') || ext.endsWith('.png')
})
setSelectedFiles(prev => [...prev, ...validFiles])
}
const removeFile = (index) => {
setSelectedFiles(prev => prev.filter((_, i) => i !== index))
}
const handleDrag = (e) => {
e.preventDefault()
e.stopPropagation()
if (e.type === 'dragenter' || e.type === 'dragover') {
setDragActive(true)
} else if (e.type === 'dragleave') {
setDragActive(false)
}
}
const handleDrop = (e) => {
e.preventDefault()
e.stopPropagation()
setDragActive(false)
const files = Array.from(e.dataTransfer.files)
addFiles(files)
}
const handleSubmit = async () => {
if (selectedFiles.length === 0) {
alert('请至少选择一个文件')
return
}
if (!patientName.trim()) {
alert('请输入患者姓名')
return
}
await onGenerate(selectedFiles, patientName.trim())
}
const formatFileSize = (bytes) => {
if (bytes === 0) return '0 Bytes'
const k = 1024
const sizes = ['Bytes', 'KB', 'MB', 'GB']
const i = Math.floor(Math.log(bytes) / Math.log(k))
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i]
}
return (
<div className="bg-white rounded-lg shadow-md p-6">
<h2 className="text-xl font-bold text-gray-900 mb-4 flex items-center">
<Upload className="w-5 h-5 mr-2 text-indigo-600" />
批量上传生成综合报告
</h2>
<p className="text-sm text-gray-600 mb-4">
上传多个检测报告血常规尿常规生化检查等系统将自动识别并生成综合健康报告
</p>
{/* 患者姓名输入 */}
<div className="mb-4">
<label className="block text-sm font-medium text-gray-700 mb-2">
<User className="w-4 h-4 inline mr-1" />
患者姓名 *
</label>
<input
type="text"
value={patientName}
onChange={(e) => setPatientName(e.target.value)}
placeholder="请输入患者姓名"
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent"
disabled={loading}
/>
</div>
{/* 文件上传区域 */}
<div
className={`border-2 border-dashed rounded-lg p-8 text-center transition-colors ${
dragActive
? 'border-indigo-500 bg-indigo-50'
: 'border-gray-300 hover:border-gray-400'
}`}
onDragEnter={handleDrag}
onDragLeave={handleDrag}
onDragOver={handleDrag}
onDrop={handleDrop}
>
<Upload className="w-12 h-12 mx-auto mb-4 text-gray-400" />
<p className="text-gray-600 mb-2">
拖拽文件到此处
<label className="text-indigo-600 hover:text-indigo-700 cursor-pointer ml-1">
点击选择文件
<input
type="file"
multiple
accept=".pdf,.jpg,.jpeg,.png"
onChange={handleFileChange}
className="hidden"
disabled={loading}
/>
</label>
</p>
<p className="text-sm text-gray-500">支持 PDFJPGPNG 格式可多选</p>
</div>
{/* 已选文件列表 */}
{selectedFiles.length > 0 && (
<div className="mt-4">
<h3 className="text-sm font-medium text-gray-700 mb-2">
已选择 {selectedFiles.length} 个文件
</h3>
<div className="space-y-2 max-h-64 overflow-y-auto">
{selectedFiles.map((file, index) => (
<div
key={index}
className="flex items-center justify-between p-3 bg-gray-50 rounded-lg"
>
<div className="flex items-center flex-1 min-w-0">
<FileText className="w-5 h-5 text-gray-400 mr-2 flex-shrink-0" />
<div className="flex-1 min-w-0">
<p className="text-sm font-medium text-gray-900 truncate">
{file.name}
</p>
<p className="text-xs text-gray-500">
{formatFileSize(file.size)}
</p>
</div>
</div>
<button
onClick={() => removeFile(index)}
disabled={loading}
className="ml-2 text-red-500 hover:text-red-700 disabled:opacity-50"
>
<X className="w-5 h-5" />
</button>
</div>
))}
</div>
</div>
)}
{/* 生成按钮 */}
<button
onClick={handleSubmit}
disabled={loading || selectedFiles.length === 0 || !patientName.trim()}
className="w-full mt-6 bg-indigo-600 text-white py-3 px-4 rounded-lg font-medium hover:bg-indigo-700 disabled:bg-gray-300 disabled:cursor-not-allowed flex items-center justify-center space-x-2 transition-colors"
>
{loading ? (
<>
<Loader className="w-5 h-5 animate-spin" />
<span>正在生成综合报告...</span>
</>
) : (
<>
<FileText className="w-5 h-5" />
<span>生成综合健康报告</span>
</>
)}
</button>
{/* 说明 */}
<div className="mt-4 p-3 bg-yellow-50 border border-yellow-200 rounded-lg">
<p className="text-xs text-yellow-800">
<strong>注意</strong>上传的文件仅用于生成报告处理完成后会自动删除不会永久保存在系统中
</p>
</div>
</div>
)
}
export default BatchUpload

View File

@@ -0,0 +1,91 @@
import React, { useState } from 'react'
import { Upload, Loader } from 'lucide-react'
function FileUpload({ onUpload, loading }) {
const [dragging, setDragging] = useState(false)
const handleDragOver = (e) => {
e.preventDefault()
setDragging(true)
}
const handleDragLeave = () => {
setDragging(false)
}
const handleDrop = (e) => {
e.preventDefault()
setDragging(false)
const files = Array.from(e.dataTransfer.files)
if (files.length > 0) {
handleFileSelect(files[0])
}
}
const handleFileSelect = async (file) => {
if (!file) return
const allowedTypes = ['image/jpeg', 'image/png', 'image/jpg', 'application/pdf']
if (!allowedTypes.includes(file.type)) {
alert('不支持的文件格式,请上传 JPG、PNG 或 PDF 文件')
return
}
try {
await onUpload(file)
} catch (err) {
console.error('Upload failed:', err)
}
}
return (
<div className="bg-white rounded-lg shadow-md p-6">
<h2 className="text-lg font-semibold text-gray-800 mb-4 flex items-center">
<Upload className="w-5 h-5 mr-2 text-indigo-600" />
上传医疗报告
</h2>
<div
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onDrop={handleDrop}
className={`border-2 border-dashed rounded-lg p-8 text-center transition-colors ${
dragging
? 'border-indigo-500 bg-indigo-50'
: 'border-gray-300 hover:border-indigo-400'
}`}
>
{loading ? (
<div className="flex flex-col items-center">
<Loader className="w-8 h-8 text-indigo-600 animate-spin mb-2" />
<p className="text-gray-600">上传中...</p>
</div>
) : (
<>
<Upload className="w-12 h-12 text-gray-400 mx-auto mb-3" />
<p className="text-gray-700 mb-2">拖拽文件到此处或点击上传</p>
<p className="text-sm text-gray-500 mb-4">支持 JPGPNGPDF 格式</p>
<label className="inline-block">
<input
type="file"
className="hidden"
accept=".jpg,.jpeg,.png,.pdf"
onChange={(e) => {
if (e.target.files[0]) {
handleFileSelect(e.target.files[0])
}
}}
disabled={loading}
/>
<span className="px-4 py-2 bg-indigo-600 text-white rounded-lg cursor-pointer hover:bg-indigo-700 transition-colors">
选择文件
</span>
</label>
</>
)}
</div>
</div>
)
}
export default FileUpload

View File

@@ -0,0 +1,161 @@
import React from 'react'
import { X, Brain, TrendingUp, AlertTriangle, Lightbulb, Calendar } from 'lucide-react'
function IntegrationResult({ result, onClose }) {
const getSeverityColor = (severity) => {
switch (severity) {
case '高': return 'bg-red-100 border-red-300 text-red-900'
case '中': return 'bg-yellow-100 border-yellow-300 text-yellow-900'
case '低': return 'bg-green-100 border-green-300 text-green-900'
default: return 'bg-gray-100 border-gray-300 text-gray-900'
}
}
return (
<div className="bg-white rounded-lg shadow-md">
{/* Header */}
<div className="border-b border-gray-200 p-6 flex items-center justify-between">
<div>
<h2 className="text-2xl font-bold text-gray-900 flex items-center">
<Brain className="w-6 h-6 mr-2 text-purple-600" />
综合分析报告
</h2>
<p className="text-sm text-gray-600 mt-1">
整合了 {result.report_count || result.reports_included?.length || 0} 份医疗报告
</p>
</div>
<button
onClick={onClose}
className="text-gray-400 hover:text-gray-600 transition-colors"
>
<X className="w-6 h-6" />
</button>
</div>
{/* Content */}
<div className="p-6 space-y-6 max-h-[calc(100vh-300px)] overflow-y-auto">
{/* Overall Summary */}
<div className="bg-gradient-to-r from-purple-50 to-indigo-50 border border-purple-200 rounded-lg p-6">
<h3 className="font-semibold text-purple-900 mb-3 text-lg">整体健康状况</h3>
<p className="text-purple-800 leading-relaxed">
{result.integrated_analysis?.overall_summary || result.overall_summary || '暂无摘要'}
</p>
</div>
{/* Health Trends */}
{(result.integrated_analysis?.health_trends || result.health_trends) && (
<div>
<h3 className="font-semibold text-gray-900 mb-4 flex items-center text-lg">
<TrendingUp className="w-5 h-5 mr-2 text-blue-600" />
健康趋势
</h3>
<div className="grid gap-3">
{(result.integrated_analysis?.health_trends || result.health_trends).map((trend, index) => (
<div key={index} className="bg-blue-50 border border-blue-200 rounded-lg p-4">
<p className="text-blue-900">{trend}</p>
</div>
))}
</div>
</div>
)}
{/* Priority Concerns */}
{(result.integrated_analysis?.priority_concerns || result.priority_concerns) &&
(result.integrated_analysis?.priority_concerns || result.priority_concerns).length > 0 && (
<div>
<h3 className="font-semibold text-gray-900 mb-4 flex items-center text-lg">
<AlertTriangle className="w-5 h-5 mr-2 text-red-600" />
优先关注事项
</h3>
<div className="space-y-3">
{(result.integrated_analysis?.priority_concerns || result.priority_concerns).map((concern, index) => (
<div
key={index}
className={`border rounded-lg p-4 ${getSeverityColor(concern.severity)}`}
>
<div className="flex items-start justify-between mb-2">
<p className="font-semibold">{concern.concern}</p>
<span className="text-xs font-medium px-2 py-1 rounded">
{concern.severity}
</span>
</div>
<p className="text-sm">{concern.description}</p>
</div>
))}
</div>
</div>
)}
{/* Comprehensive Assessment */}
{(result.integrated_analysis?.comprehensive_assessment || result.comprehensive_assessment) && (
<div className="bg-gray-50 border border-gray-300 rounded-lg p-5">
<h3 className="font-semibold text-gray-900 mb-3">综合评估</h3>
<p className="text-gray-800 leading-relaxed">
{result.integrated_analysis?.comprehensive_assessment || result.comprehensive_assessment}
</p>
</div>
)}
{/* Integrated Recommendations */}
{(result.integrated_analysis?.integrated_recommendations || result.integrated_recommendations) && (
<div>
<h3 className="font-semibold text-gray-900 mb-4 flex items-center text-lg">
<Lightbulb className="w-5 h-5 mr-2 text-yellow-600" />
综合建议
</h3>
<ul className="space-y-2">
{(result.integrated_analysis?.integrated_recommendations || result.integrated_recommendations).map((rec, index) => (
<li key={index} className="flex items-start bg-yellow-50 border border-yellow-200 rounded-lg p-3">
<span className="inline-block w-2 h-2 bg-yellow-500 rounded-full mt-2 mr-3 flex-shrink-0"></span>
<span className="text-gray-800">{rec}</span>
</li>
))}
</ul>
</div>
)}
{/* Follow-up Suggestions */}
{(result.integrated_analysis?.follow_up_suggestions || result.follow_up_suggestions) && (
<div>
<h3 className="font-semibold text-gray-900 mb-4 flex items-center text-lg">
<Calendar className="w-5 h-5 mr-2 text-green-600" />
后续跟踪建议
</h3>
<ul className="space-y-2">
{(result.integrated_analysis?.follow_up_suggestions || result.follow_up_suggestions).map((suggestion, index) => (
<li key={index} className="flex items-start">
<span className="inline-block w-2 h-2 bg-green-500 rounded-full mt-2 mr-3 flex-shrink-0"></span>
<span className="text-gray-700">{suggestion}</span>
</li>
))}
</ul>
</div>
)}
{/* Reports Included */}
{result.reports_included && result.reports_included.length > 0 && (
<div className="border-t pt-6">
<h3 className="font-semibold text-gray-900 mb-3 text-sm">包含的报告</h3>
<div className="space-y-2">
{result.reports_included.map((report, index) => (
<div key={index} className="text-sm text-gray-600 bg-gray-50 rounded p-2">
<span className="font-medium">{index + 1}. </span>
{report.filename}
</div>
))}
</div>
</div>
)}
{/* Note */}
{result.note && (
<div className="bg-gray-50 border border-gray-200 rounded-lg p-3">
<p className="text-xs text-gray-600 italic">{result.note}</p>
</div>
)}
</div>
</div>
)
}
export default IntegrationResult

View File

@@ -0,0 +1,184 @@
import React, { useState } from 'react'
import { X, FileText, AlertTriangle, CheckCircle, Activity, Download } from 'lucide-react'
function ReportDetail({ report, onClose, onGeneratePDF }) {
const [generatingPDF, setGeneratingPDF] = useState(false)
const handleGeneratePDF = async () => {
setGeneratingPDF(true)
try {
await onGeneratePDF(report.id)
} finally {
setGeneratingPDF(false)
}
}
const analysis = report.analysis || {}
// 兼容不同结构的返回结果
const keyFindings = Array.isArray(analysis.key_findings)
? analysis.key_findings.map((item) =>
typeof item === 'string'
? item
: item?.finding || item?.text || JSON.stringify(item)
)
: []
const abnormalItems = Array.isArray(analysis.abnormal_items)
? analysis.abnormal_items
: []
let riskAssessmentText = ''
if (typeof analysis.risk_assessment === 'string') {
riskAssessmentText = analysis.risk_assessment
} else if (analysis.risk_assessment && typeof analysis.risk_assessment === 'object') {
const ra = analysis.risk_assessment
const parts = []
if (Array.isArray(ra.high_risk) && ra.high_risk.length > 0) {
parts.push(`【高风险】${ra.high_risk.join('')}`)
}
if (Array.isArray(ra.medium_risk) && ra.medium_risk.length > 0) {
parts.push(`【中风险】${ra.medium_risk.join('')}`)
}
if (Array.isArray(ra.low_risk) && ra.low_risk.length > 0) {
parts.push(`【低风险】${ra.low_risk.join('')}`)
}
riskAssessmentText = parts.join('\n')
}
const recommendations = Array.isArray(analysis.recommendations)
? analysis.recommendations.map((item) =>
typeof item === 'string'
? item
: item?.recommendation || item?.text || JSON.stringify(item)
)
: []
return (
<div className="bg-white rounded-lg shadow-md">
{/* Header */}
<div className="border-b border-gray-200 p-6 flex items-center justify-between">
<div>
<h2 className="text-2xl font-bold text-gray-900 flex items-center">
<FileText className="w-6 h-6 mr-2 text-indigo-600" />
报告详情
</h2>
<p className="text-sm text-gray-600 mt-1">{report.filename}</p>
</div>
<div className="flex items-center space-x-3">
<button
onClick={handleGeneratePDF}
disabled={generatingPDF}
className="flex items-center space-x-2 px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 disabled:bg-gray-300 disabled:cursor-not-allowed transition-colors"
>
<Download className="w-4 h-4" />
<span>{generatingPDF ? '生成中...' : '生成PDF报告'}</span>
</button>
<button
onClick={onClose}
className="text-gray-400 hover:text-gray-600 transition-colors"
>
<X className="w-6 h-6" />
</button>
</div>
</div>
{/* Content */}
<div className="p-6 space-y-6 max-h-[calc(100vh-300px)] overflow-y-auto">
{/* Summary */}
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4">
<h3 className="font-semibold text-blue-900 mb-2 flex items-center">
<Activity className="w-5 h-5 mr-2" />
摘要
</h3>
<p className="text-blue-800">{analysis.summary || '暂无摘要'}</p>
</div>
{/* Key Findings */}
{keyFindings.length > 0 && (
<div>
<h3 className="font-semibold text-gray-900 mb-3 flex items-center">
<CheckCircle className="w-5 h-5 mr-2 text-green-600" />
关键发现
</h3>
<ul className="space-y-2">
{keyFindings.map((finding, index) => (
<li key={index} className="flex items-start">
<span className="inline-block w-2 h-2 bg-green-500 rounded-full mt-2 mr-3 flex-shrink-0"></span>
<span className="text-gray-700">{finding}</span>
</li>
))}
</ul>
</div>
)}
{/* Abnormal Items */}
{abnormalItems.length > 0 && (
<div>
<h3 className="font-semibold text-gray-900 mb-3 flex items-center">
<AlertTriangle className="w-5 h-5 mr-2 text-red-600" />
异常指标
</h3>
<div className="space-y-3">
{abnormalItems.map((item, index) => (
<div key={index} className="bg-red-50 border border-red-200 rounded-lg p-3">
<p className="font-medium text-red-900">{item.name || item}</p>
{item.value && (
<p className="text-sm text-red-700 mt-1">
测量值: {item.value} {item.reference && `(参考: ${item.reference})`}
</p>
)}
</div>
))}
</div>
</div>
)}
{/* Risk Assessment */}
{riskAssessmentText && (
<div>
<h3 className="font-semibold text-gray-900 mb-3">风险评估</h3>
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-4">
<p className="text-yellow-900 whitespace-pre-wrap">{riskAssessmentText}</p>
</div>
</div>
)}
{/* Recommendations */}
{recommendations.length > 0 && (
<div>
<h3 className="font-semibold text-gray-900 mb-3">建议</h3>
<ul className="space-y-2">
{recommendations.map((rec, index) => (
<li key={index} className="flex items-start">
<span className="inline-block w-2 h-2 bg-indigo-500 rounded-full mt-2 mr-3 flex-shrink-0"></span>
<span className="text-gray-700">{rec}</span>
</li>
))}
</ul>
</div>
)}
{/* OCR Text */}
{report.ocr_text && (
<div>
<h3 className="font-semibold text-gray-900 mb-3">原始文本</h3>
<div className="bg-gray-50 border border-gray-200 rounded-lg p-4">
<pre className="text-sm text-gray-700 whitespace-pre-wrap font-mono">
{report.ocr_text}
</pre>
</div>
</div>
)}
{/* Note */}
{analysis.note && (
<div className="bg-gray-50 border border-gray-200 rounded-lg p-3">
<p className="text-xs text-gray-600 italic">{analysis.note}</p>
</div>
)}
</div>
</div>
)
}
export default ReportDetail

View File

@@ -0,0 +1,113 @@
import React from 'react'
import { FileText, Search, Brain, Trash2, CheckCircle, Clock, AlertCircle } from 'lucide-react'
function ReportList({ reports, selectedReports, onToggleSelect, onView, onOCR, onAnalyze, onDelete, loading }) {
const getStatusIcon = (report) => {
if (report.has_analysis) return <CheckCircle className="w-4 h-4 text-green-500" />
if (report.has_ocr) return <Clock className="w-4 h-4 text-blue-500" />
return <AlertCircle className="w-4 h-4 text-gray-400" />
}
const getStatusText = (report) => {
if (report.has_analysis) return '已分析'
if (report.has_ocr) return '已识别'
return '已上传'
}
return (
<div className="bg-white rounded-lg shadow-md p-6">
<h2 className="text-lg font-semibold text-gray-800 mb-4">
报告列表 ({reports.length})
</h2>
{reports.length === 0 ? (
<div className="text-center py-8 text-gray-500">
<FileText className="w-12 h-12 mx-auto mb-2 text-gray-300" />
<p>暂无报告</p>
</div>
) : (
<div className="space-y-3 max-h-[500px] overflow-y-auto">
{reports.map((report) => (
<div
key={report.id}
className={`border rounded-lg p-4 transition-all ${
selectedReports.includes(report.id)
? 'border-indigo-500 bg-indigo-50'
: 'border-gray-200 hover:border-gray-300'
}`}
>
<div className="flex items-start space-x-3">
<input
type="checkbox"
checked={selectedReports.includes(report.id)}
onChange={() => onToggleSelect(report.id)}
className="mt-1 w-4 h-4 text-indigo-600 rounded focus:ring-indigo-500"
disabled={!report.has_analysis}
/>
<div className="flex-1 min-w-0">
<div className="flex items-center space-x-2 mb-1">
<FileText className="w-4 h-4 text-gray-600 flex-shrink-0" />
<p className="text-sm font-medium text-gray-900 truncate">
{report.filename}
</p>
</div>
<div className="flex items-center space-x-2 text-xs text-gray-500 mb-2">
{getStatusIcon(report)}
<span>{getStatusText(report)}</span>
</div>
<div className="flex flex-wrap gap-2">
{!report.has_ocr && (
<button
onClick={() => onOCR(report.id)}
disabled={loading}
className="text-xs px-2 py-1 bg-blue-500 text-white rounded hover:bg-blue-600 disabled:bg-gray-300 flex items-center space-x-1"
>
<Search className="w-3 h-3" />
<span>识别</span>
</button>
)}
{report.has_ocr && !report.has_analysis && (
<button
onClick={() => onAnalyze(report.id)}
disabled={loading}
className="text-xs px-2 py-1 bg-purple-500 text-white rounded hover:bg-purple-600 disabled:bg-gray-300 flex items-center space-x-1"
>
<Brain className="w-3 h-3" />
<span>分析</span>
</button>
)}
{report.has_analysis && (
<button
onClick={() => onView(report.id)}
className="text-xs px-2 py-1 bg-green-500 text-white rounded hover:bg-green-600 flex items-center space-x-1"
>
<FileText className="w-3 h-3" />
<span>查看</span>
</button>
)}
<button
onClick={() => onDelete(report.id)}
disabled={loading}
className="text-xs px-2 py-1 bg-red-500 text-white rounded hover:bg-red-600 disabled:bg-gray-300 flex items-center space-x-1"
>
<Trash2 className="w-3 h-3" />
<span>删除</span>
</button>
</div>
</div>
</div>
</div>
))}
</div>
)}
</div>
)
}
export default ReportList

16
frontend/src/index.css Normal file
View File

@@ -0,0 +1,16 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
}

10
frontend/src/main.jsx Normal file
View File

@@ -0,0 +1,10 @@
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import './index.css'
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App />
</React.StrictMode>,
)

View File

@@ -0,0 +1,96 @@
import axios from 'axios'
const API_BASE_URL = 'http://localhost:8001'
const apiClient = axios.create({
baseURL: API_BASE_URL,
headers: {
'Content-Type': 'application/json',
},
})
export const api = {
// 上传文件
uploadFile: async (file) => {
const formData = new FormData()
formData.append('file', file)
const response = await axios.post(`${API_BASE_URL}/api/upload`, formData, {
headers: { 'Content-Type': 'multipart/form-data' }
})
return response.data
},
// 执行OCR
performOCR: async (fileId) => {
const response = await apiClient.post(`/api/ocr/${fileId}`)
return response.data
},
// 分析报告
analyzeReport: async (fileId) => {
const response = await apiClient.post(`/api/analyze/${fileId}`)
return response.data
},
// 整合报告
integrateReports: async (fileIds) => {
const response = await apiClient.post('/api/integrate', fileIds)
return response.data
},
// 获取所有报告
getReports: async () => {
const response = await apiClient.get('/api/reports')
return response.data
},
// 获取报告详情
getReportDetail: async (fileId) => {
const response = await apiClient.get(`/api/report/${fileId}`)
return response.data
},
// 删除报告
deleteReport: async (fileId) => {
const response = await apiClient.delete(`/api/report/${fileId}`)
return response.data
},
// 生成PDF报告
generatePDF: async (fileId) => {
const response = await apiClient.post(`/api/report/${fileId}/pdf`)
return response.data
},
// 下载PDF报告
downloadPDF: (fileId) => {
window.open(`${API_BASE_URL}/api/report/${fileId}/pdf/download`, '_blank')
},
// 批量上传并生成综合报告
generateComprehensiveReport: async (files, patientName = '患者') => {
const formData = new FormData()
// 添加所有文件
files.forEach(file => {
formData.append('files', file)
})
// 添加患者姓名
formData.append('patient_name', patientName)
const response = await axios.post(
`${API_BASE_URL}/api/comprehensive-report`,
formData,
{
headers: { 'Content-Type': 'multipart/form-data' }
}
)
return response.data
},
// 下载综合报告
downloadComprehensiveReport: (pdfFilename) => {
window.open(`${API_BASE_URL}/api/comprehensive-report/download/${pdfFilename}`, '_blank')
},
}

View File

@@ -0,0 +1,11 @@
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}

15
frontend/vite.config.js Normal file
View File

@@ -0,0 +1,15 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
server: {
port: 5173,
proxy: {
'/api': {
target: 'http://localhost:8001',
changeOrigin: true
}
}
}
})

BIN
template_docxtpl.docx Normal file

Binary file not shown.

BIN
医疗干预模板(1).docx Normal file

Binary file not shown.

94
检查项目分类.md Normal file
View File

@@ -0,0 +1,94 @@
# 医疗检查项目分类
## 一、肝功能 (Liver Function)
| 缩写 | 英文名称 | 中文名称 |
|------|----------|----------|
| ALT | Alanine Aminotransferase | 丙氨酸氨基转移酶 |
| AST | Aspartate Aminotransferase | 天门冬氨酸氨基转移酶 |
| GGT | Gamma-Glutamyl Transferase | 谷氨酰转肽酶 |
| ALP | Alkaline Phosphatase | 碱性磷酸酶 |
| TBIL | Total Bilirubin | 总胆红素 |
| DBIL | Direct Bilirubin | 直接胆红素 |
| TP | Total Protein | 总蛋白 |
| ALB | Albumin | 白蛋白 |
| GLB | Globulin | 球蛋白 |
| LDH | Lactate Dehydrogenase | 乳酸脱氢酶 |
---
## 二、肾功能 (Kidney Function)
| 缩写 | 英文名称 | 中文名称 |
|------|----------|----------|
| BUN | Blood Urea Nitrogen | 尿素氮 |
| CREA | Creatinine | 肌酐 |
| UA | Uric Acid | 尿酸 |
| CysC | Cystatin C | 胱抑素C |
| eGFR | Estimated GFR | 估算肾小球滤过率 |
---
## 三、血常规 (Complete Blood Count)
### 红细胞系列
| 缩写 | 英文名称 | 中文名称 |
|------|----------|----------|
| RBC | RBC count | 红细胞计数 |
| Hb | Hemoglobin | 血红蛋白 |
| HCT | Hematocrit | 红细胞压积 |
| MCV | Mean Corpuscular Volume | 平均红细胞体积 |
| MCH | Mean Corpuscular Hemoglobin | 平均红细胞血红蛋白含量 |
| MCHC | Mean Corpuscular Hemoglobin Concentration | 平均红细胞血红蛋白浓度 |
| RDW | Red Cell Distribution Width | 红细胞体积分布宽度 |
### 血小板系列
| 缩写 | 英文名称 | 中文名称 |
|------|----------|----------|
| PLT | Platelet Count | 血小板计数 |
| MPV | Mean Platelet Volume | 平均血小板体积 |
### 白细胞系列
| 缩写 | 英文名称 | 中文名称 |
|------|----------|----------|
| WBC | White Blood Cell Count | 白细胞计数 |
| NEUT | Neutrophil | 中性粒细胞 |
| NEUT% | Neutrophil % | 中性粒细胞百分比 |
| EOS | Eosinophil | 嗜酸细胞 |
| EOS% | Eosinophil % | 嗜酸细胞百分比 |
| BAS | Basophil | 嗜碱细胞 |
| BAS% | Basophil % | 嗜碱细胞百分比 |
| LYMPH | Lymphocyte | 淋巴细胞 |
| LYMPH% | Lymphocyte % | 淋巴细胞百分比 |
| MONO | Monocyte | 单核细胞 |
| MONO% | Monocyte % | 单核细胞百分比 |
---
## 四、尿常规 (Urine Detection)
| 缩写 | 英文名称 | 中文名称 |
|------|----------|----------|
| Color | Color | 颜色 |
| Clarity | Clarity | 透明度 |
| pH | pH | 酸碱度 |
| TUR | Turbidity | 浊度 |
| SG | Specific Gravity | 比重 |
| PRO | Protein | 蛋白质 |
| GLU | Glucose | 糖 |
| BLD/ERY | Occult Blood/RBC | 隐血或红细胞 |
| ERY | Erythrocyte | 红细胞 |
| WBC | White Blood Cell | 白细胞 |
| LEU | Leucocyte | 白细胞(尿) |
| NIT | Nitrite | 亚硝酸盐 |
| KET | Ketone | 酮体 |
| BIL | Bilirubin | 胆红素 |
| URO | Urobilinogen | 尿胆原 |
| CRY | Crystal | 结晶 |
| SEC | Squamous Epithelial Cells | 鳞状上皮细胞 |
---