147 lines
4.8 KiB
JavaScript
147 lines
4.8 KiB
JavaScript
// src/store/slices/subscriptionSlice.js
|
|
// 订阅信息状态管理 Redux Slice
|
|
|
|
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
|
|
import { logger } from '../../utils/logger';
|
|
import { getApiBase } from '../../utils/apiConfig';
|
|
|
|
/**
|
|
* 异步 Thunk: 获取用户订阅信息
|
|
*/
|
|
export const fetchSubscriptionInfo = createAsyncThunk(
|
|
'subscription/fetchInfo',
|
|
async (_, { rejectWithValue }) => {
|
|
try {
|
|
const base = getApiBase();
|
|
logger.debug('subscriptionSlice', '开始加载订阅信息');
|
|
|
|
const response = await fetch(base + '/api/subscription/current', {
|
|
credentials: 'include',
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP ${response.status}`);
|
|
}
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success && data.data) {
|
|
// 数据标准化处理
|
|
const normalizedData = {
|
|
type: (data.data.type || data.data.subscription_type || 'free').toLowerCase(),
|
|
status: data.data.status || 'active',
|
|
days_left: data.data.days_left || 0,
|
|
is_active: data.data.is_active !== false,
|
|
end_date: data.data.end_date || null
|
|
};
|
|
|
|
logger.info('subscriptionSlice', '订阅信息加载成功', normalizedData);
|
|
return normalizedData;
|
|
} else {
|
|
// API 返回成功但无数据,返回默认免费版
|
|
return {
|
|
type: 'free',
|
|
status: 'active',
|
|
days_left: 0,
|
|
is_active: false,
|
|
end_date: null
|
|
};
|
|
}
|
|
} catch (error) {
|
|
logger.error('subscriptionSlice', '加载订阅信息失败', error);
|
|
return rejectWithValue(error.message);
|
|
}
|
|
}
|
|
);
|
|
|
|
/**
|
|
* Subscription Slice
|
|
* 管理用户订阅信息和订阅 Modal 状态
|
|
*/
|
|
const subscriptionSlice = createSlice({
|
|
name: 'subscription',
|
|
initialState: {
|
|
// 订阅信息
|
|
info: {
|
|
type: 'free',
|
|
status: 'active',
|
|
days_left: 0,
|
|
is_active: false,
|
|
end_date: null
|
|
},
|
|
// 加载状态
|
|
loading: false,
|
|
loaded: false, // 是否已加载过(用于防止重复请求)
|
|
error: null,
|
|
// 订阅 Modal 状态
|
|
isModalOpen: false,
|
|
},
|
|
reducers: {
|
|
/**
|
|
* 打开订阅 Modal
|
|
*/
|
|
openModal: (state) => {
|
|
state.isModalOpen = true;
|
|
logger.debug('subscriptionSlice', '打开订阅 Modal');
|
|
},
|
|
|
|
/**
|
|
* 关闭订阅 Modal
|
|
*/
|
|
closeModal: (state) => {
|
|
state.isModalOpen = false;
|
|
logger.debug('subscriptionSlice', '关闭订阅 Modal');
|
|
},
|
|
|
|
/**
|
|
* 重置为免费版 (用户登出时调用)
|
|
*/
|
|
resetToFree: (state) => {
|
|
state.info = {
|
|
type: 'free',
|
|
status: 'active',
|
|
days_left: 0,
|
|
is_active: false,
|
|
end_date: null
|
|
};
|
|
state.loading = false;
|
|
state.loaded = false; // 重置已加载标记,下次登录时重新获取
|
|
state.error = null;
|
|
},
|
|
},
|
|
extraReducers: (builder) => {
|
|
builder
|
|
// fetchSubscriptionInfo - pending
|
|
.addCase(fetchSubscriptionInfo.pending, (state) => {
|
|
state.loading = true;
|
|
state.error = null;
|
|
})
|
|
// fetchSubscriptionInfo - fulfilled
|
|
.addCase(fetchSubscriptionInfo.fulfilled, (state, action) => {
|
|
state.loading = false;
|
|
state.loaded = true; // 标记已加载
|
|
state.info = action.payload;
|
|
state.error = null;
|
|
})
|
|
// fetchSubscriptionInfo - rejected
|
|
.addCase(fetchSubscriptionInfo.rejected, (state, action) => {
|
|
state.loading = false;
|
|
state.error = action.payload || 'Unknown error';
|
|
// 加载失败时保持当前状态,不重置为免费版
|
|
});
|
|
},
|
|
});
|
|
|
|
// 导出 actions
|
|
export const { openModal, closeModal, resetToFree } = subscriptionSlice.actions;
|
|
|
|
// 导出 selectors
|
|
export const selectSubscriptionInfo = (state) => state.subscription.info;
|
|
export const selectSubscriptionLoading = (state) => state.subscription.loading;
|
|
export const selectSubscriptionLoaded = (state) => state.subscription.loaded;
|
|
export const selectSubscriptionError = (state) => state.subscription.error;
|
|
export const selectSubscriptionModalOpen = (state) => state.subscription.isModalOpen;
|
|
|
|
// 导出 reducer
|
|
export default subscriptionSlice.reducer;
|