feat: 优化设备冷却管理,增加原子设备占用逻辑和过期记录处理
This commit is contained in:
@@ -303,3 +303,4 @@ WHERE created_at < DATE_SUB(NOW(), INTERVAL 30 DAY);
|
||||
4. 说明当前系统状态
|
||||
|
||||
这样可以更快速地定位和解决问题。
|
||||
|
||||
|
||||
@@ -96,3 +96,4 @@ CREATE TABLE `system_monitor` (
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统监控表' ROW_FORMAT = DYNAMIC;
|
||||
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.gameplatform.server;
|
||||
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
@@ -9,8 +11,13 @@ import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
@MapperScan("com.gameplatform.server.mapper")
|
||||
@EnableScheduling
|
||||
public class GamePlatformServerApplication {
|
||||
private static final Logger log = LoggerFactory.getLogger(GamePlatformServerApplication.class);
|
||||
|
||||
public static void main(String[] args) {
|
||||
log.info("=== 游戏平台服务器启动中 ===");
|
||||
log.debug("Debug 日志级别已启用");
|
||||
SpringApplication.run(GamePlatformServerApplication.class, args);
|
||||
log.info("=== 游戏平台服务器启动完成 ===");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ public class AuthController {
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
public Mono<LoginResponse> login(@Valid @RequestBody LoginRequest req) {
|
||||
// Avoid logging raw usernames at info level
|
||||
log.debug("/api/auth/login called");
|
||||
log.info("/api/auth/login called");
|
||||
return authService.login(req);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@ import com.gameplatform.server.model.dto.link.UserLinkStatusResponse;
|
||||
import com.gameplatform.server.model.dto.link.TargetScoreResponse;
|
||||
import com.gameplatform.server.service.link.LinkGenerationService;
|
||||
import com.gameplatform.server.service.link.LinkListService;
|
||||
import com.gameplatform.server.service.link.LinkStatusService;
|
||||
import com.gameplatform.server.service.external.ScriptClient;
|
||||
import com.gameplatform.server.service.link.LinkStatusService;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
@@ -115,4 +115,14 @@ public interface LinkTaskMapper extends BaseMapper<LinkTask> {
|
||||
* 根据状态查询所有链接任务
|
||||
*/
|
||||
List<LinkTask> findByStatus(@Param("status") String status);
|
||||
|
||||
/**
|
||||
* 原子方式占用设备:仅当该设备当前未被 USING/LOGGED_IN 占用时,
|
||||
* 才将指定任务更新为 USING 并写入设备与时间字段。
|
||||
* 返回受影响行数(1=成功,0=设备已被占用)。
|
||||
*/
|
||||
int reserveDeviceIfFree(@Param("id") Long id,
|
||||
@Param("region") String region,
|
||||
@Param("deviceId") String deviceId,
|
||||
@Param("qrExpireSeconds") int qrExpireSeconds);
|
||||
}
|
||||
|
||||
@@ -61,6 +61,12 @@ public interface MachineCooldownMapper extends BaseMapper<MachineCooldown> {
|
||||
* 清理指定时间之前的已过期冷却记录
|
||||
*/
|
||||
int cleanupExpiredCooldowns(@Param("beforeTime") LocalDateTime beforeTime);
|
||||
|
||||
/**
|
||||
* 删除将要过期(ACTIVE→EXPIRED)的设备,已存在的 EXPIRED 记录,避免唯一键 (machine_id,status) 冲突。
|
||||
* 使用当前时间参数筛选出需要过期的设备列表。
|
||||
*/
|
||||
int deleteExistingExpiredForMachinesToExpire(@Param("currentTime") LocalDateTime currentTime);
|
||||
|
||||
/**
|
||||
* 获取指定设备的冷却历史记录
|
||||
@@ -69,3 +75,4 @@ public interface MachineCooldownMapper extends BaseMapper<MachineCooldown> {
|
||||
@Param("limit") int limit,
|
||||
@Param("offset") int offset);
|
||||
}
|
||||
|
||||
|
||||
@@ -182,3 +182,4 @@ public class MachineCooldown {
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -197,3 +197,4 @@ public class GameCompletionLog {
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ package com.gameplatform.server.service.detection;
|
||||
import com.gameplatform.server.mapper.agent.LinkTaskMapper;
|
||||
import com.gameplatform.server.model.entity.agent.LinkTask;
|
||||
import com.gameplatform.server.model.entity.detection.GameCompletionLog;
|
||||
import com.gameplatform.server.service.cooldown.MachineCooldownService;
|
||||
import com.gameplatform.server.service.cooldown.MemoryMachineCooldownService;
|
||||
import com.gameplatform.server.mapper.detection.GameCompletionLogMapper;
|
||||
import com.gameplatform.server.mapper.history.LinkTaskStatusHistoryMapper;
|
||||
import com.gameplatform.server.model.entity.history.LinkTaskStatusHistory;
|
||||
@@ -37,7 +37,7 @@ public class GameCompletionDetectionService {
|
||||
private static final int COMPLETION_CONFIRMATION_INTERVAL_SECONDS = 10;
|
||||
|
||||
private final LinkTaskMapper linkTaskMapper;
|
||||
private final MachineCooldownService machineCooldownService;
|
||||
private final MemoryMachineCooldownService machineCooldownService;
|
||||
private final GameCompletionLogMapper gameCompletionLogMapper;
|
||||
private final LinkTaskStatusHistoryMapper statusHistoryMapper;
|
||||
|
||||
@@ -48,7 +48,7 @@ public class GameCompletionDetectionService {
|
||||
private final ConcurrentMap<String, LocalDateTime> recentLogins = new ConcurrentHashMap<>();
|
||||
|
||||
public GameCompletionDetectionService(LinkTaskMapper linkTaskMapper,
|
||||
MachineCooldownService machineCooldownService,
|
||||
MemoryMachineCooldownService machineCooldownService,
|
||||
GameCompletionLogMapper gameCompletionLogMapper,
|
||||
LinkTaskStatusHistoryMapper statusHistoryMapper) {
|
||||
this.linkTaskMapper = linkTaskMapper;
|
||||
@@ -297,3 +297,4 @@ public class GameCompletionDetectionService {
|
||||
LOW // 低置信度:不可信的状态变化
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
package com.gameplatform.server.task;
|
||||
|
||||
import com.gameplatform.server.service.cooldown.MachineCooldownService;
|
||||
import com.gameplatform.server.service.cooldown.MemoryMachineCooldownService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
@@ -14,9 +14,9 @@ import org.springframework.stereotype.Component;
|
||||
public class MachineCooldownCleanupTask {
|
||||
private static final Logger log = LoggerFactory.getLogger(MachineCooldownCleanupTask.class);
|
||||
|
||||
private final MachineCooldownService machineCooldownService;
|
||||
private final MemoryMachineCooldownService machineCooldownService;
|
||||
|
||||
public MachineCooldownCleanupTask(MachineCooldownService machineCooldownService) {
|
||||
public MachineCooldownCleanupTask(MemoryMachineCooldownService machineCooldownService) {
|
||||
this.machineCooldownService = machineCooldownService;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ mybatis-plus:
|
||||
type-aliases-package: com.gameplatform.server.model.entity
|
||||
configuration:
|
||||
map-underscore-to-camel-case: true
|
||||
# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 注释掉以关闭SQL日志
|
||||
global-config:
|
||||
db-config:
|
||||
id-type: auto
|
||||
@@ -37,10 +37,28 @@ management:
|
||||
logging:
|
||||
level:
|
||||
root: info
|
||||
com.gameplatform.server: info
|
||||
com.baomidou.mybatisplus: info
|
||||
org.apache.ibatis: info
|
||||
com.zaxxer.hikari: info
|
||||
com.gameplatform.server: debug # 保持整体调试
|
||||
# 仅保留设备解析最终汇总(INFO),其余降级
|
||||
com.gameplatform.server.service.device.DeviceStatusService: info
|
||||
com.gameplatform.server.service.device.DeviceStatusCheckService: info
|
||||
# 脚本客户端与定时任务降噪
|
||||
com.gameplatform.server.service.external.ScriptClient: warn
|
||||
com.gameplatform.server.task.DeviceStatusCheckTask: warn
|
||||
com.gameplatform.server.task.UsingLinkCheckTask: warn
|
||||
# 完成检测服务降噪(屏蔽debug“置信度低”之类日志)
|
||||
com.gameplatform.server.service.detection.GameCompletionDetectionService: warn
|
||||
# 设备任务更新服务:只保留警告/错误(不输出“开始处理设备/点数已更新为”等调试信息)
|
||||
com.gameplatform.server.service.link.DeviceTaskUpdateService: warn
|
||||
# Mapper 与 SQL 调用降噪(屏蔽 MyBatis 的参数/SQL DEBUG)
|
||||
com.gameplatform.server.mapper: warn
|
||||
com.baomidou.mybatisplus: warn
|
||||
org.apache.ibatis: warn
|
||||
org.mybatis: warn
|
||||
com.zaxxer.hikari: warn
|
||||
pattern:
|
||||
console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
|
||||
console:
|
||||
enabled: true
|
||||
|
||||
security:
|
||||
jwt:
|
||||
|
||||
@@ -1,7 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration scan="true">
|
||||
<!-- Include Spring Boot defaults -->
|
||||
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
|
||||
|
||||
<!-- Explicit console appender with UTF-8 to avoid garbled output on Windows -->
|
||||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<property name="LOG_PATH" value="logs"/>
|
||||
|
||||
<!-- Optional: include file appender if you also want general file logs -->
|
||||
<!-- <include resource="org/springframework/boot/logging/logback/file-appender.xml"/> -->
|
||||
|
||||
<!-- Audit appender for status transitions -->
|
||||
<appender name="AUDIT-FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${LOG_PATH}/audit-status.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
@@ -23,6 +38,26 @@
|
||||
<appender-ref ref="AUDIT-FILE"/>
|
||||
</logger>
|
||||
|
||||
<!-- Let Spring Boot default console/file config handle others -->
|
||||
</configuration>
|
||||
<!-- General rolling file appender to mirror console output -->
|
||||
<appender name="GENERAL-FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${LOG_PATH}/server.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>${LOG_PATH}/server.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
|
||||
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
|
||||
<maxFileSize>20MB</maxFileSize>
|
||||
</timeBasedFileNamingAndTriggeringPolicy>
|
||||
<maxHistory>30</maxHistory>
|
||||
<totalSizeCap>2GB</totalSizeCap>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- Root logger: send to console and general file -->
|
||||
<root level="INFO">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
<appender-ref ref="GENERAL-FILE"/>
|
||||
</root>
|
||||
</configuration>
|
||||
|
||||
@@ -255,4 +255,25 @@
|
||||
WHERE status = #{status}
|
||||
ORDER BY created_at ASC
|
||||
</select>
|
||||
|
||||
<!-- 原子占用设备,避免并发下同一设备被多个链接占用 -->
|
||||
<update id="reserveDeviceIfFree">
|
||||
UPDATE link_task lt
|
||||
LEFT JOIN (
|
||||
SELECT 1 as has_conflict
|
||||
FROM link_task x
|
||||
WHERE x.machine_id = #{deviceId}
|
||||
AND x.status IN ('USING','LOGGED_IN')
|
||||
AND x.id <> #{id}
|
||||
) conflict_check ON 1=1
|
||||
SET lt.status = 'USING',
|
||||
lt.region = #{region},
|
||||
lt.machine_id = #{deviceId},
|
||||
lt.qr_created_at = NOW(),
|
||||
lt.qr_expire_at = DATE_ADD(NOW(), INTERVAL #{qrExpireSeconds} SECOND),
|
||||
lt.updated_at = NOW()
|
||||
WHERE lt.id = #{id}
|
||||
AND conflict_check.has_conflict IS NULL
|
||||
</update>
|
||||
|
||||
</mapper>
|
||||
|
||||
@@ -44,10 +44,18 @@
|
||||
|
||||
<!-- 批量更新过期的冷却记录状态 -->
|
||||
<update id="batchUpdateExpiredCooldowns">
|
||||
UPDATE machine_cooldown
|
||||
SET status = 'EXPIRED', updated_at = NOW()
|
||||
WHERE status = 'ACTIVE'
|
||||
AND cooldown_end_time <= #{currentTime}
|
||||
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 <= #{currentTime}
|
||||
AND ex.machine_id IS NULL
|
||||
</update>
|
||||
|
||||
<!-- 手动移除设备的冷却状态 -->
|
||||
@@ -84,6 +92,19 @@
|
||||
WHERE status = 'EXPIRED'
|
||||
AND updated_at < #{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 <= #{currentTime}
|
||||
) t ON t.machine_id = mc.machine_id
|
||||
WHERE mc.status = 'EXPIRED'
|
||||
</delete>
|
||||
|
||||
<!-- 获取指定设备的冷却历史记录 -->
|
||||
<select id="getCooldownHistory" resultMap="MachineCooldownMap">
|
||||
@@ -94,3 +115,4 @@
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user