feat: 删除设备冷却相关的实体、映射和服务,优化代码结构

This commit is contained in:
yahaozhang
2025-09-15 15:30:10 +08:00
parent 40f02f4539
commit cb69777499
10 changed files with 198 additions and 680 deletions

View File

@@ -0,0 +1,50 @@
package com.gameplatform.server.controller.admin;
import com.gameplatform.server.model.dto.cooldown.CooldownStatsResponse;
import com.gameplatform.server.model.dto.cooldown.MachineCooldownView;
import com.gameplatform.server.service.cooldown.MemoryMachineCooldownService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/api/admin/cooldown")
@Tag(name = "机器冷却", description = "查询与管理内存中的机器冷却状态")
public class CooldownController {
private final MemoryMachineCooldownService machineCooldownService;
public CooldownController(MemoryMachineCooldownService machineCooldownService) {
this.machineCooldownService = machineCooldownService;
}
@GetMapping("/list")
@Operation(summary = "列出冷却记录", description = "获取当前内存中的机器冷却记录")
public ResponseEntity<List<MachineCooldownView>> listCooldowns(
@Parameter(description = "仅返回仍在冷却中的记录")
@RequestParam(name = "activeOnly", required = false, defaultValue = "true") boolean activeOnly
) {
List<MachineCooldownView> list = machineCooldownService.listAllCooldowns(activeOnly);
return ResponseEntity.ok(list);
}
@GetMapping("/stats")
@Operation(summary = "冷却统计", description = "获取冷却总数、活跃数与总剩余分钟数")
public ResponseEntity<CooldownStatsResponse> getStats() {
MemoryMachineCooldownService.CooldownStats stats = machineCooldownService.getCooldownStats();
CooldownStatsResponse body = new CooldownStatsResponse(true,
stats.getTotalDevices(),
stats.getActiveDevices(),
stats.getTotalRemainingMinutes());
return ResponseEntity.ok(body);
}
}

View File

@@ -1,78 +0,0 @@
package com.gameplatform.server.mapper.cooldown;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.gameplatform.server.model.entity.cooldown.MachineCooldown;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDateTime;
import java.util.List;
/**
* 设备冷却状态Mapper
*/
@Mapper
public interface MachineCooldownMapper extends BaseMapper<MachineCooldown> {
/**
* 根据设备ID查找活跃的冷却记录
*/
MachineCooldown findActiveCooldownByMachineId(@Param("machineId") String machineId);
/**
* 根据设备ID和状态查找冷却记录
*/
List<MachineCooldown> findByMachineIdAndStatus(@Param("machineId") String machineId,
@Param("status") String status);
/**
* 查找已过期但状态仍为ACTIVE的冷却记录
*/
List<MachineCooldown> findExpiredActiveCooldowns(@Param("currentTime") LocalDateTime currentTime,
@Param("limit") int limit);
/**
* 批量更新过期的冷却记录状态
*/
int batchUpdateExpiredCooldowns(@Param("currentTime") LocalDateTime currentTime);
/**
* 手动移除设备的冷却状态
*/
int removeMachineCooldown(@Param("machineId") String machineId);
/**
* 根据链接任务ID查找冷却记录
*/
List<MachineCooldown> findByLinkTaskId(@Param("linkTaskId") Long linkTaskId);
/**
* 统计活跃的冷却记录数量
*/
long countActiveCooldowns();
/**
* 统计指定时间范围内的冷却记录数量
*/
long countCooldownsByTimeRange(@Param("startTime") LocalDateTime startTime,
@Param("endTime") LocalDateTime endTime);
/**
* 清理指定时间之前的已过期冷却记录
*/
int cleanupExpiredCooldowns(@Param("beforeTime") LocalDateTime beforeTime);
/**
* 删除将要过期ACTIVE→EXPIRED的设备已存在的 EXPIRED 记录,避免唯一键 (machine_id,status) 冲突。
* 使用当前时间参数筛选出需要过期的设备列表。
*/
int deleteExistingExpiredForMachinesToExpire(@Param("currentTime") LocalDateTime currentTime);
/**
* 获取指定设备的冷却历史记录
*/
List<MachineCooldown> getCooldownHistory(@Param("machineId") String machineId,
@Param("limit") int limit,
@Param("offset") int offset);
}

View File

