feat(health): Web 管理端设备数据集成补全 — Phase 2
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

- 新增告警三页面(仪表盘/列表/规则)+ 设备管理菜单种子数据
- 新增设备管理后端 API(GET /devices + DELETE /devices/{id})
- 新增设备数据查看组件 DeviceReadingsTab(原始数据 + 小时聚合)
- 新增设备管理页面 DeviceManage(列表/筛选/解绑)
- 患者详情页新增设备数据 Tab
This commit is contained in:
iven
2026-04-29 06:28:30 +08:00
parent f6ccb8a35c
commit cac61637ce
14 changed files with 784 additions and 1 deletions

View File

@@ -0,0 +1,72 @@
//! 设备管理服务 — 设备绑定记录的查询与解绑
use chrono::Utc;
use sea_orm::entity::prelude::*;
use sea_orm::ActiveValue::Set;
use sea_orm::{QueryOrder, QuerySelect};
use uuid::Uuid;
use crate::entity::patient_devices;
use crate::error::{HealthError, HealthResult};
use crate::state::HealthState;
/// 查询设备绑定记录(分页),支持按 patient_id / device_type 筛选
pub async fn list_devices(
state: &HealthState,
tenant_id: Uuid,
patient_id: Option<Uuid>,
device_type: Option<&str>,
page: u64,
page_size: u64,
) -> HealthResult<(Vec<patient_devices::Model>, u64)> {
let limit = page_size.min(100);
let offset = page.saturating_sub(1) * limit;
let mut query = patient_devices::Entity::find()
.filter(patient_devices::Column::TenantId.eq(tenant_id))
.filter(patient_devices::Column::DeletedAt.is_null());
if let Some(pid) = patient_id {
query = query.filter(patient_devices::Column::PatientId.eq(pid));
}
if let Some(dt) = device_type {
query = query.filter(patient_devices::Column::DeviceType.eq(dt));
}
let total = query.clone().count(&state.db).await?;
let items = query
.order_by_desc(patient_devices::Column::CreatedAt)
.limit(limit)
.offset(offset)
.all(&state.db)
.await?;
Ok((items, total))
}
/// 解绑设备 — 设置 deleted_at 实现软删除,递增 version
pub async fn unbind_device(
state: &HealthState,
tenant_id: Uuid,
device_id: Uuid,
user_id: Uuid,
version: i32,
) -> HealthResult<patient_devices::Model> {
let device = patient_devices::Entity::find_by_id(device_id)
.filter(patient_devices::Column::TenantId.eq(tenant_id))
.filter(patient_devices::Column::DeletedAt.is_null())
.one(&state.db)
.await?
.ok_or(HealthError::DeviceNotFound)?;
// 乐观锁校验
erp_core::error::check_version(device.version, version)?;
let mut active: patient_devices::ActiveModel = device.into();
active.deleted_at = Set(Some(Utc::now()));
active.updated_at = Set(Utc::now());
active.updated_by = Set(Some(user_id));
active.version = Set(version + 1);
Ok(active.update(&state.db).await?)
}

View File

@@ -11,6 +11,7 @@ pub mod critical_alert_service;
pub mod critical_value_threshold_service;
pub mod daily_monitoring_service;
pub mod device_reading_service;
pub mod device_service;
pub mod diagnosis_service;
pub mod medication_record_service;
pub mod doctor_service;