blob: 289ce1656507389d732207b1d247620726d9317f [file] [log] [blame]
223010144ce05872025-06-08 22:33:28 +08001// src/feature/auth/authSlice.ts
2
2230101462240ab2025-06-07 09:28:16 +08003import { createAsyncThunk, createSlice, type PayloadAction } from "@reduxjs/toolkit";
223010144ce05872025-06-08 22:33:28 +08004import type { AuthState } from "./types";
22301014b1477f72025-06-07 22:54:40 +08005import type { LoginRequest } from "../../api/Auth/type";
6import AuthAPI from "../../api/Auth/AuthApi";
22301014bc4616f2025-06-03 16:59:44 +08007
22301014b1477f72025-06-07 22:54:40 +08008// 获取本地存储的 token
9const storedToken = localStorage.getItem('token');
22301014bc4616f2025-06-03 16:59:44 +080010
11const initialState: AuthState = {
22301014b1477f72025-06-07 22:54:40 +080012 token: storedToken || '',
22301014bc4616f2025-06-03 16:59:44 +080013 loading: false,
223010144ce05872025-06-08 22:33:28 +080014 isAuth: !!storedToken, // 如果有token就设为true
15 error: '',
16};
22301014bc4616f2025-06-03 16:59:44 +080017
2230101462240ab2025-06-07 09:28:16 +080018export const loginUser = createAsyncThunk<
19 {token: string},
20 LoginRequest,
21 { rejectValue: string }
22>(
23 'auth/login',
24 async (loginRequest: LoginRequest, { rejectWithValue }) => {
25 try {
22301014b1477f72025-06-07 22:54:40 +080026 const response = await AuthAPI.login(loginRequest);
2230101462240ab2025-06-07 09:28:16 +080027 if(response.data.code == 0) {
28 return {token: response.data.data};
29 }
30 else
31 return rejectWithValue(response.data.message);
32 } catch {
33 return rejectWithValue('登录失败');
34 }
35 }
36);
37
223010144ce05872025-06-08 22:33:28 +080038// 新增:从localStorage恢复登录状态
39export const loginFromLocalStorage = createAsyncThunk<
40 {token: string} | {empty: true},
41 void,
42 { rejectValue: string }
43>(
44 'auth/loginFromLocalStorage',
45 async (_, { rejectWithValue, dispatch }) => {
46 try {
47 const token = localStorage.getItem('token');
48 if (!token) {
49 // 返回空状态而不是错误
50 return { empty: true };
51 }
52
53 // 直接使用refreshToken来验证token有效性
54 const result = await dispatch(refreshToken(token));
55 if (refreshToken.fulfilled.match(result)) {
56 // refresh成功,返回新的token
57 return { token: result.payload.token };
58 } else {
59 // refresh失败,token无效
60 localStorage.removeItem('token');
61 return rejectWithValue('token已失效,需要重新登录');
62 }
63 } catch {
64 localStorage.removeItem('token');
65 return rejectWithValue('恢复登录状态失败');
66 }
67 }
68);
69
2230101462240ab2025-06-07 09:28:16 +080070export const refreshToken = createAsyncThunk<
71 {token: string},
72 string,
73 { rejectValue: string }
74>(
2230101462240ab2025-06-07 09:28:16 +080075 'auth/refresh',
76 async (oldToken: string, { rejectWithValue }) => {
77 try {
22301014b1477f72025-06-07 22:54:40 +080078 const response = await AuthAPI.refreshToken(oldToken);
2230101462240ab2025-06-07 09:28:16 +080079 if(response.data.code == 0)
80 return {token: response.data.data};
81 else
82 return rejectWithValue(response.data.message);
83 } catch {
84 return rejectWithValue('刷新失败');
85 }
86 }
87);
88
22301014bc4616f2025-06-03 16:59:44 +080089const authSlice = createSlice({
90 name: 'auth',
91 initialState,
2230101462240ab2025-06-07 09:28:16 +080092 reducers: {
93 logout: (state) => {
94 state.token = '';
95 state.isAuth = false;
223010144ce05872025-06-08 22:33:28 +080096 localStorage.clear();
2230101462240ab2025-06-07 09:28:16 +080097 },
98
223010144ce05872025-06-08 22:33:28 +080099 // 清除错误信息
100 clearError: (state) => {
101 state.error = '';
102 },
103 },
104 extraReducers: (builder) => {
105 // 处理普通登录的异步操作
106 builder
107 .addCase(loginUser.pending, (state) => {
108 state.loading = true;
109 state.error = '';
110 })
111 .addCase(loginUser.fulfilled, (state, action: PayloadAction<{token: string}>) => {
112 state.loading = false;
113 state.token = action.payload.token;
114 state.isAuth = true;
115 localStorage.setItem('token', state.token);
116 })
117 .addCase(loginUser.rejected, (state, action) => {
118 state.loading = false;
119 state.error = action.payload ? action.payload : '';
120 });
121
122 // 处理从localStorage恢复登录状态
123 builder
124 .addCase(loginFromLocalStorage.pending, (state) => {
125 state.loading = true;
126 state.error = '';
127 })
128 .addCase(loginFromLocalStorage.fulfilled, (state, action) => {
129 state.loading = false;
130 if ('token' in action.payload) {
131 // 有token的情况
132 state.token = action.payload.token;
133 state.isAuth = true;
134 } else {
135 // 空token的情况
136 state.token = '';
137 state.isAuth = false;
138 }
139 })
140 .addCase(loginFromLocalStorage.rejected, (state, action) => {
141 state.loading = false;
142 state.token = '';
143 state.isAuth = false;
144 state.error = action.payload ? action.payload : '';
145 localStorage.removeItem('token');
146 });
147
148 // 处理刷新 token 的异步操作
149 builder
150 .addCase(refreshToken.pending, (state) => {
151 state.loading = true;
152 state.error = '';
153 })
154 .addCase(refreshToken.fulfilled, (state, action) => {
155 state.loading = false;
156 state.token = action.payload.token;
157 state.isAuth = true;
158 localStorage.setItem('token', state.token);
159 })
160 .addCase(refreshToken.rejected, (state, action) => {
161 state.loading = false;
162 state.error = action.payload ? action.payload : '';
163 state.isAuth = false;
164 });
165 },
22301014bc4616f2025-06-03 16:59:44 +0800166});
22301014b1477f72025-06-07 22:54:40 +0800167
223010144ce05872025-06-08 22:33:28 +0800168export const { logout, clearError } = authSlice.actions;
22301014bc4616f2025-06-03 16:59:44 +0800169export default authSlice.reducer;