@@ -0,0 +1,34 @@
package com.gameplatform.server.model.dto.cooldown;
/**
* 冷却统计响应
*/
public class CooldownStatsResponse {
private boolean success;
private int totalDevices;
private int activeDevices;
private long totalRemainingMinutes;
public CooldownStatsResponse() {}
public CooldownStatsResponse(boolean success, int totalDevices, int activeDevices, long totalRemainingMinutes) {
this.success = success;
this.totalDevices = totalDevices;
this.activeDevices = activeDevices;
this.totalRemainingMinutes = totalRemainingMinutes;
}
public boolean isSuccess() { return success; }
public void setSuccess(boolean success) { this.success = success; }
public int getTotalDevices() { return totalDevices; }
public void setTotalDevices(int totalDevices) { this.totalDevices = totalDevices; }
public int getActiveDevices() { return activeDevices; }
public void setActiveDevices(int activeDevices) { this.activeDevices = activeDevices; }
public long getTotalRemainingMinutes() { return totalRemainingMinutes; }
public void setTotalRemainingMinutes(long totalRemainingMinutes) { this.totalRemainingMinutes = totalRemainingMinutes; }
}

View File

@@ -0,0 +1,64 @@
package com.gameplatform.server.model.dto.cooldown;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.time.LocalDateTime;
/**
* 内存冷却视图对象
*/
public class MachineCooldownView {
private String machineId;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime cooldownStartTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime cooldownEndTime;
private String reason;
private Long linkTaskId;
private long remainingMinutes;
private boolean active;
public MachineCooldownView() {}
public MachineCooldownView(String machineId,
LocalDateTime cooldownStartTime,
LocalDateTime cooldownEndTime,
String reason,
Long linkTaskId,
long remainingMinutes,
boolean active) {
this.machineId = machineId;
this.cooldownStartTime = cooldownStartTime;
this.cooldownEndTime = cooldownEndTime;
this.reason = reason;
this.linkTaskId = linkTaskId;
this.remainingMinutes = remainingMinutes;
this.active = active;
}
public String getMachineId() { return machineId; }
public void setMachineId(String machineId) { this.machineId = machineId; }
public LocalDateTime getCooldownStartTime() { return cooldownStartTime; }
public void setCooldownStartTime(LocalDateTime cooldownStartTime) { this.cooldownStartTime = cooldownStartTime; }
public LocalDateTime getCooldownEndTime() { return cooldownEndTime; }
public void setCooldownEndTime(LocalDateTime cooldownEndTime) { this.cooldownEndTime = cooldownEndTime; }
public String getReason() { return reason; }
public void setReason(String reason) { this.reason = reason; }
public Long getLinkTaskId() { return linkTaskId; }
public void setLinkTaskId(Long linkTaskId) { this.linkTaskId = linkTaskId; }
public long getRemainingMinutes() { return remainingMinutes; }
public void setRemainingMinutes(long remainingMinutes) { this.remainingMinutes = remainingMinutes; }
public boolean isActive() { return active; }
public void setActive(boolean active) { this.active = active; }
}

View File

