Files
vf_react/src/store/slices/subscriptionSlice.js

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;