From 21bc9f56a325e3f42a7edb840cefdb6c07386c81 Mon Sep 17 00:00:00 2001 From: KazeNoYumeX Date: Wed, 20 Mar 2024 23:08:44 +0800 Subject: [PATCH 01/10] Feat: Added the necessary fields to create a game and added the deck_cards table --- .../migrations/000001_create_games_table.up.sql | 8 ++++---- .../migrations/000002_create_players_table.up.sql | 3 ++- .../migrations/000003_create_cards_table.up.sql | 13 +++++++------ .../migrations/000004_create_decks_table.down.sql | 1 + .../migrations/000004_create_decks_table.up.sql | 14 +++++++++++++- .../000005_create_player_cards_table.up.sql | 2 -- ...7_add_intelligence_type_to_cards_table.down.sql | 5 ----- ...007_add_intelligence_type_to_cards_table.up.sql | 5 ----- Backend/service/delivery/http/v1/game_handler.go | 4 +--- Backend/service/repository/card_repository.go | 1 - Backend/service/repository/deck_repository.go | 14 +++++++++++--- Backend/service/repository/game_repository.go | 5 +++-- .../service/repository/player_card_repository.go | 12 +++++------- Backend/service/repository/player_repository.go | 7 ++++--- Backend/service/service/game_service.go | 2 +- Backend/service/service/player_service.go | 3 +-- 16 files changed, 53 insertions(+), 46 deletions(-) delete mode 100644 Backend/database/migrations/000007_add_intelligence_type_to_cards_table.down.sql delete mode 100644 Backend/database/migrations/000007_add_intelligence_type_to_cards_table.up.sql diff --git a/Backend/database/migrations/000001_create_games_table.up.sql b/Backend/database/migrations/000001_create_games_table.up.sql index 7d8ab74..85165cd 100644 --- a/Backend/database/migrations/000001_create_games_table.up.sql +++ b/Backend/database/migrations/000001_create_games_table.up.sql @@ -3,11 +3,11 @@ BEGIN; CREATE TABLE games ( id INT AUTO_INCREMENT PRIMARY KEY, - token LONGTEXT NOT NULL, - status VARCHAR(10) NOT NULL, + room_id VARCHAR(100) NOT NULL, + status VARCHAR(10) NOT NULL, current_player_id INT, - created_at DATETIME NOT NULL, - updated_at DATETIME NOT NULL, + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, deleted_at DATETIME ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/Backend/database/migrations/000002_create_players_table.up.sql b/Backend/database/migrations/000002_create_players_table.up.sql index 3566df0..2596495 100644 --- a/Backend/database/migrations/000002_create_players_table.up.sql +++ b/Backend/database/migrations/000002_create_players_table.up.sql @@ -4,8 +4,9 @@ CREATE TABLE players ( id INT AUTO_INCREMENT PRIMARY KEY, game_id INT NOT NULL, + user_id VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, - order_number INT NOT NULL, + priority TINYINT NOT NULL, identity_card VARCHAR(255) NOT NULL, status VARCHAR(10) NOT NULL, created_at DATETIME NOT NULL, diff --git a/Backend/database/migrations/000003_create_cards_table.up.sql b/Backend/database/migrations/000003_create_cards_table.up.sql index 9ce71a1..18e1d19 100644 --- a/Backend/database/migrations/000003_create_cards_table.up.sql +++ b/Backend/database/migrations/000003_create_cards_table.up.sql @@ -2,12 +2,13 @@ BEGIN; CREATE TABLE cards ( - id INT AUTO_INCREMENT PRIMARY KEY, - name VARCHAR(255) NOT NULL, - color VARCHAR(255) NOT NULL, - created_at DATETIME NOT NULL, - updated_at DATETIME NOT NULL, - deleted_at DATETIME + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(255) NOT NULL, + color VARCHAR(255) NOT NULL, + intelligence_type TINYINT COMMENT '1 密電, 2 直達, 3 文件', + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, + deleted_at DATETIME ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; COMMIT; diff --git a/Backend/database/migrations/000004_create_decks_table.down.sql b/Backend/database/migrations/000004_create_decks_table.down.sql index bd69e7b..afff84f 100644 --- a/Backend/database/migrations/000004_create_decks_table.down.sql +++ b/Backend/database/migrations/000004_create_decks_table.down.sql @@ -1,5 +1,6 @@ BEGIN; +DROP TABLE IF EXISTS deck_cards; DROP TABLE IF EXISTS decks; COMMIT; diff --git a/Backend/database/migrations/000004_create_decks_table.up.sql b/Backend/database/migrations/000004_create_decks_table.up.sql index 3179477..3bd3078 100644 --- a/Backend/database/migrations/000004_create_decks_table.up.sql +++ b/Backend/database/migrations/000004_create_decks_table.up.sql @@ -4,10 +4,22 @@ CREATE TABLE decks ( id INT AUTO_INCREMENT PRIMARY KEY, game_id INT NOT NULL, + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, + deleted_at DATETIME, + FOREIGN KEY (game_id) REFERENCES games (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +CREATE TABLE deck_cards +( + id INT AUTO_INCREMENT PRIMARY KEY, + deck_id INT NOT NULL, card_id INT NOT NULL, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, - deleted_at DATETIME + deleted_at DATETIME, + FOREIGN KEY (deck_id) REFERENCES decks (id), + FOREIGN KEY (card_id) REFERENCES cards (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; COMMIT; diff --git a/Backend/database/migrations/000005_create_player_cards_table.up.sql b/Backend/database/migrations/000005_create_player_cards_table.up.sql index f5f6ef1..0ae9e59 100644 --- a/Backend/database/migrations/000005_create_player_cards_table.up.sql +++ b/Backend/database/migrations/000005_create_player_cards_table.up.sql @@ -4,14 +4,12 @@ CREATE TABLE player_cards ( id INT AUTO_INCREMENT PRIMARY KEY, player_id INT NOT NULL, - game_id INT NOT NULL, card_id INT NOT NULL, type VARCHAR(255) NOT NULL, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, deleted_at DATETIME, FOREIGN KEY (player_id) REFERENCES players (id), - FOREIGN KEY (game_id) REFERENCES games (id), FOREIGN KEY (card_id) REFERENCES cards (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/Backend/database/migrations/000007_add_intelligence_type_to_cards_table.down.sql b/Backend/database/migrations/000007_add_intelligence_type_to_cards_table.down.sql deleted file mode 100644 index 56a1574..0000000 --- a/Backend/database/migrations/000007_add_intelligence_type_to_cards_table.down.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -ALTER TABLE cards DROP COLUMN intelligence_type; - -COMMIT; diff --git a/Backend/database/migrations/000007_add_intelligence_type_to_cards_table.up.sql b/Backend/database/migrations/000007_add_intelligence_type_to_cards_table.up.sql deleted file mode 100644 index 0c9aea3..0000000 --- a/Backend/database/migrations/000007_add_intelligence_type_to_cards_table.up.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -ALTER TABLE cards ADD COLUMN intelligence_type TINYINT COMMENT '1 密電, 2 直達, 3 文件' AFTER color; - -COMMIT; diff --git a/Backend/service/delivery/http/v1/game_handler.go b/Backend/service/delivery/http/v1/game_handler.go index 91d9cb2..18cfedb 100644 --- a/Backend/service/delivery/http/v1/game_handler.go +++ b/Backend/service/delivery/http/v1/game_handler.go @@ -91,7 +91,6 @@ func (g *GameHandler) StartGame(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "Id": game.Id, - "Token": game.Token, }) } @@ -106,8 +105,7 @@ func (g *GameHandler) GetGame(c *gin.Context) { } c.JSON(http.StatusOK, gin.H{ - "Id": game.Id, - "Token": game.Token, + "Id": game.ID, }) } diff --git a/Backend/service/repository/card_repository.go b/Backend/service/repository/card_repository.go index ab174b3..d7ccaba 100644 --- a/Backend/service/repository/card_repository.go +++ b/Backend/service/repository/card_repository.go @@ -13,7 +13,6 @@ type Card struct { Name string Color string IntelligenceType int - PlayerCards []PlayerCard CreatedAt time.Time `gorm:"autoCreateTime"` UpdatedAt time.Time `gorm:"autoCreateTime"` DeletedAt gorm.DeletedAt diff --git a/Backend/service/repository/deck_repository.go b/Backend/service/repository/deck_repository.go index 580c249..87702d0 100644 --- a/Backend/service/repository/deck_repository.go +++ b/Backend/service/repository/deck_repository.go @@ -9,12 +9,20 @@ import ( type Deck struct { gorm.Model - Id int `gorm:"primaryKey;auto_increment"` - GameId int - CardId int CreatedAt time.Time `gorm:"autoCreateTime"` UpdatedAt time.Time `gorm:"autoCreateTime"` DeletedAt gorm.DeletedAt + GameId uint + Cards []DeckCard + Game Game `gorm:"foreignKey:GameId"` +} + +type DeckCard struct { + gorm.Model + CardId uint + DeckId uint + Deck Deck `gorm:"foreignKey:DeckId"` + Card Card `gorm:"foreignKey:CardId"` } type DeckRepository interface { diff --git a/Backend/service/repository/game_repository.go b/Backend/service/repository/game_repository.go index 9e1c506..34e1262 100644 --- a/Backend/service/repository/game_repository.go +++ b/Backend/service/repository/game_repository.go @@ -10,13 +10,14 @@ import ( type Game struct { gorm.Model Id int `gorm:"primaryKey;auto_increment"` - Token string + RoomId string Status string - CurrentPlayerId int + CurrentPlayerId uint Players []Player CreatedAt time.Time `gorm:"autoCreateTime"` UpdatedAt time.Time `gorm:"autoCreateTime"` DeletedAt gorm.DeletedAt + Deck *Deck } type GameRepository interface { diff --git a/Backend/service/repository/player_card_repository.go b/Backend/service/repository/player_card_repository.go index e833bd2..7212879 100644 --- a/Backend/service/repository/player_card_repository.go +++ b/Backend/service/repository/player_card_repository.go @@ -9,16 +9,14 @@ import ( type PlayerCard struct { gorm.Model - Id int `gorm:"primaryKey;auto_increment"` - PlayerId int - GameId int - CardId int - Type string CreatedAt time.Time `gorm:"autoCreateTime"` UpdatedAt time.Time `gorm:"autoCreateTime"` DeletedAt gorm.DeletedAt - Card Card `gorm:"foreignKey:CardId"` - Player Player `gorm:"foreignKey:PlayerId"` + PlayerId uint + CardId uint + Type string + Card Card `gorm:"foreignKey:CardId"` + Player Player `gorm:"foreignKey:PlayerId"` } type PlayerCardRepository interface { diff --git a/Backend/service/repository/player_repository.go b/Backend/service/repository/player_repository.go index 9aceabf..4ef4f8d 100644 --- a/Backend/service/repository/player_repository.go +++ b/Backend/service/repository/player_repository.go @@ -10,16 +10,17 @@ import ( type Player struct { gorm.Model Id int `gorm:"primaryKey;auto_increment"` + GameId uint `gorm:"foreignKey:GameId;references:ID"` + UserId string Name string - GameId int `gorm:"foreignKey:GameId;references:Id"` IdentityCard string Status string - OrderNumber int + Priority int PlayerCards []PlayerCard - Game *Game `gorm:"foreignKey:GameId;references:Id"` CreatedAt time.Time `gorm:"autoCreateTime"` UpdatedAt time.Time `gorm:"autoCreateTime"` DeletedAt gorm.DeletedAt + Game *Game `gorm:"foreignKey:GameId;references:ID"` } type PlayerRepository interface { diff --git a/Backend/service/service/game_service.go b/Backend/service/service/game_service.go index 3fe72c8..2816e1e 100644 --- a/Backend/service/service/game_service.go +++ b/Backend/service/service/game_service.go @@ -39,7 +39,7 @@ func (g *GameService) InitGame(c context.Context) (*repository.Game, error) { } game, err := g.CreateGame(c, &repository.Game{ - Token: token, + RoomId: roomId, Status: enums.GameStart, }) if err != nil { diff --git a/Backend/service/service/player_service.go b/Backend/service/service/player_service.go index 7692c75..980133f 100644 --- a/Backend/service/service/player_service.go +++ b/Backend/service/service/player_service.go @@ -43,7 +43,7 @@ func (p *PlayerService) InitPlayers(c context.Context, game *repository.Game, re Name: reqPlayer.Name, GameId: game.Id, IdentityCard: identityCards[i], - OrderNumber: i + 1, + Priority: i + 1, Status: enums.PlayerStatusAlive, }) if err != nil { @@ -242,7 +242,6 @@ func (p *PlayerService) AcceptCard(c context.Context, playerId int, accept bool) if accept { _, err := p.PlayerCardRepo.CreatePlayerCard(c, &repository.PlayerCard{ PlayerId: playerId, - GameId: gameId, CardId: cardId, Type: "intelligence", }) From 88b528c4c48a1adc91f55f10c40a5ee0a3b12b2a Mon Sep 17 00:00:00 2001 From: KazeNoYumeX Date: Wed, 20 Mar 2024 23:27:50 +0800 Subject: [PATCH 02/10] Feat: Applied gorm.Model to the struct in the Repository, and removed duplicate fields --- .../service/delivery/http/v1/card_handler.go | 8 +++-- .../service/delivery/http/v1/game_handler.go | 31 ++++++++++++++----- .../delivery/http/v1/player_handler.go | 16 +++++++--- Backend/service/repository/card_repository.go | 9 ++---- Backend/service/repository/deck_repository.go | 8 ++--- .../repository/game_progresses_repository.go | 18 +++++------ Backend/service/repository/game_repository.go | 12 +++---- .../repository/mysql/card_repository.go | 4 +-- .../repository/mysql/deck_repository.go | 4 +-- .../mysql/game_process_repository.go | 7 ++--- .../repository/mysql/game_repository.go | 6 ++-- .../mysql/player_card_repository.go | 10 +++--- .../repository/mysql/player_repository.go | 10 +++--- .../repository/player_card_repository.go | 14 +++------ .../service/repository/player_repository.go | 15 +++------ Backend/service/request/game_request.go | 2 +- Backend/service/service/deck_service.go | 4 +-- Backend/service/service/game_service.go | 25 +++++++-------- Backend/service/service/player_service.go | 26 ++++++++-------- Backend/tests/e2e/game_api_test.go | 4 +-- 20 files changed, 115 insertions(+), 118 deletions(-) diff --git a/Backend/service/delivery/http/v1/card_handler.go b/Backend/service/delivery/http/v1/card_handler.go index bb34d69..f3e7e90 100644 --- a/Backend/service/delivery/http/v1/card_handler.go +++ b/Backend/service/delivery/http/v1/card_handler.go @@ -34,18 +34,20 @@ func RegisterCardHandler(opts *CardHandlerOptions) { // @Success 200 {object} request.PlayerCardsResponse // @Router /api/v1/player/{id}/player-cards/ [get] func (p *CardHandler) GetPlayerCards(c *gin.Context) { - playerId, _ := strconv.Atoi(c.Param("playerId")) + reqPlayerId, _ := strconv.Atoi(c.Param("playerId")) + playerId := uint(reqPlayerId) + playerCards, err := p.cardService.GetPlayerCardsByPlayerId(c, playerId) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) return } - playerCardsInfo := []map[string]interface{}{} + var playerCardsInfo []map[string]interface{} for _, card := range playerCards { dict := map[string]interface{}{ - "id": card.Id, + "id": card.ID, "name": card.Name, "color": card.Color, } diff --git a/Backend/service/delivery/http/v1/game_handler.go b/Backend/service/delivery/http/v1/game_handler.go index 18cfedb..2c1325a 100644 --- a/Backend/service/delivery/http/v1/game_handler.go +++ b/Backend/service/delivery/http/v1/game_handler.go @@ -85,8 +85,8 @@ func (g *GameHandler) StartGame(c *gin.Context) { g.SSE.Message <- gin.H{ "message": "Game started", "status": "started", - "game_id": game.Id, - "next_player": game.Players[0].Id, + "game_id": game.ID, + "next_player": game.Players[0].ID, } c.JSON(http.StatusOK, gin.H{ @@ -95,8 +95,14 @@ func (g *GameHandler) StartGame(c *gin.Context) { } func (g *GameHandler) GetGame(c *gin.Context) { - gameId, _ := strconv.Atoi(c.Param("gameId")) + var req request.GetGameRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + gameId := req.GameID game, err := g.gameService.GetGameById(c, gameId) if err != nil { @@ -110,7 +116,14 @@ func (g *GameHandler) GetGame(c *gin.Context) { } func (g *GameHandler) DeleteGame(c *gin.Context) { - gameId, _ := strconv.Atoi(c.Param("gameId")) + var req request.GetGameRequest + + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + gameId := req.GameID err := g.gameService.DeleteGame(c, gameId) @@ -132,11 +145,15 @@ func (g *GameHandler) DeleteGame(c *gin.Context) { // @Success 200 {object} GameSSERequest // @Router /api/v1/games/{gameId}/events [get] func (g *GameHandler) GameEvent(c *gin.Context) { - gameId, err := strconv.Atoi(c.Param("gameId")) - if err != nil { + var req request.GetGameRequest + + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } + gameId := req.GameID + v, ok := c.Get("clientChan") if !ok { return @@ -178,7 +195,7 @@ func (g *GameHandler) GameEvent(c *gin.Context) { } type GameSSERequest struct { - GameId int `json:"game_id,int"` + GameId uint `json:"game_id,int"` Message string `json:"message"` Status string `json:"status"` } diff --git a/Backend/service/delivery/http/v1/player_handler.go b/Backend/service/delivery/http/v1/player_handler.go index 7f7b977..ef4fc88 100644 --- a/Backend/service/delivery/http/v1/player_handler.go +++ b/Backend/service/delivery/http/v1/player_handler.go @@ -46,7 +46,9 @@ func RegisterPlayerHandler(opts *PlayerHandlerOptions) { // @Success 200 {object} request.PlayCardResponse // @Router /api/v1/players/{playerId}/player-cards [post] func (p *PlayerHandler) PlayCard(c *gin.Context) { - playerId, _ := strconv.Atoi(c.Param("playerId")) + reqPlayerId, _ := strconv.Atoi(c.Param("playerId")) + playerId := uint(reqPlayerId) + var req request.PlayCardRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) @@ -61,7 +63,7 @@ func (p *PlayerHandler) PlayCard(c *gin.Context) { // TODO to Service p.SSE.Message <- gin.H{ - "game_id": game.Id, + "game_id": game.ID, "status": game.Status, "message": fmt.Sprintf("玩家: %d 已出牌", playerId), "card": card.Name, @@ -84,7 +86,9 @@ func (p *PlayerHandler) PlayCard(c *gin.Context) { // @Success 200 {object} request.PlayCardResponse // @Router /api/v1/player/{playerId}/transmit-intelligence [post] func (p *PlayerHandler) TransmitIntelligence(c *gin.Context) { - playerId, _ := strconv.Atoi(c.Param("playerId")) + reqPlayerId, _ := strconv.Atoi(c.Param("playerId")) + playerId := uint(reqPlayerId) + var req request.PlayCardRequest if err := c.ShouldBindJSON(&req); err != nil { @@ -128,7 +132,9 @@ func (p *PlayerHandler) TransmitIntelligence(c *gin.Context) { // @Success 200 {object} request.PlayCardResponse // @Router /api/v1/players/{playerId}/accept [post] func (p *PlayerHandler) AcceptCard(c *gin.Context) { - playerId, _ := strconv.Atoi(c.Param("playerId")) + reqPlayerId, _ := strconv.Atoi(c.Param("playerId")) + playerId := uint(reqPlayerId) + var req request.AcceptCardRequest if err := c.ShouldBindJSON(&req); err != nil { @@ -145,7 +151,7 @@ func (p *PlayerHandler) AcceptCard(c *gin.Context) { winner, err := p.playerService.CheckWin(c, playerId) if winner != nil { p.SSE.Message <- gin.H{ - "game_id": winner.Game.Id, + "game_id": winner.Game.ID, "status": winner.Game.Status, "message": fmt.Sprintf("玩家: %d 已贏得遊戲", playerId), "winner": winner.Name, diff --git a/Backend/service/repository/card_repository.go b/Backend/service/repository/card_repository.go index d7ccaba..2668a47 100644 --- a/Backend/service/repository/card_repository.go +++ b/Backend/service/repository/card_repository.go @@ -2,25 +2,20 @@ package repository import ( "context" - "time" "gorm.io/gorm" ) type Card struct { gorm.Model - Id int `gorm:"primaryKey;auto_increment"` Name string Color string IntelligenceType int - CreatedAt time.Time `gorm:"autoCreateTime"` - UpdatedAt time.Time `gorm:"autoCreateTime"` - DeletedAt gorm.DeletedAt } type CardRepository interface { - GetCardById(ctx context.Context, id int) (*Card, error) + GetCardById(ctx context.Context, id uint) (*Card, error) CreateCard(ctx context.Context, card *Card) (*Card, error) GetCards(ctx context.Context) ([]*Card, error) - GetPlayerCardsByPlayerId(ctx context.Context, id int, gid int) ([]*Card, error) + GetPlayerCardsByPlayerIdWithGameId(ctx context.Context, playerId uint, gameId uint) ([]*Card, error) } diff --git a/Backend/service/repository/deck_repository.go b/Backend/service/repository/deck_repository.go index 87702d0..32e2f2e 100644 --- a/Backend/service/repository/deck_repository.go +++ b/Backend/service/repository/deck_repository.go @@ -2,16 +2,12 @@ package repository import ( "context" - "time" "gorm.io/gorm" ) type Deck struct { gorm.Model - CreatedAt time.Time `gorm:"autoCreateTime"` - UpdatedAt time.Time `gorm:"autoCreateTime"` - DeletedAt gorm.DeletedAt GameId uint Cards []DeckCard Game Game `gorm:"foreignKey:GameId"` @@ -27,6 +23,6 @@ type DeckCard struct { type DeckRepository interface { CreateDeck(ctx context.Context, deck *Deck) (*Deck, error) - GetDecksByGameId(ctx context.Context, id int) ([]*Deck, error) - DeleteDeck(ctx context.Context, id int) error + GetDecksByGameId(ctx context.Context, id uint) ([]*Deck, error) + DeleteDeck(ctx context.Context, id uint) error } diff --git a/Backend/service/repository/game_progresses_repository.go b/Backend/service/repository/game_progresses_repository.go index 77630f9..8ca7262 100644 --- a/Backend/service/repository/game_progresses_repository.go +++ b/Backend/service/repository/game_progresses_repository.go @@ -2,25 +2,21 @@ package repository import ( "context" - "time" "gorm.io/gorm" ) type GameProgresses struct { - Id int `gorm:"primaryKey;auto_increment"` - PlayerId int - GameId int - CardId int + gorm.Model + PlayerId uint + GameId uint + CardId uint Action string - TargetPlayerId int - CreatedAt time.Time `gorm:"autoCreateTime"` - UpdatedAt time.Time `gorm:"autoCreateTime"` - DeletedAt gorm.DeletedAt + TargetPlayerId uint } type GameProgressesRepository interface { CreateGameProgress(c context.Context, gameProgress *GameProgresses) (*GameProgresses, error) - GetGameProgresses(c context.Context, targetPlayerId int, gameId int) (*GameProgresses, error) - UpdateGameProgress(c context.Context, gameProgress *GameProgresses, next_playerId int) (*GameProgresses, error) + GetGameProgresses(c context.Context, targetPlayerId uint, gameId uint) (*GameProgresses, error) + UpdateGameProgress(c context.Context, gameProgress *GameProgresses, nextPlayerId uint) (*GameProgresses, error) } diff --git a/Backend/service/repository/game_repository.go b/Backend/service/repository/game_repository.go index 34e1262..9a2fecc 100644 --- a/Backend/service/repository/game_repository.go +++ b/Backend/service/repository/game_repository.go @@ -2,28 +2,24 @@ package repository import ( "context" - "time" "gorm.io/gorm" ) type Game struct { gorm.Model - Id int `gorm:"primaryKey;auto_increment"` RoomId string Status string CurrentPlayerId uint Players []Player - CreatedAt time.Time `gorm:"autoCreateTime"` - UpdatedAt time.Time `gorm:"autoCreateTime"` - DeletedAt gorm.DeletedAt Deck *Deck } type GameRepository interface { - GetGameById(ctx context.Context, id int) (*Game, error) + GetGameById(ctx context.Context, id uint) (*Game, error) CreateGame(ctx context.Context, game *Game) (*Game, error) - DeleteGame(ctx context.Context, id int) error - GetGameWithPlayers(ctx context.Context, id int) (*Game, error) + DeleteGame(ctx context.Context, id uint) error + GetGameWithPlayers(ctx context.Context, id uint) (*Game, error) UpdateGame(ctx context.Context, game *Game) error + CreateGameWithPlayers(c context.Context, game *Game) (*Game, error) } diff --git a/Backend/service/repository/mysql/card_repository.go b/Backend/service/repository/mysql/card_repository.go index 9ba518b..586730d 100644 --- a/Backend/service/repository/mysql/card_repository.go +++ b/Backend/service/repository/mysql/card_repository.go @@ -17,7 +17,7 @@ func NewCardRepository(db *gorm.DB) *CardRepository { } } -func (c *CardRepository) GetCardById(ctx context.Context, id int) (*repository.Card, error) { +func (c *CardRepository) GetCardById(ctx context.Context, id uint) (*repository.Card, error) { card := new(repository.Card) result := c.db.First(&card, "id = ?", id) @@ -61,7 +61,7 @@ func (c *CardRepository) GetPlayerCardsByPlayerId(ctx context.Context, id int, g if result.Error != nil { return nil, result.Error } - var cardIDs []int + var cardIDs []uint for _, pc := range playerCards { cardIDs = append(cardIDs, pc.CardId) } diff --git a/Backend/service/repository/mysql/deck_repository.go b/Backend/service/repository/mysql/deck_repository.go index 01e880f..bb5e486 100644 --- a/Backend/service/repository/mysql/deck_repository.go +++ b/Backend/service/repository/mysql/deck_repository.go @@ -27,7 +27,7 @@ func (d *DeckRepository) CreateDeck(ctx context.Context, deck *repository.Deck) return deck, nil } -func (d *DeckRepository) GetDecksByGameId(ctx context.Context, id int) ([]*repository.Deck, error) { +func (d *DeckRepository) GetDecksByGameId(ctx context.Context, id uint) ([]*repository.Deck, error) { var decks []*repository.Deck result := d.db.Find(&decks, "game_id = ?", id) @@ -39,7 +39,7 @@ func (d *DeckRepository) GetDecksByGameId(ctx context.Context, id int) ([]*repos return decks, nil } -func (d *DeckRepository) DeleteDeck(ctx context.Context, id int) error { +func (d *DeckRepository) DeleteDeck(ctx context.Context, id uint) error { deck := new(repository.Deck) result := d.db.Delete(&deck, "id = ?", id) diff --git a/Backend/service/repository/mysql/game_process_repository.go b/Backend/service/repository/mysql/game_process_repository.go index 04cc433..fb2ea39 100644 --- a/Backend/service/repository/mysql/game_process_repository.go +++ b/Backend/service/repository/mysql/game_process_repository.go @@ -27,7 +27,7 @@ func (g *GameProgressRepository) CreateGameProgress(c context.Context, gameProgr return gameProgress, nil } -func (g *GameProgressRepository) GetGameProgresses(c context.Context, targetPlayerId int, gameId int) (*repository.GameProgresses, error) { +func (g *GameProgressRepository) GetGameProgresses(c context.Context, targetPlayerId uint, gameId uint) (*repository.GameProgresses, error) { var gameProgress *repository.GameProgresses result := g.db.First(&gameProgress, "target_player_id = ? AND game_id = ?", targetPlayerId, gameId) @@ -38,11 +38,10 @@ func (g *GameProgressRepository) GetGameProgresses(c context.Context, targetPlay return gameProgress, nil } -func (g *GameProgressRepository) UpdateGameProgress(c context.Context, gameProgress *repository.GameProgresses, next_playerId int) (*repository.GameProgresses, error) { - // result := g.db.Update(&gameProgress) +func (g *GameProgressRepository) UpdateGameProgress(c context.Context, gameProgress *repository.GameProgresses, nextPlayerId uint) (*repository.GameProgresses, error) { result := g.db.First(&gameProgress) - gameProgress.TargetPlayerId = next_playerId + gameProgress.TargetPlayerId = nextPlayerId g.db.Save(&gameProgress) if result.Error != nil { diff --git a/Backend/service/repository/mysql/game_repository.go b/Backend/service/repository/mysql/game_repository.go index aae3fcc..1aa48df 100644 --- a/Backend/service/repository/mysql/game_repository.go +++ b/Backend/service/repository/mysql/game_repository.go @@ -17,7 +17,7 @@ func NewGameRepository(db *gorm.DB) *GameRepository { } } -func (g *GameRepository) GetGameById(ctx context.Context, id int) (*repository.Game, error) { +func (g *GameRepository) GetGameById(ctx context.Context, id uint) (*repository.Game, error) { game := new(repository.Game) result := g.db.First(&game, "id = ?", id) @@ -40,7 +40,7 @@ func (g *GameRepository) CreateGame(ctx context.Context, game *repository.Game) return game, nil } -func (g *GameRepository) DeleteGame(ctx context.Context, id int) error { +func (g *GameRepository) DeleteGame(ctx context.Context, id uint) error { game := new(repository.Game) result := g.db.Delete(&game, "id = ?", id) @@ -52,7 +52,7 @@ func (g *GameRepository) DeleteGame(ctx context.Context, id int) error { return nil } -func (g *GameRepository) GetGameWithPlayers(ctx context.Context, id int) (*repository.Game, error) { +func (g *GameRepository) GetGameWithPlayers(ctx context.Context, id uint) (*repository.Game, error) { var game repository.Game if err := g.db.Preload("Players").First(&game, id).Error; err != nil { return nil, err diff --git a/Backend/service/repository/mysql/player_card_repository.go b/Backend/service/repository/mysql/player_card_repository.go index ebfd84b..9d96224 100644 --- a/Backend/service/repository/mysql/player_card_repository.go +++ b/Backend/service/repository/mysql/player_card_repository.go @@ -28,7 +28,7 @@ func (p PlayerCardRepository) CreatePlayerCard(ctx context.Context, card *reposi return card, nil } -func (p PlayerCardRepository) DeletePlayerCard(ctx context.Context, id int) error { +func (p PlayerCardRepository) DeletePlayerCard(ctx context.Context, id uint) error { card := new(repository.PlayerCard) result := p.db.Delete(&card, "id = ?", id) @@ -40,7 +40,7 @@ func (p PlayerCardRepository) DeletePlayerCard(ctx context.Context, id int) erro return nil } -func (p PlayerCardRepository) DeletePlayerCardByPlayerIdAndCardId(ctx context.Context, playerId int, gameId int, cardId int) (bool, error) { +func (p PlayerCardRepository) DeletePlayerCardByPlayerIdAndCardId(ctx context.Context, playerId uint, gameId uint, cardId uint) (bool, error) { card := new(repository.PlayerCard) result := p.db.Delete(&card, "player_id = ? AND game_id = ? AND card_id = ?", playerId, gameId, cardId) @@ -51,7 +51,7 @@ func (p PlayerCardRepository) DeletePlayerCardByPlayerIdAndCardId(ctx context.Co return true, nil } -func (p *PlayerCardRepository) ExistPlayerCardByPlayerIdAndCardId(ctx context.Context, playerId int, gameId int, cardId int) (bool, error) { +func (p *PlayerCardRepository) ExistPlayerCardByPlayerIdAndCardId(ctx context.Context, playerId uint, gameId uint, cardId uint) (bool, error) { var card repository.PlayerCard result := p.db.First(&card, "player_id = ? AND game_id = ? AND card_id = ?", playerId, gameId, cardId) if result.Error != nil { @@ -63,7 +63,7 @@ func (p *PlayerCardRepository) ExistPlayerCardByPlayerIdAndCardId(ctx context.Co return true, nil } -func (p PlayerCardRepository) GetPlayerCardById(ctx context.Context, id int) (*repository.PlayerCard, error) { +func (p PlayerCardRepository) GetPlayerCardById(ctx context.Context, id uint) (*repository.PlayerCard, error) { card := new(repository.PlayerCard) result := p.db.First(&card, "id = ?", id) @@ -85,7 +85,7 @@ func (p *PlayerCardRepository) GetPlayerCards(ctx context.Context, playerCard *r return playerCards, nil } -func (p PlayerCardRepository) GetPlayerCardsByGameId(ctx context.Context, id int) ([]*repository.PlayerCard, error) { +func (p PlayerCardRepository) GetPlayerCardsByGameId(ctx context.Context, id uint) ([]*repository.PlayerCard, error) { var cards []*repository.PlayerCard result := p.db.Find(&cards, "game_id = ?", id) diff --git a/Backend/service/repository/mysql/player_repository.go b/Backend/service/repository/mysql/player_repository.go index 5fe8914..26aae4e 100644 --- a/Backend/service/repository/mysql/player_repository.go +++ b/Backend/service/repository/mysql/player_repository.go @@ -22,7 +22,7 @@ func (p *PlayerRepository) CreatePlayer(ctx context.Context, player *repository. return player, err } -func (p *PlayerRepository) GetPlayerById(ctx context.Context, playerId int) (*repository.Player, error) { +func (p *PlayerRepository) GetPlayerById(ctx context.Context, playerId uint) (*repository.Player, error) { player := new(repository.Player) result := p.db.First(&player, "id = ?", playerId) @@ -34,7 +34,7 @@ func (p *PlayerRepository) GetPlayerById(ctx context.Context, playerId int) (*re return player, nil } -func (p *PlayerRepository) GetPlayersByGameId(ctx context.Context, id int) ([]*repository.Player, error) { +func (p *PlayerRepository) GetPlayersByGameId(ctx context.Context, id uint) ([]*repository.Player, error) { var players []*repository.Player result := p.db.Find(&players, "game_id = ?", id) @@ -46,7 +46,7 @@ func (p *PlayerRepository) GetPlayersByGameId(ctx context.Context, id int) ([]*r return players, nil } -func (p *PlayerRepository) GetPlayerWithPlayerCards(ctx context.Context, playerId int) (*repository.Player, error) { +func (p *PlayerRepository) GetPlayerWithPlayerCards(ctx context.Context, playerId uint) (*repository.Player, error) { var player repository.Player if err := p.db.Preload("PlayerCards").Preload("PlayerCards.Card").First(&player, playerId).Error; err != nil { return nil, err @@ -54,7 +54,7 @@ func (p *PlayerRepository) GetPlayerWithPlayerCards(ctx context.Context, playerI return &player, nil } -func (p *PlayerRepository) GetPlayerWithGame(ctx context.Context, playerId int) (*repository.Player, error) { +func (p *PlayerRepository) GetPlayerWithGame(ctx context.Context, playerId uint) (*repository.Player, error) { var player repository.Player if err := p.db.Preload("Game").First(&player, playerId).Error; err != nil { return nil, err @@ -62,7 +62,7 @@ func (p *PlayerRepository) GetPlayerWithGame(ctx context.Context, playerId int) return &player, nil } -func (p *PlayerRepository) GetPlayerWithGamePlayersAndPlayerCardsCard(ctx context.Context, playerId int) (*repository.Player, error) { +func (p *PlayerRepository) GetPlayerWithGamePlayersAndPlayerCardsCard(ctx context.Context, playerId uint) (*repository.Player, error) { var player repository.Player if err := p.db.Preload("Game.Players.PlayerCards.Card").Preload("Game.Players.Game").Preload("PlayerCards.Card").First(&player, playerId).Error; err != nil { return nil, err diff --git a/Backend/service/repository/player_card_repository.go b/Backend/service/repository/player_card_repository.go index 7212879..a721ea0 100644 --- a/Backend/service/repository/player_card_repository.go +++ b/Backend/service/repository/player_card_repository.go @@ -2,16 +2,12 @@ package repository import ( "context" - "time" "gorm.io/gorm" ) type PlayerCard struct { gorm.Model - CreatedAt time.Time `gorm:"autoCreateTime"` - UpdatedAt time.Time `gorm:"autoCreateTime"` - DeletedAt gorm.DeletedAt PlayerId uint CardId uint Type string @@ -20,11 +16,11 @@ type PlayerCard struct { } type PlayerCardRepository interface { - GetPlayerCardById(ctx context.Context, id int) (*PlayerCard, error) - GetPlayerCardsByGameId(ctx context.Context, id int) ([]*PlayerCard, error) + GetPlayerCardById(ctx context.Context, id uint) (*PlayerCard, error) + GetPlayerCardsByGameId(ctx context.Context, id uint) ([]*PlayerCard, error) CreatePlayerCard(ctx context.Context, card *PlayerCard) (*PlayerCard, error) - DeletePlayerCard(ctx context.Context, id int) error - DeletePlayerCardByPlayerIdAndCardId(ctx context.Context, playerId int, gameId int, cardId int) (bool, error) - ExistPlayerCardByPlayerIdAndCardId(ctx context.Context, playerId int, gameId int, cardId int) (bool, error) + DeletePlayerCard(ctx context.Context, id uint) error + DeletePlayerCardByPlayerIdAndCardId(ctx context.Context, playerId uint, gameId uint, cardId uint) (bool, error) + ExistPlayerCardByPlayerIdAndCardId(ctx context.Context, playerId uint, gameId uint, cardId uint) (bool, error) GetPlayerCards(ctx context.Context, playerCard *PlayerCard) (*[]PlayerCard, error) } diff --git a/Backend/service/repository/player_repository.go b/Backend/service/repository/player_repository.go index 4ef4f8d..ed48564 100644 --- a/Backend/service/repository/player_repository.go +++ b/Backend/service/repository/player_repository.go @@ -2,14 +2,12 @@ package repository import ( "context" - "time" "gorm.io/gorm" ) type Player struct { gorm.Model - Id int `gorm:"primaryKey;auto_increment"` GameId uint `gorm:"foreignKey:GameId;references:ID"` UserId string Name string @@ -17,17 +15,14 @@ type Player struct { Status string Priority int PlayerCards []PlayerCard - CreatedAt time.Time `gorm:"autoCreateTime"` - UpdatedAt time.Time `gorm:"autoCreateTime"` - DeletedAt gorm.DeletedAt Game *Game `gorm:"foreignKey:GameId;references:ID"` } type PlayerRepository interface { CreatePlayer(ctx context.Context, player *Player) (*Player, error) - GetPlayerById(ctx context.Context, playerId int) (*Player, error) - GetPlayersByGameId(ctx context.Context, id int) ([]*Player, error) - GetPlayerWithPlayerCards(ctx context.Context, playerId int) (*Player, error) - GetPlayerWithGame(ctx context.Context, playerId int) (*Player, error) - GetPlayerWithGamePlayersAndPlayerCardsCard(ctx context.Context, playerId int) (*Player, error) + GetPlayerById(ctx context.Context, playerId uint) (*Player, error) + GetPlayersByGameId(ctx context.Context, id uint) ([]*Player, error) + GetPlayerWithPlayerCards(ctx context.Context, playerId uint) (*Player, error) + GetPlayerWithGame(ctx context.Context, playerId uint) (*Player, error) + GetPlayerWithGamePlayersAndPlayerCardsCard(ctx context.Context, playerId uint) (*Player, error) } diff --git a/Backend/service/request/game_request.go b/Backend/service/request/game_request.go index 710e060..ef53b1e 100644 --- a/Backend/service/request/game_request.go +++ b/Backend/service/request/game_request.go @@ -19,7 +19,7 @@ type PlayCardResponse struct { } type PlayCardRequest struct { - CardID int `json:"card_id"` + CardID uint `json:"card_id"` } type AcceptCardRequest struct { diff --git a/Backend/service/service/deck_service.go b/Backend/service/service/deck_service.go index 1768edb..c85952b 100644 --- a/Backend/service/service/deck_service.go +++ b/Backend/service/service/deck_service.go @@ -66,7 +66,7 @@ func (d *DeckService) CreateDeck(c context.Context, deck *repository.Deck) (*rep return deck, nil } -func (d *DeckService) GetDecksByGameId(c context.Context, id int) ([]*repository.Deck, error) { +func (d *DeckService) GetDecksByGameId(c context.Context, id uint) ([]*repository.Deck, error) { decks, err := d.DeckRepo.GetDecksByGameId(c, id) if err != nil { return nil, err @@ -75,7 +75,7 @@ func (d *DeckService) GetDecksByGameId(c context.Context, id int) ([]*repository } -func (d *DeckService) DeleteDeckFromGame(c context.Context, id int) error { +func (d *DeckService) DeleteDeckFromGame(c context.Context, id uint) error { err := d.DeckRepo.DeleteDeck(c, id) if err != nil { return err diff --git a/Backend/service/service/game_service.go b/Backend/service/service/game_service.go index 2816e1e..bfae93f 100644 --- a/Backend/service/service/game_service.go +++ b/Backend/service/service/game_service.go @@ -60,16 +60,15 @@ func (g *GameService) InitDeck(c context.Context, game *repository.Game) error { func (g *GameService) DrawCard(c context.Context, game *repository.Game, player *repository.Player, drawCards []*repository.Deck, count int) error { for i := 0; i < count; i++ { card := &repository.PlayerCard{ - GameId: game.Id, - PlayerId: player.Id, - CardId: drawCards[i].CardId, + PlayerId: player.ID, + CardId: drawCards[i].ID, Type: "hand", } err := g.PlayerService.CreatePlayerCard(c, card) if err != nil { return err } - err = g.DeckService.DeleteDeckFromGame(c, drawCards[i].Id) + err = g.DeckService.DeleteDeckFromGame(c, drawCards[i].ID) if err != nil { return err } @@ -78,12 +77,12 @@ func (g *GameService) DrawCard(c context.Context, game *repository.Game, player } func (g *GameService) DrawCardsForAllPlayers(c context.Context, game *repository.Game) error { - players, err := g.PlayerService.GetPlayersByGameId(c, game.Id) + players, err := g.PlayerService.GetPlayersByGameId(c, game.ID) if err != nil { return err } for _, player := range players { - drawCards, _ := g.DeckService.GetDecksByGameId(c, game.Id) + drawCards, _ := g.DeckService.GetDecksByGameId(c, game.ID) err2 := g.DrawCard(c, game, player, drawCards, 3) if err2 != nil { return err2 @@ -109,7 +108,7 @@ func (g *GameService) CreateGame(c context.Context, game *repository.Game) (*rep return game, nil } -func (g *GameService) GetGameById(c context.Context, id int) (*repository.Game, error) { +func (g *GameService) GetGameById(c context.Context, id uint) (*repository.Game, error) { game, err := g.GameRepo.GetGameWithPlayers(c, id) if err != nil { return nil, err @@ -117,7 +116,7 @@ func (g *GameService) GetGameById(c context.Context, id int) (*repository.Game, return game, nil } -func (g *GameService) DeleteGame(c context.Context, id int) error { +func (g *GameService) DeleteGame(c context.Context, id uint) error { err := g.GameRepo.DeleteGame(c, id) if err != nil { return err @@ -125,7 +124,7 @@ func (g *GameService) DeleteGame(c context.Context, id int) error { return nil } -func (g *GameService) UpdateCurrentPlayer(c context.Context, game *repository.Game, playerId int) { +func (g *GameService) UpdateCurrentPlayer(c context.Context, game *repository.Game, playerId uint) { game.CurrentPlayerId = playerId err := g.GameRepo.UpdateGame(c, game) if err != nil { @@ -136,21 +135,21 @@ func (g *GameService) UpdateCurrentPlayer(c context.Context, game *repository.Ga func (g *GameService) NextPlayer(c context.Context, player *repository.Player) (*repository.Game, error) { players := player.Game.Players - currentPlayerId := player.Id + currentPlayerId := player.ID var currentPlayerIndex int for index, gPlayer := range players { - if gPlayer.Id == currentPlayerId { + if gPlayer.ID == currentPlayerId { currentPlayerIndex = index break } } if currentPlayerIndex+1 >= len(players) { - player.Game.CurrentPlayerId = players[0].Id + player.Game.CurrentPlayerId = players[0].ID player.Game.Status = enums.TransmitIntelligenceStage } else { - player.Game.CurrentPlayerId = players[currentPlayerIndex+1].Id + player.Game.CurrentPlayerId = players[currentPlayerIndex+1].ID } return player.Game, nil } diff --git a/Backend/service/service/player_service.go b/Backend/service/service/player_service.go index 980133f..3a59815 100644 --- a/Backend/service/service/player_service.go +++ b/Backend/service/service/player_service.go @@ -41,7 +41,7 @@ func (p *PlayerService) InitPlayers(c context.Context, game *repository.Game, re for i, reqPlayer := range req.Players { _, err := p.CreatePlayer(c, &repository.Player{ Name: reqPlayer.Name, - GameId: game.Id, + GameId: game.ID, IdentityCard: identityCards[i], Priority: i + 1, Status: enums.PlayerStatusAlive, @@ -82,14 +82,14 @@ func (p *PlayerService) CanPlayCard(c context.Context, player *repository.Player return false, errors.New("你已死亡") } - if player.Game.CurrentPlayerId != player.Id { + if player.Game.CurrentPlayerId != player.ID { return false, errors.New("尚未輪到你出牌") } return true, nil } -func (p *PlayerService) CheckPlayerCardExist(c context.Context, playerId int, gameId int, cardId int) (bool, error) { +func (p *PlayerService) CheckPlayerCardExist(c context.Context, playerId uint, gameId uint, cardId uint) (bool, error) { exist, err := p.PlayerCardRepo.ExistPlayerCardByPlayerIdAndCardId(c, playerId, gameId, cardId) if err != nil { @@ -115,7 +115,7 @@ func (p *PlayerService) CreatePlayerCard(c context.Context, card *repository.Pla return nil } -func (p *PlayerService) GetPlayerById(c context.Context, id int) (*repository.Player, error) { +func (p *PlayerService) GetPlayerById(c context.Context, id uint) (*repository.Player, error) { player, err := p.PlayerRepo.GetPlayerById(c, id) if err != nil { return nil, err @@ -123,7 +123,7 @@ func (p *PlayerService) GetPlayerById(c context.Context, id int) (*repository.Pl return player, nil } -func (p *PlayerService) GetPlayersByGameId(c context.Context, id int) ([]*repository.Player, error) { +func (p *PlayerService) GetPlayersByGameId(c context.Context, id uint) ([]*repository.Player, error) { players, err := p.PlayerRepo.GetPlayersByGameId(c, id) if err != nil { return nil, err @@ -131,7 +131,7 @@ func (p *PlayerService) GetPlayersByGameId(c context.Context, id int) ([]*reposi return players, nil } -func (p *PlayerService) GetHandCardId(player *repository.Player, cardId int) (*repository.PlayerCard, error) { +func (p *PlayerService) GetHandCardId(player *repository.Player, cardId uint) (*repository.PlayerCard, error) { for _, card := range player.PlayerCards { if card.CardId == cardId && card.Type == "hand" { return &card, nil @@ -140,7 +140,7 @@ func (p *PlayerService) GetHandCardId(player *repository.Player, cardId int) (*r return nil, errors.New("找不到手牌") } -func (p *PlayerService) PlayCard(c context.Context, playerId int, cardId int) (*repository.Game, *repository.Card, error) { +func (p *PlayerService) PlayCard(c context.Context, playerId uint, cardId uint) (*repository.Game, *repository.Card, error) { player, err := p.PlayerRepo.GetPlayerWithGamePlayersAndPlayerCardsCard(c, playerId) if err != nil { return nil, nil, err @@ -161,7 +161,7 @@ func (p *PlayerService) PlayCard(c context.Context, playerId int, cardId int) (* return nil, nil, err } - err = p.PlayerCardRepo.DeletePlayerCard(c, handCard.Id) + err = p.PlayerCardRepo.DeletePlayerCard(c, handCard.ID) if err != nil { return nil, nil, err } @@ -174,7 +174,7 @@ func (p *PlayerService) PlayCard(c context.Context, playerId int, cardId int) (* return game, &handCard.Card, nil } -func (p *PlayerService) TransmitIntelligenceCard(c context.Context, playerId int, gameId int, cardId int) (bool, error) { +func (p *PlayerService) TransmitIntelligenceCard(c context.Context, playerId uint, gameId uint, cardId uint) (bool, error) { player, err := p.PlayerRepo.GetPlayerWithGamePlayersAndPlayerCardsCard(c, playerId) if err != nil { return false, err @@ -201,7 +201,7 @@ func (p *PlayerService) TransmitIntelligenceCard(c context.Context, playerId int } _, err = p.GameProgressRepo.CreateGameProgress(c, &repository.GameProgresses{ - GameId: game.Id, + GameId: game.ID, PlayerId: playerId, CardId: cardId, Action: enums.TransmitIntelligence, @@ -215,7 +215,7 @@ func (p *PlayerService) TransmitIntelligenceCard(c context.Context, playerId int return ret, nil } -func (p *PlayerService) AcceptCard(c context.Context, playerId int, accept bool) (bool, error) { +func (p *PlayerService) AcceptCard(c context.Context, playerId uint, accept bool) (bool, error) { player, err := p.PlayerRepo.GetPlayerWithGamePlayersAndPlayerCardsCard(c, playerId) if err != nil { return false, err @@ -231,7 +231,7 @@ func (p *PlayerService) AcceptCard(c context.Context, playerId int, accept bool) return false, err } - gameId := game.Id + gameId := game.ID gameProgress, err := p.GameProgressRepo.GetGameProgresses(c, playerId, gameId) if err != nil { return false, err @@ -265,7 +265,7 @@ func (p *PlayerService) AcceptCard(c context.Context, playerId int, accept bool) return res, nil } -func (p *PlayerService) CheckWin(c context.Context, playerId int) (*repository.Player, error) { +func (p *PlayerService) CheckWin(c context.Context, playerId uint) (*repository.Player, error) { player, err := p.PlayerRepo.GetPlayerWithGamePlayersAndPlayerCardsCard(c, playerId) if err != nil { return nil, err diff --git a/Backend/tests/e2e/game_api_test.go b/Backend/tests/e2e/game_api_test.go index 7174c64..4e2f565 100644 --- a/Backend/tests/e2e/game_api_test.go +++ b/Backend/tests/e2e/game_api_test.go @@ -32,14 +32,14 @@ func (suite *IntegrationTestSuite) TestStartGameE2E() { assert.NotNil(suite.T(), responseJson["Id"], "JSON response should contain a 'Id' field") // 驗證Game內的玩家都持有identity - game, _ := suite.gameRepo.GetGameWithPlayers(context.TODO(), int(responseJson["Id"].(float64))) + game, _ := suite.gameRepo.GetGameWithPlayers(context.TODO(), uint(int(responseJson["Id"].(float64)))) assert.NotEmpty(suite.T(), game.Players[0].IdentityCard) assert.NotEmpty(suite.T(), game.Players[1].IdentityCard) assert.NotEmpty(suite.T(), game.Players[2].IdentityCard) for _, player := range game.Players { - playerCards, _ := suite.playerRepo.GetPlayerWithPlayerCards(context.TODO(), player.Id) + playerCards, _ := suite.playerRepo.GetPlayerWithPlayerCards(context.TODO(), player.ID) assert.NotEmpty(suite.T(), playerCards.PlayerCards) } } From 0cacbe5e721560039647ea5b9e757c7e8baea835 Mon Sep 17 00:00:00 2001 From: KazeNoYumeX Date: Wed, 20 Mar 2024 23:32:48 +0800 Subject: [PATCH 03/10] Feat: Added env variables to append the game registration info and start game url of the GAAS --- .github/workflows/test-go-unit.yml | 4 ++-- Backend/.env.example | 17 +++++++++++++++++ Backend/env | 6 ------ .../service/delivery/http/v1/game_handler.go | 13 ++++++++++++- Backend/service/request/game_request.go | 3 +-- 5 files changed, 32 insertions(+), 11 deletions(-) create mode 100644 Backend/.env.example delete mode 100644 Backend/env diff --git a/.github/workflows/test-go-unit.yml b/.github/workflows/test-go-unit.yml index aacc9b8..b97fdfc 100644 --- a/.github/workflows/test-go-unit.yml +++ b/.github/workflows/test-go-unit.yml @@ -28,7 +28,7 @@ jobs: - name: Env handle working-directory: ./Backend - run: cp env .env + run: cp .env.example .env - name: Install dependencies working-directory: ./Backend @@ -88,7 +88,7 @@ jobs: - name: Env handle working-directory: ./Backend - run: cp env .env + run: cp .env.example .env - name: Wait for MySQL run: | diff --git a/Backend/.env.example b/Backend/.env.example new file mode 100644 index 0000000..dfcf7d9 --- /dev/null +++ b/Backend/.env.example @@ -0,0 +1,17 @@ +# |-------------------------------------------------------------------------- +# | 基本設定 Basic +# |-------------------------------------------------------------------------- +APP_NAME="風聲-黑名單" +APP_VERSION="v1" +APP_FRONTEND_URL="https://messagefrontenddev.zeabur.app/" +APP_BACKEND_URL="https://messagebackenddev.zeabur.app/api/" + +# |-------------------------------------------------------------------------- +# | 資料庫設定 Database +# |-------------------------------------------------------------------------- +DB_HOST=127.0.0.1 +DB_DATABASE=test +DB_USER=user +DB_PASSWORD=P@ssw0rd! +DB_PORT=3306 +DB_ROOT_PASSWORD=P@ssw0rd! diff --git a/Backend/env b/Backend/env deleted file mode 100644 index 921f6a5..0000000 --- a/Backend/env +++ /dev/null @@ -1,6 +0,0 @@ -DB_HOST=127.0.0.1 -DB_DATABASE=test -DB_USER=user -DB_PASSWORD=P@ssw0rd! -DB_PORT=3306 -DB_ROOT_PASSWORD=P@ssw0rd! \ No newline at end of file diff --git a/Backend/service/delivery/http/v1/game_handler.go b/Backend/service/delivery/http/v1/game_handler.go index 2c1325a..82fc399 100644 --- a/Backend/service/delivery/http/v1/game_handler.go +++ b/Backend/service/delivery/http/v1/game_handler.go @@ -5,12 +5,14 @@ import ( "io" "log" "net/http" + "os" "strconv" "github.com/Game-as-a-Service/The-Message/enums" "github.com/Game-as-a-Service/The-Message/service/request" "github.com/Game-as-a-Service/The-Message/service/service" "github.com/gin-gonic/gin" + "github.com/joho/godotenv" ) type GameHandler struct { @@ -89,8 +91,17 @@ func (g *GameHandler) StartGame(c *gin.Context) { "next_player": game.Players[0].ID, } + // Load .env file + err = godotenv.Load() + if err != nil { + log.Fatal("Error loading .env file") + } + + url := os.Getenv("APP_FRONTEND_URL") + version := os.Getenv("APP_VERSION") + c.JSON(http.StatusOK, gin.H{ - "Id": game.Id, + "uri": url + version + "/games/" + strconv.Itoa(int(game.ID)), }) } diff --git a/Backend/service/request/game_request.go b/Backend/service/request/game_request.go index ef53b1e..6b1e997 100644 --- a/Backend/service/request/game_request.go +++ b/Backend/service/request/game_request.go @@ -10,8 +10,7 @@ type PlayerInfo struct { } type CreateGameResponse struct { - ID string `json:"id"` - Token string `json:"token"` + URL string `json:"url"` } type PlayCardResponse struct { From 2854c245a9faf4f9f6eb8933904d922491a796be Mon Sep 17 00:00:00 2001 From: KazeNoYumeX Date: Thu, 21 Mar 2024 00:42:53 +0800 Subject: [PATCH 04/10] Feat: Added start game api to GAAS platform --- .../service/delivery/http/v1/game_handler.go | 30 +--- .../delivery/http/v1/player_handler.go | 6 +- Backend/service/repository/deck_repository.go | 12 +- .../repository/game_progresses_repository.go | 8 +- Backend/service/repository/game_repository.go | 4 +- .../repository/mysql/card_repository.go | 8 +- .../mysql/game_process_repository.go | 2 +- .../repository/mysql/game_repository.go | 16 ++ .../repository/player_card_repository.go | 8 +- .../service/repository/player_repository.go | 6 +- Backend/service/request/game_request.go | 11 +- Backend/service/service/card_service.go | 26 +++- Backend/service/service/deck_service.go | 70 +++++---- Backend/service/service/game_service.go | 143 ++++++++++++++---- Backend/service/service/player_service.go | 22 +-- 15 files changed, 245 insertions(+), 127 deletions(-) diff --git a/Backend/service/delivery/http/v1/game_handler.go b/Backend/service/delivery/http/v1/game_handler.go index 82fc399..d619e32 100644 --- a/Backend/service/delivery/http/v1/game_handler.go +++ b/Backend/service/delivery/http/v1/game_handler.go @@ -8,7 +8,6 @@ import ( "os" "strconv" - "github.com/Game-as-a-Service/The-Message/enums" "github.com/Game-as-a-Service/The-Message/service/request" "github.com/Game-as-a-Service/The-Message/service/service" "github.com/gin-gonic/gin" @@ -53,34 +52,17 @@ func (g *GameHandler) StartGame(c *gin.Context) { return } - game, err := g.gameService.InitGame(c) - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) - return - } - - // TODO 這邊可以優化 https://gorm.io/zh_CN/docs/associations.html - if err := g.gameService.PlayerService.InitPlayers(c, game, req); err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) - return - } - - game, _ = g.gameService.GetGameById(c, game.Id) - g.gameService.UpdateCurrentPlayer(c, game, game.Players[0].Id) - g.gameService.UpdateStatus(c, game, enums.ActionCardStage) + cards, err := g.gameService.InitCards(c) - if err := g.gameService.InitDeck(c, game); err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"message": err}) return } - if err := g.gameService.DrawCardsForAllPlayers(c, game); err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) - return - } + game, err := g.gameService.CreateGameWithPlayers(c, req, cards) - game, err = g.gameService.GetGameById(c, game.Id) if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"message": err}) return } @@ -184,7 +166,7 @@ func (g *GameHandler) GameEvent(c *gin.Context) { "message": game.Status, "status": game.Status, "game_id": gameId, - "current_player": game.CurrentPlayerId, + "current_player": game.CurrentPlayerID, } c.Stream(func(w io.Writer) bool { diff --git a/Backend/service/delivery/http/v1/player_handler.go b/Backend/service/delivery/http/v1/player_handler.go index ef4fc88..4f8ba06 100644 --- a/Backend/service/delivery/http/v1/player_handler.go +++ b/Backend/service/delivery/http/v1/player_handler.go @@ -67,7 +67,7 @@ func (p *PlayerHandler) PlayCard(c *gin.Context) { "status": game.Status, "message": fmt.Sprintf("玩家: %d 已出牌", playerId), "card": card.Name, - "next_player": game.CurrentPlayerId, + "next_player": game.CurrentPlayerID, } c.JSON(http.StatusOK, gin.H{ @@ -104,13 +104,13 @@ func (p *PlayerHandler) TransmitIntelligence(c *gin.Context) { } // Check card_id exists in player_cards - exist, err := p.playerService.CheckPlayerCardExist(c, playerId, player.GameId, req.CardID) + exist, err := p.playerService.CheckPlayerCardExist(c, playerId, player.GameID, req.CardID) if err != nil || !exist { c.JSON(http.StatusInternalServerError, gin.H{"message": "Card not found"}) return } - ret, err := p.playerService.TransmitIntelligenceCard(c, playerId, player.GameId, req.CardID) + ret, err := p.playerService.TransmitIntelligenceCard(c, playerId, player.GameID, req.CardID) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) return diff --git a/Backend/service/repository/deck_repository.go b/Backend/service/repository/deck_repository.go index 32e2f2e..0aa0dd2 100644 --- a/Backend/service/repository/deck_repository.go +++ b/Backend/service/repository/deck_repository.go @@ -8,17 +8,17 @@ import ( type Deck struct { gorm.Model - GameId uint + GameID uint Cards []DeckCard - Game Game `gorm:"foreignKey:GameId"` + Game Game `gorm:"foreignKey:GameID"` } type DeckCard struct { gorm.Model - CardId uint - DeckId uint - Deck Deck `gorm:"foreignKey:DeckId"` - Card Card `gorm:"foreignKey:CardId"` + CardID uint + DeckID uint + Deck Deck `gorm:"foreignKey:DeckID"` + Card Card `gorm:"foreignKey:CardID"` } type DeckRepository interface { diff --git a/Backend/service/repository/game_progresses_repository.go b/Backend/service/repository/game_progresses_repository.go index 8ca7262..4905cc6 100644 --- a/Backend/service/repository/game_progresses_repository.go +++ b/Backend/service/repository/game_progresses_repository.go @@ -8,11 +8,11 @@ import ( type GameProgresses struct { gorm.Model - PlayerId uint - GameId uint - CardId uint + PlayerID uint + GameID uint + CardID uint Action string - TargetPlayerId uint + TargetPlayerID uint } type GameProgressesRepository interface { diff --git a/Backend/service/repository/game_repository.go b/Backend/service/repository/game_repository.go index 9a2fecc..c832777 100644 --- a/Backend/service/repository/game_repository.go +++ b/Backend/service/repository/game_repository.go @@ -8,9 +8,9 @@ import ( type Game struct { gorm.Model - RoomId string + RoomID string Status string - CurrentPlayerId uint + CurrentPlayerID uint Players []Player Deck *Deck } diff --git a/Backend/service/repository/mysql/card_repository.go b/Backend/service/repository/mysql/card_repository.go index 586730d..5ce0497 100644 --- a/Backend/service/repository/mysql/card_repository.go +++ b/Backend/service/repository/mysql/card_repository.go @@ -52,21 +52,21 @@ func (c *CardRepository) GetCards(ctx context.Context) ([]*repository.Card, erro return cards, nil } -func (c *CardRepository) GetPlayerCardsByPlayerId(ctx context.Context, id int, gid int) ([]*repository.Card, error) { +func (c *CardRepository) GetPlayerCardsByPlayerIdWithGameId(ctx context.Context, playerId uint, gameId uint) ([]*repository.Card, error) { var playerCards []*repository.PlayerCard var cards []*repository.Card - result := c.db.Find(&playerCards, "player_id = ? AND game_id = ?", id, gid) + result := c.db.Find(&playerCards, "player_id = ? AND game_id = ?", playerId, gameId) if result.Error != nil { return nil, result.Error } var cardIDs []uint for _, pc := range playerCards { - cardIDs = append(cardIDs, pc.CardId) + cardIDs = append(cardIDs, pc.CardID) } - result = c.db.Find(&cards, "id IN ?", cardIDs) + result = c.db.Find(&cards, "playerId IN ?", cardIDs) if result.Error != nil { return nil, result.Error } diff --git a/Backend/service/repository/mysql/game_process_repository.go b/Backend/service/repository/mysql/game_process_repository.go index fb2ea39..fd2be1d 100644 --- a/Backend/service/repository/mysql/game_process_repository.go +++ b/Backend/service/repository/mysql/game_process_repository.go @@ -41,7 +41,7 @@ func (g *GameProgressRepository) GetGameProgresses(c context.Context, targetPlay func (g *GameProgressRepository) UpdateGameProgress(c context.Context, gameProgress *repository.GameProgresses, nextPlayerId uint) (*repository.GameProgresses, error) { result := g.db.First(&gameProgress) - gameProgress.TargetPlayerId = nextPlayerId + gameProgress.TargetPlayerID = nextPlayerId g.db.Save(&gameProgress) if result.Error != nil { diff --git a/Backend/service/repository/mysql/game_repository.go b/Backend/service/repository/mysql/game_repository.go index 1aa48df..a34c04d 100644 --- a/Backend/service/repository/mysql/game_repository.go +++ b/Backend/service/repository/mysql/game_repository.go @@ -69,3 +69,19 @@ func (g *GameRepository) UpdateGame(ctx context.Context, game *repository.Game) return nil } + +func (g *GameRepository) CreateGameWithPlayers(c context.Context, game *repository.Game) (*repository.Game, error) { + err := g.db.Transaction(func(tx *gorm.DB) error { + if err := tx.Create(&game).Error; err != nil { + return err + } + + return nil + }) + + if err != nil { + return game, err + } + + return game, nil +} diff --git a/Backend/service/repository/player_card_repository.go b/Backend/service/repository/player_card_repository.go index a721ea0..8cd6d2e 100644 --- a/Backend/service/repository/player_card_repository.go +++ b/Backend/service/repository/player_card_repository.go @@ -8,11 +8,11 @@ import ( type PlayerCard struct { gorm.Model - PlayerId uint - CardId uint + PlayerID uint + CardID uint Type string - Card Card `gorm:"foreignKey:CardId"` - Player Player `gorm:"foreignKey:PlayerId"` + Card Card `gorm:"foreignKey:CardID"` + Player Player `gorm:"foreignKey:PlayerID"` } type PlayerCardRepository interface { diff --git a/Backend/service/repository/player_repository.go b/Backend/service/repository/player_repository.go index ed48564..dcdb04b 100644 --- a/Backend/service/repository/player_repository.go +++ b/Backend/service/repository/player_repository.go @@ -8,14 +8,14 @@ import ( type Player struct { gorm.Model - GameId uint `gorm:"foreignKey:GameId;references:ID"` - UserId string + GameID uint `gorm:"foreignKey:GameID;references:ID"` + UserID string Name string IdentityCard string Status string Priority int PlayerCards []PlayerCard - Game *Game `gorm:"foreignKey:GameId;references:ID"` + Game *Game `gorm:"foreignKey:GameID;references:ID"` } type PlayerRepository interface { diff --git a/Backend/service/request/game_request.go b/Backend/service/request/game_request.go index 6b1e997..14c5eb8 100644 --- a/Backend/service/request/game_request.go +++ b/Backend/service/request/game_request.go @@ -1,12 +1,17 @@ package request type CreateGameRequest struct { - Players []PlayerInfo `json:"players"` + RoomID string `json:"roomId" binding:"required"` + Players []PlayerInfo `json:"players" binding:"required"` +} + +type GetGameRequest struct { + GameID uint `json:"gameId" binding:"required"` } type PlayerInfo struct { - ID string `json:"id"` - Name string `json:"name"` + ID string `json:"id" binding:"required"` + Name string `json:"nickname" binding:"required"` } type CreateGameResponse struct { diff --git a/Backend/service/service/card_service.go b/Backend/service/service/card_service.go index d703aa9..6d25141 100644 --- a/Backend/service/service/card_service.go +++ b/Backend/service/service/card_service.go @@ -2,6 +2,9 @@ package service import ( "context" + "errors" + "math/rand" + "time" "github.com/Game-as-a-Service/The-Message/service/repository" ) @@ -31,23 +34,40 @@ func NewCardService(opts *CardServiceOptions) CardService { func (c *CardService) GetCards(ctx context.Context) ([]*repository.Card, error) { cards, err := c.CardRepo.GetCards(ctx) + if err != nil { return nil, err } + + if len(cards) == 0 { + return nil, errors.New("no cards found") + } + return cards, nil } -func (c *CardService) GetPlayerCardsByPlayerId(ctx context.Context, id int) ([]*repository.Card, error) { +func (c *CardService) GetPlayerCardsByPlayerId(ctx context.Context, id uint) ([]*repository.Card, error) { player, err := c.PlayerRepo.GetPlayerById(ctx, id) - game, err := c.GameRepo.GetGameWithPlayers(ctx, player.GameId) + game, err := c.GameRepo.GetGameWithPlayers(ctx, player.GameID) if err != nil { return nil, err } - cards, err := c.CardRepo.GetPlayerCardsByPlayerId(ctx, player.Id, game.Id) + cards, err := c.CardRepo.GetPlayerCardsByPlayerIdWithGameId(ctx, player.ID, game.ID) if err != nil { return nil, err } + return cards, nil } + +func (c *CardService) ShuffleCards(ctx context.Context, cards []*repository.Card) []*repository.Card { + rand.New(rand.NewSource(time.Now().UnixNano())) + + rand.Shuffle(len(cards), func(i, j int) { + cards[i], cards[j] = cards[j], cards[i] + }) + + return cards +} diff --git a/Backend/service/service/deck_service.go b/Backend/service/service/deck_service.go index c85952b..cc7391c 100644 --- a/Backend/service/service/deck_service.go +++ b/Backend/service/service/deck_service.go @@ -25,39 +25,6 @@ func NewDeckService(opts *DeckServiceOptions) DeckService { } } -func (d *DeckService) InitDeck(c context.Context, game *repository.Game) error { - cards, err := d.CardService.GetCards(c) - if err != nil { - return err - } - - cards = d.ShuffleDeck(cards) - - var deck []*repository.Deck - - for _, card := range cards { - card, err := d.DeckRepo.CreateDeck(c, &repository.Deck{ - GameId: game.Id, - CardId: card.Id, - }) - if err != nil { - return err - } - deck = append(deck, card) - } - - return nil -} - -func (d *DeckService) ShuffleDeck(cards []*repository.Card) []*repository.Card { - rand.New(rand.NewSource(time.Now().UnixNano())) - - rand.Shuffle(len(cards), func(i, j int) { - cards[i], cards[j] = cards[j], cards[i] - }) - return cards -} - func (d *DeckService) CreateDeck(c context.Context, deck *repository.Deck) (*repository.Deck, error) { deck, err := d.DeckRepo.CreateDeck(c, deck) if err != nil { @@ -82,3 +49,40 @@ func (d *DeckService) DeleteDeckFromGame(c context.Context, id uint) error { } return nil } + +func (d *DeckService) InitDeck(c context.Context, game *repository.Game) error { + cards, err := d.CardService.GetCards(c) + if err != nil { + return err + } + + cards = d.ShuffleDeck(cards) + + deck := &repository.Deck{ + GameID: game.ID, + Cards: []repository.DeckCard{}, + } + + for _, card := range cards { + deck.Cards = append(deck.Cards, repository.DeckCard{ + CardID: card.ID, + }) + } + + _, err = d.DeckRepo.CreateDeck(c, deck) + + if err != nil { + return err + } + + return nil +} + +func (d *DeckService) ShuffleDeck(cards []*repository.Card) []*repository.Card { + rand.New(rand.NewSource(time.Now().UnixNano())) + + rand.Shuffle(len(cards), func(i, j int) { + cards[i], cards[j] = cards[j], cards[i] + }) + return cards +} diff --git a/Backend/service/service/game_service.go b/Backend/service/service/game_service.go index bfae93f..a65e456 100644 --- a/Backend/service/service/game_service.go +++ b/Backend/service/service/game_service.go @@ -2,11 +2,14 @@ package service import ( "context" - "crypto/rand" - "encoding/hex" + "errors" + "math/rand" + "time" "github.com/Game-as-a-Service/The-Message/enums" "github.com/Game-as-a-Service/The-Message/service/repository" + "github.com/Game-as-a-Service/The-Message/service/request" + "github.com/gin-gonic/gin" ) type GameService struct { @@ -32,16 +35,12 @@ func NewGameService(opts *GameServiceOptions) GameService { } } -func (g *GameService) InitGame(c context.Context) (*repository.Game, error) { - token, err := g.GenerateSecureToken(256) - if err != nil { - return nil, err - } - +func (g *GameService) InitGame(c context.Context, roomId string) (*repository.Game, error) { game, err := g.CreateGame(c, &repository.Game{ - RoomId: roomId, + RoomID: roomId, Status: enums.GameStart, }) + if err != nil { return nil, err } @@ -49,19 +48,23 @@ func (g *GameService) InitGame(c context.Context) (*repository.Game, error) { return game, nil } -func (g *GameService) InitDeck(c context.Context, game *repository.Game) error { - err := g.DeckService.InitDeck(c, game) +func (g *GameService) InitCards(c context.Context) ([]*repository.Card, error) { + cards, err := g.CardService.GetCards(c) + if err != nil { - return err + return nil, err } - return nil + + cards = g.CardService.ShuffleCards(c, cards) + + return cards, nil } func (g *GameService) DrawCard(c context.Context, game *repository.Game, player *repository.Player, drawCards []*repository.Deck, count int) error { for i := 0; i < count; i++ { card := &repository.PlayerCard{ - PlayerId: player.ID, - CardId: drawCards[i].ID, + PlayerID: player.ID, + CardID: drawCards[i].ID, Type: "hand", } err := g.PlayerService.CreatePlayerCard(c, card) @@ -76,6 +79,14 @@ func (g *GameService) DrawCard(c context.Context, game *repository.Game, player return nil } +func (g *GameService) InitDeck(c context.Context, game *repository.Game) error { + err := g.DeckService.InitDeck(c, game) + if err != nil { + return err + } + return nil +} + func (g *GameService) DrawCardsForAllPlayers(c context.Context, game *repository.Game) error { players, err := g.PlayerService.GetPlayersByGameId(c, game.ID) if err != nil { @@ -92,14 +103,6 @@ func (g *GameService) DrawCardsForAllPlayers(c context.Context, game *repository return nil } -func (g *GameService) GenerateSecureToken(n int) (string, error) { - bytes := make([]byte, n) - if _, err := rand.Read(bytes); err != nil { - return "", err - } - return hex.EncodeToString(bytes), nil -} - func (g *GameService) CreateGame(c context.Context, game *repository.Game) (*repository.Game, error) { game, err := g.GameRepo.CreateGame(c, game) if err != nil { @@ -125,7 +128,7 @@ func (g *GameService) DeleteGame(c context.Context, id uint) error { } func (g *GameService) UpdateCurrentPlayer(c context.Context, game *repository.Game, playerId uint) { - game.CurrentPlayerId = playerId + game.CurrentPlayerID = playerId err := g.GameRepo.UpdateGame(c, game) if err != nil { panic(err) @@ -146,10 +149,10 @@ func (g *GameService) NextPlayer(c context.Context, player *repository.Player) ( } if currentPlayerIndex+1 >= len(players) { - player.Game.CurrentPlayerId = players[0].ID + player.Game.CurrentPlayerID = players[0].ID player.Game.Status = enums.TransmitIntelligenceStage } else { - player.Game.CurrentPlayerId = players[currentPlayerIndex+1].ID + player.Game.CurrentPlayerID = players[currentPlayerIndex+1].ID } return player.Game, nil } @@ -161,3 +164,91 @@ func (g *GameService) UpdateStatus(c context.Context, game *repository.Game, sta panic(err) } } + +func (g *GameService) CreateGameWithPlayers(c *gin.Context, req request.CreateGameRequest, cards []*repository.Card) (*repository.Game, error) { + // Get the number of players and assign identity cards + length := len(req.Players) + identityCards, err := g.AssignIdentityCards(c, length) + + if err != nil { + return nil, err + } + + // Create the players + var players []repository.Player + for i, playerReq := range req.Players { + player := repository.Player{ + UserID: playerReq.ID, + Name: playerReq.Name, + Priority: i, + IdentityCard: identityCards[i], + Status: enums.PlayerStatusAlive, + PlayerCards: []repository.PlayerCard{}, + } + + // Each player gets 3 cards + for j := 0; j < 3; j++ { + player.PlayerCards = append(player.PlayerCards, repository.PlayerCard{ + CardID: cards[i*3+j].ID, + Type: "hand", + }) + } + + players = append(players, player) + } + + // Remove the player cards and create the deck + deck := &repository.Deck{ + Cards: []repository.DeckCard{}, + } + + for _, card := range cards[length:] { + deck.Cards = append(deck.Cards, repository.DeckCard{ + CardID: card.ID, + }) + } + + // Create a game and players in one transaction + game := &repository.Game{ + RoomID: req.RoomID, + Players: players, + Status: enums.ActionCardStage, + Deck: deck, + } + + game, err = g.GameRepo.CreateGameWithPlayers(c, game) + + if err != nil { + return nil, err + } + + return game, nil +} + +func (g *GameService) AssignIdentityCards(c context.Context, playCount int) ([]string, error) { + var result []string + + if playCount == 3 { + result = []string{enums.UndercoverFront, enums.MilitaryAgency, enums.Bystander} + } else if playCount == 4 { + result = []string{enums.UndercoverFront, enums.MilitaryAgency, enums.Bystander, enums.Bystander} + } else if playCount == 5 { + result = []string{enums.UndercoverFront, enums.UndercoverFront, enums.MilitaryAgency, enums.MilitaryAgency, enums.Bystander} + } else if playCount == 6 { + result = []string{enums.UndercoverFront, enums.UndercoverFront, enums.MilitaryAgency, enums.MilitaryAgency, enums.Bystander, enums.Bystander} + } else if playCount == 7 { + result = []string{enums.UndercoverFront, enums.UndercoverFront, enums.UndercoverFront, enums.MilitaryAgency, enums.MilitaryAgency, enums.MilitaryAgency, enums.Bystander} + } else if playCount == 8 { + result = []string{enums.UndercoverFront, enums.UndercoverFront, enums.UndercoverFront, enums.MilitaryAgency, enums.MilitaryAgency, enums.MilitaryAgency, enums.Bystander, enums.Bystander} + } else if playCount == 9 { + result = []string{enums.UndercoverFront, enums.UndercoverFront, enums.UndercoverFront, enums.UndercoverFront, enums.MilitaryAgency, enums.MilitaryAgency, enums.MilitaryAgency, enums.MilitaryAgency, enums.Bystander} + } else { + return nil, errors.New("invalid play count") + } + + rand.Seed(time.Now().UnixNano()) + rand.Shuffle(len(result), func(i, j int) { + result[i], result[j] = result[j], result[i] + }) + return result, nil +} diff --git a/Backend/service/service/player_service.go b/Backend/service/service/player_service.go index 3a59815..c19d9ad 100644 --- a/Backend/service/service/player_service.go +++ b/Backend/service/service/player_service.go @@ -41,7 +41,7 @@ func (p *PlayerService) InitPlayers(c context.Context, game *repository.Game, re for i, reqPlayer := range req.Players { _, err := p.CreatePlayer(c, &repository.Player{ Name: reqPlayer.Name, - GameId: game.ID, + GameID: game.ID, IdentityCard: identityCards[i], Priority: i + 1, Status: enums.PlayerStatusAlive, @@ -82,7 +82,7 @@ func (p *PlayerService) CanPlayCard(c context.Context, player *repository.Player return false, errors.New("你已死亡") } - if player.Game.CurrentPlayerId != player.ID { + if player.Game.CurrentPlayerID != player.ID { return false, errors.New("尚未輪到你出牌") } @@ -133,7 +133,7 @@ func (p *PlayerService) GetPlayersByGameId(c context.Context, id uint) ([]*repos func (p *PlayerService) GetHandCardId(player *repository.Player, cardId uint) (*repository.PlayerCard, error) { for _, card := range player.PlayerCards { - if card.CardId == cardId && card.Type == "hand" { + if card.CardID == cardId && card.Type == "hand" { return &card, nil } } @@ -201,11 +201,11 @@ func (p *PlayerService) TransmitIntelligenceCard(c context.Context, playerId uin } _, err = p.GameProgressRepo.CreateGameProgress(c, &repository.GameProgresses{ - GameId: game.ID, - PlayerId: playerId, - CardId: cardId, + GameID: game.ID, + PlayerID: playerId, + CardID: cardId, Action: enums.TransmitIntelligence, - TargetPlayerId: game.CurrentPlayerId, + TargetPlayerID: game.CurrentPlayerID, }) if err != nil { @@ -236,13 +236,13 @@ func (p *PlayerService) AcceptCard(c context.Context, playerId uint, accept bool if err != nil { return false, err } - cardId := gameProgress.CardId + cardId := gameProgress.CardID // assume the type is SecretTelegram res := accept if accept { _, err := p.PlayerCardRepo.CreatePlayerCard(c, &repository.PlayerCard{ - PlayerId: playerId, - CardId: cardId, + PlayerID: playerId, + CardID: cardId, Type: "intelligence", }) if err != nil { @@ -251,7 +251,7 @@ func (p *PlayerService) AcceptCard(c context.Context, playerId uint, accept bool p.GameServ.UpdateStatus(c, game, enums.ActionCardStage) } else { - _, err := p.GameProgressRepo.UpdateGameProgress(c, gameProgress, game.CurrentPlayerId) + _, err := p.GameProgressRepo.UpdateGameProgress(c, gameProgress, game.CurrentPlayerID) if err != nil { return false, err } From 46a1f5473da22125626f0f77c910d160d226e431 Mon Sep 17 00:00:00 2001 From: KazeNoYumeX Date: Thu, 21 Mar 2024 15:22:41 +0800 Subject: [PATCH 05/10] Feat: Added create game player count validation --- Backend/cmd/app/docs/docs.go | 32 ++++++++----------- .../service/delivery/http/v1/game_handler.go | 17 +++++----- 2 files changed, 22 insertions(+), 27 deletions(-) diff --git a/Backend/cmd/app/docs/docs.go b/Backend/cmd/app/docs/docs.go index 1a72e82..28955a5 100644 --- a/Backend/cmd/app/docs/docs.go +++ b/Backend/cmd/app/docs/docs.go @@ -81,7 +81,7 @@ const docTemplate = `{ } } }, - "/api/v1/heartbeat": { + "/api/v1/health": { "get": { "description": "Check if the server is alive", "consumes": [ @@ -159,15 +159,6 @@ const docTemplate = `{ "schema": { "$ref": "#/definitions/request.PlayCardRequest" } - }, - { - "description": "Intelligence Type", - "name": "intelligence_type", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.PlayCardRequest" - } } ], "responses": { @@ -288,22 +279,26 @@ const docTemplate = `{ }, "request.CreateGameRequest": { "type": "object", + "required": [ + "players", + "roomId" + ], "properties": { "players": { "type": "array", "items": { "$ref": "#/definitions/request.PlayerInfo" } + }, + "roomId": { + "type": "string" } } }, "request.CreateGameResponse": { "type": "object", "properties": { - "id": { - "type": "string" - }, - "token": { + "url": { "type": "string" } } @@ -313,9 +308,6 @@ const docTemplate = `{ "properties": { "card_id": { "type": "integer" - }, - "intelligence_type": { - "type": "integer" } } }, @@ -341,11 +333,15 @@ const docTemplate = `{ }, "request.PlayerInfo": { "type": "object", + "required": [ + "id", + "nickname" + ], "properties": { "id": { "type": "string" }, - "name": { + "nickname": { "type": "string" } } diff --git a/Backend/service/delivery/http/v1/game_handler.go b/Backend/service/delivery/http/v1/game_handler.go index d619e32..5f9606a 100644 --- a/Backend/service/delivery/http/v1/game_handler.go +++ b/Backend/service/delivery/http/v1/game_handler.go @@ -11,7 +11,6 @@ import ( "github.com/Game-as-a-Service/The-Message/service/request" "github.com/Game-as-a-Service/The-Message/service/service" "github.com/gin-gonic/gin" - "github.com/joho/godotenv" ) type GameHandler struct { @@ -48,7 +47,13 @@ func (g *GameHandler) StartGame(c *gin.Context) { var req request.CreateGameRequest if err := c.ShouldBindJSON(&req); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + c.JSON(http.StatusBadRequest, gin.H{"message": err.Error()}) + return + } + + // Players must be at least 3 and at most 9 + if len(req.Players) < 3 || len(req.Players) > 9 { + c.JSON(http.StatusBadRequest, gin.H{"message": "Players must be at least 3 and at most 9"}) return } @@ -73,17 +78,11 @@ func (g *GameHandler) StartGame(c *gin.Context) { "next_player": game.Players[0].ID, } - // Load .env file - err = godotenv.Load() - if err != nil { - log.Fatal("Error loading .env file") - } - url := os.Getenv("APP_FRONTEND_URL") version := os.Getenv("APP_VERSION") c.JSON(http.StatusOK, gin.H{ - "uri": url + version + "/games/" + strconv.Itoa(int(game.ID)), + "url": url + version + "/games/" + strconv.Itoa(int(game.ID)), }) } From 5232cbd84c8472de5ff33cd24ce6760c608b3ea0 Mon Sep 17 00:00:00 2001 From: KazeNoYumeX Date: Thu, 21 Mar 2024 15:23:30 +0800 Subject: [PATCH 06/10] Test: Added e2e test for creating game --- Backend/tests/e2e/game_api_test.go | 161 +++++++++++++++++++++++------ Backend/utils/json.go | 19 ++++ Backend/utils/response_writer.go | 1 - 3 files changed, 147 insertions(+), 34 deletions(-) create mode 100644 Backend/utils/json.go delete mode 100644 Backend/utils/response_writer.go diff --git a/Backend/tests/e2e/game_api_test.go b/Backend/tests/e2e/game_api_test.go index 4e2f565..4348263 100644 --- a/Backend/tests/e2e/game_api_test.go +++ b/Backend/tests/e2e/game_api_test.go @@ -1,54 +1,149 @@ package e2e import ( - "context" "encoding/json" + "io" + "math/rand" "net/http" + "os" + "strconv" + "testing" + "github.com/Game-as-a-Service/The-Message/database/seeders" + "github.com/Game-as-a-Service/The-Message/enums" + "github.com/Game-as-a-Service/The-Message/service/repository" + "github.com/Game-as-a-Service/The-Message/service/request" + "github.com/Game-as-a-Service/The-Message/utils" + "github.com/go-faker/faker/v4" "github.com/stretchr/testify/assert" ) func (suite *IntegrationTestSuite) TestStartGameE2E() { - players := []Player{ - {ID: "6497f6f226b40d440b9a90cc", Name: "A"}, - {ID: "6498112b26b40d440b9a90ce", Name: "B"}, - {ID: "6499df157fed0c21a4fd0425", Name: "C"}, - } - requestBody := StartGameRequest{Players: players} - - jsonBody, err := json.Marshal(requestBody) - if err != nil { - suite.T().Fatalf("Failed to marshal JSON: %v", err) - } + // Set the cards + seeders.SeederCards(suite.db) api := "/api/v1/games" - resp := suite.requestJson(api, jsonBody, http.MethodPost) - assert.Equal(suite.T(), 200, resp.StatusCode) + suite.T().Run("it can validate room id and players", func(t *testing.T) { + invalidRoomReq := utils.RequestToJsonBody(struct { + Players []request.PlayerInfo `json:"players"` + }{ + Players: []request.PlayerInfo{}, + }) - responseJson := suite.responseJson(resp) + invalidPlayerReq := utils.RequestToJsonBody(struct { + RoomID string `json:"room_id"` + }{ + RoomID: faker.UUIDDigit(), + }) - assert.NotNil(suite.T(), responseJson["Token"], "JSON response should contain a 'Token' field") - assert.NotNil(suite.T(), responseJson["Id"], "JSON response should contain a 'Id' field") + invalidRoomRes := suite.requestJson(api, invalidRoomReq, http.MethodPost) + invalidPlayerRes := suite.requestJson(api, invalidPlayerReq, http.MethodPost) - // 驗證Game內的玩家都持有identity - game, _ := suite.gameRepo.GetGameWithPlayers(context.TODO(), uint(int(responseJson["Id"].(float64)))) + // Convert response body from json to map + resBodyAsByteArray, _ := io.ReadAll(invalidRoomRes.Body) + resBody := make(map[string]interface{}) + _ = json.Unmarshal(resBodyAsByteArray, &resBody) - assert.NotEmpty(suite.T(), game.Players[0].IdentityCard) - assert.NotEmpty(suite.T(), game.Players[1].IdentityCard) - assert.NotEmpty(suite.T(), game.Players[2].IdentityCard) + assert.Equal(t, http.StatusBadRequest, invalidRoomRes.StatusCode) + assert.Equal(t, http.StatusBadRequest, invalidPlayerRes.StatusCode) + }) - for _, player := range game.Players { - playerCards, _ := suite.playerRepo.GetPlayerWithPlayerCards(context.TODO(), player.ID) - assert.NotEmpty(suite.T(), playerCards.PlayerCards) - } -} + suite.T().Run("it can fail when player count less than 3", func(t *testing.T) { + num := rand.Intn(3) -type Player struct { - ID string `json:"id"` - Name string `json:"name"` -} + var players []request.PlayerInfo + for i := 0; i < num; i++ { + player := request.PlayerInfo{ + ID: faker.UUIDDigit(), + Name: faker.FirstName(), + } + players = append(players, player) + } + + req := utils.RequestToJsonBody(request.CreateGameRequest{ + RoomID: faker.UUIDDigit(), + Players: players, + }) + + res := suite.requestJson(api, req, http.MethodPost) + + // Convert response body from json to map + resBody := utils.JsonBodyToMap(res) + + assert.Equal(t, http.StatusBadRequest, res.StatusCode) + assert.Equal(t, "Players must be at least 3 and at most 9", resBody["message"]) + }) + + suite.T().Run("it can fail when player count more than 9", func(t *testing.T) { + num := 10 + + var players []request.PlayerInfo + for i := 0; i < num; i++ { + player := request.PlayerInfo{ + ID: faker.UUIDDigit(), + Name: faker.FirstName(), + } + players = append(players, player) + } + + req := utils.RequestToJsonBody(request.CreateGameRequest{ + RoomID: faker.UUIDDigit(), + Players: players, + }) + + res := suite.requestJson(api, req, http.MethodPost) + + // Convert response body from json to map + resBody := utils.JsonBodyToMap(res) + + assert.Equal(t, http.StatusBadRequest, res.StatusCode) + assert.Equal(t, "Players must be at least 3 and at most 9", resBody["message"]) + }) + + suite.T().Run("it can success when player count between 3 and 9", func(t *testing.T) { + // Set up environment variables + frontendURL := os.Getenv("APP_FRONTEND_URL") + version := os.Getenv("APP_VERSION") + suite.T().Setenv("APP_FRONTEND_URL", frontendURL) + suite.T().Setenv("APP_VERSION", version) + + num := rand.Intn(7) + 3 + roomID := faker.UUIDDigit() + url := frontendURL + version + "/games/" + strconv.Itoa(1) + + var players []request.PlayerInfo + for i := 0; i < num; i++ { + player := request.PlayerInfo{ + ID: faker.UUIDDigit(), + Name: faker.FirstName(), + } + players = append(players, player) + } + + req := utils.RequestToJsonBody(request.CreateGameRequest{ + RoomID: roomID, + Players: players, + }) + + res := suite.requestJson(api, req, http.MethodPost) + + // Convert response body from json to map + resBody := utils.JsonBodyToMap(res) + + assert.Equal(t, http.StatusOK, res.StatusCode) + assert.Equal(t, url, resBody["url"]) + + // Assert Game and Players + game := new(repository.Game) + _ = suite.db.First(&game, "id = ?", 1) + + assert.Equal(t, roomID, game.RoomID) + assert.Equal(t, enums.ActionCardStage, game.Status) + + gamePlayers := new([]repository.Player) + _ = suite.db.Find(&gamePlayers, "game_id = ?", game.ID) -type StartGameRequest struct { - Players []Player `json:"players"` + assert.Equal(t, num, len(*gamePlayers)) + }) } diff --git a/Backend/utils/json.go b/Backend/utils/json.go new file mode 100644 index 0000000..e845b10 --- /dev/null +++ b/Backend/utils/json.go @@ -0,0 +1,19 @@ +package utils + +import ( + "encoding/json" + "io" + "net/http" +) + +func RequestToJsonBody(requestBody any) []byte { + bytesRepresentation, _ := json.Marshal(requestBody) + return bytesRepresentation +} + +func JsonBodyToMap(response *http.Response) map[string]interface{} { + responseBodyAsByteArray, _ := io.ReadAll(response.Body) + responseBody := make(map[string]interface{}) + _ = json.Unmarshal(responseBodyAsByteArray, &responseBody) + return responseBody +} diff --git a/Backend/utils/response_writer.go b/Backend/utils/response_writer.go deleted file mode 100644 index d4b585b..0000000 --- a/Backend/utils/response_writer.go +++ /dev/null @@ -1 +0,0 @@ -package utils From f9ae51c0a3224b36377014f96d5c02fd79e1caa6 Mon Sep 17 00:00:00 2001 From: KazeNoYumeX Date: Thu, 21 Mar 2024 15:28:34 +0800 Subject: [PATCH 07/10] Fix: Removed the game ID that is no longer needed from GetPlayerCards --- Backend/service/repository/card_repository.go | 2 +- Backend/service/repository/mysql/card_repository.go | 6 +++--- Backend/service/service/card_service.go | 3 +-- Backend/tests/e2e/player_card_api_test.go | 9 ++++----- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/Backend/service/repository/card_repository.go b/Backend/service/repository/card_repository.go index 2668a47..833caf8 100644 --- a/Backend/service/repository/card_repository.go +++ b/Backend/service/repository/card_repository.go @@ -17,5 +17,5 @@ type CardRepository interface { GetCardById(ctx context.Context, id uint) (*Card, error) CreateCard(ctx context.Context, card *Card) (*Card, error) GetCards(ctx context.Context) ([]*Card, error) - GetPlayerCardsByPlayerIdWithGameId(ctx context.Context, playerId uint, gameId uint) ([]*Card, error) + GetPlayerCardsByPlayerIdWithGameId(ctx context.Context, playerId uint) ([]*Card, error) } diff --git a/Backend/service/repository/mysql/card_repository.go b/Backend/service/repository/mysql/card_repository.go index 5ce0497..89fcc0a 100644 --- a/Backend/service/repository/mysql/card_repository.go +++ b/Backend/service/repository/mysql/card_repository.go @@ -52,11 +52,11 @@ func (c *CardRepository) GetCards(ctx context.Context) ([]*repository.Card, erro return cards, nil } -func (c *CardRepository) GetPlayerCardsByPlayerIdWithGameId(ctx context.Context, playerId uint, gameId uint) ([]*repository.Card, error) { +func (c *CardRepository) GetPlayerCardsByPlayerIdWithGameId(ctx context.Context, playerId uint) ([]*repository.Card, error) { var playerCards []*repository.PlayerCard var cards []*repository.Card - result := c.db.Find(&playerCards, "player_id = ? AND game_id = ?", playerId, gameId) + result := c.db.Find(&playerCards, "player_id = ?", playerId) if result.Error != nil { return nil, result.Error @@ -66,7 +66,7 @@ func (c *CardRepository) GetPlayerCardsByPlayerIdWithGameId(ctx context.Context, cardIDs = append(cardIDs, pc.CardID) } - result = c.db.Find(&cards, "playerId IN ?", cardIDs) + result = c.db.Find(&cards, "ID IN ?", cardIDs) if result.Error != nil { return nil, result.Error } diff --git a/Backend/service/service/card_service.go b/Backend/service/service/card_service.go index 6d25141..8c053f6 100644 --- a/Backend/service/service/card_service.go +++ b/Backend/service/service/card_service.go @@ -48,12 +48,11 @@ func (c *CardService) GetCards(ctx context.Context) ([]*repository.Card, error) func (c *CardService) GetPlayerCardsByPlayerId(ctx context.Context, id uint) ([]*repository.Card, error) { player, err := c.PlayerRepo.GetPlayerById(ctx, id) - game, err := c.GameRepo.GetGameWithPlayers(ctx, player.GameID) if err != nil { return nil, err } - cards, err := c.CardRepo.GetPlayerCardsByPlayerIdWithGameId(ctx, player.ID, game.ID) + cards, err := c.CardRepo.GetPlayerCardsByPlayerIdWithGameId(ctx, player.ID) if err != nil { return nil, err diff --git a/Backend/tests/e2e/player_card_api_test.go b/Backend/tests/e2e/player_card_api_test.go index a9af03f..9473799 100644 --- a/Backend/tests/e2e/player_card_api_test.go +++ b/Backend/tests/e2e/player_card_api_test.go @@ -11,13 +11,13 @@ import ( ) func (suite *IntegrationTestSuite) TestGetPlayerCards() { - // given + ID := uint(1) game := repository.Game{} _, err := suite.gameRepo.CreateGame(context.TODO(), &game) player := repository.Player{ Name: "player1", - GameId: 1, + GameID: ID, IdentityCard: "醬油", } _, err = suite.playerRepo.CreatePlayer(context.TODO(), &player) @@ -26,9 +26,8 @@ func (suite *IntegrationTestSuite) TestGetPlayerCards() { } _, err = suite.playerCardRepo.CreatePlayerCard(context.TODO(), &repository.PlayerCard{ - PlayerId: 1, - GameId: 1, - CardId: 1, + PlayerID: ID, + CardID: ID, Type: "hand", }) if err != nil { From 0e8fd0adde50deb70998f91d688158cc9fe7cb8f Mon Sep 17 00:00:00 2001 From: KazeNoYumeX Date: Thu, 21 Mar 2024 16:44:48 +0800 Subject: [PATCH 08/10] Feat: Added game seeder --- Backend/cmd/migrate/game_card_seeder.go | 2 +- Backend/cmd/migrate/game_seeder.go | 14 +++++++ Backend/database/seeders/database_seeder.go | 5 +++ Backend/database/seeders/game_seeder.go | 46 +++++++++++++++++++++ Backend/tests/e2e/suite_test.go | 2 +- 5 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 Backend/cmd/migrate/game_seeder.go create mode 100644 Backend/database/seeders/game_seeder.go diff --git a/Backend/cmd/migrate/game_card_seeder.go b/Backend/cmd/migrate/game_card_seeder.go index 9677b20..768c094 100644 --- a/Backend/cmd/migrate/game_card_seeder.go +++ b/Backend/cmd/migrate/game_card_seeder.go @@ -10,5 +10,5 @@ import ( func main() { db := config.NewDatabase() - seeders.Run(db) + seeders.OnlyCardsRun(db) } diff --git a/Backend/cmd/migrate/game_seeder.go b/Backend/cmd/migrate/game_seeder.go new file mode 100644 index 0000000..9677b20 --- /dev/null +++ b/Backend/cmd/migrate/game_seeder.go @@ -0,0 +1,14 @@ +//go:build migrate + +package main + +import ( + "github.com/Game-as-a-Service/The-Message/config" + "github.com/Game-as-a-Service/The-Message/database/seeders" + _ "github.com/joho/godotenv/autoload" +) + +func main() { + db := config.NewDatabase() + seeders.Run(db) +} diff --git a/Backend/database/seeders/database_seeder.go b/Backend/database/seeders/database_seeder.go index 6ee14a4..1b970ae 100644 --- a/Backend/database/seeders/database_seeder.go +++ b/Backend/database/seeders/database_seeder.go @@ -6,4 +6,9 @@ import ( func Run(db *gorm.DB) { SeederCards(db) + SeederGameWithPlayers(db) +} + +func OnlyCardsRun(db *gorm.DB) { + SeederCards(db) } diff --git a/Backend/database/seeders/game_seeder.go b/Backend/database/seeders/game_seeder.go new file mode 100644 index 0000000..99b25b4 --- /dev/null +++ b/Backend/database/seeders/game_seeder.go @@ -0,0 +1,46 @@ +package seeders + +import ( + "math/rand" + + "github.com/Game-as-a-Service/The-Message/enums" + "github.com/Game-as-a-Service/The-Message/service/repository" + "github.com/go-faker/faker/v4" + "gorm.io/gorm" +) + +func SeederGameWithPlayers(db *gorm.DB) { + // Get all cards + var cards []*repository.Card + _ = db.Find(&cards) + + // Fake game data + game := &repository.Game{ + RoomID: faker.UUIDDigit(), + Status: enums.ActionCardStage, + Players: []repository.Player{}, + } + + // Fake player count random 1~3 + playerCount := rand.Intn(3) + 1 + + // Fake players data + for i := 0; i < playerCount; i++ { + player := repository.Player{ + UserID: faker.UUIDDigit(), + Name: faker.FirstName(), + PlayerCards: []repository.PlayerCard{}, + } + + // Each player gets 3 cards + for j := 0; j < 3; j++ { + player.PlayerCards = append(player.PlayerCards, repository.PlayerCard{ + CardID: cards[i*3+j].ID, + Type: "hand", + }) + } + game.Players = append(game.Players, player) + } + + _ = db.Create(&game) +} diff --git a/Backend/tests/e2e/suite_test.go b/Backend/tests/e2e/suite_test.go index a2f70d9..ecaab09 100644 --- a/Backend/tests/e2e/suite_test.go +++ b/Backend/tests/e2e/suite_test.go @@ -166,7 +166,7 @@ func (suite *IntegrationTestSuite) SetupTest() { //Fixme Run db refresh and seeders config.RunRefresh() db := config.NewDatabase() - seeders.Run(db) + seeders.OnlyCardsRun(db) } func (suite *IntegrationTestSuite) TearDownTest() { From 4311836f7d7eceefca9f85cf61e9c7e822182fc8 Mon Sep 17 00:00:00 2001 From: KazeNoYumeX Date: Thu, 21 Mar 2024 16:46:03 +0800 Subject: [PATCH 09/10] Fix: Removed the game ID that is no longer needed from PlayCard and TransmitIntelligence --- Backend/config/test_database.go | 11 - .../delivery/http/v1/player_handler.go | 8 +- .../mysql/player_card_repository.go | 8 +- .../repository/player_card_repository.go | 4 +- .../service/request/player_card_request.go | 2 +- Backend/service/service/game_service.go | 4 +- Backend/service/service/player_service.go | 8 +- Backend/tests/e2e/game_api_test.go | 8 +- Backend/tests/e2e/player_api_test.go | 226 +++++++++--------- 9 files changed, 130 insertions(+), 149 deletions(-) diff --git a/Backend/config/test_database.go b/Backend/config/test_database.go index 82bc76b..e93e871 100644 --- a/Backend/config/test_database.go +++ b/Backend/config/test_database.go @@ -6,19 +6,8 @@ import ( "os" "github.com/joho/godotenv" - "gorm.io/gorm" ) -func InitTestDB() *gorm.DB { - _, err := LoadEnvPath() - if err != nil { - return nil - } - - db := NewDatabase() - return db -} - func LoadEnvPath() (string, error) { envPath, err := getEnvPath() diff --git a/Backend/service/delivery/http/v1/player_handler.go b/Backend/service/delivery/http/v1/player_handler.go index 4f8ba06..45832a2 100644 --- a/Backend/service/delivery/http/v1/player_handler.go +++ b/Backend/service/delivery/http/v1/player_handler.go @@ -70,9 +70,7 @@ func (p *PlayerHandler) PlayCard(c *gin.Context) { "next_player": game.CurrentPlayerID, } - c.JSON(http.StatusOK, gin.H{ - "result": true, - }) + c.JSON(http.StatusOK, gin.H{}) } // TransmitIntelligence godoc @@ -104,13 +102,13 @@ func (p *PlayerHandler) TransmitIntelligence(c *gin.Context) { } // Check card_id exists in player_cards - exist, err := p.playerService.CheckPlayerCardExist(c, playerId, player.GameID, req.CardID) + exist, err := p.playerService.CheckPlayerCardExist(c, playerId, req.CardID) if err != nil || !exist { c.JSON(http.StatusInternalServerError, gin.H{"message": "Card not found"}) return } - ret, err := p.playerService.TransmitIntelligenceCard(c, playerId, player.GameID, req.CardID) + ret, err := p.playerService.TransmitIntelligenceCard(c, playerId, req.CardID) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) return diff --git a/Backend/service/repository/mysql/player_card_repository.go b/Backend/service/repository/mysql/player_card_repository.go index 9d96224..ac77643 100644 --- a/Backend/service/repository/mysql/player_card_repository.go +++ b/Backend/service/repository/mysql/player_card_repository.go @@ -40,10 +40,10 @@ func (p PlayerCardRepository) DeletePlayerCard(ctx context.Context, id uint) err return nil } -func (p PlayerCardRepository) DeletePlayerCardByPlayerIdAndCardId(ctx context.Context, playerId uint, gameId uint, cardId uint) (bool, error) { +func (p PlayerCardRepository) DeletePlayerCardByPlayerIdAndCardId(ctx context.Context, playerId uint, cardId uint) (bool, error) { card := new(repository.PlayerCard) - result := p.db.Delete(&card, "player_id = ? AND game_id = ? AND card_id = ?", playerId, gameId, cardId) + result := p.db.Delete(&card, "player_id = ? AND card_id = ?", playerId, cardId) if result.Error != nil { return false, result.Error } @@ -51,9 +51,9 @@ func (p PlayerCardRepository) DeletePlayerCardByPlayerIdAndCardId(ctx context.Co return true, nil } -func (p *PlayerCardRepository) ExistPlayerCardByPlayerIdAndCardId(ctx context.Context, playerId uint, gameId uint, cardId uint) (bool, error) { +func (p *PlayerCardRepository) ExistPlayerCardByPlayerIdAndCardId(ctx context.Context, playerId uint, cardId uint) (bool, error) { var card repository.PlayerCard - result := p.db.First(&card, "player_id = ? AND game_id = ? AND card_id = ?", playerId, gameId, cardId) + result := p.db.First(&card, "player_id = ? AND card_id = ?", playerId, cardId) if result.Error != nil { if errors.Is(result.Error, gorm.ErrRecordNotFound) { return false, nil diff --git a/Backend/service/repository/player_card_repository.go b/Backend/service/repository/player_card_repository.go index 8cd6d2e..398a0c2 100644 --- a/Backend/service/repository/player_card_repository.go +++ b/Backend/service/repository/player_card_repository.go @@ -20,7 +20,7 @@ type PlayerCardRepository interface { GetPlayerCardsByGameId(ctx context.Context, id uint) ([]*PlayerCard, error) CreatePlayerCard(ctx context.Context, card *PlayerCard) (*PlayerCard, error) DeletePlayerCard(ctx context.Context, id uint) error - DeletePlayerCardByPlayerIdAndCardId(ctx context.Context, playerId uint, gameId uint, cardId uint) (bool, error) - ExistPlayerCardByPlayerIdAndCardId(ctx context.Context, playerId uint, gameId uint, cardId uint) (bool, error) + DeletePlayerCardByPlayerIdAndCardId(ctx context.Context, playerId uint, cardId uint) (bool, error) + ExistPlayerCardByPlayerIdAndCardId(ctx context.Context, playerId uint, cardId uint) (bool, error) GetPlayerCards(ctx context.Context, playerCard *PlayerCard) (*[]PlayerCard, error) } diff --git a/Backend/service/request/player_card_request.go b/Backend/service/request/player_card_request.go index a283e8e..ca3c2e0 100644 --- a/Backend/service/request/player_card_request.go +++ b/Backend/service/request/player_card_request.go @@ -1,5 +1,5 @@ package request type PlayerCardsResponse struct { - Player_cards []map[string]interface{} `json:"player_cards"` + PlayerCards []map[string]interface{} `json:"player_cards"` } diff --git a/Backend/service/service/game_service.go b/Backend/service/service/game_service.go index a65e456..02c6a30 100644 --- a/Backend/service/service/game_service.go +++ b/Backend/service/service/game_service.go @@ -60,7 +60,7 @@ func (g *GameService) InitCards(c context.Context) ([]*repository.Card, error) { return cards, nil } -func (g *GameService) DrawCard(c context.Context, game *repository.Game, player *repository.Player, drawCards []*repository.Deck, count int) error { +func (g *GameService) DrawCard(c context.Context, player *repository.Player, drawCards []*repository.Deck, count int) error { for i := 0; i < count; i++ { card := &repository.PlayerCard{ PlayerID: player.ID, @@ -94,7 +94,7 @@ func (g *GameService) DrawCardsForAllPlayers(c context.Context, game *repository } for _, player := range players { drawCards, _ := g.DeckService.GetDecksByGameId(c, game.ID) - err2 := g.DrawCard(c, game, player, drawCards, 3) + err2 := g.DrawCard(c, player, drawCards, 3) if err2 != nil { return err2 } diff --git a/Backend/service/service/player_service.go b/Backend/service/service/player_service.go index c19d9ad..8aec416 100644 --- a/Backend/service/service/player_service.go +++ b/Backend/service/service/player_service.go @@ -89,8 +89,8 @@ func (p *PlayerService) CanPlayCard(c context.Context, player *repository.Player return true, nil } -func (p *PlayerService) CheckPlayerCardExist(c context.Context, playerId uint, gameId uint, cardId uint) (bool, error) { - exist, err := p.PlayerCardRepo.ExistPlayerCardByPlayerIdAndCardId(c, playerId, gameId, cardId) +func (p *PlayerService) CheckPlayerCardExist(c context.Context, playerId uint, cardId uint) (bool, error) { + exist, err := p.PlayerCardRepo.ExistPlayerCardByPlayerIdAndCardId(c, playerId, cardId) if err != nil { return false, err @@ -174,7 +174,7 @@ func (p *PlayerService) PlayCard(c context.Context, playerId uint, cardId uint) return game, &handCard.Card, nil } -func (p *PlayerService) TransmitIntelligenceCard(c context.Context, playerId uint, gameId uint, cardId uint) (bool, error) { +func (p *PlayerService) TransmitIntelligenceCard(c context.Context, playerId uint, cardId uint) (bool, error) { player, err := p.PlayerRepo.GetPlayerWithGamePlayersAndPlayerCardsCard(c, playerId) if err != nil { return false, err @@ -190,7 +190,7 @@ func (p *PlayerService) TransmitIntelligenceCard(c context.Context, playerId uin return false, err } - ret, err := p.PlayerCardRepo.DeletePlayerCardByPlayerIdAndCardId(c, playerId, gameId, cardId) + ret, err := p.PlayerCardRepo.DeletePlayerCardByPlayerIdAndCardId(c, playerId, cardId) if err != nil { return false, err } diff --git a/Backend/tests/e2e/game_api_test.go b/Backend/tests/e2e/game_api_test.go index 4348263..4e54920 100644 --- a/Backend/tests/e2e/game_api_test.go +++ b/Backend/tests/e2e/game_api_test.go @@ -20,7 +20,7 @@ import ( func (suite *IntegrationTestSuite) TestStartGameE2E() { // Set the cards - seeders.SeederCards(suite.db) + seeders.OnlyCardsRun(suite.db) api := "/api/v1/games" @@ -50,7 +50,7 @@ func (suite *IntegrationTestSuite) TestStartGameE2E() { }) suite.T().Run("it can fail when player count less than 3", func(t *testing.T) { - num := rand.Intn(3) + num := rand.Intn(2) + 1 var players []request.PlayerInfo for i := 0; i < num; i++ { @@ -135,13 +135,13 @@ func (suite *IntegrationTestSuite) TestStartGameE2E() { assert.Equal(t, url, resBody["url"]) // Assert Game and Players - game := new(repository.Game) + game := &repository.Game{} _ = suite.db.First(&game, "id = ?", 1) assert.Equal(t, roomID, game.RoomID) assert.Equal(t, enums.ActionCardStage, game.Status) - gamePlayers := new([]repository.Player) + gamePlayers := &[]repository.Player{} _ = suite.db.Find(&gamePlayers, "game_id = ?", game.ID) assert.Equal(t, num, len(*gamePlayers)) diff --git a/Backend/tests/e2e/player_api_test.go b/Backend/tests/e2e/player_api_test.go index 7fdde4c..37d82c7 100644 --- a/Backend/tests/e2e/player_api_test.go +++ b/Backend/tests/e2e/player_api_test.go @@ -1,9 +1,6 @@ package e2e import ( - "context" - "encoding/json" - "io" "math" "math/rand" "net/http" @@ -12,7 +9,8 @@ import ( "testing" "github.com/Game-as-a-Service/The-Message/enums" - "github.com/Game-as-a-Service/The-Message/service/request" + "github.com/Game-as-a-Service/The-Message/service/repository" + "github.com/Game-as-a-Service/The-Message/utils" "github.com/go-faker/faker/v4" "github.com/stretchr/testify/assert" ) @@ -20,100 +18,117 @@ import ( func (suite *IntegrationTestSuite) TestPlayCardE2E() { // Given api := "/api/v1/players/{player_id}/player-cards" - game, _ := suite.gameServ.InitGame(context.TODO()) + + // Get all cards + var cards []*repository.Card + _ = suite.db.Find(&cards) + + // Fake game data + game := &repository.Game{ + RoomID: faker.UUIDDigit(), + Status: enums.GameStart, + Players: []repository.Player{}, + } // Fake player count random 1~3 playerCount := rand.Intn(3) + 1 // Fake players data - var players []request.PlayerInfo for i := 0; i < playerCount; i++ { - player := request.PlayerInfo{ - ID: faker.UUIDDigit(), - Name: faker.FirstName(), + player := repository.Player{ + UserID: faker.UUIDDigit(), + Name: faker.FirstName(), + PlayerCards: []repository.PlayerCard{}, } - players = append(players, player) - } - // Fake game data - createGameRequest := request.CreateGameRequest{ - Players: players, + // Each player gets 3 cards + for j := 0; j < 3; j++ { + player.PlayerCards = append(player.PlayerCards, repository.PlayerCard{ + CardID: cards[i*3+j].ID, + Type: "hand", + }) + } + game.Players = append(game.Players, player) } - _ = suite.playerServ.InitPlayers(context.TODO(), game, createGameRequest) - _ = suite.gameServ.InitDeck(context.TODO(), game) - _ = suite.gameServ.DrawCardsForAllPlayers(context.TODO(), game) + _ = suite.db.Create(&game) - playerId := rand.Intn(playerCount) + 1 - - // Get player's card - cards, _ := suite.playerRepo.GetPlayerWithPlayerCards(context.TODO(), playerId) + id := rand.Intn(playerCount) + player := game.Players[id] // Random card id - num := rand.Intn(len(cards.PlayerCards)) - cardId := cards.PlayerCards[num].CardId + num := rand.Intn(len(player.PlayerCards)) + cardId := player.PlayerCards[num].CardID // Set player to current player - suite.gameServ.UpdateCurrentPlayer(context.TODO(), game, playerId) + game.CurrentPlayerID = player.ID + _ = suite.db.Save(&game) - url := strings.ReplaceAll(api, "{player_id}", strconv.Itoa(playerId)) - req := PlayCardRequest{CardId: cardId} - reqBody, _ := json.Marshal(req) + url := strings.ReplaceAll(api, "{player_id}", strconv.Itoa(int(player.ID))) - res := suite.requestJson(url, reqBody, http.MethodPost) + req := utils.RequestToJsonBody(PlayCardRequest{CardID: cardId}) - // Convert response body from json to map - resBodyAsByteArray, _ := io.ReadAll(res.Body) - resBody := make(map[string]interface{}) - _ = json.Unmarshal(resBodyAsByteArray, &resBody) + res := suite.requestJson(url, req, http.MethodPost) // Then - assert.Equal(suite.T(), 200, res.StatusCode) + assert.Equal(suite.T(), http.StatusOK, res.StatusCode) + + var count int64 + playerCard := &repository.PlayerCard{} + _ = suite.db.Model(&playerCard).Where("player_id = ?", player.ID).Count(&count) - player, _ := suite.playerRepo.GetPlayerWithPlayerCards(context.TODO(), playerId) - assert.Equal(suite.T(), len(cards.PlayerCards)-1, len(player.PlayerCards)) + PlayerCards := int64(len(player.PlayerCards) - 1) + assert.Equal(suite.T(), PlayerCards, count) } func (suite *IntegrationTestSuite) TestTransmitIntelligenceE2E() { api := "/api/v1/player/{player_id}/transmit-intelligence" - game, _ := suite.gameServ.InitGame(context.TODO()) + + // Get all cards + var cards []*repository.Card + _ = suite.db.Find(&cards) + + // Fake game data + game := &repository.Game{ + RoomID: faker.UUIDDigit(), + Status: enums.GameStart, + Players: []repository.Player{}, + } // Fake player count random 1~3 playerCount := rand.Intn(3) + 1 // Fake players data - var players []request.PlayerInfo for i := 0; i < playerCount; i++ { - player := request.PlayerInfo{ - ID: faker.UUIDDigit(), - Name: faker.FirstName(), + player := repository.Player{ + UserID: faker.UUIDDigit(), + Name: faker.FirstName(), + PlayerCards: []repository.PlayerCard{}, } - players = append(players, player) - } - // Fake game data - createGameRequest := request.CreateGameRequest{ - Players: players, + // Each player gets 3 cards + for j := 0; j < 3; j++ { + player.PlayerCards = append(player.PlayerCards, repository.PlayerCard{ + CardID: cards[i*3+j].ID, + Type: "hand", + }) + } + game.Players = append(game.Players, player) } - _ = suite.playerServ.InitPlayers(context.TODO(), game, createGameRequest) - _ = suite.gameServ.InitDeck(context.TODO(), game) - _ = suite.gameServ.DrawCardsForAllPlayers(context.TODO(), game) + _ = suite.db.Create(&game) suite.T().Run("it can validate card id", func(t *testing.T) { playerId := rand.Intn(playerCount) + 1 // Request only intelligence type url := strings.ReplaceAll(api, "{player_id}", strconv.Itoa(playerId)) - req := PlayCardRequest{} - reqBody, _ := json.Marshal(req) + req := utils.RequestToJsonBody(PlayCardRequest{}) - res := suite.requestJson(url, reqBody, http.MethodPost) + res := suite.requestJson(url, req, http.MethodPost) // Convert response body from json to map - resBodyAsByteArray, _ := io.ReadAll(res.Body) - resBody := make(map[string]interface{}) - _ = json.Unmarshal(resBodyAsByteArray, &resBody) + resBody := utils.JsonBodyToMap(res) assert.Equal(t, http.StatusInternalServerError, res.StatusCode) assert.Equal(t, "Card not found", resBody["message"]) @@ -121,18 +136,15 @@ func (suite *IntegrationTestSuite) TestTransmitIntelligenceE2E() { suite.T().Run("it can fail when player not found", func(t *testing.T) { playerId := math.MaxInt32 - cardId := rand.Intn(playerCount) + cardId := uint(rand.Intn(playerCount)) url := strings.ReplaceAll(api, "{player_id}", strconv.Itoa(playerId)) - req := PlayCardRequest{CardId: cardId} - reqBody, _ := json.Marshal(req) + req := utils.RequestToJsonBody(PlayCardRequest{CardID: cardId}) - res := suite.requestJson(url, reqBody, http.MethodPost) + res := suite.requestJson(url, req, http.MethodPost) // Convert response body from json to map - resBodyAsByteArray, _ := io.ReadAll(res.Body) - resBody := make(map[string]interface{}) - _ = json.Unmarshal(resBodyAsByteArray, &resBody) + resBody := utils.JsonBodyToMap(res) assert.Equal(t, http.StatusInternalServerError, res.StatusCode) assert.Equal(t, "Player not found", resBody["message"]) @@ -140,114 +152,96 @@ func (suite *IntegrationTestSuite) TestTransmitIntelligenceE2E() { suite.T().Run("it can fail when player card not found", func(t *testing.T) { playerId := rand.Intn(playerCount) + 1 - cardId := math.MaxInt32 + cardId := uint(math.MaxInt32) url := strings.ReplaceAll(api, "{player_id}", strconv.Itoa(playerId)) - req := PlayCardRequest{CardId: cardId} - reqBody, _ := json.Marshal(req) + req := utils.RequestToJsonBody(PlayCardRequest{CardID: cardId}) - res := suite.requestJson(url, reqBody, http.MethodPost) + res := suite.requestJson(url, req, http.MethodPost) // Convert response body from json to map - resBodyAsByteArray, _ := io.ReadAll(res.Body) - resBody := make(map[string]interface{}) - _ = json.Unmarshal(resBodyAsByteArray, &resBody) + resBody := utils.JsonBodyToMap(res) assert.Equal(t, http.StatusInternalServerError, res.StatusCode) assert.Equal(t, "Card not found", resBody["message"]) }) suite.T().Run("it can fail when game is end", func(t *testing.T) { - playerId := rand.Intn(playerCount) + 1 - - // Get player's card - cards, _ := suite.playerRepo.GetPlayerWithPlayerCards(context.TODO(), playerId) + id := rand.Intn(playerCount) + player := game.Players[id] // Random card id - num := rand.Intn(len(cards.PlayerCards)) - cardId := cards.PlayerCards[num].CardId + num := rand.Intn(len(player.PlayerCards)) + cardId := player.PlayerCards[num].CardID // Set player to current player - suite.gameServ.UpdateCurrentPlayer(context.TODO(), game, playerId) + game.CurrentPlayerID = player.ID + game.Status = enums.GameEnd + _ = suite.db.Save(&game) - // Set game status to end - suite.gameServ.UpdateStatus(context.TODO(), game, enums.GameEnd) + url := strings.ReplaceAll(api, "{player_id}", strconv.Itoa(int(player.ID))) - url := strings.ReplaceAll(api, "{player_id}", strconv.Itoa(playerId)) - req := PlayCardRequest{CardId: cardId} - reqBody, _ := json.Marshal(req) + req := utils.RequestToJsonBody(PlayCardRequest{CardID: cardId}) - res := suite.requestJson(url, reqBody, http.MethodPost) + res := suite.requestJson(url, req, http.MethodPost) // Convert response body from json to map - resBodyAsByteArray, _ := io.ReadAll(res.Body) - resBody := make(map[string]interface{}) - _ = json.Unmarshal(resBodyAsByteArray, &resBody) + resBody := utils.JsonBodyToMap(res) assert.Equal(t, http.StatusInternalServerError, res.StatusCode) assert.Equal(t, "遊戲已結束", resBody["message"]) // Recover game status to start - suite.gameServ.UpdateStatus(context.TODO(), game, enums.GameStart) + game.Status = enums.GameStart + _ = suite.db.Save(&game) }) suite.T().Run("it can fail when not player's turn", func(t *testing.T) { - playerId := rand.Intn(playerCount) + 1 - - // Get player's card - cards, _ := suite.playerRepo.GetPlayerWithPlayerCards(context.TODO(), playerId) + id := rand.Intn(playerCount) + player := game.Players[id] // Random card id - num := rand.Intn(len(cards.PlayerCards)) - cardId := cards.PlayerCards[num].CardId + num := rand.Intn(len(player.PlayerCards)) + cardId := player.PlayerCards[num].CardID // Set other player to current player - suite.gameServ.UpdateCurrentPlayer(context.TODO(), game, playerId-1) + game.CurrentPlayerID = player.ID + 1 + _ = suite.db.Save(&game) - url := strings.ReplaceAll(api, "{player_id}", strconv.Itoa(playerId)) - req := PlayCardRequest{CardId: cardId} - reqBody, _ := json.Marshal(req) + url := strings.ReplaceAll(api, "{player_id}", strconv.Itoa(int(player.ID))) + req := utils.RequestToJsonBody(PlayCardRequest{CardID: cardId}) - res := suite.requestJson(url, reqBody, http.MethodPost) + res := suite.requestJson(url, req, http.MethodPost) // Convert response body from json to map - resBodyAsByteArray, _ := io.ReadAll(res.Body) - resBody := make(map[string]interface{}) - _ = json.Unmarshal(resBodyAsByteArray, &resBody) + resBody := utils.JsonBodyToMap(res) assert.Equal(t, http.StatusInternalServerError, res.StatusCode) assert.Equal(t, "尚未輪到你出牌", resBody["message"]) }) suite.T().Run("it can success when valid card id", func(t *testing.T) { - playerId := rand.Intn(playerCount) + 1 - - // Get player's card - cards, _ := suite.playerRepo.GetPlayerWithPlayerCards(context.TODO(), playerId) + id := rand.Intn(playerCount) + player := game.Players[id] // Random card id - num := rand.Intn(len(cards.PlayerCards)) - cardId := cards.PlayerCards[num].CardId + num := rand.Intn(len(player.PlayerCards)) + cardId := player.PlayerCards[num].CardID // Set player to current player - suite.gameServ.UpdateCurrentPlayer(context.TODO(), game, playerId) + game.CurrentPlayerID = player.ID + _ = suite.db.Save(&game) - url := strings.ReplaceAll(api, "{player_id}", strconv.Itoa(playerId)) - req := PlayCardRequest{CardId: cardId} - reqBody, _ := json.Marshal(req) + url := strings.ReplaceAll(api, "{player_id}", strconv.Itoa(int(player.ID))) - res := suite.requestJson(url, reqBody, http.MethodPost) + req := utils.RequestToJsonBody(PlayCardRequest{CardID: cardId}) - // Convert response body from json to map - resBodyAsByteArray, _ := io.ReadAll(res.Body) - resBody := make(map[string]interface{}) - _ = json.Unmarshal(resBodyAsByteArray, &resBody) + res := suite.requestJson(url, req, http.MethodPost) assert.Equal(t, http.StatusOK, res.StatusCode) - assert.Equal(t, true, resBody["result"]) }) } type PlayCardRequest struct { - CardId int `json:"card_id"` + CardID uint `json:"card_id"` } From ed1009b1e8a0ea5ca253d19ce960ab7b0a8f5cbb Mon Sep 17 00:00:00 2001 From: KazeNoYumeX Date: Thu, 21 Mar 2024 15:25:27 +0800 Subject: [PATCH 10/10] Chore: Regularly upgrade dependency packages --- Backend/go.mod | 19 ++++++++++--------- Backend/go.sum | 41 +++++++++++++++++++++++------------------ 2 files changed, 33 insertions(+), 27 deletions(-) diff --git a/Backend/go.mod b/Backend/go.mod index 66e0ea2..ba659a8 100644 --- a/Backend/go.mod +++ b/Backend/go.mod @@ -5,29 +5,30 @@ go 1.22 require ( github.com/gin-gonic/gin v1.9.1 github.com/go-faker/faker/v4 v4.3.0 - github.com/go-sql-driver/mysql v1.7.1 + github.com/go-sql-driver/mysql v1.8.0 github.com/golang-migrate/migrate/v4 v4.17.0 github.com/joho/godotenv v1.5.1 github.com/stretchr/testify v1.9.0 github.com/swaggo/files v1.0.1 github.com/swaggo/gin-swagger v1.6.0 github.com/swaggo/swag v1.16.3 - gorm.io/driver/mysql v1.5.4 - gorm.io/gorm v1.25.7 + gorm.io/driver/mysql v1.5.6 + gorm.io/gorm v1.25.8 ) require ( + filippo.io/edwards25519 v1.1.0 // indirect github.com/KyleBanks/depth v1.2.1 // indirect - github.com/bytedance/sonic v1.11.2 // indirect + github.com/bytedance/sonic v1.11.3 // indirect github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect github.com/chenzhuoyu/iasm v0.9.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gin-contrib/sse v0.1.0 // indirect - github.com/go-openapi/jsonpointer v0.20.3 // indirect - github.com/go-openapi/jsonreference v0.20.5 // indirect - github.com/go-openapi/spec v0.20.15 // indirect - github.com/go-openapi/swag v0.22.10 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect + github.com/go-openapi/spec v0.21.0 // indirect + github.com/go-openapi/swag v0.23.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.19.0 // indirect @@ -45,7 +46,7 @@ require ( github.com/mattn/go-isatty v0.0.20 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pelletier/go-toml/v2 v2.1.1 // indirect + github.com/pelletier/go-toml/v2 v2.2.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect diff --git a/Backend/go.sum b/Backend/go.sum index 8c3c65c..d5feecc 100644 --- a/Backend/go.sum +++ b/Backend/go.sum @@ -1,3 +1,5 @@ +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= @@ -6,8 +8,8 @@ github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migc github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= -github.com/bytedance/sonic v1.11.2 h1:ywfwo0a/3j9HR8wsYGWsIWl2mvRsI950HyoxiBERw5A= -github.com/bytedance/sonic v1.11.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= +github.com/bytedance/sonic v1.11.3 h1:jRN+yEjakWh8aK5FzrciUHG8OFXK+4/KrAX/ysEtHAA= +github.com/bytedance/sonic v1.11.3/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= @@ -38,14 +40,14 @@ github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/go-faker/faker/v4 v4.3.0 h1:UXOW7kn/Mwd0u6MR30JjUKVzguT20EB/hBOddAAO+DY= github.com/go-faker/faker/v4 v4.3.0/go.mod h1:F/bBy8GH9NxOxMInug5Gx4WYeG6fHJZ8Ol/dhcpRub4= -github.com/go-openapi/jsonpointer v0.20.3 h1:jykzYWS/kyGtsHfRt6aV8JTB9pcQAXPIA7qlZ5aRlyk= -github.com/go-openapi/jsonpointer v0.20.3/go.mod h1:c7l0rjoouAuIxCm8v/JWKRgMjDG/+/7UBWsXMrv6PsM= -github.com/go-openapi/jsonreference v0.20.5 h1:hutI+cQI+HbSQaIGSfsBsYI0pHk+CATf8Fk5gCSj0yI= -github.com/go-openapi/jsonreference v0.20.5/go.mod h1:thAqAp31UABtI+FQGKAQfmv7DbFpKNUlva2UPCxKu2Y= -github.com/go-openapi/spec v0.20.15 h1:8bDcVxF607pTh9NpPwgsH4J5Uhh5mV5XoWnkurdiY+U= -github.com/go-openapi/spec v0.20.15/go.mod h1:o0upgqg5uYFG7O5mADrDVmSG3Wa6y6OLhwiCqQ+sTv4= -github.com/go-openapi/swag v0.22.10 h1:4y86NVn7Z2yYd6pfS4Z+Nyh3aAUL3Nul+LMbhFKy0gA= -github.com/go-openapi/swag v0.22.10/go.mod h1:Cnn8BYtRlx6BNE3DPN86f/xkapGIcLWzh3CLEb4C1jI= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY= +github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= @@ -55,8 +57,8 @@ github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91 github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4= github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= -github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= -github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-sql-driver/mysql v1.8.0 h1:UtktXaU2Nb64z/pLiGIxY4431SJ4/dR5cjMmlVHgnT4= +github.com/go-sql-driver/mysql v1.8.0/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -110,8 +112,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= -github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pelletier/go-toml/v2 v2.2.0 h1:QLgLl2yMN7N+ruc31VynXs1vhMZa7CeHHejIeBAsoHo= +github.com/pelletier/go-toml/v2 v2.2.0/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -121,6 +123,7 @@ github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUz github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -192,10 +195,12 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gorm.io/driver/mysql v1.5.4 h1:igQmHfKcbaTVyAIHNhhB888vvxh8EdQ2uSUT0LPcBso= -gorm.io/driver/mysql v1.5.4/go.mod h1:9rYxJph/u9SWkWc9yY4XJ1F/+xO0S/ChOmbk3+Z5Tvs= -gorm.io/gorm v1.25.7-0.20240204074919-46816ad31dde/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= -gorm.io/gorm v1.25.7 h1:VsD6acwRjz2zFxGO50gPO6AkNs7KKnvfzUjHQhZDz/A= +gorm.io/driver/mysql v1.5.5 h1:WxklwX6FozMs1gk9yVadxGfjGiJjrBKPvIIvYZOMyws= +gorm.io/driver/mysql v1.5.5/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= +gorm.io/driver/mysql v1.5.6 h1:Ld4mkIickM+EliaQZQx3uOJDJHtrd70MxAUqWqlx3Y8= +gorm.io/driver/mysql v1.5.6/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/gorm v1.25.8 h1:WAGEZ/aEcznN4D03laj8DKnehe1e9gYQAjW8xyPRdeo= +gorm.io/gorm v1.25.8/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=