diff --git a/build.gradle b/build.gradle index 85b7f01..c9f725f 100644 --- a/build.gradle +++ b/build.gradle @@ -36,7 +36,7 @@ dependencyManagement { dependencies { // Spring Boot implementation 'org.springframework.boot:spring-boot-starter-web' - implementation 'org.springframework.boot:spring-boot-starter-websocket' +// implementation 'org.springframework.boot:spring-boot-starter-websocket' developmentOnly 'org.springframework.boot:spring-boot-devtools' // Spring Cloud diff --git a/src/main/java/io/urdego/urdego_game_service/common/client/ContentServiceClient.java b/src/main/java/io/urdego/urdego_game_service/common/client/content/ContentServiceClient.java similarity index 77% rename from src/main/java/io/urdego/urdego_game_service/common/client/ContentServiceClient.java rename to src/main/java/io/urdego/urdego_game_service/common/client/content/ContentServiceClient.java index c966f19..df761ea 100644 --- a/src/main/java/io/urdego/urdego_game_service/common/client/ContentServiceClient.java +++ b/src/main/java/io/urdego/urdego_game_service/common/client/content/ContentServiceClient.java @@ -1,13 +1,13 @@ -package io.urdego.urdego_game_service.common.client; +package io.urdego.urdego_game_service.common.client.content; -import io.urdego.urdego_game_service.common.client.dto.ContentRes; +import io.urdego.urdego_game_service.common.client.content.dto.ContentRes; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import java.util.List; -@FeignClient(name = "content-service", url = "${feign.client.config.content-service.url}") +@FeignClient(name = "content-service", url = "${feign.client.config.service.url}") public interface ContentServiceClient { @GetMapping("/api/content-service/content") ContentRes getContent(@RequestParam Long contentId); diff --git a/src/main/java/io/urdego/urdego_game_service/common/client/dto/ContentRes.java b/src/main/java/io/urdego/urdego_game_service/common/client/content/dto/ContentRes.java similarity index 73% rename from src/main/java/io/urdego/urdego_game_service/common/client/dto/ContentRes.java rename to src/main/java/io/urdego/urdego_game_service/common/client/content/dto/ContentRes.java index f27a0b9..85e79df 100644 --- a/src/main/java/io/urdego/urdego_game_service/common/client/dto/ContentRes.java +++ b/src/main/java/io/urdego/urdego_game_service/common/client/content/dto/ContentRes.java @@ -1,4 +1,4 @@ -package io.urdego.urdego_game_service.common.client.dto; +package io.urdego.urdego_game_service.common.client.content.dto; public record ContentRes( Long contentId, diff --git a/src/main/java/io/urdego/urdego_game_service/common/client/user/UserServiceClient.java b/src/main/java/io/urdego/urdego_game_service/common/client/user/UserServiceClient.java new file mode 100644 index 0000000..d03be31 --- /dev/null +++ b/src/main/java/io/urdego/urdego_game_service/common/client/user/UserServiceClient.java @@ -0,0 +1,7 @@ +package io.urdego.urdego_game_service.common.client.user; + +import org.springframework.cloud.openfeign.FeignClient; + +@FeignClient(name = "user-service", url = "${feign.client.config.service.url}") +public interface UserServiceClient { +} diff --git a/src/main/java/io/urdego/urdego_game_service/common/config/WebSocketConfig.java b/src/main/java/io/urdego/urdego_game_service/common/config/WebSocketConfig.java index 481e1df..8529f91 100644 --- a/src/main/java/io/urdego/urdego_game_service/common/config/WebSocketConfig.java +++ b/src/main/java/io/urdego/urdego_game_service/common/config/WebSocketConfig.java @@ -1,24 +1,20 @@ package io.urdego.urdego_game_service.common.config; import org.springframework.context.annotation.Configuration; -import org.springframework.messaging.simp.config.MessageBrokerRegistry; -import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; -import org.springframework.web.socket.config.annotation.StompEndpointRegistry; -import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; -@Configuration -@EnableWebSocketMessageBroker -public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { - - @Override - public void configureMessageBroker(MessageBrokerRegistry registry) { - registry.enableSimpleBroker("/game-service/sub"); - registry.setApplicationDestinationPrefixes("/game-service/pub"); - } - - @Override - public void registerStompEndpoints(StompEndpointRegistry registry) { - registry.addEndpoint("/game-service/connect") - .setAllowedOrigins("*"); - } -} +//@Configuration +//@EnableWebSocketMessageBroker +//public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { +// +// @Override +// public void configureMessageBroker(MessageBrokerRegistry registry) { +// registry.enableSimpleBroker("/game-service/sub"); +// registry.setApplicationDestinationPrefixes("/game-service/pub"); +// } +// +// @Override +// public void registerStompEndpoints(StompEndpointRegistry registry) { +// registry.addEndpoint("/game-service/connect") +// .setAllowedOrigins("*"); +// } +//} diff --git a/src/main/java/io/urdego/urdego_game_service/controller/game/GameController.java b/src/main/java/io/urdego/urdego_game_service/controller/game/GameController.java index 5fba39a..118f098 100644 --- a/src/main/java/io/urdego/urdego_game_service/controller/game/GameController.java +++ b/src/main/java/io/urdego/urdego_game_service/controller/game/GameController.java @@ -3,7 +3,10 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import io.urdego.urdego_game_service.controller.game.dto.request.GameCreateReq; +import io.urdego.urdego_game_service.controller.game.dto.request.ScoreReq; import io.urdego.urdego_game_service.controller.game.dto.response.GameCreateRes; +import io.urdego.urdego_game_service.controller.game.dto.response.GameEndRes; +import io.urdego.urdego_game_service.controller.game.dto.response.ScoreRes; import io.urdego.urdego_game_service.domain.game.service.GameService; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; @@ -15,18 +18,33 @@ @RestController @RequiredArgsConstructor -@RequestMapping("/api/game-service") +@RequestMapping("/api/game-service/game") public class GameController { private final GameService gameService; - // 게임 생성 @Tag(name = "게임 API") @Operation(summary = "게임 생성", description = "roomId로 게임 생성") - @PostMapping("/game/start") + @PostMapping("/start") public ResponseEntity startGame(@RequestBody GameCreateReq request) { GameCreateRes response = gameService.createGame(request); return new ResponseEntity<>(response, HttpStatus.CREATED); } + @Tag(name = "게임 Socket") + @Operation(summary = "게임 점수", description = "게임 점수 조회") + @PostMapping("/score") + public ResponseEntity giveScores(@RequestBody ScoreReq request) { + ScoreRes response = gameService.giveScores(request); + return new ResponseEntity<>(response, HttpStatus.OK); + } + + @Tag(name = "게임 Socket") + @Operation(summary = "게임 종료", description = "게임 종료") + @PostMapping("/end") + public ResponseEntity endGame(@RequestBody String gameId) { + GameEndRes response = gameService.finishGame(gameId); + return new ResponseEntity<>(response, HttpStatus.OK); + } + } diff --git a/src/main/java/io/urdego/urdego_game_service/controller/game/GameSocketController.java b/src/main/java/io/urdego/urdego_game_service/controller/game/GameSocketController.java index 9f74b18..db63bb2 100644 --- a/src/main/java/io/urdego/urdego_game_service/controller/game/GameSocketController.java +++ b/src/main/java/io/urdego/urdego_game_service/controller/game/GameSocketController.java @@ -5,8 +5,6 @@ import io.urdego.urdego_game_service.controller.game.dto.response.ScoreRes; import io.urdego.urdego_game_service.domain.game.service.GameService; import lombok.RequiredArgsConstructor; -import org.springframework.messaging.handler.annotation.MessageMapping; -import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.stereotype.Controller; @Controller @@ -14,19 +12,18 @@ public class GameSocketController { private final GameService gameService; - private final SimpMessagingTemplate messagingTemplate; // 점수 요청 - @MessageMapping("/game/score") - public void giveScores(ScoreReq request) { - ScoreRes response = gameService.giveScores(request); - messagingTemplate.convertAndSend("/game-service/sub/" + response.roomId(), response); - } +// @MessageMapping("/game/score") +// public void giveScores(ScoreReq request) { +// ScoreRes response = gameService.giveScores(request); +// messagingTemplate.convertAndSend("/game-service/sub/" + response.roomId(), response); +// } // 게임 종료 - @MessageMapping("/game/end") - public void endGame(String gameId) { - GameEndRes response = gameService.finishGame(gameId); - messagingTemplate.convertAndSend("/game-service/sub/" + response.roomId(), response); - } +// @MessageMapping("/game/end") +// public void endGame(String gameId) { +// GameEndRes response = gameService.finishGame(gameId); +// messagingTemplate.convertAndSend("/game-service/sub/" + response.roomId(), response); +// } } diff --git a/src/main/java/io/urdego/urdego_game_service/controller/game/dto/response/GameEndRes.java b/src/main/java/io/urdego/urdego_game_service/controller/game/dto/response/GameEndRes.java index 1b2ad46..e730cb3 100644 --- a/src/main/java/io/urdego/urdego_game_service/controller/game/dto/response/GameEndRes.java +++ b/src/main/java/io/urdego/urdego_game_service/controller/game/dto/response/GameEndRes.java @@ -9,14 +9,16 @@ public record GameEndRes( String gameId, String roomId, Status status, - Map totalScores + Map totalScores, + Map exp ) { - public static GameEndRes from(Game game) { + public static GameEndRes of(Game game, Map exp) { return new GameEndRes( game.getGameId(), game.getRoomId(), game.getStatus(), - game.getTotalScores() + game.getTotalScores(), + exp ); } } diff --git a/src/main/java/io/urdego/urdego_game_service/controller/room/RoomController.java b/src/main/java/io/urdego/urdego_game_service/controller/room/RoomController.java index ceab247..6d880f4 100644 --- a/src/main/java/io/urdego/urdego_game_service/controller/room/RoomController.java +++ b/src/main/java/io/urdego/urdego_game_service/controller/room/RoomController.java @@ -2,7 +2,10 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import io.urdego.urdego_game_service.controller.room.dto.request.ContentSelectReq; +import io.urdego.urdego_game_service.controller.room.dto.request.PlayerReq; import io.urdego.urdego_game_service.controller.room.dto.request.RoomCreateReq; +import io.urdego.urdego_game_service.controller.room.dto.response.RoomPlayersRes; import io.urdego.urdego_game_service.controller.room.dto.response.RoomCreateRes; import io.urdego.urdego_game_service.controller.room.dto.response.RoomInfoRes; import io.urdego.urdego_game_service.domain.room.service.RoomService; @@ -15,27 +18,49 @@ @RestController @RequiredArgsConstructor -@RequestMapping("/api/game-service") +@RequestMapping("/api/game-service/room") public class RoomController { private final RoomService roomService; - // 대기방 생성 @Tag(name = "대기방 API") @Operation(summary = "대기방 생성", description = "방장이 입력한 정보로 대기방 생성") - @PostMapping("/room/create") + @PostMapping("/create") public ResponseEntity createRoom(@RequestBody RoomCreateReq request) { RoomCreateRes response = roomService.createRoom(request); return new ResponseEntity<>(response, HttpStatus.CREATED); } - // 대기방 목록 조회 @Tag(name = "대기방 API") @Operation(summary = "대기방 목록 조회", description = "현재 생성되어 있는 대기방 목록 전체 조회") - @GetMapping("/room/list") + @GetMapping("/list") public ResponseEntity> viewRooms() { List response = roomService.getRoomList(); return new ResponseEntity<>(response, HttpStatus.OK); } + @Tag(name = "대기방 Socket") + @Operation(summary = "플레이어 초대", description = "대기방에 플레이어 참여") + @PostMapping("/player/invite") + public ResponseEntity invitePlayer(@RequestBody PlayerReq request) { + RoomPlayersRes response = roomService.joinRoom(request); + return new ResponseEntity<>(response, HttpStatus.OK); + } + + @Tag(name = "대기방 Socket") + @Operation(summary = "플레이어 삭제", description = "대기방에 플레이어 삭제") + @PostMapping("/player/remove") + public ResponseEntity removePlayer(@RequestBody PlayerReq request) { + RoomPlayersRes response = roomService.removePlayer(request); + return new ResponseEntity<>(response, HttpStatus.OK); + } + + @Tag(name = "대기방 Socket") + @Operation(summary = "게임 컨텐츠 선택", description = "게임 문제 출제를 위한 컨텐츠 선택") + @PostMapping("/select-content") + public ResponseEntity selectContent(@RequestBody ContentSelectReq request) { + roomService.registerContents(request); + return new ResponseEntity<>(HttpStatus.OK); + } + } diff --git a/src/main/java/io/urdego/urdego_game_service/controller/room/RoomSocketController.java b/src/main/java/io/urdego/urdego_game_service/controller/room/RoomSocketController.java index 102a1b8..814a1e1 100644 --- a/src/main/java/io/urdego/urdego_game_service/controller/room/RoomSocketController.java +++ b/src/main/java/io/urdego/urdego_game_service/controller/room/RoomSocketController.java @@ -1,12 +1,7 @@ package io.urdego.urdego_game_service.controller.room; -import io.urdego.urdego_game_service.controller.room.dto.request.ContentSelectReq; -import io.urdego.urdego_game_service.controller.room.dto.request.PlayerReq; -import io.urdego.urdego_game_service.controller.room.dto.response.PlayerRes; import io.urdego.urdego_game_service.domain.room.service.RoomService; import lombok.RequiredArgsConstructor; -import org.springframework.messaging.handler.annotation.MessageMapping; -import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.stereotype.Controller; @Controller @@ -14,25 +9,24 @@ public class RoomSocketController { private final RoomService roomService; - private final SimpMessagingTemplate messagingTemplate; - // 친구 초대 - @MessageMapping("/room/player/invite") - public void invitePlayer(PlayerReq request) { - PlayerRes response = roomService.joinRoom(request); - messagingTemplate.convertAndSend("/game-service/sub/" + response.roomId(), response); - } - - // 컨텐츠 선택 (최대 5개, 0개일 경우 자체 컨텐츠 제공) - @MessageMapping("/room/select-content") - public void selectContent(ContentSelectReq request) { - roomService.registerContents(request); - } - - // 플레이어 삭제 - @MessageMapping("/room/player/remove") - public void removePlayer(PlayerReq request) { - PlayerRes response = roomService.removePlayer(request); - messagingTemplate.convertAndSend("/game-service/sub/" + response.roomId(), response); - } +// // 친구 초대 +// @MessageMapping("/room/player/invite") +// public void invitePlayer(PlayerReq request) { +// PlayerRes response = roomService.joinRoom(request); +// messagingTemplate.convertAndSend("/game-service/sub/" + response.roomId(), response); +// } +// +// // 컨텐츠 선택 (최대 5개, 0개일 경우 자체 컨텐츠 제공) +// @MessageMapping("/room/select-content") +// public void selectContent(ContentSelectReq request) { +// roomService.registerContents(request); +// } +// +// // 플레이어 삭제 +// @MessageMapping("/room/player/remove") +// public void removePlayer(PlayerReq request) { +// PlayerRes response = roomService.removePlayer(request); +// messagingTemplate.convertAndSend("/game-service/sub/" + response.roomId(), response); +// } } diff --git a/src/main/java/io/urdego/urdego_game_service/controller/room/dto/request/PlayerReq.java b/src/main/java/io/urdego/urdego_game_service/controller/room/dto/request/PlayerReq.java index 7f7d8ed..6709dc2 100644 --- a/src/main/java/io/urdego/urdego_game_service/controller/room/dto/request/PlayerReq.java +++ b/src/main/java/io/urdego/urdego_game_service/controller/room/dto/request/PlayerReq.java @@ -2,6 +2,7 @@ public record PlayerReq( String roomId, - Long userId + Long userId, + Boolean isReady ) { } diff --git a/src/main/java/io/urdego/urdego_game_service/controller/room/dto/request/RoomCreateReq.java b/src/main/java/io/urdego/urdego_game_service/controller/room/dto/request/RoomCreateReq.java index 23984a3..c9859c7 100644 --- a/src/main/java/io/urdego/urdego_game_service/controller/room/dto/request/RoomCreateReq.java +++ b/src/main/java/io/urdego/urdego_game_service/controller/room/dto/request/RoomCreateReq.java @@ -4,7 +4,6 @@ public record RoomCreateReq( String userId, String roomName, int maxPlayers, - int totalRounds, - int timer + int totalRounds ) { } diff --git a/src/main/java/io/urdego/urdego_game_service/controller/room/dto/response/PlayerRes.java b/src/main/java/io/urdego/urdego_game_service/controller/room/dto/response/PlayerRes.java deleted file mode 100644 index c02b68a..0000000 --- a/src/main/java/io/urdego/urdego_game_service/controller/room/dto/response/PlayerRes.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.urdego.urdego_game_service.controller.room.dto.response; - -import io.urdego.urdego_game_service.common.enums.Status; -import io.urdego.urdego_game_service.domain.room.entity.Room; - -import java.util.List; - -public record PlayerRes( - String roomId, - Status status, - List currentPlayers -) { - public static PlayerRes from(Room room) { - return new PlayerRes( - room.getRoomId(), - room.getStatus(), - room.getCurrentPlayers() - ); - } -} diff --git a/src/main/java/io/urdego/urdego_game_service/controller/room/dto/response/RoomPlayersRes.java b/src/main/java/io/urdego/urdego_game_service/controller/room/dto/response/RoomPlayersRes.java new file mode 100644 index 0000000..a75ba22 --- /dev/null +++ b/src/main/java/io/urdego/urdego_game_service/controller/room/dto/response/RoomPlayersRes.java @@ -0,0 +1,31 @@ +package io.urdego.urdego_game_service.controller.room.dto.response; + +import io.urdego.urdego_game_service.common.enums.Status; +import io.urdego.urdego_game_service.domain.room.entity.Room; + +import java.util.List; +import java.util.Map; + +public record RoomPlayersRes( + String roomId, + Status status, + List currentPlayers, + String isHost, + Map readyStatus, + Boolean allReady +) { + public static RoomPlayersRes from(Room room) { + String hostId = room.getCurrentPlayers().isEmpty() ? null : room.getCurrentPlayers().get(0); + boolean allReady = room.getReadyStatus().size() == room.getCurrentPlayers().size() + && room.getReadyStatus().values().stream().allMatch(ready -> ready); + + return new RoomPlayersRes( + room.getRoomId(), + room.getStatus(), + room.getCurrentPlayers(), + hostId, + room.getReadyStatus(), + allReady + ); + } +} diff --git a/src/main/java/io/urdego/urdego_game_service/controller/round/RoundController.java b/src/main/java/io/urdego/urdego_game_service/controller/round/RoundController.java new file mode 100644 index 0000000..5b5e6ac --- /dev/null +++ b/src/main/java/io/urdego/urdego_game_service/controller/round/RoundController.java @@ -0,0 +1,40 @@ +package io.urdego.urdego_game_service.controller.round; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.urdego.urdego_game_service.controller.round.dto.request.AnswerReq; +import io.urdego.urdego_game_service.controller.round.dto.request.QuestionReq; +import io.urdego.urdego_game_service.controller.round.dto.response.AnswerRes; +import io.urdego.urdego_game_service.controller.round.dto.response.QuestionRes; +import io.urdego.urdego_game_service.domain.round.service.RoundService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/game-service/round") +public class RoundController { + + private final RoundService roundService; + + @Tag(name = "게임 라운드 Socket") + @Operation(summary = "문제 출제", description = "라운드 시작") + @PostMapping("/question") + public ResponseEntity giveQuestion(@RequestBody QuestionReq request) { + QuestionRes response = roundService.getQuestion(request); + return ResponseEntity.ok(response); + } + + @Tag(name = "게임 라운드 Socket") + @Operation(summary = "답안 제출", description = "사용자별 답안 제출") + @PostMapping("/answer") + public ResponseEntity submitAnswer(@RequestBody AnswerReq request) { + AnswerRes response = roundService.submitAnswer(request); + return ResponseEntity.ok(response); + } + +} diff --git a/src/main/java/io/urdego/urdego_game_service/controller/round/RoundSocketController.java b/src/main/java/io/urdego/urdego_game_service/controller/round/RoundSocketController.java index 871a737..ae85c5b 100644 --- a/src/main/java/io/urdego/urdego_game_service/controller/round/RoundSocketController.java +++ b/src/main/java/io/urdego/urdego_game_service/controller/round/RoundSocketController.java @@ -6,8 +6,6 @@ import io.urdego.urdego_game_service.controller.round.dto.response.QuestionRes; import io.urdego.urdego_game_service.domain.round.service.RoundService; import lombok.RequiredArgsConstructor; -import org.springframework.messaging.handler.annotation.MessageMapping; -import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.stereotype.Controller; @Controller @@ -15,19 +13,19 @@ public class RoundSocketController { private final RoundService roundService; - private final SimpMessagingTemplate messagingTemplate; +// private final SimpMessagingTemplate messagingTemplate; - // 라운드 문제 출제 - @MessageMapping("/round/question") - public void giveQuestion(QuestionReq request) { - QuestionRes response = roundService.getQuestion(request); - messagingTemplate.convertAndSend("/game-service/sub/" + response.roomId(), response); - } - - // 플레이어 정답 제출 - @MessageMapping("/round/answer") - public void submitAnswer(AnswerReq request) { - AnswerRes response = roundService.submitAnswer(request); - } +// // 라운드 문제 출제 +// @MessageMapping("/round/question") +// public void giveQuestion(QuestionReq request) { +// QuestionRes response = roundService.getQuestion(request); +// messagingTemplate.convertAndSend("/game-service/sub/" + response.roomId(), response); +// } +// +// // 플레이어 정답 제출 +// @MessageMapping("/round/answer") +// public void submitAnswer(AnswerReq request) { +// AnswerRes response = roundService.submitAnswer(request); +// } } diff --git a/src/main/java/io/urdego/urdego_game_service/domain/game/entity/Game.java b/src/main/java/io/urdego/urdego_game_service/domain/game/entity/Game.java index 76e45c3..0a62b4a 100644 --- a/src/main/java/io/urdego/urdego_game_service/domain/game/entity/Game.java +++ b/src/main/java/io/urdego/urdego_game_service/domain/game/entity/Game.java @@ -11,7 +11,7 @@ @Getter @Setter -@RedisHash(value = "game", timeToLive = 3600) +@RedisHash(value = "game", timeToLive = 36000) @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Game { diff --git a/src/main/java/io/urdego/urdego_game_service/domain/game/service/GameService.java b/src/main/java/io/urdego/urdego_game_service/domain/game/service/GameService.java index 2717fd7..53cdf54 100644 --- a/src/main/java/io/urdego/urdego_game_service/domain/game/service/GameService.java +++ b/src/main/java/io/urdego/urdego_game_service/domain/game/service/GameService.java @@ -4,10 +4,7 @@ import io.urdego.urdego_game_service.controller.game.dto.request.ScoreReq; import io.urdego.urdego_game_service.controller.game.dto.response.GameCreateRes; import io.urdego.urdego_game_service.controller.game.dto.response.GameEndRes; -import io.urdego.urdego_game_service.common.enums.Status; import io.urdego.urdego_game_service.controller.game.dto.response.ScoreRes; -import io.urdego.urdego_game_service.domain.game.entity.Game; -import org.springframework.transaction.annotation.Transactional; public interface GameService { // 게임 생성 @@ -18,11 +15,4 @@ public interface GameService { // 게임 종료 GameEndRes finishGame(String gameId); - - // 게임 상태 변경 - Game updateGameStatusById(String gameId, Status status); - - // 게임 정보 조회 - @Transactional(readOnly = true) - Game findGameById(String gameId); } diff --git a/src/main/java/io/urdego/urdego_game_service/domain/game/service/GameServiceImpl.java b/src/main/java/io/urdego/urdego_game_service/domain/game/service/GameServiceImpl.java index a73430f..c8a6fe6 100644 --- a/src/main/java/io/urdego/urdego_game_service/domain/game/service/GameServiceImpl.java +++ b/src/main/java/io/urdego/urdego_game_service/domain/game/service/GameServiceImpl.java @@ -56,11 +56,13 @@ public GameCreateRes createGame(GameCreateReq request) { // Question 생성 for (int roundNum = 1; roundNum <= room.getTotalRounds(); roundNum++) { + log.info("{}라운드 문제 생성 중...", roundNum); Question question = roundService.createQuestion(game.getRoomId(), roundNum); game.getQuestionIds().add(question.getQuestionId()); } gameRepository.save(game); + log.info("게임 생성 | gameId: {}, roomId: {}", game.getGameId(), game.getRoomId()); return GameCreateRes.from(game); } @@ -73,12 +75,15 @@ public ScoreRes giveScores(ScoreReq request) { String questionId = game.getQuestionIds().get(request.roundNum() - 1); List answers = roundService.findAnswersByQuestionId(questionId); + if (answers.isEmpty()) { + log.warn("점수 계산 실패 | gameId: {}, roundNum: {} | 정답 데이터가 없습니다.", request.gameId(), request.roundNum()); + } + updateRoundScores(game, request.roundNum(), answers); updateTotalScores(game); gameRepository.save(game); - - log.info("게임 점수 정보 : roundScores={}, totalScores={}", game.getRoundScores(), game.getTotalScores()); + log.info("게임 점수 정보 | roundScores: {}, totalScores: {}", game.getRoundScores(), game.getTotalScores()); return ScoreRes.from(game); } @@ -92,27 +97,18 @@ public GameEndRes finishGame(String gameId) { throw new GameException(ExceptionMessage.GAME_ALREADY_COMPLETED); } - game.setStatus(Status.COMPLETED); game.setEndedAt(Instant.now()); + log.info("게임 종료 | gameId: {}, endedAt: {}", game.getGameId(), game.getEndedAt()); - gameRepository.save(game); + updateGameStatusById(gameId, Status.COMPLETED); + Map exp = calculateExp(game.getTotalScores()); + log.info("경험치 계산 결과: {}", exp); - return GameEndRes.from(game); - } - - // 게임 상태 변경 - @Override - public Game updateGameStatusById(String gameId, Status status) { - Game game = findGameById(gameId); - game.setStatus(status); - - return gameRepository.save(game); + return GameEndRes.of(game, exp); } // 게임 정보 조회 - @Transactional(readOnly = true) - @Override - public Game findGameById(String gameId) { + private Game findGameById(String gameId) { Game game = gameRepository.findById(gameId) .orElseThrow(() -> new GameException(ExceptionMessage.GAME_NOT_FOUND)); @@ -128,6 +124,15 @@ public Game findGameById(String gameId) { return game; } + // 게임 상태 변경 + private void updateGameStatusById(String gameId, Status status) { + Game game = findGameById(gameId); + game.setStatus(status); + + gameRepository.save(game); + log.info("게임 상태 변경 | gameId: {}, Status: {}", game.getGameId(), game.getStatus()); + } + // 라운드 점수 업뎃 private void updateRoundScores(Game game, int roundNum, List answers) { Map> roundScores = game.getRoundScores(); @@ -144,6 +149,10 @@ private void updateRoundScores(Game game, int roundNum, List answers) { roundScore.put(answer.getUserId(), answer.getScore()); } + for (String player : game.getPlayers()) { + roundScore.putIfAbsent(player, 0); + } + game.setRoundScores(roundScores); log.info("{}라운드 점수가 업데이트 되었습니다. : {}", roundNum, game.getRoundScores()); @@ -163,4 +172,15 @@ private void updateTotalScores(Game game) { log.info("전체 점수가 업데이트 되었습니다. : {}", game.getTotalScores()); } + + // 경험치 계산 (점수의 0.1%) + private Map calculateExp(Map totalScores) { + Map expMap = new HashMap<>(); + totalScores.forEach((userId, score) -> { + int exp = (int) Math.ceil(score * 0.001); + expMap.put(userId, exp); + }); + + return expMap; + } } diff --git a/src/main/java/io/urdego/urdego_game_service/domain/room/entity/Room.java b/src/main/java/io/urdego/urdego_game_service/domain/room/entity/Room.java index e4f1e7c..4661d77 100644 --- a/src/main/java/io/urdego/urdego_game_service/domain/room/entity/Room.java +++ b/src/main/java/io/urdego/urdego_game_service/domain/room/entity/Room.java @@ -5,12 +5,13 @@ import org.springframework.data.annotation.Id; import org.springframework.data.redis.core.RedisHash; +import java.util.HashMap; import java.util.List; import java.util.Map; @Getter @Setter -@RedisHash(value = "room", timeToLive = 3600) +@RedisHash(value = "room", timeToLive = 36000) @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Room { @@ -20,21 +21,20 @@ public class Room { private Status status; private int maxPlayers; private int totalRounds; - private int timer; private List currentPlayers; private Map> playerContents; - // private String gameType; + private Map readyStatus = new HashMap<>(); @Builder - public Room(String roomId, String roomName, Status status, int maxPlayers, int totalRounds, int timer, List currentPlayers, Map> playerContents) { + public Room(String roomId, String roomName, Status status, int maxPlayers, int totalRounds, List currentPlayers, Map> playerContents, Map readyStatus) { this.roomId = roomId; this.roomName = roomName; this.status = status; this.maxPlayers = maxPlayers; this.totalRounds = totalRounds; - this.timer = timer; this.currentPlayers = currentPlayers; this.playerContents = playerContents; + this.readyStatus = readyStatus; } } diff --git a/src/main/java/io/urdego/urdego_game_service/domain/room/service/RoomService.java b/src/main/java/io/urdego/urdego_game_service/domain/room/service/RoomService.java index 5a0d2d7..a194933 100644 --- a/src/main/java/io/urdego/urdego_game_service/domain/room/service/RoomService.java +++ b/src/main/java/io/urdego/urdego_game_service/domain/room/service/RoomService.java @@ -3,7 +3,7 @@ import io.urdego.urdego_game_service.controller.room.dto.request.ContentSelectReq; import io.urdego.urdego_game_service.controller.room.dto.request.PlayerReq; import io.urdego.urdego_game_service.controller.room.dto.request.RoomCreateReq; -import io.urdego.urdego_game_service.controller.room.dto.response.PlayerRes; +import io.urdego.urdego_game_service.controller.room.dto.response.RoomPlayersRes; import io.urdego.urdego_game_service.controller.room.dto.response.RoomCreateRes; import io.urdego.urdego_game_service.controller.room.dto.response.RoomInfoRes; import io.urdego.urdego_game_service.common.enums.Status; @@ -20,15 +20,18 @@ public interface RoomService { // 대기방 리스트 조회 List getRoomList(); - // 방 참가 - PlayerRes joinRoom(PlayerReq request); + // 플레이어 참여 + RoomPlayersRes joinRoom(PlayerReq request); + + // 플레이어 삭제 + RoomPlayersRes removePlayer(PlayerReq request); + + // 플레이어 준비 + RoomPlayersRes readyPlayer(PlayerReq request); // 컨텐츠 등록 void registerContents(ContentSelectReq request); - // 플레이어 삭제 - PlayerRes removePlayer(PlayerReq request); - // 방 상태 변경 Room updateRoomStatusById(String roomId, Status status); diff --git a/src/main/java/io/urdego/urdego_game_service/domain/room/service/RoomServiceImpl.java b/src/main/java/io/urdego/urdego_game_service/domain/room/service/RoomServiceImpl.java index a28e9b7..1b572d9 100644 --- a/src/main/java/io/urdego/urdego_game_service/domain/room/service/RoomServiceImpl.java +++ b/src/main/java/io/urdego/urdego_game_service/domain/room/service/RoomServiceImpl.java @@ -3,7 +3,7 @@ import io.urdego.urdego_game_service.controller.room.dto.request.ContentSelectReq; import io.urdego.urdego_game_service.controller.room.dto.request.PlayerReq; import io.urdego.urdego_game_service.controller.room.dto.request.RoomCreateReq; -import io.urdego.urdego_game_service.controller.room.dto.response.PlayerRes; +import io.urdego.urdego_game_service.controller.room.dto.response.RoomPlayersRes; import io.urdego.urdego_game_service.controller.room.dto.response.RoomCreateRes; import io.urdego.urdego_game_service.controller.room.dto.response.RoomInfoRes; import io.urdego.urdego_game_service.common.enums.Status; @@ -12,20 +12,19 @@ import io.urdego.urdego_game_service.domain.room.entity.Room; import io.urdego.urdego_game_service.domain.room.repository.RoomRepository; import lombok.RequiredArgsConstructor; -import org.springframework.data.redis.core.RedisTemplate; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.*; +@Slf4j @Service @Transactional @RequiredArgsConstructor public class RoomServiceImpl implements RoomService { private final RoomRepository roomRepository; - private final RedisTemplate redisTemplate; - // 대기방 생성 @Override @@ -42,12 +41,12 @@ public RoomCreateRes createRoom(RoomCreateReq request) { .status(Status.WAITING) .maxPlayers(request.maxPlayers()) .totalRounds(request.totalRounds()) - .timer(request.timer()) .currentPlayers(currentPlayers) .playerContents(new HashMap<>()) .build(); roomRepository.save(room); + log.info("대기방 생성 | roomId: {}", room.getRoomId()); return RoomCreateRes.from(room); } @@ -64,50 +63,69 @@ public List getRoomList() { } } + log.info("대기방 조회 | {}개", roomList.size()); + return roomList; } - // 대기방 참여 + // 플레이어 참여 @Override - public PlayerRes joinRoom(PlayerReq request) { + public RoomPlayersRes joinRoom(PlayerReq request) { Room room = findRoomById(request.roomId()); + String user = request.userId().toString(); + if (room.getCurrentPlayers().size() >= room.getMaxPlayers()) { throw new RoomException(ExceptionMessage.ROOM_FULL); } - room.getCurrentPlayers().add(request.userId().toString()); + room.getCurrentPlayers().add(user); + room.getReadyStatus().put(user, false); roomRepository.save(room); + log.info("대기방 참여 | roomId: {}, currentPlayers: {}", request.roomId(), room.getCurrentPlayers()); - return PlayerRes.from(room); + return RoomPlayersRes.from(room); } - // 컨텐츠 등록 + // 플레이어 삭제 @Override - public void registerContents(ContentSelectReq request) { + public RoomPlayersRes removePlayer(PlayerReq request) { Room room = findRoomById(request.roomId()); - if (request.contentIds().size() > 5) { - throw new RoomException(ExceptionMessage.CONTENTS_OVER); + String user = request.userId().toString(); + + if (!room.getCurrentPlayers().contains(user)) { + throw new RoomException(ExceptionMessage.USER_NOT_FOUND); } + room.getCurrentPlayers().remove(user); + room.getPlayerContents().remove(user); + room.getReadyStatus().remove(user); - room.getPlayerContents().put(request.userId().toString(), request.contentIds()); roomRepository.save(room); + log.info("대기방 플레이어 삭제 | roomId: {}, currentPlayers: {}", request.roomId(), room.getCurrentPlayers()); + + return RoomPlayersRes.from(room); } - // 플레이어 삭제 + // 플레이어 준비 @Override - public PlayerRes removePlayer(PlayerReq request) { + public RoomPlayersRes readyPlayer(PlayerReq request) { Room room = findRoomById(request.roomId()); - if (!room.getCurrentPlayers().contains(request.userId().toString())) { - throw new RoomException(ExceptionMessage.USER_NOT_FOUND); - } + room.getReadyStatus().put(request.userId().toString(), request.isReady()); + roomRepository.save(room); - room.getCurrentPlayers().remove(request.userId().toString()); - room.getPlayerContents().remove(request.userId().toString()); + return RoomPlayersRes.from(room); + } - roomRepository.save(room); + // 컨텐츠 등록 + @Override + public void registerContents(ContentSelectReq request) { + Room room = findRoomById(request.roomId()); + if (request.contentIds().size() > 5) { + throw new RoomException(ExceptionMessage.CONTENTS_OVER); + } - return PlayerRes.from(room); + room.getPlayerContents().put(request.userId().toString(), request.contentIds()); + roomRepository.save(room); } // 대기방 상태 변경 @@ -116,7 +134,10 @@ public Room updateRoomStatusById(String roomId, Status status) { Room room = findRoomById(roomId); room.setStatus(status); - return roomRepository.save(room); + roomRepository.save(room); + log.info("대기방 상태 변경 | roomId: {}, Status: {}", room.getRoomId(), room.getStatus()); + + return room; } // 방 정보 조회 @@ -132,5 +153,4 @@ public Room findRoomById(String roomId) { return room; } - } diff --git a/src/main/java/io/urdego/urdego_game_service/domain/round/entity/Answer.java b/src/main/java/io/urdego/urdego_game_service/domain/round/entity/Answer.java index 6010139..4188ded 100644 --- a/src/main/java/io/urdego/urdego_game_service/domain/round/entity/Answer.java +++ b/src/main/java/io/urdego/urdego_game_service/domain/round/entity/Answer.java @@ -9,7 +9,7 @@ import org.springframework.data.redis.core.index.Indexed; @Getter -@RedisHash(value = "answer", timeToLive = 3600) +@RedisHash(value = "answer", timeToLive = 36000) @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Answer { diff --git a/src/main/java/io/urdego/urdego_game_service/domain/round/entity/Question.java b/src/main/java/io/urdego/urdego_game_service/domain/round/entity/Question.java index d0f7b74..18943ab 100644 --- a/src/main/java/io/urdego/urdego_game_service/domain/round/entity/Question.java +++ b/src/main/java/io/urdego/urdego_game_service/domain/round/entity/Question.java @@ -8,7 +8,7 @@ @Getter @Setter -@RedisHash(value = "question", timeToLive = 3600) +@RedisHash(value = "question", timeToLive = 36000) @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Question { diff --git a/src/main/java/io/urdego/urdego_game_service/domain/round/service/RoundServiceImpl.java b/src/main/java/io/urdego/urdego_game_service/domain/round/service/RoundServiceImpl.java index 8900171..9b16b94 100644 --- a/src/main/java/io/urdego/urdego_game_service/domain/round/service/RoundServiceImpl.java +++ b/src/main/java/io/urdego/urdego_game_service/domain/round/service/RoundServiceImpl.java @@ -4,8 +4,8 @@ import io.urdego.urdego_game_service.controller.round.dto.request.QuestionReq; import io.urdego.urdego_game_service.controller.round.dto.response.AnswerRes; import io.urdego.urdego_game_service.controller.round.dto.response.QuestionRes; -import io.urdego.urdego_game_service.common.client.ContentServiceClient; -import io.urdego.urdego_game_service.common.client.dto.ContentRes; +import io.urdego.urdego_game_service.common.client.content.ContentServiceClient; +import io.urdego.urdego_game_service.common.client.content.dto.ContentRes; import io.urdego.urdego_game_service.common.exception.ExceptionMessage; import io.urdego.urdego_game_service.common.exception.round.QuestionException; import io.urdego.urdego_game_service.domain.room.entity.Room; @@ -49,6 +49,7 @@ public Question createQuestion(String roomId, int roundNum) { // 총 3개 이상의 컨텐츠가 준비되지 않았으면? if (allContents.size() < 3) { int needed = 3 - allContents.size(); + log.info("사용자 제공 컨텐츠 부족 | 필요 추가 컨텐츠: {}개", needed); List serviceContents = contentServiceClient.getUrdegoContents(needed); serviceContents.forEach(content -> allContents.add(content.contentId().toString())); } @@ -81,6 +82,8 @@ public Question createQuestion(String roomId, int roundNum) { .build(); } while (isDuplicateQuestion(newQuestion, existingQuestions)); + log.info("문제 생성 | roomId: {}, roundNum: {}", roomId, roundNum); + return questionRepository.save(newQuestion); } @@ -88,6 +91,8 @@ public Question createQuestion(String roomId, int roundNum) { @Override public QuestionRes getQuestion(QuestionReq request) { Question question = findQuestionByRoomIdAndRoundNum(request.roomId(), request.roundNum()); + log.info("문제 출제 | roomId: {}, roundNum: {}", request.roomId(), request.roundNum()); + return QuestionRes.from(question); } @@ -109,6 +114,7 @@ public AnswerRes submitAnswer(AnswerReq request) { .build(); answerRepository.save(answer); + log.info("정답 저장 완료 | userId: {}, questionId: {}", answer.getUserId(), answer.getQuestionId()); return AnswerRes.from(answer); } @@ -150,7 +156,10 @@ private double calculateDistance(double lat1, double lon1, double lat2, double l + Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) * Math.sin(dLon / 2) * Math.sin(dLon / 2); double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); - return EARTH_RADIUS * c; + double distance = EARTH_RADIUS * c; + + log.info("거리 계산 | distance: {}", distance); + return distance; } // 점수 계산 @@ -162,6 +171,9 @@ private int calculateScore(double distance) { return 0; } - return (int) Math.max(0, maxScore - (distance / maxDistance) * maxScore); + int score = (int) Math.max(0, maxScore - (distance / maxDistance) * maxScore); + + log.info("점수 계산 | score: {}", score); + return score; } }