@@ -1,185 +0,0 @@
package com.gameplatform.server.model.entity.cooldown;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.time.LocalDateTime;
/**
* 设备冷却状态实体
*/
@TableName("machine_cooldown")
public class MachineCooldown {
@TableId(type = IdType.AUTO)
private Long id;
/**
* 设备ID
*/
private String machineId;
/**
* 冷却开始时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime cooldownStartTime;
/**
* 冷却结束时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime cooldownEndTime;
/**
* 冷却原因
*/
private String reason;
/**
* 关联的链接任务ID
*/
private Long linkTaskId;
/**
* 冷却状态ACTIVE-活跃, EXPIRED-已过期, MANUALLY_REMOVED-手动移除
*/
private String status;
/**
* 创建时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createdAt;
/**
* 更新时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updatedAt;
// 构造函数
public MachineCooldown() {}
public MachineCooldown(String machineId, LocalDateTime cooldownStartTime,
LocalDateTime cooldownEndTime, String reason, Long linkTaskId) {
this.machineId = machineId;
this.cooldownStartTime = cooldownStartTime;
this.cooldownEndTime = cooldownEndTime;
this.reason = reason;
this.linkTaskId = linkTaskId;
this.status = "ACTIVE";
this.createdAt = LocalDateTime.now();
this.updatedAt = LocalDateTime.now();
}
// Getter and Setter methods
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getMachineId() {
return machineId;
}
public void setMachineId(String machineId) {
this.machineId = machineId;
}
public LocalDateTime getCooldownStartTime() {
return cooldownStartTime;
}
public void setCooldownStartTime(LocalDateTime cooldownStartTime) {
this.cooldownStartTime = cooldownStartTime;
}
public LocalDateTime getCooldownEndTime() {
return cooldownEndTime;
}
public void setCooldownEndTime(LocalDateTime cooldownEndTime) {
this.cooldownEndTime = cooldownEndTime;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
public Long getLinkTaskId() {
return linkTaskId;
}
public void setLinkTaskId(Long linkTaskId) {
this.linkTaskId = linkTaskId;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
public LocalDateTime getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(LocalDateTime updatedAt) {
this.updatedAt = updatedAt;
}
/**
* 检查冷却是否仍然有效
*/
public boolean isActive() {
return "ACTIVE".equals(status) &&
cooldownEndTime != null &&
LocalDateTime.now().isBefore(cooldownEndTime);
}
/**
* 获取剩余冷却时间(分钟)
*/
public long getRemainingMinutes() {
if (!isActive()) {
return 0;
}
return java.time.Duration.between(LocalDateTime.now(), cooldownEndTime).toMinutes();
}
@Override
public String toString() {
return "MachineCooldown{" +
"id=" + id +
", machineId='" + machineId + '\'' +
", cooldownStartTime=" + cooldownStartTime +
", cooldownEndTime=" + cooldownEndTime +
", reason='" + reason + '\'' +
", linkTaskId=" + linkTaskId +
", status='" + status + '\'' +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt +
'}';
}
}

View File

