534 lines
23 KiB
Java
534 lines
23 KiB
Java
package com.webgame.webgamebackend.service.impl;
|
||
|
||
import com.alibaba.fastjson2.JSON;
|
||
import com.webgame.webgamebackend.common.dto.game.*;
|
||
import com.webgame.webgamebackend.common.exception.BusinessException;
|
||
import com.webgame.webgamebackend.common.exception.ErrorCode;
|
||
import com.webgame.webgamebackend.entities.*;
|
||
import com.webgame.webgamebackend.repository.*;
|
||
import com.webgame.webgamebackend.common.event.SseEventPublisher;
|
||
import com.webgame.webgamebackend.common.event.SseEventType;
|
||
import com.webgame.webgamebackend.service.GameService;
|
||
import com.webgame.webgamebackend.service.GomokuGameService;
|
||
import lombok.RequiredArgsConstructor;
|
||
import lombok.extern.slf4j.Slf4j;
|
||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||
import org.springframework.stereotype.Service;
|
||
import org.springframework.transaction.annotation.Transactional;
|
||
|
||
import java.time.LocalDateTime;
|
||
import java.util.ArrayList;
|
||
import java.util.List;
|
||
import java.util.Map;
|
||
import java.util.stream.Collectors;
|
||
|
||
/**
|
||
* 游戏服务实现
|
||
*/
|
||
@Slf4j
|
||
@Service
|
||
@RequiredArgsConstructor
|
||
public class GameServiceImpl implements GameService {
|
||
|
||
private final GameConfigRepository gameConfigRepository;
|
||
private final GameRoomRepository gameRoomRepository;
|
||
private final RoomPlayerRepository roomPlayerRepository;
|
||
private final UserRepository userRepository;
|
||
private final BCryptPasswordEncoder passwordEncoder;
|
||
private final SseEventPublisher sseEventPublisher;
|
||
private final GameMoveRepository gameMoveRepository;
|
||
|
||
@Override
|
||
public GameListResponse getGameList() {
|
||
List<GameConfigEntity> configs = gameConfigRepository.findByEnabledTrueOrderBySortOrderAsc();
|
||
List<GameItemResponse> list = configs.stream()
|
||
.map(GameItemResponse::from)
|
||
.toList();
|
||
log.info("[游戏服务] 获取游戏列表, 共 {} 款", list.size());
|
||
return new GameListResponse(list);
|
||
}
|
||
|
||
@Override
|
||
public RoomListResponse getRoomList(String gameId, String status) {
|
||
// 校验游戏存在且启用
|
||
GameConfigEntity gameConfig = gameConfigRepository.findByGameId(gameId)
|
||
.orElseThrow(() -> new BusinessException(ErrorCode.GAME_NOT_FOUND));
|
||
if (!gameConfig.getEnabled()) {
|
||
throw new BusinessException(ErrorCode.GAME_NOT_FOUND);
|
||
}
|
||
|
||
// 查询房间:有状态筛选则按状态查,否则查所有
|
||
List<GameRoomEntity> rooms;
|
||
if (status != null && !status.isBlank()) {
|
||
rooms = gameRoomRepository.findByGameIdAndStatus(gameId, status);
|
||
} else {
|
||
// 默认不返回已结束的房间
|
||
rooms = gameRoomRepository.findByGameId(gameId).stream()
|
||
.filter(r -> !"finished".equals(r.getStatus()))
|
||
.toList();
|
||
}
|
||
|
||
// 组装每个房间的玩家信息和房主昵称
|
||
List<RoomItemResponse> list = new ArrayList<>();
|
||
for (GameRoomEntity room : rooms) {
|
||
List<RoomPlayerResponse> players = buildRoomPlayers(room.getId());
|
||
String creatorName = getNickname(room.getCreatorId());
|
||
list.add(RoomItemResponse.from(room, players, gameConfig.getIcon(), creatorName));
|
||
}
|
||
|
||
log.info("[游戏服务] 获取房间列表, gameId={}, status={}, 共 {} 个", gameId, status, list.size());
|
||
return new RoomListResponse(list);
|
||
}
|
||
|
||
@Override
|
||
@Transactional
|
||
public CreateRoomResponse createRoom(CreateRoomRequest request, String creatorId) {
|
||
// 校验游戏存在且启用
|
||
GameConfigEntity gameConfig = gameConfigRepository.findByGameId(request.gameId())
|
||
.orElseThrow(() -> new BusinessException(ErrorCode.GAME_NOT_FOUND));
|
||
if (!gameConfig.getEnabled()) {
|
||
throw new BusinessException(ErrorCode.GAME_NOT_FOUND);
|
||
}
|
||
|
||
// 校验 mode 在可选列表中
|
||
List<String> modes = JSON.parseArray(gameConfig.getModes(), String.class);
|
||
if (!modes.contains(request.mode())) {
|
||
throw new BusinessException(ErrorCode.BAD_REQUEST, "不支持的模式: " + request.mode());
|
||
}
|
||
|
||
// 校验 stakes 在可选列表中
|
||
List<Integer> stakesOptions = JSON.parseArray(gameConfig.getStakesOptions(), Integer.class);
|
||
if (!stakesOptions.contains(request.stakes())) {
|
||
throw new BusinessException(ErrorCode.BAD_REQUEST, "不支持的底注: " + request.stakes());
|
||
}
|
||
|
||
// 检查用户是否已在其他房间中
|
||
if (roomPlayerRepository.existsByUserId(creatorId)) {
|
||
throw new BusinessException(ErrorCode.ALREADY_IN_ROOM);
|
||
}
|
||
|
||
// 生成桌号
|
||
Integer maxRoomNumber = gameRoomRepository.findMaxRoomNumberByGameId(request.gameId());
|
||
int nextRoomNumber = (maxRoomNumber == null) ? 1 : maxRoomNumber + 1;
|
||
|
||
// 生成桌名
|
||
String roomName = (request.name() != null && !request.name().isBlank())
|
||
? request.name()
|
||
: "桌 " + String.format("%02d", nextRoomNumber);
|
||
|
||
// 创建房间
|
||
GameRoomEntity room = new GameRoomEntity()
|
||
.setGameId(request.gameId())
|
||
.setRoomNumber(nextRoomNumber)
|
||
.setName(roomName)
|
||
.setMaxPlayers(request.maxPlayers())
|
||
.setMode(request.mode())
|
||
.setStakes(request.stakes())
|
||
.setStatus("waiting")
|
||
.setCreatorId(creatorId)
|
||
.setAllowSpectate(request.allowSpectate());
|
||
|
||
// 密码加密(如果提供了密码)
|
||
if (request.password() != null && !request.password().isBlank()) {
|
||
room.setPassword(passwordEncoder.encode(request.password()));
|
||
}
|
||
|
||
gameRoomRepository.save(room);
|
||
|
||
// 房主自动加入房间(座位 1,已准备)
|
||
RoomPlayerEntity creatorPlayer = new RoomPlayerEntity()
|
||
.setRoomId(room.getId())
|
||
.setUserId(creatorId)
|
||
.setSeatNumber(1)
|
||
.setReadyStatus(true)
|
||
.setJoinTime(LocalDateTime.now());
|
||
roomPlayerRepository.save(creatorPlayer);
|
||
|
||
// 组装响应
|
||
List<RoomPlayerResponse> players = buildRoomPlayers(room.getId());
|
||
String creatorName = getNickname(creatorId);
|
||
RoomItemResponse roomResponse = RoomItemResponse.from(room, players, gameConfig.getIcon(), creatorName);
|
||
|
||
log.info("[游戏服务] 创建房间成功, roomId={}, gameId={}, creatorId={}", room.getId(), request.gameId(), creatorId);
|
||
|
||
// 通知所有在线用户:新房间创建
|
||
sseEventPublisher.broadcast(SseEventType.ROOM_CREATED, roomResponse);
|
||
|
||
return new CreateRoomResponse(roomResponse);
|
||
}
|
||
|
||
@Override
|
||
@Transactional
|
||
public RoomItemResponse joinRoom(JoinRoomRequest request, String userId) {
|
||
GameRoomEntity room = gameRoomRepository.findById(request.roomId())
|
||
.orElseThrow(() -> new BusinessException(ErrorCode.ROOM_NOT_FOUND));
|
||
|
||
// 检查房间状态
|
||
if (!"waiting".equals(room.getStatus())) {
|
||
throw new BusinessException(ErrorCode.ROOM_ALREADY_STARTED);
|
||
}
|
||
|
||
// 检查密码
|
||
if (room.getPassword() != null && !room.getPassword().isBlank()) {
|
||
if (request.password() == null || request.password().isBlank()) {
|
||
throw new BusinessException(ErrorCode.ROOM_PASSWORD_ERROR, "请输入房间密码");
|
||
}
|
||
if (!passwordEncoder.matches(request.password(), room.getPassword())) {
|
||
throw new BusinessException(ErrorCode.ROOM_PASSWORD_ERROR);
|
||
}
|
||
}
|
||
|
||
// 检查房间是否已满
|
||
int currentCount = roomPlayerRepository.countByRoomId(room.getId());
|
||
if (currentCount >= room.getMaxPlayers()) {
|
||
throw new BusinessException(ErrorCode.ROOM_FULL);
|
||
}
|
||
|
||
// 检查用户是否已在其他房间
|
||
if (roomPlayerRepository.existsByUserId(userId)) {
|
||
throw new BusinessException(ErrorCode.ALREADY_IN_ROOM);
|
||
}
|
||
|
||
// 分配座位号
|
||
List<RoomPlayerEntity> existingPlayers = roomPlayerRepository.findByRoomId(room.getId());
|
||
int nextSeat = 1;
|
||
if (!existingPlayers.isEmpty()) {
|
||
int maxSeat = existingPlayers.stream()
|
||
.mapToInt(RoomPlayerEntity::getSeatNumber)
|
||
.max()
|
||
.orElse(0);
|
||
nextSeat = maxSeat + 1;
|
||
}
|
||
|
||
// 加入房间
|
||
RoomPlayerEntity player = new RoomPlayerEntity()
|
||
.setRoomId(room.getId())
|
||
.setUserId(userId)
|
||
.setSeatNumber(nextSeat)
|
||
.setReadyStatus(false)
|
||
.setJoinTime(LocalDateTime.now());
|
||
roomPlayerRepository.save(player);
|
||
|
||
// 组装响应
|
||
GameConfigEntity gameConfig = gameConfigRepository.findByGameId(room.getGameId())
|
||
.orElseThrow(() -> new BusinessException(ErrorCode.GAME_NOT_FOUND));
|
||
List<RoomPlayerResponse> players = buildRoomPlayers(room.getId());
|
||
String creatorName = getNickname(room.getCreatorId());
|
||
|
||
log.info("[游戏服务] 玩家加入房间, roomId={}, userId={}", room.getId(), userId);
|
||
|
||
RoomItemResponse response = RoomItemResponse.from(room, players, gameConfig.getIcon(), creatorName);
|
||
// 通知所有在线用户:房间信息更新
|
||
sseEventPublisher.broadcast(SseEventType.ROOM_UPDATED, response);
|
||
return response;
|
||
}
|
||
|
||
@Override
|
||
@Transactional
|
||
public void leaveRoom(String roomId, String userId) {
|
||
GameRoomEntity room = gameRoomRepository.findById(roomId)
|
||
.orElseThrow(() -> new BusinessException(ErrorCode.ROOM_NOT_FOUND));
|
||
|
||
// 检查玩家是否在房间中
|
||
RoomPlayerEntity player = roomPlayerRepository.findByRoomIdAndUserId(roomId, userId)
|
||
.orElseThrow(() -> new BusinessException(ErrorCode.PLAYER_NOT_IN_ROOM));
|
||
|
||
roomPlayerRepository.delete(player);
|
||
|
||
// remaining 需要在后面的 SSE 推送中用到,提取到方法级
|
||
List<RoomPlayerEntity> remaining = roomPlayerRepository.findByRoomId(roomId);
|
||
|
||
if (remaining.isEmpty()) {
|
||
// 无人了,房间标记为结束
|
||
room.setStatus("finished");
|
||
gameRoomRepository.save(room);
|
||
log.info("[游戏服务] 房间无人,标记为结束, roomId={}", roomId);
|
||
} else if (userId.equals(room.getCreatorId())) {
|
||
// 房主离开,顺位转让给下一个玩家
|
||
RoomPlayerEntity nextOwner = remaining.get(0);
|
||
room.setCreatorId(nextOwner.getUserId());
|
||
// 新房主自动设为已准备
|
||
nextOwner.setReadyStatus(true);
|
||
roomPlayerRepository.save(nextOwner);
|
||
gameRoomRepository.save(room);
|
||
log.info("[游戏服务] 房主离开,转让给 userId={}, roomId={}", nextOwner.getUserId(), roomId);
|
||
}
|
||
|
||
log.info("[游戏服务] 玩家离开房间, roomId={}, userId={}", roomId, userId);
|
||
|
||
// 获取剩余玩家重新组装房间信息并推送事件
|
||
if (remaining.isEmpty()) {
|
||
sseEventPublisher.broadcast(SseEventType.ROOM_REMOVED,
|
||
Map.of("roomId", roomId));
|
||
} else {
|
||
List<RoomPlayerResponse> players = buildRoomPlayers(roomId);
|
||
GameConfigEntity gameConfig = gameConfigRepository.findByGameId(room.getGameId())
|
||
.orElse(null);
|
||
String icon = gameConfig != null ? gameConfig.getIcon() : "";
|
||
String creatorName = getNickname(room.getCreatorId());
|
||
RoomItemResponse response = RoomItemResponse.from(room, players, icon, creatorName);
|
||
sseEventPublisher.broadcast(SseEventType.ROOM_UPDATED, response);
|
||
}
|
||
}
|
||
|
||
@Override
|
||
@Transactional
|
||
public void kickPlayer(String roomId, String ownerId, String targetUserId) {
|
||
GameRoomEntity room = gameRoomRepository.findById(roomId)
|
||
.orElseThrow(() -> new BusinessException(ErrorCode.ROOM_NOT_FOUND));
|
||
|
||
// 校验操作者是房主
|
||
if (!ownerId.equals(room.getCreatorId())) {
|
||
throw new BusinessException(ErrorCode.NOT_ROOM_OWNER);
|
||
}
|
||
|
||
// 不能踢自己
|
||
if (ownerId.equals(targetUserId)) {
|
||
throw new BusinessException(ErrorCode.BAD_REQUEST, "不能踢出自己,请使用离开房间");
|
||
}
|
||
|
||
// 检查目标玩家是否在房间中
|
||
RoomPlayerEntity target = roomPlayerRepository.findByRoomIdAndUserId(roomId, targetUserId)
|
||
.orElseThrow(() -> new BusinessException(ErrorCode.PLAYER_NOT_IN_ROOM));
|
||
|
||
roomPlayerRepository.delete(target);
|
||
log.info("[游戏服务] 房主踢出玩家, roomId={}, targetUserId={}", roomId, targetUserId);
|
||
|
||
// 通知被踢玩家
|
||
sseEventPublisher.sendTo(targetUserId, SseEventType.KICKED,
|
||
Map.of("roomId", roomId));
|
||
// 通知房间内其他玩家:房间更新
|
||
List<RoomPlayerResponse> players = buildRoomPlayers(roomId);
|
||
GameConfigEntity gameConfig = gameConfigRepository.findByGameId(room.getGameId())
|
||
.orElse(null);
|
||
String icon = gameConfig != null ? gameConfig.getIcon() : "";
|
||
String creatorName = getNickname(room.getCreatorId());
|
||
RoomItemResponse response = RoomItemResponse.from(room, players, icon, creatorName);
|
||
sseEventPublisher.broadcast(SseEventType.ROOM_UPDATED, response);
|
||
}
|
||
|
||
@Override
|
||
@Transactional
|
||
public boolean toggleReady(String roomId, String userId) {
|
||
GameRoomEntity room = gameRoomRepository.findById(roomId)
|
||
.orElseThrow(() -> new BusinessException(ErrorCode.ROOM_NOT_FOUND));
|
||
|
||
if (!"waiting".equals(room.getStatus())) {
|
||
throw new BusinessException(ErrorCode.ROOM_ALREADY_STARTED);
|
||
}
|
||
|
||
RoomPlayerEntity player = roomPlayerRepository.findByRoomIdAndUserId(roomId, userId)
|
||
.orElseThrow(() -> new BusinessException(ErrorCode.PLAYER_NOT_IN_ROOM));
|
||
|
||
boolean newStatus = !player.getReadyStatus();
|
||
player.setReadyStatus(newStatus);
|
||
roomPlayerRepository.save(player);
|
||
|
||
log.info("[游戏服务] 玩家{}准备, roomId={}, userId={}", newStatus ? "" : "取消", roomId, userId);
|
||
|
||
// 通知房间更新
|
||
List<RoomPlayerResponse> players = buildRoomPlayers(roomId);
|
||
GameConfigEntity gameConfig = gameConfigRepository.findByGameId(room.getGameId())
|
||
.orElse(null);
|
||
String icon = gameConfig != null ? gameConfig.getIcon() : "";
|
||
String creatorName = getNickname(room.getCreatorId());
|
||
RoomItemResponse response = RoomItemResponse.from(room, players, icon, creatorName);
|
||
sseEventPublisher.broadcast(SseEventType.ROOM_UPDATED, response);
|
||
|
||
return newStatus;
|
||
}
|
||
|
||
@Override
|
||
@Transactional
|
||
public void startGame(String roomId, String ownerId) {
|
||
GameRoomEntity room = gameRoomRepository.findById(roomId)
|
||
.orElseThrow(() -> new BusinessException(ErrorCode.ROOM_NOT_FOUND));
|
||
|
||
// 校验操作者是房主
|
||
if (!ownerId.equals(room.getCreatorId())) {
|
||
throw new BusinessException(ErrorCode.NOT_ROOM_OWNER);
|
||
}
|
||
|
||
// 校验房间状态
|
||
if (!"waiting".equals(room.getStatus())) {
|
||
throw new BusinessException(ErrorCode.ROOM_ALREADY_STARTED);
|
||
}
|
||
|
||
// 校验玩家人数 >= 2
|
||
List<RoomPlayerEntity> players = roomPlayerRepository.findByRoomId(roomId);
|
||
if (players.size() < 2) {
|
||
throw new BusinessException(ErrorCode.PLAYER_COUNT_INSUFFICIENT);
|
||
}
|
||
|
||
// 校验所有玩家已准备(房主除外,房主默认准备)
|
||
for (RoomPlayerEntity player : players) {
|
||
if (!player.getReadyStatus() && !player.getUserId().equals(room.getCreatorId())) {
|
||
throw new BusinessException(ErrorCode.NOT_ALL_READY);
|
||
}
|
||
}
|
||
|
||
// 扣除房主底注
|
||
UserEntity ownerUser = userRepository.findByAccountId(ownerId)
|
||
.orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND));
|
||
if (ownerUser.getBalance() < room.getStakes()) {
|
||
throw new BusinessException(ErrorCode.BALANCE_INSUFFICIENT);
|
||
}
|
||
ownerUser.setBalance(ownerUser.getBalance() - room.getStakes());
|
||
userRepository.save(ownerUser);
|
||
|
||
// 扣除其他玩家底注
|
||
for (RoomPlayerEntity player : players) {
|
||
if (!player.getUserId().equals(ownerId)) {
|
||
UserEntity user = userRepository.findByAccountId(player.getUserId())
|
||
.orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND));
|
||
if (user.getBalance() < room.getStakes()) {
|
||
throw new BusinessException(ErrorCode.BALANCE_INSUFFICIENT,
|
||
"玩家 " + user.getNickName() + " 余额不足");
|
||
}
|
||
user.setBalance(user.getBalance() - room.getStakes());
|
||
userRepository.save(user);
|
||
}
|
||
}
|
||
|
||
// 更新房间状态为进行中
|
||
room.setStatus("playing");
|
||
gameRoomRepository.save(room);
|
||
|
||
log.info("[游戏服务] 游戏开始, roomId={}, 参与人数={}, 底注={}", roomId, players.size(), room.getStakes());
|
||
|
||
// 通知房间状态更新
|
||
List<RoomPlayerResponse> allPlayers = buildRoomPlayers(roomId);
|
||
GameConfigEntity gameConfig = gameConfigRepository.findByGameId(room.getGameId())
|
||
.orElse(null);
|
||
String icon = gameConfig != null ? gameConfig.getIcon() : "";
|
||
String creatorName = getNickname(room.getCreatorId());
|
||
RoomItemResponse response = RoomItemResponse.from(room, allPlayers, icon, creatorName);
|
||
sseEventPublisher.broadcast(SseEventType.ROOM_UPDATED, response);
|
||
|
||
// 通知房间内每个玩家游戏已开始
|
||
for (RoomPlayerEntity player : players) {
|
||
sseEventPublisher.sendTo(player.getUserId(), SseEventType.GAME_STARTED,
|
||
Map.of("roomId", roomId));
|
||
}
|
||
|
||
// 通知余额变动(房主已在前面扣除了底注)
|
||
ownerUser = userRepository.findByAccountId(ownerId).orElse(null);
|
||
if (ownerUser != null) {
|
||
sseEventPublisher.sendTo(ownerId, SseEventType.BALANCE_CHG,
|
||
Map.of("balance", ownerUser.getBalance(), "delta", -room.getStakes()));
|
||
}
|
||
for (RoomPlayerEntity player : players) {
|
||
if (!player.getUserId().equals(ownerId)) {
|
||
UserEntity user = userRepository.findByAccountId(player.getUserId()).orElse(null);
|
||
if (user != null) {
|
||
sseEventPublisher.sendTo(player.getUserId(), SseEventType.BALANCE_CHG,
|
||
Map.of("balance", user.getBalance(), "delta", -room.getStakes()));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public RoomDetailResponse getRoomDetail(String roomId) {
|
||
GameRoomEntity room = gameRoomRepository.findById(roomId)
|
||
.orElseThrow(() -> new BusinessException(ErrorCode.ROOM_NOT_FOUND));
|
||
|
||
GameConfigEntity gameConfig = gameConfigRepository.findByGameId(room.getGameId())
|
||
.orElseThrow(() -> new BusinessException(ErrorCode.GAME_NOT_FOUND));
|
||
|
||
// 组装玩家详情(含座位号和准备状态)
|
||
List<RoomPlayerEntity> roomPlayers = roomPlayerRepository.findByRoomId(roomId);
|
||
List<String> userIds = roomPlayers.stream()
|
||
.map(RoomPlayerEntity::getUserId).toList();
|
||
Map<String, UserEntity> userMap = userRepository.findByAccountIdIn(userIds).stream()
|
||
.collect(Collectors.toMap(u -> u.getAccount().getId(), u -> u));
|
||
|
||
List<RoomDetailResponse.RoomPlayerDetail> playerDetails = roomPlayers.stream()
|
||
.map(rp -> {
|
||
UserEntity user = userMap.get(rp.getUserId());
|
||
return new RoomDetailResponse.RoomPlayerDetail(
|
||
rp.getUserId(),
|
||
user != null ? user.getAvatar() : "",
|
||
user != null ? user.getNickName() : "未知玩家",
|
||
rp.getSeatNumber(),
|
||
rp.getReadyStatus()
|
||
);
|
||
}).toList();
|
||
|
||
String creatorName = getNickname(room.getCreatorId());
|
||
|
||
// 对局中或有对局记录时,查询棋盘状态
|
||
int[][] boardState = null;
|
||
String currentTurn = null;
|
||
List<RoomDetailResponse.MoveItem> moves = List.of();
|
||
|
||
if ("playing".equals(room.getStatus()) || "finished".equals(room.getStatus())) {
|
||
// 查询走棋记录
|
||
List<GameMoveEntity> moveEntities = gameMoveRepository.findByRoomIdOrderByMoveIndexAsc(roomId);
|
||
moves = moveEntities.stream()
|
||
.map(m -> new RoomDetailResponse.MoveItem(
|
||
m.getMoveIndex(), m.getPlayerId(), m.getRowNum(), m.getColNum()))
|
||
.toList();
|
||
|
||
// 从走棋记录重建棋盘
|
||
boardState = GomokuGameService.rebuildBoard(moveEntities, GomokuGameService.BOARD_SIZE);
|
||
// currentTurn 由服务端运行时管理,HTTP 快照中暂不返回
|
||
}
|
||
|
||
log.info("[游戏服务] 获取房间详情, roomId={}, status={}, 玩家数={}",
|
||
roomId, room.getStatus(), playerDetails.size());
|
||
|
||
return new RoomDetailResponse(
|
||
room.getId(), room.getName(), room.getGameId(),
|
||
gameConfig.getIcon(), gameConfig.getName(),
|
||
room.getMaxPlayers(), room.getMode(), room.getStakes(),
|
||
room.getStatus(), room.getCreatorId(), creatorName,
|
||
room.getAllowSpectate(), playerDetails, boardState, currentTurn, moves
|
||
);
|
||
}
|
||
|
||
// ==================== 私有辅助方法 ====================
|
||
|
||
/**
|
||
* 组装房间内的玩家信息列表
|
||
*
|
||
* @param roomId 房间 ID
|
||
* @return 玩家响应列表(含头像和昵称)
|
||
*/
|
||
private List<RoomPlayerResponse> buildRoomPlayers(String roomId) {
|
||
List<RoomPlayerEntity> roomPlayers = roomPlayerRepository.findByRoomId(roomId);
|
||
if (roomPlayers.isEmpty()) {
|
||
return List.of();
|
||
}
|
||
|
||
// 批量查询玩家用户信息
|
||
List<String> userIds = roomPlayers.stream()
|
||
.map(RoomPlayerEntity::getUserId)
|
||
.toList();
|
||
List<UserEntity> users = userRepository.findByAccountIdIn(userIds);
|
||
Map<String, UserEntity> userMap = users.stream()
|
||
.collect(Collectors.toMap(u -> u.getAccount().getId(), u -> u));
|
||
|
||
return roomPlayers.stream()
|
||
.map(rp -> {
|
||
UserEntity user = userMap.get(rp.getUserId());
|
||
String avatar = (user != null) ? user.getAvatar() : "";
|
||
String nickname = (user != null) ? user.getNickName() : "未知玩家";
|
||
return new RoomPlayerResponse(avatar, nickname);
|
||
})
|
||
.toList();
|
||
}
|
||
|
||
/**
|
||
* 获取用户昵称
|
||
*
|
||
* @param accountId 账号 ID
|
||
* @return 昵称,查不到则返回 "未知玩家"
|
||
*/
|
||
private String getNickname(String accountId) {
|
||
return userRepository.findByAccountId(accountId)
|
||
.map(UserEntity::getNickName)
|
||
.orElse("未知玩家");
|
||
}
|
||
}
|