feat: 添加房间详情 API

This commit is contained in:
2026-06-23 11:05:17 +08:00
parent 5209df46d1
commit 6e4eb6120a
5 changed files with 152 additions and 0 deletions

View File

@@ -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<RoomPlayerDetail> players,
/** 对局中才有棋盘状态 */
int[][] boardState,
/** 当前轮到哪方落子(黑方/白方的 userIdnull 表示对局未开始 */
String currentTurn,
/** 走棋记录(对局中才有) */
List<MoveItem> 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
) {}
}

View File

@@ -112,6 +112,18 @@ public class GameController {
return ApiResponse.ok(ready);
}
/**
* 获取房间详情
*
* 无需登录即可查看(观战者也可查看)。
*/
@SaIgnore
@GetMapping("/room/{roomId}")
public ApiResponse<RoomDetailResponse> roomDetail(@PathVariable String roomId) {
RoomDetailResponse result = gameService.getRoomDetail(roomId);
return ApiResponse.ok(result);
}
/**
* 开始游戏
*

View File

@@ -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);
}

View File

@@ -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<GameMoveEntity> 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);
}

View File

@@ -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<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, 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
);
}
// ==================== 私有辅助方法 ====================
/**