fix: 修复测试发现的 7 个问题 + 全 workspace clippy 清零
Some checks failed
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled

功能修复:
1. 患者创建空名称验证:后端添加 name.trim().is_empty() 检查
2. 仪表盘统计容错:单个查询失败返回零值而非 500
3. FHIR 路由修复:从 /fhir 移到 /api/v1/fhir 保持一致
4. 冻结模块后端中间件:新增 frozen_module_middleware 拦截冻结路径
5. 积分端点权限码:health.health-data.list → health.points.list
6. 角色权限迁移:护士补充 devices.list,运营补充 points.list/manage
7. 测试结果文档:R01-R05 角色测试 + T00/T10 结果归档

Clippy 全 workspace 清零(14→0 errors):
- erp-core: 修复 empty doc line、collapsible if、redundant closure 等 9 处
- erp-health: 修复 too_many_arguments、unused var、unnecessary parens 等 58 处
- erp-ai: 修复 dead_code、unused import 等 11 处
- erp-plugin: 修复 too_many_arguments、wildcard pattern 等 11 处
- erp-server-migration: 修复 enum_variant_names 5 处
- erp-auth/config/workflow/message: 各 1-3 处

工程改进:
- lint-staged 配置迁移到 .lintstagedrc.js(函数式避免文件列表传给 clippy)
- cargo fmt 统一格式化
This commit is contained in:
iven
2026-05-07 23:43:14 +08:00
parent 786f57c151
commit 6d5a711d2c
323 changed files with 15662 additions and 6603 deletions

View File

@@ -1,5 +1,5 @@
use axum::extract::{FromRef, Json, Path, Query, State};
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};
@@ -82,8 +82,7 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.action-inbox.team")?;
let result =
action_inbox_service::get_team_overview(&state.db, ctx.tenant_id).await?;
let result = action_inbox_service::get_team_overview(&state.db, ctx.tenant_id).await?;
Ok(Json(ApiResponse::ok(result)))
}

View File

