add team service

This commit is contained in:
2026-09-17 02:29:13 +07:00
parent 49fe193ed7
commit c70d839038
27 changed files with 227 additions and 190 deletions
+3 -3
View File
@@ -1,14 +1,14 @@
package app
import (
"evening_detective_server/internal/services/game_service"
"evening_detective_server/internal/modules/game_model"
"evening_detective_server/proto"
"google.golang.org/protobuf/types/known/timestamppb"
)
func mapGame(
o *game_service.Game,
o *game_model.Game,
) *proto.Game {
var startedAt *timestamppb.Timestamp
if o.StartedAt != nil {
@@ -38,7 +38,7 @@ func mapGame(
}
func mapGames(
o []*game_service.Game,
o []*game_model.Game,
) []*proto.Game {
res := make([]*proto.Game, 0, len(o))
for _, item := range o {
+2 -1
View File
@@ -1,6 +1,7 @@
package app
import (
"evening_detective_server/internal/modules/game_model"
"testing"
"evening_detective_server/internal/services/game_service"
@@ -10,7 +11,7 @@ func TestMapTeamResults(t *testing.T) {
sc := 5
results := []game_service.TeamResult{
{
Team: &game_service.Team{
Team: &game_model.Team{
ID: 1,
Name: "Команда А",
Status: "play",
+4
View File
@@ -8,6 +8,7 @@ import (
"evening_detective_server/internal/services/game_service"
"evening_detective_server/internal/services/participation_service"
"evening_detective_server/internal/services/scenarios_service"
"evening_detective_server/internal/services/team_service"
"evening_detective_server/internal/services/ui_service"
"evening_detective_server/internal/services/users_service"
proto "evening_detective_server/proto"
@@ -24,6 +25,7 @@ type server struct {
fileService *file_service.FileService
scenarioService *scenarios_service.ScenarioService
gameService *game_service.GameService
teamService *team_service.TeamService
participationService *participation_service.ParticipationService
screenRepo *screen_sessions_repo.ScreenSessionsRepo
mapRepo *game_map_repo.Repo
@@ -35,6 +37,7 @@ func NewServer(
fileService *file_service.FileService,
scenarioService *scenarios_service.ScenarioService,
gameService *game_service.GameService,
teamService *team_service.TeamService,
participationService *participation_service.ParticipationService,
screenRepo *screen_sessions_repo.ScreenSessionsRepo,
mapRepo *game_map_repo.Repo,
@@ -45,6 +48,7 @@ func NewServer(
fileService: fileService,
scenarioService: scenarioService,
gameService: gameService,
teamService: teamService,
participationService: participationService,
screenRepo: screenRepo,
mapRepo: mapRepo,
+4 -4
View File
@@ -1,12 +1,12 @@
package app
import (
"evening_detective_server/internal/services/game_service"
"evening_detective_server/internal/modules/game_model"
"evening_detective_server/proto"
)
func mapTeam(
o *game_service.Team,
o *game_model.Team,
) *proto.Team {
return &proto.Team{
Id: int32(o.ID),
@@ -18,7 +18,7 @@ func mapTeam(
}
}
func mapTeamDetails(o *game_service.Team) *proto.Team {
func mapTeamDetails(o *game_model.Team) *proto.Team {
team := mapTeam(o)
team.ParticipationStatus = o.ParticipationStatus
team.ParticipationSource = o.ParticipationSource
@@ -30,7 +30,7 @@ func mapTeamDetails(o *game_service.Team) *proto.Team {
}
func mapTeams(
o []*game_service.Team,
o []*game_model.Team,
) []*proto.Team {
res := make([]*proto.Team, 0, len(o))
for _, item := range o {
+1 -1
View File
@@ -29,7 +29,7 @@ func (s *server) DownloadTeamQR(ctx context.Context, req *proto.GetGameReq) (*ht
if req == nil || req.Id <= 0 {
return nil, status.Error(codes.InvalidArgument, "некорректный идентификатор игры")
}
data, filename, err := s.gameService.DownloadTeamQR(ctx, int(req.Id))
data, filename, err := s.teamService.DownloadTeamQR(ctx, int(req.Id))
if err != nil {
switch {
case errors.Is(err, games_repo.ErrGameNotFound):
+11 -11
View File
@@ -6,6 +6,7 @@ import (
"crypto/subtle"
"encoding/json"
"errors"
"evening_detective_server/internal/services/team_service"
"fmt"
"io"
"net/http"
@@ -13,11 +14,10 @@ import (
"time"
"evening_detective_server/internal/repos/teams_repo"
"evening_detective_server/internal/services/game_service"
)
// TeamRegistrationHandler принимает webhook Яндекс Форм отдельно от пользовательского JWT.
func TeamRegistrationHandler(token string, register func(context.Context, int, string, game_service.Registration) (int, error)) http.Handler {
func TeamRegistrationHandler(token string, register func(context.Context, int, string, team_service.Registration) (int, error)) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Cache-Control", "no-store")
@@ -63,7 +63,7 @@ func TeamRegistrationHandler(token string, register func(context.Context, int, s
defer cancel()
id, err := register(ctx, int(gameID), deliveryID, registration)
switch {
case errors.Is(err, game_service.ErrInvalidRegistration):
case errors.Is(err, team_service.ErrInvalidRegistration):
fail(http.StatusBadRequest, err.Error())
case errors.Is(err, teams_repo.ErrRegistrationClosed):
fail(http.StatusConflict, err.Error())
@@ -79,29 +79,29 @@ func TeamRegistrationHandler(token string, register func(context.Context, int, s
})
}
func decodeRegistrationPayload(payload json.RawMessage) (game_service.Registration, json.RawMessage, bool, error) {
func decodeRegistrationPayload(payload json.RawMessage) (team_service.Registration, json.RawMessage, bool, error) {
var fields map[string]json.RawMessage
if err := json.Unmarshal(payload, &fields); err != nil {
return game_service.Registration{}, nil, false, err
return team_service.Registration{}, nil, false, err
}
if rawRPC, ok := fields["jsonrpc"]; ok {
var version string
if err := json.Unmarshal(rawRPC, &version); err != nil || version != "2.0" {
return game_service.Registration{}, nil, true, errors.New(`поле "jsonrpc" должно быть равно "2.0"`)
return team_service.Registration{}, nil, true, errors.New(`поле "jsonrpc" должно быть равно "2.0"`)
}
params, ok := fields["params"]
if !ok {
return game_service.Registration{}, nil, true, errors.New(`в JSON-RPC запросе отсутствует поле "params"`)
return team_service.Registration{}, nil, true, errors.New(`в JSON-RPC запросе отсутствует поле "params"`)
}
var registration game_service.Registration
var registration team_service.Registration
if err := decodeStrict(params, &registration); err != nil {
return game_service.Registration{}, nil, true, fmt.Errorf("поле params: %w", err)
return team_service.Registration{}, nil, true, fmt.Errorf("поле params: %w", err)
}
return registration, fields["id"], true, nil
}
var registration game_service.Registration
var registration team_service.Registration
if err := decodeStrict(payload, &registration); err != nil {
return game_service.Registration{}, nil, false, err
return team_service.Registration{}, nil, false, err
}
return registration, nil, false, nil
}
+6 -5
View File
@@ -3,13 +3,14 @@ package app
import (
"context"
"errors"
"evening_detective_server/internal/modules/game_model"
"evening_detective_server/internal/services/team_service"
"net/http"
"net/http/httptest"
"strings"
"testing"
"evening_detective_server/internal/repos/teams_repo"
"evening_detective_server/internal/services/game_service"
)
func TestTeamRegistrationHandler(t *testing.T) {
@@ -32,14 +33,14 @@ func TestTeamRegistrationHandler(t *testing.T) {
{"jsonrpc", "secret", "Bearer secret", "42", "delivery", `{"jsonrpc":"2.0","method":"register","params":{"name":"Команда","email":"captain@example.org","participantsCount":5},"id":1}`, nil, 200, true},
{"jsonrpc string count", "secret", "Bearer secret", "42", "delivery", `{"jsonrpc":"2.0","method":"register","params":{"name":"Команда","email":"captain@example.org","participantsCount":"5"},"id":1}`, nil, 200, true},
{"too large", "secret", "Bearer secret", "42", "delivery", `{"name":"` + strings.Repeat("a", 17000) + `"}`, nil, 400, false},
{"invalid fields", "secret", "Bearer secret", "42", "delivery", valid, game_service.ErrInvalidRegistration, 400, true},
{"invalid fields", "secret", "Bearer secret", "42", "delivery", valid, team_service.ErrInvalidRegistration, 400, true},
{"closed", "secret", "Bearer secret", "42", "delivery", valid, teams_repo.ErrRegistrationClosed, 409, true},
{"internal", "secret", "Bearer secret", "42", "delivery", valid, errors.New("private database detail"), 500, true},
} {
t.Run(tc.name, func(t *testing.T) {
called := false
mux := http.NewServeMux()
mux.Handle("POST /api/games/{id}/registrations", TeamRegistrationHandler(tc.token, func(ctx context.Context, gameID int, deliveryID string, registration game_service.Registration) (int, error) {
mux.Handle("POST /api/games/{id}/registrations", TeamRegistrationHandler(tc.token, func(ctx context.Context, gameID int, deliveryID string, registration team_service.Registration) (int, error) {
called = true
if gameID != 42 || deliveryID != "delivery" || registration.Name != "Команда" || registration.Email != "captain@example.org" || registration.ParticipantsCount != 5 {
t.Fatal("incorrect registration passed to service")
@@ -80,8 +81,8 @@ func TestTeamRegistrationHandler(t *testing.T) {
}
func TestCaptainContactOnlyInGameDetails(t *testing.T) {
team := &game_service.Team{ID: 1, CaptainEmail: "captain@example.org", ParticipantsCount: 5}
details := mapGame(&game_service.Game{Teams: []*game_service.Team{team}})
team := &game_model.Team{ID: 1, CaptainEmail: "captain@example.org", ParticipantsCount: 5}
details := mapGame(&game_model.Game{Teams: []*game_model.Team{team}})
if details.Teams[0].CaptainEmail != team.CaptainEmail || details.Teams[0].ParticipantsCount != 5 {
t.Fatal("registration details missing from organizer view")
}
+3 -3
View File
@@ -17,7 +17,7 @@ func (s *server) AddTeam(ctx context.Context, req *proto.AddTeamReq) (*proto.Add
return nil, status.Errorf(codes.PermissionDenied, "permission denied")
}
team, err := s.gameService.AddTeam(
team, err := s.teamService.AddTeam(
ctx,
claims.UserID,
int(req.GameId),
@@ -42,7 +42,7 @@ func (s *server) UpdateTeam(ctx context.Context, req *proto.UpdateTeamReq) (*pro
return nil, status.Errorf(codes.PermissionDenied, "permission denied")
}
err := s.gameService.UpdateTeam(
err := s.teamService.UpdateTeam(
ctx,
int(req.Id),
req.Name,
@@ -62,7 +62,7 @@ func (s *server) DeleteTeam(ctx context.Context, req *proto.DeleteTeamReq) (*pro
return nil, status.Errorf(codes.PermissionDenied, "permission denied")
}
err := s.gameService.DeleteTeam(
err := s.teamService.DeleteTeam(
ctx,
int(req.Id),
)