init
This commit is contained in:
@@ -0,0 +1,334 @@
|
||||
package com.tailbet.job;
|
||||
|
||||
import com.tailbet.model.entity.GameRound;
|
||||
import com.tailbet.model.entity.RedPacket;
|
||||
import com.tailbet.model.entity.User;
|
||||
import com.tailbet.mapper.GameRoundMapper;
|
||||
import com.tailbet.mapper.RedPacketMapper;
|
||||
import com.tailbet.mapper.UserMapper;
|
||||
import com.tailbet.mapper.DigitWhiteMapper;
|
||||
import com.tailbet.model.entity.DigitWhite;
|
||||
import com.tailbet.service.GameService;
|
||||
import com.tailbet.service.RedPacketService;
|
||||
import com.tailbet.service.FakeUserService;
|
||||
import com.tailbet.openim.OpenIMClient;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 游戏定时任务
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class GameJob {
|
||||
|
||||
private final GameRoundMapper gameRoundMapper;
|
||||
private final RedPacketMapper redPacketMapper;
|
||||
private final UserMapper userMapper;
|
||||
private final DigitWhiteMapper digitWhiteMapper;
|
||||
private final GameService gameService;
|
||||
private final RedPacketService redPacketService;
|
||||
private final FakeUserService fakeUserService;
|
||||
private final OpenIMClient openIMClient;
|
||||
|
||||
// 局状态监控(使用内存缓存,实际生产应使用Redis)
|
||||
private final Map<Long, RoundTask> roundTasks = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 检查红包是否需要托领取(每3秒执行)
|
||||
*/
|
||||
@Scheduled(fixedDelay = 3000)
|
||||
public void checkRedPacketForFakeReceive() {
|
||||
// 查找进行中的游戏红包
|
||||
List<RedPacket> ongoingRps = redPacketMapper.selectList(new LambdaQueryWrapper<RedPacket>()
|
||||
.eq(RedPacket::getStatus, 0)
|
||||
.eq(RedPacket::getType, RedPacketService.TYPE_GAME)
|
||||
.isNotNull(RedPacket::getRoundId));
|
||||
|
||||
for (RedPacket rp : ongoingRps) {
|
||||
LocalDateTime createTime = rp.getCreateTime();
|
||||
// 发出超过5秒未领完,让托领取
|
||||
if (createTime.plusSeconds(5).isBefore(LocalDateTime.now())) {
|
||||
// 查找未领取的用户,随机选一个托领取
|
||||
try {
|
||||
fakeUserReceiveRemaining(rp);
|
||||
} catch (Exception e) {
|
||||
log.error("托领取红包失败: rpId={}, error={}", rp.getId(), e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 假人托领取剩余份额
|
||||
*/
|
||||
private void fakeUserReceiveRemaining(RedPacket rp) {
|
||||
List<User> fakes = fakeUserService.getFakeUsers();
|
||||
if (fakes.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 随机选一个假人
|
||||
User fake = fakes.get((int) (Math.random() * fakes.size()));
|
||||
|
||||
// 检查是否已经领取过
|
||||
var existing = redPacketMapper.selectList(new LambdaQueryWrapper<RedPacket>()
|
||||
.eq(RedPacket::getUserId, fake.getId()));
|
||||
if (!existing.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
redPacketService.receiveRp(rp.getId(), fake.getId());
|
||||
log.info("假人{}领取红包{}剩余份额", fake.getId(), rp.getId());
|
||||
} catch (Exception e) {
|
||||
// 忽略领取失败
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查局超时(每10秒执行)
|
||||
*/
|
||||
@Scheduled(fixedDelay = 10000)
|
||||
public void checkRoundTimeout() {
|
||||
List<GameRound> ongoingRounds = gameRoundMapper.selectList(new LambdaQueryWrapper<GameRound>()
|
||||
.eq(GameRound::getStatus, 0));
|
||||
|
||||
for (GameRound round : ongoingRounds) {
|
||||
// 检查是否超过接注时间(默认60秒)
|
||||
RoundTask task = roundTasks.get(round.getId());
|
||||
if (task != null && task.endBetTime.isBefore(LocalDateTime.now())) {
|
||||
// 自动结束接注
|
||||
try {
|
||||
gameService.endBet(round.getGroupId(), 0L); // 0L表示系统自动触发
|
||||
// 触发开始发红包
|
||||
scheduleStartRp(round.getId(), round.getGroupId(), 20); // 20秒后开始发红包
|
||||
} catch (Exception e) {
|
||||
log.error("自动结束接注失败: roundId={}, error={}", round.getId(), e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 每日凌晨重置活跃假人池
|
||||
*/
|
||||
@Scheduled(cron = "0 0 0 * * ?")
|
||||
public void resetDailyFakeUsers() {
|
||||
fakeUserService.resetDailyActiveFakes();
|
||||
}
|
||||
|
||||
/**
|
||||
* 安排开始发红包
|
||||
*/
|
||||
public void scheduleStartRp(Long roundId, Long groupId, int delaySeconds) {
|
||||
RoundTask task = new RoundTask();
|
||||
task.roundId = roundId;
|
||||
task.groupId = groupId;
|
||||
task.startRpTime = LocalDateTime.now().plusSeconds(delaySeconds);
|
||||
roundTasks.put(roundId, task);
|
||||
}
|
||||
|
||||
/**
|
||||
* 安排自动结算
|
||||
*/
|
||||
public void scheduleSettle(Long roundId, Long groupId, int digit, int delaySeconds) {
|
||||
// TODO: 使用延迟队列或定时任务实现
|
||||
log.info("安排{}秒后结算 roundId={}, digit={}", delaySeconds, roundId, digit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始游戏后安排定时器
|
||||
*/
|
||||
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);
|
||||
roundTasks.put(roundId, task);
|
||||
|
||||
// 广播开始下注
|
||||
GameRound round = gameRoundMapper.selectById(roundId);
|
||||
if (round != null) {
|
||||
broadcastStartBet(groupId, round.getRoundNo());
|
||||
}
|
||||
|
||||
log.info("游戏开始: roundId={}, {}秒后结束接注", roundId, intervalSeconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束接注后安排发红包
|
||||
*/
|
||||
public void onEndBet(Long roundId, Long groupId) {
|
||||
// 移除结束接注定时器
|
||||
roundTasks.remove(roundId);
|
||||
|
||||
// 广播结束下注
|
||||
GameRound round = gameRoundMapper.selectById(roundId);
|
||||
if (round != null) {
|
||||
broadcastEndBet(groupId, round.getRoundNo());
|
||||
}
|
||||
|
||||
// 通知白名单用户尾数试算完成
|
||||
notifyWhiteUsers(roundId);
|
||||
|
||||
// 安排20秒后开始发红包
|
||||
scheduleStartRp(roundId, groupId, 20);
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始发红包
|
||||
*/
|
||||
public void onStartRp(Long roundId, Long groupId) {
|
||||
GameRound round = gameRoundMapper.selectById(roundId);
|
||||
if (round == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 广播开始发红包
|
||||
broadcastStartRp(groupId, round.getRoundNo());
|
||||
|
||||
// 安排10秒后如果没人发包则机器人自动发
|
||||
// TODO: 实现延迟检查逻辑
|
||||
}
|
||||
|
||||
/**
|
||||
* 机器人自动发包
|
||||
*/
|
||||
public void botSendRp(Long roundId, Long groupId) {
|
||||
GameRound round = gameRoundMapper.selectById(roundId);
|
||||
if (round == null || round.getTargetDigit() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 查找机器人账号
|
||||
User bot = userMapper.selectOne(new LambdaQueryWrapper<User>()
|
||||
.eq(User::getIsBot, 1)
|
||||
.last("LIMIT 1"));
|
||||
|
||||
if (bot == null) {
|
||||
log.error("未找到机器人账号");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 机器人发1元3个包
|
||||
RedPacket rp = redPacketService.sendRp(bot.getId(), groupId,
|
||||
new BigDecimal("1"), 3, roundId);
|
||||
|
||||
// 设置目标尾数
|
||||
// TODO: 实现红包尾数控制
|
||||
|
||||
log.info("机器人自动发包: roundId={}, rpId={}", roundId, rp.getId());
|
||||
} catch (Exception e) {
|
||||
log.error("机器人发包失败: roundId={}, error={}", roundId, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 开奖结算
|
||||
*/
|
||||
public void onSettle(Long roundId, Long groupId, int digit) {
|
||||
try {
|
||||
gameService.settle(roundId, digit);
|
||||
|
||||
// 广播开奖结果
|
||||
GameRound round = gameRoundMapper.selectById(roundId);
|
||||
if (round != null) {
|
||||
broadcastResult(groupId, digit, round.getRoundNo());
|
||||
}
|
||||
|
||||
// 检查是否自动开启下一期
|
||||
GameRound roundAfterSettle = gameRoundMapper.selectById(roundId);
|
||||
if (roundAfterSettle != null && roundAfterSettle.getAutoNext() == 1) {
|
||||
// 5秒后自动开始下一期
|
||||
scheduleAutoNext(groupId, 5);
|
||||
}
|
||||
|
||||
// 清理任务
|
||||
roundTasks.remove(roundId);
|
||||
|
||||
log.info("局结算完成: roundId={}, digit={}", roundId, digit);
|
||||
} catch (Exception e) {
|
||||
log.error("结算失败: roundId={}, error={}", roundId, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知白名单用户(尾数试算完成)
|
||||
*/
|
||||
private void notifyWhiteUsers(Long roundId) {
|
||||
List<DigitWhite> whites = digitWhiteMapper.selectList(new LambdaQueryWrapper<DigitWhite>());
|
||||
for (DigitWhite white : whites) {
|
||||
User user = userMapper.selectById(white.getUserId());
|
||||
if (user != null) {
|
||||
// TODO: 发送推送通知
|
||||
log.info("通知白名单用户尾数试算完成: userId={}", user.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 安排自动开始下一期
|
||||
*/
|
||||
private void scheduleAutoNext(Long groupId, int delaySeconds) {
|
||||
// TODO: 实现延迟自动开局
|
||||
log.info("安排{}秒后自动开始下一期", delaySeconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 广播开始下注
|
||||
*/
|
||||
private void broadcastStartBet(Long groupId, String roundNo) {
|
||||
// TODO: 获取群对应的OpenIM群ID并发送
|
||||
openIMClient.sendGroupMessage(String.valueOf(groupId),
|
||||
roundNo + "期开始下注");
|
||||
}
|
||||
|
||||
/**
|
||||
* 广播结束下注
|
||||
*/
|
||||
private void broadcastEndBet(Long groupId, String roundNo) {
|
||||
openIMClient.sendGroupMessage(String.valueOf(groupId),
|
||||
roundNo + "期结束下注");
|
||||
}
|
||||
|
||||
/**
|
||||
* 广播开始发红包
|
||||
*/
|
||||
private void broadcastStartRp(Long groupId, String roundNo) {
|
||||
openIMClient.sendGroupMessage(String.valueOf(groupId),
|
||||
roundNo + "期开始发红包");
|
||||
}
|
||||
|
||||
/**
|
||||
* 广播开奖结果
|
||||
*/
|
||||
private void broadcastResult(Long groupId, int digit, String roundNo) {
|
||||
String playWinDesc = gameService.getPlayWinDesc(digit);
|
||||
String message = roundNo + "期开奖\n" +
|
||||
"尾数: " + digit + "\n" +
|
||||
"中奖玩法: " + playWinDesc;
|
||||
openIMClient.sendGroupMessage(String.valueOf(groupId), message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 局任务信息
|
||||
*/
|
||||
private static class RoundTask {
|
||||
Long roundId;
|
||||
Long groupId;
|
||||
LocalDateTime endBetTime;
|
||||
LocalDateTime startRpTime;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user