# 问题修复 - 重试时保留成功Token进度 v3.12.7 **版本**: v3.12.7 **日期**: 2025-10-08 **类型**: 问题修复 ## 问题描述 用户反馈: > "在失败重试阶段,执行进度并未能显示失败token的进度" **现象**: - 点击"重试失败"按钮后 - 执行进度界面显示所有Token都是"等待中"状态 - 看不到之前成功的Token的进度 - 看不到正在重试的Token的执行状态 - 统计信息显示:成功0、失败0、跳过0 **预期行为**: - 之前成功的Token应该显示为"已完成"状态 - 正在重试的Token应该显示为"执行中"状态 - 统计信息应该保留之前的成功数量 ## 问题分析 ### 根本原因 在 `startBatchExecution` 函数中,无论是全新开始还是重试,都会完全重置 `taskProgress`: ```javascript // 🆕 如果不是继续执行,重置taskProgress if (!continueFromSaved) { taskProgress.value = {} // ❌ 完全清空,包括成功的token } ``` ### 问题流程 ``` 第1次执行批量任务: - 100个Token - 成功:97个 - 失败:3个 - taskProgress 包含所有100个Token的进度 用户点击"重试失败": - 调用 retryFailedTasks() - 筛选出3个失败的Token - 调用 startBatchExecution(failedTokenIds, tasks, true) ↓ 在 startBatchExecution 中: - isRetry = true - continueFromSaved = false - 执行:taskProgress.value = {} ← 清空所有进度 ↓ 结果: - 97个成功Token的进度信息丢失 ❌ - UI上看不到任何历史进度 ❌ - 统计数据重置为0 ❌ ``` ### 数据流对比 **期望的数据流**: ``` 初始状态(执行完成后): taskProgress = { 'token1': { status: 'completed', ... }, ← 保留 'token2': { status: 'completed', ... }, ← 保留 'token3': { status: 'failed', ... }, ← 删除并重新初始化 'token4': { status: 'completed', ... }, ← 保留 ... } 重试后: taskProgress = { 'token1': { status: 'completed', ... }, ← 保留 'token2': { status: 'completed', ... }, ← 保留 'token3': { status: 'executing', ... }, ← 新初始化 'token4': { status: 'completed', ... }, ← 保留 ... } ``` **实际的数据流(修复前)**: ``` 初始状态(执行完成后): taskProgress = { 'token1': { status: 'completed', ... }, 'token2': { status: 'completed', ... }, 'token3': { status: 'failed', ... }, 'token4': { status: 'completed', ... }, ... } 重试后: taskProgress = { 'token3': { status: 'executing', ... }, ← 只有这个 } ``` ## 解决方案 ### 修改1:重试时保留成功Token的进度 只删除要重试的Token的进度,保留其他Token的进度: #### 修改前 ```javascript // 🆕 如果不是继续执行,重置taskProgress if (!continueFromSaved) { taskProgress.value = {} } ``` #### 修改后 ```javascript // 🆕 如果不是继续执行,重置taskProgress if (!continueFromSaved) { // 如果是重试模式,保留成功的token进度,只重置失败的token if (isRetry) { // 保留已有的进度数据,只清空要重试的token targetTokens.forEach(tokenId => { if (taskProgress.value[tokenId]) { delete taskProgress.value[tokenId] } }) } else { // 全新开始,清空所有进度 taskProgress.value = {} } } ``` ### 修改2:重试时保留统计数据 保持原有的成功数量,只重置失败计数: #### 修改前 ```javascript // 🆕 初始化统计(如果不是继续执行) if (!continueFromSaved) { executionStats.value = { total: targetTokens.length, success: 0, failed: 0, skipped: 0, startTime: Date.now(), endTime: null } } ``` #### 修改后 ```javascript // 🆕 初始化统计(如果不是继续执行) if (!continueFromSaved) { if (isRetry) { // 重试模式:保持原有的total和success,重置失败和跳过计数 executionStats.value = { total: executionStats.value.total, success: executionStats.value.success, failed: 0, // 重置失败计数,重新统计 skipped: executionStats.value.skipped, startTime: Date.now(), endTime: null } } else { // 全新开始:重置所有统计 executionStats.value = { total: targetTokens.length, success: 0, failed: 0, skipped: 0, startTime: Date.now(), endTime: null } } } ``` ## 修改文件 ### src/stores/batchTaskStore.js **修改位置1**: Line 331-345 - 重试时保留成功Token的进度 - 只删除要重试的Token的进度 **修改位置2**: Line 350-377 - 重试时保留统计数据 - 只重置失败计数 ## 用户体验改进 ### 修改前 **UI显示**: ``` 执行进度: ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ Token1 │ │ Token2 │ │ Token3 │ │ 等待中 │ │ 等待中 │ │ 等待中 │ ← 所有都是等待中 └─────────────┘ └─────────────┘ └─────────────┘ ... (其余97个看不到) 统计: 总计:3 成功:0 失败:0 ← 丢失了之前的97个成功 ``` **问题**: - ❌ 看不到之前成功的97个Token - ❌ 统计数据显示0,让用户困惑 - ❌ 看起来像是重新执行所有任务 ### 修改后 **UI显示**: ``` 执行进度: ✅ 已完成 ✅ 已完成 🔄 执行中 ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ Token1 │ │ Token2 │ │ Token3 │ │ 已完成 │ │ 已完成 │ │ 执行中 │ ← 状态清晰 │ 成功: 7 │ │ 成功: 7 │ │ 进度: 42% │ └─────────────┘ └─────────────┘ └─────────────┘ ... (包括其余97个已完成的) 统计: 总计:100 成功:97 失败:0 ← 保留了之前的成功数 重试轮数:3/5 ``` **改进**: - ✅ 显示所有Token的状态 - ✅ 成功的Token显示为"已完成" - ✅ 重试的Token显示为"执行中" - ✅ 统计数据准确反映实际情况 ## 技术实现 ### 数据结构变化 **修复前(重试时)**: ```javascript // 执行完第1次后 taskProgress = { 'token1': { status: 'completed', progress: 100, ... }, // 97个 'token2': { status: 'completed', progress: 100, ... }, 'token98': { status: 'failed', error: '...', ... }, // 3个 'token99': { status: 'failed', error: '...', ... }, 'token100': { status: 'failed', error: '...', ... } } // 点击重试后 taskProgress = {} // ❌ 全部清空 // 重新初始化 taskProgress = { 'token98': { status: 'pending', progress: 0, ... }, 'token99': { status: 'pending', progress: 0, ... }, 'token100': { status: 'pending', progress: 0, ... } } // 丢失了97个成功的token ❌ ``` **修复后(重试时)**: ```javascript // 执行完第1次后 taskProgress = { 'token1': { status: 'completed', progress: 100, ... }, // 97个 'token2': { status: 'completed', progress: 100, ... }, 'token98': { status: 'failed', error: '...', ... }, // 3个 'token99': { status: 'failed', error: '...', ... }, 'token100': { status: 'failed', error: '...', ... } } // 点击重试后 // 只删除失败的token delete taskProgress['token98'] delete taskProgress['token99'] delete taskProgress['token100'] taskProgress = { 'token1': { status: 'completed', progress: 100, ... }, // ✅ 保留 'token2': { status: 'completed', progress: 100, ... }, // ✅ 保留 // ... 其余95个成功的也保留 } // 重新初始化失败的token taskProgress = { 'token1': { status: 'completed', progress: 100, ... }, // ✅ 保留 'token2': { status: 'completed', progress: 100, ... }, // ✅ 保留 'token98': { status: 'pending', progress: 0, ... }, // ✅ 新初始化 'token99': { status: 'pending', progress: 0, ... }, // ✅ 新初始化 'token100': { status: 'pending', progress: 0, ... } // ✅ 新初始化 } ``` ### 统计数据处理 **修复前**: ```javascript // 第1次执行完成 executionStats = { total: 100, success: 97, failed: 3, skipped: 0 } // 重试时 executionStats = { total: 3, // ❌ 错误:应该是100 success: 0, // ❌ 丢失:应该保留97 failed: 0, skipped: 0 } ``` **修复后**: ```javascript // 第1次执行完成 executionStats = { total: 100, success: 97, failed: 3, skipped: 0 } // 重试时 executionStats = { total: 100, // ✅ 保留:总数不变 success: 97, // ✅ 保留:之前的成功数 failed: 0, // ✅ 重置:重新统计重试结果 skipped: 0 // ✅ 保留:之前的跳过数 } // 重试完成后(假设3个都成功了) executionStats = { total: 100, success: 100, // ✅ 97 + 3 = 100 failed: 0, skipped: 0 } ``` ## 重试场景测试 ### 场景1:部分失败后重试全部成功 ``` 第1次执行: - 总计:100 - 成功:97 - 失败:3 点击"重试失败 (3个)": - taskProgress 保留97个成功的 - 删除3个失败的 - 重新初始化3个失败的 重试执行中: - UI显示100个Token卡片 - 97个显示"已完成" - 3个显示"执行中" - 统计:总计100,成功97,失败0 重试完成(全部成功): - UI显示100个Token卡片 - 100个都显示"已完成" - 统计:总计100,成功100,失败0 ✅ ``` ### 场景2:部分失败后重试仍有失败 ``` 第1次执行: - 总计:100 - 成功:97 - 失败:3 第1次重试: - 成功:2(从失败变成功) - 失败:1(仍然失败) - 统计:总计100,成功99,失败1 第2次重试: - 成功:1(最后1个成功) - 失败:0 - 统计:总计100,成功100,失败0 ✅ ``` ### 场景3:多轮重试 ``` 第1次执行: - 成功:95,失败:5 第1次重试(自动): - 成功:3,失败:2 - 统计:总计100,成功98,失败2 第2次重试(自动): - 成功:1,失败:1 - 统计:总计100,成功99,失败1 第3次重试(自动): - 成功:0,失败:1 - 统计:总计100,成功99,失败1 - 达到最大重试次数,停止重试 ``` ## 边界情况处理 ### 情况1:全部失败后重试 ``` 第1次执行: - 成功:0 - 失败:100 重试: - taskProgress 原本就是空的(或只有失败的) - 删除所有失败的 - 重新初始化所有token - 行为正常 ✅ ``` ### 情况2:全部成功(无需重试) ``` 第1次执行: - 成功:100 - 失败:0 点击重试: - failedTokenIds = [] - 提示"没有失败的任务需要重试" - 不执行重试 - 行为正常 ✅ ``` ### 情况3:连续重试 ``` 第1次执行: - 成功:97,失败:3 第1次重试: - 成功:2,失败:1 - taskProgress 保留99个(97+2) - 删除1个失败的 - 统计正确 ✅ 第2次重试: - 成功:1,失败:0 - taskProgress 保留100个 - 统计正确 ✅ ``` ## 代码逻辑说明 ### 删除失败Token的逻辑 ```javascript if (isRetry) { // 遍历要重试的token(失败的token) targetTokens.forEach(tokenId => { // 如果该token在taskProgress中存在,删除它 if (taskProgress.value[tokenId]) { delete taskProgress.value[tokenId] } }) } ``` **为什么使用 delete?** - `delete` 从对象中完全移除属性 - 不留痕迹,确保后续初始化时是全新的状态 - 避免旧数据影响新的执行 **为什么检查 `if (taskProgress.value[tokenId])`?** - 防御性编程,避免删除不存在的属性 - 虽然理论上失败的token一定存在,但加上检查更安全 ### 保留统计数据的逻辑 ```javascript if (isRetry) { executionStats.value = { total: executionStats.value.total, // 保持不变 success: executionStats.value.success, // 保持已有的成功数 failed: 0, // 重置为0,重新统计 skipped: executionStats.value.skipped, // 保持已有的跳过数 startTime: Date.now(), // 更新为当前时间 endTime: null // 清空结束时间 } } ``` **为什么重置 failed 为 0?** - 失败的token正在重试 - 重试后可能成功,也可能仍然失败 - 需要重新统计重试的结果 **为什么保留 success 和 skipped?** - 这些是之前已经确定的结果 - 不会因为重试而改变 ## 相关版本 - **v3.7.0**: 首次添加自动重试功能 - **v3.12.6**: 增强重试日志输出 - **v3.12.7**: 修复重试时保留成功Token进度(本版本) ## 总结 **问题**: - ❌ 重试时清空所有进度,丢失成功Token信息 - ❌ UI显示不完整,只能看到重试的Token - ❌ 统计数据重置,无法反映真实情况 **修复**: - ✅ 重试时保留成功Token的进度 - ✅ 只删除和重新初始化失败Token - ✅ 保留统计数据中的成功数和总数 - ✅ 只重置失败计数 **效果**: - ✅ UI完整显示所有Token状态 - ✅ 成功Token显示为"已完成" - ✅ 重试Token显示为"执行中" - ✅ 统计数据准确反映实际情况 - ✅ 用户体验大幅提升 --- **状态**: ✅ 已修复 **版本**: v3.12.7