@@ -1,298 +0,0 @@
package com.gameplatform.server.service.cooldown;
import com.gameplatform.server.mapper.cooldown.MachineCooldownMapper;
import com.gameplatform.server.model.entity.cooldown.MachineCooldown;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* 机器冷却服务 - 支持数据库持久化
* 实现同一台机器在10分钟内不会重复调用的机制
*
* 优化点:
* 1. 数据库持久化,服务重启不丢失状态
* 2. 双重缓存机制,提高查询性能
* 3. 分布式锁支持,避免并发问题
*/
@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> machineCooldownCache = new ConcurrentHashMap<>();
private final MachineCooldownMapper machineCooldownMapper;
public MachineCooldownService(MachineCooldownMapper machineCooldownMapper) {
this.machineCooldownMapper = machineCooldownMapper;
// 启动时加载活跃的冷却记录到缓存
loadActiveCooldownsToCache();
}
/**
* 启动时加载活跃的冷却记录到缓存
*/
private void loadActiveCooldownsToCache() {
try {
List<MachineCooldown> activeCooldowns = machineCooldownMapper.findExpiredActiveCooldowns(
LocalDateTime.now().plusMinutes(COOLDOWN_MINUTES), 1000);
for (MachineCooldown cooldown : activeCooldowns) {
if (cooldown.isActive()) {
machineCooldownCache.put(cooldown.getMachineId(), cooldown.getCooldownStartTime());
}
}
log.info("加载了 {} 个活跃冷却记录到缓存", activeCooldowns.size());
} catch (Exception e) {
log.error("加载冷却记录到缓存失败", e);
}
}
/**
* 检查机器是否在冷却期内(优先从数据库查询确保准确性)
* @param machineId 机器ID
* @return true表示在冷却期内false表示可以操作
*/
public boolean isMachineInCooldown(String machineId) {
if (machineId == null || machineId.trim().isEmpty()) {
return false;
}
try {
// 首先查询数据库获取最准确的状态
MachineCooldown activeCooldown = machineCooldownMapper.findActiveCooldownByMachineId(machineId);
if (activeCooldown == null || !activeCooldown.isActive()) {
// 数据库中没有活跃冷却记录,清理缓存
machineCooldownCache.remove(machineId);
log.debug("机器{}没有活跃冷却记录,允许操作", machineId);
return false;
}
// 更新缓存
machineCooldownCache.put(machineId, activeCooldown.getCooldownStartTime());
long remainingMinutes = activeCooldown.getRemainingMinutes();
log.info("机器{}在冷却期内,剩余冷却时间:{}分钟,原因:{}",
machineId, remainingMinutes + 1, activeCooldown.getReason());
return true;
} catch (Exception e) {
log.error("检查机器{}冷却状态时发生异常,默认允许操作", machineId, e);
// 发生异常时,回退到缓存检查
return isMachineInCooldownFromCache(machineId);
}
}
/**
* 从缓存检查机器冷却状态(备用方法)
*/
private boolean isMachineInCooldownFromCache(String machineId) {
LocalDateTime lastOperationTime = machineCooldownCache.get(machineId);
if (lastOperationTime == null) {
return false;
}
LocalDateTime now = LocalDateTime.now();
LocalDateTime cooldownExpireTime = lastOperationTime.plusMinutes(COOLDOWN_MINUTES);
boolean inCooldown = now.isBefore(cooldownExpireTime);
if (!inCooldown) {
// 冷却已过期,清理缓存
machineCooldownCache.remove(machineId);
}
return inCooldown;
}
/**
* 将机器加入冷却队列(支持关联链接任务)
* @param machineId 机器ID
* @param reason 加入冷却的原因
*/
@Transactional
public void addMachineToCooldown(String machineId, String reason) {
try {
addMachineToCooldown(machineId, reason, null);
} catch (RuntimeException ex) {
// 当上层已完成占用(或并发占用导致唯一键冲突)时,吞掉异常以便上层逻辑继续
log.warn("添加冷却记录失败或已存在machineId={},原因={}(忽略)", machineId, ex.getMessage());
}
}
/**
* 将机器加入冷却队列
* @param machineId 机器ID
* @param reason 加入冷却的原因
* @param linkTaskId 关联的链接任务ID可选
*/
@Transactional
public void addMachineToCooldown(String machineId, String reason, Long linkTaskId) {
if (machineId == null || machineId.trim().isEmpty()) {
log.warn("尝试添加空的机器ID到冷却队列");
return;
}
LocalDateTime now = LocalDateTime.now();
LocalDateTime cooldownEndTime = now.plusMinutes(COOLDOWN_MINUTES);
try {
// 先移除该设备现有的活跃冷却记录(若存在非并发场景下的残留)
machineCooldownMapper.removeMachineCooldown(machineId);
// 创建新的冷却记录(依赖数据库唯一约束防止并发重复占用)
MachineCooldown cooldown = new MachineCooldown(
machineId, now, cooldownEndTime, reason, linkTaskId);
machineCooldownMapper.insert(cooldown);
// 更新缓存
machineCooldownCache.put(machineId, now);
log.info("机器{}已加入冷却队列,原因:{},冷却时间:{}分钟,冷却结束时间:{},关联任务:{}",
machineId, reason, COOLDOWN_MINUTES, cooldownEndTime, linkTaskId);
} catch (Exception e) {
log.error("将机器{}加入冷却队列失败:{}", machineId, e.getMessage(), e);
// 抛出异常,交由上层流程回滚/重试,避免同一设备被并发占用
throw new RuntimeException("设备正忙或分配冲突,请稍后重试");
}
}
/**
* 获取机器剩余冷却时间(分钟)
* @param machineId 机器ID
* @return 剩余冷却时间如果不在冷却期则返回0
*/
public long getRemainingCooldownMinutes(String machineId) {
if (machineId == null || machineId.trim().isEmpty()) {
return 0;
}
try {
MachineCooldown activeCooldown = machineCooldownMapper.findActiveCooldownByMachineId(machineId);
if (activeCooldown != null && activeCooldown.isActive()) {
return activeCooldown.getRemainingMinutes();
}
} catch (Exception e) {
log.warn("查询机器{}剩余冷却时间失败,回退到缓存查询", machineId, e);
// 回退到缓存查询
LocalDateTime lastOperationTime = machineCooldownCache.get(machineId);
if (lastOperationTime != null) {
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
*/
@Transactional
public void removeMachineFromCooldown(String machineId) {
if (machineId == null || machineId.trim().isEmpty()) {
return;
}
try {
int updated = machineCooldownMapper.removeMachineCooldown(machineId);
if (updated > 0) {
log.info("已手动移除机器{}的冷却状态", machineId);
} else {
log.debug("机器{}不在数据库冷却队列中", machineId);
}
} catch (Exception e) {
log.error("手动移除机器{}冷却状态失败", machineId, e);
}
// 清理缓存
LocalDateTime removedTime = machineCooldownCache.remove(machineId);
if (removedTime != null) {
log.debug("已从缓存中移除机器{}的冷却状态", machineId);
}
}
/**
* 清理过期的冷却记录(可定期调用以释放内存)
*/
@Transactional
public void cleanupExpiredCooldowns() {
LocalDateTime now = LocalDateTime.now();
try {
// 批量更新数据库中过期的冷却记录
int dbCleanedCount = machineCooldownMapper.batchUpdateExpiredCooldowns(now);
// 清理缓存中过期的记录
int cacheCleanedCount = 0;
var iterator = machineCooldownCache.entrySet().iterator();
while (iterator.hasNext()) {
var entry = iterator.next();
String machineId = entry.getKey();
LocalDateTime lastOperationTime = entry.getValue();
LocalDateTime cooldownExpireTime = lastOperationTime.plusMinutes(COOLDOWN_MINUTES);
if (now.isAfter(cooldownExpireTime)) {
iterator.remove();
cacheCleanedCount++;
}
}
if (dbCleanedCount > 0 || cacheCleanedCount > 0) {
log.info("清理过期冷却记录完成:数据库{}个,缓存{}个", dbCleanedCount, cacheCleanedCount);
}
} catch (Exception e) {
log.error("清理过期冷却记录失败", e);
}
}
/**
* 获取当前冷却队列的大小
* @return 冷却队列中的机器数量
*/
public int getCooldownQueueSize() {
try {
return (int) machineCooldownMapper.countActiveCooldowns();
} catch (Exception e) {
log.warn("查询活跃冷却记录数量失败,返回缓存大小", e);
return machineCooldownCache.size();
}
}
/**
* 获取机器的冷却历史记录
* @param machineId 机器ID
* @param limit 返回记录数量限制
* @return 冷却历史记录列表
*/
public List<MachineCooldown> getCooldownHistory(String machineId, int limit) {
if (machineId == null || machineId.trim().isEmpty()) {
return List.of();
}
try {
return machineCooldownMapper.getCooldownHistory(machineId, limit, 0);
} catch (Exception e) {
log.error("查询机器{}冷却历史失败", machineId, e);
return List.of();
}
}
}

View File

@@ -3,8 +3,13 @@ package com.gameplatform.server.service.cooldown;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import com.gameplatform.server.model.dto.cooldown.MachineCooldownView;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
@@ -226,6 +231,41 @@ public class MemoryMachineCooldownService {
return cooldownMap.size(); return cooldownMap.size();
} }
/**
* 列出内存中的所有冷却记录
* @param activeOnly 是否仅返回仍在冷却中的记录
*/
public List<MachineCooldownView> listAllCooldowns(boolean activeOnly) {
// 主动清理一次,避免返回过期项
cleanupExpiredCooldowns();
List<MachineCooldownView> result = new ArrayList<>();
for (Map.Entry<String, CooldownInfo> entry : cooldownMap.entrySet()) {
String machineId = entry.getKey();
CooldownInfo info = entry.getValue();
boolean active = info.isActive();
if (activeOnly && !active) {
continue;
}
long remaining = active ? info.getRemainingMinutes() : 0L;
result.add(new MachineCooldownView(
machineId,
info.getCooldownStartTime(),
info.getCooldownEndTime(),
info.getReason(),
info.getLinkTaskId(),
remaining,
active
));
}
// 便于排查:按结束时间升序排列
result.sort(Comparator.comparing(MachineCooldownView::getCooldownEndTime,
Comparator.nullsLast(Comparator.naturalOrder())));
return result;
}
/** /**
* 清理过期的冷却记录 * 清理过期的冷却记录
*/ */

View File

@@ -88,7 +88,7 @@ script:
# 服务器配置 # 服务器配置
app: app:
base-url: "https://2.uzi0.cc" # 生产环境需要配置为实际域名 base-url: "https://uzi1.cn" # 生产环境需要配置为实际域名
# base-url: "http://localhost:18080" # 本地测试环境 # base-url: "http://localhost:18080" # 本地测试环境
link: link:

View File

@@ -0,0 +1,9 @@
-- 清理未使用的设备冷却表
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
DROP TABLE IF EXISTS `machine_cooldown`;
SET FOREIGN_KEY_CHECKS = 1;

View File

@@ -1,118 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gameplatform.server.mapper.cooldown.MachineCooldownMapper">
<!-- 结果映射 -->
<resultMap id="MachineCooldownMap" type="com.gameplatform.server.model.entity.cooldown.MachineCooldown">
<id property="id" column="id" />
<result property="machineId" column="machine_id" />
<result property="cooldownStartTime" column="cooldown_start_time" />
<result property="cooldownEndTime" column="cooldown_end_time" />
<result property="reason" column="reason" />
<result property="linkTaskId" column="link_task_id" />
<result property="status" column="status" />
<result property="createdAt" column="created_at" />
<result property="updatedAt" column="updated_at" />
</resultMap>
<!-- 根据设备ID查找活跃的冷却记录 -->
<select id="findActiveCooldownByMachineId" parameterType="string" resultMap="MachineCooldownMap">
SELECT * FROM machine_cooldown
WHERE machine_id = #{machineId}
AND status = 'ACTIVE'
AND cooldown_end_time > NOW()
ORDER BY created_at DESC
LIMIT 1
</select>
<!-- 根据设备ID和状态查找冷却记录 -->
<select id="findByMachineIdAndStatus" resultMap="MachineCooldownMap">
SELECT * FROM machine_cooldown
WHERE machine_id = #{machineId}
AND status = #{status}
ORDER BY created_at DESC
</select>
<!-- 查找已过期但状态仍为ACTIVE的冷却记录 -->
<select id="findExpiredActiveCooldowns" resultMap="MachineCooldownMap">
SELECT * FROM machine_cooldown
WHERE status = 'ACTIVE'
AND cooldown_end_time &lt;= #{currentTime}
ORDER BY cooldown_end_time ASC
LIMIT #{limit}
</select>
<!-- 批量更新过期的冷却记录状态 -->
<update id="batchUpdateExpiredCooldowns">
UPDATE machine_cooldown mc
LEFT JOIN (
SELECT machine_id FROM (
SELECT DISTINCT machine_id
FROM machine_cooldown
WHERE status = 'EXPIRED'
) ex0
) ex ON ex.machine_id = mc.machine_id
SET mc.status = 'EXPIRED', mc.updated_at = NOW()
WHERE mc.status = 'ACTIVE'
AND mc.cooldown_end_time &lt;= #{currentTime}
AND ex.machine_id IS NULL
</update>
<!-- 手动移除设备的冷却状态 -->
<update id="removeMachineCooldown">
UPDATE machine_cooldown
SET status = 'MANUALLY_REMOVED', updated_at = NOW()
WHERE machine_id = #{machineId}
AND status = 'ACTIVE'
</update>
<!-- 根据链接任务ID查找冷却记录 -->
<select id="findByLinkTaskId" parameterType="long" resultMap="MachineCooldownMap">
SELECT * FROM machine_cooldown
WHERE link_task_id = #{linkTaskId}
ORDER BY created_at DESC
</select>
<!-- 统计活跃的冷却记录数量 -->
<select id="countActiveCooldowns" resultType="long">
SELECT COUNT(*) FROM machine_cooldown
WHERE status = 'ACTIVE'
AND cooldown_end_time > NOW()
</select>
<!-- 统计指定时间范围内的冷却记录数量 -->
<select id="countCooldownsByTimeRange" resultType="long">
SELECT COUNT(*) FROM machine_cooldown
WHERE created_at BETWEEN #{startTime} AND #{endTime}
</select>
<!-- 清理指定时间之前的已过期冷却记录 -->
<delete id="cleanupExpiredCooldowns">
DELETE FROM machine_cooldown
WHERE status = 'EXPIRED'
AND updated_at &lt; #{beforeTime}
</delete>
<!-- 在批量将 ACTIVE 更新为 EXPIRED 前,
先删除这些设备已存在的 EXPIRED 记录,避免 (machine_id,status) 唯一键冲突 -->
<delete id="deleteExistingExpiredForMachinesToExpire">
DELETE mc FROM machine_cooldown mc
JOIN (
SELECT DISTINCT machine_id
FROM machine_cooldown
WHERE status = 'ACTIVE'
AND cooldown_end_time &lt;= #{currentTime}
) t ON t.machine_id = mc.machine_id
WHERE mc.status = 'EXPIRED'
</delete>
<!-- 获取指定设备的冷却历史记录 -->
<select id="getCooldownHistory" resultMap="MachineCooldownMap">
SELECT * FROM machine_cooldown
WHERE machine_id = #{machineId}
ORDER BY created_at DESC
LIMIT #{limit} OFFSET #{offset}
</select>
</mapper>