This commit is contained in:
2026-10-04 13:23:26 +07:00
parent 88307ed402
commit c663fbb504
20 changed files with 2125 additions and 502 deletions
+71
View File
@@ -685,6 +685,36 @@ service EveningDetectiveServer {
};
}
rpc GetScreenPlan(GetScreenPlanReq) returns (GetScreenPlanRsp) {
option (google.api.http) = { get: "/api/games/{gameID}/screen-plan" };
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Экраны";
summary: "Получить план экранов";
};
}
rpc SaveScreenPlan(SaveScreenPlanReq) returns (SaveScreenPlanRsp) {
option (google.api.http) = {
put : "/api/games/{gameID}/screen-plan"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Экраны";
summary: "Сохранить план экранов";
};
}
rpc SetScreenPlanStep(SetScreenPlanStepReq) returns (SetScreenPlanStepRsp) {
option (google.api.http) = {
post: "/api/games/{gameID}/screen-plan/step"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Экраны";
summary: "Перейти на шаг плана экранов";
};
}
rpc GetScreenResults(GetScreenResultsReq) returns (GetScreenResultsRsp) {
option (google.api.http) = { post: "/api/screens/results" body: "*" };
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
@@ -1410,6 +1440,7 @@ enum ScreenMode {
SCREEN_MODE_CONCLUSION_DONE = 5;
SCREEN_MODE_RESULTS = 6;
SCREEN_MODE_TIMER = 7;
SCREEN_MODE_TEXT = 8;
}
enum ScreenTimerState {
@@ -1455,6 +1486,7 @@ message ScreenState {
int64 timer_duration_seconds = 17;
int64 timer_remaining_seconds = 18;
ScreenTimerState timer_state = 19;
string text = 20;
}
message CreateScreenSessionReq {}
@@ -1563,6 +1595,45 @@ message SendScreenCommandReq {
message SendScreenCommandRsp {
}
message ScreenPlanStep {
ScreenMode mode = 1;
string text = 2;
string timer_label = 3;
int64 timer_duration_seconds = 4;
}
message ScreenPlan {
repeated ScreenPlanStep steps = 1;
int32 current_step = 2;
}
message GetScreenPlanReq {
int32 gameID = 1;
}
message GetScreenPlanRsp {
ScreenPlan plan = 1;
}
message SaveScreenPlanReq {
int32 gameID = 1;
repeated ScreenPlanStep steps = 2;
int32 current_step = 3;
}
message SaveScreenPlanRsp {
ScreenPlan plan = 1;
}
message SetScreenPlanStepReq {
int32 gameID = 1;
int32 current_step = 2;
}
message SetScreenPlanStepRsp {
ScreenPlan plan = 1;
}
message GetScreenResultsReq {
string screenID = 1;
string accessToken = 2;
+7
View File
@@ -18,6 +18,7 @@ import (
"evening_detective_server/internal/repos/applications_repo"
"evening_detective_server/internal/repos/game_map_repo"
"evening_detective_server/internal/repos/game_reviews_repo"
"evening_detective_server/internal/repos/game_screen_plans_repo"
"evening_detective_server/internal/repos/games_repo"
"evening_detective_server/internal/repos/health_repo"
"evening_detective_server/internal/repos/refresh_tokens_repo"
@@ -36,6 +37,7 @@ import (
"evening_detective_server/internal/services/mcp_service"
"evening_detective_server/internal/services/participation_service"
"evening_detective_server/internal/services/scenarios_service"
"evening_detective_server/internal/services/screen_plan_service"
"evening_detective_server/internal/services/screen_service"
"evening_detective_server/internal/services/team_service"
"evening_detective_server/internal/services/ticket_service"
@@ -134,6 +136,10 @@ func main() {
answersRepo := team_answers_repo.NewTeamAnswersRepo(dbpool)
screenRepo := screen_sessions_repo.NewScreenSessionsRepo(dbpool)
screenService := screen_service.NewScreenService(screenRepo)
screenPlanService := screen_plan_service.NewScreenPlanService(
game_screen_plans_repo.NewGameScreenPlansRepo(dbpool),
screenService,
)
mapService := map_service.NewMapService(game_map_repo.NewGameMapRepo(dbpool))
reviewsRepo := game_reviews_repo.NewGameReviewsRepo(dbpool)
gameService := game_service.NewGameService(
@@ -213,6 +219,7 @@ func main() {
ticketService,
participationService,
screenService,
screenPlanService,
mapService,
),
)
+198 -1
View File
@@ -1012,6 +1012,116 @@
]
}
},
"/api/games/{gameID}/screen-plan": {
"get": {
"summary": "Получить план экранов",
"operationId": "EveningDetectiveServer_GetScreenPlan",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/evening_detective_serverGetScreenPlanRsp"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/rpcStatus"
}
}
},
"parameters": [
{
"name": "gameID",
"in": "path",
"required": true,
"type": "integer",
"format": "int32"
}
],
"tags": [
"Экраны"
]
},
"put": {
"summary": "Сохранить план экранов",
"operationId": "EveningDetectiveServer_SaveScreenPlan",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/evening_detective_serverSaveScreenPlanRsp"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/rpcStatus"
}
}
},
"parameters": [
{
"name": "gameID",
"in": "path",
"required": true,
"type": "integer",
"format": "int32"
},
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/EveningDetectiveServerSaveScreenPlanBody"
}
}
],
"tags": [
"Экраны"
]
}
},
"/api/games/{gameID}/screen-plan/step": {
"post": {
"summary": "Перейти на шаг плана экранов",
"operationId": "EveningDetectiveServer_SetScreenPlanStep",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/evening_detective_serverSetScreenPlanStepRsp"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/rpcStatus"
}
}
},
"parameters": [
{
"name": "gameID",
"in": "path",
"required": true,
"type": "integer",
"format": "int32"
},
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/EveningDetectiveServerSetScreenPlanStepBody"
}
}
],
"tags": [
"Экраны"
]
}
},
"/api/games/{gameID}/screens": {
"get": {
"summary": "Получить экраны игры",
@@ -3176,6 +3286,22 @@
"EveningDetectiveServerResetGameBody": {
"type": "object"
},
"EveningDetectiveServerSaveScreenPlanBody": {
"type": "object",
"properties": {
"steps": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/evening_detective_serverScreenPlanStep"
}
},
"currentStep": {
"type": "integer",
"format": "int32"
}
}
},
"EveningDetectiveServerSendScreenCommandBody": {
"type": "object",
"properties": {
@@ -3200,6 +3326,15 @@
}
}
},
"EveningDetectiveServerSetScreenPlanStepBody": {
"type": "object",
"properties": {
"currentStep": {
"type": "integer",
"format": "int32"
}
}
},
"EveningDetectiveServerSetScreenSoundBody": {
"type": "object",
"properties": {
@@ -3903,6 +4038,14 @@
}
}
},
"evening_detective_serverGetScreenPlanRsp": {
"type": "object",
"properties": {
"plan": {
"$ref": "#/definitions/evening_detective_serverScreenPlan"
}
}
},
"evening_detective_serverGetScreenResultsReq": {
"type": "object",
"properties": {
@@ -4261,6 +4404,14 @@
},
"description": "Вопрос в шапке итоговой таблицы: правильный ответ (поле answer сообщения\nQuestion) командам/таблице не передаётся — он виден только в панели оценки."
},
"evening_detective_serverSaveScreenPlanRsp": {
"type": "object",
"properties": {
"plan": {
"$ref": "#/definitions/evening_detective_serverScreenPlan"
}
}
},
"evening_detective_serverScenario": {
"type": "object",
"properties": {
@@ -4339,10 +4490,45 @@
"SCREEN_MODE_CONCLUSION",
"SCREEN_MODE_CONCLUSION_DONE",
"SCREEN_MODE_RESULTS",
"SCREEN_MODE_TIMER"
"SCREEN_MODE_TIMER",
"SCREEN_MODE_TEXT"
],
"default": "SCREEN_MODE_UNSPECIFIED"
},
"evening_detective_serverScreenPlan": {
"type": "object",
"properties": {
"steps": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/evening_detective_serverScreenPlanStep"
}
},
"currentStep": {
"type": "integer",
"format": "int32"
}
}
},
"evening_detective_serverScreenPlanStep": {
"type": "object",
"properties": {
"mode": {
"$ref": "#/definitions/evening_detective_serverScreenMode"
},
"text": {
"type": "string"
},
"timerLabel": {
"type": "string"
},
"timerDurationSeconds": {
"type": "string",
"format": "int64"
}
}
},
"evening_detective_serverScreenState": {
"type": "object",
"properties": {
@@ -4408,6 +4594,9 @@
},
"timerState": {
"$ref": "#/definitions/evening_detective_serverScreenTimerState"
},
"text": {
"type": "string"
}
}
},
@@ -4428,6 +4617,14 @@
"evening_detective_serverSetScreenModeRsp": {
"type": "object"
},
"evening_detective_serverSetScreenPlanStepRsp": {
"type": "object",
"properties": {
"plan": {
"$ref": "#/definitions/evening_detective_serverScreenPlan"
}
}
},
"evening_detective_serverSetScreenSoundRsp": {
"type": "object"
},
+66
View File
@@ -0,0 +1,66 @@
package app
import (
"context"
"errors"
"evening_detective_server/internal/services/screen_plan_service"
proto "evening_detective_server/proto"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
func (s *server) GetScreenPlan(ctx context.Context, req *proto.GetScreenPlanReq) (*proto.GetScreenPlanRsp, error) {
if !canManageScreens(ctx) {
return nil, status.Error(codes.PermissionDenied, "permission denied")
}
if req == nil || req.GameID <= 0 {
return nil, status.Error(codes.InvalidArgument, "некорректный идентификатор игры")
}
if _, err := s.gameService.GetGame(ctx, int(req.GameID)); err != nil {
return nil, status.Error(codes.NotFound, "игра не найдена")
}
plan, err := s.screenPlanService.Get(ctx, int(req.GameID))
if errors.Is(err, screen_plan_service.ErrPlanNotFound) {
return &proto.GetScreenPlanRsp{}, nil
}
if err != nil {
return nil, status.Error(codes.Internal, "не удалось получить план экранов")
}
return &proto.GetScreenPlanRsp{Plan: mapScreenPlan(plan)}, nil
}
func (s *server) SaveScreenPlan(ctx context.Context, req *proto.SaveScreenPlanReq) (*proto.SaveScreenPlanRsp, error) {
if !canManageScreens(ctx) {
return nil, status.Error(codes.PermissionDenied, "permission denied")
}
if req == nil || req.GameID <= 0 {
return nil, status.Error(codes.InvalidArgument, "некорректный идентификатор игры")
}
if _, err := s.gameService.GetGame(ctx, int(req.GameID)); err != nil {
return nil, status.Error(codes.NotFound, "игра не найдена")
}
plan, err := s.screenPlanService.Save(ctx, int(req.GameID), mapScreenPlanSteps(req.Steps), int(req.CurrentStep))
if err != nil {
return nil, screenPlanStatus(err, "не удалось сохранить план экранов")
}
return &proto.SaveScreenPlanRsp{Plan: mapScreenPlan(plan)}, nil
}
func (s *server) SetScreenPlanStep(ctx context.Context, req *proto.SetScreenPlanStepReq) (*proto.SetScreenPlanStepRsp, error) {
if !canManageScreens(ctx) {
return nil, status.Error(codes.PermissionDenied, "permission denied")
}
if req == nil || req.GameID <= 0 || req.CurrentStep < 0 {
return nil, status.Error(codes.InvalidArgument, "некорректный шаг плана экранов")
}
if _, err := s.gameService.GetGame(ctx, int(req.GameID)); err != nil {
return nil, status.Error(codes.NotFound, "игра не найдена")
}
plan, err := s.screenPlanService.SetStep(ctx, int(req.GameID), int(req.CurrentStep))
if err != nil {
return nil, screenPlanStatus(err, "не удалось применить шаг плана экранов")
}
return &proto.SetScreenPlanStepRsp{Plan: mapScreenPlan(plan)}, nil
}
+22
View File
@@ -0,0 +1,22 @@
package app
import (
"errors"
"evening_detective_server/internal/services/screen_plan_service"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
func screenPlanStatus(err error, fallback string) error {
switch {
case errors.Is(err, screen_plan_service.ErrPlanNotFound):
return status.Error(codes.NotFound, "план экранов не найден")
case errors.Is(err, screen_plan_service.ErrStepNotFound):
return status.Error(codes.InvalidArgument, "шаг плана экранов не найден")
case errors.Is(err, screen_plan_service.ErrInvalidPlan):
return status.Error(codes.InvalidArgument, "некорректный план экранов")
default:
return status.Error(codes.Internal, fallback)
}
}
+39
View File
@@ -0,0 +1,39 @@
package app
import (
"evening_detective_server/internal/services/screen_plan_service"
proto "evening_detective_server/proto"
)
func mapScreenPlanSteps(steps []*proto.ScreenPlanStep) []screen_plan_service.Step {
mapped := make([]screen_plan_service.Step, 0, len(steps))
for _, step := range steps {
if step == nil {
mapped = append(mapped, screen_plan_service.Step{})
continue
}
mapped = append(mapped, screen_plan_service.Step{
Mode: int32(step.Mode),
Text: step.Text,
TimerLabel: step.TimerLabel,
TimerDurationSeconds: step.TimerDurationSeconds,
})
}
return mapped
}
func mapScreenPlan(plan *screen_plan_service.Plan) *proto.ScreenPlan {
if plan == nil {
return nil
}
steps := make([]*proto.ScreenPlanStep, 0, len(plan.Steps))
for _, step := range plan.Steps {
steps = append(steps, &proto.ScreenPlanStep{
Mode: proto.ScreenMode(step.Mode),
Text: step.Text,
TimerLabel: step.TimerLabel,
TimerDurationSeconds: step.TimerDurationSeconds,
})
}
return &proto.ScreenPlan{Steps: steps, CurrentStep: int32(plan.CurrentStep)}
}
+4
View File
@@ -93,6 +93,9 @@ func (s *server) BindScreen(ctx context.Context, req *proto.BindScreenReq) (*pro
}
return nil, status.Error(codes.Internal, "не удалось привязать экран")
}
if err := s.screenPlanService.ApplyCurrent(ctx, int(req.GameID)); err != nil {
return nil, status.Error(codes.Internal, "не удалось применить план экранов")
}
session, err := s.screenService.Get(ctx, id, hash)
if err != nil {
return nil, status.Error(codes.Internal, "не удалось получить состояние экрана")
@@ -179,6 +182,7 @@ func screenState(session *screen_service.ScreenSession) *proto.ScreenState {
TimerDurationSeconds: session.TimerDurationSeconds,
TimerRemainingSeconds: timerRemaining,
TimerState: timerState,
Text: session.Text,
}
if time.Since(session.LastSeen) <= 5*time.Second {
state.Status = "connected"
+4
View File
@@ -9,6 +9,7 @@ import (
"evening_detective_server/internal/services/map_service"
"evening_detective_server/internal/services/participation_service"
"evening_detective_server/internal/services/scenarios_service"
"evening_detective_server/internal/services/screen_plan_service"
"evening_detective_server/internal/services/screen_service"
"evening_detective_server/internal/services/team_service"
"evening_detective_server/internal/services/ticket_service"
@@ -32,6 +33,7 @@ type server struct {
ticketService *ticket_service.TicketService
participationService *participation_service.ParticipationService
screenService *screen_service.ScreenService
screenPlanService *screen_plan_service.ScreenPlanService
mapService *map_service.MapService
}
@@ -49,6 +51,7 @@ func NewServer(
ticketService *ticket_service.TicketService,
participationService *participation_service.ParticipationService,
screenService *screen_service.ScreenService,
screenPlanService *screen_plan_service.ScreenPlanService,
mapService *map_service.MapService,
) proto.EveningDetectiveServerServer {
return &server{
@@ -64,6 +67,7 @@ func NewServer(
ticketService: ticketService,
participationService: participationService,
screenService: screenService,
screenPlanService: screenPlanService,
mapService: mapService,
}
}
@@ -0,0 +1,95 @@
// Package game_screen_plans_repo persists game screen plans.
package game_screen_plans_repo
import (
"context"
"encoding/json"
"errors"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"
)
var ErrPlanNotFound = errors.New("план экранов не найден")
// Step is a stored screen plan step.
type Step struct {
Mode int32 `json:"mode"`
Text string `json:"text,omitempty"`
TimerLabel string `json:"timerLabel,omitempty"`
TimerDurationSeconds int64 `json:"timerDurationSeconds,omitempty"`
}
// Plan is a stored game screen plan.
type Plan struct {
GameID int
Steps []Step
CurrentStep int
}
// GameScreenPlansRepo persists game screen plans.
type GameScreenPlansRepo struct {
pool *pgxpool.Pool
}
// NewGameScreenPlansRepo creates a game screen plans repository.
func NewGameScreenPlansRepo(pool *pgxpool.Pool) *GameScreenPlansRepo {
return &GameScreenPlansRepo{pool: pool}
}
func (r *GameScreenPlansRepo) Get(ctx context.Context, gameID int) (*Plan, error) {
plan := &Plan{GameID: gameID}
var raw []byte
err := r.pool.QueryRow(
ctx,
`
SELECT steps, current_step
FROM game_screen_plans
WHERE game_id = $1`,
gameID,
).Scan(&raw, &plan.CurrentStep)
if errors.Is(err, pgx.ErrNoRows) {
return nil, ErrPlanNotFound
}
if err != nil {
return nil, err
}
if err := json.Unmarshal(raw, &plan.Steps); err != nil {
return nil, err
}
return plan, nil
}
func (r *GameScreenPlansRepo) Save(ctx context.Context, gameID int, steps []Step, currentStep int) (*Plan, error) {
raw, err := json.Marshal(steps)
if err != nil {
return nil, err
}
_, err = r.pool.Exec(
ctx,
`
INSERT INTO game_screen_plans (game_id, steps, current_step)
VALUES ($1, $2::jsonb, $3)
ON CONFLICT (game_id) DO UPDATE SET
steps = EXCLUDED.steps,
current_step = EXCLUDED.current_step,
updated_at = NOW()`,
gameID,
raw,
currentStep,
)
if err != nil {
return nil, err
}
return &Plan{GameID: gameID, Steps: steps, CurrentStep: currentStep}, nil
}
func (r *GameScreenPlansRepo) Delete(ctx context.Context, gameID int) error {
_, err := r.pool.Exec(
ctx,
`DELETE FROM game_screen_plans
WHERE game_id = $1`,
gameID,
)
return err
}
+36 -5
View File
@@ -29,6 +29,7 @@ type ScreenSession struct {
TimerRemainingSeconds int64
TimerState int32
TimerEndsAt *time.Time
Text string
}
// ScreenSessionsRepo persists screen sessions.
@@ -56,11 +57,11 @@ func (r *ScreenSessionsRepo) Get(ctx context.Context, id int, tokenHash string)
session := &ScreenSession{ID: id}
err := r.pool.QueryRow(ctx, `
SELECT game_id, sound_enabled, last_seen_at, mode, state_version, position_ms, command, volume,
timer_label, timer_duration_seconds, timer_remaining_seconds, timer_state, timer_ends_at
timer_label, timer_duration_seconds, timer_remaining_seconds, timer_state, timer_ends_at, text
FROM screen_sessions
WHERE id = $1 AND access_token_hash = $2`, id, tokenHash).
Scan(&session.GameID, &session.Sound, &session.LastSeen, &session.Mode, &session.Version, &session.Position, &session.Command, &session.Volume,
&session.TimerLabel, &session.TimerDurationSeconds, &session.TimerRemainingSeconds, &session.TimerState, &session.TimerEndsAt)
&session.TimerLabel, &session.TimerDurationSeconds, &session.TimerRemainingSeconds, &session.TimerState, &session.TimerEndsAt, &session.Text)
if errors.Is(err, pgx.ErrNoRows) {
return nil, ErrScreenNotFound
}
@@ -79,7 +80,7 @@ func (r *ScreenSessionsRepo) Get(ctx context.Context, id int, tokenHash string)
func (r *ScreenSessionsRepo) ListByGame(ctx context.Context, gameID int) ([]*ScreenSession, error) {
rows, err := r.pool.Query(ctx, `
SELECT id, game_id, sound_enabled, last_seen_at, mode, state_version, position_ms, command, volume,
timer_label, timer_duration_seconds, timer_remaining_seconds, timer_state, timer_ends_at
timer_label, timer_duration_seconds, timer_remaining_seconds, timer_state, timer_ends_at, text
FROM screen_sessions
WHERE game_id = $1
ORDER BY id`, gameID)
@@ -92,7 +93,7 @@ func (r *ScreenSessionsRepo) ListByGame(ctx context.Context, gameID int) ([]*Scr
for rows.Next() {
session := &ScreenSession{}
if err := rows.Scan(&session.ID, &session.GameID, &session.Sound, &session.LastSeen, &session.Mode, &session.Version, &session.Position, &session.Command, &session.Volume,
&session.TimerLabel, &session.TimerDurationSeconds, &session.TimerRemainingSeconds, &session.TimerState, &session.TimerEndsAt); err != nil {
&session.TimerLabel, &session.TimerDurationSeconds, &session.TimerRemainingSeconds, &session.TimerState, &session.TimerEndsAt, &session.Text); err != nil {
return nil, err
}
sessions = append(sessions, session)
@@ -102,7 +103,35 @@ func (r *ScreenSessionsRepo) ListByGame(ctx context.Context, gameID int) ([]*Scr
// SetMode updates the display mode for a game.
func (r *ScreenSessionsRepo) SetMode(ctx context.Context, gameID int, mode int32) error {
_, err := r.pool.Exec(ctx, `UPDATE screen_sessions SET mode = $2, command = 0, state_version = state_version + 1, updated_at = NOW() WHERE game_id = $1`, gameID, mode)
_, err := r.pool.Exec(
ctx,
`UPDATE screen_sessions
SET mode = $2,
text = '',
command = 0,
state_version = state_version + 1,
updated_at = NOW()
WHERE game_id = $1`,
gameID,
mode,
)
return err
}
// SetText updates the text screen for a game.
func (r *ScreenSessionsRepo) SetText(ctx context.Context, gameID int, text string) error {
_, err := r.pool.Exec(
ctx,
`UPDATE screen_sessions
SET mode = 8,
text = $2,
command = 0,
state_version = state_version + 1,
updated_at = NOW()
WHERE game_id = $1`,
gameID,
text,
)
return err
}
@@ -130,6 +159,7 @@ func (r *ScreenSessionsRepo) SetTimer(ctx context.Context, gameID int, duration
timer_ends_at = NULL,
command = 0,
state_version = state_version + 1,
text = '',
updated_at = NOW()
WHERE game_id = $1`, gameID, label, duration)
return err
@@ -194,6 +224,7 @@ func (r *ScreenSessionsRepo) Unbind(ctx context.Context, id int) error {
timer_remaining_seconds = 600,
timer_state = 1,
timer_ends_at = NULL,
text = '',
state_version = state_version + 1,
updated_at = NOW()
WHERE id = $1`, id)
@@ -0,0 +1,29 @@
package screen_plan_service
import "evening_detective_server/internal/repos/game_screen_plans_repo"
func mapPlan(plan *game_screen_plans_repo.Plan) *Plan {
steps := make([]Step, 0, len(plan.Steps))
for _, step := range plan.Steps {
steps = append(steps, Step{
Mode: step.Mode,
Text: step.Text,
TimerLabel: step.TimerLabel,
TimerDurationSeconds: step.TimerDurationSeconds,
})
}
return &Plan{GameID: plan.GameID, Steps: steps, CurrentStep: plan.CurrentStep}
}
func mapSteps(steps []Step) []game_screen_plans_repo.Step {
mapped := make([]game_screen_plans_repo.Step, 0, len(steps))
for _, step := range steps {
mapped = append(mapped, game_screen_plans_repo.Step{
Mode: step.Mode,
Text: step.Text,
TimerLabel: step.TimerLabel,
TimerDurationSeconds: step.TimerDurationSeconds,
})
}
return mapped
}
@@ -0,0 +1,157 @@
// Package screen_plan_service handles game screen plans.
package screen_plan_service
import (
"context"
"errors"
"strings"
"evening_detective_server/internal/repos/game_screen_plans_repo"
"evening_detective_server/internal/services/screen_service"
)
var (
// ErrPlanNotFound indicates that a game has no screen plan.
ErrPlanNotFound = errors.New("план экранов не найден")
// ErrInvalidPlan indicates invalid screen plan data.
ErrInvalidPlan = errors.New("некорректный план экранов")
// ErrStepNotFound indicates that a screen plan step does not exist.
ErrStepNotFound = errors.New("шаг плана экранов не найден")
)
// Step is a screen plan step used by the application layer.
type Step struct {
Mode int32
Text string
TimerLabel string
TimerDurationSeconds int64
}
// Plan is a game screen plan used by the application layer.
type Plan struct {
GameID int
Steps []Step
CurrentStep int
}
// ScreenPlanService handles game screen plans and applies their current step.
type ScreenPlanService struct {
repo *game_screen_plans_repo.GameScreenPlansRepo
screenService *screen_service.ScreenService
}
// NewScreenPlanService creates a screen plan service.
func NewScreenPlanService(
repo *game_screen_plans_repo.GameScreenPlansRepo,
screenService *screen_service.ScreenService,
) *ScreenPlanService {
return &ScreenPlanService{repo: repo, screenService: screenService}
}
// Get returns a game's screen plan.
func (s *ScreenPlanService) Get(ctx context.Context, gameID int) (*Plan, error) {
stored, err := s.repo.Get(ctx, gameID)
if errors.Is(err, game_screen_plans_repo.ErrPlanNotFound) {
return nil, ErrPlanNotFound
}
if err != nil {
return nil, err
}
return mapPlan(stored), nil
}
// Save validates, stores and applies a game's screen plan.
func (s *ScreenPlanService) Save(ctx context.Context, gameID int, steps []Step, currentStep int) (*Plan, error) {
steps, err := normalizePlan(steps, currentStep)
if err != nil {
return nil, err
}
if len(steps) == 0 {
if err := s.repo.Delete(ctx, gameID); err != nil {
return nil, err
}
return nil, nil
}
stored, err := s.repo.Save(ctx, gameID, mapSteps(steps), currentStep)
if err != nil {
return nil, err
}
plan := mapPlan(stored)
if err := s.applyStep(ctx, gameID, plan); err != nil {
return nil, err
}
return plan, nil
}
// SetStep stores and applies the selected step.
func (s *ScreenPlanService) SetStep(ctx context.Context, gameID, currentStep int) (*Plan, error) {
plan, err := s.Get(ctx, gameID)
if err != nil {
return nil, err
}
if currentStep < 0 || currentStep >= len(plan.Steps) {
return nil, ErrStepNotFound
}
stored, err := s.repo.Save(ctx, gameID, mapSteps(plan.Steps), currentStep)
if err != nil {
return nil, err
}
plan = mapPlan(stored)
if err := s.applyStep(ctx, gameID, plan); err != nil {
return nil, err
}
return plan, nil
}
// ApplyCurrent applies the current step when a screen joins a game.
func (s *ScreenPlanService) ApplyCurrent(ctx context.Context, gameID int) error {
plan, err := s.Get(ctx, gameID)
if errors.Is(err, ErrPlanNotFound) {
return nil
}
if err != nil {
return err
}
return s.applyStep(ctx, gameID, plan)
}
func normalizePlan(steps []Step, currentStep int) ([]Step, error) {
if currentStep < 0 || (len(steps) > 0 && currentStep >= len(steps)) || (len(steps) == 0 && currentStep != 0) {
return nil, ErrInvalidPlan
}
normalized := make([]Step, 0, len(steps))
for _, step := range steps {
if step.Mode <= 0 || step.Mode > 8 {
return nil, ErrInvalidPlan
}
step.Text = strings.TrimSpace(step.Text)
step.TimerLabel = strings.TrimSpace(step.TimerLabel)
if step.Mode == 8 && (step.Text == "" || len(step.Text) > 10000) {
return nil, ErrInvalidPlan
}
if step.Mode == 7 {
if step.TimerDurationSeconds <= 0 || step.TimerDurationSeconds > 24*60*60 {
return nil, ErrInvalidPlan
}
if step.TimerLabel == "" {
step.TimerLabel = "Перерыв"
}
if len(step.TimerLabel) > 200 {
return nil, ErrInvalidPlan
}
}
normalized = append(normalized, step)
}
return normalized, nil
}
func (s *ScreenPlanService) applyStep(ctx context.Context, gameID int, plan *Plan) error {
step := plan.Steps[plan.CurrentStep]
if step.Mode == 8 {
return s.screenService.SetText(ctx, gameID, step.Text)
}
if step.Mode == 7 {
return s.screenService.SetTimer(ctx, gameID, step.TimerDurationSeconds, step.TimerLabel)
}
return s.screenService.SetMode(ctx, gameID, step.Mode)
}
@@ -0,0 +1,46 @@
package screen_plan_service
import "testing"
func TestNormalizePlan(t *testing.T) {
tests := []struct {
name string
steps []Step
currentStep int
wantErr bool
}{
{
name: "normalizes text and timer defaults",
steps: []Step{
{Mode: 8, Text: " Внимание "},
{Mode: 7, TimerDurationSeconds: 60},
},
},
{
name: "rejects empty text",
steps: []Step{{Mode: 8}},
wantErr: true,
},
{
name: "rejects missing current step",
steps: []Step{{Mode: 1}},
currentStep: 1,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
steps, err := normalizePlan(tt.steps, tt.currentStep)
if (err != nil) != tt.wantErr {
t.Fatalf("normalizePlan() error = %v, want error: %v", err, tt.wantErr)
}
if tt.wantErr {
return
}
if steps[0].Text != "Внимание" || steps[1].TimerLabel != "Перерыв" {
t.Fatalf("plan defaults were not normalized: %+v", steps)
}
})
}
}
@@ -23,6 +23,7 @@ type ScreenSession struct {
TimerRemainingSeconds int64
TimerState int32
TimerEndsAt *time.Time
Text string
}
func mapSession(session *screen_sessions_repo.ScreenSession) *ScreenSession {
@@ -41,5 +42,6 @@ func mapSession(session *screen_sessions_repo.ScreenSession) *ScreenSession {
TimerRemainingSeconds: session.TimerRemainingSeconds,
TimerState: session.TimerState,
TimerEndsAt: session.TimerEndsAt,
Text: session.Text,
}
}
@@ -57,6 +57,11 @@ func (s *ScreenService) SetTimer(ctx context.Context, gameID int, duration int64
return s.repo.SetTimer(ctx, gameID, duration, label)
}
// SetText updates the text screen for a game.
func (s *ScreenService) SetText(ctx context.Context, gameID int, text string) error {
return s.repo.SetText(ctx, gameID, text)
}
// SetTimerCommand updates the timer command for a game.
func (s *ScreenService) SetTimerCommand(ctx context.Context, gameID int, command int32) error {
return s.repo.SetTimerCommand(ctx, gameID, command)
@@ -0,0 +1,10 @@
-- +goose Up
CREATE TABLE IF NOT EXISTS game_screen_plans (
game_id INTEGER PRIMARY KEY REFERENCES games(id) ON DELETE CASCADE,
steps JSONB NOT NULL DEFAULT '[]'::jsonb,
current_step INTEGER NOT NULL DEFAULT 0,
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);
-- +goose Down
DROP TABLE IF EXISTS game_screen_plans;
@@ -0,0 +1,6 @@
-- +goose Up
ALTER TABLE screen_sessions
ADD COLUMN IF NOT EXISTS text TEXT NOT NULL DEFAULT '';
-- +goose Down
ALTER TABLE screen_sessions DROP COLUMN IF EXISTS text;
+968 -496
View File
File diff suppressed because it is too large Load Diff
+246
View File
@@ -2180,6 +2180,135 @@ func local_request_EveningDetectiveServer_SendScreenCommand_0(ctx context.Contex
return msg, metadata, err
}
func request_EveningDetectiveServer_GetScreenPlan_0(ctx context.Context, marshaler runtime.Marshaler, client EveningDetectiveServerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq GetScreenPlanReq
metadata runtime.ServerMetadata
err error
)
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
val, ok := pathParams["gameID"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "gameID")
}
protoReq.GameID, err = runtime.Int32(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "gameID", err)
}
msg, err := client.GetScreenPlan(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_EveningDetectiveServer_GetScreenPlan_0(ctx context.Context, marshaler runtime.Marshaler, server EveningDetectiveServerServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq GetScreenPlanReq
metadata runtime.ServerMetadata
err error
)
val, ok := pathParams["gameID"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "gameID")
}
protoReq.GameID, err = runtime.Int32(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "gameID", err)
}
msg, err := server.GetScreenPlan(ctx, &protoReq)
return msg, metadata, err
}
func request_EveningDetectiveServer_SaveScreenPlan_0(ctx context.Context, marshaler runtime.Marshaler, client EveningDetectiveServerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq SaveScreenPlanReq
metadata runtime.ServerMetadata
err error
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
val, ok := pathParams["gameID"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "gameID")
}
protoReq.GameID, err = runtime.Int32(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "gameID", err)
}
msg, err := client.SaveScreenPlan(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_EveningDetectiveServer_SaveScreenPlan_0(ctx context.Context, marshaler runtime.Marshaler, server EveningDetectiveServerServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq SaveScreenPlanReq
metadata runtime.ServerMetadata
err error
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
val, ok := pathParams["gameID"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "gameID")
}
protoReq.GameID, err = runtime.Int32(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "gameID", err)
}
msg, err := server.SaveScreenPlan(ctx, &protoReq)
return msg, metadata, err
}
func request_EveningDetectiveServer_SetScreenPlanStep_0(ctx context.Context, marshaler runtime.Marshaler, client EveningDetectiveServerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq SetScreenPlanStepReq
metadata runtime.ServerMetadata
err error
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
val, ok := pathParams["gameID"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "gameID")
}
protoReq.GameID, err = runtime.Int32(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "gameID", err)
}
msg, err := client.SetScreenPlanStep(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_EveningDetectiveServer_SetScreenPlanStep_0(ctx context.Context, marshaler runtime.Marshaler, server EveningDetectiveServerServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq SetScreenPlanStepReq
metadata runtime.ServerMetadata
err error
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
val, ok := pathParams["gameID"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "gameID")
}
protoReq.GameID, err = runtime.Int32(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "gameID", err)
}
msg, err := server.SetScreenPlanStep(ctx, &protoReq)
return msg, metadata, err
}
func request_EveningDetectiveServer_GetScreenResults_0(ctx context.Context, marshaler runtime.Marshaler, client EveningDetectiveServerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq GetScreenResultsReq
@@ -4479,6 +4608,66 @@ func RegisterEveningDetectiveServerHandlerServer(ctx context.Context, mux *runti
}
forward_EveningDetectiveServer_SendScreenCommand_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodGet, pattern_EveningDetectiveServer_GetScreenPlan_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/crabs.evening_detective_server.EveningDetectiveServer/GetScreenPlan", runtime.WithHTTPPathPattern("/api/games/{gameID}/screen-plan"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_EveningDetectiveServer_GetScreenPlan_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_EveningDetectiveServer_GetScreenPlan_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPut, pattern_EveningDetectiveServer_SaveScreenPlan_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/crabs.evening_detective_server.EveningDetectiveServer/SaveScreenPlan", runtime.WithHTTPPathPattern("/api/games/{gameID}/screen-plan"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_EveningDetectiveServer_SaveScreenPlan_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_EveningDetectiveServer_SaveScreenPlan_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_EveningDetectiveServer_SetScreenPlanStep_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/crabs.evening_detective_server.EveningDetectiveServer/SetScreenPlanStep", runtime.WithHTTPPathPattern("/api/games/{gameID}/screen-plan/step"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_EveningDetectiveServer_SetScreenPlanStep_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_EveningDetectiveServer_SetScreenPlanStep_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_EveningDetectiveServer_GetScreenResults_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
@@ -6045,6 +6234,57 @@ func RegisterEveningDetectiveServerHandlerClient(ctx context.Context, mux *runti
}
forward_EveningDetectiveServer_SendScreenCommand_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodGet, pattern_EveningDetectiveServer_GetScreenPlan_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/crabs.evening_detective_server.EveningDetectiveServer/GetScreenPlan", runtime.WithHTTPPathPattern("/api/games/{gameID}/screen-plan"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_EveningDetectiveServer_GetScreenPlan_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_EveningDetectiveServer_GetScreenPlan_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPut, pattern_EveningDetectiveServer_SaveScreenPlan_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/crabs.evening_detective_server.EveningDetectiveServer/SaveScreenPlan", runtime.WithHTTPPathPattern("/api/games/{gameID}/screen-plan"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_EveningDetectiveServer_SaveScreenPlan_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_EveningDetectiveServer_SaveScreenPlan_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_EveningDetectiveServer_SetScreenPlanStep_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/crabs.evening_detective_server.EveningDetectiveServer/SetScreenPlanStep", runtime.WithHTTPPathPattern("/api/games/{gameID}/screen-plan/step"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_EveningDetectiveServer_SetScreenPlanStep_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_EveningDetectiveServer_SetScreenPlanStep_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_EveningDetectiveServer_GetScreenResults_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
@@ -6566,6 +6806,9 @@ var (
pattern_EveningDetectiveServer_SetScreenMode_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3, 2, 4}, []string{"api", "games", "gameID", "screens", "mode"}, ""))
pattern_EveningDetectiveServer_SetScreenTimer_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3, 2, 4}, []string{"api", "games", "gameID", "screens", "timer"}, ""))
pattern_EveningDetectiveServer_SendScreenCommand_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3, 2, 4}, []string{"api", "games", "gameID", "screens", "command"}, ""))
pattern_EveningDetectiveServer_GetScreenPlan_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3}, []string{"api", "games", "gameID", "screen-plan"}, ""))
pattern_EveningDetectiveServer_SaveScreenPlan_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3}, []string{"api", "games", "gameID", "screen-plan"}, ""))
pattern_EveningDetectiveServer_SetScreenPlanStep_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3, 2, 4}, []string{"api", "games", "gameID", "screen-plan", "step"}, ""))
pattern_EveningDetectiveServer_GetScreenResults_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "screens", "results"}, ""))
pattern_EveningDetectiveServer_DownloadTeamQR_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3, 2, 4}, []string{"api", "games", "ID", "teams", "qr.pdf"}, ""))
pattern_EveningDetectiveServer_GetGameAnswers_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3}, []string{"api", "games", "gameID", "answers"}, ""))
@@ -6654,6 +6897,9 @@ var (
forward_EveningDetectiveServer_SetScreenMode_0 = runtime.ForwardResponseMessage
forward_EveningDetectiveServer_SetScreenTimer_0 = runtime.ForwardResponseMessage
forward_EveningDetectiveServer_SendScreenCommand_0 = runtime.ForwardResponseMessage
forward_EveningDetectiveServer_GetScreenPlan_0 = runtime.ForwardResponseMessage
forward_EveningDetectiveServer_SaveScreenPlan_0 = runtime.ForwardResponseMessage
forward_EveningDetectiveServer_SetScreenPlanStep_0 = runtime.ForwardResponseMessage
forward_EveningDetectiveServer_GetScreenResults_0 = runtime.ForwardResponseMessage
forward_EveningDetectiveServer_DownloadTeamQR_0 = runtime.ForwardResponseMessage
forward_EveningDetectiveServer_GetGameAnswers_0 = runtime.ForwardResponseMessage
+114
View File
@@ -84,6 +84,9 @@ const (
EveningDetectiveServer_SetScreenMode_FullMethodName = "/crabs.evening_detective_server.EveningDetectiveServer/SetScreenMode"
EveningDetectiveServer_SetScreenTimer_FullMethodName = "/crabs.evening_detective_server.EveningDetectiveServer/SetScreenTimer"
EveningDetectiveServer_SendScreenCommand_FullMethodName = "/crabs.evening_detective_server.EveningDetectiveServer/SendScreenCommand"
EveningDetectiveServer_GetScreenPlan_FullMethodName = "/crabs.evening_detective_server.EveningDetectiveServer/GetScreenPlan"
EveningDetectiveServer_SaveScreenPlan_FullMethodName = "/crabs.evening_detective_server.EveningDetectiveServer/SaveScreenPlan"
EveningDetectiveServer_SetScreenPlanStep_FullMethodName = "/crabs.evening_detective_server.EveningDetectiveServer/SetScreenPlanStep"
EveningDetectiveServer_GetScreenResults_FullMethodName = "/crabs.evening_detective_server.EveningDetectiveServer/GetScreenResults"
EveningDetectiveServer_DownloadTeamQR_FullMethodName = "/crabs.evening_detective_server.EveningDetectiveServer/DownloadTeamQR"
EveningDetectiveServer_GetGameAnswers_FullMethodName = "/crabs.evening_detective_server.EveningDetectiveServer/GetGameAnswers"
@@ -185,6 +188,9 @@ type EveningDetectiveServerClient interface {
SetScreenTimer(ctx context.Context, in *SetScreenTimerReq, opts ...grpc.CallOption) (*SetScreenTimerRsp, error)
// Отправить общую команду экранам игры.
SendScreenCommand(ctx context.Context, in *SendScreenCommandReq, opts ...grpc.CallOption) (*SendScreenCommandRsp, error)
GetScreenPlan(ctx context.Context, in *GetScreenPlanReq, opts ...grpc.CallOption) (*GetScreenPlanRsp, error)
SaveScreenPlan(ctx context.Context, in *SaveScreenPlanReq, opts ...grpc.CallOption) (*SaveScreenPlanRsp, error)
SetScreenPlanStep(ctx context.Context, in *SetScreenPlanStepReq, opts ...grpc.CallOption) (*SetScreenPlanStepRsp, error)
GetScreenResults(ctx context.Context, in *GetScreenResultsReq, opts ...grpc.CallOption) (*GetScreenResultsRsp, error)
DownloadTeamQR(ctx context.Context, in *GetGameReq, opts ...grpc.CallOption) (*httpbody.HttpBody, error)
GetGameAnswers(ctx context.Context, in *GetGameAnswersReq, opts ...grpc.CallOption) (*GetGameAnswersRsp, error)
@@ -802,6 +808,36 @@ func (c *eveningDetectiveServerClient) SendScreenCommand(ctx context.Context, in
return out, nil
}
func (c *eveningDetectiveServerClient) GetScreenPlan(ctx context.Context, in *GetScreenPlanReq, opts ...grpc.CallOption) (*GetScreenPlanRsp, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetScreenPlanRsp)
err := c.cc.Invoke(ctx, EveningDetectiveServer_GetScreenPlan_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *eveningDetectiveServerClient) SaveScreenPlan(ctx context.Context, in *SaveScreenPlanReq, opts ...grpc.CallOption) (*SaveScreenPlanRsp, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(SaveScreenPlanRsp)
err := c.cc.Invoke(ctx, EveningDetectiveServer_SaveScreenPlan_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *eveningDetectiveServerClient) SetScreenPlanStep(ctx context.Context, in *SetScreenPlanStepReq, opts ...grpc.CallOption) (*SetScreenPlanStepRsp, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(SetScreenPlanStepRsp)
err := c.cc.Invoke(ctx, EveningDetectiveServer_SetScreenPlanStep_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *eveningDetectiveServerClient) GetScreenResults(ctx context.Context, in *GetScreenResultsReq, opts ...grpc.CallOption) (*GetScreenResultsRsp, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetScreenResultsRsp)
@@ -1144,6 +1180,9 @@ type EveningDetectiveServerServer interface {
SetScreenTimer(context.Context, *SetScreenTimerReq) (*SetScreenTimerRsp, error)
// Отправить общую команду экранам игры.
SendScreenCommand(context.Context, *SendScreenCommandReq) (*SendScreenCommandRsp, error)
GetScreenPlan(context.Context, *GetScreenPlanReq) (*GetScreenPlanRsp, error)
SaveScreenPlan(context.Context, *SaveScreenPlanReq) (*SaveScreenPlanRsp, error)
SetScreenPlanStep(context.Context, *SetScreenPlanStepReq) (*SetScreenPlanStepRsp, error)
GetScreenResults(context.Context, *GetScreenResultsReq) (*GetScreenResultsRsp, error)
DownloadTeamQR(context.Context, *GetGameReq) (*httpbody.HttpBody, error)
GetGameAnswers(context.Context, *GetGameAnswersReq) (*GetGameAnswersRsp, error)
@@ -1355,6 +1394,15 @@ func (UnimplementedEveningDetectiveServerServer) SetScreenTimer(context.Context,
func (UnimplementedEveningDetectiveServerServer) SendScreenCommand(context.Context, *SendScreenCommandReq) (*SendScreenCommandRsp, error) {
return nil, status.Error(codes.Unimplemented, "method SendScreenCommand not implemented")
}
func (UnimplementedEveningDetectiveServerServer) GetScreenPlan(context.Context, *GetScreenPlanReq) (*GetScreenPlanRsp, error) {
return nil, status.Error(codes.Unimplemented, "method GetScreenPlan not implemented")
}
func (UnimplementedEveningDetectiveServerServer) SaveScreenPlan(context.Context, *SaveScreenPlanReq) (*SaveScreenPlanRsp, error) {
return nil, status.Error(codes.Unimplemented, "method SaveScreenPlan not implemented")
}
func (UnimplementedEveningDetectiveServerServer) SetScreenPlanStep(context.Context, *SetScreenPlanStepReq) (*SetScreenPlanStepRsp, error) {
return nil, status.Error(codes.Unimplemented, "method SetScreenPlanStep not implemented")
}
func (UnimplementedEveningDetectiveServerServer) GetScreenResults(context.Context, *GetScreenResultsReq) (*GetScreenResultsRsp, error) {
return nil, status.Error(codes.Unimplemented, "method GetScreenResults not implemented")
}
@@ -2502,6 +2550,60 @@ func _EveningDetectiveServer_SendScreenCommand_Handler(srv interface{}, ctx cont
return interceptor(ctx, in, info, handler)
}
func _EveningDetectiveServer_GetScreenPlan_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetScreenPlanReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(EveningDetectiveServerServer).GetScreenPlan(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: EveningDetectiveServer_GetScreenPlan_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(EveningDetectiveServerServer).GetScreenPlan(ctx, req.(*GetScreenPlanReq))
}
return interceptor(ctx, in, info, handler)
}
func _EveningDetectiveServer_SaveScreenPlan_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SaveScreenPlanReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(EveningDetectiveServerServer).SaveScreenPlan(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: EveningDetectiveServer_SaveScreenPlan_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(EveningDetectiveServerServer).SaveScreenPlan(ctx, req.(*SaveScreenPlanReq))
}
return interceptor(ctx, in, info, handler)
}
func _EveningDetectiveServer_SetScreenPlanStep_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SetScreenPlanStepReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(EveningDetectiveServerServer).SetScreenPlanStep(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: EveningDetectiveServer_SetScreenPlanStep_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(EveningDetectiveServerServer).SetScreenPlanStep(ctx, req.(*SetScreenPlanStepReq))
}
return interceptor(ctx, in, info, handler)
}
func _EveningDetectiveServer_GetScreenResults_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetScreenResultsReq)
if err := dec(in); err != nil {
@@ -3227,6 +3329,18 @@ var EveningDetectiveServer_ServiceDesc = grpc.ServiceDesc{
MethodName: "SendScreenCommand",
Handler: _EveningDetectiveServer_SendScreenCommand_Handler,
},
{
MethodName: "GetScreenPlan",
Handler: _EveningDetectiveServer_GetScreenPlan_Handler,
},
{
MethodName: "SaveScreenPlan",
Handler: _EveningDetectiveServer_SaveScreenPlan_Handler,
},
{
MethodName: "SetScreenPlanStep",
Handler: _EveningDetectiveServer_SetScreenPlanStep_Handler,
},
{
MethodName: "GetScreenResults",
Handler: _EveningDetectiveServer_GetScreenResults_Handler,