Phase 4 — Dead-letter 重试 + 内容推送 + 安全加固: - erp-core: retry_dead_letters() 定时重试 + PII payload 脱敏 - erp-core: audit_service 哈希链定时验证 + 写入失败告警 - erp-health: article.published 消费者匹配 patient_tag 推送消息 - erp-health: care_plan 事件消费者 (激活通知 + 完成积分) Phase 5 — 患者批量操作 + 咨询增强 + 护理事件: - patient: batch_import_patients + bind_by_phone + refer_patient - consultation: rate_session 满意度评价 (rating + feedback) - consent: patient_sign_consent 患者端签署 - validation: source 枚举 (7值) + relationship 枚举 (7值) + 12 单元测试 Phase 6 — 咨询文件上传 + AI 引用标注: - consultation_message: media_id 附件上传端点 - ai_suggestion: references JSONB + [ref:id] 格式引用标注 - AI system prompt 增加引用指令 + output_parser 提取逻辑 迁移: 000161 (media_id + references) + 000162 (rating + feedback) 集成测试: consultation/follow_up/pii_encryption 新字段同步修复 讨论文档: 2026-05-20-business-process-brainstorm.md (10域审核报告)
125 lines
4.0 KiB
Rust
125 lines
4.0 KiB
Rust
use axum::Extension;
|
|
use axum::extract::{FromRef, Json, Path, Query, State};
|
|
use erp_core::error::AppError;
|
|
use erp_core::rbac::require_permission;
|
|
use erp_core::types::{ApiResponse, PaginatedResponse, TenantContext};
|
|
use serde::Deserialize;
|
|
use validator::Validate;
|
|
|
|
use crate::dto::consent_dto::*;
|
|
use crate::service::consent_service;
|
|
use crate::state::HealthState;
|
|
|
|
#[derive(Debug, Deserialize, utoipa::IntoParams)]
|
|
pub struct ConsentListParams {
|
|
pub page: Option<u64>,
|
|
pub page_size: Option<u64>,
|
|
}
|
|
|
|
#[utoipa::path(
|
|
get,
|
|
path = "/health/patients/{patient_id}/consents",
|
|
responses((status = 200, description = "知情同意列表")),
|
|
tag = "知情同意",
|
|
security(("bearer_auth" = [])),
|
|
)]
|
|
pub async fn list_consents<S>(
|
|
State(state): State<HealthState>,
|
|
Extension(ctx): Extension<TenantContext>,
|
|
Path(patient_id): Path<uuid::Uuid>,
|
|
Query(params): Query<ConsentListParams>,
|
|
) -> Result<Json<ApiResponse<PaginatedResponse<ConsentResp>>>, AppError>
|
|
where
|
|
HealthState: FromRef<S>,
|
|
S: Clone + Send + Sync + 'static,
|
|
{
|
|
require_permission(&ctx, "health.consent.list")?;
|
|
let page = params.page.unwrap_or(1);
|
|
let page_size = params.page_size.unwrap_or(20);
|
|
let result =
|
|
consent_service::list_consents(&state, ctx.tenant_id, patient_id, page, page_size).await?;
|
|
Ok(Json(ApiResponse::ok(result)))
|
|
}
|
|
|
|
#[utoipa::path(
|
|
post,
|
|
path = "/health/consents",
|
|
responses((status = 200, description = "授权成功")),
|
|
tag = "知情同意",
|
|
security(("bearer_auth" = [])),
|
|
)]
|
|
pub async fn grant_consent<S>(
|
|
State(state): State<HealthState>,
|
|
Extension(ctx): Extension<TenantContext>,
|
|
Json(req): Json<CreateConsentReq>,
|
|
) -> Result<Json<ApiResponse<ConsentResp>>, AppError>
|
|
where
|
|
HealthState: FromRef<S>,
|
|
S: Clone + Send + Sync + 'static,
|
|
{
|
|
require_permission(&ctx, "health.consent.manage")?;
|
|
let mut req = req;
|
|
req.sanitize();
|
|
let result =
|
|
consent_service::grant_consent(&state, ctx.tenant_id, Some(ctx.user_id), req).await?;
|
|
Ok(Json(ApiResponse::ok(result)))
|
|
}
|
|
|
|
#[utoipa::path(
|
|
put,
|
|
path = "/health/consents/{consent_id}/revoke",
|
|
responses((status = 200, description = "撤销成功")),
|
|
tag = "知情同意",
|
|
security(("bearer_auth" = [])),
|
|
)]
|
|
pub async fn revoke_consent<S>(
|
|
State(state): State<HealthState>,
|
|
Extension(ctx): Extension<TenantContext>,
|
|
Path(consent_id): Path<uuid::Uuid>,
|
|
Json(req): Json<RevokeConsentReq>,
|
|
) -> Result<Json<ApiResponse<ConsentResp>>, AppError>
|
|
where
|
|
HealthState: FromRef<S>,
|
|
S: Clone + Send + Sync + 'static,
|
|
{
|
|
require_permission(&ctx, "health.consent.manage")?;
|
|
let mut req = req;
|
|
req.sanitize();
|
|
let result =
|
|
consent_service::revoke_consent(&state, ctx.tenant_id, consent_id, Some(ctx.user_id), req)
|
|
.await?;
|
|
Ok(Json(ApiResponse::ok(result)))
|
|
}
|
|
|
|
/// 患者端签署知情同意 — 验证 consent 归属当前患者后更新状态为 granted
|
|
#[utoipa::path(
|
|
post,
|
|
path = "/health/consents/{consent_id}/patient-sign",
|
|
request_body = PatientSignConsentReq,
|
|
responses(
|
|
(status = 200, description = "签署成功"),
|
|
(status = 400, description = "状态不允许签署或不属于该患者"),
|
|
(status = 404, description = "知情同意记录不存在"),
|
|
),
|
|
tag = "知情同意",
|
|
security(("bearer_auth" = [])),
|
|
)]
|
|
pub async fn patient_sign_consent<S>(
|
|
State(state): State<HealthState>,
|
|
Extension(ctx): Extension<TenantContext>,
|
|
Path(consent_id): Path<uuid::Uuid>,
|
|
Json(req): Json<crate::dto::consent_dto::PatientSignConsentReq>,
|
|
) -> Result<Json<ApiResponse<crate::dto::consent_dto::ConsentResp>>, AppError>
|
|
where
|
|
HealthState: FromRef<S>,
|
|
S: Clone + Send + Sync + 'static,
|
|
{
|
|
// 患者自己签署,只需认证,不需要特殊权限
|
|
req.validate()
|
|
.map_err(|e| AppError::Validation(e.to_string()))?;
|
|
let result =
|
|
consent_service::patient_sign_consent(&state, ctx.tenant_id, ctx.user_id, consent_id, req)
|
|
.await?;
|
|
Ok(Json(ApiResponse::ok(result)))
|
|
}
|