Преглед изворни кода

fix: 修复账号同步逻辑和粉丝/作品数更新条件

修复粉丝数和作品数从非零值变为零时的错误处理逻辑,避免因数据获取失败导致的错误归零
修复小红书API数据捕获中粉丝和作品数可能为0的错误赋值
将登录后的账号后台刷新改为使用任务队列进行同步,避免重复刷新和网络不稳定问题
Ethanfly пре 13 часа
родитељ
комит
887373f766

+ 47 - 18
client/src/stores/auth.ts

@@ -4,11 +4,15 @@ import { authApi } from '@/api/auth';
 import { accountsApi } from '@/api/accounts';
 import type { User, LoginRequest } from '@media-manager/shared';
 import { useServerStore } from './server';
+import { useTaskQueueStore } from './taskQueue';
 
 export const useAuthStore = defineStore('auth', () => {
   const user = ref<User | null>(null);
   const accessToken = ref<string | null>(null);
   const refreshToken = ref<string | null>(null);
+  let autoAccountSyncTimer: ReturnType<typeof setInterval> | null = null;
+  let autoAccountSyncTimeout: ReturnType<typeof setTimeout> | null = null;
+  let autoAccountSyncRunning = false;
 
   const isAuthenticated = computed(() => !!accessToken.value && !!user.value);
   const isAdmin = computed(() => user.value?.role === 'admin');
@@ -38,6 +42,7 @@ export const useAuthStore = defineStore('auth', () => {
     accessToken.value = null;
     refreshToken.value = null;
     user.value = null;
+    stopAutoAccountSync();
     localStorage.removeItem(`${serverKey}_accessToken`);
     localStorage.removeItem(`${serverKey}_refreshToken`);
   }
@@ -48,25 +53,51 @@ export const useAuthStore = defineStore('auth', () => {
     saveTokens(result.accessToken, result.refreshToken);
     user.value = result.user;
     
-    // 登录成功后自动刷新所有账号状态
-    refreshAllAccountsInBackground();
+    startAutoAccountSync();
     
     return result;
   }
   
-  // 后台刷新所有账号状态(延迟执行,避免服务启动时网络不稳定导致误判)
-  async function refreshAllAccountsInBackground(delay = 5000) {
-    // 延迟执行,给后端服务足够的启动时间
-    setTimeout(async () => {
-      try {
-        console.log('[Auth] Starting background account refresh...');
-        await accountsApi.refreshAllAccounts();
-        console.log('[Auth] Background account refresh completed');
-      } catch (error) {
-        // 刷新失败不应影响用户体验,静默处理
-        console.warn('[Auth] Background account refresh failed:', error);
+  async function enqueueSyncAccountTasks(): Promise<void> {
+    if (!isAuthenticated.value || autoAccountSyncRunning) return;
+    autoAccountSyncRunning = true;
+
+    try {
+      const taskStore = useTaskQueueStore();
+      const accounts = await accountsApi.getAccounts();
+
+      for (const account of accounts) {
+        await taskStore.syncAccount(account.id, account.accountName);
       }
-    }, delay);
+    } catch (error) {
+      console.warn('[Auth] Auto account sync failed:', error);
+    } finally {
+      autoAccountSyncRunning = false;
+    }
+  }
+
+  function startAutoAccountSync(initialDelay = 5000) {
+    stopAutoAccountSync();
+
+    autoAccountSyncTimeout = setTimeout(() => {
+      enqueueSyncAccountTasks();
+    }, initialDelay);
+
+    const intervalMs = 10 * 60 * 1000;
+    autoAccountSyncTimer = setInterval(() => {
+      enqueueSyncAccountTasks();
+    }, intervalMs);
+  }
+
+  function stopAutoAccountSync() {
+    if (autoAccountSyncTimeout) {
+      clearTimeout(autoAccountSyncTimeout);
+      autoAccountSyncTimeout = null;
+    }
+    if (autoAccountSyncTimer) {
+      clearInterval(autoAccountSyncTimer);
+      autoAccountSyncTimer = null;
+    }
   }
 
   // 注册
@@ -82,8 +113,7 @@ export const useAuthStore = defineStore('auth', () => {
     try {
       user.value = await authApi.getMe();
       
-      // 应用启动时自动刷新所有账号状态
-      refreshAllAccountsInBackground();
+      startAutoAccountSync();
       
       return true;
     } catch {
@@ -97,8 +127,7 @@ export const useAuthStore = defineStore('auth', () => {
           localStorage.setItem(`${serverKey}_accessToken`, result.accessToken);
           user.value = await authApi.getMe();
           
-          // Token 刷新成功后也刷新账号状态
-          refreshAllAccountsInBackground();
+          startAutoAccountSync();
           
           return true;
         } catch {

+ 6 - 3
server/src/services/AccountService.ts

@@ -514,15 +514,18 @@ export class AccountService {
                   // 仅在粉丝数有效时更新(避免因获取失败导致的归零)
                   if (profile.fansCount !== undefined) {
                     // 如果新粉丝数为 0,但原粉丝数 > 0,可能是获取失败,记录警告并跳过更新
-                    // 除非 worksCount 也为 0(可能是新号或被封号)
-                    if (profile.fansCount === 0 && (account.fansCount || 0) > 0 && profile.worksCount > 0) {
+                    if (profile.fansCount === 0 && (account.fansCount || 0) > 0) {
                       logger.warn(`[refreshAccount] Fans count dropped to 0 for ${accountId} (was ${account.fansCount}). Ignoring potential fetch error.`);
                     } else {
                       updateData.fansCount = profile.fansCount;
                     }
                   }
                   
-                  updateData.worksCount = profile.worksCount;
+                  if (profile.worksCount === 0 && (account.worksCount || 0) > 0) {
+                    logger.warn(`[refreshAccount] Works count dropped to 0 for ${accountId} (was ${account.worksCount}). Ignoring potential fetch error.`);
+                  } else {
+                    updateData.worksCount = profile.worksCount;
+                  }
                   
                   // 如果获取到了有效的 accountId(如抖音号),也更新它
                   // 这样可以修正之前使用错误 ID(如 Cookie 值)保存的账号

+ 6 - 6
server/src/services/HeadlessBrowserService.ts

@@ -1607,8 +1607,8 @@ class HeadlessBrowserService {
                 avatar: userInfo.image || userInfo.avatar || userInfo.images,
                 userId: userInfo.user_id || userInfo.userId,
                 redId: userInfo.red_id || userInfo.redId,
-                fans: userInfo.fans || userInfo.fansCount,
-                notes: userInfo.notes || userInfo.noteCount,
+                fans: userInfo.fans ?? userInfo.fansCount,
+                notes: userInfo.notes ?? userInfo.noteCount,
               };
               logger.info(`[Xiaohongshu API] Captured user info:`, capturedData.userInfo);
             }
@@ -1734,10 +1734,10 @@ class HeadlessBrowserService {
         } else if (capturedData.userInfo.userId) {
           accountId = `xiaohongshu_${capturedData.userInfo.userId}`;
         }
-        if (capturedData.userInfo.fans) {
+        if (capturedData.userInfo.fans !== undefined) {
           fansCount = capturedData.userInfo.fans;
         }
-        if (capturedData.userInfo.notes) {
+        if (capturedData.userInfo.notes !== undefined) {
           worksCount = capturedData.userInfo.notes;
         }
       }
@@ -2511,8 +2511,8 @@ class HeadlessBrowserService {
           accountId: result.account_id || `${platform}_${Date.now()}`,
           accountName: result.account_name,
           avatarUrl: result.avatar_url || '',
-          fansCount: result.fans_count || 0,
-          worksCount: result.works_count || 0,
+          fansCount: result.fans_count,
+          worksCount: result.works_count ?? 0,
         };
       }