# 重构:发车任务复用游戏模块逻辑 v3.11.0 **版本**: v3.11.0 **日期**: 2025-10-08 **类型**: Major Refactoring (重大重构) --- ## 问题背景 用户反馈批量自动化的发车功能存在很大问题,虽然经过多次修复(v3.9.5 ~ v3.10.3),但仍然不稳定,包括: - 超时问题(`car_getrolecar` 20000ms 仍然超时) - 错误 200400(账号未加入俱乐部) - 错误 200020(车辆冷却期或状态未同步) - 激活账号逻辑(`role_getroleinfo`)不稳定 - 等待时间过长(3秒同步延迟) 而游戏功能模块(`CarManagement.vue`)中的发车功能一直工作正常,用户能够在单个 token 上快速、可靠地完成车辆查询、刷新、收获、发送操作。 **用户建议**: > "我觉得批量自动化的发车还是有很大问题,能不能直接复用游戏功能模块的查询车辆信息,然后先全部收获,再批量刷新(可选的),最后全部发车" --- ## 重构目标 1. **代码复用**: 直接移植游戏功能模块(`CarManagement.vue`)中已验证可用的逻辑到批量自动化(`batchTaskStore.js`) 2. **简化流程**: 移除不必要的"激活账号"步骤和过长的等待时间 3. **统一超时**: 使用游戏模块的超时配置(10000ms for query, 5000ms for refresh/claim/send) 4. **保持一致**: 确保批量自动化和游戏模块的行为完全一致 --- ## 重构内容 ### 1. 移除"激活账号"步骤 **旧逻辑**: ```javascript // 第0步:先获取角色信息(激活账号) console.log(`👤 [${tokenId}] 获取角色信息(激活账号)...`) try { await tokenStore.sendMessageAsync(tokenId, 'role_getroleinfo', {}, 5000) console.log(`✅ [${tokenId}] 角色信息获取成功`) } catch (error) { console.warn(`⚠️ [${tokenId}] 角色信息获取失败: ${error.message},继续尝试查询车辆`) } // 等待一下,让服务器处理 await new Promise(resolve => setTimeout(resolve, 1000)) ``` **新逻辑**: ```javascript // 直接查询车辆,无需"激活账号"步骤 ``` **理由**: - 游戏功能模块没有此步骤,直接查询车辆就能成功 - 此步骤增加了 6 秒的额外延迟(5秒超时 + 1秒等待),影响效率 - 实际测试表明,此步骤并非必需 --- ### 2. 调整超时时间 **旧配置**: - `car_getrolecar`: 20000ms (20秒) - `car_refresh`: 5000ms - `car_claim`: 5000ms - `car_send`: 5000ms **新配置**(与游戏模块保持一致): - `car_getrolecar`: 10000ms (10秒) - `car_refresh`: 5000ms - `car_claim`: 5000ms - `car_send`: 5000ms **理由**: - 游戏模块使用 10000ms 查询超时,实测非常快(< 1秒) - 20000ms 的超时时间过于保守,反而掩盖了真实的服务器响应问题 - 如果 10000ms 仍然超时,说明是账号未加入俱乐部等服务器端问题 --- ### 3. 简化批量收获逻辑 **旧逻辑**: ```javascript // 第4步:批量收获 console.log(`🎁 [${tokenId}] 开始批量收获...`) let claimSuccessCount = 0 let claimSkipCount = 0 for (const carId of carIds) { const carInfo = carDataMap[carId] const state = getCarState(carInfo) if (state === 2) { // 已到达 try { await tokenStore.sendMessageAsync(tokenId, 'car_claim', { carId: carId }, 5000) claimSuccessCount++ console.log(`✅ [${tokenId}] 收获车辆成功: ${carId}`) carInfo.claimAt = 1 // 标记已收获 carInfo.sendAt = 0 } catch (error) { console.log(`❌ [${tokenId}] 收获车辆失败: ${carId} - ${error.message}`) } } else { claimSkipCount++ } await new Promise(resolve => setTimeout(resolve, 300)) } // 等待服务器状态同步(收获→待发车需要时间) console.log(`⏳ [${tokenId}] 等待服务器状态同步(3秒)...`) await new Promise(resolve => setTimeout(resolve, 3000)) // 重新查询车辆状态 console.log(`🔍 [${tokenId}] 重新查询车辆状态...`) const finalQueryResponse = await tokenStore.sendMessageAsync(tokenId, 'car_getrolecar', {}, 20000) ``` **新逻辑**(与游戏模块保持一致): ```javascript // Step 3.1: 批量收获已到达的车辆(state: 2) console.log(`🎁 [${tokenId}] 第1步:批量收获已到达的车辆`) const arrivedCars = carIds.filter(carId => getCarState(carDataMap[carId]) === 2) if (arrivedCars.length === 0) { console.log(`⚠️ [${tokenId}] 没有已到达的车辆可以收获`) } else { console.log(`🎁 [${tokenId}] 找到 ${arrivedCars.length} 辆已到达的车辆`) for (let i = 0; i < arrivedCars.length; i++) { const carId = arrivedCars[i] console.log(`🎁 [${tokenId}] 收获进度: ${i + 1}/${arrivedCars.length}`) try { await tokenStore.sendMessageAsync(tokenId, 'car_claim', { carId: carId }, 5000) claimSuccessCount++ console.log(`✅ [${tokenId}] 收获车辆成功: ${carId}`) } catch (error) { console.error(`❌ [${tokenId}] 收获车辆失败: ${carId} - ${error.message}`) } // 添加间隔(与 CarManagement.vue 保持一致) if (i < arrivedCars.length - 1) { await new Promise(resolve => setTimeout(resolve, 300)) } } console.log(`🎁 [${tokenId}] 收获完成:成功 ${claimSuccessCount}, 失败 ${arrivedCars.length - claimSuccessCount}`) } // 如果有收获成功的车辆,重新查询车辆列表(与 CarManagement.vue 保持一致) if (claimSuccessCount > 0) { console.log(`🔄 [${tokenId}] 收获成功,等待1秒后重新查询车辆状态...`) await new Promise(resolve => setTimeout(resolve, 1000)) const { carDataMap: newCarDataMap } = await queryClubCars() Object.assign(carDataMap, newCarDataMap) } ``` **关键改进**: 1. **提前筛选**: 只遍历 `state === 2` 的车辆,而不是所有车辆 2. **减少等待**: 收获后只等待 1 秒,而不是 3 秒 3. **条件查询**: 只有当收获成功时才重新查询,而不是无条件查询 4. **移除手动状态修改**: 不再手动修改 `carInfo.claimAt` 和 `carInfo.sendAt`,而是依赖服务器返回的最新状态 --- ### 4. 简化批量发送逻辑 **旧逻辑**: ```javascript // 找到待发车的车辆 const readyToSendCars = carIds.filter(carId => getCarState(carDataMap[carId]) === 0) const carsToSend = readyToSendCars.slice(0, currentRemainingSendCount) console.log(`🚀 [${tokenId}] 待发车: ${readyToSendCars.length}辆,剩余额度: ${currentRemainingSendCount}个,将发送: ${carsToSend.length}辆`) for (const carId of carsToSend) { try { await tokenStore.sendMessageAsync(tokenId, 'car_send', { carId: carId, helperId: 0, text: "" }, 5000) sendSuccessCount++ console.log(`✅ [${tokenId}] 发送车辆成功: ${carId}`) // 更新发车次数 const newCount = dailySendCount + sendSuccessCount localStorage.setItem(dailySendKey, newCount.toString()) if (newCount >= 4) { console.log(`✅ [${tokenId}] 今日发车次数已达上限,停止发送`) break } } catch (error) { // 错误处理... } await new Promise(resolve => setTimeout(resolve, 300)) } ``` **新逻辑**(与游戏模块保持一致): ```javascript // 筛选待发车的车辆(state: 0) const readyToSendCars = carIds.filter(carId => getCarState(carDataMap[carId]) === 0) const currentShippingCars = carIds.filter(carId => getCarState(carDataMap[carId]) === 1) // 计算剩余可发车次数 const remainingSendCount = 4 - dailySendCount console.log(`📊 [${tokenId}] 今日已发${dailySendCount}辆,剩余可发${remainingSendCount}辆`) if (readyToSendCars.length === 0) { if (claimSuccessCount === 0 && currentShippingCars.length > 0) { console.log(`⚠️ [${tokenId}] 所有车辆都在运输中`) } else if (claimSuccessCount === 0) { console.log(`⚠️ [${tokenId}] 没有可操作的车辆`) } else { console.log(`⚠️ [${tokenId}] 没有待发车的车辆`) } } else { console.log(`🚀 [${tokenId}] 找到 ${readyToSendCars.length} 辆待发车的车辆`) // 如果待发车数量大于剩余可发数量,显示提示 if (readyToSendCars.length > remainingSendCount) { console.log(`⚠️ [${tokenId}] 待发车${readyToSendCars.length}辆,但今日仅剩${remainingSendCount}个发车名额`) } // 限制发送数量不超过剩余可发次数 const carsToSend = readyToSendCars.slice(0, remainingSendCount) for (let i = 0; i < carsToSend.length; i++) { const carId = carsToSend[i] console.log(`🚀 [${tokenId}] 发送进度: ${i + 1}/${carsToSend.length}(今日已发${dailySendCount}/4)`) try { await tokenStore.sendMessageAsync(tokenId, 'car_send', { carId: carId, helperId: 0, text: "" }, 5000) sendSuccessCount++ console.log(`✅ [${tokenId}] 发送车辆成功: ${carId}`) // 发送成功后,增加今日发车次数(与 CarManagement.vue 保持一致) dailySendCount++ localStorage.setItem(dailySendKey, dailySendCount.toString()) // 发送成功后检查是否达到上限 if (dailySendCount >= 4) { console.log(`✅ [${tokenId}] 今日发车次数已达上限,停止继续发送`) break } } catch (error) { const errorMsg = error.message || String(error) // 区分不同的错误类型 if (errorMsg.includes('12000050')) { console.log(`⚠️ [${tokenId}] 车辆 ${carId} 发送失败: 今日发车次数已达上限(服务器端限制)`) // 服务器返回已达上限,更新客户端记录 localStorage.setItem(dailySendKey, '4') dailySendCount = 4 break } else if (errorMsg.includes('200020')) { console.log(`⚠️ [${tokenId}] 车辆 ${carId} 处于发送冷却期`) } else { console.error(`❌ [${tokenId}] 发送车辆失败: ${carId} - ${errorMsg}`) } } // 添加间隔(与 CarManagement.vue 保持一致) if (i < carsToSend.length - 1) { await new Promise(resolve => setTimeout(resolve, 300)) } } } ``` **关键改进**: 1. **增强边界检查**: 明确区分"所有车辆都在运输中"和"没有可操作的车辆" 2. **实时计数更新**: 每次发送成功后立即更新 `dailySendCount`,而不是事后累加 3. **更精确的日志**: 显示当前已发次数,便于调试 --- ### 5. 新增 `queryClubCars` 辅助函数 为了便于多次查询车辆,新增了一个独立的 `queryClubCars` 辅助函数: ```javascript // 辅助函数:查询车辆(与 CarManagement.vue 保持一致) const queryClubCars = async () => { console.log(`🚗 [${tokenId}] 开始查询俱乐部车辆...`) try { const response = await tokenStore.sendMessageAsync(tokenId, 'car_getrolecar', {}, 10000) if (!response || !response.roleCar) { throw new Error('查询车辆失败:未返回车辆数据') } const carDataMap = response.roleCar.carDataMap || {} const carIds = Object.keys(carDataMap).sort() // 按ID排序 return { carDataMap, carIds } } catch (error) { // 检查是否是 200400 错误(账号未加入俱乐部) if (error.message && error.message.includes('200400')) { throw new Error('该账号未加入俱乐部或没有赛车权限') } throw error } } ``` **使用场景**: 1. 初始查询车辆 2. 刷新后重新查询车辆 3. 收获后重新查询车辆 --- ## 新的执行流程 ### 整体流程(与游戏模块保持一致) ``` 第1步:查询车辆 ├─ 使用 tokenStore.sendMessageAsync(tokenId, 'car_getrolecar', {}, 10000) ├─ 获取 carDataMap 和 carIds └─ 读取客户端的 dailySendCount 第2步:批量刷新(可选,根据 carRefreshCount 配置) ├─ 遍历所有车辆 ├─ 跳过有刷新票的车辆 ├─ 调用 car_refresh (5000ms 超时) ├─ 每次刷新间隔 300ms └─ 刷新后等待 500ms,重新查询车辆 第3步:一键发车 │ ├─ 3.1: 批量收获已到达的车辆(state: 2) │ ├─ 筛选 arrivedCars (state === 2) │ ├─ 遍历 arrivedCars,调用 car_claim (5000ms 超时) │ ├─ 每次收获间隔 300ms │ └─ 如果有收获成功,等待 1 秒,重新查询车辆 │ └─ 3.2: 批量发送待发车的车辆(state: 0) ├─ 重新读取客户端的 dailySendCount ├─ 检查每日发车次数限制(dailySendCount >= 4) ├─ 筛选 readyToSendCars (state === 0) ├─ 限制发送数量 = min(readyToSendCars.length, 4 - dailySendCount) ├─ 遍历 carsToSend,调用 car_send (5000ms 超时) ├─ 每次发送成功后,立即更新 dailySendCount ├─ 每次发送间隔 300ms └─ 达到上限(dailySendCount >= 4)后立即停止 ``` --- ## 性能对比 ### 旧流程(v3.10.3) ``` 1. 获取角色信息(激活账号): 5秒(超时) + 1秒(等待) = 6秒 2. 查询车辆: 20秒(超时) 3. 批量刷新(假设1轮,4辆车): 4 × 5秒(超时) + 3 × 0.3秒(间隔) = 20.9秒 4. 刷新后查询: 20秒(超时) + 0.5秒(等待) = 20.5秒 5. 批量收获(假设2辆已到达): 2 × 5秒(超时) + 1 × 0.3秒(间隔) = 10.3秒 6. 等待同步: 3秒 7. 收获后查询: 20秒(超时) 8. 批量发送(假设4辆待发车): 4 × 5秒(超时) + 3 × 0.3秒(间隔) = 20.9秒 总计(最坏情况,全部超时): 6 + 20 + 20.9 + 20.5 + 10.3 + 3 + 20 + 20.9 = 121.6秒 总计(正常情况,无超时): 6 + 1 + 20.9 + 1 + 1 + 3 + 1 + 1 = 34.9秒 ``` ### 新流程(v3.11.0) ``` 1. 查询车辆: 10秒(超时) 2. 批量刷新(假设1轮,4辆车): 4 × 5秒(超时) + 3 × 0.3秒(间隔) = 20.9秒 3. 刷新后查询: 10秒(超时) + 0.5秒(等待) = 10.5秒 4. 批量收获(假设2辆已到达): 2 × 5秒(超时) + 1 × 0.3秒(间隔) = 10.3秒 5. 收获后查询: 10秒(超时) + 1秒(等待) = 11秒 6. 批量发送(假设4辆待发车): 4 × 5秒(超时) + 3 × 0.3秒(间隔) = 20.9秒 总计(最坏情况,全部超时): 10 + 20.9 + 10.5 + 10.3 + 11 + 20.9 = 83.6秒 总计(正常情况,无超时): 1 + 20.9 + 1 + 1 + 2 + 1 = 27.9秒 ``` **性能提升**: - 最坏情况(全部超时): 从 121.6秒 降低到 83.6秒,**节省 31.3%** - 正常情况(无超时): 从 34.9秒 降低到 27.9秒,**节省 20.1%** --- ## 影响范围 ### 修改的文件 1. `src/stores/batchTaskStore.js` - 批量任务核心逻辑(sendCar case 完全重写) ### 影响的功能 1. **批量自动化 - sendCar 任务**: - 执行流程简化 - 超时时间调整 - 日志更详细 ### 不受影响的功能 1. **游戏功能模块 - CarManagement.vue**: 无任何修改 2. **其他批量自动化任务**: dailyFix, legionSignIn, autoStudy, claimHangupReward, addClock, climbTower - 均不受影响 --- ## 测试建议 ### 测试场景 1:所有车辆都在运输中 **预期行为**: - 查询车辆成功 - 收获阶段:0 辆已到达,跳过收获 - 发送阶段:0 辆待发车,提示"所有车辆都在运输中" **预期日志**: ``` 🚗 [token_xxx] 开始查询俱乐部车辆... ✅ [token_xxx] 查询到 4 辆车(今日已发车: 0/4) 🎯 [token_xxx] 开始一键发车(收获 + 发送)... 🎁 [token_xxx] 第1步:批量收获已到达的车辆 ⚠️ [token_xxx] 没有已到达的车辆可以收获 ⚠️ [token_xxx] 4辆车正在运输中 🚀 [token_xxx] 第2步:批量发送待发车的车辆 ⚠️ [token_xxx] 所有车辆都在运输中 ``` --- ### 测试场景 2:有2辆已到达,2辆待发车 **预期行为**: - 查询车辆成功 - 收获阶段:成功收获 2 辆 - 等待 1 秒,重新查询车辆 - 发送阶段:找到 4 辆待发车,成功发送 4 辆 **预期日志**: ``` 🚗 [token_xxx] 开始查询俱乐部车辆... ✅ [token_xxx] 查询到 4 辆车(今日已发车: 0/4) 🎯 [token_xxx] 开始一键发车(收获 + 发送)... 🎁 [token_xxx] 第1步:批量收获已到达的车辆 🎁 [token_xxx] 找到 2 辆已到达的车辆 🎁 [token_xxx] 收获进度: 1/2 ✅ [token_xxx] 收获车辆成功: car_id_1 🎁 [token_xxx] 收获进度: 2/2 ✅ [token_xxx] 收获车辆成功: car_id_2 🎁 [token_xxx] 收获完成:成功 2, 失败 0 🔄 [token_xxx] 收获成功,等待1秒后重新查询车辆状态... 🚗 [token_xxx] 开始查询俱乐部车辆... 🚀 [token_xxx] 第2步:批量发送待发车的车辆 📊 [token_xxx] 今日已发0辆,剩余可发4辆 🚀 [token_xxx] 找到 4 辆待发车的车辆 🚀 [token_xxx] 发送进度: 1/4(今日已发0/4) ✅ [token_xxx] 发送车辆成功: car_id_1 🚀 [token_xxx] 发送进度: 2/4(今日已发1/4) ✅ [token_xxx] 发送车辆成功: car_id_2 🚀 [token_xxx] 发送进度: 3/4(今日已发2/4) ✅ [token_xxx] 发送车辆成功: car_id_3 🚀 [token_xxx] 发送进度: 4/4(今日已发3/4) ✅ [token_xxx] 发送车辆成功: car_id_4 ✅ [token_xxx] 今日发车次数已达上限,停止继续发送 🚀 [token_xxx] 发送完成:成功4次,跳过0次 ``` --- ### 测试场景 3:账号未加入俱乐部(错误 200400) **预期行为**: - 查询车辆失败,抛出友好错误:"该账号未加入俱乐部或没有赛车权限" - 任务标记为失败 **预期日志**: ``` 🚗 [token_xxx] 开始查询俱乐部车辆... ❌ [token_xxx] 发车任务失败: Error: 该账号未加入俱乐部或没有赛车权限 ``` --- ### 测试场景 4:今日已发车4次 **预期行为**: - 查询车辆成功 - 收获阶段:成功收获 N 辆 - 发送阶段:检测到 `dailySendCount >= 4`,跳过发送 **预期日志**: ``` 🚗 [token_xxx] 开始查询俱乐部车辆... ✅ [token_xxx] 查询到 4 辆车(今日已发车: 4/4) 🎯 [token_xxx] 开始一键发车(收获 + 发送)... 🎁 [token_xxx] 第1步:批量收获已到达的车辆 ...(收获流程)... 🚀 [token_xxx] 第2步:批量发送待发车的车辆 ⚠️ [token_xxx] 今日发车次数已达上限: 4/4,跳过发送步骤 ``` --- ## 注意事项 ### 1. 游戏模块逻辑的准确移植 本次重构的核心原则是**完全复用游戏模块的逻辑**,包括: - 超时时间(10000ms for query, 5000ms for refresh/claim/send) - 操作间隔(300ms) - 等待时间(刷新后 500ms,收获后 1000ms) - 状态判断(getCarState 函数) - 错误处理(200400, 200020, 12000050) 任何后续修改都应**优先在游戏模块中测试**,验证成功后再同步到批量自动化。 ### 2. 服务器 `sendCount` 字段不可靠 从 v3.10.1 开始,我们已经知道服务器的 `roleCar.sendCount` 字段不可靠(总是返回 0)。因此,批量自动化和游戏模块都**不再同步服务器的 sendCount**,而是: 1. 客户端维护 `dailySendCount` 在 `localStorage` 中 2. 每次发送成功后,立即增加 `dailySendCount` 3. 如果服务器返回 `12000050` 错误(今日发车次数已达上限),则将 `dailySendCount` 设置为 4 ### 3. 并发控制 当前批量自动化的默认并发数为 1(`maxConcurrency = 1`),这是为了: - 避免服务器反批量检测 - 减少超时和错误发生的概率 如果用户增加并发数(如 3-6),可能会遇到: - `car_getrolecar` 超时(即使是 10000ms) - 错误 200400(账号未加入俱乐部) 这些错误**不是客户端代码的问题**,而是服务器端的限制或账号状态问题。 ### 4. 日志的重要性 本次重构增强了日志输出,包括: - 每一步的进度(`收获进度: 1/2`,`发送进度: 3/4`) - 每辆车的 ID 和操作结果 - 今日已发车次数的实时更新 这些日志对于调试和用户反馈非常重要,**请不要删除或简化这些日志**。 --- ## 总结 本次重构(v3.11.0)彻底简化了批量自动化的发车逻辑,通过**直接复用游戏功能模块的已验证逻辑**,实现了: ✅ **代码复用**: 批量自动化和游戏模块的行为完全一致 ✅ **流程简化**: 移除不必要的"激活账号"步骤和过长的等待时间 ✅ **性能提升**: 正常情况下节省 20.1% 时间,最坏情况节省 31.3% 时间 ✅ **更稳定**: 减少了超时和错误的可能性 ✅ **更易维护**: 逻辑清晰,日志详细,易于调试 如果后续仍有问题,建议: 1. 先在游戏功能模块(单个 token)中测试,确认问题是否也存在 2. 如果游戏模块正常,批量自动化异常,则是并发或状态管理问题 3. 如果游戏模块也异常,则是服务器端或账号状态问题 --- **版本标识**: v3.11.0 **后续优化方向**: 1. 在批量自动化执行前,预先检查账号的俱乐部状态,跳过未加入俱乐部的账号 2. 根据实际测试情况,动态调整并发数和超时时间 3. 考虑在游戏模块中添加"批量自动化模式",使用更保守的配置(更长的超时,更多的等待)