马一丁

Fix the Display of Report Engine Console Logs

@@ -1392,7 +1392,10 @@ @@ -1392,7 +1392,10 @@
1392 refreshForumLog(); 1392 refreshForumLog();
1393 } 1393 }
1394 if (currentApp === 'report') { 1394 if (currentApp === 'report') {
1395 - refreshReportLog(); 1395 + // 使用新的日志管理器刷新
  1396 + if (reportLogManager && reportLogManager.isRunning) {
  1397 + reportLogManager.refresh();
  1398 + }
1396 } 1399 }
1397 }, 100); 1400 }, 100);
1398 } else { 1401 } else {
@@ -3536,7 +3539,7 @@ @@ -3536,7 +3539,7 @@
3536 // 仅保留特殊页面的初始化逻辑 3539 // 仅保留特殊页面的初始化逻辑
3537 if (app === 'report') { 3540 if (app === 'report') {
3538 // 【修复】切换到Report Engine时启动日志刷新 3541 // 【修复】切换到Report Engine时启动日志刷新
3539 - startReportLogRefresh(); 3542 + reportLogManager.start();
3540 3543
3541 // 只在报告界面未初始化时才重新加载 3544 // 只在报告界面未初始化时才重新加载
3542 const reportContent = document.getElementById('reportContent'); 3545 const reportContent = document.getElementById('reportContent');
@@ -3549,7 +3552,7 @@ @@ -3549,7 +3552,7 @@
3549 }, 500); 3552 }, 500);
3550 } else { 3553 } else {
3551 // 【修复】切换离开Report Engine时停止日志刷新,节省资源 3554 // 【修复】切换离开Report Engine时停止日志刷新,节省资源
3552 - stopReportLogRefresh(); 3555 + reportLogManager.stop();
3553 } 3556 }
3554 } 3557 }
3555 3558
@@ -3822,9 +3825,12 @@ @@ -3822,9 +3825,12 @@
3822 refreshForumLog(); 3825 refreshForumLog();
3823 return; 3826 return;
3824 } 3827 }
3825 - 3828 +
3826 if (currentApp === 'report') { 3829 if (currentApp === 'report') {
3827 - refreshReportLog(); 3830 + // 使用新的日志管理器刷新
  3831 + if (reportLogManager && reportLogManager.isRunning) {
  3832 + reportLogManager.refresh();
  3833 + }
3828 return; 3834 return;
3829 } 3835 }
3830 3836
@@ -4246,6 +4252,325 @@ @@ -4246,6 +4252,325 @@
4246 let reportLockCheckInterval = null; 4252 let reportLockCheckInterval = null;
4247 let lastCompletedReportTask = null; 4253 let lastCompletedReportTask = null;
4248 4254
  4255 + // ====== Report Engine 日志管理器 ======
  4256 + class ReportLogManager {
  4257 + constructor() {
  4258 + this.intervalId = null;
  4259 + this.lineCount = 0;
  4260 + this.isRunning = false;
  4261 + this.refreshInterval = 250; // 250ms轮询一次,更加实时
  4262 + this.lastError = null;
  4263 + this.retryCount = 0;
  4264 + this.maxRetries = 3;
  4265 + }
  4266 +
  4267 + // 启动日志轮询
  4268 + start() {
  4269 + if (this.isRunning) {
  4270 + console.log('[ReportLogManager] 已在运行,跳过重复启动');
  4271 + return;
  4272 + }
  4273 +
  4274 + console.log('[ReportLogManager] ===== 启动日志轮询系统 =====');
  4275 + this.isRunning = true;
  4276 + this.retryCount = 0;
  4277 +
  4278 + // 立即执行一次
  4279 + console.log('[ReportLogManager] 执行初始刷新...');
  4280 + this.refresh();
  4281 +
  4282 + // 启动定时轮询
  4283 + this.intervalId = setInterval(() => {
  4284 + if (currentApp === 'report' && this.isRunning) {
  4285 + console.log('[ReportLogManager] 定时器触发,执行刷新...');
  4286 + this.refresh();
  4287 + }
  4288 + }, this.refreshInterval);
  4289 +
  4290 + console.log(`[ReportLogManager] 轮询已启动,频率: ${this.refreshInterval}ms, intervalId: ${this.intervalId}`);
  4291 + }
  4292 +
  4293 + // 停止日志轮询
  4294 + stop() {
  4295 + if (!this.isRunning) {
  4296 + console.log('[ReportLogManager] 未在运行,无需停止');
  4297 + return;
  4298 + }
  4299 +
  4300 + console.log('[ReportLogManager] ===== 停止日志轮询系统 =====');
  4301 + this.isRunning = false;
  4302 +
  4303 + if (this.intervalId) {
  4304 + clearInterval(this.intervalId);
  4305 + this.intervalId = null;
  4306 + }
  4307 +
  4308 + console.log('[ReportLogManager] 轮询已停止');
  4309 + }
  4310 +
  4311 + // 重置计数器(任务开始时调用)
  4312 + reset() {
  4313 + console.log(`[ReportLogManager] 重置计数器,原值: ${this.lineCount}`);
  4314 + this.lineCount = 0;
  4315 + this.lastError = null;
  4316 + this.retryCount = 0;
  4317 + }
  4318 +
  4319 + // 刷新日志
  4320 + refresh() {
  4321 + if (!this.isRunning) {
  4322 + console.log('[ReportLogManager.refresh] 管理器未运行,跳过刷新');
  4323 + return;
  4324 + }
  4325 +
  4326 + console.log('[ReportLogManager.refresh] 开始刷新日志...');
  4327 +
  4328 + // 【修复】使用传统的Promise方式,避免async/await兼容性问题
  4329 + const controller = new AbortController();
  4330 + const timeoutId = setTimeout(() => controller.abort(), 3000);
  4331 +
  4332 + fetch('/api/report/log', {
  4333 + method: 'GET',
  4334 + headers: { 'Cache-Control': 'no-cache' },
  4335 + signal: controller.signal
  4336 + })
  4337 + .then(response => {
  4338 + clearTimeout(timeoutId);
  4339 + console.log(`[ReportLogManager.refresh] 收到响应,状态: ${response.status}`);
  4340 +
  4341 + if (!response.ok) {
  4342 + throw new Error(`HTTP ${response.status}`);
  4343 + }
  4344 +
  4345 + return response.json();
  4346 + })
  4347 + .then(data => {
  4348 + console.log('[ReportLogManager.refresh] 解析JSON成功');
  4349 +
  4350 + if (!data.success) {
  4351 + throw new Error(data.error || '未知错误');
  4352 + }
  4353 +
  4354 + // 成功后重置重试计数
  4355 + this.retryCount = 0;
  4356 +
  4357 + // 【调试】输出获取到的日志行数
  4358 + if (data.log_lines) {
  4359 + console.log(`[ReportLogManager.refresh] API返回 ${data.log_lines.length} 行日志`);
  4360 + } else {
  4361 + console.log('[ReportLogManager.refresh] API返回的log_lines为空');
  4362 + }
  4363 +
  4364 + // 处理日志数据
  4365 + this.processLogs(data.log_lines || []);
  4366 + })
  4367 + .catch(error => {
  4368 + clearTimeout(timeoutId);
  4369 + console.error('[ReportLogManager.refresh] 错误:', error);
  4370 + this.handleError(error);
  4371 + });
  4372 + }
  4373 +
  4374 + // 处理日志数据
  4375 + processLogs(logLines) {
  4376 + const totalLines = logLines.length;
  4377 +
  4378 + console.log(`[ReportLogManager.processLogs] 总行数: ${totalLines}, 当前计数: ${this.lineCount}`);
  4379 +
  4380 + // 如果有新日志
  4381 + if (totalLines > this.lineCount) {
  4382 + const newLines = logLines.slice(this.lineCount);
  4383 + console.log(`[ReportLogManager] 发现 ${newLines.length} 条新日志 (${this.lineCount} -> ${totalLines})`);
  4384 +
  4385 + // 输出前3行新日志用于调试
  4386 + if (newLines.length > 0) {
  4387 + console.log('[ReportLogManager] 新日志样本:');
  4388 + newLines.slice(0, 3).forEach((line, idx) => {
  4389 + console.log(` [${idx}] ${line.substring(0, 100)}...`);
  4390 + });
  4391 + }
  4392 +
  4393 + // 逐行处理并显示
  4394 + newLines.forEach((line, index) => {
  4395 + console.log(`[ReportLogManager.processLogs] 处理第 ${index + 1}/${newLines.length} 行`);
  4396 + this.displayLogLine(line);
  4397 + });
  4398 +
  4399 + // 更新计数器
  4400 + this.lineCount = totalLines;
  4401 + console.log(`[ReportLogManager] 计数器更新为: ${this.lineCount}`);
  4402 + } else {
  4403 + console.log(`[ReportLogManager] 没有新日志 (总数: ${totalLines}, 已读: ${this.lineCount})`);
  4404 + }
  4405 + }
  4406 +
  4407 + // 显示单行日志(带格式化)
  4408 + displayLogLine(line) {
  4409 + // 解析loguru格式的日志
  4410 + const logPattern = /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3})\s*\|\s*(INFO|DEBUG|WARNING|ERROR|CRITICAL)\s*\|\s*(.+?)\s*-\s*(.*)$/;
  4411 + const match = line.match(logPattern);
  4412 +
  4413 + if (match) {
  4414 + const [, timestamp, level, location, message] = match;
  4415 +
  4416 + // 【调试】输出匹配到的日志级别
  4417 + if (level === 'WARNING' || level === 'ERROR' || level === 'DEBUG') {
  4418 + console.log(`[ReportLogManager] 检测到 ${level} 日志: ${message.substring(0, 50)}...`);
  4419 + }
  4420 +
  4421 + // 格式化输出 - 简化时间戳,只显示时间部分
  4422 + const timeOnly = timestamp.split(' ')[1];
  4423 + const formattedLine = `[${timeOnly}] [${level}] ${message}`;
  4424 +
  4425 + // 添加到控制台(带样式提示)
  4426 + if (level === 'ERROR' || level === 'CRITICAL') {
  4427 + appendConsoleTextLine('report', formattedLine, 'error');
  4428 + } else if (level === 'WARNING') {
  4429 + appendConsoleTextLine('report', formattedLine, 'warning');
  4430 + } else if (level === 'DEBUG') {
  4431 + appendConsoleTextLine('report', formattedLine, 'debug');
  4432 + } else {
  4433 + appendConsoleTextLine('report', formattedLine);
  4434 + }
  4435 +
  4436 + // 同时在浏览器控制台输出(便于调试)- 降低输出频率
  4437 + if (level === 'ERROR' || level === 'CRITICAL') {
  4438 + console.error(`[ReportLog] ${formattedLine}`);
  4439 + } else if (level === 'WARNING') {
  4440 + console.warn(`[ReportLog] ${formattedLine}`);
  4441 + } else if (level === 'DEBUG') {
  4442 + console.debug(`[ReportLog] ${formattedLine}`);
  4443 + }
  4444 + } else {
  4445 + // 非标准格式的日志,直接显示
  4446 + // 【调试】输出未匹配的行,帮助调试正则
  4447 + if (line.includes('WARNING') || line.includes('ERROR') || line.includes('DEBUG')) {
  4448 + console.log(`[ReportLogManager] 未匹配的日志行: "${line}"`);
  4449 + }
  4450 + appendConsoleTextLine('report', line);
  4451 + }
  4452 + }
  4453 +
  4454 + // 处理错误
  4455 + handleError(error) {
  4456 + // 避免重复错误日志
  4457 + const errorMsg = error.message || error.toString();
  4458 + if (errorMsg === this.lastError) {
  4459 + return; // 相同错误不重复输出
  4460 + }
  4461 +
  4462 + this.lastError = errorMsg;
  4463 + this.retryCount++;
  4464 +
  4465 + // 只在前几次重试时输出错误
  4466 + if (this.retryCount <= this.maxRetries) {
  4467 + console.warn(`[ReportLogManager] 获取日志失败 (${this.retryCount}/${this.maxRetries}): ${errorMsg}`);
  4468 + }
  4469 +
  4470 + // 超过最大重试次数时暂停一段时间
  4471 + if (this.retryCount > this.maxRetries) {
  4472 + this.stop();
  4473 + console.error('[ReportLogManager] 多次失败,暂停轮询');
  4474 +
  4475 + // 5秒后自动重试
  4476 + setTimeout(() => {
  4477 + if (currentApp === 'report') {
  4478 + console.log('[ReportLogManager] 尝试恢复轮询...');
  4479 + this.start();
  4480 + }
  4481 + }, 5000);
  4482 + }
  4483 + }
  4484 +
  4485 + // 获取状态信息
  4486 + getStatus() {
  4487 + return {
  4488 + isRunning: this.isRunning,
  4489 + lineCount: this.lineCount,
  4490 + intervalId: this.intervalId,
  4491 + lastError: this.lastError,
  4492 + retryCount: this.retryCount
  4493 + };
  4494 + }
  4495 + }
  4496 +
  4497 + // 创建全局日志管理器实例
  4498 + const reportLogManager = new ReportLogManager();
  4499 +
  4500 + // 【调试】测试日志管理器
  4501 + window.testReportLogManager = function() {
  4502 + console.log('[测试] ===== 开始测试Report日志管理器 =====');
  4503 +
  4504 + // 检查当前状态
  4505 + const status = reportLogManager.getStatus();
  4506 + console.log('[测试] 当前状态:', status);
  4507 +
  4508 + // 如果未运行,启动它
  4509 + if (!status.isRunning) {
  4510 + console.log('[测试] 启动日志管理器...');
  4511 + reportLogManager.start();
  4512 + }
  4513 +
  4514 + // 手动刷新一次
  4515 + console.log('[测试] 手动触发刷新...');
  4516 + reportLogManager.refresh();
  4517 +
  4518 + // 模拟添加日志
  4519 + console.log('[测试] 模拟添加WARNING日志...');
  4520 + appendConsoleTextLine('report', '[21:02:43.014] [WARNING] 测试警告消息', 'warning');
  4521 +
  4522 + console.log('[测试] 模拟添加ERROR日志...');
  4523 + appendConsoleTextLine('report', '[21:02:43.018] [ERROR] 测试错误消息', 'error');
  4524 +
  4525 + console.log('[测试] ===== 测试完成 =====');
  4526 + };
  4527 +
  4528 + // 【调试】直接测试API
  4529 + window.testReportAPI = function() {
  4530 + console.log('[测试API] ===== 开始测试Report API =====');
  4531 +
  4532 + fetch('/api/report/log', {
  4533 + method: 'GET',
  4534 + headers: { 'Cache-Control': 'no-cache' }
  4535 + })
  4536 + .then(response => {
  4537 + console.log('[测试API] 响应状态:', response.status);
  4538 + return response.json();
  4539 + })
  4540 + .then(data => {
  4541 + console.log('[测试API] 返回数据:', data);
  4542 + if (data.success && data.log_lines) {
  4543 + console.log('[测试API] 日志行数:', data.log_lines.length);
  4544 + console.log('[测试API] 前5行日志:');
  4545 + data.log_lines.slice(0, 5).forEach((line, idx) => {
  4546 + console.log(` ${idx}: ${line}`);
  4547 + });
  4548 +
  4549 + // 查找WARNING和ERROR日志
  4550 + const warnings = data.log_lines.filter(line => line.includes('WARNING'));
  4551 + const errors = data.log_lines.filter(line => line.includes('ERROR'));
  4552 +
  4553 + console.log(`[测试API] 找到 ${warnings.length} WARNING日志`);
  4554 + console.log(`[测试API] 找到 ${errors.length} ERROR日志`);
  4555 +
  4556 + if (warnings.length > 0) {
  4557 + console.log('[测试API] WARNING日志示例:');
  4558 + warnings.slice(0, 3).forEach(line => console.log(' ', line));
  4559 + }
  4560 +
  4561 + if (errors.length > 0) {
  4562 + console.log('[测试API] ERROR日志示例:');
  4563 + errors.slice(0, 3).forEach(line => console.log(' ', line));
  4564 + }
  4565 + }
  4566 + })
  4567 + .catch(error => {
  4568 + console.error('[测试API] 错误:', error);
  4569 + });
  4570 +
  4571 + console.log('[测试API] ===== 测试完成 =====');
  4572 + };
  4573 +
