fix(health): 设备数据管线 Phase 1 缺陷修复 + AI 产品策略讨论
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

- device_readings 批量插入添加 ON CONFLICT 去重唯一索引
- 小程序 BLEManager 增加离线缓存(Storage 持久化 + 启动重传)
- 新增 device_readings 90 天数据保留清理定时任务
- 小米手环适配器增加 RACP 历史心率读取支持
- SSE 告警按医生过滤已确认实现(patient_doctor_relation)
- 新增 AI 产品策略与设备数据医院场景讨论记录
This commit is contained in:
iven
2026-04-29 06:17:23 +08:00
parent a491eb19a6
commit f6ccb8a35c
8 changed files with 389 additions and 9 deletions

View File

@@ -9,6 +9,9 @@ import type {
BLEManagerConfig,
} from './types';
const CACHE_KEY = 'ble_pending_readings';
const CACHE_MAX = 2000;
const DEFAULT_CONFIG: BLEManagerConfig = {
scanTimeout: 10000,
maxReadingsPerSync: 500,
@@ -179,6 +182,7 @@ export class BLEManager {
);
if (newReadings.length > 0) {
this.readings = [...this.readings, ...newReadings];
this.persistPendingReadings();
this.onReadings?.(newReadings);
}
});
@@ -248,6 +252,7 @@ export class BLEManager {
try {
const uploaded = await uploadFn(batch);
this.readings = this.readings.slice(batch.length);
this.clearPendingReadings();
this.updateState('connected');
return {
success: true,
@@ -303,6 +308,50 @@ export class BLEManager {
clearReadings(): void {
this.readings = [];
}
// ── 离线缓存 ──
/** 将当前未上传读数同步写入 Storage */
private persistPendingReadings(): void {
try {
const batch = this.readings.slice(-CACHE_MAX);
Taro.setStorageSync(CACHE_KEY, JSON.stringify(batch));
} catch {
// Storage 写入失败不影响主流程
}
}
/** 上传成功后清除缓存 */
private clearPendingReadings(): void {
try {
Taro.removeStorageSync(CACHE_KEY);
} catch {
// 忽略
}
}
/** 启动时检查缓存,有未上传数据则自动重传。
* 返回重传的记录数 */
async flushPendingReadings(
uploadFn: (readings: NormalizedReading[]) => Promise<number>,
): Promise<number> {
try {
const raw = Taro.getStorageSync(CACHE_KEY) as string;
if (!raw) return 0;
const cached: NormalizedReading[] = JSON.parse(raw);
if (!Array.isArray(cached) || cached.length === 0) {
Taro.removeStorageSync(CACHE_KEY);
return 0;
}
const batch = cached.slice(0, this.config.maxReadingsPerSync);
const uploaded = await uploadFn(batch);
Taro.removeStorageSync(CACHE_KEY);
return uploaded;
} catch {
return 0;
}
}
}
export default new BLEManager();