diff --git a/src/main/java/com/tailbet/job/GameJob.java b/src/main/java/com/tailbet/job/GameJob.java index 435c011..f76c67d 100644 --- a/src/main/java/com/tailbet/job/GameJob.java +++ b/src/main/java/com/tailbet/job/GameJob.java @@ -1,6 +1,5 @@ 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; @@ -8,7 +7,6 @@ import com.tailbet.mapper.DrawRecordMapper; import com.tailbet.mapper.DigitWhiteMapper; import com.tailbet.mapper.GameRoundMapper; import com.tailbet.mapper.GroupMemberMapper; -import com.tailbet.mapper.PendingTaskMapper; import com.tailbet.mapper.RedPacketMapper; import com.tailbet.mapper.RedPacketRecvMapper; import com.tailbet.mapper.SysConfigMapper; @@ -18,8 +16,6 @@ import com.tailbet.model.entity.DrawRecord; import com.tailbet.model.entity.GroupMember; 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.OpenIMApiClient; import com.tailbet.service.IGameService; import com.tailbet.service.IRedPacketService; @@ -40,6 +36,17 @@ import java.util.stream.Collectors; /** * 游戏定时任务 + * + *

状态完全由 {@code game_round} 数据库表承载,不再使用内存 Map。 + * 定时任务只做"数据库扫描 + 状态机推进",天然支持单实例重启恢复。 + * + *

扫描任务(每 2 秒): + *

*/ @Slf4j @Component @@ -53,7 +60,6 @@ public class GameJob { private final SysConfigMapper sysConfigMapper; private final UserMapper userMapper; private final DigitWhiteMapper digitWhiteMapper; - private final PendingTaskMapper pendingTaskMapper; private final GroupMemberMapper groupMemberMapper; private final IGameService gameService; private final IRedPacketService redPacketService; @@ -61,127 +67,26 @@ public class GameJob { private final OpenIMApiClient openIMApiClient; private final com.tailbet.config.OpenIMProperties openIMProperties; - // 局状态监控(使用内存缓存,实际生产应使用Redis) - private final Map roundTasks = new ConcurrentHashMap<>(); - // 配置缓存 private final Map configCache = new ConcurrentHashMap<>(); - // 延迟任务队列 - private final Map pendingTasks = new ConcurrentHashMap<>(); - - // 任务ID生成器 (格式: {type}_{groupId}_{自增序号}) - private java.util.concurrent.atomic.AtomicLong taskIdGenerator = new java.util.concurrent.atomic.AtomicLong(1); - /** - * 启动时加载配置缓存并恢复内存状态 + * 启动时加载配置缓存 + *

不再做 roundTasks/pendingTasks 内存恢复 —— 扫描任务启动后会自动从数据库接管 */ @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(), e); } } - /** - * 从数据库恢复进行中的局状态 - */ - private void recoverRoundTasks() { - try { - List ongoingRounds = gameRoundMapper.selectList( - new LambdaQueryWrapper() - .lt(GameRound::getStatus, IGameService.STATUS_FINISHED)); - - 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); - } - } - - /** - * 从数据库恢复延迟任务队列 - * 注:包括已过期的任务,过期任务会在processPendingTasks中立即执行 - */ - private void recoverPendingTasks() { - try { - // 数据库中残留的记录都是未执行的(执行成功后会立即删除),全部恢复 - List dbTasks = pendingTaskMapper.selectList(null); - - if (dbTasks == null || dbTasks.isEmpty()) { - log.info("无待执行的延迟任务,无需恢复"); - return; - } - - LocalDateTime now = LocalDateTime.now(); - int recovered = 0; - int expiredCount = 0; - for (PendingTask dbTask : dbTasks) { - if (dbTask.getExecuteTime() == null) { - log.warn("跳过executeTime为空的延迟任务: taskId={}", dbTask.getTaskId()); - 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); - recovered++; - if (!dbTask.getExecuteTime().isAfter(now)) { - expiredCount++; - log.warn("恢复已过期任务,启动后立即执行: taskId={}, type={}, groupId={}, executeTime={}", - dbTask.getTaskId(), dbTask.getType(), dbTask.getGroupId(), dbTask.getExecuteTime()); - } - } - - log.info("延迟任务恢复完成,共恢复 {} 个待执行任务,其中 {} 个已过期将立即执行", - recovered, expiredCount); - } catch (Exception e) { - log.error("恢复延迟任务失败: {}", e.getMessage(), e); - } - } - /** * 获取配置值 */ @@ -308,26 +213,102 @@ public class GameJob { } /** - * 检查局超时(每10秒执行) + * 扫描下注超时局 —— 每 2 秒 + *

条件:{@code status=0 (下注中) AND end_bet_time < NOW()} */ - @Scheduled(fixedDelay = 10000) - public void checkRoundTimeout() { - List ongoingRounds = gameRoundMapper.selectList(new LambdaQueryWrapper() - .eq(GameRound::getStatus, IGameService.STATUS_BETTING)); + @Scheduled(fixedDelay = 2000) + public void scanRoundTimeouts() { + List overdue = gameRoundMapper.selectList( + new LambdaQueryWrapper() + .eq(GameRound::getStatus, IGameService.STATUS_BETTING) + .lt(GameRound::getEndBetTime, LocalDateTime.now()) + .last("LIMIT 50")); - for (GameRound round : ongoingRounds) { - // 检查是否超过接注时间 - RoundTask task = roundTasks.get(round.getId()); - if (task != null && task.getEndBetTime().isBefore(LocalDateTime.now())) { - // 自动结束接注 - try { - gameService.endBet(round.getGroupId(), 0L); // 0L表示系统自动触发 - // 触发开始发红包,延迟时间从配置读取 - int sendRpDelay = getConfigInt("send_rp_delay", 20); - scheduleStartRp(round.getId(), round.getGroupId(), sendRpDelay); - } catch (Exception e) { - log.error("自动结束接注失败: roundId={}, error={}", round.getId(), e.getMessage()); + for (GameRound round : overdue) { + try { + gameService.endBet(round.getGroupId(), 0L); // 0L 表示系统自动触发 + } catch (Exception e) { + log.error("自动结束接注失败: roundId={}, error={}", round.getId(), e.getMessage(), e); + } + } + } + + /** + * 扫描"封盘后到点开始发红包" —— 每 2 秒 + *

条件:{@code status=1 (已封盘) AND start_rp_time IS NULL AND end_bet_time + send_rp_delay 秒 < NOW()} + */ + @Scheduled(fixedDelay = 2000) + public void scanStartRpTimeouts() { + int sendRpDelay = getConfigInt("send_rp_delay", 20); + LocalDateTime threshold = LocalDateTime.now().minusSeconds(sendRpDelay); + List due = gameRoundMapper.selectList( + new LambdaQueryWrapper() + .eq(GameRound::getStatus, IGameService.STATUS_BET_CLOSED) + .isNull(GameRound::getStartRpTime) + .lt(GameRound::getEndBetTime, threshold) + .last("LIMIT 50")); + + for (GameRound round : due) { + try { + onStartRp(round.getId(), round.getGroupId()); + } catch (Exception e) { + log.error("延迟开始发红包失败: roundId={}, error={}", round.getId(), e.getMessage(), e); + } + } + } + + /** + * 扫描"发包超时由机器人代发" —— 每 2 秒 + *

条件:{@code status=2 (发红包中) AND first_rp_id IS NULL AND start_rp_time + auto_rp_timeout 秒 < NOW()} + */ + @Scheduled(fixedDelay = 2000) + public void scanBotSendRpTimeouts() { + int autoRpTimeout = getConfigInt("auto_rp_timeout", 10); + LocalDateTime threshold = LocalDateTime.now().minusSeconds(autoRpTimeout); + List due = gameRoundMapper.selectList( + new LambdaQueryWrapper() + .eq(GameRound::getStatus, IGameService.STATUS_RP_SENDING) + .isNull(GameRound::getFirstRpId) + .isNotNull(GameRound::getStartRpTime) + .lt(GameRound::getStartRpTime, threshold) + .last("LIMIT 50")); + + for (GameRound round : due) { + try { + botSendRp(round.getId(), round.getGroupId()); + } catch (Exception e) { + log.error("机器人自动发包扫描执行失败: roundId={}, error={}", round.getId(), e.getMessage(), e); + } + } + } + + /** + * 扫描"结算后自动开下一期" —— 每 2 秒 + *

条件:{@code status=3 (已结算) AND auto_next=1 AND finish_time + 5 秒 < NOW()} + * 且本群当前无进行中局 + */ + @Scheduled(fixedDelay = 2000) + public void scanAutoNextTimeouts() { + int autoNextDelay = getConfigInt("auto_next_delay", 5); + LocalDateTime threshold = LocalDateTime.now().minusSeconds(autoNextDelay); + List finished = gameRoundMapper.selectList( + new LambdaQueryWrapper() + .eq(GameRound::getStatus, IGameService.STATUS_FINISHED) + .eq(GameRound::getAutoNext, 1) + .isNotNull(GameRound::getFinishTime) + .lt(GameRound::getFinishTime, threshold) + .last("LIMIT 50")); + + for (GameRound round : finished) { + try { + // 仅当该群当前无进行中局时才开下一期 + GameRound ongoing = gameService.getOngoingRound(round.getGroupId()); + if (ongoing != null) { + continue; } + onAutoNext(round.getGroupId()); + } catch (Exception e) { + log.error("自动开下一期失败: groupId={}, error={}", round.getGroupId(), e.getMessage(), e); } } } @@ -339,224 +320,33 @@ public class GameJob { public void resetDailyFakeUsers() { fakeUserService.resetDailyActiveFakes(); } - /** - * 处理延迟任务队列(每秒执行) - */ - @Scheduled(fixedDelay = 1000) - public void processPendingTasks() { - LocalDateTime now = LocalDateTime.now(); - for (Map.Entry entry : pendingTasks.entrySet()) { - PendingTaskInfo task = entry.getValue(); - if (task.getExecuteTime() == null) { - pendingTasks.remove(entry.getKey()); - deleteTaskFromDb(task.getTaskId()); - continue; - } - if (task.getExecuteTime().isBefore(now) || task.getExecuteTime().isEqual(now)) { - boolean success = executeTask(task); - if (success) { - pendingTasks.remove(entry.getKey()); - deleteTaskFromDb(task.getTaskId()); - } else { - // 失败时保留内存与数据库中的任务,5秒后再重试 - task.setExecuteTime(LocalDateTime.now().plusSeconds(5)); - updateTaskExecuteTimeInDb(task); - log.warn("任务执行失败,保留任务待重试: taskId={}", task.getTaskId()); - } - } - } - } /** - * 更新延迟任务的执行时间(失败重试场景) - */ - private void updateTaskExecuteTimeInDb(PendingTaskInfo task) { - try { - PendingTask dbTask = pendingTaskMapper.selectOne( - new LambdaQueryWrapper().eq(PendingTask::getTaskId, task.getTaskId())); - if (dbTask != null) { - dbTask.setExecuteTime(task.getExecuteTime()); - pendingTaskMapper.updateById(dbTask); - } - } catch (Exception e) { - log.error("更新延迟任务执行时间失败: taskId={}, error={}", task.getTaskId(), e.getMessage()); - } - } - - /** - * 执行延迟任务 - * - * @return true 表示执行成功(可清理);false 表示执行失败(需保留以重试) - */ - private boolean executeTask(PendingTaskInfo task) { - try { - switch (task.getType()) { - case "settle": - return onSettle(task.getRoundId(), task.getGroupId(), task.getDigit()); - case "auto_next": - return onAutoNext(task.getGroupId()); - case "start_rp": - return onStartRp(task.getRoundId(), task.getGroupId()); - case "bot_send_rp": - return botSendRp(task.getRoundId(), task.getGroupId()); - default: - log.warn("未知任务类型: {}", task.getType()); - return false; - } - } catch (Exception e) { - log.error("执行任务失败: taskId={}, error={}", task.getTaskId(), e.getMessage()); - return false; - } - } - /** - * 安排开始发红包 - */ - public String scheduleStartRp(Long roundId, Long groupId, int delaySeconds) { - String taskId = "start_rp_" + groupId + "_" + taskIdGenerator.getAndIncrement(); - PendingTaskInfo task = new PendingTaskInfo(); - task.setTaskId(taskId); - task.setType("start_rp"); - task.setRoundId(roundId); - task.setGroupId(groupId); - 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; - } - - /** - * 安排自动结算 - */ - public String scheduleSettle(Long roundId, Long groupId, int digit, int delaySeconds) { - 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); - 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; - } - - /** - * 安排自动下一期 - */ - public String scheduleAutoNextTask(Long groupId, int delaySeconds) { - String taskId = "auto_next_" + groupId + "_" + taskIdGenerator.getAndIncrement(); - PendingTaskInfo task = new PendingTaskInfo(); - task.setTaskId(taskId); - task.setType("auto_next"); - task.setGroupId(groupId); - 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; - } - - /** - * 安排机器人发包 - */ - public String scheduleBotSendRp(Long roundId, Long groupId, int delaySeconds) { - 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); - 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.setRoundId(roundId); - task.setGroupId(groupId); - task.setEndBetTime(LocalDateTime.now().plusSeconds(intervalSeconds)); - roundTasks.put(roundId, task); - - // 广播开始下注 GameRound round = gameRoundMapper.selectById(roundId); if (round != null) { broadcastStartBet(groupId, round.getRoundNo()); } - log.info("游戏开始: roundId={}, {}秒后结束接注", roundId, intervalSeconds); } /** - * 结束接注后安排发红包 + * 结束接注后广播 + 通知白名单 + *

延迟开始发红包由 {@link #scanStartRpTimeouts()} 接管 */ public void onEndBet(Long roundId, Long groupId) { - // 移除结束接注定时器 - roundTasks.remove(roundId); - - // 广播结束下注 GameRound round = gameRoundMapper.selectById(roundId); if (round != null) { broadcastEndBet(groupId, round.getRoundNo()); } - - // 通知白名单用户尾数试算完成 notifyWhiteUsers(roundId); - - // 安排延迟后开始发红包 - int sendRpDelay = getConfigInt("send_rp_delay", 20); - scheduleStartRp(roundId, groupId, sendRpDelay); } /** * 开始发红包 + *

机器人超时发包由 {@link #scanBotSendRpTimeouts()} 接管 */ public boolean onStartRp(Long roundId, Long groupId) { try { @@ -565,6 +355,13 @@ public class GameJob { return true; } + // 再次校验:避免重复推进状态机 + if (round.getStatus() == null || round.getStatus() != IGameService.STATUS_BET_CLOSED) { + log.debug("本局{}当前状态非封盘中(status={}),跳过发红包开始", + roundId, round.getStatus()); + return true; + } + // 更新局状态为开始发红包 round.setStatus(IGameService.STATUS_RP_SENDING); round.setStartRpTime(LocalDateTime.now()); @@ -572,13 +369,9 @@ public class GameJob { // 广播开始发红包 broadcastStartRp(groupId, round.getRoundNo()); - - // 安排超时后如果没人发包则机器人自动发 - int autoRpTimeout = getConfigInt("auto_rp_timeout", 10); - scheduleBotSendRp(roundId, groupId, autoRpTimeout); return true; } catch (Exception e) { - log.error("开始发红包失败: roundId={}, error={}", roundId, e.getMessage()); + log.error("开始发红包失败: roundId={}, error={}", roundId, e.getMessage(), e); return false; } } @@ -649,6 +442,7 @@ public class GameJob { /** * 开奖结算 + *

自动开下一期由 {@link #scanAutoNextTimeouts()} 接管 */ public boolean onSettle(Long roundId, Long groupId, int digit) { try { @@ -660,20 +454,10 @@ public class GameJob { broadcastResult(groupId, digit, round.getRoundNo()); } - // 检查是否自动开启下一期 - GameRound roundAfterSettle = gameRoundMapper.selectById(roundId); - if (roundAfterSettle != null && roundAfterSettle.getAutoNext() == 1) { - // 5秒后自动开始下一期 - scheduleAutoNextTask(groupId, 5); - } - - // 清理任务 - roundTasks.remove(roundId); - log.info("局结算完成: roundId={}, digit={}", roundId, digit); return true; } catch (Exception e) { - log.error("结算失败: roundId={}, error={}", roundId, e.getMessage()); + log.error("结算失败: roundId={}, error={}", roundId, e.getMessage(), e); return false; } } @@ -787,9 +571,6 @@ public class GameJob { return sb.toString().trim(); } - - - /** * 自动开始下一期 */ @@ -800,12 +581,12 @@ public class GameJob { log.warn("自动开始下一期失败:startGame未返回局信息, groupId={}", groupId); return false; } - // 复用与手动流程一致的逻辑:注册roundTasks监控 + 广播开始下注 + // 复用与手动流程一致的逻辑:广播开始下注 int betWindowSeconds = getConfigInt("bet_window_seconds", 60); onGameStart(round.getId(), groupId, betWindowSeconds); return true; } catch (Exception e) { - log.error("自动开始下一期失败: groupId={}, error={}", groupId, e.getMessage()); + log.error("自动开始下一期失败: groupId={}, error={}", groupId, e.getMessage(), e); return false; } } diff --git a/src/main/java/com/tailbet/mapper/PendingTaskMapper.java b/src/main/java/com/tailbet/mapper/PendingTaskMapper.java deleted file mode 100644 index 49c32ce..0000000 --- a/src/main/java/com/tailbet/mapper/PendingTaskMapper.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.tailbet.mapper; - -import com.tailbet.model.entity.PendingTask; -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import org.apache.ibatis.annotations.Mapper; - -/** - * 延迟任务Mapper - */ -@Mapper -public interface PendingTaskMapper extends BaseMapper { -} diff --git a/src/main/java/com/tailbet/model/PendingTaskInfo.java b/src/main/java/com/tailbet/model/PendingTaskInfo.java deleted file mode 100644 index 729b677..0000000 --- a/src/main/java/com/tailbet/model/PendingTaskInfo.java +++ /dev/null @@ -1,17 +0,0 @@ -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 deleted file mode 100644 index d764969..0000000 --- a/src/main/java/com/tailbet/model/RoundTask.java +++ /dev/null @@ -1,16 +0,0 @@ -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/java/com/tailbet/model/entity/PendingTask.java b/src/main/java/com/tailbet/model/entity/PendingTask.java deleted file mode 100644 index 3db8ad3..0000000 --- a/src/main/java/com/tailbet/model/entity/PendingTask.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.tailbet.model.entity; - -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.Data; -import java.time.LocalDateTime; - -/** - * 延迟任务表 - */ -@Data -@TableName("pending_task") -public class PendingTask { - - @TableId(type = IdType.AUTO) - private Long id; - - /** - * 任务ID - */ - private String taskId; - - /** - * 任务类型: start_rp/settle/auto_next/bot_send_rp - */ - private String type; - - /** - * 局ID - */ - private Long roundId; - - /** - * 群ID - */ - private Long groupId; - - /** - * 开奖尾数(settle任务用) - */ - private Integer digit; - - /** - * 执行时间 - */ - private LocalDateTime executeTime; - - /** - * 创建时间 - */ - private LocalDateTime createTime; -} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index c9f7287..90347e3 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -29,8 +29,8 @@ mybatis-plus: type-aliases-package: com.tailbet.entity configuration: map-underscore-to-camel-case: true - log-impl: org.apache.ibatis.logging.stdout.StdOutImpl -# log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl +# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl global-config: db-config: @@ -64,9 +64,9 @@ openim: jwt: - secret: ${JWT_SECRET:4d19a5c2984044848df9bed721512f9f} + secret: ${JWT_SECRET:ecc6de473d6f4e0fbdaf5ea899fd2e26} expiration: 604800000 logging: level: - com.tailbet: debug + com.tailbet: info diff --git a/src/main/resources/sql/init.sql b/src/main/resources/sql/init.sql index d243e70..c94d893 100644 --- a/src/main/resources/sql/init.sql +++ b/src/main/resources/sql/init.sql @@ -89,7 +89,10 @@ CREATE TABLE IF NOT EXISTS game_round ( update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_group_round (group_id, round_no), INDEX idx_status (status), - INDEX idx_group_id (group_id) + INDEX idx_group_id (group_id), + INDEX idx_status_endbet (status, end_bet_time), + INDEX idx_status_startrp (status, start_rp_time), + INDEX idx_status_finishtime (status, finish_time) ) COMMENT '游戏局表'; -- 注单表 @@ -238,18 +241,21 @@ 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; +-- ====== 去内存 Map 重构后,pending_task 表已不再被应用写入,保留 DDL 仅作历史兼容参考 ====== +-- 实际生产如需清理,可执行: +-- DROP TABLE IF EXISTS `pending_task`; +-- 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; -- ====== 积分字段类型迁移:BIGINT → DECIMAL(20,2),与红包金额统一精度 ====== -- 历史BIGINT都是整数,DECIMAL(20,2)可无损容纳,无需特殊转换