4249 // 实时刷新论坛消息(适用于所有页面) 4574 // 实时刷新论坛消息(适用于所有页面)
4250 function refreshForumMessages() { 4575 function refreshForumMessages() {
4251 fetch('/api/forum/log') 4576 fetch('/api/forum/log')
@@ -4466,119 +4791,30 @@ @@ -4466,119 +4791,30 @@
4466 }); 4791 });
4467 } 4792 }
4468 4793
  4794 + // 【重构】刷新Report日志(使用新的日志管理器)
4469 function refreshReportLog() { 4795 function refreshReportLog() {
4470 - // 【修复】添加超时控制和完整错误处理  
4471 - const controller = new AbortController();  
4472 - const timeoutId = setTimeout(() => controller.abort(), 5000); // 5秒超时  
4473 -  
4474 - fetch('/api/report/log', { signal: controller.signal })  
4475 - .then(response => {  
4476 - clearTimeout(timeoutId);  
4477 - // 【修复】检查HTTP状态  
4478 - if (!response.ok) {  
4479 - throw new Error(`HTTP ${response.status}: ${response.statusText}`);  
4480 - }  
4481 - return response.json();  
4482 - })  
4483 - .then(data => {  
4484 - // 【修复】检查返回的成功状态  
4485 - if (!data.success) {  
4486 - console.error('[Report日志] 刷新失败:', data.error || '未知错误');  
4487 - return;  
4488 - }  
4489 -  
4490 - if (data.log_lines && data.log_lines.length > reportLogLineCount) {  
4491 - // 只添加新的行  
4492 - const newLines = data.log_lines.slice(reportLogLineCount);  
4493 -  
4494 - // 【调试日志】记录实时读取的日志数量  
4495 - if (newLines.length > 0) {  
4496 - console.log(`[Report日志] 读取 ${newLines.length} 条新日志(总计 ${data.log_lines.length})`);  
4497 - }  
4498 -  
4499 - newLines.forEach(line => {  
4500 - // 直接添加,使用LogVirtualList的默认批处理机制  
4501 - appendConsoleTextLine('report', line);  
4502 - });  
4503 -  
4504 - reportLogLineCount = data.log_lines.length;  
4505 - }  
4506 - })  
4507 - .catch(error => {  
4508 - clearTimeout(timeoutId);  
4509 - // 【修复】区分错误类型  
4510 - if (error.name === 'AbortError') {  
4511 - console.warn('[Report日志] 刷新超时(5秒)');  
4512 - } else if (error.message.includes('Failed to fetch')) {  
4513 - console.error('[Report日志] 刷新失败: 网络连接错误');  
4514 - } else {  
4515 - console.error('[Report日志] 刷新失败:', error.message || error);  
4516 - }  
4517 - }); 4796 + // 兼容旧代码:直接调用日志管理器的刷新
  4797 + if (reportLogManager && reportLogManager.isRunning) {
  4798 + reportLogManager.refresh();
  4799 + } else {
  4800 + console.log('[RefreshReportLog] 日志管理器未运行,跳过刷新');
  4801 + }
4518 } 4802 }
4519 4803
4520 - // 加载Report Engine日志 4804 + // 加载Report Engine日志(初始化时使用)
4521 function loadReportLog() { 4805 function loadReportLog() {
4522 - fetch('/api/report/log')  
4523 - .then(response => response.json())  
4524 - .then(data => {  
4525 - // 【FIX Bug #5】检查是否仍然在report页面  
4526 - if (currentApp !== 'report') {  
4527 - console.log('忽略report日志响应(已切换到其他app)');  
4528 - return;  
4529 - }  
4530 -  
4531 - if (data.success) {  
4532 - if (reportLogLineCount === 0) {  
4533 - clearConsoleLayer('report', '[系统] Report Engine 日志监控已启动');  
4534 - logRenderers['report'].render(); // 立即渲染  
4535 - }  
4536 -  
4537 - if (data.log_lines && data.log_lines.length > 0) {  
4538 - const newLines = data.log_lines.slice(reportLogLineCount);  
4539 - const linesToProcess = reportLogLineCount === 0 ? data.log_lines : newLines;  
4540 -  
4541 - linesToProcess.forEach(line => {  
4542 - appendConsoleTextLine('report', line);  
4543 - });  
4544 -  
4545 - // 重置计数器以确保后续消息能正确显示  
4546 - reportLogLineCount = data.log_lines.length;  
4547 -  
4548 - // 移除"正在加载"提示(如果存在)  
4549 - const renderer = logRenderers['report'];  
4550 - if (renderer && renderer.lines.length > 0) {  
4551 - const firstLine = renderer.lines[0];  
4552 - if (firstLine && firstLine.text.includes('正在加载')) {  
4553 - renderer.lines.shift();  
4554 - renderer.lastRenderHash = null;  
4555 - renderer.scheduleRender(true);  
4556 - }  
4557 - }  
4558 - } else {  
4559 - // 如果没有日志,重置计数器  
4560 - reportLogLineCount = 0;  
4561 - }  
4562 - } else {  
4563 - // 【优化】加载失败显示错误  
4564 - const renderer = logRenderers['report'];  
4565 - if (renderer && currentApp === 'report') {  
4566 - renderer.clear('[错误] 加载Report日志失败');  
4567 - renderer.render();  
4568 - }  
4569 - }  
4570 - })  
4571 - .catch(error => {  
4572 - console.error('加载Report日志失败:', error);  
4573 - // 【优化】显示错误提示  
4574 - if (currentApp === 'report') {  
4575 - const renderer = logRenderers['report'];  
4576 - if (renderer) {  
4577 - renderer.clear('[错误] 加载Report日志失败: ' + error.message);  
4578 - renderer.render();  
4579 - }  
4580 - }  
4581 - }); 4806 + // 使用新的日志管理器
  4807 + if (!reportLogManager.isRunning) {
  4808 + // 清空控制台
  4809 + clearConsoleLayer('report', '[系统] Report Engine 日志监控已启动');
  4810 +
  4811 + // 重置计数器并启动
  4812 + reportLogManager.reset();
  4813 + reportLogManager.start();
  4814 + } else {
  4815 + // 如果已经在运行,只是刷新一次
  4816 + reportLogManager.refresh();
  4817 + }
4582 } 4818 }
4583 4819
4584 // 解析论坛消息并添加到对话区 4820 // 解析论坛消息并添加到对话区
@@ -4739,7 +4975,7 @@ @@ -4739,7 +4975,7 @@
4739 4975
4740 // 【修复】加载Report界面时启动日志刷新 4976 // 【修复】加载Report界面时启动日志刷新
4741 if (currentApp === 'report') { 4977 if (currentApp === 'report') {
4742 - startReportLogRefresh(); 4978 + reportLogManager.start();
4743 } 4979 }
4744 } else { 4980 } else {
4745 reportContent.innerHTML = ` 4981 reportContent.innerHTML = `
@@ -5110,9 +5346,10 @@ @@ -5110,9 +5346,10 @@
5110 } 5346 }
5111 5347
5112 const query = document.getElementById('searchInput').value.trim() || '智能舆情分析报告'; 5348 const query = document.getElementById('searchInput').value.trim() || '智能舆情分析报告';
5113 -  
5114 - // 重置日志计数器,因为后台会清空日志文件  
5115 - reportLogLineCount = 0; 5349 +
  5350 + // 【修复】先停止现有的日志轮询,避免与后端清空日志的竞态条件
  5351 + reportLogManager.stop();
  5352 +
