From 828e6430151f596e7d32b5f2c451ed4de31a342a Mon Sep 17 00:00:00 2001 From: timking Date: Wed, 8 Nov 2023 22:39:14 +0800 Subject: [PATCH 1/2] api start game --- .../app/repositories/GameRepository.java | 13 ++ .../citadels/app/usecases/GetGameUseCase.java | 38 ++++++ .../app/usecases/StartGameUseCase.java | 104 +++++++++++++++ .../gaas/citadels/domain/BuildCard.java | 27 ++++ .../gaas/citadels/domain/CharacterCard.java | 11 ++ .../gaas/citadels/domain/Game.java | 119 ++++++++++++++++++ .../gaas/citadels/domain/Player.java | 53 ++++++++ .../controllers/CreateRoomController.java | 1 + .../spring/controllers/GameController.java | 100 +++++++++++++++ .../viewmodel/CharacterCardView.java | 23 ++++ .../controllers/viewmodel/GameView.java | 30 +++++ .../controllers/viewmodel/GetGameView.java | 20 +++ .../controllers/viewmodel/PlayerView.java | 35 ++++++ .../controllers/viewmodel/StartGameView.java | 19 +++ .../repositories/GameRepositoryImpl.java | 32 +++++ .../spring/repositories/dao/GameDAO.java | 12 ++ .../repositories/data/BuildCardData.java | 36 ++++++ .../repositories/data/CharacterCardData.java | 43 +++++++ .../spring/repositories/data/GameData.java | 49 ++++++++ .../spring/repositories/data/PlayerData.java | 59 +++++++++ .../spring/CitadelsSpringBootTest.java | 11 +- .../controllers/CreateGameAndGetGameTest.java | 75 +++++++++++ .../spring/controllers/CreateRoomTest.java | 2 +- 23 files changed, 908 insertions(+), 4 deletions(-) create mode 100644 packages/backend/app/src/main/java/tw/waterballsa/gaas/citadels/app/repositories/GameRepository.java create mode 100644 packages/backend/app/src/main/java/tw/waterballsa/gaas/citadels/app/usecases/GetGameUseCase.java create mode 100644 packages/backend/app/src/main/java/tw/waterballsa/gaas/citadels/app/usecases/StartGameUseCase.java create mode 100644 packages/backend/domain/src/main/java/tw/waterballsa/gaas/citadels/domain/BuildCard.java create mode 100644 packages/backend/domain/src/main/java/tw/waterballsa/gaas/citadels/domain/CharacterCard.java create mode 100644 packages/backend/domain/src/main/java/tw/waterballsa/gaas/citadels/domain/Game.java create mode 100644 packages/backend/domain/src/main/java/tw/waterballsa/gaas/citadels/domain/Player.java create mode 100644 packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/GameController.java create mode 100644 packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/CharacterCardView.java create mode 100644 packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/GameView.java create mode 100644 packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/GetGameView.java create mode 100644 packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/PlayerView.java create mode 100644 packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/StartGameView.java create mode 100644 packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/GameRepositoryImpl.java create mode 100644 packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/dao/GameDAO.java create mode 100644 packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/data/BuildCardData.java create mode 100644 packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/data/CharacterCardData.java create mode 100644 packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/data/GameData.java create mode 100644 packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/data/PlayerData.java create mode 100644 packages/backend/spring/src/test/java/tw/waterballsa/gaas/citadels/spring/controllers/CreateGameAndGetGameTest.java diff --git a/packages/backend/app/src/main/java/tw/waterballsa/gaas/citadels/app/repositories/GameRepository.java b/packages/backend/app/src/main/java/tw/waterballsa/gaas/citadels/app/repositories/GameRepository.java new file mode 100644 index 00000000..66ad9e52 --- /dev/null +++ b/packages/backend/app/src/main/java/tw/waterballsa/gaas/citadels/app/repositories/GameRepository.java @@ -0,0 +1,13 @@ +package tw.waterballsa.gaas.citadels.app.repositories; + +import tw.waterballsa.gaas.citadels.domain.Game; +import tw.waterballsa.gaas.citadels.domain.Room; + +import java.util.Optional; + +public interface GameRepository { + + Game createGame(Game game); + + Optional findGameById(String gameId); +} diff --git a/packages/backend/app/src/main/java/tw/waterballsa/gaas/citadels/app/usecases/GetGameUseCase.java b/packages/backend/app/src/main/java/tw/waterballsa/gaas/citadels/app/usecases/GetGameUseCase.java new file mode 100644 index 00000000..7b97fc23 --- /dev/null +++ b/packages/backend/app/src/main/java/tw/waterballsa/gaas/citadels/app/usecases/GetGameUseCase.java @@ -0,0 +1,38 @@ +package tw.waterballsa.gaas.citadels.app.usecases; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import tw.waterballsa.gaas.citadels.app.repositories.GameRepository; +import tw.waterballsa.gaas.citadels.domain.Game; +import tw.waterballsa.gaas.citadels.exceptions.NotFoundException; + +import javax.inject.Named; +import java.util.Optional; + +@Named +@RequiredArgsConstructor +public class GetGameUseCase { + + private final GameRepository gameRepository; + + public void execute(String gameId, Presenter presenter) { + Game game = findGameById(gameId); + presenter.setGame(game); + } + + private Game findGameById(String gameId) { + Optional game = gameRepository.findGameById(gameId); + return game.orElseThrow(() -> new NotFoundException("CAN NOT FIND GAME, ID=" + gameId)); + } + + @Data + @AllArgsConstructor + public static class Request { + private String gameId; + } + + public interface Presenter { + void setGame(Game game); + } +} diff --git a/packages/backend/app/src/main/java/tw/waterballsa/gaas/citadels/app/usecases/StartGameUseCase.java b/packages/backend/app/src/main/java/tw/waterballsa/gaas/citadels/app/usecases/StartGameUseCase.java new file mode 100644 index 00000000..2a32f878 --- /dev/null +++ b/packages/backend/app/src/main/java/tw/waterballsa/gaas/citadels/app/usecases/StartGameUseCase.java @@ -0,0 +1,104 @@ +package tw.waterballsa.gaas.citadels.app.usecases; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import tw.waterballsa.gaas.citadels.app.repositories.GameRepository; +import tw.waterballsa.gaas.citadels.domain.BuildCard; +import tw.waterballsa.gaas.citadels.domain.CharacterCard; +import tw.waterballsa.gaas.citadels.domain.Game; +import tw.waterballsa.gaas.citadels.domain.Player; + +import javax.inject.Named; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + + +@Named +@RequiredArgsConstructor +public class StartGameUseCase { + + private final GameRepository gameRepository; + + public void execute(Request request, Presenter presenter) { + Game game = createGame(request); + presenter.setGame(gameRepository.createGame(game)); + } + + private Game createGame(Request request) { + List players = getPlayers(request.getPlayers()); + List characterCards = getCharacterCards(); + List buildCards = getBuildCards(); + Game game = new Game(request.getRoomId(), request.getRoomName(), players, characterCards, buildCards); + game.start(); + return game; + } + + private List getCharacterCards() { + return Arrays.asList(new CharacterCard(1, "刺客"), + new CharacterCard(2, "小偷"), + new CharacterCard(3, "魔術師"), + new CharacterCard(4, "國王"), + new CharacterCard(5, "住持"), + new CharacterCard(6, "商人"), + new CharacterCard(7, "建築師"), + new CharacterCard(8, "領主")); + } + + private List getPlayers(List playerIds) { + return playerIds.stream() + .map(user -> new Player(user.getId(), user.getName(), user.getImageName())) + .collect(Collectors.toList()); + } + + public interface Presenter { + void setGame(Game game); + } + + @Data + @Builder + @AllArgsConstructor + public static class Request { + public String roomId; + public String roomName; + public String holderId; + public List players; + + } + + @Data + @AllArgsConstructor + public static class UserRequest { + public String id; + public String name; + public String imageName; + } + + private List getBuildCards() { + List cards = new ArrayList<>(); + Stream.of( + getBuildCards(BuildCard.Color.YELLOW, 12), + getBuildCards(BuildCard.Color.BLUE, 11), + getBuildCards(BuildCard.Color.GREEN, 11), + getBuildCards(BuildCard.Color.RED, 11), + getBuildCards(BuildCard.Color.PURPLE, 30) + ) + .flatMap(Collection::stream) + .forEach(cards::add); + + return cards; + } + + private List getBuildCards(BuildCard.Color color, int count) { + return IntStream.range(0, count) + .mapToObj(i -> new BuildCard("card name", 2, color)) + .collect(Collectors.toList()); + } + +} diff --git a/packages/backend/domain/src/main/java/tw/waterballsa/gaas/citadels/domain/BuildCard.java b/packages/backend/domain/src/main/java/tw/waterballsa/gaas/citadels/domain/BuildCard.java new file mode 100644 index 00000000..7e14a71e --- /dev/null +++ b/packages/backend/domain/src/main/java/tw/waterballsa/gaas/citadels/domain/BuildCard.java @@ -0,0 +1,27 @@ +package tw.waterballsa.gaas.citadels.domain; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class BuildCard { + private String name; + private int coins; + private Color color; + + public enum Color { + PURPLE("特別地區"), + YELLOW("貴族地區"), + RED("軍事地區"), + BLUE("商業地區"), + GREEN("宗教地區"); + private final String area; + + Color(String area) { + this.area = area; + } + } + +} + diff --git a/packages/backend/domain/src/main/java/tw/waterballsa/gaas/citadels/domain/CharacterCard.java b/packages/backend/domain/src/main/java/tw/waterballsa/gaas/citadels/domain/CharacterCard.java new file mode 100644 index 00000000..0fa9d974 --- /dev/null +++ b/packages/backend/domain/src/main/java/tw/waterballsa/gaas/citadels/domain/CharacterCard.java @@ -0,0 +1,11 @@ +package tw.waterballsa.gaas.citadels.domain; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class CharacterCard { + private int sequence; + private String name; +} diff --git a/packages/backend/domain/src/main/java/tw/waterballsa/gaas/citadels/domain/Game.java b/packages/backend/domain/src/main/java/tw/waterballsa/gaas/citadels/domain/Game.java new file mode 100644 index 00000000..9123f4c1 --- /dev/null +++ b/packages/backend/domain/src/main/java/tw/waterballsa/gaas/citadels/domain/Game.java @@ -0,0 +1,119 @@ +package tw.waterballsa.gaas.citadels.domain; + +import lombok.Getter; + +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static java.util.UUID.randomUUID; +import static tw.waterballsa.gaas.citadels.domain.Game.Status.PLAYING; + + +@Getter +public class Game { + private final String id; + private final String roomId; + private final String name; + private final Status status; + private final LocalDateTime createTime; + private final List players; + private final List characterCards; + private final List buildCards; + public static final Integer DEFAULT_COINS = 2; + public static final Integer DEFAULT_CARD_QUANTITY = 2; + + public Game(String roomId, String name, List players, List characterCards, List buildCards) { + this.id = randomUUID().toString(); + this.roomId = roomId; + this.name = name; + this.createTime = LocalDateTime.now(); + this.players = players; + this.status = PLAYING; + this.characterCards = characterCards; + this.buildCards = buildCards; + } + + public Game(String id, String roomId, String name, Status status, + LocalDateTime createTime, List players, List characterCards, + List buildCards) { + this.id = id; + this.roomId = roomId; + this.name = name; + this.status = status; + this.createTime = createTime; + this.players = players; + this.characterCards = characterCards; + this.buildCards = buildCards; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public String getRoomId() { + return roomId; + } + + public Status getStatus() { + return status; + } + + public List getPlayers() { + return players; + } + + + public LocalDateTime getCreateTime() { + return createTime; + } + + public void randomlyAwardCrownToOnePlayer() { + Collections.shuffle(players); + Player kingPlayer = players.get(0); + kingPlayer.acquireCrown(); + } + + public void start() { + randomlyAwardCrownToOnePlayer(); + distributingCardsAndCoinsToEachPlayer(); + } + + private void distributingCardsAndCoinsToEachPlayer() { + players.forEach(player -> { + player.plusCards(getTwoCards()); + player.plusCoins(2); + }); + + } + + public List getTwoCards(){ + return Arrays.asList(new BuildCard("test",3, BuildCard.Color.BLUE) + ,new BuildCard("test",2, BuildCard.Color.BLUE)); + } + + public enum Status { + PLAYING, + CLOSE + } + + @Override + public String toString() { + return "Game{" + + "id='" + id + '\'' + + ", roomId='" + roomId + '\'' + + ", name='" + name + '\'' + + ", status=" + status + + ", createTime=" + createTime + + ", players=" + players + + ", districtCards=" + buildCards + + '}'; + } +} + + diff --git a/packages/backend/domain/src/main/java/tw/waterballsa/gaas/citadels/domain/Player.java b/packages/backend/domain/src/main/java/tw/waterballsa/gaas/citadels/domain/Player.java new file mode 100644 index 00000000..3565eb49 --- /dev/null +++ b/packages/backend/domain/src/main/java/tw/waterballsa/gaas/citadels/domain/Player.java @@ -0,0 +1,53 @@ +package tw.waterballsa.gaas.citadels.domain; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import java.util.*; + + +@Getter +@AllArgsConstructor +public class Player { + private String id; + private String name; + private String imageName; + private Integer coins; + private List buildCards; + private CharacterCard characterCard; + private Boolean hasCrown; + static final int DEFAULT_COIN = 0; + + public Player(String id, String name, String image) { + this(id, name, image, DEFAULT_COIN, new ArrayList<>(), null, null); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + Player user = (Player) obj; + return Objects.equals(id, user.id) && + Objects.equals(name, user.name) && + Objects.equals(imageName, user.imageName); + } + + public void removeCrown() { + hasCrown = false; + } + + public void acquireCrown() { + hasCrown = true; + } + + public void plusCoins(Integer coins) { + this.coins = this.coins + coins; + } + + public void plusCards(List buildCardList) { + buildCards.addAll(buildCardList); + } +} \ No newline at end of file diff --git a/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/CreateRoomController.java b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/CreateRoomController.java index 73781b48..b30274e9 100644 --- a/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/CreateRoomController.java +++ b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/CreateRoomController.java @@ -21,6 +21,7 @@ @AllArgsConstructor @RequestMapping(value = "/api/citadels") public class CreateRoomController { + private final CreateRoomUseCase createRoomUseCase; @PostMapping("/room") diff --git a/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/GameController.java b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/GameController.java new file mode 100644 index 00000000..10b824f2 --- /dev/null +++ b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/GameController.java @@ -0,0 +1,100 @@ +package tw.waterballsa.gaas.citadels.spring.controllers; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.Value; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import tw.waterballsa.gaas.citadels.app.usecases.GetGameUseCase; +import tw.waterballsa.gaas.citadels.app.usecases.StartGameUseCase; +import tw.waterballsa.gaas.citadels.domain.Game; +import tw.waterballsa.gaas.citadels.spring.controllers.viewmodel.GetGameView; +import tw.waterballsa.gaas.citadels.spring.controllers.viewmodel.StartGameView; + +import javax.validation.constraints.NotNull; +import java.util.ArrayList; +import java.util.List; + +import static org.springframework.http.HttpStatus.OK; +import static org.springframework.http.ResponseEntity.status; +import static tw.waterballsa.gaas.citadels.app.usecases.StartGameUseCase.Presenter; +import static tw.waterballsa.gaas.citadels.app.usecases.StartGameUseCase.Request; +import static tw.waterballsa.gaas.citadels.spring.controllers.viewmodel.StartGameView.toViewModel; + +@RestController +@AllArgsConstructor +@RequestMapping(value = "/api/citadels") +public class GameController { + + private final StartGameUseCase startGameUseCase; + + private final GetGameUseCase getGameUseCase; + + @PostMapping("/games") + public ResponseEntity startGame(@RequestBody StartGameRequest request) { + StartGamePresenter presenter = new StartGamePresenter(); + startGameUseCase.execute(request.toRequest(), presenter); + return status(OK).body(presenter.getStartGameView()); + } + + @GetMapping("/games/{gameId}") + public ResponseEntity getGame(@PathVariable String gameId) { + GetGamePresenter presenter = new GetGamePresenter(); + getGameUseCase.execute(gameId, presenter); + return status(OK).body(presenter.getGameView()); + } + + + class GetGamePresenter implements GetGameUseCase.Presenter { + private Game game; + + public GetGameView getGameView() { + return GetGameView.toViewModel(game); + } + + @Override + public void setGame(Game game) { + this.game = game; + } + } + + + class StartGamePresenter implements Presenter { + private Game game; + + @Override + public void setGame(Game game) { + this.game = game; + } + + public StartGameView getStartGameView() { + return toViewModel(game); + } + } + + @AllArgsConstructor + @Value + static class StartGameRequest { + @NotNull(message = "The roomId must not be null.") + String roomId; + @NotNull(message = "The roomRoom must not be null.") + String roomName; + @NotNull(message = "The holderId must not be null.") + String holderId; + List players; + + public Request toRequest() { + List userRequests = new ArrayList<>(); + players.forEach(player -> userRequests.add(new StartGameUseCase.UserRequest(player.getId(), player.name, player.getImageName()))); + return new Request(roomId, roomName, holderId, userRequests); + } + } + + @Data + @AllArgsConstructor + public static class UserRequest { + public String id; + public String name; + public String imageName; + } +} diff --git a/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/CharacterCardView.java b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/CharacterCardView.java new file mode 100644 index 00000000..2d1a5d00 --- /dev/null +++ b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/CharacterCardView.java @@ -0,0 +1,23 @@ +package tw.waterballsa.gaas.citadels.spring.controllers.viewmodel; + +import lombok.AllArgsConstructor; +import lombok.Data; +import tw.waterballsa.gaas.citadels.domain.CharacterCard; + +import java.util.List; +import java.util.stream.Collectors; + +@Data +@AllArgsConstructor +public class CharacterCardView { + private int sequence; + private String name; + + public static CharacterCardView toViewModel(CharacterCard characterCard) { + return (characterCard != null) ? new CharacterCardView(characterCard.getSequence(), characterCard.getName()) : null; + } + + public static List toViewModels(List characterCards) { + return characterCards.stream().map(CharacterCardView::toViewModel).collect(Collectors.toList()); + } +} diff --git a/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/GameView.java b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/GameView.java new file mode 100644 index 00000000..12bf07d1 --- /dev/null +++ b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/GameView.java @@ -0,0 +1,30 @@ +package tw.waterballsa.gaas.citadels.spring.controllers.viewmodel; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import tw.waterballsa.gaas.citadels.domain.*; + +import java.time.LocalDateTime; +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class GameView { + private String id; + private String roomId; + private String name; + private Game.Status status; + private LocalDateTime createTime; + private List playerViews; + private List characterCardViews; + private List buildCardViews; + + public static GameView toViewModel(Game game) { + List playerViews = PlayerView.toViewModels(game.getPlayers()); + List characterCardViews = CharacterCardView.toViewModels(game.getCharacterCards()); + List buildCardViews = BuildCardView.toViewModels(game.getBuildCards()); + return new GameView(game.getId(), game.getRoomId(), game.getName(),game.getStatus(),game.getCreateTime(),playerViews,characterCardViews,buildCardViews); + } +} \ No newline at end of file diff --git a/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/GetGameView.java b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/GetGameView.java new file mode 100644 index 00000000..b111fe90 --- /dev/null +++ b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/GetGameView.java @@ -0,0 +1,20 @@ +package tw.waterballsa.gaas.citadels.spring.controllers.viewmodel; + +import lombok.AllArgsConstructor; +import lombok.Data; +import tw.waterballsa.gaas.citadels.domain.Game; + +import java.time.LocalDateTime; + +@Data +@AllArgsConstructor +public class GetGameView { + private String createTime; + private String status; + private String msg; + private GameView gameView; + + public static GetGameView toViewModel(Game game) { + return new GetGameView(LocalDateTime.now().toString(), "OK", "",GameView.toViewModel(game)); + } +} diff --git a/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/PlayerView.java b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/PlayerView.java new file mode 100644 index 00000000..5f8e7b4b --- /dev/null +++ b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/PlayerView.java @@ -0,0 +1,35 @@ +package tw.waterballsa.gaas.citadels.spring.controllers.viewmodel; + +import lombok.AllArgsConstructor; +import lombok.Data; +import tw.waterballsa.gaas.citadels.domain.BuildCard; +import tw.waterballsa.gaas.citadels.domain.CharacterCard; +import tw.waterballsa.gaas.citadels.domain.Game; +import tw.waterballsa.gaas.citadels.domain.Player; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.stream.Collectors; + +@Data +@AllArgsConstructor +public class PlayerView { + private String id; + private String name; + private String imageName; + private Integer coins; + private List buildCards; + private CharacterCardView characterCard; + private Boolean hasCrown; + + public static List toViewModels(List players) { + return players.stream().map(PlayerView::toViewModel).collect(Collectors.toList()); + } + + + public static PlayerView toViewModel(Player player) { + List buildCardViews = BuildCardView.toViewModels(player.getBuildCards()); + CharacterCardView characterCardView = CharacterCardView.toViewModel(player.getCharacterCard()); + return new PlayerView(player.getId(),player.getName(),player.getImageName(),player.getCoins(),buildCardViews,characterCardView,player.getHasCrown()); + } +} diff --git a/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/StartGameView.java b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/StartGameView.java new file mode 100644 index 00000000..82a8686b --- /dev/null +++ b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/StartGameView.java @@ -0,0 +1,19 @@ +package tw.waterballsa.gaas.citadels.spring.controllers.viewmodel; + +import lombok.AllArgsConstructor; +import lombok.Data; +import tw.waterballsa.gaas.citadels.domain.Game; +import java.time.LocalDateTime; + +@Data +@AllArgsConstructor +public class StartGameView { + private String createTime; + private String status; + private String msg; + private String gameId; + + public static StartGameView toViewModel(Game game) { + return new StartGameView(LocalDateTime.now().toString(), "OK", "", game.getId()); + } +} diff --git a/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/GameRepositoryImpl.java b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/GameRepositoryImpl.java new file mode 100644 index 00000000..5bf63420 --- /dev/null +++ b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/GameRepositoryImpl.java @@ -0,0 +1,32 @@ +package tw.waterballsa.gaas.citadels.spring.repositories; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; +import tw.waterballsa.gaas.citadels.app.repositories.GameRepository; +import tw.waterballsa.gaas.citadels.app.repositories.RoomRepository; +import tw.waterballsa.gaas.citadels.domain.Game; +import tw.waterballsa.gaas.citadels.domain.Room; +import tw.waterballsa.gaas.citadels.spring.repositories.dao.GameDAO; +import tw.waterballsa.gaas.citadels.spring.repositories.dao.RoomDAO; +import tw.waterballsa.gaas.citadels.spring.repositories.data.GameData; +import tw.waterballsa.gaas.citadels.spring.repositories.data.RoomData; + +import java.util.Optional; + +@Repository +@RequiredArgsConstructor +public class GameRepositoryImpl implements GameRepository { + + private final GameDAO gameDAO; + + @Override + public Game createGame(Game game) { + GameData data = GameData.toData(game); + return gameDAO.save(data).toDomain(); + } + + @Override + public Optional findGameById(String gameId) { + return gameDAO.findById(gameId).map(GameData::toDomain); + } +} diff --git a/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/dao/GameDAO.java b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/dao/GameDAO.java new file mode 100644 index 00000000..3a85526d --- /dev/null +++ b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/dao/GameDAO.java @@ -0,0 +1,12 @@ +package tw.waterballsa.gaas.citadels.spring.repositories.dao; + +import org.springframework.data.mongodb.repository.MongoRepository; +import org.springframework.stereotype.Repository; +import tw.waterballsa.gaas.citadels.domain.Game; +import tw.waterballsa.gaas.citadels.spring.repositories.data.GameData; +import tw.waterballsa.gaas.citadels.spring.repositories.data.RoomData; + +@Repository +public interface GameDAO extends MongoRepository { + +} diff --git a/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/data/BuildCardData.java b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/data/BuildCardData.java new file mode 100644 index 00000000..df384644 --- /dev/null +++ b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/data/BuildCardData.java @@ -0,0 +1,36 @@ +package tw.waterballsa.gaas.citadels.spring.repositories.data; + +import lombok.AllArgsConstructor; +import lombok.Data; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; +import tw.waterballsa.gaas.citadels.domain.BuildCard; + +import java.util.List; +import java.util.stream.Collectors; + +@Data +@Document +@AllArgsConstructor +public class BuildCardData { + @Id + private String name; + private int coins; + private BuildCard.Color color; + + public static BuildCardData toData(BuildCard buildCard) { + return new BuildCardData(buildCard.getName(), buildCard.getCoins(), buildCard.getColor()); + } + + public static List toData(List buildCards) { + return buildCards.stream().map(BuildCardData::toData).collect(Collectors.toList()); + } + + public static BuildCard toDomain(BuildCardData buildCardData) { + return new BuildCard(buildCardData.getName(), buildCardData.getCoins(), buildCardData.getColor()); + } + + public static List toDomains(List buildCardDataList) { + return buildCardDataList.stream().map(BuildCardData::toDomain).collect(Collectors.toList()); + } +} diff --git a/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/data/CharacterCardData.java b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/data/CharacterCardData.java new file mode 100644 index 00000000..827bcc24 --- /dev/null +++ b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/data/CharacterCardData.java @@ -0,0 +1,43 @@ +package tw.waterballsa.gaas.citadels.spring.repositories.data; + +import lombok.AllArgsConstructor; +import lombok.Data; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; +import tw.waterballsa.gaas.citadels.domain.CharacterCard; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Data +@Document +@AllArgsConstructor +public class CharacterCardData { + @Id + private int sequence; + private String name; + + public static CharacterCardData toData(CharacterCard characterCard) { + return (characterCard != null) ? new CharacterCardData(characterCard.getSequence(), characterCard.getName()) : null; + } + + public static List toData(List characterCards) { + return characterCards.stream().map(CharacterCardData::toData).collect(Collectors.toList()); + } + + public static CharacterCard toDomain(CharacterCardData characterCardData) { + return characterCardData != null ? new CharacterCard(characterCardData.getSequence(), characterCardData.getName()) : null; + } + + public static Map toDomains(List characterCardDataList) { + Map cards = new LinkedHashMap<>(); + characterCardDataList.forEach(characterCardData -> cards.put(characterCardData.getSequence(), toDomain(characterCardData))); + return cards; + } + + public static List toDomainList(List characterCardDataList) { + return characterCardDataList.stream().map(CharacterCardData::toDomain).collect(Collectors.toList()); + } +} diff --git a/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/data/GameData.java b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/data/GameData.java new file mode 100644 index 00000000..d3c48230 --- /dev/null +++ b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/data/GameData.java @@ -0,0 +1,49 @@ +package tw.waterballsa.gaas.citadels.spring.repositories.data; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; +import tw.waterballsa.gaas.citadels.domain.Game; + +import java.time.LocalDateTime; +import java.util.List; + + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Document("game") +public class GameData { + @Id + private String id; + private String roomId; + private String name; + private LocalDateTime createTime; + private Game.Status status; + private List players; + private String kingPlayerId; + private List characterCards; + private List buildCards; + + public static GameData toData(Game game) { + return GameData.builder().id(game.getId()) + .roomId(game.getRoomId()) + .name(game.getName()) + .players(PlayerData.toData(game.getPlayers())) + .status(game.getStatus()) + .characterCards(CharacterCardData.toData(game.getCharacterCards())) + .buildCards(BuildCardData.toData(game.getBuildCards())) + .createTime(game.getCreateTime()) + .build(); + } + + public Game toDomain() { + return new Game(id, roomId, name, status, createTime, PlayerData.toDomainList(players), + CharacterCardData.toDomainList(characterCards), BuildCardData.toDomains(buildCards)); + } + +} diff --git a/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/data/PlayerData.java b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/data/PlayerData.java new file mode 100644 index 00000000..5c2329b7 --- /dev/null +++ b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/data/PlayerData.java @@ -0,0 +1,59 @@ +package tw.waterballsa.gaas.citadels.spring.repositories.data; + +import lombok.AllArgsConstructor; +import lombok.Data; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; +import tw.waterballsa.gaas.citadels.domain.BuildCard; +import tw.waterballsa.gaas.citadels.domain.CharacterCard; +import tw.waterballsa.gaas.citadels.domain.Player; + +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Data +@Document +@AllArgsConstructor +public class PlayerData { + @Id + private String id; + private String name; + private String imageName; + private Integer coins; + private List buildCardDataList; + private CharacterCardData characterCardData; + private Boolean hasCrown; + + public static PlayerData toData(Player player) { + return new PlayerData(player.getId(), player.getName(), player.getImageName(), player.getCoins(), + BuildCardData.toData(player.getBuildCards()), CharacterCardData.toData(player.getCharacterCard()), player.getHasCrown()); + } + + public static List toData(List players) { + return players.stream().map(PlayerData::toData).collect(Collectors.toList()); + } + + public static Player toDomain(PlayerData playerData) { + List buildCards = BuildCardData.toDomains(playerData.getBuildCardDataList()); + CharacterCard characterCard = CharacterCardData.toDomain(playerData.getCharacterCardData()); + return new Player(playerData.getId(), playerData.getName(), playerData.getImageName(), playerData.getCoins(), buildCards, characterCard, playerData.getHasCrown()); + } + + public static List toDomainList(List playersDataList) { + return playersDataList.stream().map(PlayerData::toDomain).collect(Collectors.toList()); + } + + public static Map toDomains(PlayerData... playersData) { + return toDomains(Arrays.asList(playersData)); + } + + public static Map toDomains(List playersData) { + Map players = new LinkedHashMap<>(); + playersData.forEach(playerData -> players.put(playerData.getId(), toDomain(playerData))); + return players; + } + +} diff --git a/packages/backend/spring/src/test/java/tw/waterballsa/gaas/citadels/spring/CitadelsSpringBootTest.java b/packages/backend/spring/src/test/java/tw/waterballsa/gaas/citadels/spring/CitadelsSpringBootTest.java index 3cac8881..d6fabb1d 100644 --- a/packages/backend/spring/src/test/java/tw/waterballsa/gaas/citadels/spring/CitadelsSpringBootTest.java +++ b/packages/backend/spring/src/test/java/tw/waterballsa/gaas/citadels/spring/CitadelsSpringBootTest.java @@ -16,14 +16,20 @@ import java.util.stream.Collectors; import java.util.stream.Stream; + @SpringBootTest @AutoConfigureMockMvc @ActiveProfiles("test") public abstract class CitadelsSpringBootTest { + @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") @Autowired protected MockMvc mockMvc; @Autowired - protected tw.waterballsa.gaas.citadels.spring.repositories.dao.RoomDAO RoomDAO; + protected tw.waterballsa.gaas.citadels.spring.repositories.dao.RoomDAO roomDAO; + @Autowired + protected tw.waterballsa.gaas.citadels.spring.repositories.dao.GameDAO gameDAO; + + @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") @Autowired protected ObjectMapper objectMapper; @Autowired @@ -71,7 +77,6 @@ protected Room givenRoomStarted(String name, String holderId, User... users) { } protected Room findRoomById(String roomId) { - Room room = roomRepository.findRoomById(roomId).orElseThrow(() -> new NotFoundException("CAN NOT FIND ROOM ID=" + roomId)); - return room; + return roomRepository.findRoomById(roomId).orElseThrow(() -> new NotFoundException("CAN NOT FIND ROOM ID=" + roomId)); } } diff --git a/packages/backend/spring/src/test/java/tw/waterballsa/gaas/citadels/spring/controllers/CreateGameAndGetGameTest.java b/packages/backend/spring/src/test/java/tw/waterballsa/gaas/citadels/spring/controllers/CreateGameAndGetGameTest.java new file mode 100644 index 00000000..1d9f0d21 --- /dev/null +++ b/packages/backend/spring/src/test/java/tw/waterballsa/gaas/citadels/spring/controllers/CreateGameAndGetGameTest.java @@ -0,0 +1,75 @@ +package tw.waterballsa.gaas.citadels.spring.controllers; + +import lombok.SneakyThrows; +import org.junit.jupiter.api.Test; +import org.springframework.test.web.servlet.ResultActions; +import tw.waterballsa.gaas.citadels.spring.CitadelsSpringBootTest; +import tw.waterballsa.gaas.citadels.spring.controllers.viewmodel.GameView; +import tw.waterballsa.gaas.citadels.spring.controllers.viewmodel.GetGameView; +import tw.waterballsa.gaas.citadels.spring.controllers.viewmodel.PlayerView; +import tw.waterballsa.gaas.citadels.spring.controllers.viewmodel.StartGameView; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.springframework.http.MediaType.APPLICATION_JSON; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static tw.waterballsa.gaas.citadels.domain.Game.DEFAULT_CARD_QUANTITY; +import static tw.waterballsa.gaas.citadels.domain.Game.DEFAULT_COINS; +import static tw.waterballsa.gaas.citadels.domain.Game.Status.PLAYING; + +public class CreateGameAndGetGameTest extends CitadelsSpringBootTest { + + @Test + public void whenRoomReady_ShouldGetStartGameSuccessfully() { + GameController.StartGameRequest request = getTestGame(); + StartGameView startGameView = createGame(request); + + GetGameView getGameViewResponse = getGame(startGameView.getGameId()); + GameView gameView = getGameViewResponse.getGameView(); + + assertNotNull(gameView); + assertEquals(PLAYING,gameView.getStatus()); + assertEquals(request.getRoomId(), gameView.getRoomId()); + assertEquals(startGameView.getGameId(), gameView.getId()); + List playerViews = gameView.getPlayerViews(); + assertEquals(request.getPlayers().size(), playerViews.size()); + playerViews.forEach(player -> { + assertEquals(DEFAULT_COINS, player.getCoins()); + assertEquals(DEFAULT_CARD_QUANTITY, player.getBuildCards().size()); + }); + } + + + @SneakyThrows + private GetGameView getGame(String gameId) { + ResultActions mvcResult = mockMvc.perform(get(API_PREFIX + "/games/{gameId}", gameId)) + .andExpect(status().isOk()); + return getBody(mvcResult, GetGameView.class); + } + + + private GameController.StartGameRequest getTestGame() { + List players = Arrays.asList( + new GameController.UserRequest("1", "p1", "1"), + new GameController.UserRequest("2", "p2", "2"), + new GameController.UserRequest("3", "p3", "3"), + new GameController.UserRequest("4", "p4", "4"), + new GameController.UserRequest("5", "p5", "5") + ); + return new GameController.StartGameRequest("1", "roomA", "1", players); + } + + @SneakyThrows + private StartGameView createGame(GameController.StartGameRequest startGameRequest) { + ResultActions resultActions = mockMvc.perform(post(API_PREFIX + "/games") + .contentType(APPLICATION_JSON) + .content(toJson(startGameRequest))) + .andExpect(status().isOk()); + return getBody(resultActions, StartGameView.class); + } +} diff --git a/packages/backend/spring/src/test/java/tw/waterballsa/gaas/citadels/spring/controllers/CreateRoomTest.java b/packages/backend/spring/src/test/java/tw/waterballsa/gaas/citadels/spring/controllers/CreateRoomTest.java index 5ca0ff6d..3aadccee 100644 --- a/packages/backend/spring/src/test/java/tw/waterballsa/gaas/citadels/spring/controllers/CreateRoomTest.java +++ b/packages/backend/spring/src/test/java/tw/waterballsa/gaas/citadels/spring/controllers/CreateRoomTest.java @@ -22,7 +22,7 @@ public class CreateRoomTest extends CitadelsSpringBootTest { public void whenUserACreateRoom_ShouldCreateSuccessfully() { CreateRoomController.CreateRoomRequest createRoomRequest = new CreateRoomController.CreateRoomRequest("RoomA", "userA", "user.png"); CreateRoomView createRoomView = getBody(createRoom(createRoomRequest), CreateRoomView.class); - Optional roomData = RoomDAO.findById(createRoomView.getRoom().getRoomId()); + Optional roomData = roomDAO.findById(createRoomView.getRoom().getRoomId()); assertTrue(roomData.isPresent()); RoomData room = roomData.get(); From 15100e1c5277f0c9d47d7594c62781d1308c251c Mon Sep 17 00:00:00 2001 From: timking Date: Wed, 8 Nov 2023 22:39:25 +0800 Subject: [PATCH 2/2] api start game --- .../controllers/viewmodel/BuildCardView.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/BuildCardView.java diff --git a/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/BuildCardView.java b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/BuildCardView.java new file mode 100644 index 00000000..8ee10753 --- /dev/null +++ b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/BuildCardView.java @@ -0,0 +1,25 @@ +package tw.waterballsa.gaas.citadels.spring.controllers.viewmodel; + +import lombok.AllArgsConstructor; +import lombok.Data; +import tw.waterballsa.gaas.citadels.domain.BuildCard; + +import java.util.List; +import java.util.stream.Collectors; + +@Data +@AllArgsConstructor +public class BuildCardView { + private String name; + private int coins; + private BuildCard.Color color; + + public static List toViewModels(List buildCards) { + return buildCards.stream().map(BuildCardView::toViewModel).collect(Collectors.toList()); + } + + public static BuildCardView toViewModel(BuildCard buildCard) { + return new BuildCardView(buildCard.getName(), buildCard.getCoins(), buildCard.getColor()); + } + +}