- DoctorDashboard 增加 pending_dialysis_review/pending_lab_review/today_appointments - 医护小程序首页增加「健康审核」区块(待审透析/化验/今日预约) - 患者端文章列表增加分类 tabs 横向滚动筛选 - article service 增加 listCategories + category_id 筛选
257 lines
8.0 KiB
Rust
257 lines
8.0 KiB
Rust
use axum::Extension;
|
|
use axum::extract::{FromRef, Json, Path, Query, State};
|
|
use sea_orm::{ColumnTrait, EntityTrait, QueryFilter};
|
|
use serde::Deserialize;
|
|
use utoipa::IntoParams;
|
|
use uuid::Uuid;
|
|
|
|
use erp_core::error::AppError;
|
|
use erp_core::rbac::require_permission;
|
|
use erp_core::types::{ApiResponse, PaginatedResponse, TenantContext};
|
|
|
|
use crate::dto::consultation_dto::*;
|
|
use crate::service::consultation_service;
|
|
use crate::state::HealthState;
|
|
|
|
#[derive(Debug, Deserialize, IntoParams)]
|
|
pub struct SessionListParams {
|
|
pub page: Option<u64>,
|
|
pub page_size: Option<u64>,
|
|
pub status: Option<String>,
|
|
pub patient_id: Option<Uuid>,
|
|
pub doctor_id: Option<Uuid>,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, IntoParams)]
|
|
pub struct MessageListParams {
|
|
pub page: Option<u64>,
|
|
pub page_size: Option<u64>,
|
|
pub after_id: Option<Uuid>,
|
|
}
|
|
|
|
#[derive(Debug, serde::Deserialize, utoipa::ToSchema)]
|
|
pub struct CloseSessionReq {
|
|
pub version: i32,
|
|
}
|
|
|
|
#[derive(Debug, serde::Deserialize, utoipa::ToSchema)]
|
|
pub struct CreateConsultationMessageReq {
|
|
pub session_id: Uuid,
|
|
pub content_type: Option<String>,
|
|
pub content: String,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, IntoParams)]
|
|
pub struct ExportSessionsParams {
|
|
pub status: Option<String>,
|
|
pub patient_id: Option<Uuid>,
|
|
pub doctor_id: Option<Uuid>,
|
|
pub page: Option<u64>,
|
|
pub page_size: Option<u64>,
|
|
}
|
|
|
|
pub async fn create_session<S>(
|
|
State(state): State<HealthState>,
|
|
Extension(ctx): Extension<TenantContext>,
|
|
Json(req): Json<CreateSessionReq>,
|
|
) -> Result<Json<ApiResponse<SessionResp>>, AppError>
|
|
where
|
|
HealthState: FromRef<S>,
|
|
S: Clone + Send + Sync + 'static,
|
|
{
|
|
require_permission(&ctx, "health.consultation.manage")?;
|
|
let result = consultation_service::create_session(
|
|
&state, ctx.tenant_id, Some(ctx.user_id), req,
|
|
)
|
|
.await?;
|
|
Ok(Json(ApiResponse::ok(result)))
|
|
}
|
|
|
|
pub async fn list_sessions<S>(
|
|
State(state): State<HealthState>,
|
|
Extension(ctx): Extension<TenantContext>,
|
|
Query(params): Query<SessionListParams>,
|
|
) -> Result<Json<ApiResponse<PaginatedResponse<SessionResp>>>, AppError>
|
|
where
|
|
HealthState: FromRef<S>,
|
|
S: Clone + Send + Sync + 'static,
|
|
{
|
|
require_permission(&ctx, "health.consultation.list")?;
|
|
let page = params.page.unwrap_or(1);
|
|
let page_size = params.page_size.unwrap_or(20);
|
|
let result = consultation_service::list_sessions(
|
|
&state, ctx.tenant_id, page, page_size, params.status, params.patient_id,
|
|
params.doctor_id,
|
|
)
|
|
.await?;
|
|
Ok(Json(ApiResponse::ok(result)))
|
|
}
|
|
|
|
pub async fn get_session<S>(
|
|
State(state): State<HealthState>,
|
|
Extension(ctx): Extension<TenantContext>,
|
|
Path(id): Path<Uuid>,
|
|
) -> Result<Json<ApiResponse<SessionResp>>, AppError>
|
|
where
|
|
HealthState: FromRef<S>,
|
|
S: Clone + Send + Sync + 'static,
|
|
{
|
|
require_permission(&ctx, "health.consultation.list")?;
|
|
let result = consultation_service::get_session(&state, ctx.tenant_id, id).await?;
|
|
Ok(Json(ApiResponse::ok(result)))
|
|
}
|
|
|
|
pub async fn list_messages<S>(
|
|
State(state): State<HealthState>,
|
|
Extension(ctx): Extension<TenantContext>,
|
|
Path(session_id): Path<Uuid>,
|
|
Query(params): Query<MessageListParams>,
|
|
) -> Result<Json<ApiResponse<PaginatedResponse<MessageResp>>>, AppError>
|
|
where
|
|
HealthState: FromRef<S>,
|
|
S: Clone + Send + Sync + 'static,
|
|
{
|
|
require_permission(&ctx, "health.consultation.list")?;
|
|
let page = params.page.unwrap_or(1);
|
|
let page_size = params.page_size.unwrap_or(20);
|
|
let result = consultation_service::list_messages(
|
|
&state, ctx.tenant_id, session_id, page, page_size, params.after_id,
|
|
)
|
|
.await?;
|
|
Ok(Json(ApiResponse::ok(result)))
|
|
}
|
|
|
|
pub async fn close_session<S>(
|
|
State(state): State<HealthState>,
|
|
Extension(ctx): Extension<TenantContext>,
|
|
Path(id): Path<Uuid>,
|
|
Json(req): Json<CloseSessionReq>,
|
|
) -> Result<Json<ApiResponse<SessionResp>>, AppError>
|
|
where
|
|
HealthState: FromRef<S>,
|
|
S: Clone + Send + Sync + 'static,
|
|
{
|
|
require_permission(&ctx, "health.consultation.manage")?;
|
|
let result = consultation_service::close_session(
|
|
&state, ctx.tenant_id, id, Some(ctx.user_id), req.version,
|
|
)
|
|
.await?;
|
|
Ok(Json(ApiResponse::ok(result)))
|
|
}
|
|
|
|
pub async fn create_message<S>(
|
|
State(state): State<HealthState>,
|
|
Extension(ctx): Extension<TenantContext>,
|
|
Json(req): Json<CreateConsultationMessageReq>,
|
|
) -> Result<Json<ApiResponse<MessageResp>>, AppError>
|
|
where
|
|
HealthState: FromRef<S>,
|
|
S: Clone + Send + Sync + 'static,
|
|
{
|
|
require_permission(&ctx, "health.consultation.manage")?;
|
|
let is_doctor = crate::entity::doctor_profile::Entity::find()
|
|
.filter(crate::entity::doctor_profile::Column::UserId.eq(ctx.user_id))
|
|
.filter(crate::entity::doctor_profile::Column::TenantId.eq(ctx.tenant_id))
|
|
.filter(crate::entity::doctor_profile::Column::DeletedAt.is_null())
|
|
.one(&state.db)
|
|
.await
|
|
.map_err(|e| AppError::Internal(e.to_string()))?
|
|
.is_some();
|
|
let mut msg_req = CreateMessageReq {
|
|
session_id: req.session_id,
|
|
sender_id: ctx.user_id,
|
|
sender_role: if is_doctor { "doctor" } else { "patient" }.to_string(),
|
|
content_type: req.content_type,
|
|
content: req.content,
|
|
};
|
|
msg_req.sanitize();
|
|
let result = consultation_service::create_message(
|
|
&state, ctx.tenant_id, Some(ctx.user_id), msg_req,
|
|
)
|
|
.await?;
|
|
Ok(Json(ApiResponse::ok(result)))
|
|
}
|
|
|
|
pub async fn export_sessions<S>(
|
|
State(state): State<HealthState>,
|
|
Extension(ctx): Extension<TenantContext>,
|
|
Query(params): Query<ExportSessionsParams>,
|
|
) -> Result<Json<ApiResponse<PaginatedResponse<SessionResp>>>, AppError>
|
|
where
|
|
HealthState: FromRef<S>,
|
|
S: Clone + Send + Sync + 'static,
|
|
{
|
|
require_permission(&ctx, "health.consultation.list")?;
|
|
let result = consultation_service::export_sessions(
|
|
&state, ctx.tenant_id, params.status, params.patient_id, params.doctor_id,
|
|
params.page, params.page_size,
|
|
)
|
|
.await?;
|
|
Ok(Json(ApiResponse::ok(result)))
|
|
}
|
|
|
|
/// 标记会话消息为已读。
|
|
#[utoipa::path(
|
|
put,
|
|
path = "/consultation-sessions/{id}/read",
|
|
responses(
|
|
(status = 200, description = "标记成功"),
|
|
(status = 404, description = "会话不存在"),
|
|
),
|
|
tag = "咨询管理",
|
|
)]
|
|
pub async fn mark_session_read<S>(
|
|
State(state): State<HealthState>,
|
|
Extension(ctx): Extension<TenantContext>,
|
|
Path(id): Path<Uuid>,
|
|
) -> Result<Json<ApiResponse<()>>, AppError>
|
|
where
|
|
HealthState: FromRef<S>,
|
|
S: Clone + Send + Sync + 'static,
|
|
{
|
|
require_permission(&ctx, "health.consultation.manage")?;
|
|
let is_doctor = crate::entity::doctor_profile::Entity::find()
|
|
.filter(crate::entity::doctor_profile::Column::UserId.eq(ctx.user_id))
|
|
.filter(crate::entity::doctor_profile::Column::TenantId.eq(ctx.tenant_id))
|
|
.filter(crate::entity::doctor_profile::Column::DeletedAt.is_null())
|
|
.one(&state.db)
|
|
.await
|
|
.map_err(|e| AppError::Internal(e.to_string()))?
|
|
.is_some();
|
|
let role = if is_doctor { "doctor" } else { "patient" };
|
|
consultation_service::mark_session_read(
|
|
&state, ctx.tenant_id, id, ctx.user_id, role,
|
|
)
|
|
.await?;
|
|
Ok(Json(ApiResponse::ok(())))
|
|
}
|
|
|
|
/// 获取当前医生的仪表盘数据。
|
|
#[utoipa::path(
|
|
get,
|
|
path = "/doctor/dashboard",
|
|
responses(
|
|
(status = 200, description = "仪表盘数据"),
|
|
),
|
|
tag = "医护端",
|
|
)]
|
|
pub async fn get_doctor_dashboard<S>(
|
|
State(state): State<HealthState>,
|
|
Extension(ctx): Extension<TenantContext>,
|
|
) -> Result<Json<ApiResponse<consultation_service::DoctorDashboard>>, AppError>
|
|
where
|
|
HealthState: FromRef<S>,
|
|
S: Clone + Send + Sync + 'static,
|
|
{
|
|
require_permission(&ctx, "health.consultation.list")?;
|
|
let mut result = consultation_service::get_doctor_dashboard(
|
|
&state, ctx.tenant_id, ctx.user_id,
|
|
)
|
|
.await?;
|
|
consultation_service::enrich_doctor_dashboard_health(
|
|
&state, ctx.tenant_id, ctx.user_id, &mut result,
|
|
)
|
|
.await?;
|
|
Ok(Json(ApiResponse::ok(result)))
|
|
}
|