105 lines
5.1 KiB
Python
105 lines
5.1 KiB
Python
from sqlalchemy.orm import Session
|
|
|
|
from app.core.exceptions import AppError
|
|
from app.repositories.case_repository import CaseRepository
|
|
from app.schemas.training_config import (
|
|
ConfigOption,
|
|
PatientConfig,
|
|
TrainingConfigOptionsResponse,
|
|
TrainingConfigRecommendedResponse,
|
|
)
|
|
|
|
|
|
class TrainingConfigService:
|
|
"""训练配置服务:提供训练页病人初始化配置,不写数据库。"""
|
|
|
|
def __init__(self, db: Session) -> None:
|
|
self.db = db
|
|
self.case_repo = CaseRepository(db)
|
|
|
|
def get_recommended(self, case_id: int) -> TrainingConfigRecommendedResponse:
|
|
"""推荐配置:根据病例返回训练页默认病人初始化配置。"""
|
|
self._ensure_case(case_id)
|
|
recommended = self.default_patient_config()
|
|
return TrainingConfigRecommendedResponse(
|
|
case_id=case_id,
|
|
recommended=recommended,
|
|
recommended_labels=self.config_labels(recommended),
|
|
options=self.config_options(),
|
|
)
|
|
|
|
def get_options(self, case_id: int) -> TrainingConfigOptionsResponse:
|
|
"""配置选项:返回训练页自定义配置的全部可选项。"""
|
|
self._ensure_case(case_id)
|
|
recommended = self.default_patient_config()
|
|
return TrainingConfigOptionsResponse(
|
|
case_id=case_id,
|
|
recommended=recommended,
|
|
recommended_labels=self.config_labels(recommended),
|
|
options=self.config_options(),
|
|
)
|
|
|
|
def default_patient_config(self) -> PatientConfig:
|
|
"""默认配置:按当前产品原型初始化病人信息。"""
|
|
return PatientConfig(
|
|
visit_environment="outpatient",
|
|
age_group="youth",
|
|
education_level="higher",
|
|
personality="calm",
|
|
)
|
|
|
|
def normalize_patient_config(self, config: PatientConfig | None) -> dict:
|
|
"""配置归一:校验并补齐前端传入的病人初始化配置。"""
|
|
selected = config or self.default_patient_config()
|
|
values = selected.model_dump()
|
|
allowed = {key: {item.value for item in items} for key, items in self.config_options().items()}
|
|
for key, value in values.items():
|
|
if value not in allowed.get(key, set()):
|
|
raise AppError("TRAINING_CONFIG_INVALID", f"invalid patient config field: {key}", 400)
|
|
return {
|
|
"values": values,
|
|
"labels": self.config_labels(selected),
|
|
}
|
|
|
|
def config_labels(self, config: PatientConfig) -> dict[str, str]:
|
|
"""配置标签:把配置值转换为前端和提示词可读的中文标签。"""
|
|
option_map = {
|
|
key: {item.value: item.label for item in items}
|
|
for key, items in self.config_options().items()
|
|
}
|
|
values = config.model_dump()
|
|
return {key: option_map.get(key, {}).get(value, value) for key, value in values.items()}
|
|
|
|
def config_options(self) -> dict[str, list[ConfigOption]]:
|
|
"""配置选项:训练页可选病人初始化配置。"""
|
|
return {
|
|
"visit_environment": [
|
|
ConfigOption(value="outpatient", label="门诊", description="适合常规问诊训练"),
|
|
ConfigOption(value="emergency", label="急诊", description="病情紧急、沟通节奏更快"),
|
|
ConfigOption(value="ward", label="病房", description="适合住院病情追踪和处置沟通"),
|
|
],
|
|
"age_group": [
|
|
ConfigOption(value="child", label="儿童", description="由家属代述为主"),
|
|
ConfigOption(value="youth", label="青年", description="表达清楚,能配合问诊"),
|
|
ConfigOption(value="middle_aged", label="中年", description="关注工作、家庭和慢病背景"),
|
|
ConfigOption(value="elderly", label="老年", description="表达较慢,需关注基础病和用药史"),
|
|
],
|
|
"education_level": [
|
|
ConfigOption(value="primary_or_below", label="小学及以下", description="医学术语理解弱"),
|
|
ConfigOption(value="secondary", label="中等教育", description="能理解常见健康解释"),
|
|
ConfigOption(value="higher", label="高等教育", description="理解能力强,能描述细节"),
|
|
],
|
|
"personality": [
|
|
ConfigOption(value="calm", label="平和", description="情绪稳定,按问题回答"),
|
|
ConfigOption(value="anxious", label="焦虑", description="更担心病情和治疗风险"),
|
|
ConfigOption(value="impatient", label="急躁", description="希望快速获得结论"),
|
|
ConfigOption(value="cooperative", label="配合", description="愿意补充细节"),
|
|
ConfigOption(value="suspicious", label="多疑", description="会追问检查和用药依据"),
|
|
],
|
|
}
|
|
|
|
def _ensure_case(self, case_id: int) -> None:
|
|
"""病例校验:确认配置请求对应已发布病例。"""
|
|
if not self.case_repo.get_active_case(case_id):
|
|
raise AppError("CASE_NOT_FOUND", "case not found or inactive", 404)
|