diff --git a/src/main/java/com/webgame/webgamebackend/common/dto/game/RoomDetailResponse.java b/src/main/java/com/webgame/webgamebackend/common/dto/game/RoomDetailResponse.java new file mode 100644 index 0000000..d9b0ae8 --- /dev/null +++ b/src/main/java/com/webgame/webgamebackend/common/dto/game/RoomDetailResponse.java @@ -0,0 +1,53 @@ +package com.webgame.webgamebackend.common.dto.game; + +import java.util.List; + +/** + * 房间详情响应 + * + * 包含房间基本信息、玩家信息(含座位号和准备状态)、对局状态。 + * 用于前端进入房间页时获取初始快照。 + */ +public record RoomDetailResponse( + String roomId, + String name, + String gameId, + String gameIcon, + String gameName, + Integer maxPlayers, + String mode, + Integer stakes, + String status, + String creatorId, + String creatorName, + Boolean allowSpectate, + List players, + /** 对局中才有棋盘状态 */ + int[][] boardState, + /** 当前轮到哪方落子(黑方/白方的 userId),null 表示对局未开始 */ + String currentTurn, + /** 走棋记录(对局中才有) */ + List moves +) { + + /** + * 带座位号和准备状态的玩家信息 + */ + public record RoomPlayerDetail( + String userId, + String avatar, + String nickname, + int seatNumber, + boolean ready + ) {} + + /** + * 走棋记录项 + */ + public record MoveItem( + int moveIndex, + String playerId, + int row, + int col + ) {} +} diff --git a/src/main/java/com/webgame/webgamebackend/controller/GameController.java b/src/main/java/com/webgame/webgamebackend/controller/GameController.java index 5613eb8..2b1eef6 100644 --- a/src/main/java/com/webgame/webgamebackend/controller/GameController.java +++ b/src/main/java/com/webgame/webgamebackend/controller/GameController.java @@ -112,6 +112,18 @@ public class GameController { return ApiResponse.ok(ready); } + /** + * 获取房间详情 + * + * 无需登录即可查看(观战者也可查看)。 + */ + @SaIgnore + @GetMapping("/room/{roomId}") + public ApiResponse roomDetail(@PathVariable String roomId) { + RoomDetailResponse result = gameService.getRoomDetail(roomId); + return ApiResponse.ok(result); + } + /** * 开始游戏 * diff --git a/src/main/java/com/webgame/webgamebackend/service/GameService.java b/src/main/java/com/webgame/webgamebackend/service/GameService.java index 0bdcee4..12f0763 100644 --- a/src/main/java/com/webgame/webgamebackend/service/GameService.java +++ b/src/main/java/com/webgame/webgamebackend/service/GameService.java @@ -74,4 +74,12 @@ public interface GameService { * @param ownerId 房主 account_id */ void startGame(String roomId, String ownerId); + + /** + * 获取房间详情(进入房间时获取初始快照) + * + * @param roomId 房间 ID + * @return 房间详情 + */ + RoomDetailResponse getRoomDetail(String roomId); } diff --git a/src/main/java/com/webgame/webgamebackend/service/GomokuGameService.java b/src/main/java/com/webgame/webgamebackend/service/GomokuGameService.java index dbf5226..50a5e5b 100644 --- a/src/main/java/com/webgame/webgamebackend/service/GomokuGameService.java +++ b/src/main/java/com/webgame/webgamebackend/service/GomokuGameService.java @@ -1,9 +1,12 @@ package com.webgame.webgamebackend.service; +import com.webgame.webgamebackend.entities.GameMoveEntity; import com.webgame.webgamebackend.ws.RoomSessionManager; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import java.util.List; + /** * 五子棋游戏服务 * @@ -14,6 +17,22 @@ import org.springframework.stereotype.Service; @Service public class GomokuGameService { + /** + * 从走棋记录重建棋盘状态 + * + * @param moves 走棋记录(按步序升序) + * @param boardSize 棋盘大小(五子棋通常 15 或 19) + * @return 二维棋盘数组,0=空 1=黑子 2=白子 + */ + public static int[][] rebuildBoard(List moves, int boardSize) { + int[][] board = new int[boardSize][boardSize]; + for (int i = 0; i < moves.size(); i++) { + GameMoveEntity move = moves.get(i); + board[move.getRowNum()][move.getColNum()] = (i % 2 == 0) ? 1 : 2; + } + return board; + } + public void placeStone(String roomId, String userId, int row, int col, RoomSessionManager sessionManager) { log.warn("[GomokuGameService] placeStone 暂未实现, roomId={}, userId={}, row={}, col={}", roomId, userId, row, col); } diff --git a/src/main/java/com/webgame/webgamebackend/service/impl/GameServiceImpl.java b/src/main/java/com/webgame/webgamebackend/service/impl/GameServiceImpl.java index b71139e..cbfb93a 100644 --- a/src/main/java/com/webgame/webgamebackend/service/impl/GameServiceImpl.java +++ b/src/main/java/com/webgame/webgamebackend/service/impl/GameServiceImpl.java @@ -9,6 +9,7 @@ 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; @@ -427,6 +428,65 @@ public class GameServiceImpl implements GameService { } } + @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 roomPlayers = roomPlayerRepository.findByRoomId(roomId); + List userIds = roomPlayers.stream() + .map(RoomPlayerEntity::getUserId).toList(); + Map userMap = userRepository.findByAccountIdIn(userIds).stream() + .collect(Collectors.toMap(u -> u.getAccount().getId(), u -> u)); + + List 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 moves = List.of(); + + if ("playing".equals(room.getStatus()) || "finished".equals(room.getStatus())) { + // 查询走棋记录 + List 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, room.getMaxPlayers()); + // 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 + ); + } + // ==================== 私有辅助方法 ==================== /**