/** * 家庭健康管理 Redux Slice */ import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit'; import * as healthProfileApi from '@/services/healthProfile'; import { RootState } from './index'; // ==================== State 类型定义 ==================== export interface FamilyHealthState { // 家庭组信息 familyGroup: healthProfileApi.FamilyGroup | null; // 家庭成员列表 members: healthProfileApi.FamilyMember[]; // 邀请码信息 inviteCode: healthProfileApi.InviteCode | null; // 加载状态 loading: boolean; membersLoading: boolean; inviteLoading: boolean; // 错误信息 error: string | null; } // ==================== 初始状态 ==================== const initialState: FamilyHealthState = { familyGroup: null, members: [], inviteCode: null, loading: false, membersLoading: false, inviteLoading: false, error: null, }; // ==================== Async Thunks ==================== /** * 获取用户所属家庭组 */ export const fetchFamilyGroup = createAsyncThunk( 'familyHealth/fetchGroup', async (_, { rejectWithValue }) => { try { const data = await healthProfileApi.getFamilyGroup(); return data; } catch (err: any) { return rejectWithValue(err?.message ?? '获取家庭组失败'); } } ); /** * 创建家庭组 */ export const createFamilyGroup = createAsyncThunk( 'familyHealth/createGroup', async (name: string, { rejectWithValue }) => { try { const data = await healthProfileApi.createFamilyGroup(name); return data; } catch (err: any) { return rejectWithValue(err?.message ?? '创建家庭组失败'); } } ); /** * 生成邀请码 */ export const generateInviteCode = createAsyncThunk( 'familyHealth/generateInvite', async (expiresInHours: number = 24, { rejectWithValue }) => { try { const data = await healthProfileApi.generateInviteCode(expiresInHours); return data; } catch (err: any) { return rejectWithValue(err?.message ?? '生成邀请码失败'); } } ); /** * 加入家庭组 */ export const joinFamilyGroup = createAsyncThunk( 'familyHealth/joinGroup', async ( { inviteCode, relationship }: { inviteCode: string; relationship: string }, { rejectWithValue } ) => { try { const data = await healthProfileApi.joinFamilyGroup(inviteCode, relationship); return data; } catch (err: any) { return rejectWithValue(err?.message ?? '加入家庭组失败'); } } ); /** * 获取家庭成员列表 */ export const fetchFamilyMembers = createAsyncThunk( 'familyHealth/fetchMembers', async (_, { rejectWithValue }) => { try { const data = await healthProfileApi.getFamilyMembers(); return data; } catch (err: any) { return rejectWithValue(err?.message ?? '获取家庭成员失败'); } } ); /** * 更新家庭成员权限 */ export const updateFamilyMember = createAsyncThunk( 'familyHealth/updateMember', async ( { memberId, permissions, }: { memberId: string; permissions: healthProfileApi.UpdateMemberPermissionsRequest; }, { rejectWithValue } ) => { try { const data = await healthProfileApi.updateFamilyMember(memberId, permissions); return data; } catch (err: any) { return rejectWithValue(err?.message ?? '更新成员权限失败'); } } ); /** * 移除家庭成员 */ export const removeFamilyMember = createAsyncThunk( 'familyHealth/removeMember', async (memberId: string, { rejectWithValue }) => { try { await healthProfileApi.removeFamilyMember(memberId); return memberId; } catch (err: any) { return rejectWithValue(err?.message ?? '移除成员失败'); } } ); /** * 退出家庭组 */ export const leaveFamilyGroup = createAsyncThunk( 'familyHealth/leaveGroup', async (_, { rejectWithValue }) => { try { await healthProfileApi.leaveFamilyGroup(); return true; } catch (err: any) { return rejectWithValue(err?.message ?? '退出家庭组失败'); } } ); // ==================== Slice ==================== const familyHealthSlice = createSlice({ name: 'familyHealth', initialState, reducers: { // 清除错误 clearError: (state) => { state.error = null; }, // 清除邀请码 clearInviteCode: (state) => { state.inviteCode = null; }, // 重置状态(用于登出时) resetFamilyHealth: () => initialState, }, extraReducers: (builder) => { builder // 获取家庭组 .addCase(fetchFamilyGroup.pending, (state) => { state.loading = true; state.error = null; }) .addCase(fetchFamilyGroup.fulfilled, (state, action) => { state.loading = false; state.familyGroup = action.payload; }) .addCase(fetchFamilyGroup.rejected, (state, action) => { state.loading = false; state.error = action.payload as string; }) // 创建家庭组 .addCase(createFamilyGroup.pending, (state) => { state.loading = true; state.error = null; }) .addCase(createFamilyGroup.fulfilled, (state, action) => { state.loading = false; state.familyGroup = action.payload; }) .addCase(createFamilyGroup.rejected, (state, action) => { state.loading = false; state.error = action.payload as string; }) // 生成邀请码 .addCase(generateInviteCode.pending, (state) => { state.inviteLoading = true; state.error = null; }) .addCase(generateInviteCode.fulfilled, (state, action) => { state.inviteLoading = false; state.inviteCode = action.payload; }) .addCase(generateInviteCode.rejected, (state, action) => { state.inviteLoading = false; state.error = action.payload as string; }) // 加入家庭组 .addCase(joinFamilyGroup.pending, (state) => { state.loading = true; state.error = null; }) .addCase(joinFamilyGroup.fulfilled, (state, action) => { state.loading = false; state.familyGroup = action.payload; }) .addCase(joinFamilyGroup.rejected, (state, action) => { state.loading = false; state.error = action.payload as string; }) // 获取家庭成员 .addCase(fetchFamilyMembers.pending, (state) => { state.membersLoading = true; state.error = null; }) .addCase(fetchFamilyMembers.fulfilled, (state, action) => { state.membersLoading = false; state.members = action.payload; }) .addCase(fetchFamilyMembers.rejected, (state, action) => { state.membersLoading = false; state.error = action.payload as string; }) // 更新成员权限 .addCase(updateFamilyMember.fulfilled, (state, action) => { const updatedMember = action.payload; const index = state.members.findIndex((m) => m.id === updatedMember.id); if (index !== -1) { state.members[index] = updatedMember; } }) .addCase(updateFamilyMember.rejected, (state, action) => { state.error = action.payload as string; }) // 移除成员 .addCase(removeFamilyMember.fulfilled, (state, action) => { const memberId = action.payload; state.members = state.members.filter((m) => m.id !== memberId); if (state.familyGroup) { state.familyGroup.memberCount = Math.max(0, state.familyGroup.memberCount - 1); } }) .addCase(removeFamilyMember.rejected, (state, action) => { state.error = action.payload as string; }) // 退出家庭组 .addCase(leaveFamilyGroup.fulfilled, (state) => { state.familyGroup = null; state.members = []; state.inviteCode = null; }) .addCase(leaveFamilyGroup.rejected, (state, action) => { state.error = action.payload as string; }); }, }); // ==================== Actions ==================== export const { clearError, clearInviteCode, resetFamilyHealth } = familyHealthSlice.actions; // ==================== Selectors ==================== export const selectFamilyGroup = (state: RootState) => state.familyHealth.familyGroup; export const selectFamilyMembers = (state: RootState) => state.familyHealth.members; export const selectInviteCode = (state: RootState) => state.familyHealth.inviteCode; export const selectFamilyHealthLoading = (state: RootState) => state.familyHealth.loading; export const selectFamilyMembersLoading = (state: RootState) => state.familyHealth.membersLoading; export const selectInviteLoading = (state: RootState) => state.familyHealth.inviteLoading; export const selectFamilyHealthError = (state: RootState) => state.familyHealth.error; // 判断当前用户是否是家庭组 owner export const selectIsOwner = (state: RootState) => { const currentUserId = state.user.profile?.id; const familyGroup = state.familyHealth.familyGroup; return currentUserId && familyGroup && familyGroup.ownerId === currentUserId; }; // 判断当前用户是否是管理员(owner 或 admin) export const selectIsAdmin = (state: RootState) => { const currentUserId = state.user.profile?.id; const members = state.familyHealth.members; const currentMember = members.find((m) => m.userId === currentUserId); return currentMember && (currentMember.role === 'owner' || currentMember.role === 'admin'); }; export default familyHealthSlice.reducer;