5116 reportAutoPreviewLoaded = false; 5353 reportAutoPreviewLoaded = false;
5117 safeCloseReportStream(true); 5354 safeCloseReportStream(true);
5118 5355
@@ -5157,10 +5394,14 @@ @@ -5157,10 +5394,14 @@
5157 5394
5158 appendReportStreamLine('任务创建成功,正在建立流式连接...', 'info', { force: true }); 5395 appendReportStreamLine('任务创建成功,正在建立流式连接...', 'info', { force: true });
5159 5396
5160 - // 【优化】先启动日志轮询,再建立SSE连接 5397 + // 【修复】在API成功后重置计数器,此时后端已清空日志文件
  5398 + // 避免在API调用期间旧interval读取旧日志导致的竞态条件
  5399 + reportLogManager.reset();
  5400 +
  5401 + // 【优化】启动日志轮询
5161 // 确保从任务开始就能读取日志 5402 // 确保从任务开始就能读取日志
5162 - startReportLogRefresh();  
5163 - console.log('[Report日志] 任务创建成功,启动日志轮询'); 5403 + reportLogManager.start();
  5404 + console.log('[ReportLogManager] 任务创建成功,计数器已重置,启动日志轮询');
5164 5405
5165 if (window.EventSource) { 5406 if (window.EventSource) {
5166 openReportStream(reportTaskId); 5407 openReportStream(reportTaskId);
@@ -5192,38 +5433,8 @@ @@ -5192,38 +5433,8 @@
5192 } 5433 }
5193 5434
5194 // 【修复】启动Report Engine日志实时刷新 5435 // 【修复】启动Report Engine日志实时刷新
5195 - function startReportLogRefresh() {  
5196 - // 防重复:如果已经在运行,直接返回  
5197 - if (reportLogRefreshInterval) {  
5198 - console.log('[Report日志] 日志轮询已在运行,跳过重复启动');  
5199 - return;  
5200 - }  
5201 -  
5202 - // 立即刷新一次,获取当前所有日志  
5203 - refreshReportLog();  
5204 -  
5205 - // 启动定时器,每秒刷新一次  
5206 - reportLogRefreshInterval = setInterval(() => {  
5207 - if (currentApp === 'report') {  
5208 - refreshReportLog();  
5209 - } else {  
5210 - // 如果切换到其他应用,自动停止轮询  
5211 - console.log('[Report日志] 检测到应用切换,停止日志轮询');  
5212 - stopReportLogRefresh();  
5213 - }  
5214 - }, 1000); // 每秒刷新一次  
5215 -  
5216 - console.log('[Report日志] 启动实时日志刷新,频率: 1秒');  
5217 - }  
5218 -  
5219 - // 【修复】停止Report Engine日志刷新  
5220 - function stopReportLogRefresh() {  
5221 - if (reportLogRefreshInterval) {  
5222 - clearInterval(reportLogRefreshInterval);  
5223 - reportLogRefreshInterval = null;  
5224 - console.log('[Report日志] 停止日志刷新');  
5225 - }  
5226 - } 5436 + // 【新函数】使用新的日志管理器
  5437 + // 旧的startReportLogRefresh和stopReportLogRefresh已废弃,请使用reportLogManager
