Files
xyzw_web_helper/MD说明文件夹/功能更新-批量任务进度保存和恢复v3.12.0.md
2025-10-17 20:56:50 +08:00

662 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 功能更新 - 批量任务进度保存和恢复 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