@@ -489,7 +489,7 @@ public class LinkStatusService {
// 如果未超过10分钟, 执行自动刷新
log . info ( " 链接状态是USING, 执行自动刷新 " ) ;
performAutoRefresh( linkTask) ;
// performAutoRefresh( linkTask) ;
} else if ( " LOGGED_IN " . equals ( linkTask . getStatus ( ) ) | | " COMPLETED " . equals ( linkTask . getStatus ( ) ) | | " REFUNDED " . equals ( linkTask . getStatus ( ) ) ) {
// 已上号、已完成或已退款状态,不需要刷新
log . info ( " 链接状态为 {},不需要刷新 " , linkTask . getStatus ( ) ) ;
@@ -508,26 +508,7 @@ public class LinkStatusService {
}
}
/**
* 执行自动刷新逻辑
* 只调用判断刷新接口,不更新数据库状态
*/
private void performAutoRefresh ( LinkTask linkTask ) {
try {
log . info ( " 开始执行刷新操作 " ) ;
if ( linkTask . getMachineId ( ) = = null ) {
log . warn ( " 机器ID为空, 无法执行刷新操作 " ) ;
return ;
}
// 调用判断刷新接口( 通过ScriptClient统一管理)
String refreshResult = scriptClient . refresh ( linkTask . getMachineId ( ) ) . block ( ) ;
log . info ( " 判断刷新接口调用完成: result={} " , refreshResult ) ;
} catch ( Exception e ) {
log . warn ( " 执行刷新操作失败: {} " , e . getMessage ( ) ) ;
// 刷新失败不影响后续流程,只记录警告日志
}
}
@@ -554,42 +535,51 @@ public class LinkStatusService {
. subscribeOn ( Schedulers . boundedElastic ( ) ) ;
}
private SelectRegionResponse doSelectRegion ( String code , String region ) {
private SelectRegionResponse doSelectRegion ( String code , String region ) throws InterruptedException {
log . info ( " === 开始选区操作 === " ) ;
log . info ( " code: {}, region: {} " , code , region ) ;
log . info ( " 请求参数: code= {}, region= {}" , code , region ) ;
try {
// 1. 验证参数
log . info ( " 步骤1: 开始参数验证 " ) ;
if ( code = = null | | code . trim ( ) . isEmpty ( ) ) {
log . error ( " 参数验证失败: code不能为空 " ) ;
throw new IllegalArgumentException ( " code不能为空 " ) ;
}
if ( region = = null | | ( ! region . equals ( " Q " ) & & ! region . equals ( " V " ) ) ) {
log . error ( " 参数验证失败: region只能是Q或V, 当前值={} " , region ) ;
throw new IllegalArgumentException ( " region只能是Q或V " ) ;
}
log . info ( " 参数验证通过: code={}, region={} " , code . trim ( ) , region ) ;
// 2. 查询链接任务
log . info ( " 步骤2: 开始查询链接任务 " ) ;
LinkTask linkTask = linkTaskMapper . findByCodeNo ( code . trim ( ) ) ;
if ( linkTask = = null ) {
log . error ( " 链接任务不存在: code={} " , code ) ;
log . error ( " 链接任务查询失败: 未找到对应的链接任务, code={} " , code ) ;
throw new IllegalArgumentException ( " 链接不存在 " ) ;
}
log . info ( " 查询到 链接任务: id={}, codeNo={}, status={}, needRefresh ={}" ,
linkTask . getId ( ) , linkTask . getCodeNo ( ) , linkTask . getStatus ( ) , linkTask . getNeedRefresh ( ) ) ;
log . info ( " 链接任务查询成功 : id={}, codeNo={}, status={}, batchId={}, machineId={}, needRefresh={}, firstRegionSelectAt ={} " ,
linkTask . getId ( ) , linkTask . getCodeNo ( ) , linkTask . getStatus ( ) ,
linkTask . getBatchId ( ) , linkTask . getMachineId ( ) , linkTask . getNeedRefresh ( ) , linkTask . getFirstRegionSelectAt ( ) ) ;
// 查询批次信息获取times参数
log . info ( " 步骤3: 开始查询批次信息 " ) ;
LinkBatch linkBatch = linkBatchMapper . findById ( linkTask . getBatchId ( ) ) ;
if ( linkBatch = = null ) {
log . error ( " 批次信息不存在: batchId={} " , linkTask . getBatchId ( ) ) ;
log . error ( " 批次信息查询失败: 未找到对应的批次信息, batchId={} " , linkTask . getBatchId ( ) ) ;
throw new IllegalStateException ( " 批次信息不存在 " ) ;
}
log . info ( " 查询到 批次信息: batchId={}, times={}" , linkBatch . getId ( ) , linkBatch . getTimes ( ) ) ;
log . info ( " 批次信息查询成功 : batchId={}, times={}, quantity={} " , linkBatch . getId ( ) , linkBatch . getTimes ( ) , linkBatch . getQuantity ( ) );
// 3 . 检查链接状态, 只有NEW或USING状态才能选区
// 4 . 检查链接状态, 只有NEW或USING状态才能选区
log . info ( " 步骤4: 开始检查链接状态 " ) ;
if ( ! " NEW " . equals ( linkTask . getStatus ( ) ) & & ! " USING " . equals ( linkTask . getStatus ( ) ) ) {
log . error ( " 链接状态不正确,无法选区: status={} " , linkTask . getStatus ( ) ) ;
log . error ( " 链接状态检查失败: 当前状态={}, 只允许NEW或USING状态进行选区操作 " , linkTask . getStatus ( ) ) ;
throw new IllegalArgumentException ( " 链接状态不正确,只有新建或使用中状态的链接才能选区 " ) ;
}
log . info ( " 链接状态检查通过: 当前状态={}, 允许进行选区操作 " , linkTask . getStatus ( ) ) ;
// 5. 如果need_refresh=true, 检查是否已等待10秒
@@ -603,110 +593,136 @@ public class LinkStatusService {
// return response;
// }
// }
// 5. 设备选择逻辑
log . info ( " 步骤5: 开始设备选择逻辑 " ) ;
String selectedDeviceId ;
if ( linkTask . getFirstRegionSelectAt ( ) = = null ) {
log . info ( " 开始检查空闲设备 " ) ;
log . info ( " 首次选区: 开始检查和分配 空闲设备" ) ;
DeviceStatusResponse deviceStatus = scriptClient . checkAvailableDeviceStatus ( ) . block ( ) ;
// 检查是否有空闲设备
if ( deviceStatus . getAvailableCount ( ) = = 0 ) {
log . warn ( " 当前没有空闲设备,无法选择区域 " ) ;
log . error ( " 设备分配失败: 当前没有空闲设备可用, 总设备数={}, 空闲设备数={} " ,
deviceStatus . getTotalDevices ( ) , deviceStatus . getAvailableCount ( ) ) ;
throw new RuntimeException ( " 当前没有空闲设备,请稍后再试 " ) ;
}
log . info ( " 空闲设备检查完成: 总设备数={}, 空闲设备数={}, 空闲设备列表={} " ,
deviceStatus . getTotalDevices ( ) , deviceStatus . getAvailableCount ( ) , deviceStatus . getAvailableDevices ( ) ) ;
// 7. 选择一个空闲设备 TODO
// 选择一个空闲设备
selectedDeviceId = deviceStatus . getAvailableDevices ( ) . get ( 0 ) ; // 选择第一个空闲设备
// String selectedDevice = "cc2";
log . info ( " 从空闲设备列表中选择设备: {} " , selectedDeviceId ) ;
log . info ( " 设备选择详情: 可用设备总数={}, 选择了第一个设备={} " ,
deviceStatus . getAvailableDevices ( ) . size ( ) , selectedDeviceId ) ;
log . info ( " 首次选区设备分配成功: 选中设备={}, 从{}个空闲设备中选择第一个 " ,
selectedDeviceId , deviceStatus . getAvailableDevices ( ) . size ( ) ) ;
} else {
log . info ( " 重复选区: 检查首次选区时效性并复用设备 " ) ;
// 检查首次选区是否已过期
LocalDateTime firstSelectTime = linkTask . getFirstRegionSelectAt ( ) ;
long expireSeconds = systemConfigService . getFirstRegionExpireSeconds ( ) ;
LocalDateTime expireTime = firstSelectTime . plusSeconds ( expireSeconds ) ;
LocalDateTime now = LocalDateTime . now ( ) ;
if ( LocalDateTime . now ( ) . isAfter ( expireTime ) ) {
log . warn ( " 链接首次选区已过期: firstSelectTime={}, expireTime={}, now={} " ,
firstSelectTime , expireTime , LocalDateTime . now ( ) ) ;
log . info ( " 时效性检查: 首次选区时间={}, 过期时间={}, 当前时间={}, 过期秒数={} " ,
firstSelectTime, expireTime , now , expireSeconds ) ;
if ( now . isAfter ( expireTime ) ) {
log . error ( " 链接过期检查失败: 首次选区已过期, firstSelectTime={}, expireTime={}, now={} " ,
firstSelectTime , expireTime , now ) ;
// 将链接状态设置为过期
linkTask . setStatus ( " EXPIRED " ) ;
linkTask . setUpdatedAt ( LocalDateTime . now ( ) ) ;
linkTask . setUpdatedAt ( now ) ;
linkTaskMapper . updateById ( linkTask ) ;
log . info ( " 链接状态已更新为EXPIRED: linkTaskId={} " , linkTask . getId ( ) ) ;
log . info ( " 链接状态已更新为EXPIRED: linkTaskId={}, 更新时间={} " , linkTask . getId ( ) , now );
throw new RuntimeException ( " 链接已过期,请重新获取 " ) ;
}
selectedDeviceId = linkTask . getMachineId ( ) ;
log . info ( " 重复选区设备复用成功: 使用之前分配的设备={}, 首次选区时间={} " , selectedDeviceId , firstSelectTime ) ;
log . info ( " 执行设备刷新操作: 设备={} " , selectedDeviceId ) ;
scriptClient . refresh ( selectedDeviceId ) . block ( ) ;
log . info ( " 链接已选过区且未过期, 继续使用之前的设备: {} " , selectedDeviceId ) ;
log . info ( " 设备刷新完成: 设备={}, 继续使用之前分配 的设备" , selectedDeviceId ) ;
// 休眠5秒, 确保设备状态稳定
Thread . sleep ( 11000 ) ;
log . info ( " 等待5秒完成, 继续使用之前分配的设备: 设备={} " , selectedDeviceId ) ;
}
// 6. 检查空闲设备
log . info ( " 设备选择完成: 最终选中设备={} " , selectedDeviceId ) ;
// 7.5 . 检查该设备是否有之前的LOGGED_IN状态链接任务需要完成
// 6 . 检查该设备是否有之前的LOGGED_IN状态链接任务需要完成
log . info ( " 步骤6: 开始检查设备状态和历史任务 " ) ;
try {
log . info ( " 检查设备 {} 是否有需要完成的链接任务 " , selectedDeviceId ) ;
log . info ( " 检查设备历史任务: 设备={}, 触发原因=选区请求 " , selectedDeviceId ) ;
deviceStatusCheckService . checkDeviceStatusAndUpdateTasks ( selectedDeviceId , " 选区请求 " ) ;
log . info ( " 设备历史任务检查完成: 设备={} " , selectedDeviceId ) ;
} catch ( Exception e ) {
log . warn ( " 检查设备状态时发生异常,继续选区流程: {} " , e . getMessage ( ) ) ;
log . warn ( " 设备历史任务检查异常: 设备={}, 错误={}, 继续选区流程 " , selectedDeviceId , e . getMessage ( ) ) ;
// 不影响选区流程,只记录警告日志
}
// 8 . 调用保存总次数接口 TODO 应该放在登录成功之后
// 7 . 调用保存总次数接口
log . info ( " 步骤7: 开始保存总次数到脚本端 " ) ;
try {
log . info ( " 保存总次数: 设备={}, 次数={} " , selectedDeviceId , linkBatch . getTimes ( ) ) ;
scriptClient . saveTotalTimes ( selectedDeviceId , linkBatch . getTimes ( ) ) . block ( ) ;
// saveTotalTimes方法已经包含了详细的日志记录
log . info ( " 总次数保存成功: 设备={}, 次数={} " , selectedDeviceId , linkBatch . getTimes ( ) ) ;
} catch ( Exception e ) {
log . warn ( " 保存 总次数接口调用失败: {} " , e . getMessage ( ) ) ;
log . warn ( " 总次数保存失败: 设备={}, 次数={}, 错误={}, 继续后续流程 " , selectedDeviceId , linkBatch . getTimes ( ) , e . getMessage ( ) ) ;
// 不影响后续流程,只记录警告日志
}
// 9 . 为选中的设备创建代理code
// String proxyCode = deviceCodeMappingService.createProxyCode(selectedDeviceId) ;
// log.info("为 设备 {} 创建代理code: {}", selectedDeviceId, proxyCode) ;
// 10. 调用脚本端选区,使用选中的设备
log . info ( " 开始调用脚本端选区,设备={}, 区域={} " , selectedDeviceId , region ) ;
// 8 . 调用脚本端选区,使用选中的设备
log . info ( " 步骤8: 开始调用脚本端选区 " ) ;
log. info ( " 选区请求参数: 设备= {}, 区域={} " , selectedDeviceId , region ) ;
String selectResult = scriptClient . selectRegion ( selectedDeviceId , region ) . block ( ) ;
log . info ( " 脚本端选区结果: {} " , selectResult ) ;
log . info ( " 脚本端选区调用成功: 设备={}, 区域={}, 返回结果={} " , selectedDeviceId , region , selectResult ) ;
// 11. 等待脚本端生成二维码(这里可以添加轮询逻辑)
// log.info("等待脚本端生成二维码, 等待3秒...");
// Thread.sleep(3000);
// 12 . 更新数据库状态为USING, 保存设备信息和代理code
// 9 . 更新数据库状态为USING, 保存设备信息
log . info ( " 步骤9: 开始更新数据库状态 " ) ;
LocalDateTime now = LocalDateTime . now ( ) ;
LocalDateTime qrExpireAt = now . plusSeconds ( 60 ) ; // 60秒后过期
// 记录更新前的状态
log . info ( " 数据库更新前状态: id={}, status={}, region={}, machineId={}, firstRegionSelectAt={} " ,
linkTask . getId ( ) , linkTask . getStatus ( ) , linkTask . getRegion ( ) , linkTask . getMachineId ( ) , linkTask . getFirstRegionSelectAt ( ) ) ;
linkTask . setStatus ( " USING " ) ;
linkTask . setRegion ( region ) ;
linkTask . setCodeNo ( code ) ; // 使用代理code替换原来的codeNo
linkTask . setCodeNo ( code ) ;
linkTask . setQrCreatedAt ( now ) ;
linkTask . setQrExpireAt ( now . plusSeconds ( 60 ) ) ; // 60秒后过期 ToDO
linkTask . s etFirstRegionSelectAt( now ) ; // 记录首次选区时间
linkTask . setQrExpireAt ( qrExpireAt ) ;
if ( linkTask . g etFirstRegionSelectAt( ) = = null ) {
linkTask . setFirstRegionSelectAt ( now ) ; // 只在首次选区时记录
log . info ( " 首次选区: 记录首次选区时间={} " , now ) ;
}
linkTask . setNeedRefresh ( false ) ;
linkTask . setUpdatedAt ( now ) ;
// 在machineId字段保存真实设备编号, 便于调试和维护
linkTask . setMachineId ( selectedDeviceId ) ;
linkTaskMapper . update ( linkTask ) ;
log . info ( " 数据库更新成功: id={}, status=USING, region={}, machineId={}, qrCreatedAt={}, qrExpireAt={}, firstRegionSelectAt={} " ,
linkTask . getId ( ) , region , selectedDeviceId , now , qrExpireAt , linkTask . getFirstRegionSelectAt ( ) ) ;
log . info ( " 链接状态更新成功: status=USING, region={}, proxyCode={}, device={} " , region , code , selectedDeviceId ) ;
// 13. 构建响应
// 10. 构建响应
log . info ( " 步骤10: 开始构建响应数据 " ) ;
SelectRegionResponse response = new SelectRegionResponse ( true , " 选区成功 " ) ;
response . setQ rCodeUrl( scriptClient . getProxyQrCodeUrl ( code ) ) ;
String q rCodeUrl = scriptClient . getProxyQrCodeUrl ( code ) ;
response . setQrCodeUrl ( qrCodeUrl ) ;
response . setQrCreatedAt ( now ) ;
response . setQrExpireAt ( linkTask . getQ rExpireAt( ) );
response . setQrExpireAt ( q rExpireAt) ;
response . setStatus ( " USING " ) ;
// 不返回选区字段: response.setRegion(region);
response . setQrDelaySeconds ( 5 ) ; // 客户端收到响应后, 等待5秒再请求二维码
response . setQrDelaySeconds ( 10 ) ; // 客户端收到响应后, 等待5秒再请求二维码
response . setMecmachineId ( selectedDeviceId ) ; // 便于调试和维护
log . info ( " 响应构建完成: success=true, status=USING, qrCodeUrl={}, qrDelaySeconds=5, machineId={} " ,
qrCodeUrl , selectedDeviceId ) ;
log . info ( " === 选区操作完成 === " ) ;
log . info ( " 二维码URL: {} " , response . getQrCodeUrl ( ) ) ;
return response ;