This commit is contained in:
Your Name
2026-08-22 08:51:35 +08:00
parent 6c444a4a04
commit c06d293424
69 changed files with 11431 additions and 1601 deletions
@@ -98,10 +98,15 @@ class DoctorRepository(Protocol):
) -> PageResult[Patient]:
"""Return a doctor-scoped patient page."""
def list_consultations(
self, *, page_no: int = 1, page_size: int = 20, **filters: Any
) -> PageResult[Consultation]:
"""Return a diagnosis/consultation page."""
def list_consultations(
self, *, page_no: int = 1, page_size: int = 20, **filters: Any
) -> PageResult[Consultation]:
"""Return a diagnosis/consultation page."""
def list_ai_patient_options(
self, *, page_no: int = 1, page_size: int = 20, keyword: str = ""
) -> PageResult[dict[str, Any]]:
"""Return privacy-safe diagnosis choices for patient AI workflows."""
def list_prescription_templates(
self, *, page_no: int = 1, page_size: int = 20, **filters: Any
@@ -206,6 +211,9 @@ class DoctorRepository(Protocol):
) -> Iterator[dict[str, Any]]:
"""Yield normalized ``start``/``delta``/``done`` assistant events."""
def generate_ai_prescription(self, diagnosis_id: int) -> dict[str, Any]:
"""Generate a server-validated prescription draft for doctor review."""
def get_diagnosis_ai_analysis(
self,
diagnosis_id: int,
@@ -1422,6 +1430,15 @@ class RemoteDoctorRepository:
yield {"event": "delta", "text": answer, "fallback": True}
yield {**result, "event": "done", "fallback": True}
def generate_ai_prescription(self, diagnosis_id: int) -> dict[str, Any]:
"""Generate a structured draft using the server's full patient context."""
return self.analyze_diagnosis_ai(
diagnosis_id,
"请依据患者全部纵向资料生成一份可复核的中医处方草稿。",
task="prescription_generate",
)
def get_diagnosis_ai_analysis(
self,
diagnosis_id: int,
@@ -1992,10 +2009,10 @@ class RemoteDoctorRepository:
return self.get_diagnosis_detail(diagnosis_id, readonly=True)
def list_consultations(
self, *, page_no: int = 1, page_size: int = 20, **filters: Any
) -> PageResult[Consultation]:
"""List diagnosis records using ``tcm.diagnosis/lists``."""
def list_consultations(
self, *, page_no: int = 1, page_size: int = 20, **filters: Any
) -> PageResult[Consultation]:
"""List diagnosis records using ``tcm.diagnosis/lists``."""
request_filters = dict(filters)
start_date = str(request_filters.pop("start_date", "") or "").strip()
@@ -2017,9 +2034,26 @@ class RemoteDoctorRepository:
"tcm.diagnosis/lists",
_page_params(page_no, page_size, request_filters),
)
return PageResult.from_payload(
payload, Consultation.from_dict, page_no=page_no, page_size=page_size
)
return PageResult.from_payload(
payload, Consultation.from_dict, page_no=page_no, page_size=page_size
)
def list_ai_patient_options(
self, *, page_no: int = 1, page_size: int = 20, keyword: str = ""
) -> PageResult[dict[str, Any]]:
"""List privacy-safe diagnosis choices through the dedicated AI endpoint."""
clean_keyword = str(keyword or "").strip()
payload = self.client.get(
"tcm.diagnosis/aiPatientOptions",
_page_params(page_no, page_size, {"keyword": clean_keyword}),
)
return PageResult.from_payload(
payload,
_ai_patient_option,
page_no=page_no,
page_size=page_size,
)
def get_diagnosis_detail(self, diagnosis_id: int, *, readonly: bool = False) -> dict[str, Any]:
"""Load a full diagnosis using the appropriate routed endpoint."""
@@ -2791,6 +2825,7 @@ def _diagnosis_ai_request(diagnosis_id: int, prompt: str, task: str) -> tuple[st
"summary",
"tcm_pattern",
"prescription_review",
"prescription_generate",
"medication_review",
"exam_review",
"complication_risk",
@@ -2845,8 +2880,39 @@ def _page_params(page_no: int, page_size: int, filters: Mapping[str, Any]) -> di
for key, value in filters.items()
if value is not None and not (isinstance(value, (list, tuple, set)) and not value)
}
result.update({"page_no": page_no, "page_size": page_size})
return result
result.update({"page_no": page_no, "page_size": page_size})
return result
def _ai_patient_option(row: Mapping[str, Any]) -> dict[str, Any]:
"""Project one AI patient option without exposing a plaintext phone number."""
diagnosis_id = _to_int(row.get("diagnosis_id", row.get("id")), 0)
source_patient_id = _to_int(row.get("source_patient_id", row.get("patient_id")), 0)
phone = str(row.get("phone_masked") or "").strip()
digits = re.sub(r"\D", "", phone)
if len(digits) >= 7 and "*" not in phone:
phone = f"{digits[:3]}****{digits[-4:]}"
option: dict[str, Any] = {
"diagnosis_id": diagnosis_id,
"source_patient_id": source_patient_id,
"patient_name": str(row.get("patient_name", row.get("name")) or "").strip(),
"phone_masked": phone,
}
for key in (
"gender",
"gender_desc",
"age",
"diagnosis_date",
"diagnosis_summary",
"syndrome_type",
"clinical_diagnosis",
"last_visit_at",
"next_appointment_at",
):
if key in row:
option[key] = row[key]
return option
def _require_mapping(value: object, endpoint: str) -> Mapping[str, Any]: