add online games

This commit is contained in:
2026-09-13 01:02:40 +07:00
parent 3e555c9944
commit 0723644d3f
14 changed files with 170 additions and 12 deletions
+1
View File
@@ -6,6 +6,7 @@ import (
)
type Game struct {
Format string
ID int
Name string
Description string
@@ -15,6 +15,7 @@ func mapGame(
Description: o.Description,
StartAt: o.StartAt,
Status: o.Status,
Format: o.Format,
Scenario: scenarios_service.MapScenarioLight(o.Scenario, domain),
Teams: mapTeams(o.Teams),
StartedAt: o.StartedAt,
+29 -3
View File
@@ -72,8 +72,15 @@ func (s *GameService) AddGame(
description string,
startAt time.Time,
scenarioId int32,
format string,
) (int, error) {
id, err := s.gamesRepo.AddGame(ctx, name, description, startAt, scenarioId)
if format == "" {
format = "online"
}
if format != "online" && format != "offline" {
return 0, errors.New("Формат игры должен быть online или offline")
}
id, err := s.gamesRepo.AddGame(ctx, name, description, startAt, scenarioId, format)
if err != nil {
return 0, err
}
@@ -170,8 +177,19 @@ func (s *GameService) UpdateGame(
description string,
startAt time.Time,
scenarioId int32,
format string,
) error {
return s.gamesRepo.UpdateGame(ctx, id, name, description, startAt, scenarioId)
if format == "" {
game, err := s.gamesRepo.GetGameByID(ctx, id)
if err != nil {
return err
}
format = game.Format
}
if format != "online" && format != "offline" {
return errors.New("Формат игры должен быть online или offline")
}
return s.gamesRepo.UpdateGame(ctx, id, name, description, startAt, scenarioId, format)
}
func (s *GameService) StartGame(ctx context.Context, id int) error {
@@ -255,7 +273,9 @@ func (s *GameService) GetTeamActions(ctx context.Context, teamId int, password s
for i, action := range actions {
codes[i] = action.Code
}
return s.storyteller.GetStory(scenario.Story, codes), mapGame(game, s.domain), actions, nil
story := s.storyteller.GetStory(scenario.Story, codes)
filterTeamStory(story, game.Format)
return story, mapGame(game, s.domain), actions, nil
}
func (s *GameService) AddTeamAction(ctx context.Context, teamId int, password string, actionCode string, viaMCP bool) error {
@@ -394,6 +414,9 @@ func (s *GameService) GetTeamAnswers(ctx context.Context, teamId int, password s
if err != nil {
return nil, err
}
if game.Format == "offline" {
return nil, nil
}
_, byCode, err := s.getScenarioQuestionsByCode(ctx, game.ScenarioId)
if err != nil {
return nil, err
@@ -424,6 +447,9 @@ func (s *GameService) SubmitTeamAnswers(ctx context.Context, teamId int, passwor
if err != nil {
return err
}
if game.Format == "offline" {
return errors.New("В offline-игре ответы через сайт недоступны")
}
switch game.Status {
case "run", "pause":
// игра идёт — можно отвечать
@@ -0,0 +1,18 @@
package game_service
import "evening_detective_server/internal/modules/storytelling"
// filterTeamStory применяется только к истории команды, общей для сайта и MCP.
func filterTeamStory(story *storytelling.Story, format string) {
if format != "offline" {
return
}
story.Introduction = nil
story.Questions = nil
for _, place := range story.Places {
for _, application := range place.Applications {
application.Image = ""
application.FileType = ""
}
}
}
@@ -0,0 +1,53 @@
package game_service
import (
"testing"
"evening_detective_server/internal/modules/cleaner"
"evening_detective_server/internal/modules/storytelling"
)
func TestTeamStoryFormat(t *testing.T) {
scenario := &storytelling.Story{
Introduction: &storytelling.Introduction{Text: "Введение", Audio: "intro.mp3"},
Questions: []*storytelling.Question{{Code: "q", Text: "Вопрос"}},
Places: []*storytelling.Place{{Code: "p", Text: "Точка", Image: "place.jpg",
Applications: []*storytelling.Application{
{Name: "Фото", Image: "clue.jpg", FileType: "image"},
{Name: "Документ", Image: "clue.pdf", FileType: "pdf"},
{Name: "Запись", Image: "clue.mp3", FileType: "audio"},
},
}},
}
for _, format := range []string{"offline", "online"} {
t.Run(format, func(t *testing.T) {
story := storytelling.NewStory(cleaner.NewCleaner()).GetStory(scenario, []string{"p"})
filterTeamStory(story, format)
if len(story.Places) != 1 || story.Places[0].Text != "Точка" || story.Places[0].Image != "place.jpg" {
t.Fatal("place content changed")
}
if format == "offline" {
if story.Introduction != nil || len(story.Questions) != 0 {
t.Fatal("offline story exposes introduction or questions")
}
} else if story.Introduction == nil || len(story.Questions) != 1 {
t.Fatal("online story lost introduction or questions")
}
if len(story.Places[0].Applications) != 3 {
t.Fatal("clue names missing")
}
for i, application := range story.Places[0].Applications {
original := scenario.Places[0].Applications[i]
if application.Name != original.Name || original.Image == "" || original.FileType == "" {
t.Fatal("clue name or original scenario changed")
}
if format == "offline" && (application.Image != "" || application.FileType != "") {
t.Fatal("offline story exposes a clue file")
}
if format == "online" && application.Image != original.Image {
t.Fatal("online story lost a clue file")
}
}
})
}
}