18 KiB
18 KiB
功能更新 - 批量任务进度保存和恢复 v3.12.0
版本: v3.12.0
日期: 2025-10-08
类型: 功能更新
功能概述
新增批量任务执行进度的自动保存和恢复功能。当批量任务执行过程中刷新页面或浏览器意外关闭后,可以从上次中断的位置继续执行,无需重新开始。
用户需求
"突然发现批量自动化没有记录上次跑到哪一个token了,我需要记录一下这次跑到第几个token,刷新网页之后,还可以继续下面进行做任务"
功能特性
1. 自动保存进度
- 实时保存:每完成一个Token的任务后,自动保存进度到
localStorage - 保存内容:
- 已完成的Token ID列表
- 所有Token ID列表
- 任务列表
- 执行统计(成功、失败、跳过数量)
- 保存时间戳
2. 智能进度恢复
- 刷新检测:页面刷新后自动检测是否有未完成的任务
- 用户选择:
- ✅ 继续上次进度 - 只执行剩余未完成的Token
- 🔄 重新开始 - 清除进度,从头开始执行所有Token
- 进度过期:超过24小时的进度自动清除
3. 进度信息展示
弹窗显示详细进度信息:
- 总计Token数量
- 已完成Token数量
- 剩余Token数量(高亮显示)
技术实现
1. 数据结构
保存的进度数据
{
completedTokenIds: ['token1', 'token2', ...], // 已完成的Token ID列表
allTokenIds: ['token1', 'token2', 'token3', ...], // 所有Token ID列表
tasks: ['dailyFix', 'legionSignIn', ...], // 任务列表
timestamp: 1728374400000, // 保存时间戳
stats: {
total: 100, // 总数
success: 45, // 成功数
failed: 3, // 失败数
skipped: 0 // 跳过数
}
}
2. 核心函数
src/stores/batchTaskStore.js
保存进度:
const saveExecutionProgress = (completedTokenIds, allTokenIds, tasks) => {
const progress = {
completedTokenIds: completedTokenIds,
allTokenIds: allTokenIds,
tasks: tasks,
timestamp: Date.now(),
stats: {
total: executionStats.value.total,
success: executionStats.value.success,
failed: executionStats.value.failed,
skipped: executionStats.value.skipped
}
}
localStorage.setItem('batchTaskProgress', JSON.stringify(progress))
savedProgress.value = progress
}
清除进度:
const clearSavedProgress = () => {
localStorage.removeItem('batchTaskProgress')
savedProgress.value = null
}
检查进度:
const hasSavedProgress = computed(() => {
if (!savedProgress.value) return false
// 检查进度是否过期(超过24小时)
const elapsed = Date.now() - savedProgress.value.timestamp
const isExpired = elapsed > 24 * 60 * 60 * 1000
if (isExpired) {
clearSavedProgress()
return false
}
// 检查是否还有未完成的token
const remaining = savedProgress.value.allTokenIds.length - savedProgress.value.completedTokenIds.length
return remaining > 0
})
修改启动函数:
const startBatchExecution = async (tokenIds = null, tasks = null, isRetry = false, continueFromSaved = false) => {
// ... 省略其他代码
let targetTokens = tokenIds || tokenStore.gameTokens.map(t => t.id)
let targetTasks = tasks || currentTemplateTasks.value
let completedTokenIds = []
// 检查是否要从保存的进度继续
if (continueFromSaved && savedProgress.value && hasSavedProgress.value) {
console.log('📂 从上次保存的进度继续执行')
targetTokens = savedProgress.value.allTokenIds
targetTasks = savedProgress.value.tasks
completedTokenIds = savedProgress.value.completedTokenIds
// 恢复统计数据
executionStats.value = {
...executionStats.value,
total: savedProgress.value.stats.total,
success: savedProgress.value.stats.success,
failed: savedProgress.value.stats.failed,
skipped: savedProgress.value.stats.skipped
}
console.log(`✅ 已完成: ${completedTokenIds.length}/${targetTokens.length} 个Token`)
console.log(`🔄 继续执行剩余 ${targetTokens.length - completedTokenIds.length} 个Token`)
}
// ... 省略其他代码
// 过滤出需要执行的token(排除已完成的)
const tokensToExecute = targetTokens.filter(id => !completedTokenIds.includes(id))
// 执行批量任务
await executeBatchWithConcurrency(tokensToExecute, targetTasks, targetTokens, completedTokenIds)
}
修改并发执行函数:
const executeBatchWithConcurrency = async (tokenIds, tasks, allTokenIds = null, completedTokenIds = []) => {
const queue = [...tokenIds]
const executing = []
const completed = [...completedTokenIds] // 已完成的token列表
const all = allTokenIds || tokenIds // 所有token列表
while (queue.length > 0 || executing.length > 0) {
// ... 省略其他代码
const promise = (async () => {
// 执行任务
return executeTokenTasks(tokenId, tasks)
})()
.then(() => {
// 从执行队列中移除
const index = executing.indexOf(promise)
if (index > -1) {
executing.splice(index, 1)
}
executingTokens.value.delete(tokenId)
// 保存进度:记录此token已完成
completed.push(tokenId)
saveExecutionProgress(completed, all, tasks)
})
.catch(error => {
// ... 省略错误处理
// 保存进度:即使失败也记录为已完成(避免下次重复执行)
completed.push(tokenId)
saveExecutionProgress(completed, all, tasks)
})
}
}
完成时清除进度:
const finishBatchExecution = async () => {
// ... 省略其他代码
// 清除保存的进度(任务已全部完成)
clearSavedProgress()
// ... 省略其他代码
}
src/components/BatchTaskPanel.vue
修改开始执行函数:
const handleStart = () => {
// 检查是否有保存的进度
if (batchStore.hasSavedProgress && batchStore.savedProgress) {
const completed = batchStore.savedProgress.completedTokenIds.length
const total = batchStore.savedProgress.allTokenIds.length
const remaining = total - completed
dialog.warning({
title: '发现未完成的任务',
content: () => {
return h('div', [
h('p', `检测到上次有未完成的批量任务:`),
h('ul', { style: 'margin: 12px 0; padding-left: 24px;' }, [
h('li', `总计:${total} 个Token`),
h('li', `已完成:${completed} 个`),
h('li', { style: 'color: #ff6b6b; font-weight: bold;' }, `剩余:${remaining} 个`)
]),
h('p', { style: 'margin-top: 16px; font-weight: bold;' }, '您想要:')
])
},
positiveText: '继续上次进度',
negativeText: '重新开始',
onPositiveClick: () => {
batchStore.startBatchExecution(null, null, false, true)
message.success(`继续执行剩余 ${remaining} 个Token`)
},
onNegativeClick: () => {
dialog.warning({
title: '确认重新开始',
content: `确定要重新开始执行所有 ${tokenStore.gameTokens.length} 个角色的任务吗?\n\n已完成的 ${completed} 个Token将会重新执行。`,
positiveText: '确定重新开始',
negativeText: '取消',
onPositiveClick: () => {
batchStore.clearSavedProgress()
batchStore.startBatchExecution()
message.success('批量任务已启动(重新开始)')
}
})
}
})
} else {
// 没有保存的进度,正常开始
dialog.warning({
title: '确认执行',
content: `即将对 ${tokenStore.gameTokens.length} 个角色执行批量任务,是否继续?`,
positiveText: '确定',
negativeText: '取消',
onPositiveClick: () => {
batchStore.startBatchExecution()
message.success('批量任务已启动')
}
})
}
}
3. 新增导出
src/stores/batchTaskStore.js
return {
// 状态
savedProgress, // 新增:保存的进度数据
// 计算属性
hasSavedProgress, // 新增:是否有保存的进度
// 方法
clearSavedProgress, // 新增:清除保存的进度
// ... 其他已有的导出
}
使用场景
场景1:正常完成
用户操作:
1. 开始执行批量任务(100个Token)
2. 任务全部执行完成
3. 系统自动清除保存的进度
结果:
✅ 所有任务完成
✅ 进度已清除
场景2:中途刷新(继续执行)
用户操作:
1. 开始执行批量任务(100个Token)
2. 执行到第50个时,刷新页面
3. 点击"开始执行"按钮
4. 弹窗显示:已完成50个,剩余50个
5. 选择"继续上次进度"
结果:
✅ 从第51个Token继续执行
✅ 已完成的50个Token不会重复执行
✅ 统计数据累加(成功、失败数量保持)
场景3:中途刷新(重新开始)
用户操作:
1. 开始执行批量任务(100个Token)
2. 执行到第50个时,刷新页面
3. 点击"开始执行"按钮
4. 弹窗显示:已完成50个,剩余50个
5. 选择"重新开始"
6. 二次确认弹窗
7. 确认"确定重新开始"
结果:
✅ 清除保存的进度
✅ 从第1个Token重新执行所有100个
✅ 统计数据重置
场景4:进度过期
用户操作:
1. 开始执行批量任务(100个Token)
2. 执行到第50个时,关闭浏览器
3. 24小时后重新打开页面
4. 点击"开始执行"按钮
结果:
✅ 进度已自动过期清除
✅ 直接弹出正常开始确认框
✅ 从头开始执行所有Token
用户界面
弹窗1:发现未完成的任务
┌─────────────────────────────────────┐
│ ⚠ 发现未完成的任务 │
├─────────────────────────────────────┤
│ 检测到上次有未完成的批量任务: │
│ │
│ • 总计:100 个Token │
│ • 已完成:50 个 │
│ • 剩余:50 个 ← 红色高亮 │
│ │
│ 您想要: │
│ │
│ [继续上次进度] [重新开始] │
└─────────────────────────────────────┘
弹窗2:确认重新开始
┌─────────────────────────────────────┐
│ ⚠ 确认重新开始 │
├─────────────────────────────────────┤
│ 确定要重新开始执行所有 100 个角色的 │
│ 任务吗? │
│ │
│ 已完成的 50 个Token将会重新执行。 │
│ │
│ [确定重新开始] [取消] │
└─────────────────────────────────────┘
控制台日志
继续执行时:
📂 从上次保存的进度继续执行
✅ 已完成: 50/100 个Token
🔄 继续执行剩余 50 个Token
🚀 开始批量执行任务
📋 Token数量: 100
📋 任务列表: ['dailyFix', 'legionSignIn', ...]
每完成一个Token:
💾 已保存进度: 51/100 个Token已完成
💾 已保存进度: 52/100 个Token已完成
...
全部完成时:
🎉 批量任务执行完成
🗑️ 已清除保存的进度
✅ 所有任务成功完成!
数据存储
localStorage 键名
batchTaskProgress
数据示例
{
"completedTokenIds": [
"10601服-0-7145...",
"10601服-1-7146...",
"10602服-0-7147..."
],
"allTokenIds": [
"10601服-0-7145...",
"10601服-1-7146...",
"10602服-0-7147...",
"10603服-0-7148...",
"10604服-0-7149..."
],
"tasks": [
"dailyFix",
"legionSignIn",
"autoStudy",
"claimHangupReward",
"addClock",
"sendCar",
"climbTower"
],
"timestamp": 1728374400000,
"stats": {
"total": 5,
"success": 3,
"failed": 0,
"skipped": 0
}
}
技术要点
1. 进度保存时机
- ✅ 成功完成:Token任务执行成功后保存
- ✅ 失败:Token任务执行失败后也保存(避免重复执行失败的任务)
- ❌ 执行中:不保存(避免状态不一致)
2. 进度恢复逻辑
// 初始化每个Token的进度
targetTokens.forEach(tokenId => {
if (completedTokenIds.includes(tokenId)) {
// 已完成的token标记为completed状态
taskProgress.value[tokenId] = {
status: 'completed',
progress: 100,
// ...
}
} else {
// 未完成的token初始化为pending状态
taskProgress.value[tokenId] = {
status: 'pending',
progress: 0,
// ...
}
}
})
// 过滤出需要执行的token
const tokensToExecute = targetTokens.filter(id => !completedTokenIds.includes(id))
// 只执行未完成的token
await executeBatchWithConcurrency(tokensToExecute, targetTasks, targetTokens, completedTokenIds)
3. 统计数据累加
if (continueFromSaved && savedProgress.value) {
// 恢复之前的统计数据
executionStats.value = {
...executionStats.value,
total: savedProgress.value.stats.total,
success: savedProgress.value.stats.success, // 保持之前的成功数
failed: savedProgress.value.stats.failed, // 保持之前的失败数
skipped: savedProgress.value.stats.skipped // 保持之前的跳过数
}
}
4. 进度过期检查
const hasSavedProgress = computed(() => {
if (!savedProgress.value) return false
const elapsed = Date.now() - savedProgress.value.timestamp
const isExpired = elapsed > 24 * 60 * 60 * 1000 // 24小时
if (isExpired) {
clearSavedProgress()
return false
}
const remaining = savedProgress.value.allTokenIds.length - savedProgress.value.completedTokenIds.length
return remaining > 0
})
优势与特性
1. 可靠性
- ✅ 实时保存到 localStorage,不依赖网络
- ✅ 每完成一个Token就保存,最小化数据丢失
- ✅ 自动过期机制,避免过时数据干扰
2. 灵活性
- ✅ 用户可选择继续或重新开始
- ✅ 支持部分完成的恢复
- ✅ 失败的Token也记录为已完成(避免无限重试)
3. 用户体验
- ✅ 详细的进度信息展示
- ✅ 明确的操作选项
- ✅ 二次确认机制(重新开始时)
- ✅ 友好的提示消息
4. 性能优化
- ✅ 只执行未完成的Token,节省时间
- ✅ 避免重复执行,减少服务器压力
- ✅ localStorage 存储,无网络开销
注意事项
1. Token列表变化
如果Token列表发生变化(添加/删除Token),保存的进度可能不准确:
- 建议:重新开始执行
- 未来改进:可以添加Token指纹验证
2. 任务列表变化
如果任务模板发生变化,保存的进度仍使用旧的任务列表:
- 当前行为:继续使用保存的任务列表
- 未来改进:可以提示任务列表已变化
3. 数据大小
对于大量Token(如1000+),进度数据可能较大:
- 当前限制:localStorage 通常 5-10MB
- 评估:1000个Token ID × 20字节 ≈ 20KB(完全可接受)
4. 浏览器隐私模式
在隐私/无痕模式下,localStorage 可能不持久化:
- 影响:刷新后进度丢失
- 解决:建议使用正常模式执行长时间任务
测试用例
测试1:正常完成
- 开始执行10个Token
- 等待全部完成
- 检查localStorage:
batchTaskProgress应该被删除 - 再次点击"开始执行":应该是正常的确认框
测试2:中途刷新继续
- 开始执行10个Token
- 完成5个后刷新页面
- 点击"开始执行":应该弹出"发现未完成的任务"
- 选择"继续上次进度"
- 应该从第6个Token开始执行
- 统计数据应该累加
测试3:中途刷新重新开始
- 开始执行10个Token
- 完成5个后刷新页面
- 点击"开始执行":应该弹出"发现未完成的任务"
- 选择"重新开始"
- 二次确认后,应该清除进度
- 从第1个Token重新执行所有10个
测试4:进度过期
- 开始执行10个Token
- 完成5个后关闭浏览器
- 手动修改localStorage中的timestamp为25小时前
- 重新打开页面,点击"开始执行"
- 应该弹出正常的确认框(进度已自动清除)
测试5:失败Token处理
- 开始执行10个Token,其中3个会失败
- 完成5个(包括2个失败)后刷新
- 继续执行:失败的2个不应该再次执行
- 统计数据中的失败数应该保持
修改文件
-
✅ src/stores/batchTaskStore.js
- 新增
savedProgress状态 - 新增
saveExecutionProgress函数 - 新增
clearSavedProgress函数 - 新增
hasSavedProgress计算属性 - 修改
startBatchExecution函数支持continueFromSaved参数 - 修改
executeBatchWithConcurrency函数支持进度保存 - 修改
finishBatchExecution函数清除进度
- 新增
-
✅ src/components/BatchTaskPanel.vue
- 导入
h函数 - 修改
handleStart函数支持进度恢复提示
- 导入
相关版本
- v3.11.x: 批量任务优化和错误处理
- v3.12.0: 新增进度保存和恢复功能(本版本)
总结
核心功能:
- 💾 自动保存批量任务执行进度
- 🔄 刷新后可继续执行剩余任务
- 🎯 用户可选择继续或重新开始
- ⏰ 24小时自动过期机制
用户获益:
- ✅ 不怕浏览器意外关闭
- ✅ 不怕页面刷新中断
- ✅ 节省时间(继续执行而非重新开始)
- ✅ 灵活控制(可选择重新开始)
技术优势:
- ✅ 实时保存,可靠性高
- ✅ 自动过期,避免脏数据
- ✅ 累加统计,数据准确
- ✅ 用户友好,操作简单
状态: ✅ 已完成
版本: v3.12.0