feat(health): BLE 网关后端接入 — 网关管理 + API Key 认证 + 多患者批量上报
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

- 新增 ble_gateways + gateway_patient_bindings 表迁移 (000113)
- 网关 CRUD:注册/编辑/删除/重生成 API Key,含患者绑定管理
- API Key 认证中间件(SHA-256 hash + prefix 快速查找)
- 网关数据上报端点:多患者批量读数,复用 device_reading_service 管道
- 网关心跳端点:固件版本/IP 更新 + last_heartbeat_at
- 10 个管理端路由(JWT)+ 2 个网关端路由(API Key)
- health.ble-gateways.list/manage 权限声明
- 修复 000112 迁移 ForeignKey 借用错误
This commit is contained in:
iven
2026-05-04 20:28:26 +08:00
parent 7b17f94bc0
commit 7e57565ecd
16 changed files with 1379 additions and 4 deletions

View File

@@ -0,0 +1,141 @@
use serde::{Deserialize, Serialize};
use uuid::Uuid;
// ---------------------------------------------------------------------------
// BLE Gateway DTOs
// ---------------------------------------------------------------------------
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BleGatewayResp {
pub id: Uuid,
pub tenant_id: Uuid,
pub gateway_id: String,
pub name: String,
pub status: String,
pub firmware_version: Option<String>,
pub ip_address: Option<String>,
pub last_heartbeat_at: Option<chrono::DateTime<chrono::Utc>>,
pub metadata: Option<serde_json::Value>,
pub created_at: chrono::DateTime<chrono::Utc>,
pub updated_at: chrono::DateTime<chrono::Utc>,
pub version: i32,
/// 网关 API Key仅在创建时返回一次
pub api_key: Option<String>,
/// 绑定的患者数量
pub patient_count: Option<i64>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CreateBleGatewayReq {
pub gateway_id: String,
pub name: String,
pub firmware_version: Option<String>,
pub metadata: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UpdateBleGatewayReq {
pub name: Option<String>,
pub status: Option<String>,
pub firmware_version: Option<String>,
pub metadata: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UpdateBleGatewayWithVersion {
pub version: i32,
pub data: UpdateBleGatewayReq,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ListBleGatewaysParams {
pub status: Option<String>,
pub page: Option<u64>,
pub page_size: Option<u64>,
}
// ---------------------------------------------------------------------------
// GatewayPatientBinding DTOs
// ---------------------------------------------------------------------------
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GatewayBindingResp {
pub id: Uuid,
pub tenant_id: Uuid,
pub gateway_id_fk: Uuid,
pub patient_id: Uuid,
pub peripheral_mac: Option<String>,
pub device_type: Option<String>,
pub status: String,
pub created_at: chrono::DateTime<chrono::Utc>,
pub updated_at: chrono::DateTime<chrono::Utc>,
pub version: i32,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CreateBindingReq {
pub patient_id: Uuid,
pub peripheral_mac: Option<String>,
pub device_type: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BatchBindReq {
pub bindings: Vec<CreateBindingReq>,
}
// ---------------------------------------------------------------------------
// 网关数据上报 DTOs
// ---------------------------------------------------------------------------
/// 网关多患者批量上报请求
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GatewayUploadReq {
pub readings: Vec<PatientReadingBatch>,
}
/// 单个患者的读数批次
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PatientReadingBatch {
pub patient_id: Uuid,
pub device_id: String,
pub device_model: Option<String>,
pub readings: Vec<ReadingEntry>,
}
/// 单条读数
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ReadingEntry {
pub device_type: String,
pub values: serde_json::Value,
pub measured_at: String,
}
/// 网关上报结果
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GatewayUploadResp {
pub total_patients: u64,
pub total_readings: u64,
pub total_duplicates: u64,
pub errors: Vec<String>,
}
/// 网关心跳请求
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct HeartbeatReq {
pub firmware_version: Option<String>,
pub ip_address: Option<String>,
}

View File

@@ -1,5 +1,6 @@
pub mod appointment_dto;
pub mod alert_dto;
pub mod ble_gateway_dto;
pub mod care_plan_dto;
pub mod article_dto;
pub mod consent_dto;