diff --git a/src/family/services/moodService.ts b/src/family/services/moodService.ts new file mode 100644 index 0000000..93a043c --- /dev/null +++ b/src/family/services/moodService.ts @@ -0,0 +1,271 @@ +/** + * 家属端情绪记录服务 + * 负责查询老人的情绪记录和统计数据 + */ + +const API_BASE_URL = 'http://localhost:8000/api'; + +export type MoodType = 'happy' | 'calm' | 'sad' | 'anxious' | 'angry' | 'tired'; + +export interface MoodRecord { + id?: number; + family_id: string; + elderly_id?: number; + elderly_name?: string; + mood_type: MoodType; + mood_score: number; + note?: string; + source?: 'manual' | 'ai_detect' | 'voice'; + trigger_event?: string; + location?: string; + weather?: string; + recorded_at?: string; + created_at?: string; +} + +export interface MoodRecordResponse { + records: MoodRecord[]; + total: number; + limit: number; + offset: number; +} + +export interface MoodTypeStats { + mood_type: MoodType; + count: number; + avg_score: number; +} + +export interface DailyStats { + date: string; + avg_score: number; + count: number; +} + +export interface MoodStatsResponse { + mood_type_stats: MoodTypeStats[]; + daily_stats: DailyStats[]; + overall: { + total_records: number; + avg_score: number; + max_score: number; + min_score: number; + }; + today_count: number; + days: number; +} + +export interface TrendItem { + date: string; + mood_type: MoodType; + avg_score: number; + count: number; +} + +export interface MoodTrendResponse { + trend: TrendItem[]; + days: number; +} + +/** + * 情绪类型对应的中文名称 + */ +export const moodLabelMap: Record = { + happy: '开心', + calm: '平静', + sad: '难过', + anxious: '焦虑', + angry: '生气', + tired: '疲惫', +}; + +/** + * 情绪类型对应的emoji + */ +export const moodEmojiMap: Record = { + happy: '😊', + calm: '😌', + sad: '😔', + anxious: '😰', + angry: '😠', + tired: '😫', +}; + +/** + * 情绪类型对应的颜色 + */ +export const moodColorMap: Record = { + happy: '#22c55e', // green-500 + calm: '#3b82f6', // blue-500 + sad: '#eab308', // yellow-500 + anxious: '#f97316', // orange-500 + angry: '#ef4444', // red-500 + tired: '#8b5cf6', // violet-500 +}; + +/** + * 获取家庭情绪记录 + */ +export async function getFamilyMoods( + familyId: string, + options?: { + elderlyId?: number; + moodType?: MoodType; + startDate?: string; + endDate?: string; + limit?: number; + offset?: number; + } +): Promise { + try { + const params = new URLSearchParams({ + family_id: familyId, + }); + + if (options?.elderlyId) { + params.append('elderly_id', options.elderlyId.toString()); + } + if (options?.moodType) { + params.append('mood_type', options.moodType); + } + if (options?.startDate) { + params.append('start_date', options.startDate); + } + if (options?.endDate) { + params.append('end_date', options.endDate); + } + if (options?.limit) { + params.append('limit', options.limit.toString()); + } + if (options?.offset) { + params.append('offset', options.offset.toString()); + } + + const response = await fetch(`${API_BASE_URL}/family/moods?${params}`); + + if (!response.ok) { + throw new Error(`获取情绪记录失败: ${response.statusText}`); + } + + return await response.json(); + } catch (error) { + console.error('获取家庭情绪记录错误:', error); + throw error; + } +} + +/** + * 获取情绪统计数据 + */ +export async function getMoodStats( + familyId: string, + options?: { + elderlyId?: number; + days?: number; + } +): Promise { + try { + const params = new URLSearchParams({ + family_id: familyId, + }); + + if (options?.elderlyId) { + params.append('elderly_id', options.elderlyId.toString()); + } + if (options?.days) { + params.append('days', options.days.toString()); + } + + const response = await fetch(`${API_BASE_URL}/family/moods/stats?${params}`); + + if (!response.ok) { + throw new Error(`获取情绪统计失败: ${response.statusText}`); + } + + return await response.json(); + } catch (error) { + console.error('获取情绪统计错误:', error); + throw error; + } +} + +/** + * 获取情绪趋势数据 + */ +export async function getMoodTrend( + familyId: string, + options?: { + elderlyId?: number; + days?: number; + } +): Promise { + try { + const params = new URLSearchParams({ + family_id: familyId, + }); + + if (options?.elderlyId) { + params.append('elderly_id', options.elderlyId.toString()); + } + if (options?.days) { + params.append('days', options.days.toString()); + } + + const response = await fetch(`${API_BASE_URL}/family/moods/trend?${params}`); + + if (!response.ok) { + throw new Error(`获取情绪趋势失败: ${response.statusText}`); + } + + return await response.json(); + } catch (error) { + console.error('获取情绪趋势错误:', error); + throw error; + } +} + +/** + * 格式化情绪分数为描述 + */ +export function formatMoodScore(score: number): string { + if (score >= 8) return '非常好'; + if (score >= 6) return '良好'; + if (score >= 4) return '一般'; + if (score >= 2) return '较差'; + return '很差'; +} + +/** + * 获取情绪分数对应的颜色 + */ +export function getMoodScoreColor(score: number): string { + if (score >= 8) return '#22c55e'; // green + if (score >= 6) return '#3b82f6'; // blue + if (score >= 4) return '#eab308'; // yellow + if (score >= 2) return '#f97316'; // orange + return '#ef4444'; // red +} + +/** + * 格式化时间为易读格式 + */ +export function formatRecordTime(timeString: string): string { + const date = new Date(timeString); + const now = new Date(); + const diff = now.getTime() - date.getTime(); + const minutes = Math.floor(diff / 60000); + const hours = Math.floor(diff / 3600000); + const days = Math.floor(diff / 86400000); + + if (minutes < 1) return '刚刚'; + if (minutes < 60) return `${minutes}分钟前`; + if (hours < 24) return `${hours}小时前`; + if (days < 7) return `${days}天前`; + + return date.toLocaleDateString('zh-CN', { + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + }); +}