hookehuyr

fix(user): 修复冷启动时接口抢在登录态前请求的竞态问题

- 新增 waitForLoginStatusReady 方法供外部等待登录态就绪
- checkLoginStatus 使用 Promise 缓存避免并发重复调用
- 首页 fetchHotArticles 调用前等待登录态初始化完成
- 清除首页残留的导航栏标题配置

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
/*
* @Date: 2025-06-28 10:33:00
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2026-01-30 10:07:28
* @LastEditTime: 2026-05-28 17:28:54
* @FilePath: /manulife-weapp/src/app.config.js
* @Description: 小程序配置文件
*/
......@@ -57,7 +57,7 @@ export default {
window: {
backgroundTextStyle: 'light',
navigationBarBackgroundColor: '#fff',
navigationBarTitleText: '西园寺预约',
navigationBarTitleText: '',
navigationBarTextStyle: 'black',
},
// 开发环境开启调试模式
......
......@@ -355,6 +355,10 @@ const hotArticlesLoading = ref(false);
const fetchHotArticles = async () => {
try {
hotArticlesLoading.value = true;
// 冷启动时先等登录态初始化完成,避免 week_hot 抢在 session 建立前发出请求
await userStore.waitForLoginStatusReady();
const res = await weekHotAPI({
page: 0,
limit: 10
......
......@@ -29,9 +29,15 @@ export const useUserStore = defineStore('user', () => {
/** 加载状态 */
const loading = ref(false)
/** 登录态是否已经完成过首次检查 */
const loginStatusChecked = ref(false)
/** 上次获取用户信息的时间戳(用于防抖) */
let lastFetchTime = 0
/** 进行中的登录态检查 Promise,用于并发复用 */
let loginStatusPromise = null
/** 防抖时间间隔(毫秒) */
const FETCH_DEBOUNCE_TIME = 5000
......@@ -48,41 +54,67 @@ export const useUserStore = defineStore('user', () => {
* await userStore.checkLoginStatus()
*/
async function checkLoginStatus() {
loading.value = true
try {
// 1. 确保 openid 已授权并尝试自动登录
const user = await ensureOpenidAuthorized()
if (loginStatusPromise) {
return loginStatusPromise
}
if (user) {
// miniProgramAuthAPI 返回了用户信息,说明已自动登录
userInfo.value = user
isOpenid.value = true
isLoggedIn.value = true
return
}
loginStatusPromise = (async () => {
loading.value = true
// 2. 查询登录状态
const res = await loginStatusAPI()
try {
// 1. 确保 openid 已授权并尝试自动登录
const user = await ensureOpenidAuthorized()
if (res.code === 1) {
isOpenid.value = res.data.is_openid
isLoggedIn.value = res.data.is_login
if (user) {
// miniProgramAuthAPI 返回了用户信息,说明已自动登录
userInfo.value = user
isOpenid.value = true
isLoggedIn.value = true
return
}
// 3. 如果已登录,获取用户信息
if (isLoggedIn.value) {
await fetchUserInfo()
// 2. 查询登录状态
const res = await loginStatusAPI()
if (res.code === 1) {
isOpenid.value = res.data.is_openid
isLoggedIn.value = res.data.is_login
// 3. 如果已登录,获取用户信息
if (isLoggedIn.value) {
await fetchUserInfo()
}
// 注意:这里不跳转登录页,让用户可以浏览小程序
// 当用户操作触发接口返回 401 时,会自动跳转登录页
} else {
throw new Error(res.msg || '查询登录状态失败')
}
// 注意:这里不跳转登录页,让用户可以浏览小程序
// 当用户操作触发接口返回 401 时,会自动跳转登录页
} else {
throw new Error(res.msg || '查询登录状态失败')
} catch (err) {
console.error('检查登录状态失败:', err)
throw err
} finally {
loading.value = false
loginStatusChecked.value = true
loginStatusPromise = null
}
} catch (err) {
console.error('检查登录状态失败:', err)
throw err
} finally {
loading.value = false
})()
return loginStatusPromise
}
/**
* 等待登录态初始化完成
* @description 冷启动时复用 App.onLaunch 的登录态检查,避免页面抢跑请求
* @returns {Promise<void>}
*/
async function waitForLoginStatusReady() {
if (loginStatusPromise) {
await loginStatusPromise
return
}
if (!loginStatusChecked.value) {
await checkLoginStatus()
}
}
......@@ -235,12 +267,14 @@ export const useUserStore = defineStore('user', () => {
isOpenid,
isLoggedIn,
loading,
loginStatusChecked,
// 计算属性
tabBarBadges,
// 方法
checkLoginStatus,
waitForLoginStatusReady,
fetchUserInfo,
login,
logout
......