227 lines
7.3 KiB
TypeScript
227 lines
7.3 KiB
TypeScript
import { create } from "zustand";
|
|
import Taro from "@tarojs/taro";
|
|
import {
|
|
fetchUserProfile,
|
|
updateUserProfile,
|
|
UserInfoType,
|
|
checkNicknameChangeStatus as checkNicknameChangeStatusApi,
|
|
NicknameChangeStatus,
|
|
updateNickname as updateNicknameApi,
|
|
} from "@/services/userService";
|
|
import evaluateService, {
|
|
LastTimeTestResult,
|
|
} from "@/services/evaluateService";
|
|
import { useListStore } from "./listStore";
|
|
|
|
export interface UserState {
|
|
user: UserInfoType | {};
|
|
fetchUserInfo: () => Promise<UserInfoType | undefined>;
|
|
updateUserInfo: (userInfo: Partial<UserInfoType>) => void;
|
|
nicknameChangeStatus: Partial<NicknameChangeStatus>;
|
|
checkNicknameChangeStatus: (force?: boolean) => void;
|
|
updateNickname: (nickname: string) => void;
|
|
// NTRP 测试结果缓存
|
|
lastTestResult: LastTimeTestResult | null;
|
|
fetchLastTestResult: () => Promise<LastTimeTestResult | null>;
|
|
}
|
|
|
|
const getTimeNextDate = (time: string) => {
|
|
const date = new Date(time);
|
|
date.setDate(date.getDate() + 1);
|
|
const year = date.getFullYear();
|
|
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
const day = String(date.getDate()).padStart(2, "0");
|
|
return `${year}-${month}-${day}`;
|
|
};
|
|
|
|
// 请求锁,避免重复调用
|
|
let isFetchingLastTestResult = false;
|
|
let isCheckingNicknameStatus = false;
|
|
const CITY_CACHE_KEY = "USER_SELECTED_CITY";
|
|
|
|
export const useUser = create<UserState>()((set) => ({
|
|
user: {},
|
|
fetchUserInfo: async () => {
|
|
try {
|
|
const res = await fetchUserProfile();
|
|
const userData = res.data;
|
|
set({ user: userData });
|
|
|
|
// 优先使用缓存中的城市,不使用用户信息中的位置
|
|
// 检查是否有缓存的城市
|
|
|
|
const cachedCity = (Taro as any).getStorageSync?.(CITY_CACHE_KEY);
|
|
|
|
if (cachedCity && Array.isArray(cachedCity) && cachedCity.length === 2) {
|
|
// 如果有缓存的城市,使用缓存,不更新 area
|
|
console.log("[userStore] 检测到缓存的城市,使用缓存,不更新 area");
|
|
return userData;
|
|
}
|
|
|
|
// 只有当没有缓存时,才使用用户信息中的位置
|
|
if (userData?.last_location_province) {
|
|
const listStore = useListStore.getState();
|
|
const currentArea = listStore.area;
|
|
|
|
// 只有当 area 不存在时才使用用户信息中的位置
|
|
if (!currentArea) {
|
|
const newArea: [string, string] = [
|
|
userData.last_location_province || "",
|
|
userData.last_location_city || "",
|
|
];
|
|
listStore.updateArea(newArea);
|
|
// 保存到缓存
|
|
useUser.getState().updateCache(newArea);
|
|
}
|
|
}
|
|
|
|
return userData;
|
|
} catch (error) {
|
|
console.error("获取用户信息失败:", error);
|
|
return undefined;
|
|
}
|
|
},
|
|
|
|
// 更新缓存
|
|
updateCache: async (newArea: [string, string]) => {
|
|
try {
|
|
(Taro as any).setStorageSync?.(CITY_CACHE_KEY, newArea);
|
|
} catch (error) {
|
|
console.error("保存城市缓存失败:", error);
|
|
}
|
|
},
|
|
|
|
updateUserInfo: async (userInfo: Partial<UserInfoType>) => {
|
|
try {
|
|
// 先更新后端
|
|
await updateUserProfile(userInfo);
|
|
// 然后立即更新本地状态(乐观更新)
|
|
set((state) => {
|
|
const newUser = { ...state.user, ...userInfo };
|
|
|
|
// 当 userLastLocationProvince 更新时,同步更新 area
|
|
if (userInfo.last_location_province) {
|
|
const listStore = useListStore.getState();
|
|
const currentArea = listStore.area;
|
|
// 只有当 area 不存在或与 userLastLocationProvince 不一致时才更新
|
|
if (
|
|
!currentArea ||
|
|
currentArea[1] !== userInfo.last_location_province
|
|
) {
|
|
const newArea: [string, string] = [
|
|
userInfo.last_location_province || "",
|
|
userInfo.last_location_city || "",
|
|
];
|
|
listStore.updateArea(newArea);
|
|
}
|
|
}
|
|
|
|
return { user: newUser };
|
|
});
|
|
// 不再每次都重新获取完整用户信息,减少请求次数
|
|
// 只有在更新头像等需要服务器返回新URL的字段时才需要重新获取
|
|
// 如果需要确保数据一致性,可以在特定场景下手动调用 fetchUserInfo
|
|
} catch (error) {
|
|
console.error("更新用户信息失败:", error);
|
|
throw error;
|
|
}
|
|
},
|
|
nicknameChangeStatus: {},
|
|
checkNicknameChangeStatus: async (force = false) => {
|
|
// 如果正在请求中,直接返回,避免重复调用
|
|
if (isCheckingNicknameStatus) {
|
|
return;
|
|
}
|
|
// 如果已经有状态数据且不是强制更新,跳过,避免重复调用
|
|
if (!force) {
|
|
const currentState = useUser.getState();
|
|
if (
|
|
currentState.nicknameChangeStatus &&
|
|
Object.keys(currentState.nicknameChangeStatus).length > 0
|
|
) {
|
|
return;
|
|
}
|
|
}
|
|
try {
|
|
isCheckingNicknameStatus = true;
|
|
const res = await checkNicknameChangeStatusApi();
|
|
const { next_period_start_time } = res.data;
|
|
set({
|
|
nicknameChangeStatus: {
|
|
...res.data,
|
|
next_period_start_date: getTimeNextDate(next_period_start_time),
|
|
},
|
|
});
|
|
} catch (error) {
|
|
console.error("检查昵称变更状态失败:", error);
|
|
} finally {
|
|
isCheckingNicknameStatus = false;
|
|
}
|
|
},
|
|
updateNickname: async (nickname) => {
|
|
try {
|
|
await updateNicknameApi(nickname);
|
|
// 更新昵称后强制更新状态
|
|
await useUser.getState().checkNicknameChangeStatus(true);
|
|
set((state) => ({
|
|
user: { ...state.user, nickname },
|
|
}));
|
|
} catch (error) {
|
|
console.error("更新用户昵称失败:", error);
|
|
}
|
|
},
|
|
// NTRP 测试结果缓存
|
|
lastTestResult: null,
|
|
fetchLastTestResult: async () => {
|
|
// 如果已经有缓存数据,直接返回,避免重复调用
|
|
const currentState = useUser.getState();
|
|
if (currentState.lastTestResult) {
|
|
return currentState.lastTestResult;
|
|
}
|
|
// 如果正在请求中,等待请求完成,避免重复调用
|
|
if (isFetchingLastTestResult) {
|
|
// 等待请求完成,最多等待 3 秒
|
|
let waitCount = 0;
|
|
while (isFetchingLastTestResult && waitCount < 30) {
|
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
waitCount++;
|
|
const state = useUser.getState();
|
|
if (state.lastTestResult) {
|
|
return state.lastTestResult;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
try {
|
|
isFetchingLastTestResult = true;
|
|
const res = await evaluateService.getLastResult();
|
|
if (res.code === 0) {
|
|
set({ lastTestResult: res.data });
|
|
return res.data;
|
|
}
|
|
return null;
|
|
} catch (error) {
|
|
console.error("获取NTRP测试结果失败:", error);
|
|
return null;
|
|
} finally {
|
|
isFetchingLastTestResult = false;
|
|
}
|
|
},
|
|
}));
|
|
|
|
export const useUserInfo = () => useUser((state) => state.user);
|
|
export const useNicknameChangeStatus = () =>
|
|
useUser((state) => state.nicknameChangeStatus);
|
|
|
|
export const useUserActions = () =>
|
|
useUser((state) => ({
|
|
fetchUserInfo: state.fetchUserInfo,
|
|
updateCache: state.updateCache,
|
|
updateUserInfo: state.updateUserInfo,
|
|
checkNicknameChangeStatus: state.checkNicknameChangeStatus,
|
|
updateNickname: state.updateNickname,
|
|
fetchLastTestResult: state.fetchLastTestResult,
|
|
}));
|
|
|
|
export const useLastTestResult = () => useUser((state) => state.lastTestResult);
|