import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit'; import * as healthProfileApi from '@/services/healthProfile'; import { AppDispatch, RootState } from './index'; import { HistoryData, HistoryItemDetail, MedicalRecordItem, MedicalRecordsData, MedicalRecordType, } from '@/services/healthProfile'; // 健康数据类型定义 export interface FitnessRingsData { activeCalories: number; activeCaloriesGoal: number; exerciseMinutes: number; exerciseMinutesGoal: number; standHours: number; standHoursGoal: number; } export interface HealthData { activeCalories: number | null; heartRate: number | null; activeEnergyBurned: number; activeCaloriesGoal: number; exerciseMinutes: number; exerciseMinutesGoal: number; standHours: number; standHoursGoal: number; } // 健康史数据类型定义 export interface HistoryItemDetail { id: string; name: string; date?: string; // ISO Date string isRecommendation?: boolean; } export interface HistoryData { [key: string]: { hasHistory: boolean | null; items: HistoryItemDetail[]; }; } export interface HealthState { // 按日期存储的历史数据 dataByDate: Record; // 健康史数据 historyData: HistoryData; // 就医资料数据 medicalRecords: MedicalRecordsData; // 加载状态 loading: boolean; error: string | null; // 最后更新时间 lastUpdateTime: string | null; } // 初始状态 const initialState: HealthState = { dataByDate: {}, historyData: { allergy: { hasHistory: null, items: [] }, disease: { hasHistory: null, items: [] }, surgery: { hasHistory: null, items: [] }, familyDisease: { hasHistory: null, items: [] }, }, medicalRecords: { records: [], prescriptions: [], }, loading: false, error: null, lastUpdateTime: null, }; const healthSlice = createSlice({ name: 'health', initialState, reducers: { // 设置加载状态 setLoading: (state, action: PayloadAction) => { state.loading = action.payload; }, // 设置错误信息 setError: (state, action: PayloadAction) => { state.error = action.payload; }, // 设置完整的健康数据 setHealthData: (state, action: PayloadAction<{ date: string; data: HealthData; }>) => { const { date, data } = action.payload; // 存储到历史数据 state.dataByDate[date] = data; state.lastUpdateTime = new Date().toISOString(); }, // 清除特定日期的数据 clearHealthDataForDate: (state, action: PayloadAction) => { const date = action.payload; delete state.dataByDate[date]; }, // 清除所有健康数据 clearAllHealthData: (state) => { state.dataByDate = {}; state.error = null; state.lastUpdateTime = null; }, // 更新健康史数据(本地更新,用于乐观更新或离线模式) updateHistoryData: (state, action: PayloadAction<{ type: string; data: { hasHistory: boolean | null; items: HistoryItemDetail[]; }; }>) => { const { type, data } = action.payload; state.historyData[type] = data; state.lastUpdateTime = new Date().toISOString(); }, // 设置完整的健康史数据(从服务端同步) setHistoryData: (state, action: PayloadAction) => { state.historyData = action.payload; state.lastUpdateTime = new Date().toISOString(); }, // 清除健康史数据 clearHistoryData: (state) => { state.historyData = { allergy: { hasHistory: null, items: [] }, disease: { hasHistory: null, items: [] }, surgery: { hasHistory: null, items: [] }, familyDisease: { hasHistory: null, items: [] }, }; }, // 更新就医资料列表 setMedicalRecords: (state, action: PayloadAction) => { state.medicalRecords = action.payload; state.lastUpdateTime = new Date().toISOString(); }, // 添加就医资料项 addMedicalRecordItem: (state, action: PayloadAction) => { const item = action.payload; if (item.type === 'medical_record') { state.medicalRecords.records.unshift(item); } else { state.medicalRecords.prescriptions.unshift(item); } state.lastUpdateTime = new Date().toISOString(); }, // 删除就医资料项 removeMedicalRecordItem: (state, action: PayloadAction<{ id: string; type: MedicalRecordType }>) => { const { id, type } = action.payload; if (type === 'medical_record') { state.medicalRecords.records = state.medicalRecords.records.filter(item => item.id !== id); } else { state.medicalRecords.prescriptions = state.medicalRecords.prescriptions.filter(item => item.id !== id); } state.lastUpdateTime = new Date().toISOString(); }, }, extraReducers: (builder) => { builder // 获取健康史 .addCase(fetchHealthHistory.pending, (state) => { state.loading = true; state.error = null; }) .addCase(fetchHealthHistory.fulfilled, (state, action) => { state.loading = false; // 转换服务端数据格式到本地格式 const serverData = action.payload; const categories = ['allergy', 'disease', 'surgery', 'familyDisease'] as const; categories.forEach(category => { if (serverData[category]) { state.historyData[category] = { hasHistory: serverData[category].hasHistory, items: serverData[category].items.map(item => ({ id: item.id, name: item.name, date: item.diagnosisDate, isRecommendation: item.isRecommendation, })), }; } }); state.lastUpdateTime = new Date().toISOString(); }) .addCase(fetchHealthHistory.rejected, (state, action) => { state.loading = false; state.error = action.payload as string; }) // 保存健康史分类 .addCase(saveHealthHistoryCategory.pending, (state) => { state.loading = true; state.error = null; }) .addCase(saveHealthHistoryCategory.fulfilled, (state, action) => { state.loading = false; const { category, data } = action.payload; // 更新对应分类的数据 state.historyData[category] = { hasHistory: data.hasHistory, items: data.items.map(item => ({ id: item.id, name: item.name, date: item.diagnosisDate, isRecommendation: item.isRecommendation, })), }; state.lastUpdateTime = new Date().toISOString(); }) .addCase(saveHealthHistoryCategory.rejected, (state, action) => { state.loading = false; state.error = action.payload as string; }) // 获取健康史进度 .addCase(fetchHealthHistoryProgress.rejected, (state, action) => { state.error = action.payload as string; }) // 获取就医资料 .addCase(fetchMedicalRecords.pending, (state) => { state.loading = true; state.error = null; }) .addCase(fetchMedicalRecords.fulfilled, (state, action) => { state.loading = false; state.medicalRecords = action.payload; state.lastUpdateTime = new Date().toISOString(); }) .addCase(fetchMedicalRecords.rejected, (state, action) => { state.loading = false; state.error = action.payload as string; }) // 添加就医资料 .addCase(addNewMedicalRecord.fulfilled, (state, action) => { const item = action.payload; if (item.type === 'medical_record') { state.medicalRecords.records.unshift(item); } else { state.medicalRecords.prescriptions.unshift(item); } state.lastUpdateTime = new Date().toISOString(); }) // 删除就医资料 .addCase(deleteMedicalRecordItem.fulfilled, (state, action) => { const { id, type } = action.payload; if (type === 'medical_record') { state.medicalRecords.records = state.medicalRecords.records.filter(item => item.id !== id); } else { state.medicalRecords.prescriptions = state.medicalRecords.prescriptions.filter(item => item.id !== id); } state.lastUpdateTime = new Date().toISOString(); }); }, }); // Action creators export const { setLoading, setError, setHealthData, clearHealthDataForDate, clearAllHealthData, updateHistoryData, setHistoryData, clearHistoryData, setMedicalRecords, addMedicalRecordItem, removeMedicalRecordItem, } = healthSlice.actions; // Thunk action to fetch and set health data for a specific date export const fetchHealthDataForDate = (date: Date) => { return async (dispatch: AppDispatch, getState: () => RootState) => { try { dispatch(setLoading(true)); dispatch(setError(null)); // 这里可以添加实际的 API 调用逻辑 // 目前我们假设数据已经通过其他方式获取 dispatch(setLoading(false)); } catch (error) { dispatch(setError(error instanceof Error ? error.message : '获取健康数据失败')); dispatch(setLoading(false)); } }; }; // ==================== 健康史 API Thunks ==================== /** * 从服务端获取完整健康史数据 */ export const fetchHealthHistory = createAsyncThunk( 'health/fetchHistory', async (_, { rejectWithValue }) => { try { const data = await healthProfileApi.getHealthHistory(); return data; } catch (err: any) { return rejectWithValue(err?.message ?? '获取健康史失败'); } } ); /** * 保存健康史分类到服务端 */ export const saveHealthHistoryCategory = createAsyncThunk( 'health/saveHistoryCategory', async ( { category, data, }: { category: healthProfileApi.HealthHistoryCategory; data: healthProfileApi.UpdateHealthHistoryRequest; }, { rejectWithValue } ) => { try { const result = await healthProfileApi.updateHealthHistory(category, data); return { category, data: result }; } catch (err: any) { return rejectWithValue(err?.message ?? '保存健康史失败'); } } ); /** * 获取健康史完成进度 */ export const fetchHealthHistoryProgress = createAsyncThunk( 'health/fetchHistoryProgress', async (_, { rejectWithValue }) => { try { const data = await healthProfileApi.getHealthHistoryProgress(); return data; } catch (err: any) { return rejectWithValue(err?.message ?? '获取健康史进度失败'); } } ); // ==================== 就医资料 API Thunks ==================== /** * 获取就医资料 */ export const fetchMedicalRecords = createAsyncThunk( 'health/fetchMedicalRecords', async (_, { rejectWithValue }) => { try { const data = await healthProfileApi.getMedicalRecords(); return data; } catch (err: any) { return rejectWithValue(err?.message ?? '获取就医资料失败'); } } ); /** * 添加就医资料 */ export const addNewMedicalRecord = createAsyncThunk( 'health/addMedicalRecord', async (data: Omit, { rejectWithValue }) => { try { const result = await healthProfileApi.addMedicalRecord(data); return result; } catch (err: any) { return rejectWithValue(err?.message ?? '添加就医资料失败'); } } ); /** * 删除就医资料 */ export const deleteMedicalRecordItem = createAsyncThunk( 'health/deleteMedicalRecord', async ({ id, type }: { id: string; type: MedicalRecordType }, { rejectWithValue }) => { try { await healthProfileApi.deleteMedicalRecord(id); return { id, type }; } catch (err: any) { return rejectWithValue(err?.message ?? '删除就医资料失败'); } } ); // Selectors export const selectHealthDataByDate = (date: string) => (state: RootState) => state.health.dataByDate[date]; export const selectHealthLoading = (state: RootState) => state.health.loading; export const selectHealthError = (state: RootState) => state.health.error; export const selectLastUpdateTime = (state: RootState) => state.health.lastUpdateTime; export const selectHistoryData = (state: RootState) => state.health.historyData; export const selectMedicalRecords = (state: RootState) => state.health.medicalRecords; // 计算健康史完成度的 selector export const selectHealthHistoryProgress = (state: RootState) => { const historyData = state.health.historyData; const categories = ['allergy', 'disease', 'surgery', 'familyDisease']; let answeredCount = 0; categories.forEach(category => { const data = historyData[category]; // 只要回答了是否有历史(hasHistory !== null),就算已完成 if (data && data.hasHistory !== null) { answeredCount++; } }); return Math.round((answeredCount / categories.length) * 100); }; export default healthSlice.reducer;