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

584 lines
21 KiB
Markdown
Raw Permalink 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.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. 考虑在游戏模块中添加"批量自动化模式"使用更保守的配置更长的超时更多的等待