feat: 添加设备冷却逻辑,过滤冷却期内设备并处理冷却队列
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
package com.gameplatform.server.service.cooldown;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* 机器冷却服务
|
||||
* 实现同一台机器在10分钟内不会重复调用的机制
|
||||
*/
|
||||
@Service
|
||||
public class MachineCooldownService {
|
||||
private static final Logger log = LoggerFactory.getLogger(MachineCooldownService.class);
|
||||
|
||||
// 冷却时间:10分钟
|
||||
private static final int COOLDOWN_MINUTES = 10;
|
||||
|
||||
// 机器冷却记录:machineId -> 最后操作时间
|
||||
private final ConcurrentMap<String, LocalDateTime> machineCooldownMap = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 检查机器是否在冷却期内
|
||||
* @param machineId 机器ID
|
||||
* @return true表示在冷却期内,false表示可以操作
|
||||
*/
|
||||
public boolean isMachineInCooldown(String machineId) {
|
||||
if (machineId == null || machineId.trim().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LocalDateTime lastOperationTime = machineCooldownMap.get(machineId);
|
||||
if (lastOperationTime == null) {
|
||||
log.debug("机器{}没有冷却记录,允许操作", machineId);
|
||||
return false;
|
||||
}
|
||||
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
LocalDateTime cooldownExpireTime = lastOperationTime.plusMinutes(COOLDOWN_MINUTES);
|
||||
|
||||
boolean inCooldown = now.isBefore(cooldownExpireTime);
|
||||
|
||||
if (inCooldown) {
|
||||
long remainingMinutes = java.time.Duration.between(now, cooldownExpireTime).toMinutes();
|
||||
log.info("机器{}在冷却期内,剩余冷却时间:{}分钟", machineId, remainingMinutes + 1);
|
||||
} else {
|
||||
log.debug("机器{}冷却期已过,允许操作", machineId);
|
||||
}
|
||||
|
||||
return inCooldown;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将机器加入冷却队列
|
||||
* @param machineId 机器ID
|
||||
* @param reason 加入冷却的原因
|
||||
*/
|
||||
public void addMachineToCooldown(String machineId, String reason) {
|
||||
if (machineId == null || machineId.trim().isEmpty()) {
|
||||
log.warn("尝试添加空的机器ID到冷却队列");
|
||||
return;
|
||||
}
|
||||
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
machineCooldownMap.put(machineId, now);
|
||||
|
||||
log.info("机器{}已加入冷却队列,原因:{},冷却时间:{}分钟,冷却结束时间:{}",
|
||||
machineId, reason, COOLDOWN_MINUTES, now.plusMinutes(COOLDOWN_MINUTES));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取机器剩余冷却时间(分钟)
|
||||
* @param machineId 机器ID
|
||||
* @return 剩余冷却时间,如果不在冷却期则返回0
|
||||
*/
|
||||
public long getRemainingCooldownMinutes(String machineId) {
|
||||
if (machineId == null || machineId.trim().isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
LocalDateTime lastOperationTime = machineCooldownMap.get(machineId);
|
||||
if (lastOperationTime == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
LocalDateTime cooldownExpireTime = lastOperationTime.plusMinutes(COOLDOWN_MINUTES);
|
||||
|
||||
if (now.isBefore(cooldownExpireTime)) {
|
||||
return java.time.Duration.between(now, cooldownExpireTime).toMinutes() + 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动移除机器的冷却状态(用于测试或管理员操作)
|
||||
* @param machineId 机器ID
|
||||
*/
|
||||
public void removeMachineFromCooldown(String machineId) {
|
||||
if (machineId == null || machineId.trim().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
LocalDateTime removedTime = machineCooldownMap.remove(machineId);
|
||||
if (removedTime != null) {
|
||||
log.info("已手动移除机器{}的冷却状态,原冷却开始时间:{}", machineId, removedTime);
|
||||
} else {
|
||||
log.debug("机器{}不在冷却队列中", machineId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理过期的冷却记录(可定期调用以释放内存)
|
||||
*/
|
||||
public void cleanupExpiredCooldowns() {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
int cleanedCount = 0;
|
||||
|
||||
for (var entry : machineCooldownMap.entrySet()) {
|
||||
String machineId = entry.getKey();
|
||||
LocalDateTime lastOperationTime = entry.getValue();
|
||||
LocalDateTime cooldownExpireTime = lastOperationTime.plusMinutes(COOLDOWN_MINUTES);
|
||||
|
||||
if (now.isAfter(cooldownExpireTime)) {
|
||||
machineCooldownMap.remove(machineId);
|
||||
cleanedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (cleanedCount > 0) {
|
||||
log.info("清理了{}个过期的机器冷却记录", cleanedCount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前冷却队列的大小
|
||||
* @return 冷却队列中的机器数量
|
||||
*/
|
||||
public int getCooldownQueueSize() {
|
||||
return machineCooldownMap.size();
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import com.gameplatform.server.model.entity.agent.LinkTask;
|
||||
import com.gameplatform.server.service.external.ScriptClient;
|
||||
import com.gameplatform.server.service.device.DeviceStatusCheckService;
|
||||
import com.gameplatform.server.service.admin.SystemConfigService;
|
||||
import com.gameplatform.server.service.cooldown.MachineCooldownService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -40,6 +41,7 @@ public class LinkStatusService {
|
||||
private final ScriptClient scriptClient;
|
||||
private final DeviceStatusCheckService deviceStatusCheckService;
|
||||
private final SystemConfigService systemConfigService;
|
||||
private final MachineCooldownService machineCooldownService;
|
||||
|
||||
|
||||
// 状态描述映射
|
||||
@@ -55,12 +57,13 @@ public class LinkStatusService {
|
||||
|
||||
public LinkStatusService(LinkTaskMapper linkTaskMapper, LinkBatchMapper linkBatchMapper,
|
||||
ScriptClient scriptClient,
|
||||
DeviceStatusCheckService deviceStatusCheckService, SystemConfigService systemConfigService) {
|
||||
DeviceStatusCheckService deviceStatusCheckService, SystemConfigService systemConfigService, MachineCooldownService machineCooldownService) {
|
||||
this.linkTaskMapper = linkTaskMapper;
|
||||
this.linkBatchMapper = linkBatchMapper;
|
||||
this.scriptClient = scriptClient;
|
||||
this.deviceStatusCheckService = deviceStatusCheckService;
|
||||
this.systemConfigService = systemConfigService;
|
||||
this.machineCooldownService = machineCooldownService;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -481,6 +484,13 @@ public class LinkStatusService {
|
||||
if (linkTask.getQrCreatedAt() != null &&
|
||||
linkTask.getQrCreatedAt().isBefore(LocalDateTime.now().minusMinutes(10))) {
|
||||
log.warn("选择设备已超过10分钟未登录,链接过期: qrCreatedAt={}", linkTask.getQrCreatedAt());
|
||||
|
||||
// 将设备加入冷却队列 - 因为10分钟内没有成功登录
|
||||
if (linkTask.getMachineId() != null) {
|
||||
machineCooldownService.addMachineToCooldown(linkTask.getMachineId(), "10分钟内未登录成功");
|
||||
log.info("设备{}因10分钟内未登录成功被加入冷却队列", linkTask.getMachineId());
|
||||
}
|
||||
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
linkTask.setStatus("EXPIRED");
|
||||
linkTask.setExpireAt(now); // 设置过期时间戳
|
||||
@@ -616,10 +626,34 @@ public class LinkStatusService {
|
||||
log.info("空闲设备检查完成: 总设备数={}, 空闲设备数={}, 空闲设备列表={}",
|
||||
deviceStatus.getTotalDevices(), deviceStatus.getAvailableCount(), deviceStatus.getAvailableDevices());
|
||||
|
||||
// 选择一个空闲设备
|
||||
selectedDeviceId = deviceStatus.getAvailableDevices().get(0); // 选择第一个空闲设备
|
||||
log.info("首次选区设备分配成功: 选中设备={}, 从{}个空闲设备中选择第一个",
|
||||
selectedDeviceId, deviceStatus.getAvailableDevices().size());
|
||||
// 过滤掉冷却期内的设备
|
||||
List<String> availableDevices = deviceStatus.getAvailableDevices();
|
||||
List<String> nonCooldownDevices = new ArrayList<>();
|
||||
|
||||
for (String deviceId : availableDevices) {
|
||||
if (!machineCooldownService.isMachineInCooldown(deviceId)) {
|
||||
nonCooldownDevices.add(deviceId);
|
||||
} else {
|
||||
long remainingMinutes = machineCooldownService.getRemainingCooldownMinutes(deviceId);
|
||||
log.info("设备{}在冷却期内,剩余时间{}分钟,跳过选择", deviceId, remainingMinutes);
|
||||
}
|
||||
}
|
||||
|
||||
if (nonCooldownDevices.isEmpty()) {
|
||||
log.error("设备分配失败: 所有空闲设备都在冷却期内, 总空闲设备数={}", availableDevices.size());
|
||||
throw new RuntimeException("所有设备都在冷却期内,请稍后再试");
|
||||
}
|
||||
|
||||
log.info("冷却期过滤完成: 原空闲设备数={}, 可用设备数={}, 可用设备列表={}",
|
||||
availableDevices.size(), nonCooldownDevices.size(), nonCooldownDevices);
|
||||
|
||||
// 选择一个非冷却期设备
|
||||
selectedDeviceId = nonCooldownDevices.get(0); // 选择第一个非冷却期设备
|
||||
log.info("首次选区设备分配成功: 选中设备={}, 从{}个非冷却期设备中选择第一个",
|
||||
selectedDeviceId, nonCooldownDevices.size());
|
||||
|
||||
// 将选中的设备加入冷却队列
|
||||
machineCooldownService.addMachineToCooldown(selectedDeviceId, "首次选区");
|
||||
}else{
|
||||
log.info("重复选区: 检查首次选区时效性并复用设备");
|
||||
// 检查首次选区是否已过期
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.gameplatform.server.task;
|
||||
|
||||
import com.gameplatform.server.service.cooldown.MachineCooldownService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 机器冷却清理任务
|
||||
* 定期清理过期的机器冷却记录以释放内存
|
||||
*/
|
||||
@Component
|
||||
public class MachineCooldownCleanupTask {
|
||||
private static final Logger log = LoggerFactory.getLogger(MachineCooldownCleanupTask.class);
|
||||
|
||||
private final MachineCooldownService machineCooldownService;
|
||||
|
||||
public MachineCooldownCleanupTask(MachineCooldownService machineCooldownService) {
|
||||
this.machineCooldownService = machineCooldownService;
|
||||
}
|
||||
|
||||
/**
|
||||
* 每30分钟清理一次过期的冷却记录
|
||||
*/
|
||||
@Scheduled(fixedRate = 30 * 60 * 1000) // 30分钟
|
||||
public void cleanupExpiredCooldowns() {
|
||||
try {
|
||||
int sizeBefore = machineCooldownService.getCooldownQueueSize();
|
||||
machineCooldownService.cleanupExpiredCooldowns();
|
||||
int sizeAfter = machineCooldownService.getCooldownQueueSize();
|
||||
|
||||
if (sizeBefore != sizeAfter) {
|
||||
log.info("机器冷却记录清理完成: 清理前{}个,清理后{}个,清理了{}个过期记录",
|
||||
sizeBefore, sizeAfter, sizeBefore - sizeAfter);
|
||||
} else {
|
||||
log.debug("机器冷却记录清理完成: 无过期记录需要清理,当前记录数{}", sizeAfter);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("机器冷却记录清理失败: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user