package com.tailbet.service.impl; import com.tailbet.model.entity.BetOrder; import com.tailbet.model.entity.User; import com.tailbet.model.entity.GameRound; import com.tailbet.mapper.BetOrderMapper; import com.tailbet.mapper.UserMapper; import com.tailbet.mapper.GameRoundMapper; import com.tailbet.service.IBetService; import com.tailbet.service.IGameService; import com.tailbet.service.IPointsService; import com.tailbet.service.IAuditService; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.time.LocalDateTime; /** * 注单Service实现 */ @Slf4j @RequiredArgsConstructor @Service public class BetServiceImpl implements IBetService { private final BetOrderMapper betOrderMapper; private final UserMapper userMapper; private final GameRoundMapper gameRoundMapper; private final IPointsService pointsService; private final IAuditService auditService; /** * 下注 */ @Override @Transactional public BetOrder placeBet(Long userId, Long groupId, String clientMsgId, String playType, Integer betAmount) { // 防重检查 if (clientMsgId != null) { BetOrder existing = betOrderMapper.selectOne(new LambdaQueryWrapper() .eq(BetOrder::getClientMsgId, clientMsgId)); if (existing != null) { throw new RuntimeException("重复下注"); } } // 检查用户 User user = userMapper.selectById(userId); if (user == null) { throw new RuntimeException("用户不存在"); } if (user.getIsFake() != null && user.getIsFake() == 1) { // 假人下注只记录审计日志,不产生订单 auditService.log(groupId, null, userId, "FAKE_BET", String.format("{\"playType\":\"%s\",\"amount\":%d}", playType, betAmount)); return null; } // 检查进行中的局(下注中) GameRound round = gameRoundMapper.selectOne(new LambdaQueryWrapper() .eq(GameRound::getGroupId, groupId) .eq(GameRound::getStatus, IGameService.STATUS_BETTING)); if (round == null) { throw new RuntimeException("当前不在下注阶段"); } // 检查余额 if (user.getPoints() < betAmount) { throw new RuntimeException("余额不足"); } // 检查限额 if (betAmount < 1 || betAmount > 2000) { throw new RuntimeException("单笔下注金额需在1-2000之间"); } // 检查单局累计 Integer roundTotal = betOrderMapper.selectList(new LambdaQueryWrapper() .eq(BetOrder::getUserId, userId) .eq(BetOrder::getRoundId, round.getId()) .eq(BetOrder::getIsFake, 0)) .stream() .mapToInt(BetOrder::getBetAmount) .sum(); if (roundTotal + betAmount > 20000) { throw new RuntimeException("单局累计下注不能超过20000"); } // 获取赔率 BigDecimal odds = getOdds(playType); // 扣减积分 pointsService.subPoints(userId, betAmount.longValue(), IPointsService.TYPE_BET, null, null, "游戏下注"); // 创建注单 BetOrder bet = new BetOrder(); bet.setUserId(userId); bet.setGroupId(groupId); bet.setRoundId(round.getId()); bet.setClientMsgId(clientMsgId); bet.setPlayType(playType); bet.setBetAmount(betAmount); bet.setOdds(odds); bet.setIsFake(0); bet.setStatus(0); bet.setCreateTime(LocalDateTime.now()); betOrderMapper.insert(bet); // 审计日志 auditService.log(groupId, round.getId(), userId, "BET_PLACE", String.format("{\"betId\":%d,\"playType\":\"%s\",\"amount\":%d}", bet.getId(), playType, betAmount)); return bet; } /** * 获取赔率 */ @Override public BigDecimal getOdds(String playType) { if ("单".equals(playType) || "双".equals(playType)) { return IGameService.ODDS_SINGLE_DOUBLE; } else if ("大".equals(playType) || "小".equals(playType)) { return IGameService.ODDS_SIZE; } else { return IGameService.ODDS_DIGIT; } } /** * 获取用户本局下注总额 */ @Override public Integer getUserRoundBetAmount(Long userId, Long roundId) { return betOrderMapper.selectList(new LambdaQueryWrapper() .eq(BetOrder::getUserId, userId) .eq(BetOrder::getRoundId, roundId) .eq(BetOrder::getIsFake, 0)) .stream() .mapToInt(BetOrder::getBetAmount) .sum(); } }