diff --git a/bin/evening_detective_server b/bin/evening_detective_server index 895f1e1..3eb1722 100755 Binary files a/bin/evening_detective_server and b/bin/evening_detective_server differ diff --git a/internal/repos/team_answers_repo/repo.go b/internal/repos/team_answers_repo/repo.go index 5174443..4eac14e 100644 --- a/internal/repos/team_answers_repo/repo.go +++ b/internal/repos/team_answers_repo/repo.go @@ -2,16 +2,11 @@ package team_answers_repo import ( "context" - "errors" "evening_detective_server/internal/repos" "github.com/jackc/pgx/v5/pgxpool" ) -var ( - ErrAnswerNotFound = errors.New("Ответ не найден") -) - type TeamAnswersRepo struct { pool *pgxpool.Pool } @@ -136,28 +131,26 @@ func (r *TeamAnswersRepo) GetAnswersByGameID( } // SetAnswerScore выставляет балл за ответ; score == nil сбрасывает балл. +// Балл можно выставить и за неотвеченный вопрос (команда может не дать +// ответ): если строки (team_id, question_code) ещё нет, она создаётся с +// пустым ответом и выставленным баллом. func (r *TeamAnswersRepo) SetAnswerScore( ctx context.Context, teamId int, questionCode string, score *int, ) error { - tag, err := r.pool.Exec( + _, err := r.pool.Exec( ctx, - `UPDATE team_answers - SET - score = $1, - updated_at = NOW() - WHERE team_id = $2 AND question_code = $3`, - score, + `INSERT INTO team_answers (team_id, question_code, answer, score) + VALUES ($1, $2, '', $3) + ON CONFLICT (team_id, question_code) + DO UPDATE SET + score = EXCLUDED.score, + updated_at = NOW()`, teamId, questionCode, + score, ) - if err != nil { - return err - } - if tag.RowsAffected() == 0 { - return ErrAnswerNotFound - } - return nil + return err } diff --git a/internal/services/game_service/service.go b/internal/services/game_service/service.go index 3c1635d..8bf9f7b 100644 --- a/internal/services/game_service/service.go +++ b/internal/services/game_service/service.go @@ -418,8 +418,10 @@ func (s *GameService) SubmitTeamAnswers(ctx context.Context, teamId int, passwor // GetGameAnswers возвращает полную картину для оценки организатором: // вопросы сценария с правильными ответами и ответы всех команд с баллами. -// Ответы на удалённые из сценария вопросы не возвращаются (как в -// GetTeamAnswers). +// Команда может не дать ответ на вопрос — такой вопрос попадает в список +// ответов команды с пустым текстом и без балла (организатор видит его и +// может выставить балл, в т.ч. штрафной). Ответы на удалённые из сценария +// вопросы не возвращаются (как в GetTeamAnswers). func (s *GameService) GetGameAnswers(ctx context.Context, gameId int) (*GameAnswers, error) { game, err := s.gamesRepo.GetGameByID(ctx, gameId) if err != nil { @@ -437,23 +439,37 @@ func (s *GameService) GetGameAnswers(ctx context.Context, gameId int) (*GameAnsw if err != nil { return nil, err } - answersByTeam := make(map[int][]AnswerScore, len(teams)) + answerByTeamAndCode := make(map[int]map[string]AnswerScore, len(teams)) for _, row := range rows { if _, ok := byCode[row.QuestionCode]; !ok { continue } score := row.Score - answersByTeam[row.TeamId] = append(answersByTeam[row.TeamId], AnswerScore{ + if answerByTeamAndCode[row.TeamId] == nil { + answerByTeamAndCode[row.TeamId] = make(map[string]AnswerScore, len(questions)) + } + answerByTeamAndCode[row.TeamId][row.QuestionCode] = AnswerScore{ QuestionCode: row.QuestionCode, Text: row.Text, Score: score, - }) + } } teamAnswers := make([]TeamAnswers, 0, len(teams)) for _, team := range teams { + byCode := answerByTeamAndCode[team.ID] + // Все вопросы сценария в порядке сценария; неотвеченные — с пустым + // текстом и без балла, чтобы организатор видел пропуски. + answers := make([]AnswerScore, 0, len(questions)) + for _, question := range questions { + if answer, ok := byCode[question.Code]; ok { + answers = append(answers, answer) + continue + } + answers = append(answers, AnswerScore{QuestionCode: question.Code}) + } teamAnswers = append(teamAnswers, TeamAnswers{ Team: mapTeam(team), - Answers: answersByTeam[team.ID], + Answers: answers, }) } return &GameAnswers{