diff --git a/src/family/services/messageService.ts b/src/family/services/messageService.ts new file mode 100644 index 0000000..995db31 --- /dev/null +++ b/src/family/services/messageService.ts @@ -0,0 +1,375 @@ +/** + * 家属端留言服务 + * 负责与后端API交互,管理留言的创建、查询和删除 + */ + +const API_BASE_URL = 'http://localhost:8000/api'; + +export interface FamilyMessage { + id: number; + family_id: string; + content: string; + sender_name: string; + sender_relation: string; + scheduled_time: string; + played: boolean; + played_at?: string; + liked: boolean; + is_active: boolean; + created_at: string; + updated_at: string; +} + +export interface CreateMessageData { + family_id: string; + content: string; + sender_name: string; + sender_relation: string; + scheduled_time: string; +} + +/** + * 获取家庭所有留言 + */ +export async function getFamilyMessages(familyId: string): Promise { + try { + const response = await fetch( + `${API_BASE_URL}/family/messages?family_id=${familyId}` + ); + + if (!response.ok) { + throw new Error(`获取留言失败: ${response.statusText}`); + } + + const data = await response.json(); + return data.messages || []; + } catch (error) { + console.error('获取留言错误:', error); + throw error; + } +} + +/** + * 创建新留言 + */ +export async function createMessage(messageData: CreateMessageData): Promise { + try { + const response = await fetch(`${API_BASE_URL}/family/messages`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(messageData), + }); + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.error || `创建留言失败: ${response.statusText}`); + } + + const data = await response.json(); + return data.message_id; + } catch (error) { + console.error('创建留言错误:', error); + throw error; + } +} + +/** + * 删除留言 + */ +export async function deleteMessage(messageId: number): Promise { + try { + const response = await fetch( + `${API_BASE_URL}/family/messages/${messageId}`, + { + method: 'DELETE', + } + ); + + if (!response.ok) { + throw new Error(`删除留言失败: ${response.statusText}`); + } + + const data = await response.json(); + return data.success; + } catch (error) { + console.error('删除留言错误:', error); + throw error; + } +} + +/** + * 格式化日期时间为本地格式 + */ +export function formatDateTime(dateTimeStr: string): string { + try { + const date = new Date(dateTimeStr); + const now = new Date(); + const today = new Date(now.getFullYear(), now.getMonth(), now.getDate()); + const yesterday = new Date(today); + yesterday.setDate(yesterday.getDate() - 1); + const messageDate = new Date(date.getFullYear(), date.getMonth(), date.getDate()); + + const timeStr = `${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`; + + if (messageDate.getTime() === today.getTime()) { + return `今天 ${timeStr}`; + } else if (messageDate.getTime() === yesterday.getTime()) { + return `昨天 ${timeStr}`; + } else { + return `${date.getMonth() + 1}/${date.getDate()} ${timeStr}`; + } + } catch { + return dateTimeStr; + } +} + +// ==================== 家属端消息/告警 API ==================== + +export interface FamilyAlert { + id: number; + family_id: string; + elderly_id?: number; + elderly_name?: string; + alert_type: 'sos_emergency' | 'contact_family' | 'medication' | 'emotion' | 'inactive' | 'emergency'; + level: 'low' | 'medium' | 'high'; + title?: string; + message: string; + metadata?: any; + source: 'elderly' | 'system' | 'family'; + handled: boolean; + handled_at?: string; + handled_by?: number; + handler_name?: string; + reply_message?: string; + read: boolean; + read_at?: string; + is_active: boolean; + created_at: string; + updated_at: string; +} + +export interface CreateAlertData { + family_id: string; + alert_type: FamilyAlert['alert_type']; + level: FamilyAlert['level']; + message: string; +} + +/** + * 获取家庭所有消息/告警 + */ +export async function getFamilyAlerts( + familyId: string, + options?: { + handled?: boolean; + read?: boolean; + level?: string; + alert_type?: string; + elderly_id?: number; + limit?: number; + offset?: number; + } +): Promise<{ alerts: FamilyAlert[]; total: number }> { + try { + const params = new URLSearchParams({ family_id: familyId }); + + if (options) { + if (options.handled !== undefined) params.append('handled', options.handled.toString()); + if (options.read !== undefined) params.append('read', options.read.toString()); + if (options.level) params.append('level', options.level); + if (options.alert_type) params.append('alert_type', options.alert_type); + if (options.elderly_id) params.append('elderly_id', options.elderly_id.toString()); + 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/alerts?${params.toString()}` + ); + + if (!response.ok) { + throw new Error(`获取消息失败: ${response.statusText}`); + } + + const data = await response.json(); + return { + alerts: data.alerts || [], + total: data.total || 0 + }; + } catch (error) { + console.error('获取消息错误:', error); + throw error; + } +} + +/** + * 创建新消息/告警(通常由老人端触发) + */ +export async function createAlert(alertData: CreateAlertData): Promise { + try { + const response = await fetch(`${API_BASE_URL}/family/alerts`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(alertData), + }); + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.error || `创建消息失败: ${response.statusText}`); + } + + const data = await response.json(); + return data.alert_id; + } catch (error) { + console.error('创建消息错误:', error); + throw error; + } +} + +/** + * 标记消息为已读 + */ +export async function markAlertAsRead(alertId: number): Promise { + try { + const response = await fetch( + `${API_BASE_URL}/family/alerts/${alertId}/read`, + { + method: 'POST', + } + ); + + if (!response.ok) { + throw new Error(`标记已读失败: ${response.statusText}`); + } + + const data = await response.json(); + return data.success; + } catch (error) { + console.error('标记已读错误:', error); + throw error; + } +} + +/** + * 标记消息/告警为已处理(带回复) + */ +export async function handleAlert( + alertId: number, + handledBy?: number, + replyMessage?: string +): Promise { + try { + const body: any = {}; + if (handledBy) body.handled_by = handledBy; + if (replyMessage) body.reply_message = replyMessage; + + const response = await fetch( + `${API_BASE_URL}/family/alerts/${alertId}/handle`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body), + } + ); + + if (!response.ok) { + throw new Error(`处理消息失败: ${response.statusText}`); + } + + const data = await response.json(); + return data.success; + } catch (error) { + console.error('处理消息错误:', error); + throw error; + } +} + +/** + * 回复消息给老人 + */ +export async function replyAlert( + alertId: number, + replyMessage: string +): Promise { + try { + const response = await fetch( + `${API_BASE_URL}/family/alerts/${alertId}/reply`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ reply_message: replyMessage }), + } + ); + + if (!response.ok) { + throw new Error(`回复消息失败: ${response.statusText}`); + } + + const data = await response.json(); + return data.success; + } catch (error) { + console.error('回复消息错误:', error); + throw error; + } +} + +/** + * 删除消息/告警 + */ +export async function deleteAlert(alertId: number): Promise { + try { + const response = await fetch( + `${API_BASE_URL}/family/alerts/${alertId}`, + { + method: 'DELETE', + } + ); + + if (!response.ok) { + throw new Error(`删除消息失败: ${response.statusText}`); + } + + const data = await response.json(); + return data.success; + } catch (error) { + console.error('删除消息错误:', error); + throw error; + } +} + +/** + * 获取消息统计 + */ +export async function getAlertStats(familyId: string): Promise<{ + today_count: number; + status_stats: { + unread: number; + unhandled: number; + handled: number; + }; + level_stats: Record; + type_stats: Record; +}> { + try { + const response = await fetch( + `${API_BASE_URL}/family/alerts/stats?family_id=${familyId}` + ); + + if (!response.ok) { + throw new Error(`获取统计失败: ${response.statusText}`); + } + + return await response.json(); + } catch (error) { + console.error('获取消息统计错误:', error); + throw error; + } +}