@@ -1,6 +1,6 @@
use axum::Extension;
use axum::extract::{FromRef, Path, Query, State};
use axum::response::IntoResponse;
use axum::Extension;
use serde::Deserialize;
use utoipa::IntoParams;
use uuid::Uuid;
@@ -36,9 +36,15 @@ where
let page_size = query.page_size.unwrap_or(20);
let (items, total) = alert_service::list_alerts(
&state, ctx.tenant_id, query.patient_id, query.doctor_id, query.status.as_deref(),
page, page_size,
).await?;
&state,
ctx.tenant_id,
query.patient_id,
query.doctor_id,
query.status.as_deref(),
page,
page_size,
)
.await?;
Ok(axum::Json(ApiResponse::ok(PaginatedResponse {
data: items,
@@ -74,9 +80,9 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.alerts.manage")?;
let alert = alert_service::acknowledge_alert(
&state, ctx.tenant_id, id, ctx.user_id, body.version,
).await?;
let alert =
alert_service::acknowledge_alert(&state, ctx.tenant_id, id, ctx.user_id, body.version)
.await?;
Ok(axum::Json(ApiResponse::ok(alert)))
}
@@ -91,9 +97,8 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.alerts.manage")?;
let alert = alert_service::dismiss_alert(
&state, ctx.tenant_id, id, ctx.user_id, body.version,
).await?;
let alert =
alert_service::dismiss_alert(&state, ctx.tenant_id, id, ctx.user_id, body.version).await?;
Ok(axum::Json(ApiResponse::ok(alert)))
}
@@ -108,8 +113,6 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.alerts.manage")?;
let alert = alert_service::resolve_alert(
&state, ctx.tenant_id, id, body.version,
).await?;
let alert = alert_service::resolve_alert(&state, ctx.tenant_id, id, body.version).await?;
Ok(axum::Json(ApiResponse::ok(alert)))
}

View File

@@ -1,6 +1,6 @@
use axum::Extension;
use axum::extract::{FromRef, Path, Query, State};
use axum::response::IntoResponse;
use axum::Extension;
use serde::Deserialize;
use utoipa::IntoParams;
use uuid::Uuid;
@@ -39,8 +39,13 @@ where
let page_size = query.page_size.unwrap_or(20);
let (items, total) = alert_rule_service::list_rules(
&state, ctx.tenant_id, query.device_type.as_deref(), page, page_size,
).await?;
&state,
ctx.tenant_id,
query.device_type.as_deref(),
page,
page_size,
)
.await?;
Ok(axum::Json(ApiResponse::ok(PaginatedResponse {
data: items,
@@ -62,9 +67,7 @@ where
{
require_permission(&ctx, "health.alert-rules.manage")?;
body.sanitize();
let rule = alert_rule_service::create_rule(
&state, ctx.tenant_id, ctx.user_id, body,
).await?;
let rule = alert_rule_service::create_rule(&state, ctx.tenant_id, ctx.user_id, body).await?;
Ok(axum::Json(ApiResponse::ok(rule)))
}
@@ -80,9 +83,8 @@ where
{
require_permission(&ctx, "health.alert-rules.manage")?;
body.sanitize();
let rule = alert_rule_service::update_rule(
&state, ctx.tenant_id, id, ctx.user_id, body,
).await?;
let rule =
alert_rule_service::update_rule(&state, ctx.tenant_id, id, ctx.user_id, body).await?;
Ok(axum::Json(ApiResponse::ok(rule)))
}
@@ -97,8 +99,6 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.alert-rules.manage")?;
let rule = alert_rule_service::deactivate_rule(
&state, ctx.tenant_id, id, body.version,
).await?;
let rule = alert_rule_service::deactivate_rule(&state, ctx.tenant_id, id, body.version).await?;
Ok(axum::Json(ApiResponse::ok(rule)))
}

View File

@@ -64,8 +64,14 @@ where
let page = params.page.unwrap_or(1);
let page_size = params.page_size.unwrap_or(20);
let result = appointment_service::list_appointments(
&state, ctx.tenant_id, page, page_size, params.status, params.patient_id,
params.doctor_id, params.date,
&state,
ctx.tenant_id,
page,
page_size,
params.status,
params.patient_id,
params.doctor_id,
params.date,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
@@ -83,10 +89,9 @@ where
require_permission(&ctx, "health.appointment.manage")?;
let mut req = req;
req.sanitize();
let result = appointment_service::create_appointment(
&state, ctx.tenant_id, Some(ctx.user_id), req,
)
.await?;
let result =
appointment_service::create_appointment(&state, ctx.tenant_id, Some(ctx.user_id), req)
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -121,7 +126,12 @@ where
};
update_req.sanitize();
let result = appointment_service::update_appointment_status(
&state, ctx.tenant_id, id, Some(ctx.user_id), update_req, req.version,
&state,
ctx.tenant_id,
id,
Some(ctx.user_id),
update_req,
req.version,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
@@ -140,7 +150,12 @@ where
let page = params.page.unwrap_or(1);
let page_size = params.page_size.unwrap_or(20);
let result = appointment_service::list_schedules(
&state, ctx.tenant_id, page, page_size, params.doctor_id, params.date,
&state,
ctx.tenant_id,
page,
page_size,
params.doctor_id,
params.date,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
@@ -156,10 +171,8 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.appointment.manage")?;
let result = appointment_service::create_schedule(
&state, ctx.tenant_id, Some(ctx.user_id), req,
)
.await?;
let result =
appointment_service::create_schedule(&state, ctx.tenant_id, Some(ctx.user_id), req).await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -175,7 +188,12 @@ where
{
require_permission(&ctx, "health.appointment.manage")?;
let result = appointment_service::update_schedule(
&state, ctx.tenant_id, id, Some(ctx.user_id), req.data, req.version,
&state,
ctx.tenant_id,
id,
Some(ctx.user_id),
req.data,
req.version,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
@@ -192,7 +210,11 @@ where
{
require_permission(&ctx, "health.appointment.list")?;
let result = appointment_service::calendar_view(
&state, ctx.tenant_id, params.start_date, params.end_date, params.doctor_id,
&state,
ctx.tenant_id,
params.start_date,
params.end_date,
params.doctor_id,
)
.await?;
Ok(Json(ApiResponse::ok(result)))

View File

@@ -34,9 +34,9 @@ where
{
require_permission(&ctx, "health.articles.manage")?;
req.sanitize();
let result = article_category_service::create_category(
&state, ctx.tenant_id, Some(ctx.user_id), req.0,
).await?;
let result =
article_category_service::create_category(&state, ctx.tenant_id, Some(ctx.user_id), req.0)
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -53,8 +53,13 @@ where
require_permission(&ctx, "health.articles.manage")?;
req.sanitize();
let result = article_category_service::update_category(
&state, ctx.tenant_id, id, Some(ctx.user_id), req.0,
).await?;
&state,
ctx.tenant_id,
id,
Some(ctx.user_id),
req.0,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -75,7 +80,12 @@ where
{
require_permission(&ctx, "health.articles.manage")?;
article_category_service::delete_category(
&state, ctx.tenant_id, id, Some(ctx.user_id), req.version,
).await?;
&state,
ctx.tenant_id,
id,
Some(ctx.user_id),
req.version,
)
.await?;
Ok(Json(ApiResponse::ok(())))
}

View File

@@ -5,7 +5,10 @@ use erp_core::error::AppError;
use erp_core::rbac::{require_any_permission, require_permission};
use erp_core::types::{ApiResponse, PaginatedResponse, TenantContext};
use crate::dto::article_dto::{ArticleListItem, ArticleListParams, ArticleResp, CreateArticleReq, ReviewArticleReq, UpdateArticleReq};
use crate::dto::article_dto::{
ArticleListItem, ArticleListParams, ArticleResp, CreateArticleReq, ReviewArticleReq,
UpdateArticleReq,
};
use crate::service::article_service;
use crate::state::HealthState;
@@ -22,14 +25,24 @@ where
let page = params.page.unwrap_or(1);
let page_size = params.page_size.unwrap_or(20);
// 非管理权限用户只能查看已发布文章,防止草稿泄露
let status = if require_any_permission(&ctx, &["health.articles.manage", "health.articles.review"]).is_ok() {
params.status
} else {
Some("published".to_string())
};
let status =
if require_any_permission(&ctx, &["health.articles.manage", "health.articles.review"])
.is_ok()
{
params.status
} else {
Some("published".to_string())
};
let result = article_service::list_articles(
&state, ctx.tenant_id, page, page_size,
params.category, status, params.category_id, params.tag_id, params.keyword,
&state,
ctx.tenant_id,
page,
page_size,
params.category,
status,
params.category_id,
params.tag_id,
params.keyword,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
@@ -45,7 +58,8 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.articles.list")?;
let is_admin = require_any_permission(&ctx, &["health.articles.manage", "health.articles.review"]).is_ok();
let is_admin =
require_any_permission(&ctx, &["health.articles.manage", "health.articles.review"]).is_ok();
let result = article_service::get_article(&state, ctx.tenant_id, id, is_admin).await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -61,9 +75,8 @@ where
{
require_permission(&ctx, "health.articles.manage")?;
req.sanitize();
let result = article_service::create_article(
&state, ctx.tenant_id, Some(ctx.user_id), req.0,
).await?;
let result =
article_service::create_article(&state, ctx.tenant_id, Some(ctx.user_id), req.0).await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -79,9 +92,9 @@ where
{
require_permission(&ctx, "health.articles.manage")?;
req.sanitize();
let result = article_service::update_article(
&state, ctx.tenant_id, id, Some(ctx.user_id), req.0,
).await?;
let result =
article_service::update_article(&state, ctx.tenant_id, id, Some(ctx.user_id), req.0)
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -101,7 +114,8 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.articles.manage")?;
article_service::delete_article(&state, ctx.tenant_id, id, Some(ctx.user_id), req.version).await?;
article_service::delete_article(&state, ctx.tenant_id, id, Some(ctx.user_id), req.version)
.await?;
Ok(Json(ApiResponse::ok(())))
}
@@ -126,9 +140,9 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.articles.manage")?;
let result = article_service::submit_article(
&state, ctx.tenant_id, id, Some(ctx.user_id), req.version,
).await?;
let result =
article_service::submit_article(&state, ctx.tenant_id, id, Some(ctx.user_id), req.version)
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -147,8 +161,14 @@ where
req.sanitize();
let version = req.version.unwrap_or(0);
let result = article_service::approve_article(
&state, ctx.tenant_id, id, Some(ctx.user_id), req.0, version,
).await?;
&state,
ctx.tenant_id,
id,
Some(ctx.user_id),
req.0,
version,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -167,8 +187,14 @@ where
req.sanitize();
let version = req.version.unwrap_or(0);
let result = article_service::reject_article(
&state, ctx.tenant_id, id, Some(ctx.user_id), req.0, version,
).await?;
&state,
ctx.tenant_id,
id,
Some(ctx.user_id),
req.0,
version,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -185,8 +211,13 @@ where
{
require_permission(&ctx, "health.articles.manage")?;
let result = article_service::unpublish_article(
&state, ctx.tenant_id, id, Some(ctx.user_id), req.version,
).await?;
&state,
ctx.tenant_id,
id,
Some(ctx.user_id),
req.version,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -216,7 +247,10 @@ pub async fn list_revisions<S>(
Extension(ctx): Extension<TenantContext>,
Path(id): Path<uuid::Uuid>,
Query(params): Query<ListRevisionsQuery>,
) -> Result<Json<ApiResponse<PaginatedResponse<crate::dto::article_dto::ArticleRevisionResp>>>, AppError>
) -> Result<
Json<ApiResponse<PaginatedResponse<crate::dto::article_dto::ArticleRevisionResp>>>,
AppError,
>
where
HealthState: FromRef<S>,
S: Clone + Send + Sync + 'static,
@@ -224,8 +258,7 @@ where
require_permission(&ctx, "health.articles.list")?;
let page = params.page.unwrap_or(1);
let page_size = params.page_size.unwrap_or(20);
let result = article_service::list_revisions(
&state, ctx.tenant_id, id, page, page_size,
).await?;
let result =
article_service::list_revisions(&state, ctx.tenant_id, id, page, page_size).await?;
Ok(Json(ApiResponse::ok(result)))
}

View File

@@ -34,9 +34,8 @@ where
{
require_permission(&ctx, "health.articles.manage")?;
req.sanitize();
let result = article_tag_service::create_tag(
&state, ctx.tenant_id, Some(ctx.user_id), req.0,
).await?;
let result =
article_tag_service::create_tag(&state, ctx.tenant_id, Some(ctx.user_id), req.0).await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -52,9 +51,9 @@ where
{
require_permission(&ctx, "health.articles.manage")?;
req.sanitize();
let result = article_tag_service::update_tag(
&state, ctx.tenant_id, id, Some(ctx.user_id), req.0,
).await?;
let result =
article_tag_service::update_tag(&state, ctx.tenant_id, id, Some(ctx.user_id), req.0)
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -74,8 +73,7 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.articles.manage")?;
article_tag_service::delete_tag(
&state, ctx.tenant_id, id, Some(ctx.user_id), req.version,
).await?;
article_tag_service::delete_tag(&state, ctx.tenant_id, id, Some(ctx.user_id), req.version)
.await?;
Ok(Json(ApiResponse::ok(())))
}

View File

@@ -1,12 +1,12 @@
use axum::extract::{FromRef, Json, Path, Query, State};
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, TenantContext};
use uuid::Uuid;
use crate::dto::ble_gateway_dto::*;
use crate::dto::DeleteWithVersion;
use crate::dto::ble_gateway_dto::*;
use crate::gateway_auth::GatewayAuthContext;
use crate::service::ble_gateway_service;
use crate::state::HealthState;
@@ -54,8 +54,7 @@ where
{
require_permission(&ctx, "health.ble-gateways.manage")?;
let result =
ble_gateway_service::create_gateway(&state, ctx.tenant_id, Some(ctx.user_id), body)
.await?;
ble_gateway_service::create_gateway(&state, ctx.tenant_id, Some(ctx.user_id), body).await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -179,14 +178,9 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.ble-gateways.manage")?;
let result = ble_gateway_service::batch_bind(
&state,
ctx.tenant_id,
gateway_id,
Some(ctx.user_id),
body,
)
.await?;
let result =
ble_gateway_service::batch_bind(&state, ctx.tenant_id, gateway_id, Some(ctx.user_id), body)
.await?;
Ok(Json(ApiResponse::ok(result)))
}

View File

@@ -1,5 +1,5 @@
use axum::extract::{FromRef, Json, Path, Query, State};
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};
@@ -74,14 +74,9 @@ where
{
require_permission(&ctx, "health.care-plan.manage")?;
req.data.sanitize();
let result = care_plan_service::update_care_plan(
&state,
ctx.tenant_id,
plan_id,
Some(ctx.user_id),
req,
)
.await?;
let result =
care_plan_service::update_care_plan(&state, ctx.tenant_id, plan_id, Some(ctx.user_id), req)
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -124,14 +119,9 @@ where
require_permission(&ctx, "health.care-plan.list")?;
let page = params.page.unwrap_or(1);
let page_size = params.page_size.unwrap_or(20);
let result = care_plan_service::list_care_plan_items(
&state,
ctx.tenant_id,
plan_id,
page,
page_size,
)
.await?;
let result =
care_plan_service::list_care_plan_items(&state, ctx.tenant_id, plan_id, page, page_size)
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -222,14 +212,9 @@ where
require_permission(&ctx, "health.care-plan.list")?;
let page = params.page.unwrap_or(1);
let page_size = params.page_size.unwrap_or(20);
let result = care_plan_service::list_care_plan_outcomes(
&state,
ctx.tenant_id,
plan_id,
page,
page_size,
)
.await?;
let result =
care_plan_service::list_care_plan_outcomes(&state, ctx.tenant_id, plan_id, page, page_size)
.await?;
Ok(Json(ApiResponse::ok(result)))
}

View File

@@ -1,9 +1,9 @@
use axum::Extension;
use axum::extract::{FromRef, Json, Path, Query, State};
use serde::Deserialize;
use erp_core::error::AppError;
use erp_core::rbac::require_permission;
use erp_core::types::{ApiResponse, PaginatedResponse, TenantContext};
use serde::Deserialize;
use crate::dto::consent_dto::*;
use crate::service::consent_service;
@@ -28,10 +28,8 @@ where
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?;
let result =
consent_service::list_consents(&state, ctx.tenant_id, patient_id, page, page_size).await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -47,10 +45,8 @@ where
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?;
let result =
consent_service::grant_consent(&state, ctx.tenant_id, Some(ctx.user_id), req).await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -67,9 +63,8 @@ where
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?;
let result =
consent_service::revoke_consent(&state, ctx.tenant_id, consent_id, Some(ctx.user_id), req)
.await?;
Ok(Json(ApiResponse::ok(result)))
}

View File

@@ -60,10 +60,8 @@ where
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?;
let result =
consultation_service::create_session(&state, ctx.tenant_id, Some(ctx.user_id), req).await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -80,7 +78,12 @@ where
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,
&state,
ctx.tenant_id,
page,
page_size,
params.status,
params.patient_id,
params.doctor_id,
)
.await?;
@@ -115,7 +118,12 @@ where
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,
&state,
ctx.tenant_id,
session_id,
page,
page_size,
params.after_id,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
@@ -133,7 +141,11 @@ where
{
require_permission(&ctx, "health.consultation.manage")?;
let result = consultation_service::close_session(
&state, ctx.tenant_id, id, Some(ctx.user_id), req.version,
&state,
ctx.tenant_id,
id,
Some(ctx.user_id),
req.version,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
@@ -166,7 +178,12 @@ where
};
msg_req.sanitize();
let result = consultation_service::create_message(
&state, ctx.tenant_id, Some(ctx.user_id), ctx.user_id, sender_role, msg_req,
&state,
ctx.tenant_id,
Some(ctx.user_id),
ctx.user_id,
sender_role,
msg_req,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
@@ -183,8 +200,13 @@ where
{
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,
&state,
ctx.tenant_id,
params.status,
params.patient_id,
params.doctor_id,
params.page,
params.page_size,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
@@ -219,10 +241,7 @@ where
.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?;
consultation_service::mark_session_read(&state, ctx.tenant_id, id, ctx.user_id, role).await?;
Ok(Json(ApiResponse::ok(())))
}
@@ -244,12 +263,13 @@ where
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?;
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,
&state,
ctx.tenant_id,
ctx.user_id,
&mut result,
)
.await?;
Ok(Json(ApiResponse::ok(result)))

View File

@@ -1,6 +1,6 @@
use axum::Extension;
use axum::extract::{FromRef, Path, Query, State};
use axum::response::IntoResponse;
use axum::Extension;
use serde::Deserialize;
use utoipa::IntoParams;
use uuid::Uuid;
@@ -31,16 +31,19 @@ where
let page = query.page.unwrap_or(1);
let page_size = query.page_size.unwrap_or(20);
let (items, total) = critical_alert_service::list_pending_alerts(
&state, ctx.tenant_id, page, page_size,
)
.await
.map_err(|e| {
tracing::error!(error = %e, tenant_id = %ctx.tenant_id, "查询危急值告警列表失败");
e
})?;
let (items, total) =
critical_alert_service::list_pending_alerts(&state, ctx.tenant_id, page, page_size)
.await
.map_err(|e| {
tracing::error!(error = %e, tenant_id = %ctx.tenant_id, "查询危急值告警列表失败");
e
})?;
let total_pages = if page_size > 0 { total.div_ceil(page_size) } else { 0 };
let total_pages = if page_size > 0 {
total.div_ceil(page_size)
} else {
0
};
Ok(axum::Json(ApiResponse::ok(PaginatedResponse {
data: items,
@@ -81,13 +84,9 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.critical-alerts.manage")?;
critical_alert_service::acknowledge_alert(
&state,
ctx.tenant_id,
id,
ctx.user_id,
body.notes,
)
.await?;
Ok(axum::Json(ApiResponse::ok(serde_json::json!({"message": "告警已确认"}))))
critical_alert_service::acknowledge_alert(&state, ctx.tenant_id, id, ctx.user_id, body.notes)
.await?;
Ok(axum::Json(ApiResponse::ok(
serde_json::json!({"message": "告警已确认"}),
)))
}

View File

@@ -1,9 +1,9 @@
use axum::Extension;
use axum::extract::{FromRef, Json, Path, State};
use serde::Deserialize;
use erp_core::error::AppError;
use erp_core::rbac::require_permission;
use erp_core::types::{ApiResponse, TenantContext};
use serde::Deserialize;
use crate::service::critical_value_threshold_service;
use crate::state::HealthState;
@@ -105,8 +105,13 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.critical-value-thresholds.manage")?;
critical_value_threshold_service::delete_threshold(&state.db, ctx.tenant_id, id, Some(ctx.user_id))
.await?;
critical_value_threshold_service::delete_threshold(
&state.db,
ctx.tenant_id,
id,
Some(ctx.user_id),
)
.await?;
Ok(Json(ApiResponse::ok(())))
}

View File

@@ -8,8 +8,8 @@ use erp_core::error::AppError;
use erp_core::rbac::require_permission;
use erp_core::types::{ApiResponse, PaginatedResponse, TenantContext};
use crate::dto::daily_monitoring_dto::*;
use crate::dto::DeleteWithVersion;
use crate::dto::daily_monitoring_dto::*;
use crate::service::daily_monitoring_service;
use crate::state::HealthState;
@@ -40,7 +40,11 @@ where
let page = params.page.unwrap_or(1);
let page_size = params.page_size.unwrap_or(20);
let result = daily_monitoring_service::list_daily_monitoring(
&state, ctx.tenant_id, patient_id, page, page_size,
&state,
ctx.tenant_id,
patient_id,
page,
page_size,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
@@ -56,10 +60,8 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.daily-monitoring.list")?;
let result = daily_monitoring_service::get_daily_monitoring(
&state, ctx.tenant_id, record_id,
)
.await?;
let result =
daily_monitoring_service::get_daily_monitoring(&state, ctx.tenant_id, record_id).await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -76,7 +78,10 @@ where
let mut req = req;
req.sanitize();
let result = daily_monitoring_service::create_daily_monitoring(
&state, ctx.tenant_id, Some(ctx.user_id), req,
&state,
ctx.tenant_id,
Some(ctx.user_id),
req,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
@@ -96,7 +101,12 @@ where
let mut data = req.data;
data.sanitize();
let result = daily_monitoring_service::update_daily_monitoring(
&state, ctx.tenant_id, record_id, Some(ctx.user_id), data, req.version,
&state,
ctx.tenant_id,
record_id,
Some(ctx.user_id),
data,
req.version,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
@@ -114,7 +124,11 @@ where
{
require_permission(&ctx, "health.daily-monitoring.manage")?;
daily_monitoring_service::delete_daily_monitoring(
&state, ctx.tenant_id, record_id, Some(ctx.user_id), req.version,
&state,
ctx.tenant_id,
record_id,
Some(ctx.user_id),
req.version,
)
.await?;
Ok(Json(ApiResponse::ok(())))

View File

@@ -1,8 +1,8 @@
//! 设备管理 API — 设备列表查询与解绑
use axum::Extension;
use axum::extract::{FromRef, Path, Query, State};
use axum::response::IntoResponse;
use axum::Extension;
use serde::Deserialize;
use utoipa::IntoParams;
use uuid::Uuid;
@@ -72,14 +72,8 @@ where
{
require_permission(&ctx, "health.devices.manage")?;
let device = device_service::unbind_device(
&state,
ctx.tenant_id,
id,
ctx.user_id,
body.version,
)
.await?;
let device =
device_service::unbind_device(&state, ctx.tenant_id, id, ctx.user_id, body.version).await?;
Ok(axum::Json(ApiResponse::ok(device)))
}

View File

@@ -1,6 +1,6 @@
use axum::Extension;
use axum::extract::{FromRef, Path, Query, State};
use axum::response::IntoResponse;
use axum::Extension;
use serde::Deserialize;
use utoipa::IntoParams;
use uuid::Uuid;
@@ -44,9 +44,9 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.device-readings.manage")?;
let result = device_reading_service::batch_create_readings(
&state, ctx.tenant_id, path.patient_id, body,
).await?;
let result =
device_reading_service::batch_create_readings(&state, ctx.tenant_id, path.patient_id, body)
.await?;
Ok(axum::Json(ApiResponse::ok(result)))
}
@@ -64,9 +64,15 @@ where
let page = query.page.unwrap_or(1);
let page_size = query.page_size.unwrap_or(20);
let result = device_reading_service::query_device_readings(
&state, ctx.tenant_id, path.patient_id,
query.device_type.as_deref(), query.hours, page, page_size,
).await?;
&state,
ctx.tenant_id,
path.patient_id,
query.device_type.as_deref(),
query.hours,
page,
page_size,
)
.await?;
Ok(axum::Json(ApiResponse::ok(result)))
}
@@ -85,8 +91,14 @@ where
let page_size = query.page_size.unwrap_or(20);
let days = query.days.unwrap_or(7);
let result = device_reading_service::query_hourly_readings(
&state, ctx.tenant_id, path.patient_id,
&query.device_type, days, page, page_size,
).await?;
&state,
ctx.tenant_id,
path.patient_id,
&query.device_type,
days,
page,
page_size,
)
.await?;
Ok(axum::Json(ApiResponse::ok(result)))
}

View File

@@ -1,12 +1,12 @@
use axum::Extension;
use axum::extract::{FromRef, Json, Path, Query, State};
use serde::Deserialize;
use erp_core::error::AppError;
use erp_core::rbac::require_permission;
use erp_core::types::{ApiResponse, PaginatedResponse, TenantContext};
use serde::Deserialize;
use crate::dto::diagnosis_dto::*;
use crate::dto::DeleteWithVersion;
use crate::dto::diagnosis_dto::*;
use crate::service::diagnosis_service;
use crate::state::HealthState;
@@ -29,10 +29,9 @@ where
require_permission(&ctx, "health.health-data.list")?;
let page = params.page.unwrap_or(1);
let page_size = params.page_size.unwrap_or(20);
let result = diagnosis_service::list_diagnoses(
&state, ctx.tenant_id, patient_id, page, page_size,
)
.await?;
let result =
diagnosis_service::list_diagnoses(&state, ctx.tenant_id, patient_id, page, page_size)
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -50,7 +49,11 @@ where
let mut req = req;
req.sanitize();
let result = diagnosis_service::create_diagnosis(
&state, ctx.tenant_id, patient_id, Some(ctx.user_id), req,
&state,
ctx.tenant_id,
patient_id,
Some(ctx.user_id),
req,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
@@ -70,7 +73,12 @@ where
let mut data = req.data;
data.sanitize();
let result = diagnosis_service::update_diagnosis(
&state, ctx.tenant_id, diagnosis_id, Some(ctx.user_id), data, req.version,
&state,
ctx.tenant_id,
diagnosis_id,
Some(ctx.user_id),
data,
req.version,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
@@ -88,7 +96,11 @@ where
{
require_permission(&ctx, "health.health-data.manage")?;
diagnosis_service::delete_diagnosis(
&state, ctx.tenant_id, diagnosis_id, Some(ctx.user_id), req.version,
&state,
ctx.tenant_id,
diagnosis_id,
Some(ctx.user_id),
req.version,
)
.await?;
Ok(Json(ApiResponse::ok(())))

View File

@@ -8,8 +8,8 @@ use erp_core::error::AppError;
use erp_core::rbac::require_permission;
use erp_core::types::{ApiResponse, PaginatedResponse, TenantContext};
use crate::dto::doctor_dto::*;
use crate::dto::DeleteWithVersion;
use crate::dto::doctor_dto::*;
use crate::service::doctor_service;
use crate::state::HealthState;
@@ -42,7 +42,13 @@ where
let page = params.page.unwrap_or(1);
let page_size = params.page_size.unwrap_or(20);
let result = doctor_service::list_doctors(
&state, ctx.tenant_id, page, page_size, params.search, params.department, params.title,
&state,
ctx.tenant_id,
page,
page_size,
params.search,
params.department,
params.title,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
@@ -60,10 +66,8 @@ where
require_permission(&ctx, "health.doctor.manage")?;
let mut req = req;
req.sanitize();
let result = doctor_service::create_doctor(
&state, ctx.tenant_id, Some(ctx.user_id), req,
)
.await?;
let result =
doctor_service::create_doctor(&state, ctx.tenant_id, Some(ctx.user_id), req).await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -95,7 +99,12 @@ where
let mut data = req.data;
data.sanitize();
let result = doctor_service::update_doctor(
&state, ctx.tenant_id, id, Some(ctx.user_id), data, req.version,
&state,
ctx.tenant_id,
id,
Some(ctx.user_id),
data,
req.version,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
@@ -112,6 +121,7 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.doctor.manage")?;
doctor_service::delete_doctor(&state, ctx.tenant_id, id, Some(ctx.user_id), req.version).await?;
doctor_service::delete_doctor(&state, ctx.tenant_id, id, Some(ctx.user_id), req.version)
.await?;
Ok(Json(ApiResponse::ok(())))
}

View File

@@ -1,7 +1,7 @@
//! 家庭成员健康代理 Handler — 同意管理 + 健康摘要查看
use axum::extract::{Json, Path, Query, State};
use axum::Extension;
use axum::extract::{Json, Path, Query, State};
use erp_core::error::AppError;
use erp_core::rbac::require_permission;
use erp_core::types::{ApiResponse, TenantContext};
@@ -27,9 +27,15 @@ pub async fn grant_family_access(
) -> Result<Json<ApiResponse<FamilyMemberResp>>, AppError> {
require_permission(&ctx, "health.patient.manage")?;
let result = family_proxy_service::grant_family_access(
&state, ctx.tenant_id, patient_id, family_member_id,
Some(ctx.user_id), req, params.version,
).await?;
&state,
ctx.tenant_id,
patient_id,
family_member_id,
Some(ctx.user_id),
req,
params.version,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -42,9 +48,14 @@ pub async fn revoke_family_access(
) -> Result<Json<ApiResponse<FamilyMemberResp>>, AppError> {
require_permission(&ctx, "health.patient.manage")?;
let result = family_proxy_service::revoke_family_access(
&state, ctx.tenant_id, patient_id, family_member_id,
Some(ctx.user_id), params.version,
).await?;
&state,
ctx.tenant_id,
patient_id,
family_member_id,
Some(ctx.user_id),
params.version,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -53,9 +64,8 @@ pub async fn list_my_family_patients(
State(state): State<HealthState>,
Extension(ctx): Extension<TenantContext>,
) -> Result<Json<ApiResponse<Vec<FamilyPatientSummaryResp>>>, AppError> {
let result = family_proxy_service::list_family_patients(
&state, ctx.tenant_id, ctx.user_id,
).await?;
let result =
family_proxy_service::list_family_patients(&state, ctx.tenant_id, ctx.user_id).await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -66,8 +76,12 @@ pub async fn get_family_health_summary(
Path(patient_id): Path<Uuid>,
) -> Result<Json<ApiResponse<FamilyHealthSummaryResp>>, AppError> {
let result = family_proxy_service::get_family_health_summary(
&state, ctx.tenant_id, ctx.user_id, patient_id,
).await?;
&state,
ctx.tenant_id,
ctx.user_id,
patient_id,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -78,7 +92,11 @@ pub async fn link_family_member_user(
Path(family_member_id): Path<Uuid>,
) -> Result<Json<ApiResponse<FamilyMemberResp>>, AppError> {
let result = family_proxy_service::link_family_member_user(
&state, ctx.tenant_id, family_member_id, ctx.user_id,
).await?;
&state,
ctx.tenant_id,
family_member_id,
ctx.user_id,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
}

View File

@@ -8,8 +8,8 @@ use erp_core::error::AppError;
use erp_core::rbac::require_permission;
use erp_core::types::{ApiResponse, PaginatedResponse, TenantContext};
use crate::dto::follow_up_dto::*;
use crate::dto::DeleteWithVersion;
use crate::dto::follow_up_dto::*;
use crate::service::follow_up_service;
use crate::state::HealthState;
@@ -33,10 +33,9 @@ where
if req.patient_ids.len() > 100 {
return Err(AppError::Validation("单次批量最多 100 条".to_string()));
}
let result = follow_up_service::batch_create_tasks(
&state, ctx.tenant_id, Some(ctx.user_id), req,
)
.await?;
let result =
follow_up_service::batch_create_tasks(&state, ctx.tenant_id, Some(ctx.user_id), req)
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -56,10 +55,9 @@ where
if req.task_ids.len() > 100 {
return Err(AppError::Validation("单次批量最多 100 条".to_string()));
}
let result = follow_up_service::batch_assign_tasks(
&state, ctx.tenant_id, Some(ctx.user_id), req,
)
.await?;
let result =
follow_up_service::batch_assign_tasks(&state, ctx.tenant_id, Some(ctx.user_id), req)
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -79,10 +77,9 @@ where
if req.task_ids.len() > 100 {
return Err(AppError::Validation("单次批量最多 100 条".to_string()));
}
let result = follow_up_service::batch_complete_tasks(
&state, ctx.tenant_id, Some(ctx.user_id), req,
)
.await?;
let result =
follow_up_service::batch_complete_tasks(&state, ctx.tenant_id, Some(ctx.user_id), req)
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -123,7 +120,12 @@ where
let page = params.page.unwrap_or(1);
let page_size = params.page_size.unwrap_or(20);
let result = follow_up_service::list_tasks(
&state, ctx.tenant_id, page, page_size, params.patient_id, params.assigned_to,
&state,
ctx.tenant_id,
page,
page_size,
params.patient_id,
params.assigned_to,
params.status,
)
.await?;
@@ -156,10 +158,8 @@ where
require_permission(&ctx, "health.follow-up.manage")?;
let mut req = req;
req.sanitize();
let result = follow_up_service::create_task(
&state, ctx.tenant_id, Some(ctx.user_id), req,
)
.await?;
let result =
follow_up_service::create_task(&state, ctx.tenant_id, Some(ctx.user_id), req).await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -177,7 +177,12 @@ where
let mut data = req.data;
data.sanitize();
let result = follow_up_service::update_task(
&state, ctx.tenant_id, id, Some(ctx.user_id), data, req.version,
&state,
ctx.tenant_id,
id,
Some(ctx.user_id),
data,
req.version,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
@@ -194,7 +199,8 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.follow-up.manage")?;
follow_up_service::delete_task(&state, ctx.tenant_id, id, Some(ctx.user_id), req.version).await?;
follow_up_service::delete_task(&state, ctx.tenant_id, id, Some(ctx.user_id), req.version)
.await?;
Ok(Json(ApiResponse::ok(())))
}
@@ -210,14 +216,14 @@ where
{
require_permission(&ctx, "health.follow-up.manage")?;
if req.task_id != task_id {
return Err(AppError::Validation("路径中的 task_id 与请求体不一致".to_string()));
return Err(AppError::Validation(
"路径中的 task_id 与请求体不一致".to_string(),
));
}
let mut req = req;
req.sanitize();
let result = follow_up_service::create_record(
&state, ctx.tenant_id, Some(ctx.user_id), req,
)
.await?;
let result =
follow_up_service::create_record(&state, ctx.tenant_id, Some(ctx.user_id), req).await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -234,7 +240,12 @@ where
let page = params.page.unwrap_or(1);
let page_size = params.page_size.unwrap_or(20);
let result = follow_up_service::list_records(
&state, ctx.tenant_id, page, page_size, params.task_id, params.patient_id,
&state,
ctx.tenant_id,
page,
page_size,
params.task_id,
params.patient_id,
)
.await?;
Ok(Json(ApiResponse::ok(result)))

View File

@@ -8,8 +8,8 @@ use erp_core::error::AppError;
use erp_core::rbac::require_permission;
use erp_core::types::{ApiResponse, PaginatedResponse, TenantContext};
use crate::dto::follow_up_template_dto::*;
use crate::dto::DeleteWithVersion;
use crate::dto::follow_up_template_dto::*;
use crate::service::follow_up_template_service;
use crate::state::HealthState;
@@ -41,7 +41,12 @@ where
let page = params.page.unwrap_or(1);
let page_size = params.page_size.unwrap_or(20);
let result = follow_up_template_service::list_templates(
&state, ctx.tenant_id, page, page_size, params.follow_up_type, params.status,
&state,
ctx.tenant_id,
page,
page_size,
params.follow_up_type,
params.status,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
@@ -73,10 +78,9 @@ where
require_permission(&ctx, "health.follow-up-templates.manage")?;
let mut req = req;
req.sanitize();
let result = follow_up_template_service::create_template(
&state, ctx.tenant_id, Some(ctx.user_id), req,
)
.await?;
let result =
follow_up_template_service::create_template(&state, ctx.tenant_id, Some(ctx.user_id), req)
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -94,7 +98,12 @@ where
let mut data = req.data;
data.sanitize();
let result = follow_up_template_service::update_template(
&state, ctx.tenant_id, id, Some(ctx.user_id), data, req.version,
&state,
ctx.tenant_id,
id,
Some(ctx.user_id),
data,
req.version,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
@@ -112,7 +121,11 @@ where
{
require_permission(&ctx, "health.follow-up-templates.manage")?;
follow_up_template_service::delete_template(
&state, ctx.tenant_id, id, Some(ctx.user_id), req.version,
&state,
ctx.tenant_id,
id,
Some(ctx.user_id),
req.version,
)
.await?;
Ok(Json(ApiResponse::ok(())))

View File

@@ -8,8 +8,8 @@ use erp_core::error::AppError;
use erp_core::rbac::require_permission;
use erp_core::types::{ApiResponse, PaginatedResponse, TenantContext};
use crate::dto::health_data_dto::*;
use crate::dto::DeleteWithVersion;
use crate::dto::health_data_dto::*;
use crate::service::health_data_service;
use crate::service::trend_service;
use crate::state::HealthState;
@@ -59,10 +59,9 @@ where
require_permission(&ctx, "health.health-data.list")?;
let page = params.page.unwrap_or(1);
let page_size = params.page_size.unwrap_or(20);
let result = health_data_service::list_vital_signs(
&state, ctx.tenant_id, patient_id, page, page_size,
)
.await?;
let result =
health_data_service::list_vital_signs(&state, ctx.tenant_id, patient_id, page, page_size)
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -80,7 +79,11 @@ where
let mut req = req;
req.sanitize();
let result = health_data_service::create_vital_signs(
&state, ctx.tenant_id, patient_id, Some(ctx.user_id), req,
&state,
ctx.tenant_id,
patient_id,
Some(ctx.user_id),
req,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
@@ -100,7 +103,13 @@ where
let mut data = req.data;
data.sanitize();
let result = health_data_service::update_vital_signs(
&state, ctx.tenant_id, patient_id, vid, Some(ctx.user_id), data, req.version,
&state,
ctx.tenant_id,
patient_id,
vid,
Some(ctx.user_id),
data,
req.version,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
@@ -117,7 +126,14 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.health-data.manage")?;
health_data_service::delete_vital_signs(&state, ctx.tenant_id, vid, Some(ctx.user_id), req.version).await?;
health_data_service::delete_vital_signs(
&state,
ctx.tenant_id,
vid,
Some(ctx.user_id),
req.version,
)
.await?;
Ok(Json(ApiResponse::ok(())))
}
@@ -138,10 +154,9 @@ where
require_permission(&ctx, "health.health-data.list")?;
let page = params.page.unwrap_or(1);
let page_size = params.page_size.unwrap_or(20);
let result = health_data_service::list_lab_reports(
&state, ctx.tenant_id, patient_id, page, page_size,
)
.await?;
let result =
health_data_service::list_lab_reports(&state, ctx.tenant_id, patient_id, page, page_size)
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -159,7 +174,11 @@ where
let mut req = req;
req.sanitize();
let result = health_data_service::create_lab_report(
&state, ctx.tenant_id, patient_id, Some(ctx.user_id), req,
&state,
ctx.tenant_id,
patient_id,
Some(ctx.user_id),
req,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
@@ -179,7 +198,13 @@ where
let mut data = req.data;
data.sanitize();
let result = health_data_service::update_lab_report(
&state, ctx.tenant_id, _patient_id, rid, Some(ctx.user_id), data, req.version,
&state,
ctx.tenant_id,
_patient_id,
rid,
Some(ctx.user_id),
data,
req.version,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
@@ -196,7 +221,14 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.health-data.manage")?;
health_data_service::delete_lab_report(&state, ctx.tenant_id, rid, Some(ctx.user_id), req.version).await?;
health_data_service::delete_lab_report(
&state,
ctx.tenant_id,
rid,
Some(ctx.user_id),
req.version,
)
.await?;
Ok(Json(ApiResponse::ok(())))
}
@@ -214,7 +246,13 @@ where
let mut data = req.data;
data.sanitize();
let result = health_data_service::review_lab_report(
&state, ctx.tenant_id, _patient_id, rid, ctx.user_id, data, req.version,
&state,
ctx.tenant_id,
_patient_id,
rid,
ctx.user_id,
data,
req.version,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
@@ -238,7 +276,11 @@ where
let page = params.page.unwrap_or(1);
let page_size = params.page_size.unwrap_or(20);
let result = health_data_service::list_health_records(
&state, ctx.tenant_id, patient_id, page, page_size,
&state,
ctx.tenant_id,
patient_id,
page,
page_size,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
@@ -258,7 +300,11 @@ where
let mut req = req;
req.sanitize();
let result = health_data_service::create_health_record(
&state, ctx.tenant_id, patient_id, Some(ctx.user_id), req,
&state,
ctx.tenant_id,
patient_id,
Some(ctx.user_id),
req,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
@@ -278,7 +324,13 @@ where
let mut data = req.data;
data.sanitize();
let result = health_data_service::update_health_record(
&state, ctx.tenant_id, patient_id, rid, Some(ctx.user_id), data, req.version,
&state,
ctx.tenant_id,
patient_id,
rid,
Some(ctx.user_id),
data,
req.version,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
@@ -295,7 +347,14 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.health-data.manage")?;
health_data_service::delete_health_record(&state, ctx.tenant_id, rid, Some(ctx.user_id), req.version).await?;
health_data_service::delete_health_record(
&state,
ctx.tenant_id,
rid,
Some(ctx.user_id),
req.version,
)
.await?;
Ok(Json(ApiResponse::ok(())))
}
@@ -316,10 +375,8 @@ where
require_permission(&ctx, "health.health-data.list")?;
let page = params.page.unwrap_or(1);
let page_size = params.page_size.unwrap_or(20);
let result = trend_service::list_trends(
&state, ctx.tenant_id, patient_id, page, page_size,
)
.await?;
let result =
trend_service::list_trends(&state, ctx.tenant_id, patient_id, page, page_size).await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -335,7 +392,12 @@ where
{
require_permission(&ctx, "health.health-data.manage")?;
let result = trend_service::generate_trend(
&state, ctx.tenant_id, patient_id, Some(ctx.user_id), req.period_start, req.period_end,
&state,
ctx.tenant_id,
patient_id,
Some(ctx.user_id),
req.period_start,
req.period_end,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
@@ -353,7 +415,12 @@ where
{
require_permission(&ctx, "health.health-data.list")?;
let result = trend_service::get_indicator_timeseries(
&state, ctx.tenant_id, patient_id, indicator, params.start_date, params.end_date,
&state,
ctx.tenant_id,
patient_id,
indicator,
params.start_date,
params.end_date,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
@@ -374,7 +441,11 @@ where
{
require_permission(&ctx, "health.health-data.list")?;
let result = trend_service::get_mini_trend(
&state, ctx.tenant_id, ctx.user_id, params.indicator, params.range,
&state,
ctx.tenant_id,
ctx.user_id,
params.indicator,
params.range,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
@@ -394,10 +465,9 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.health-data.list")?;
let result = trend_service::get_mini_today(
&state, ctx.tenant_id, ctx.user_id, params.patient_id,
)
.await?;
let result =
trend_service::get_mini_today(&state, ctx.tenant_id, ctx.user_id, params.patient_id)
.await?;
Ok(Json(ApiResponse::ok(result)))
}

View File

@@ -1,12 +1,12 @@
use axum::Extension;
use axum::extract::{FromRef, Json, Path, Query, State};
use serde::Deserialize;
use erp_core::error::AppError;
use erp_core::rbac::require_permission;
use erp_core::types::{ApiResponse, PaginatedResponse, TenantContext};
use serde::Deserialize;
use crate::dto::medication_record_dto::*;
use crate::dto::DeleteWithVersion;
use crate::dto::medication_record_dto::*;
use crate::service::medication_record_service;
use crate::state::HealthState;
@@ -31,7 +31,11 @@ where
let page = params.page.unwrap_or(1);
let page_size = params.page_size.unwrap_or(20);
let result = medication_record_service::list_medications(
&state, ctx.tenant_id, patient_id, page, page_size,
&state,
ctx.tenant_id,
patient_id,
page,
page_size,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
@@ -66,13 +70,9 @@ where
require_permission(&ctx, "health.medication-records.manage")?;
let mut req = req;
req.sanitize();
let result = medication_record_service::create_medication(
&state,
ctx.tenant_id,
Some(ctx.user_id),
req,
)
.await?;
let result =
medication_record_service::create_medication(&state, ctx.tenant_id, Some(ctx.user_id), req)
.await?;
Ok(Json(ApiResponse::ok(result)))
}

View File

@@ -4,7 +4,9 @@ use erp_core::error::AppError;
use erp_core::rbac::require_permission;
use erp_core::types::{ApiResponse, PaginatedResponse, TenantContext};
use crate::dto::medication_reminder_dto::{CreateMedicationReminderReq, MedicationReminderResp, UpdateMedicationReminderReq};
use crate::dto::medication_reminder_dto::{
CreateMedicationReminderReq, MedicationReminderResp, UpdateMedicationReminderReq,
};
use crate::service::medication_reminder_service;
use crate::state::HealthState;
@@ -28,8 +30,13 @@ where
let page = params.page.unwrap_or(1);
let page_size = params.page_size.unwrap_or(20);
let result = medication_reminder_service::list_reminders(
&state, ctx.tenant_id, patient_id, page, page_size,
).await?;
&state,
ctx.tenant_id,
patient_id,
page,
page_size,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -45,8 +52,12 @@ where
require_permission(&ctx, "health.medication-reminders.manage")?;
req.sanitize();
let result = medication_reminder_service::create_reminder(
&state, ctx.tenant_id, Some(ctx.user_id), req.0,
).await?;
&state,
ctx.tenant_id,
Some(ctx.user_id),
req.0,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -71,8 +82,14 @@ where
let mut data = req.data;
data.sanitize();
let result = medication_reminder_service::update_reminder(
&state, ctx.tenant_id, id, Some(ctx.user_id), req.version, data,
).await?;
&state,
ctx.tenant_id,
id,
Some(ctx.user_id),
req.version,
data,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -93,7 +110,12 @@ where
{
require_permission(&ctx, "health.medication-reminders.manage")?;
medication_reminder_service::delete_reminder(
&state, ctx.tenant_id, id, Some(ctx.user_id), req.version,
).await?;
&state,
ctx.tenant_id,
id,
Some(ctx.user_id),
req.version,
)
.await?;
Ok(Json(ApiResponse::ok(())))
}

View File

@@ -1,29 +1,29 @@
pub mod action_inbox_handler;
pub mod alert_handler;
pub mod ble_gateway_handler;
pub mod alert_rule_handler;
pub mod appointment_handler;
pub mod article_category_handler;
pub mod article_handler;
pub mod article_tag_handler;
pub mod ble_gateway_handler;
pub mod care_plan_handler;
pub mod consultation_handler;
pub mod consent_handler;
pub mod consultation_handler;
pub mod critical_alert_handler;
pub mod critical_value_threshold_handler;
pub mod daily_monitoring_handler;
pub mod device_handler;
pub mod device_reading_handler;
pub mod diagnosis_handler;
pub mod family_proxy_handler;
pub mod medication_record_handler;
pub mod medication_reminder_handler;
pub mod doctor_handler;
pub mod family_proxy_handler;
pub mod follow_up_handler;
pub mod follow_up_template_handler;
pub mod health_data_handler;
pub mod medication_record_handler;
pub mod medication_reminder_handler;
pub mod patient_handler;
pub mod points_handler;
pub mod stats_handler;
pub mod shift_handler;
pub mod stats_handler;
pub mod vital_signs_daily_handler;

View File

@@ -8,11 +8,11 @@ use erp_core::error::AppError;
use erp_core::rbac::require_permission;
use erp_core::types::{ApiResponse, PaginatedResponse, TenantContext};
use crate::dto::DeleteWithVersion;
use crate::dto::patient_dto::{
CreatePatientReq, FamilyMemberReq, FamilyMemberResp, ManageTagsReq, PatientResp,
UpdatePatientReq,
};
use crate::dto::DeleteWithVersion;
use crate::service::patient_service;
use crate::state::HealthState;
@@ -44,7 +44,12 @@ where
let page = params.page.unwrap_or(1);
let page_size = params.page_size.unwrap_or(20);
let result = patient_service::list_patients(
&state, ctx.tenant_id, page, page_size, params.search, params.tag_id,
&state,
ctx.tenant_id,
page,
page_size,
params.search,
params.tag_id,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
@@ -62,10 +67,11 @@ where
require_permission(&ctx, "health.patient.manage")?;
let mut req = req;
req.sanitize();
let result = patient_service::create_patient(
&state, ctx.tenant_id, Some(ctx.user_id), req,
)
.await?;
if req.name.trim().is_empty() {
return Err(AppError::Validation("患者姓名不能为空".into()));
}
let result =
patient_service::create_patient(&state, ctx.tenant_id, Some(ctx.user_id), req).await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -112,7 +118,12 @@ where
};
update.sanitize();
let result = patient_service::update_patient(
&state, ctx.tenant_id, id, Some(ctx.user_id), update, version,
&state,
ctx.tenant_id,
id,
Some(ctx.user_id),
update,
version,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
@@ -129,7 +140,8 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.patient.manage")?;
patient_service::delete_patient(&state, ctx.tenant_id, id, Some(ctx.user_id), req.version).await?;
patient_service::delete_patient(&state, ctx.tenant_id, id, Some(ctx.user_id), req.version)
.await?;
Ok(Json(ApiResponse::ok(())))
}
@@ -189,10 +201,9 @@ where
require_permission(&ctx, "health.patient.manage")?;
let mut req = req;
req.sanitize();
let result = patient_service::create_family_member(
&state, ctx.tenant_id, id, Some(ctx.user_id), req,
)
.await?;
let result =
patient_service::create_family_member(&state, ctx.tenant_id, id, Some(ctx.user_id), req)
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -217,7 +228,13 @@ where
};
update.sanitize();
let result = patient_service::update_family_member(
&state, ctx.tenant_id, _patient_id, member_id, Some(ctx.user_id), update, version,
&state,
ctx.tenant_id,
_patient_id,
member_id,
Some(ctx.user_id),
update,
version,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
@@ -235,7 +252,12 @@ where
{
require_permission(&ctx, "health.patient.manage")?;
patient_service::delete_family_member(
&state, ctx.tenant_id, patient_id, member_id, Some(ctx.user_id), req.version,
&state,
ctx.tenant_id,
patient_id,
member_id,
Some(ctx.user_id),
req.version,
)
.await?;
Ok(Json(ApiResponse::ok(())))
@@ -257,7 +279,8 @@ where
ctx.tenant_id,
id,
req.doctor_id,
req.relationship_type.unwrap_or_else(|| "primary".to_string()),
req.relationship_type
.unwrap_or_else(|| "primary".to_string()),
Some(ctx.user_id),
)
.await?;
@@ -274,7 +297,14 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.patient.manage")?;
patient_service::remove_doctor(&state, ctx.tenant_id, patient_id, doctor_id, Some(ctx.user_id)).await?;
patient_service::remove_doctor(
&state,
ctx.tenant_id,
patient_id,
doctor_id,
Some(ctx.user_id),
)
.await?;
Ok(Json(ApiResponse::ok(())))
}
@@ -338,11 +368,16 @@ where
{
require_permission(&ctx, "health.patient.manage")?;
let result = patient_service::create_tag(
&state, ctx.tenant_id, Some(ctx.user_id),
&state,
ctx.tenant_id,
Some(ctx.user_id),
patient_service::CreateTagReq {
name: req.name, color: req.color, description: req.description,
name: req.name,
color: req.color,
description: req.description,
},
).await?;
)
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -366,11 +401,18 @@ where
{
require_permission(&ctx, "health.patient.manage")?;
let result = patient_service::update_tag(
&state, ctx.tenant_id, id, Some(ctx.user_id),
&state,
ctx.tenant_id,
id,
Some(ctx.user_id),
patient_service::UpdateTagReq {
name: req.name, color: req.color, description: req.description, version: req.version,
name: req.name,
color: req.color,
description: req.description,
version: req.version,
},
).await?;
)
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -385,8 +427,6 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.patient.manage")?;
patient_service::delete_tag(
&state, ctx.tenant_id, id, Some(ctx.user_id), req.version,
).await?;
patient_service::delete_tag(&state, ctx.tenant_id, id, Some(ctx.user_id), req.version).await?;
Ok(Json(ApiResponse::ok(())))
}

View File

@@ -36,9 +36,11 @@ pub async fn get_my_account<S>(
State(state): State<HealthState>,
Extension(ctx): Extension<TenantContext>,
) -> Result<Json<ApiResponse<PointsAccountResp>>, AppError>
where HealthState: FromRef<S>, S: Clone + Send + Sync + 'static,
where
HealthState: FromRef<S>,
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.health-data.list")?;
require_permission(&ctx, "health.points.list")?;
let patient_id = resolve_patient_id(&state, ctx.tenant_id, ctx.user_id).await?;
let result = points_service::get_account(&state, ctx.tenant_id, patient_id).await?;
Ok(Json(ApiResponse::ok(result)))
@@ -48,13 +50,14 @@ pub async fn daily_checkin<S>(
State(state): State<HealthState>,
Extension(ctx): Extension<TenantContext>,
) -> Result<Json<ApiResponse<CheckinStatusResp>>, AppError>
where HealthState: FromRef<S>, S: Clone + Send + Sync + 'static,
where
HealthState: FromRef<S>,
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.health-data.list")?;
require_permission(&ctx, "health.points.list")?;
let patient_id = resolve_patient_id(&state, ctx.tenant_id, ctx.user_id).await?;
let result = points_service::daily_checkin(
&state, ctx.tenant_id, patient_id, Some(ctx.user_id),
).await?;
let result =
points_service::daily_checkin(&state, ctx.tenant_id, patient_id, Some(ctx.user_id)).await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -62,9 +65,11 @@ pub async fn get_checkin_status<S>(
State(state): State<HealthState>,
Extension(ctx): Extension<TenantContext>,
) -> Result<Json<ApiResponse<CheckinStatusResp>>, AppError>
where HealthState: FromRef<S>, S: Clone + Send + Sync + 'static,
where
HealthState: FromRef<S>,
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.health-data.list")?;
require_permission(&ctx, "health.points.list")?;
let patient_id = resolve_patient_id(&state, ctx.tenant_id, ctx.user_id).await?;
let result = points_service::get_checkin_status(&state, ctx.tenant_id, patient_id).await?;
Ok(Json(ApiResponse::ok(result)))
@@ -79,15 +84,17 @@ pub async fn list_my_transactions<S>(
Extension(ctx): Extension<TenantContext>,
Query(params): Query<PaginationParams>,
) -> Result<Json<ApiResponse<PaginatedResponse<PointsTransactionResp>>>, AppError>
where HealthState: FromRef<S>, S: Clone + Send + Sync + 'static,
where
HealthState: FromRef<S>,
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.health-data.list")?;
require_permission(&ctx, "health.points.list")?;
let patient_id = resolve_patient_id(&state, ctx.tenant_id, ctx.user_id).await?;
let page = params.page.unwrap_or(1);
let page_size = params.page_size.unwrap_or(20);
let result = points_service::list_transactions(
&state, ctx.tenant_id, patient_id, page, page_size,
).await?;
let result =
points_service::list_transactions(&state, ctx.tenant_id, patient_id, page, page_size)
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -97,14 +104,15 @@ pub async fn list_products<S>(
Query(params): Query<ProductTypeParam>,
Query(page): Query<PaginationParams>,
) -> Result<Json<ApiResponse<PaginatedResponse<PointsProductResp>>>, AppError>
where HealthState: FromRef<S>, S: Clone + Send + Sync + 'static,
where
HealthState: FromRef<S>,
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.health-data.list")?;
require_permission(&ctx, "health.points.list")?;
let p = page.page.unwrap_or(1);
let ps = page.page_size.unwrap_or(20);
let result = points_service::list_products(
&state, ctx.tenant_id, params.product_type, p, ps,
).await?;
let result =
points_service::list_products(&state, ctx.tenant_id, params.product_type, p, ps).await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -113,9 +121,11 @@ pub async fn get_product<S>(
Extension(ctx): Extension<TenantContext>,
Path(product_id): Path<Uuid>,
) -> Result<Json<ApiResponse<PointsProductResp>>, AppError>
where HealthState: FromRef<S>, S: Clone + Send + Sync + 'static,
where
HealthState: FromRef<S>,
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.health-data.list")?;
require_permission(&ctx, "health.points.list")?;
let result = points_service::get_product(&state, ctx.tenant_id, product_id).await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -125,13 +135,15 @@ pub async fn exchange_product<S>(
Extension(ctx): Extension<TenantContext>,
Json(req): Json<ExchangeReq>,
) -> Result<Json<ApiResponse<PointsOrderResp>>, AppError>
where HealthState: FromRef<S>, S: Clone + Send + Sync + 'static,
where
HealthState: FromRef<S>,
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.health-data.manage")?;
require_permission(&ctx, "health.points.manage")?;
let patient_id = resolve_patient_id(&state, ctx.tenant_id, ctx.user_id).await?;
let result = points_service::exchange_product(
&state, ctx.tenant_id, patient_id, req, Some(ctx.user_id),
).await?;
let result =
points_service::exchange_product(&state, ctx.tenant_id, patient_id, req, Some(ctx.user_id))
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -140,15 +152,16 @@ pub async fn list_my_orders<S>(
Extension(ctx): Extension<TenantContext>,
Query(params): Query<PaginationParams>,
) -> Result<Json<ApiResponse<PaginatedResponse<PointsOrderResp>>>, AppError>
where HealthState: FromRef<S>, S: Clone + Send + Sync + 'static,
where
HealthState: FromRef<S>,
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.health-data.list")?;
require_permission(&ctx, "health.points.list")?;
let patient_id = resolve_patient_id(&state, ctx.tenant_id, ctx.user_id).await?;
let page = params.page.unwrap_or(1);
let page_size = params.page_size.unwrap_or(20);
let result = points_service::list_orders(
&state, ctx.tenant_id, patient_id, page, page_size,
).await?;
let result =
points_service::list_orders(&state, ctx.tenant_id, patient_id, page, page_size).await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -161,14 +174,15 @@ pub async fn list_offline_events<S>(
Extension(ctx): Extension<TenantContext>,
Query(params): Query<PaginationParams>,
) -> Result<Json<ApiResponse<PaginatedResponse<OfflineEventResp>>>, AppError>
where HealthState: FromRef<S>, S: Clone + Send + Sync + 'static,
where
HealthState: FromRef<S>,
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.health-data.list")?;
require_permission(&ctx, "health.points.list")?;
let page = params.page.unwrap_or(1);
let page_size = params.page_size.unwrap_or(20);
let result = points_service::list_offline_events(
&state, ctx.tenant_id, page, page_size,
).await?;
let result =
points_service::list_offline_events(&state, ctx.tenant_id, page, page_size).await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -177,13 +191,20 @@ pub async fn register_event<S>(
Extension(ctx): Extension<TenantContext>,
Path(event_id): Path<Uuid>,
) -> Result<Json<ApiResponse<()>>, AppError>
where HealthState: FromRef<S>, S: Clone + Send + Sync + 'static,
where
HealthState: FromRef<S>,
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.health-data.manage")?;
require_permission(&ctx, "health.points.manage")?;
let patient_id = resolve_patient_id(&state, ctx.tenant_id, ctx.user_id).await?;
points_service::register_event(
&state, ctx.tenant_id, event_id, patient_id, Some(ctx.user_id),
).await?;
&state,
ctx.tenant_id,
event_id,
patient_id,
Some(ctx.user_id),
)
.await?;
Ok(Json(ApiResponse::ok(())))
}
@@ -196,12 +217,13 @@ pub async fn verify_order<S>(
Extension(ctx): Extension<TenantContext>,
Json(req): Json<VerifyOrderReq>,
) -> Result<Json<ApiResponse<PointsOrderResp>>, AppError>
where HealthState: FromRef<S>, S: Clone + Send + Sync + 'static,
where
HealthState: FromRef<S>,
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.points.manage")?;
let result = points_service::verify_order(
&state, ctx.tenant_id, req.qr_code, ctx.user_id,
).await?;
let result =
points_service::verify_order(&state, ctx.tenant_id, req.qr_code, ctx.user_id).await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -209,7 +231,9 @@ pub async fn list_rules<S>(
State(state): State<HealthState>,
Extension(ctx): Extension<TenantContext>,
) -> Result<Json<ApiResponse<Vec<PointsRuleResp>>>, AppError>
where HealthState: FromRef<S>, S: Clone + Send + Sync + 'static,
where
HealthState: FromRef<S>,
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.points.list")?;
let result = points_service::list_rules(&state, ctx.tenant_id).await?;
@@ -221,14 +245,14 @@ pub async fn create_rule<S>(
Extension(ctx): Extension<TenantContext>,
Json(req): Json<CreatePointsRuleReq>,
) -> Result<Json<ApiResponse<PointsRuleResp>>, AppError>
where HealthState: FromRef<S>, S: Clone + Send + Sync + 'static,
where
HealthState: FromRef<S>,
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.points.manage")?;
let mut req = req;
req.sanitize();
let result = points_service::create_rule(
&state, ctx.tenant_id, Some(ctx.user_id), req,
).await?;
let result = points_service::create_rule(&state, ctx.tenant_id, Some(ctx.user_id), req).await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -238,14 +262,22 @@ pub async fn update_rule<S>(
Path(rule_id): Path<Uuid>,
Json(wrapper): Json<crate::dto::points_dto::UpdateRuleWithVersion>,
) -> Result<Json<ApiResponse<PointsRuleResp>>, AppError>
where HealthState: FromRef<S>, S: Clone + Send + Sync + 'static,
where
HealthState: FromRef<S>,
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.points.manage")?;
let mut data = wrapper.data;
data.sanitize();
let result = points_service::update_rule(
&state, ctx.tenant_id, rule_id, Some(ctx.user_id), data, wrapper.version,
).await?;
&state,
ctx.tenant_id,
rule_id,
Some(ctx.user_id),
data,
wrapper.version,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -255,12 +287,19 @@ pub async fn delete_rule<S>(
Path(rule_id): Path<Uuid>,
Json(wrapper): Json<crate::dto::DeleteWithVersion>,
) -> Result<Json<ApiResponse<()>>, AppError>
where HealthState: FromRef<S>, S: Clone + Send + Sync + 'static,
where
HealthState: FromRef<S>,
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.points.manage")?;
points_service::delete_rule(
&state, ctx.tenant_id, rule_id, Some(ctx.user_id), wrapper.version,
).await?;
&state,
ctx.tenant_id,
rule_id,
Some(ctx.user_id),
wrapper.version,
)
.await?;
Ok(Json(ApiResponse::ok(())))
}
@@ -271,14 +310,22 @@ pub async fn admin_list_products<S>(
Query(page): Query<PaginationParams>,
Query(filter): Query<AdminProductFilter>,
) -> Result<Json<ApiResponse<PaginatedResponse<PointsProductResp>>>, AppError>
where HealthState: FromRef<S>, S: Clone + Send + Sync + 'static,
where
HealthState: FromRef<S>,
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.points.list")?;
let p = page.page.unwrap_or(1);
let ps = page.page_size.unwrap_or(20);
let result = points_service::admin_list_products(
&state, ctx.tenant_id, params.product_type, filter.is_active, p, ps,
).await?;
&state,
ctx.tenant_id,
params.product_type,
filter.is_active,
p,
ps,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -287,14 +334,15 @@ pub async fn admin_create_product<S>(
Extension(ctx): Extension<TenantContext>,
Json(req): Json<CreatePointsProductReq>,
) -> Result<Json<ApiResponse<PointsProductResp>>, AppError>
where HealthState: FromRef<S>, S: Clone + Send + Sync + 'static,
where
HealthState: FromRef<S>,
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.points.manage")?;
let mut req = req;
req.sanitize();
let result = points_service::create_product(
&state, ctx.tenant_id, Some(ctx.user_id), req,
).await?;
let result =
points_service::create_product(&state, ctx.tenant_id, Some(ctx.user_id), req).await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -304,14 +352,22 @@ pub async fn admin_update_product<S>(
Path(product_id): Path<Uuid>,
Json(wrapper): Json<crate::dto::points_dto::UpdateProductWithVersion>,
) -> Result<Json<ApiResponse<PointsProductResp>>, AppError>
where HealthState: FromRef<S>, S: Clone + Send + Sync + 'static,
where
HealthState: FromRef<S>,
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.points.manage")?;
let mut data = wrapper.data;
data.sanitize();
let result = points_service::update_product(
&state, ctx.tenant_id, product_id, Some(ctx.user_id), data, wrapper.version,
).await?;
&state,
ctx.tenant_id,
product_id,
Some(ctx.user_id),
data,
wrapper.version,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -321,12 +377,19 @@ pub async fn admin_delete_product<S>(
Path(product_id): Path<Uuid>,
Json(wrapper): Json<crate::dto::DeleteWithVersion>,
) -> Result<Json<ApiResponse<()>>, AppError>
where HealthState: FromRef<S>, S: Clone + Send + Sync + 'static,
where
HealthState: FromRef<S>,
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.points.manage")?;
points_service::delete_product(
&state, ctx.tenant_id, product_id, Some(ctx.user_id), wrapper.version,
).await?;
&state,
ctx.tenant_id,
product_id,
Some(ctx.user_id),
wrapper.version,
)
.await?;
Ok(Json(ApiResponse::ok(())))
}
@@ -335,15 +398,15 @@ pub async fn admin_list_orders<S>(
Extension(ctx): Extension<TenantContext>,
Query(params): Query<PaginationParams>,
) -> Result<Json<ApiResponse<PaginatedResponse<PointsOrderResp>>>, AppError>
where HealthState: FromRef<S>, S: Clone + Send + Sync + 'static,
where
HealthState: FromRef<S>,
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.points.list")?;
let page = params.page.unwrap_or(1);
let page_size = params.page_size.unwrap_or(20);
// 管理端查看所有订单 — 不按 patient_id 过滤
let result = points_service::admin_list_orders(
&state, ctx.tenant_id, page, page_size,
).await?;
let result = points_service::admin_list_orders(&state, ctx.tenant_id, page, page_size).await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -356,14 +419,15 @@ pub async fn admin_create_event<S>(
Extension(ctx): Extension<TenantContext>,
Json(req): Json<CreateOfflineEventReq>,
) -> Result<Json<ApiResponse<OfflineEventResp>>, AppError>
where HealthState: FromRef<S>, S: Clone + Send + Sync + 'static,
where
HealthState: FromRef<S>,
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.points.manage")?;
let mut req = req;
req.sanitize();
let result = points_service::create_offline_event(
&state, ctx.tenant_id, Some(ctx.user_id), req,
).await?;
let result =
points_service::create_offline_event(&state, ctx.tenant_id, Some(ctx.user_id), req).await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -373,14 +437,22 @@ pub async fn admin_update_event<S>(
Path(event_id): Path<Uuid>,
Json(wrapper): Json<UpdateOfflineEventWithVersion>,
) -> Result<Json<ApiResponse<OfflineEventResp>>, AppError>
where HealthState: FromRef<S>, S: Clone + Send + Sync + 'static,
where
HealthState: FromRef<S>,
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.points.manage")?;
let mut data = wrapper.data;
data.sanitize();
let result = points_service::update_offline_event(
&state, ctx.tenant_id, event_id, Some(ctx.user_id), data, wrapper.version,
).await?;
&state,
ctx.tenant_id,
event_id,
Some(ctx.user_id),
data,
wrapper.version,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -390,12 +462,19 @@ pub async fn admin_delete_event<S>(
Path(event_id): Path<Uuid>,
Json(wrapper): Json<crate::dto::DeleteWithVersion>,
) -> Result<Json<ApiResponse<()>>, AppError>
where HealthState: FromRef<S>, S: Clone + Send + Sync + 'static,
where
HealthState: FromRef<S>,
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.points.manage")?;
points_service::delete_offline_event(
&state, ctx.tenant_id, event_id, Some(ctx.user_id), wrapper.version,
).await?;
&state,
ctx.tenant_id,
event_id,
Some(ctx.user_id),
wrapper.version,
)
.await?;
Ok(Json(ApiResponse::ok(())))
}
@@ -411,14 +490,21 @@ pub async fn admin_list_events<S>(
Extension(ctx): Extension<TenantContext>,
Query(params): Query<AdminListEventsParams>,
) -> Result<Json<ApiResponse<PaginatedResponse<OfflineEventResp>>>, AppError>
where HealthState: FromRef<S>, S: Clone + Send + Sync + 'static,
where
HealthState: FromRef<S>,
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.points.list")?;
let page = params.page.unwrap_or(1);
let page_size = params.page_size.unwrap_or(20);
let result = points_service::admin_list_offline_events(
&state, ctx.tenant_id, params.status, page, page_size,
).await?;
&state,
ctx.tenant_id,
params.status,
page,
page_size,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -428,12 +514,19 @@ pub async fn admin_checkin_event<S>(
Path(event_id): Path<Uuid>,
Json(req): Json<AdminCheckinReq>,
) -> Result<Json<ApiResponse<()>>, AppError>
where HealthState: FromRef<S>, S: Clone + Send + Sync + 'static,
where
HealthState: FromRef<S>,
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.points.manage")?;
points_service::admin_checkin_event(
&state, ctx.tenant_id, event_id, req.patient_id, Some(ctx.user_id),
).await?;
&state,
ctx.tenant_id,
event_id,
req.patient_id,
Some(ctx.user_id),
)
.await?;
Ok(Json(ApiResponse::ok(())))
}
@@ -445,7 +538,9 @@ pub async fn get_points_statistics<S>(
State(state): State<HealthState>,
Extension(ctx): Extension<TenantContext>,
) -> Result<Json<ApiResponse<PointsStatisticsResp>>, AppError>
where HealthState: FromRef<S>, S: Clone + Send + Sync + 'static,
where
HealthState: FromRef<S>,
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.points.list")?;
let result = points_service::get_points_statistics(&state, ctx.tenant_id).await?;
@@ -461,7 +556,9 @@ pub async fn admin_get_patient_account<S>(
Extension(ctx): Extension<TenantContext>,
Path(patient_id): Path<Uuid>,
) -> Result<Json<ApiResponse<PointsAccountResp>>, AppError>
where HealthState: FromRef<S>, S: Clone + Send + Sync + 'static,
where
HealthState: FromRef<S>,
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.points.list")?;
let result = points_service::get_account(&state, ctx.tenant_id, patient_id).await?;
@@ -474,14 +571,16 @@ pub async fn admin_list_patient_transactions<S>(
Path(patient_id): Path<Uuid>,
Query(params): Query<PaginationParams>,
) -> Result<Json<ApiResponse<PaginatedResponse<PointsTransactionResp>>>, AppError>
where HealthState: FromRef<S>, S: Clone + Send + Sync + 'static,
where
HealthState: FromRef<S>,
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.points.list")?;
let page = params.page.unwrap_or(1);
let page_size = params.page_size.unwrap_or(20);
let result = points_service::list_transactions(
&state, ctx.tenant_id, patient_id, page, page_size,
).await?;
let result =
points_service::list_transactions(&state, ctx.tenant_id, patient_id, page, page_size)
.await?;
Ok(Json(ApiResponse::ok(result)))
}

View File

@@ -1,5 +1,5 @@
use axum::extract::{FromRef, Json, Path, Query, State};
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, TenantContext};
@@ -58,7 +58,8 @@ where
{
require_permission(&ctx, "health.shifts.manage")?;
body.sanitize();
let result = shift_service::create_shift(&state, ctx.tenant_id, Some(ctx.user_id), body).await?;
let result =
shift_service::create_shift(&state, ctx.tenant_id, Some(ctx.user_id), body).await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -73,7 +74,9 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.shifts.manage")?;
let result = shift_service::update_shift(&state, ctx.tenant_id, shift_id, Some(ctx.user_id), body).await?;
let result =
shift_service::update_shift(&state, ctx.tenant_id, shift_id, Some(ctx.user_id), body)
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -88,7 +91,14 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.shifts.manage")?;
shift_service::delete_shift(&state, ctx.tenant_id, shift_id, Some(ctx.user_id), params.version).await?;
shift_service::delete_shift(
&state,
ctx.tenant_id,
shift_id,
Some(ctx.user_id),
params.version,
)
.await?;
Ok(Json(ApiResponse::ok(())))
}
@@ -109,7 +119,8 @@ where
require_permission(&ctx, "health.shifts.list")?;
let page = params.page.unwrap_or(1);
let page_size = params.page_size.unwrap_or(20);
let result = shift_service::list_assignments(&state, ctx.tenant_id, shift_id, page, page_size).await?;
let result =
shift_service::list_assignments(&state, ctx.tenant_id, shift_id, page, page_size).await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -125,7 +136,9 @@ where
{
require_permission(&ctx, "health.shifts.manage")?;
body.sanitize();
let result = shift_service::create_assignment(&state, ctx.tenant_id, shift_id, Some(ctx.user_id), body).await?;
let result =
shift_service::create_assignment(&state, ctx.tenant_id, shift_id, Some(ctx.user_id), body)
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -140,7 +153,9 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.shifts.manage")?;
let result = shift_service::batch_assign(&state, ctx.tenant_id, shift_id, Some(ctx.user_id), body).await?;
let result =
shift_service::batch_assign(&state, ctx.tenant_id, shift_id, Some(ctx.user_id), body)
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -155,7 +170,15 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.shifts.manage")?;
let result = shift_service::update_assignment(&state, ctx.tenant_id, shift_id, assignment_id, Some(ctx.user_id), body).await?;
let result = shift_service::update_assignment(
&state,
ctx.tenant_id,
shift_id,
assignment_id,
Some(ctx.user_id),
body,
)
.await?;
Ok(Json(ApiResponse::ok(result)))
}
@@ -170,7 +193,15 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.shifts.manage")?;
shift_service::delete_assignment(&state, ctx.tenant_id, shift_id, assignment_id, Some(ctx.user_id), params.version).await?;
shift_service::delete_assignment(
&state,
ctx.tenant_id,
shift_id,
assignment_id,
Some(ctx.user_id),
params.version,
)
.await?;
Ok(Json(ApiResponse::ok(())))
}
@@ -202,6 +233,7 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.shifts.manage")?;
let result = shift_service::create_handoff(&state, ctx.tenant_id, Some(ctx.user_id), body).await?;
let result =
shift_service::create_handoff(&state, ctx.tenant_id, Some(ctx.user_id), body).await?;
Ok(Json(ApiResponse::ok(result)))
}

View File

@@ -4,8 +4,8 @@ use erp_core::error::AppError;
use erp_core::rbac::require_permission;
use erp_core::types::{ApiResponse, TenantContext};
use crate::service::stats_service;
use crate::dto::stats_dto::*;
use crate::service::stats_service;
use crate::state::HealthState;
pub async fn get_patient_stats<S>(
@@ -56,9 +56,44 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.patient.list")?;
let patients = stats_service::get_patient_statistics(&state, ctx.tenant_id).await?;
let consultations = stats_service::get_consultation_statistics(&state, ctx.tenant_id).await?;
let follow_ups = stats_service::get_follow_up_statistics(&state, ctx.tenant_id).await?;
let patients = stats_service::get_patient_statistics(&state, ctx.tenant_id)
.await
.unwrap_or_else(|e| {
tracing::warn!("仪表盘患者统计查询失败: {e}");
PatientStatisticsResp {
total_patients: 0,
new_this_month: 0,
new_this_week: 0,
active_this_month: 0,
}
});
let consultations = stats_service::get_consultation_statistics(&state, ctx.tenant_id)
.await
.unwrap_or_else(|e| {
tracing::warn!("仪表盘咨询统计查询失败: {e}");
ConsultationStatisticsResp {
total_sessions: 0,
pending_reply: 0,
avg_response_time_minutes: None,
this_month: 0,
}
});
let follow_ups = stats_service::get_follow_up_statistics(&state, ctx.tenant_id)
.await
.unwrap_or_else(|e| {
tracing::warn!("仪表盘随访统计查询失败: {e}");
FollowUpStatisticsResp {
total_tasks: 0,
completed: 0,
pending: 0,
overdue: 0,
completion_rate: 0.0,
}
});
Ok(Json(ApiResponse::ok(DashboardStatsResp {
patients,
consultations,

View File

@@ -1,6 +1,6 @@
use axum::Extension;
use axum::extract::{FromRef, Query, State};
use axum::response::IntoResponse;
use axum::Extension;
use serde::Deserialize;
use utoipa::IntoParams;
@@ -33,9 +33,10 @@ where
let start = query.start_date.parse::<chrono::NaiveDate>().map_err(|_| {
AppError::Validation("Invalid start_date format, expected YYYY-MM-DD".into())
})?;
let end = query.end_date.parse::<chrono::NaiveDate>().map_err(|_| {
AppError::Validation("Invalid end_date format, expected YYYY-MM-DD".into())
})?;
let end = query
.end_date
.parse::<chrono::NaiveDate>()
.map_err(|_| AppError::Validation("Invalid end_date format, expected YYYY-MM-DD".into()))?;
let results = vital_signs_daily_service::query_daily(
&state.db,