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
+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,
}
}