Files
xyzw_web_helper/MD说明文件夹/重构-发车任务复用游戏模块逻辑v3.11.0.md
2025-10-17 20:56:50 +08:00

21 KiB
Raw Permalink Blame History

重构:发车任务复用游戏模块逻辑 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. 移除"激活账号"步骤

旧逻辑:

// 第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))

新逻辑:

// 直接查询车辆,无需"激活账号"步骤

理由:

  • 游戏功能模块没有此步骤,直接查询车辆就能成功
  • 此步骤增加了 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. 简化批量收获逻辑

旧逻辑:

// 第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)

新逻辑(与游戏模块保持一致):

// 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.claimAtcarInfo.sendAt,而是依赖服务器返回的最新状态

4. 简化批量发送逻辑

旧逻辑:

// 找到待发车的车辆
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))
}

新逻辑(与游戏模块保持一致):

// 筛选待发车的车辆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 辅助函数:

// 辅助函数:查询车辆(与 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. 客户端维护 dailySendCountlocalStorage
  2. 每次发送成功后,立即增加 dailySendCount
  3. 如果服务器返回 12000050 错误(今日发车次数已达上限),则将 dailySendCount 设置为 4

3. 并发控制

当前批量自动化的默认并发数为 1maxConcurrency = 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. 考虑在游戏模块中添加"批量自动化模式",使用更保守的配置(更长的超时,更多的等待)