Explorar o código

fix: #6137 首屏加载优化,非关键初始化移至HTTP服务就绪后执行

ethanfly hai 4 días
pai
achega
b32bbadfd8

+ 24 - 16
server/src/app.ts

@@ -224,22 +224,7 @@ async function bootstrap() {
     logger.warn('Redis connection failed - some features may not work');
   }
 
-  // 初始化任务队列(必须在注册执行器和启动 Worker 之前)
-  await initTaskQueue();
-
-  // 只有在数据库连接成功时才启动调度器和注册任务执行器
-  if (dbConnected) {
-    registerTaskExecutors();
-    taskScheduler.start();
-    
-    // 启动任务队列 Worker
-    taskQueueService.startWorker();
-  }
-
-  // 注册浏览器登录服务的事件监听(用于 AI 分析结果的 WebSocket 推送)
-  setupBrowserLoginEvents();
-
-  // 启动 HTTP 服务
+  // 启动 HTTP 服务(尽快接受连接,不必等待调度器/Worker 就绪)
   httpServer.listen(config.port, config.host, () => {
     logger.info(`Server running on http://${config.host}:${config.port}`);
     logger.info(`Environment: ${config.env}`);
@@ -247,6 +232,29 @@ async function bootstrap() {
       logger.warn('⚠️  Running without database - API endpoints will not work');
       logger.warn('⚠️  Please configure MySQL in .env file');
     }
+
+    // 【Bug #6137 修复】把非关键初始化(任务队列/调度器/Worker/事件监听)放到 HTTP 服务就绪之后,
+    // 避免长耗时的同步阻塞导致首屏加载转圈/超时
+    (async () => {
+      try {
+        // 初始化任务队列(必须在注册执行器和启动 Worker 之前)
+        await initTaskQueue();
+        logger.info('Task queue initialized');
+
+        // 注册浏览器登录服务的事件监听(用于 AI 分析结果的 WebSocket 推送)
+        setupBrowserLoginEvents();
+
+        // 只有在数据库连接成功时才启动调度器和注册任务执行器
+        if (dbConnected) {
+          registerTaskExecutors();
+          taskScheduler.start();
+          taskQueueService.startWorker();
+          logger.info('Scheduler and task worker started');
+        }
+      } catch (initError) {
+        logger.error('Deferred initialization failed:', initError instanceof Error ? initError.message : initError);
+      }
+    })();
   });
 }
 

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

@@ -191,6 +191,12 @@ export class AccountService {
       decryptedCookies = cookieData;
     }
 
+    // 【Bug #6084 修复】Cookie 为空时不应该创建账号,否则后台刷新时会失败但前端显示"添加成功"
+    const trimmedCookie = (decryptedCookies || '').trim();
+    if (!trimmedCookie) {
+      throw new AppError('Cookie 不能为空,请先登录获取 Cookie', HTTP_STATUS.BAD_REQUEST, ERROR_CODES.VALIDATION);
+    }
+
     // 检查客户端传入的 accountId 是否有效(不是纯时间戳)
     const clientAccountId = data.accountInfo?.accountId;
     const isValidClientAccountId = clientAccountId && !this.isTimestampBasedId(clientAccountId);

+ 2 - 1
server/src/services/login/BaseLoginService.ts

@@ -342,10 +342,11 @@ export abstract class BaseLoginService extends EventEmitter {
 
   /**
    * 等待页面稳定
+   * 【Bug #6136 修复】增加超时:网络慢时 10s 可能不够,改 30s
    */
   protected async waitPageStable(page: Page): Promise<void> {
     try {
-      await page.waitForLoadState('networkidle', { timeout: 10000 });
+      await page.waitForLoadState('networkidle', { timeout: 30000 });
     } catch {
       // 超时继续
     }

+ 5 - 3
server/src/services/login/XiaohongshuLoginService.ts

@@ -69,14 +69,15 @@ export class XiaohongshuLoginService extends BaseLoginService {
   protected override async collectAccountInfo(session: LoginSession): Promise<AccountInfo | null> {
     try {
       // 步骤3: 等待个人信息 API 数据
+      // 【Bug #6136 修复】增加超时时间:网络慢或账号数据多时原 10s 不够,改 30s
       logger.info('[小红书] 等待个人信息 API...');
-      let personalInfo = await this.waitForApiData(session, 'personalInfo', 10000);
+      let personalInfo = await this.waitForApiData(session, 'personalInfo', 30000);
 
       // 如果没拿到,刷新页面重试
       if (!personalInfo) {
         logger.info('[小红书] 未拿到个人信息,刷新页面重试...');
         await session.page.reload({ waitUntil: 'domcontentloaded' });
-        personalInfo = await this.waitForApiData(session, 'personalInfo', 10000);
+        personalInfo = await this.waitForApiData(session, 'personalInfo', 30000);
       }
 
       if (!personalInfo?.redNum) {
@@ -87,9 +88,10 @@ export class XiaohongshuLoginService extends BaseLoginService {
       logger.info('[小红书] 个人信息:', personalInfo);
 
       // 步骤4+5: 跳转到笔记管理页,等待笔记列表 API
+      // 【Bug #6136 修复】增加超时时间:笔记列表 API 在数据多时可能较慢,改 30s
       logger.info('[小红书] 跳转到笔记管理页...');
       const notesUrl = 'https://creator.xiaohongshu.com/new/note-manager';
-      const noteListData = await this.navigateAndWaitForApi(session, notesUrl, 'noteList', 15000);
+      const noteListData = await this.navigateAndWaitForApi(session, notesUrl, 'noteList', 30000);
 
       const worksCount = noteListData?.count || 0;
       logger.info(`[小红书] 笔记数: ${worksCount}`);