5227 5438
5228 // 开始进度轮询 5439 // 开始进度轮询
5229 function startProgressPolling(taskId) { 5440 function startProgressPolling(taskId) {
@@ -5243,10 +5454,10 @@ @@ -5243,10 +5454,10 @@
5243 .then(data => { 5454 .then(data => {
5244 if (data.success) { 5455 if (data.success) {
5245 updateProgressDisplay(data.task); 5456 updateProgressDisplay(data.task);
5246 -  
5247 - // 在检查进度时也刷新日志  
5248 - refreshReportLog();  
5249 - 5457 +
  5458 + // 在检查进度时也刷新日志(使用新的日志管理器)
  5459 + // reportLogManager会自动处理轮询
  5460 +
5250 if (data.task.status === 'completed') { 5461 if (data.task.status === 'completed') {
5251 clearInterval(reportPollingInterval); 5462 clearInterval(reportPollingInterval);
5252 showMessage('报告生成完成!', 'success'); 5463 showMessage('报告生成完成!', 'success');
@@ -5460,8 +5671,8 @@ @@ -5460,8 +5671,8 @@
5460 5671
5461 // 【修复】启动日志轮询,读取logger.info/debug/warning/error 5672 // 【修复】启动日志轮询,读取logger.info/debug/warning/error
5462 // SSE只推送显式事件(stage/chapter_status等),logger日志需要轮询读取 5673 // SSE只推送显式事件(stage/chapter_status等),logger日志需要轮询读取
5463 - startReportLogRefresh();  
5464 - console.log('[Report日志] SSE连接建立,同步启动日志轮询'); 5674 + reportLogManager.start();
  5675 + console.log('[ReportLogManager] SSE连接建立,同步启动日志轮询');
5465 }; 5676 };
5466 reportEventSource.onerror = () => { 5677 reportEventSource.onerror = () => {
5467 appendReportStreamLine('检测到网络抖动,SSE正在等待自动重连...', 'warn', { badge: 'SSE' }); 5678 appendReportStreamLine('检测到网络抖动,SSE正在等待自动重连...', 'warn', { badge: 'SSE' });
@@ -5488,12 +5699,10 @@ @@ -5488,12 +5699,10 @@
5488 clearTimeout(reportStreamReconnectTimer); 5699 clearTimeout(reportStreamReconnectTimer);
5489 reportStreamReconnectTimer = null; 5700 reportStreamReconnectTimer = null;
5490 } 5701 }
5491 - // 清除日志刷新定时器  
5492 - if (reportLogRefreshInterval) {  
5493 - clearInterval(reportLogRefreshInterval);  
5494 - reportLogRefreshInterval = null;  
5495 - console.log('[Report日志] SSE连接关闭,停止日志轮询');  
5496 - } 5702 + // 清除日志刷新(使用新的日志管理器)
  5703 + reportLogManager.stop();
  5704 + console.log('[ReportLogManager] SSE连接关闭,停止日志轮询');
  5705 +
5497 clearStreamHeartbeat(); 5706 clearStreamHeartbeat();
5498 if (!keepIndicator) { 5707 if (!keepIndicator) {
5499 updateReportStreamStatus('idle'); 5708 updateReportStreamStatus('idle');
@@ -5600,8 +5809,10 @@ @@ -5600,8 +5809,10 @@
5600 case 'completed': 5809 case 'completed':
5601 appendReportStreamLine(payload.message || '任务完成', 'success'); 5810 appendReportStreamLine(payload.message || '任务完成', 'success');
5602 5811
5603 - // 【修复】任务完成前最后一次刷新日志,确保所有日志都被读取  
5604 - refreshReportLog(); 5812 + // 【修复】任务完成前强制刷新最后一次日志,确保所有日志都被读取
  5813 + if (reportLogManager && reportLogManager.isRunning) {
  5814 + reportLogManager.refresh();
  5815 + }
5605 5816
5606 // 延迟500ms后关闭SSE,确保最后一次日志刷新完成 5817 // 延迟500ms后关闭SSE,确保最后一次日志刷新完成
5607 setTimeout(() => { 5818 setTimeout(() => {