diff --git a/apps/miniprogram/src/services/auth.ts b/apps/miniprogram/src/services/auth.ts index ea610a1..c576b55 100644 --- a/apps/miniprogram/src/services/auth.ts +++ b/apps/miniprogram/src/services/auth.ts @@ -57,3 +57,18 @@ export async function getPatients() { const res = await api.get>('/health/patients'); return Array.isArray(res?.data) ? res.data : (Array.isArray(res) ? res : []); } + +/** 患者摘要 — 列表用,字段最小化,不含敏感信息 */ +export interface PatientSummary { + id: string; + name: string; + gender?: string; + birth_date?: string; + status: string; +} + +/** 获取患者摘要列表(字段最小化,替代 getPatients) */ +export async function getPatientSummaries() { + const res = await api.get>('/health/patients/summary'); + return Array.isArray(res?.data) ? res.data : (Array.isArray(res) ? res : []); +} diff --git a/apps/miniprogram/src/stores/auth.ts b/apps/miniprogram/src/stores/auth.ts index fbfa4f8..6127d52 100644 --- a/apps/miniprogram/src/stores/auth.ts +++ b/apps/miniprogram/src/stores/auth.ts @@ -222,16 +222,30 @@ export const useAuthStore = create((set, get) => ({ }, setCurrentPatient: (patient) => { - secureSet('current_patient_id', patient.id); - secureSet('current_patient', JSON.stringify(patient)); - setCachedPatientId(patient.id); + const safePatient: authApi.PatientInfo = { + id: patient.id, + name: patient.name, + gender: patient.gender, + birth_date: patient.birth_date, + relation: patient.relation, + }; + secureSet('current_patient_id', safePatient.id); + secureSet('current_patient', JSON.stringify(safePatient)); + setCachedPatientId(safePatient.id); clearRequestCache(); - set({ currentPatient: patient }); + set({ currentPatient: safePatient }); }, loadPatients: async () => { try { - const patients = await authApi.getPatients(); + const summaries = await authApi.getPatientSummaries(); + const patients: authApi.PatientInfo[] = summaries.map((p) => ({ + id: p.id, + name: p.name, + gender: p.gender, + birth_date: p.birth_date, + relation: 'self', + })); set({ patients }); if (patients.length > 0 && !get().currentPatient) { get().setCurrentPatient(patients[0]); diff --git a/crates/erp-health/src/dto/patient_dto.rs b/crates/erp-health/src/dto/patient_dto.rs index 277fc5d..f13644b 100644 --- a/crates/erp-health/src/dto/patient_dto.rs +++ b/crates/erp-health/src/dto/patient_dto.rs @@ -246,3 +246,13 @@ pub struct ReferResultResp { pub from_doctor_id: Option, pub to_doctor_id: Uuid, } + +/// 患者摘要 — 列表/切换用,不含敏感字段 +#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] +pub struct PatientSummary { + pub id: Uuid, + pub name: String, + pub gender: Option, + pub birth_date: Option, + pub status: String, +} diff --git a/crates/erp-health/src/handler/patient_handler.rs b/crates/erp-health/src/handler/patient_handler.rs index 04a54cb..34d063c 100644 --- a/crates/erp-health/src/handler/patient_handler.rs +++ b/crates/erp-health/src/handler/patient_handler.rs @@ -12,7 +12,7 @@ use erp_core::types::{ApiResponse, PaginatedResponse, TenantContext}; use crate::dto::DeleteWithVersion; use crate::dto::patient_dto::{ BatchImportPatientReq, BatchResultResp, BindByPhoneReq, BindResultResp, CreatePatientReq, - FamilyMemberReq, FamilyMemberResp, ManageTagsReq, PatientResp, ReferPatientReq, + FamilyMemberReq, FamilyMemberResp, ManageTagsReq, PatientResp, PatientSummary, ReferPatientReq, ReferResultResp, UpdatePatientReq, }; use crate::service::patient_service; @@ -57,6 +57,23 @@ where Ok(Json(ApiResponse::ok(result))) } +/// GET /health/patients/summary — 患者摘要列表(字段最小化,不含敏感信息) +pub async fn list_patient_summaries( + State(state): State, + Extension(ctx): Extension, + Query(params): Query, +) -> Result>>, AppError> +where + HealthState: FromRef, + S: Clone + Send + Sync + 'static, +{ + require_permission(&ctx, "health.patient.list")?; + let page = params.page.unwrap_or(1); + let page_size = params.page_size.unwrap_or(20).min(100); + let result = patient_service::list_summaries(&state, ctx.tenant_id, page, page_size).await?; + Ok(Json(ApiResponse::ok(result))) +} + pub async fn create_patient( State(state): State, Extension(ctx): Extension, diff --git a/crates/erp-health/src/routes/patient.rs b/crates/erp-health/src/routes/patient.rs index c56275d..91292c2 100644 --- a/crates/erp-health/src/routes/patient.rs +++ b/crates/erp-health/src/routes/patient.rs @@ -8,6 +8,10 @@ where S: Clone + Send + Sync + 'static, { Router::new() + .route( + "/health/patients/summary", + axum::routing::get(patient_handler::list_patient_summaries), + ) .route( "/health/patients", axum::routing::get(patient_handler::list_patients) diff --git a/crates/erp-health/src/service/patient_service/crud.rs b/crates/erp-health/src/service/patient_service/crud.rs index 70b9fb5..c3f9882 100644 --- a/crates/erp-health/src/service/patient_service/crud.rs +++ b/crates/erp-health/src/service/patient_service/crud.rs @@ -550,3 +550,47 @@ pub async fn bind_by_phone( patient_name: updated.name, }) } + +/// 患者摘要列表 — 仅返回非敏感字段,供小程序切换/列表使用 +pub async fn list_summaries( + state: &HealthState, + tenant_id: Uuid, + page: u64, + page_size: u64, +) -> HealthResult> { + let limit = page_size.min(100); + let offset = page.saturating_sub(1) * limit; + + let query = patient::Entity::find() + .filter(patient::Column::TenantId.eq(tenant_id)) + .filter(patient::Column::DeletedAt.is_null()); + + let total = query.clone().count(&state.db).await?; + + let models = query + .order_by_desc(patient::Column::CreatedAt) + .offset(offset) + .limit(limit) + .all(&state.db) + .await?; + + let total_pages = total.div_ceil(limit.max(1)); + let data: Vec = models + .into_iter() + .map(|m| PatientSummary { + id: m.id, + name: m.name, + gender: m.gender, + birth_date: m.birth_date, + status: m.status, + }) + .collect(); + + Ok(PaginatedResponse { + data, + total, + page, + page_size: limit, + total_pages, + }) +} diff --git a/crates/erp-health/src/service/patient_service/mod.rs b/crates/erp-health/src/service/patient_service/mod.rs index bd4abec..4fcec37 100644 --- a/crates/erp-health/src/service/patient_service/mod.rs +++ b/crates/erp-health/src/service/patient_service/mod.rs @@ -14,7 +14,7 @@ mod tag; // 从各子模块重新导出所有公开函数,保持 handler 层调用路径不变 pub use crud::{ batch_import_patients, bind_by_phone, create_patient, delete_patient, get_patient, - list_patients, update_patient, + list_patients, list_summaries, update_patient, }; pub use relation::{ assign_doctor, create_family_member, delete_family_member, get_health_summary,