Files
xyzw_web_helper/MD说明文件夹/功能更新-批量任务进度保存和恢复v3.12.0.md

662 lines
18 KiB
Markdown
Raw Normal View History

2025-10-17 20:56:50 +08:00
# 功能更新 - 批量任务进度保存和恢复 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. 数据结构
#### 保存的进度数据
```javascript
{
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
**保存进度**
```javascript
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
}
```
**清除进度**
```javascript
const clearSavedProgress = () => {
localStorage.removeItem('batchTaskProgress')
savedProgress.value = null
}
```
**检查进度**
```javascript
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
})
```
**修改启动函数**
```javascript
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)
}
```
**修改并发执行函数**
```javascript
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)
})
}
}
```
**完成时清除进度**
```javascript
const finishBatchExecution = async () => {
// ... 省略其他代码
// 清除保存的进度(任务已全部完成)
clearSavedProgress()
// ... 省略其他代码
}
```
#### src/components/BatchTaskPanel.vue
**修改开始执行函数**
```javascript
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
```javascript
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
```
### 数据示例
```json
{
"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. 进度恢复逻辑
```javascript
// 初始化每个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. 统计数据累加
```javascript
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. 进度过期检查
```javascript
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正常完成
1. 开始执行10个Token
2. 等待全部完成
3. 检查localStorage`batchTaskProgress` 应该被删除
4. 再次点击"开始执行":应该是正常的确认框
### 测试2中途刷新继续
1. 开始执行10个Token
2. 完成5个后刷新页面
3. 点击"开始执行":应该弹出"发现未完成的任务"
4. 选择"继续上次进度"
5. 应该从第6个Token开始执行
6. 统计数据应该累加
### 测试3中途刷新重新开始
1. 开始执行10个Token
2. 完成5个后刷新页面
3. 点击"开始执行":应该弹出"发现未完成的任务"
4. 选择"重新开始"
5. 二次确认后,应该清除进度
6. 从第1个Token重新执行所有10个
### 测试4进度过期
1. 开始执行10个Token
2. 完成5个后关闭浏览器
3. 手动修改localStorage中的timestamp为25小时前
4. 重新打开页面,点击"开始执行"
5. 应该弹出正常的确认框(进度已自动清除)
### 测试5失败Token处理
1. 开始执行10个Token其中3个会失败
2. 完成5个包括2个失败后刷新
3. 继续执行失败的2个不应该再次执行
4. 统计数据中的失败数应该保持
## 修改文件
- ✅ 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