@Transactional(rollbackFor = Exception.class)
public Long publishGame(Long gameId) {
GmGame game = getById(gameId);
//只有未发布状态的游戏才可以发布
gameCommonService.checkGamePublishStatus(game);
Integer subjectCount = gameSubjectMapper.selectCount(
new QueryWrapper().lambda().eq(GmGameSubject::getDeleted, NOT_DELETED)
.eq(GmGameSubject::getGameId, gameId));
Validate.isTrue(subjectCount > INT_0, GmcenterExceptionKeys.APIS_GAME_PUBLISH_SUBJECT_NOT_EMPTY);
//判断游戏下面是否有题目以及成员,如果没有题目和成员,也不可以发布
Integer userCount = gameUserMapper.selectCount(
new QueryWrapper().lambda().eq(GmGameUser::getDeleted, NOT_DELETED)
.eq(GmGameUser::getGameId, gameId));
Validate.isTrue(userCount > INT_0, GmcenterExceptionKeys.APIS_GAME_PUBLISH_USER_NOT_EMPTY);
getAndSetStatus(game);
gameCommonService.setUpdateField(game);
updateById(game);
setRedisClock(game);
//发布完成之后,redis设置发布的次数,默认为1,发布+1,撤销-1
changePublishCount(game, GameOperateEnum.PUBLISH);
return game.getId();
}
/**
* 设置redis定时开始/结束游戏
*
* @param game 游戏
*/
private void setRedisClock(GmGame game) {
Long gameId = game.getId();
int gameStatus = game.getGameStatus();
LocalDateTime endTime = game.getEndTime();
LocalDateTime startTime = game.getStartTime();
LocalDateTime now = LocalDateTime.now();
long startSecond = LocalDateTimeUtil.between(now, startTime, ChronoUnit.SECONDS);
long endSecond = LocalDateTimeUtil.between(now, endTime, ChronoUnit.SECONDS);
RBlockingDeque blockingDeque = redissonClient.getBlockingDeque(GAME_AUTO_ACTION_DELAY_QUEUE);
//获取到延迟队列
RDelayedQueue delayedQueue = redissonClient.getDelayedQueue(blockingDeque);
if (UN_START.getStatus().equals(gameStatus)) {
// case1:未开始状态->设置开始时间redis->判断endTime是否设置->设置结束redis
delayedQueue
.offer(StringUtils.joinWith("_", ACTION_START, gameId, startTime.format(PURE_DATETIME_FORMATTER)),
Math.abs(startSecond), TimeUnit.SECONDS);
setEndTime(game, endTime, startTime, startSecond, endSecond, delayedQueue);
} else if (STARTED.getStatus().equals(gameStatus)) {
// case2:开始状态->那就判断结束时间是否设置了->如果设置了就设置redis,没设置,就什么都不用设置了
setEndTime(game, endTime, startTime, startSecond, endSecond, delayedQueue);
}
delayedQueue.destroy();
// case3:已结束状态->nothing(发布即结束)
}
/**
* 设置结束时间
*
* @param game 游戏
* @param endTime 结束时间
* @param startTime 开始时间
* @param startSecond 从当前到开始时间的秒数
* @param endSecond 从当前时间到结束时间的秒数
* @param delayedQueue 延时队列
*/
private void setEndTime(GmGame game, LocalDateTime endTime, LocalDateTime startTime, long startSecond,
long endSecond, RDelayedQueue delayedQueue) {
if (!DateUtil.isDefaultDateTime(endTime) && GameTypeEnum.TOWER.getStatus().equals(game.getGameType())) {
//如果是智慧塔,且设置了结束时间,正常设置结束时间
delayedQueue.offer(StringUtils
.joinWith("_", ACTION_FINISH, game.getId(), endTime.format(PURE_DATETIME_FORMATTER)),
Math.abs(endSecond), TimeUnit.SECONDS);
} else if (DateUtil.isDefaultDateTime(endTime) && GameTypeEnum.ARENA.getStatus().equals(game.getGameType())) {
//如果是头脑竞技场,结束时间是后台算出来的,所以也需要设置自动结束
long useTime = gameCommonService.calcExamUseTime(game);
delayedQueue.offer(StringUtils.joinWith("_", ACTION_FINISH, game.getId(),
startTime.format(PURE_DATETIME_FORMATTER) + "&" + gameCommonService.subjectCount(game)),
Math.abs(startSecond) + useTime, TimeUnit.SECONDS);
} else {
//不是头脑竞技场,又不是智慧塔,那你就别发布了
throw new ApiException(GmcenterExceptionKeys.APIS_GAME_TYPE_NOT_SUPPORT);
}
} |