From 96207c03b9be2eb7aac75ed4c6a11325f808245f Mon Sep 17 00:00:00 2001 From: wells <123456@qq.com> Date: Wed, 5 Aug 2026 20:38:23 +0800 Subject: [PATCH] =?UTF-8?q?fix=20=E9=87=8D=E5=90=AF=E6=81=A2=E5=A4=8D?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/tailbet/job/GameJob.java | 248 +++++++++++------- .../com/tailbet/model/PendingTaskInfo.java | 17 ++ .../java/com/tailbet/model/RoundTask.java | 16 ++ src/main/resources/sql/init.sql | 15 ++ 4 files changed, 205 insertions(+), 91 deletions(-) create mode 100644 src/main/java/com/tailbet/model/PendingTaskInfo.java create mode 100644 src/main/java/com/tailbet/model/RoundTask.java diff --git a/src/main/java/com/tailbet/job/GameJob.java b/src/main/java/com/tailbet/job/GameJob.java index 00494ed..4ae7c2d 100644 --- a/src/main/java/com/tailbet/job/GameJob.java +++ b/src/main/java/com/tailbet/job/GameJob.java @@ -1,11 +1,13 @@ package com.tailbet.job; +import com.tailbet.model.RoundTask; import com.tailbet.model.entity.GameRound; import com.tailbet.model.entity.RedPacket; import com.tailbet.model.entity.User; import com.tailbet.mapper.DrawRecordMapper; import com.tailbet.mapper.DigitWhiteMapper; import com.tailbet.mapper.GameRoundMapper; +import com.tailbet.mapper.PendingTaskMapper; import com.tailbet.mapper.RedPacketMapper; import com.tailbet.mapper.RedPacketRecvMapper; import com.tailbet.mapper.SysConfigMapper; @@ -14,6 +16,8 @@ import com.tailbet.model.entity.DigitWhite; import com.tailbet.model.entity.DrawRecord; import com.tailbet.model.entity.RedPacketRecv; import com.tailbet.model.entity.SysConfig; +import com.tailbet.model.entity.PendingTask; +import com.tailbet.model.PendingTaskInfo; import com.tailbet.openim.OpenIMClient; import com.tailbet.service.IGameService; import com.tailbet.service.IRedPacketService; @@ -46,6 +50,7 @@ public class GameJob { private final SysConfigMapper sysConfigMapper; private final UserMapper userMapper; private final DigitWhiteMapper digitWhiteMapper; + private final PendingTaskMapper pendingTaskMapper; private final IGameService gameService; private final IRedPacketService redPacketService; private final IFakeUserService fakeUserService; @@ -58,24 +63,106 @@ public class GameJob { private final Map configCache = new ConcurrentHashMap<>(); // 延迟任务队列 - private final Map pendingTasks = new ConcurrentHashMap<>(); + private final Map pendingTasks = new ConcurrentHashMap<>(); - // 任务ID生成器 + // 任务ID生成器 (格式: {type}_{groupId}_{自增序号}) private java.util.concurrent.atomic.AtomicLong taskIdGenerator = new java.util.concurrent.atomic.AtomicLong(1); /** - * 启动时加载配置缓存 + * 启动时加载配置缓存并恢复内存状态 */ @jakarta.annotation.PostConstruct public void loadConfig() { try { + // 加载配置缓存 List configs = sysConfigMapper.selectList(null); for (SysConfig config : configs) { configCache.put(config.getCfgKey(), config.getCfgValue()); } log.info("配置缓存加载完成,共 {} 条配置", configs.size()); + + // 恢复进行中的局状态 + recoverRoundTasks(); + + // 恢复延迟任务队列 + recoverPendingTasks(); + } catch (Exception e) { - log.error("配置缓存加载失败: {}", e.getMessage()); + log.error("启动初始化失败: {}", e.getMessage(), e); + } + } + + /** + * 从数据库恢复进行中的局状态 + */ + private void recoverRoundTasks() { + try { + List ongoingRounds = gameRoundMapper.selectList( + new LambdaQueryWrapper() + .eq(GameRound::getStatus, 0)); + + if (ongoingRounds.isEmpty()) { + log.info("无进行中的局,无需恢复"); + return; + } + + int bettingInterval = getConfigInt("betting_interval", 60); + + for (GameRound round : ongoingRounds) { + RoundTask task = new RoundTask(); + task.setRoundId(round.getId()); + task.setGroupId(round.getGroupId()); + + // 优先使用数据库中的endBetTime,否则根据startTime计算 + if (round.getEndBetTime() != null) { + task.setEndBetTime(round.getEndBetTime()); + } else if (round.getStartTime() != null) { + task.setEndBetTime(round.getStartTime().plusSeconds(bettingInterval)); + } else { + // 兜底:使用配置的下注时长 + task.setEndBetTime(LocalDateTime.now().plusSeconds(bettingInterval)); + } + + roundTasks.put(round.getId(), task); + } + + log.info("局状态恢复完成,共恢复 {} 个进行中的局", ongoingRounds.size()); + } catch (Exception e) { + log.error("恢复局状态失败: {}", e.getMessage(), e); + } + } + + /** + * 从数据库恢复延迟任务队列 + */ + private void recoverPendingTasks() { + try { + List dbTasks = pendingTaskMapper.selectList( + new LambdaQueryWrapper() + .gt(PendingTask::getExecuteTime, LocalDateTime.now())); + + if (dbTasks == null || dbTasks.isEmpty()) { + log.info("无待执行的延迟任务,无需恢复"); + return; + } + + for (PendingTask dbTask : dbTasks) { + if (dbTask.getExecuteTime() == null || !dbTask.getExecuteTime().isAfter(LocalDateTime.now())) { + continue; + } + PendingTaskInfo info = new PendingTaskInfo(); + info.setTaskId(dbTask.getTaskId()); + info.setType(dbTask.getType()); + info.setRoundId(dbTask.getRoundId()); + info.setGroupId(dbTask.getGroupId()); + info.setDigit(dbTask.getDigit()); + info.setExecuteTime(dbTask.getExecuteTime()); + pendingTasks.put(info.getTaskId(), info); + } + + log.info("延迟任务恢复完成,共恢复 {} 个待执行任务", pendingTasks.size()); + } catch (Exception e) { + log.error("恢复延迟任务失败: {}", e.getMessage(), e); } } @@ -186,7 +273,7 @@ public class GameJob { for (GameRound round : ongoingRounds) { // 检查是否超过接注时间 RoundTask task = roundTasks.get(round.getId()); - if (task != null && task.endBetTime.isBefore(LocalDateTime.now())) { + if (task != null && task.getEndBetTime().isBefore(LocalDateTime.now())) { // 自动结束接注 try { gameService.endBet(round.getGroupId(), 0L); // 0L表示系统自动触发 @@ -212,14 +299,17 @@ public class GameJob { * 安排开始发红包 */ public String scheduleStartRp(Long roundId, Long groupId, int delaySeconds) { - String taskId = "start_rp_" + taskIdGenerator.getAndIncrement(); - PendingTask task = new PendingTask(); + String taskId = "start_rp_" + groupId + "_" + taskIdGenerator.getAndIncrement(); + PendingTaskInfo task = new PendingTaskInfo(); task.setTaskId(taskId); task.setType("start_rp"); task.setRoundId(roundId); task.setGroupId(groupId); - task.setExecuteTime(LocalDateTime.now().plusSeconds(delaySeconds)); + LocalDateTime executeTime = LocalDateTime.now().plusSeconds(delaySeconds); + task.setExecuteTime(executeTime); pendingTasks.put(taskId, task); + // 持久化到数据库 + saveTaskToDb(taskId, "start_rp", roundId, groupId, null, executeTime); log.info("安排{}秒后开始发红包: roundId={}", delaySeconds, roundId); return taskId; } @@ -228,15 +318,18 @@ public class GameJob { * 安排自动结算 */ public String scheduleSettle(Long roundId, Long groupId, int digit, int delaySeconds) { - String taskId = "settle_" + taskIdGenerator.getAndIncrement(); - PendingTask task = new PendingTask(); + String taskId = "settle_" + groupId + "_" + taskIdGenerator.getAndIncrement(); + PendingTaskInfo task = new PendingTaskInfo(); task.setTaskId(taskId); task.setType("settle"); task.setRoundId(roundId); task.setGroupId(groupId); task.setDigit(digit); - task.setExecuteTime(LocalDateTime.now().plusSeconds(delaySeconds)); + LocalDateTime executeTime = LocalDateTime.now().plusSeconds(delaySeconds); + task.setExecuteTime(executeTime); pendingTasks.put(taskId, task); + // 持久化到数据库 + saveTaskToDb(taskId, "settle", roundId, groupId, digit, executeTime); log.info("安排{}秒后结算: taskId={}, roundId={}, digit={}", delaySeconds, taskId, roundId, digit); return taskId; } @@ -245,13 +338,16 @@ public class GameJob { * 安排自动下一期 */ public String scheduleAutoNextTask(Long groupId, int delaySeconds) { - String taskId = "auto_next_" + taskIdGenerator.getAndIncrement(); - PendingTask task = new PendingTask(); + String taskId = "auto_next_" + groupId + "_" + taskIdGenerator.getAndIncrement(); + PendingTaskInfo task = new PendingTaskInfo(); task.setTaskId(taskId); task.setType("auto_next"); task.setGroupId(groupId); - task.setExecuteTime(LocalDateTime.now().plusSeconds(delaySeconds)); + LocalDateTime executeTime = LocalDateTime.now().plusSeconds(delaySeconds); + task.setExecuteTime(executeTime); pendingTasks.put(taskId, task); + // 持久化到数据库 + saveTaskToDb(taskId, "auto_next", null, groupId, null, executeTime); log.info("安排{}秒后自动开始下一期: groupId={}", delaySeconds, groupId); return taskId; } @@ -260,26 +356,60 @@ public class GameJob { * 安排机器人发包 */ public String scheduleBotSendRp(Long roundId, Long groupId, int delaySeconds) { - String taskId = "bot_send_rp_" + taskIdGenerator.getAndIncrement(); - PendingTask task = new PendingTask(); + String taskId = "bot_send_rp_" + groupId + "_" + taskIdGenerator.getAndIncrement(); + PendingTaskInfo task = new PendingTaskInfo(); task.setTaskId(taskId); task.setType("bot_send_rp"); task.setRoundId(roundId); task.setGroupId(groupId); - task.setExecuteTime(LocalDateTime.now().plusSeconds(delaySeconds)); + LocalDateTime executeTime = LocalDateTime.now().plusSeconds(delaySeconds); + task.setExecuteTime(executeTime); pendingTasks.put(taskId, task); + // 持久化到数据库 + saveTaskToDb(taskId, "bot_send_rp", roundId, groupId, null, executeTime); log.info("安排{}秒后机器人发包: roundId={}", delaySeconds, roundId); return taskId; } + /** + * 保存任务到数据库 + */ + private void saveTaskToDb(String taskId, String type, Long roundId, Long groupId, Integer digit, LocalDateTime executeTime) { + try { + PendingTask dbTask = new PendingTask(); + dbTask.setTaskId(taskId); + dbTask.setType(type); + dbTask.setRoundId(roundId); + dbTask.setGroupId(groupId); + dbTask.setDigit(digit); + dbTask.setExecuteTime(executeTime); + dbTask.setCreateTime(LocalDateTime.now()); + pendingTaskMapper.insert(dbTask); + } catch (Exception e) { + log.error("保存任务到数据库失败: taskId={}, error={}", taskId, e.getMessage()); + } + } + + /** + * 从数据库删除任务 + */ + private void deleteTaskFromDb(String taskId) { + try { + pendingTaskMapper.delete(new LambdaQueryWrapper() + .eq(PendingTask::getTaskId, taskId)); + } catch (Exception e) { + log.error("从数据库删除任务失败: taskId={}, error={}", taskId, e.getMessage()); + } + } + /** * 开始游戏后安排定时器 */ public void onGameStart(Long roundId, Long groupId, int intervalSeconds) { RoundTask task = new RoundTask(); - task.roundId = roundId; - task.groupId = groupId; - task.endBetTime = LocalDateTime.now().plusSeconds(intervalSeconds); + task.setRoundId(roundId); + task.setGroupId(groupId); + task.setEndBetTime(LocalDateTime.now().plusSeconds(intervalSeconds)); roundTasks.put(roundId, task); // 广播开始下注 @@ -475,75 +605,6 @@ public class GameJob { return sb.toString().trim(); } - /** - * 局任务信息 - */ - private static class RoundTask { - Long roundId; - Long groupId; - LocalDateTime endBetTime; - LocalDateTime startRpTime; - } - - /** - * 延迟任务信息 - */ - private static class PendingTask { - private String taskId; - private String type; - private Long roundId; - private Long groupId; - private Integer digit; - private LocalDateTime executeTime; - - public String getTaskId() { - return taskId; - } - - public void setTaskId(String taskId) { - this.taskId = taskId; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public Long getRoundId() { - return roundId; - } - - public void setRoundId(Long roundId) { - this.roundId = roundId; - } - - public Long getGroupId() { - return groupId; - } - - public void setGroupId(Long groupId) { - this.groupId = groupId; - } - - public Integer getDigit() { - return digit; - } - - public void setDigit(Integer digit) { - this.digit = digit; - } - - public LocalDateTime getExecuteTime() { - return executeTime; - } - - public void setExecuteTime(LocalDateTime executeTime) { - this.executeTime = executeTime; - } - } /** * 处理延迟任务队列(每秒执行) @@ -552,9 +613,14 @@ public class GameJob { public void processPendingTasks() { LocalDateTime now = LocalDateTime.now(); pendingTasks.entrySet().removeIf(entry -> { - PendingTask task = entry.getValue(); + PendingTaskInfo task = entry.getValue(); + if (task.getExecuteTime() == null) { + deleteTaskFromDb(task.getTaskId()); + return true; + } if (task.getExecuteTime().isBefore(now) || task.getExecuteTime().isEqual(now)) { executeTask(task); + deleteTaskFromDb(task.getTaskId()); return true; } return false; @@ -564,7 +630,7 @@ public class GameJob { /** * 执行延迟任务 */ - private void executeTask(PendingTask task) { + private void executeTask(PendingTaskInfo task) { try { switch (task.getType()) { case "settle": diff --git a/src/main/java/com/tailbet/model/PendingTaskInfo.java b/src/main/java/com/tailbet/model/PendingTaskInfo.java new file mode 100644 index 0000000..729b677 --- /dev/null +++ b/src/main/java/com/tailbet/model/PendingTaskInfo.java @@ -0,0 +1,17 @@ +package com.tailbet.model; + +import lombok.Data; +import java.time.LocalDateTime; + +/** + * 延迟任务信息(内存缓存用) + */ +@Data +public class PendingTaskInfo { + private String taskId; + private String type; + private Long roundId; + private Long groupId; + private Integer digit; + private LocalDateTime executeTime; +} diff --git a/src/main/java/com/tailbet/model/RoundTask.java b/src/main/java/com/tailbet/model/RoundTask.java new file mode 100644 index 0000000..d764969 --- /dev/null +++ b/src/main/java/com/tailbet/model/RoundTask.java @@ -0,0 +1,16 @@ +package com.tailbet.model; + +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * 局任务信息 + */ +@Data +public class RoundTask { + private Long roundId; + private Long groupId; + private LocalDateTime endBetTime; + private LocalDateTime startRpTime; +} diff --git a/src/main/resources/sql/init.sql b/src/main/resources/sql/init.sql index ee00c1b..2a1729b 100644 --- a/src/main/resources/sql/init.sql +++ b/src/main/resources/sql/init.sql @@ -229,8 +229,23 @@ ALTER TABLE red_packet ADD COLUMN share_taken_count INT DEFAULT 0 COMMENT '已 -- game_round 表增加第一个红包ID字段 ALTER TABLE game_round ADD COLUMN first_rp_id BIGINT COMMENT '第一个红包ID'; + + + -- 新增配置项 INSERT INTO sys_config (cfg_key, cfg_value, remark) VALUES ('bet_window_seconds', '60', '接注窗口时间(秒)'), ('fake_recv_seconds', '5', '托领取超时时间(秒)'); +CREATE TABLE `pending_task` ( + `id` BIGINT AUTO_INCREMENT PRIMARY KEY, + `task_id` VARCHAR(64) NOT NULL COMMENT '任务ID', + `type` VARCHAR(32) NOT NULL COMMENT 'start_rp/settle/auto_next/bot_send_rp', + `round_id` BIGINT COMMENT '局ID', + `group_id` BIGINT NOT NULL COMMENT '群ID', + `digit` INT COMMENT '开奖尾数(settle任务用)', + `execute_time` DATETIME NOT NULL COMMENT '执行时间', + `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + INDEX `idx_execute_time` (`execute_time`), + UNIQUE INDEX `idx_task_id` (`task_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;