import Taro from '@tarojs/taro'; import { api } from './request'; export interface VitalSignInput { indicator_type: string; value: number; measured_at?: string; note?: string; extra?: Record; } export interface TodaySummary { blood_pressure?: { systolic: number; diastolic: number; status: string; reference_range?: string }; heart_rate?: { value: number; status: string; reference_range?: string }; blood_sugar?: { value: number; status: string; reference_range?: string }; weight?: { value: number; status: string; reference_range?: string }; } export async function getTodaySummary(patientId?: string) { const params: Record = {}; if (patientId) params.patient_id = patientId; return api.get('/health/vital-signs/today', params); } /** * 提交生命体征数据。 * 小程序使用简单的 indicator_type + value 模型, * 后端 CreateVitalSignsReq 期望结构化字段(systolic_bp_morning 等)。 * 此函数负责将指示器类型映射到后端结构化格式。 */ export async function inputVitalSign(patientId: string, data: VitalSignInput) { const today = new Date().toISOString().slice(0, 10); const body: Record = { record_date: today }; switch (data.indicator_type) { case 'blood_pressure': if (data.extra?.systolic) body.systolic_bp_morning = data.extra.systolic; if (data.extra?.diastolic) body.diastolic_bp_morning = data.extra.diastolic; break; case 'blood_pressure_evening': if (data.extra?.systolic) body.systolic_bp_evening = data.extra.systolic; if (data.extra?.diastolic) body.diastolic_bp_evening = data.extra.diastolic; break; case 'heart_rate': body.heart_rate = Math.round(data.value); break; case 'weight': body.weight = data.value; break; case 'blood_sugar': body.blood_sugar = data.value; break; case 'body_temperature': body.body_temperature = data.value; break; case 'spo2': body.spo2 = Math.round(data.value); break; case 'water_intake': body.water_intake_ml = Math.round(data.value); break; case 'urine_output': body.urine_output_ml = Math.round(data.value); break; default: console.warn(`[inputVitalSign] 未知的 indicator_type: ${data.indicator_type}`); break; } if (data.note) body.notes = data.note; return api.post(`/health/patients/${patientId}/vital-signs`, body); } export async function getTrend(indicator: string, range: string) { return api.get<{ indicator: string; data_points: { date: string; value: number }[] }>( '/health/vital-signs/trend', { indicator, range }, ); } // ---- Daily Monitoring (日常监测) ---- export interface DailyMonitoring { id: string; patient_id: string; record_date: string; morning_bp_systolic: number | null; morning_bp_diastolic: number | null; evening_bp_systolic: number | null; evening_bp_diastolic: number | null; weight: number | null; blood_sugar: number | null; fluid_intake: number | null; urine_output: number | null; notes: string | null; created_at: string; updated_at: string; version: number; } export interface CreateDailyMonitoringReq { patient_id: string; record_date: string; morning_bp_systolic?: number; morning_bp_diastolic?: number; evening_bp_systolic?: number; evening_bp_diastolic?: number; weight?: number; blood_sugar?: number; fluid_intake?: number; urine_output?: number; notes?: string; } export async function createDailyMonitoring(data: CreateDailyMonitoringReq) { return api.post('/health/daily-monitoring', data); } export async function listDailyMonitoring( patientId: string, params?: { page?: number; page_size?: number }, ) { return api.get<{ data: DailyMonitoring[]; total: number }>( `/health/patients/${patientId}/daily-monitoring`, params, ); } // ---- Health Thresholds (健康阈值) ---- export interface HealthThreshold { id: string; indicator: string; direction: string; threshold_value: number; level: string; department: string | null; age_min: number | null; age_max: number | null; is_active: boolean; } const THRESHOLD_CACHE_KEY = 'health_thresholds'; const THRESHOLD_TTL = 24 * 60 * 60 * 1000; // 24h /** 从缓存或 API 获取健康阈值列表 */ export async function getHealthThresholds(): Promise { try { const cached = Taro.getStorageSync(THRESHOLD_CACHE_KEY) as | { data: HealthThreshold[]; ts: number } | undefined; if (cached && Date.now() - cached.ts < THRESHOLD_TTL) { return cached.data; } } catch { /* cache miss */ } try { const data = await api.get('/health/critical-value-thresholds/public'); Taro.setStorageSync(THRESHOLD_CACHE_KEY, { data, ts: Date.now() }); return data; } catch { return []; } } /** 查找匹配的阈值,缓存未命中时返回 undefined */ export function findThreshold( thresholds: HealthThreshold[], indicator: string, direction: string, level = 'warning', ): HealthThreshold | undefined { return thresholds.find( (t) => t.indicator === indicator && t.direction === direction && t.level === level && t.is_active, ); } /** 内置默认阈值(API 不可用时的降级方案) */ export const DEFAULT_THRESHOLDS: HealthThreshold[] = [ { id: '_bp_sys_high', indicator: 'systolic_bp', direction: 'high', threshold_value: 140, level: 'warning', department: null, age_min: null, age_max: null, is_active: true }, { id: '_bp_dia_high', indicator: 'diastolic_bp', direction: 'high', threshold_value: 90, level: 'warning', department: null, age_min: null, age_max: null, is_active: true }, { id: '_hr_high', indicator: 'heart_rate', direction: 'high', threshold_value: 100, level: 'warning', department: null, age_min: null, age_max: null, is_active: true }, { id: '_hr_low', indicator: 'heart_rate', direction: 'low', threshold_value: 60, level: 'warning', department: null, age_min: null, age_max: null, is_active: true }, { id: '_bs_fasting_high', indicator: 'blood_sugar_fasting', direction: 'high', threshold_value: 6.1, level: 'warning', department: null, age_min: null, age_max: null, is_active: true }, { id: '_bs_pp_high', indicator: 'blood_sugar_postprandial', direction: 'high', threshold_value: 7.8, level: 'warning', department: null, age_min: null, age_max: null, is_active: true }, ];