generated from VLADIMIR/template
add game
This commit is contained in:
+51
-5
@@ -605,15 +605,51 @@ func (s *server) GiveTeamApplications(ctx context.Context, req *proto.GiveTeamAp
|
||||
return nil, status.Errorf(codes.PermissionDenied, "permission denied")
|
||||
}
|
||||
|
||||
panic("unimplemented")
|
||||
err := s.gameService.GiveTeamApplications(
|
||||
ctx,
|
||||
int(req.Id),
|
||||
req.Name,
|
||||
)
|
||||
if err != nil {
|
||||
return &proto.GiveTeamApplicationsRsp{
|
||||
Error: err.Error(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
return &proto.GiveTeamApplicationsRsp{}, nil
|
||||
}
|
||||
|
||||
func (s *server) GetTeamActions(ctx context.Context, req *proto.GetTeamActionsReq) (*proto.GetTeamActionsRsp, error) {
|
||||
panic("unimplemented")
|
||||
func (s *server) GetTeamStory(ctx context.Context, req *proto.GetTeamStoryReq) (*proto.GetTeamStoryRsp, error) {
|
||||
story, err := s.gameService.GetTeamActions(
|
||||
ctx,
|
||||
int(req.Id),
|
||||
req.Password,
|
||||
)
|
||||
if err != nil {
|
||||
return &proto.GetTeamStoryRsp{
|
||||
Error: err.Error(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
return &proto.GetTeamStoryRsp{
|
||||
Story: mapStory(story),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *server) AddTeamAction(ctx context.Context, req *proto.AddTeamActionReq) (*proto.AddTeamActionRsp, error) {
|
||||
panic("unimplemented")
|
||||
err := s.gameService.AddTeamAction(
|
||||
ctx,
|
||||
int(req.Id),
|
||||
req.Password,
|
||||
req.Code,
|
||||
)
|
||||
if err != nil {
|
||||
return &proto.AddTeamActionRsp{
|
||||
Error: err.Error(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
return &proto.AddTeamActionRsp{}, nil
|
||||
}
|
||||
|
||||
func (s *server) DeleteLastTeamAction(ctx context.Context, req *proto.DeleteLastTeamActionReq) (*proto.DeleteLastTeamActionRsp, error) {
|
||||
@@ -622,5 +658,15 @@ func (s *server) DeleteLastTeamAction(ctx context.Context, req *proto.DeleteLast
|
||||
return nil, status.Errorf(codes.PermissionDenied, "permission denied")
|
||||
}
|
||||
|
||||
panic("unimplemented")
|
||||
err := s.gameService.DeleteLastTeamAction(
|
||||
ctx,
|
||||
int(req.Id),
|
||||
)
|
||||
if err != nil {
|
||||
return &proto.DeleteLastTeamActionRsp{
|
||||
Error: err.Error(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
return &proto.DeleteLastTeamActionRsp{}, nil
|
||||
}
|
||||
|
||||
@@ -9,10 +9,12 @@ func mapTeam(
|
||||
o *game_service.Team,
|
||||
) *proto.Team {
|
||||
return &proto.Team{
|
||||
Id: int32(o.ID),
|
||||
Name: o.Name,
|
||||
Status: o.Status,
|
||||
Password: o.Password,
|
||||
Id: int32(o.ID),
|
||||
Name: o.Name,
|
||||
Status: o.Status,
|
||||
Password: o.Password,
|
||||
ActionsCount: int32(o.ActionsCount),
|
||||
Applications: mapApplications(o.Applications),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
package actions_repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
type ActionsRepo struct {
|
||||
pool *pgxpool.Pool
|
||||
}
|
||||
|
||||
func NewActionsRepo(pool *pgxpool.Pool) *ActionsRepo {
|
||||
return &ActionsRepo{
|
||||
pool: pool,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ActionsRepo) AddAction(ctx context.Context, teamId int, code string) (int, error) {
|
||||
id := 0
|
||||
err := r.pool.QueryRow(
|
||||
ctx,
|
||||
`INSERT INTO actions (code, team_id)
|
||||
VALUES ($1, $2)
|
||||
RETURNING id`,
|
||||
code,
|
||||
teamId,
|
||||
).Scan(&id)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func (r *ActionsRepo) GetActionsByTeamID(ctx context.Context, teamId int) ([]string, error) {
|
||||
rows, err := r.pool.Query(
|
||||
ctx,
|
||||
`SELECT
|
||||
code
|
||||
FROM actions
|
||||
WHERE team_id = $1
|
||||
ORDER BY created_at ASC`,
|
||||
teamId,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var codes []string
|
||||
for rows.Next() {
|
||||
code := ""
|
||||
err := rows.Scan(
|
||||
&code,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
codes = append(codes, code)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return codes, nil
|
||||
}
|
||||
|
||||
func (r *ActionsRepo) GetActionsCountByTeamIDs(ctx context.Context, teamIds []int) (map[int]int, error) {
|
||||
rows, err := r.pool.Query(
|
||||
ctx,
|
||||
`SELECT
|
||||
team_id,
|
||||
count(*)
|
||||
FROM actions
|
||||
WHERE team_id = ANY($1)
|
||||
GROUP BY team_id`,
|
||||
teamIds,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
res := make(map[int]int, len(teamIds))
|
||||
for rows.Next() {
|
||||
id := 0
|
||||
count := 0
|
||||
err := rows.Scan(
|
||||
&id,
|
||||
&count,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res[id] = count
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package repos
|
||||
|
||||
type Application struct {
|
||||
Name string
|
||||
Image string
|
||||
TeamId int
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package applications_repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"evening_detective_server/internal/repos"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
type ApplicationsRepo struct {
|
||||
pool *pgxpool.Pool
|
||||
}
|
||||
|
||||
func NewApplicationsRepo(pool *pgxpool.Pool) *ApplicationsRepo {
|
||||
return &ApplicationsRepo{
|
||||
pool: pool,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ApplicationsRepo) AddApplication(
|
||||
ctx context.Context,
|
||||
teamId int,
|
||||
name string,
|
||||
image string,
|
||||
) (int, error) {
|
||||
id := 0
|
||||
err := r.pool.QueryRow(
|
||||
ctx,
|
||||
`INSERT INTO applications (name, image, team_id)
|
||||
VALUES ($1, $2, $3)
|
||||
RETURNING id`,
|
||||
name,
|
||||
image,
|
||||
teamId,
|
||||
).Scan(&id)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func (r *ApplicationsRepo) GetApplicationsByTeamIDsAndState(
|
||||
ctx context.Context,
|
||||
teamIds []int,
|
||||
state string,
|
||||
) (map[int][]*repos.Application, error) {
|
||||
rows, err := r.pool.Query(
|
||||
ctx,
|
||||
`SELECT
|
||||
name,
|
||||
image,
|
||||
team_id
|
||||
FROM applications
|
||||
WHERE team_id = ANY($1)`,
|
||||
teamIds,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
res := make(map[int][]*repos.Application, len(teamIds))
|
||||
for rows.Next() {
|
||||
application := &repos.Application{}
|
||||
err := rows.Scan(
|
||||
&application.Name,
|
||||
&application.Image,
|
||||
&application.TeamId,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res[application.TeamId] = append(res[application.TeamId], application)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
package repos
|
||||
|
||||
type Team struct {
|
||||
ID int
|
||||
Name string
|
||||
Status string
|
||||
Password string
|
||||
ID int
|
||||
Name string
|
||||
Status string
|
||||
Password string
|
||||
ScenarioId int
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"evening_detective_server/internal/repos"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
@@ -22,6 +23,37 @@ func NewTeamsRepo(pool *pgxpool.Pool) *TeamsRepo {
|
||||
}
|
||||
}
|
||||
|
||||
func (r *TeamsRepo) GetTeamByID(ctx context.Context, id int) (*repos.Team, error) {
|
||||
team := &repos.Team{}
|
||||
row := r.pool.QueryRow(
|
||||
ctx,
|
||||
`SELECT
|
||||
id,
|
||||
name,
|
||||
status,
|
||||
password,
|
||||
scenario_id
|
||||
FROM teams
|
||||
WHERE id = $1`,
|
||||
id,
|
||||
)
|
||||
err := row.Scan(
|
||||
&team.ID,
|
||||
&team.Name,
|
||||
&team.Status,
|
||||
&team.Password,
|
||||
&team.ScenarioId,
|
||||
)
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrTeamNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return team, nil
|
||||
}
|
||||
|
||||
func (r *TeamsRepo) GetTeamsByGameID(
|
||||
ctx context.Context,
|
||||
gameId int,
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package game_service
|
||||
|
||||
type Application struct {
|
||||
Name string
|
||||
Image string
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package game_service
|
||||
|
||||
import (
|
||||
"evening_detective_server/internal/modules/storytelling"
|
||||
"evening_detective_server/internal/repos"
|
||||
)
|
||||
|
||||
func mapApplications(o []*repos.Application) []*storytelling.Application {
|
||||
res := make([]*storytelling.Application, 0, len(o))
|
||||
for _, item := range o {
|
||||
res = append(res, mapApplication(item))
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func mapApplication(o *repos.Application) *storytelling.Application {
|
||||
return &storytelling.Application{
|
||||
Name: o.Name,
|
||||
Image: o.Image,
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,15 @@ package game_service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"evening_detective_server/internal/modules/cleaner"
|
||||
"evening_detective_server/internal/modules/password_generator"
|
||||
"evening_detective_server/internal/modules/storytelling"
|
||||
"evening_detective_server/internal/repos/actions_repo"
|
||||
"evening_detective_server/internal/repos/applications_repo"
|
||||
"evening_detective_server/internal/repos/games_repo"
|
||||
"evening_detective_server/internal/repos/scenarios_repo"
|
||||
"evening_detective_server/internal/repos/teams_repo"
|
||||
"evening_detective_server/internal/services/scenarios_service"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -13,23 +18,38 @@ type GameService struct {
|
||||
gamesRepo *games_repo.GamesRepo
|
||||
teamsRepo *teams_repo.TeamsRepo
|
||||
scenariosRepo *scenarios_repo.ScenariosRepo
|
||||
scenarioService *scenarios_service.ScenarioService
|
||||
domain string
|
||||
passwordGenerator password_generator.IPasswordGenerator
|
||||
storyteller storytelling.IStory
|
||||
cleaner cleaner.ICleaner
|
||||
actionsRepo *actions_repo.ActionsRepo
|
||||
applicationsRepo *applications_repo.ApplicationsRepo
|
||||
}
|
||||
|
||||
func NewGameService(
|
||||
gamesRepo *games_repo.GamesRepo,
|
||||
teamsRepo *teams_repo.TeamsRepo,
|
||||
scenariosRepo *scenarios_repo.ScenariosRepo,
|
||||
scenarioService *scenarios_service.ScenarioService,
|
||||
domain string,
|
||||
passwordGenerator password_generator.IPasswordGenerator,
|
||||
storyteller storytelling.IStory,
|
||||
cleaner cleaner.ICleaner,
|
||||
actionsRepo *actions_repo.ActionsRepo,
|
||||
applicationsRepo *applications_repo.ApplicationsRepo,
|
||||
) *GameService {
|
||||
return &GameService{
|
||||
gamesRepo: gamesRepo,
|
||||
teamsRepo: teamsRepo,
|
||||
scenariosRepo: scenariosRepo,
|
||||
scenarioService: scenarioService,
|
||||
domain: domain,
|
||||
passwordGenerator: passwordGenerator,
|
||||
storyteller: storyteller,
|
||||
cleaner: cleaner,
|
||||
actionsRepo: actionsRepo,
|
||||
applicationsRepo: applicationsRepo,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +85,30 @@ func (s *GameService) GetGame(
|
||||
return nil, err
|
||||
}
|
||||
game.Teams = teams
|
||||
return mapGame(game, s.domain), nil
|
||||
|
||||
res := mapGame(game, s.domain)
|
||||
|
||||
teamIds := make([]int, 0, len(game.Teams))
|
||||
for _, team := range game.Teams {
|
||||
teamIds = append(teamIds, team.ID)
|
||||
}
|
||||
|
||||
actionsCountByTeamIDs, err := s.actionsRepo.GetActionsCountByTeamIDs(ctx, teamIds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
needApplicationsByTeamIDs, err := s.applicationsRepo.GetApplicationsByTeamIDsAndState(ctx, teamIds, "need")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, team := range res.Teams {
|
||||
team.ActionsCount = actionsCountByTeamIDs[team.ID]
|
||||
team.Applications = mapApplications(needApplicationsByTeamIDs[team.ID])
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (s *GameService) DeleteGame(
|
||||
@@ -121,3 +164,74 @@ func (s *GameService) UpdateTeam(ctx context.Context, teamId int, name string) e
|
||||
func (s *GameService) DeleteTeam(ctx context.Context, teamId int) error {
|
||||
return s.teamsRepo.DeleteTeam(ctx, teamId)
|
||||
}
|
||||
|
||||
func (s *GameService) GiveTeamApplications(ctx context.Context, teamId int, name string) error {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func (s *GameService) GetTeamActions(ctx context.Context, teamId int, password string) (*storytelling.Story, error) {
|
||||
team, err := s.teamsRepo.GetTeamByID(ctx, teamId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if team.Password != password {
|
||||
return nil, teams_repo.ErrTeamNotFound
|
||||
}
|
||||
|
||||
actions, err := s.actionsRepo.GetActionsByTeamID(ctx, teamId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
scenario, err := s.scenarioService.GetFullScenarioByID(ctx, team.ScenarioId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.storyteller.GetStory(scenario.Story, actions), nil
|
||||
}
|
||||
|
||||
func (s *GameService) AddTeamAction(ctx context.Context, teamId int, password string, actionCode string) error {
|
||||
team, err := s.teamsRepo.GetTeamByID(ctx, teamId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if team.Password != password {
|
||||
return teams_repo.ErrTeamNotFound
|
||||
}
|
||||
|
||||
code := s.cleaner.ClearCode(actionCode)
|
||||
_, err = s.actionsRepo.AddAction(ctx, teamId, code)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
actions, err := s.actionsRepo.GetActionsByTeamID(ctx, teamId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
scenario, err := s.scenarioService.GetFullScenarioByID(ctx, team.ScenarioId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
teamStory := s.storyteller.GetStory(scenario.Story, actions)
|
||||
|
||||
lastPlace := teamStory.Places[len(teamStory.Places)-1]
|
||||
|
||||
for _, application := range lastPlace.Applications {
|
||||
_, err := s.applicationsRepo.AddApplication(ctx, teamId, application.Name, application.Image)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *GameService) DeleteLastTeamAction(ctx context.Context, teamId int) error {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
package game_service
|
||||
|
||||
import "evening_detective_server/internal/modules/storytelling"
|
||||
|
||||
type Team struct {
|
||||
ID int
|
||||
Name string
|
||||
Status string
|
||||
Password string
|
||||
ID int
|
||||
Name string
|
||||
Status string
|
||||
Password string
|
||||
ActionsCount int
|
||||
Applications []*storytelling.Application
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package scenarios_service
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"evening_detective_server/internal/modules/cleaner"
|
||||
"evening_detective_server/internal/modules/storytelling"
|
||||
"evening_detective_server/internal/repos/scenarios_repo"
|
||||
"strings"
|
||||
@@ -10,15 +11,18 @@ import (
|
||||
|
||||
type ScenarioService struct {
|
||||
scenariosRepo *scenarios_repo.ScenariosRepo
|
||||
cleaner cleaner.ICleaner
|
||||
domain string
|
||||
}
|
||||
|
||||
func NewScenarioService(
|
||||
scenariosRepo *scenarios_repo.ScenariosRepo,
|
||||
cleaner cleaner.ICleaner,
|
||||
domain string,
|
||||
) *ScenarioService {
|
||||
return &ScenarioService{
|
||||
scenariosRepo: scenariosRepo,
|
||||
cleaner: cleaner,
|
||||
domain: domain,
|
||||
}
|
||||
}
|
||||
@@ -167,7 +171,8 @@ func (s *ScenarioService) getStory(ctx context.Context, id int) (*storytelling.S
|
||||
func (s *ScenarioService) updateStory(ctx context.Context, id int, story *storytelling.Story) error {
|
||||
mapCodes := map[string]struct{}{}
|
||||
for _, place := range story.Places {
|
||||
mapCodes[place.Code] = struct{}{}
|
||||
code := s.cleaner.ClearCode(place.Code)
|
||||
mapCodes[code] = struct{}{}
|
||||
place.Image = strings.TrimPrefix(place.Image, s.domain)
|
||||
}
|
||||
if len(mapCodes) != len(story.Places) {
|
||||
@@ -176,9 +181,11 @@ func (s *ScenarioService) updateStory(ctx context.Context, id int, story *storyt
|
||||
|
||||
cleanPlaces := make([]*storytelling.Place, 0, len(story.Places))
|
||||
for _, place := range story.Places {
|
||||
if place.Code == "" {
|
||||
code := s.cleaner.ClearCode(place.Code)
|
||||
if code == "" {
|
||||
continue
|
||||
}
|
||||
place.Code = code
|
||||
cleanPlaces = append(cleanPlaces, place)
|
||||
}
|
||||
story.Places = cleanPlaces
|
||||
|
||||
Reference in New Issue
Block a user