diff --git a/crates/erp-health/src/service/action_inbox_service.rs b/crates/erp-health/src/service/action_inbox_service.rs index b078873..e5793ce 100644 --- a/crates/erp-health/src/service/action_inbox_service.rs +++ b/crates/erp-health/src/service/action_inbox_service.rs @@ -2,6 +2,7 @@ use chrono::{DateTime, Utc}; use erp_core::types::PaginatedResponse; use sea_orm::{DatabaseBackend, DatabaseConnection, FromQueryResult, Statement}; use serde::{Deserialize, Serialize}; +use tracing; use uuid::Uuid; use crate::error::HealthError; @@ -212,6 +213,7 @@ pub async fn list_action_items( tenant_id: Uuid, query: &ActionInboxQuery, ) -> Result, HealthError> { + tracing::info!(tenant_id = %tenant_id, status = ?query.status, action_type = ?query.action_type, "列出待办事项"); let page = query.page.unwrap_or(1).max(1); let page_size = query.page_size.unwrap_or(20).min(100); let offset = (page - 1) * page_size; @@ -324,16 +326,23 @@ pub async fn list_action_items( ) .all(db) .await - .map_err(|e| HealthError::DbError(e.to_string()))?; + .map_err(|e| { + tracing::error!(tenant_id = %tenant_id, error = %e, "查询待办事项数据失败"); + HealthError::DbError(e.to_string()) + })?; let count_row: Option = FromQueryResult::find_by_statement( Statement::from_sql_and_values(DatabaseBackend::Postgres, count_sql, [tenant_id.into()]), ) .one(db) .await - .map_err(|e| HealthError::DbError(e.to_string()))?; + .map_err(|e| { + tracing::error!(tenant_id = %tenant_id, error = %e, "查询待办事项总数失败"); + HealthError::DbError(e.to_string()) + })?; let total = count_row.map(|r| r.cnt).unwrap_or(0) as u64; + tracing::debug!(tenant_id = %tenant_id, total = total, "待办事项查询结果数量"); let items: Vec = rows .into_iter() @@ -378,13 +387,19 @@ pub async fn get_action_thread( tenant_id: Uuid, source_ref: &str, ) -> Result, HealthError> { + tracing::info!(tenant_id = %tenant_id, source_ref = %source_ref, "获取待办事项详情"); // 解析 "action_type:uuid" 格式 let (action_type_str, uuid_str) = source_ref .find(':') .map(|pos| (&source_ref[..pos], &source_ref[pos + 1..])) - .ok_or_else(|| HealthError::Validation("无效的 source_ref 格式".into()))?; - let uuid = Uuid::parse_str(uuid_str) - .map_err(|e| HealthError::Validation(format!("无效的 UUID: {e}")))?; + .ok_or_else(|| { + tracing::error!(source_ref = %source_ref, "无效的 source_ref 格式"); + HealthError::Validation("无效的 source_ref 格式".into()) + })?; + let uuid = Uuid::parse_str(uuid_str).map_err(|e| { + tracing::error!(uuid_str = %uuid_str, error = %e, "无效的 UUID"); + HealthError::Validation(format!("无效的 UUID: {e}")) + })?; match action_type_str { "ai_suggestion" => get_ai_suggestion_thread(db, tenant_id, uuid).await, @@ -775,6 +790,7 @@ pub async fn get_workbench_stats( db: &DatabaseConnection, tenant_id: Uuid, ) -> Result { + tracing::info!(tenant_id = %tenant_id, "获取工作台统计"); let ai_pending: i64 = FromQueryResult::find_by_statement( Statement::from_sql_and_values( DatabaseBackend::Postgres, @@ -784,7 +800,10 @@ pub async fn get_workbench_stats( ) .one(db) .await - .map_err(|e| HealthError::DbError(e.to_string()))? + .map_err(|e| { + tracing::error!(tenant_id = %tenant_id, error = %e, "查询AI建议待办数失败"); + HealthError::DbError(e.to_string()) + })? .map(|r: CountRow| r.cnt) .unwrap_or(0); @@ -797,7 +816,10 @@ pub async fn get_workbench_stats( ) .one(db) .await - .map_err(|e| HealthError::DbError(e.to_string()))? + .map_err(|e| { + tracing::error!(tenant_id = %tenant_id, error = %e, "查询紧急告警数失败"); + HealthError::DbError(e.to_string()) + })? .map(|r: CountRow| r.cnt) .unwrap_or(0); @@ -810,11 +832,15 @@ pub async fn get_workbench_stats( ) .one(db) .await - .map_err(|e| HealthError::DbError(e.to_string()))? + .map_err(|e| { + tracing::error!(tenant_id = %tenant_id, error = %e, "查询到期随访数失败"); + HealthError::DbError(e.to_string()) + })? .map(|r: CountRow| r.cnt) .unwrap_or(0); let total_pending = (ai_pending + urgent_alerts + followup_due) as u64; + tracing::debug!(tenant_id = %tenant_id, total_pending = total_pending, ai_pending = ai_pending, urgent_alerts = urgent_alerts, followup_due = followup_due, "工作台统计数据"); Ok(WorkbenchStats { total_pending, @@ -829,6 +855,7 @@ pub async fn get_team_overview( db: &DatabaseConnection, tenant_id: Uuid, ) -> Result { + tracing::info!(tenant_id = %tenant_id, "获取团队概览"); // 成员统计 #[derive(Debug, FromQueryResult)] struct MemberRow { diff --git a/crates/erp-health/src/service/health_data_service.rs b/crates/erp-health/src/service/health_data_service.rs index f19eb50..eed28d7 100644 --- a/crates/erp-health/src/service/health_data_service.rs +++ b/crates/erp-health/src/service/health_data_service.rs @@ -30,6 +30,7 @@ pub async fn list_vital_signs( page: u64, page_size: u64, ) -> HealthResult> { + tracing::info!(tenant_id = %tenant_id, patient_id = %patient_id, page, page_size, "查询体征记录列表"); let limit = page_size.min(100); let offset = page.saturating_sub(1) * limit; @@ -46,6 +47,7 @@ pub async fn list_vital_signs( .all(&state.db) .await?; + tracing::debug!(total, "体征记录查询结果数量"); let total_pages = total.div_ceil(limit.max(1)); let data: Vec = models.into_iter().map(|m| VitalSignsResp { id: m.id, @@ -80,6 +82,7 @@ pub async fn create_vital_signs( operator_id: Option, req: CreateVitalSignsReq, ) -> HealthResult { + tracing::info!(tenant_id = %tenant_id, patient_id = %patient_id, "创建体征记录"); // 校验患者存在 patient::Entity::find() .filter(patient::Column::Id.eq(patient_id)) @@ -87,7 +90,10 @@ pub async fn create_vital_signs( .filter(patient::Column::DeletedAt.is_null()) .one(&state.db) .await? - .ok_or(HealthError::PatientNotFound)?; + .ok_or_else(|| { + tracing::error!(patient_id = %patient_id, tenant_id = %tenant_id, "创建体征记录失败:患者不存在"); + HealthError::PatientNotFound + })?; let now = Utc::now(); let alert_req = req.clone(); @@ -118,6 +124,7 @@ pub async fn create_vital_signs( version: Set(1), }; let m = active.insert(&state.db).await?; + tracing::info!(id = %m.id, tenant_id = %tenant_id, patient_id = %patient_id, "体征记录创建成功"); // 数据持久化成功后再触发危急值检测 check_vital_signs_alert(state, tenant_id, patient_id, operator_id, alert_req).await; @@ -154,6 +161,7 @@ pub async fn update_vital_signs( req: UpdateVitalSignsReq, expected_version: i32, ) -> HealthResult { + tracing::info!(tenant_id = %tenant_id, patient_id = %patient_id, vital_signs_id = %vital_signs_id, expected_version, "更新体征记录"); let model = vital_signs::Entity::find() .filter(vital_signs::Column::Id.eq(vital_signs_id)) .filter(vital_signs::Column::PatientId.eq(patient_id)) @@ -161,9 +169,14 @@ pub async fn update_vital_signs( .filter(vital_signs::Column::DeletedAt.is_null()) .one(&state.db) .await? - .ok_or(HealthError::VitalSignsNotFound)?; - let next_ver = check_version(expected_version, model.version) - .map_err(|_| HealthError::VersionMismatch)?; + .ok_or_else(|| { + tracing::error!(vital_signs_id = %vital_signs_id, tenant_id = %tenant_id, "更新体征记录失败:记录不存在"); + HealthError::VitalSignsNotFound + })?; + let next_ver = check_version(expected_version, model.version).map_err(|e| { + tracing::error!(vital_signs_id = %vital_signs_id, expected_version, db_version = model.version, "更新体征记录失败:版本冲突"); + e + })?; // 记录变更前的关键体征值 let old_values = serde_json::json!({ @@ -198,6 +211,7 @@ pub async fn update_vital_signs( active.version = Set(next_ver); let m = active.update(&state.db).await?; + tracing::info!(id = %m.id, tenant_id = %tenant_id, version = m.version, "体征记录更新成功"); // 变更后快照 let new_values = serde_json::json!({ @@ -262,16 +276,22 @@ pub async fn delete_vital_signs( operator_id: Option, expected_version: i32, ) -> HealthResult<()> { + tracing::info!(tenant_id = %tenant_id, vital_signs_id = %vital_signs_id, expected_version, "删除体征记录"); let model = vital_signs::Entity::find() .filter(vital_signs::Column::Id.eq(vital_signs_id)) .filter(vital_signs::Column::TenantId.eq(tenant_id)) .filter(vital_signs::Column::DeletedAt.is_null()) .one(&state.db) .await? - .ok_or(HealthError::VitalSignsNotFound)?; + .ok_or_else(|| { + tracing::error!(vital_signs_id = %vital_signs_id, tenant_id = %tenant_id, "删除体征记录失败:记录不存在"); + HealthError::VitalSignsNotFound + })?; - let next_ver = check_version(expected_version, model.version) - .map_err(|_| HealthError::VersionMismatch)?; + let next_ver = check_version(expected_version, model.version).map_err(|e| { + tracing::error!(vital_signs_id = %vital_signs_id, expected_version, db_version = model.version, "删除体征记录失败:版本冲突"); + e + })?; let mut active: vital_signs::ActiveModel = model.into(); active.deleted_at = Set(Some(Utc::now())); @@ -279,6 +299,7 @@ pub async fn delete_vital_signs( active.updated_by = Set(operator_id); active.version = Set(next_ver); active.update(&state.db).await?; + tracing::info!(vital_signs_id = %vital_signs_id, tenant_id = %tenant_id, "体征记录删除成功"); audit_service::record( AuditLog::new(tenant_id, operator_id, "vital_signs.deleted", "vital_signs") @@ -300,6 +321,7 @@ pub async fn list_lab_reports( page: u64, page_size: u64, ) -> HealthResult> { + tracing::info!(tenant_id = %tenant_id, patient_id = %patient_id, page, page_size, "查询化验报告列表"); let limit = page_size.min(100); let offset = page.saturating_sub(1) * limit; @@ -309,6 +331,7 @@ pub async fn list_lab_reports( .filter(lab_report::Column::DeletedAt.is_null()); let total = query.clone().count(&state.db).await?; + tracing::debug!(total, "化验报告查询结果数量"); let models = query .order_by_desc(lab_report::Column::ReportDate) .offset(offset) @@ -350,6 +373,7 @@ pub async fn create_lab_report( operator_id: Option, req: CreateLabReportReq, ) -> HealthResult { + tracing::info!(tenant_id = %tenant_id, patient_id = %patient_id, "创建化验报告"); // 校验患者存在 patient::Entity::find() .filter(patient::Column::Id.eq(patient_id)) @@ -357,7 +381,10 @@ pub async fn create_lab_report( .filter(patient::Column::DeletedAt.is_null()) .one(&state.db) .await? - .ok_or(HealthError::PatientNotFound)?; + .ok_or_else(|| { + tracing::error!(patient_id = %patient_id, tenant_id = %tenant_id, "创建化验报告失败:患者不存在"); + HealthError::PatientNotFound + })?; let kek = state.crypto.kek();