This commit is contained in:
2026-10-23 03:02:22 +07:00
parent 980ca18b60
commit 0879a8946a
20 changed files with 1827 additions and 960 deletions
+4
View File
@@ -14,6 +14,8 @@ func mapScreenPlanSteps(steps []*proto.ScreenPlanStep) []screen_plan_service.Ste
}
mapped = append(mapped, screen_plan_service.Step{
Mode: int32(step.Mode),
QRURL: step.QrUrl,
TimerType: int32(step.TimerType),
Text: step.Text,
TimerLabel: step.TimerLabel,
TimerDurationSeconds: step.TimerDurationSeconds,
@@ -30,6 +32,8 @@ func mapScreenPlan(plan *screen_plan_service.Plan) *proto.ScreenPlan {
for _, step := range plan.Steps {
steps = append(steps, &proto.ScreenPlanStep{
Mode: proto.ScreenMode(step.Mode),
QrUrl: step.QRURL,
TimerType: proto.ScreenTimerType(step.TimerType),
Text: step.Text,
TimerLabel: step.TimerLabel,
TimerDurationSeconds: step.TimerDurationSeconds,
+40 -3
View File
@@ -222,6 +222,8 @@ func screenState(session *screen_service.ScreenSession) *proto.ScreenState {
TimerRemainingSeconds: timerRemaining,
TimerState: timerState,
Text: session.Text,
QrUrl: session.QRURL,
TimerType: proto.ScreenTimerType(session.TimerType),
}
if time.Since(session.LastSeen) <= 5*time.Second {
state.Status = "connected"
@@ -277,6 +279,15 @@ func (s *server) SetScreenMode(ctx context.Context, req *proto.SetScreenModeReq)
if _, err := s.gameService.GetGame(ctx, int(req.GameID)); err != nil {
return nil, status.Error(codes.NotFound, "игра не найдена")
}
if req.Mode == proto.ScreenMode_SCREEN_MODE_QR {
if !screen_service.ValidQR(req.QrUrl, req.Text) {
return nil, status.Error(codes.InvalidArgument, "Укажите ссылку HTTP/HTTPS до 2048 байт и текст до 10000 байт")
}
if err := s.screenService.SetQR(ctx, int(req.GameID), req.QrUrl, req.Text); err != nil {
return nil, status.Error(codes.Internal, "не удалось изменить QR-код")
}
return &proto.SetScreenModeRsp{}, nil
}
if err := s.screenService.SetMode(ctx, int(req.GameID), int32(req.Mode)); err != nil {
return nil, status.Error(codes.Internal, "не удалось изменить режим экранов")
}
@@ -287,12 +298,12 @@ func (s *server) SetScreenTimer(ctx context.Context, req *proto.SetScreenTimerRe
if !canManageScreens(ctx) {
return nil, status.Error(codes.PermissionDenied, "permission denied")
}
if req == nil || req.GameID <= 0 || req.DurationSeconds <= 0 || req.DurationSeconds > 24*60*60 {
if req == nil || req.GameID <= 0 || req.DurationSeconds <= 0 || req.DurationSeconds > 24*60*60 || !screen_service.ValidTimerType(int32(req.TimerType)) {
return nil, status.Error(codes.InvalidArgument, "некорректные параметры таймера")
}
label := strings.TrimSpace(req.Label)
if label == "" {
label = "Перерыв"
label = screen_service.TimerLabel(int32(req.TimerType))
}
if len(label) > 200 {
return nil, status.Error(codes.InvalidArgument, "слишком длинная надпись таймера")
@@ -300,7 +311,7 @@ func (s *server) SetScreenTimer(ctx context.Context, req *proto.SetScreenTimerRe
if _, err := s.gameService.GetGame(ctx, int(req.GameID)); err != nil {
return nil, status.Error(codes.NotFound, "игра не найдена")
}
if err := s.screenService.SetTimer(ctx, int(req.GameID), req.DurationSeconds, label); err != nil {
if err := s.screenService.SetTimer(ctx, int(req.GameID), req.DurationSeconds, label, int32(req.TimerType)); err != nil {
return nil, status.Error(codes.Internal, "не удалось настроить таймер экранов")
}
return &proto.SetScreenTimerRsp{}, nil
@@ -366,3 +377,29 @@ func (s *server) GetScreenResults(ctx context.Context, req *proto.GetScreenResul
}
return &proto.GetScreenResultsRsp{Questions: mapResultsQuestions(results.Questions), Teams: mapTeamResults(results.Teams)}, nil
}
func (s *server) GetScreenTeams(ctx context.Context, req *proto.GetScreenTeamsReq) (*proto.GetScreenTeamsRsp, error) {
if req == nil || req.ScreenID == "" || req.AccessToken == "" {
return nil, status.Error(codes.InvalidArgument, "некорректные данные экрана")
}
id, err := strconv.Atoi(req.ScreenID)
if err != nil || id <= 0 {
return nil, status.Error(codes.InvalidArgument, "некорректный идентификатор экрана")
}
session, err := s.screenService.Get(ctx, id, hashScreenToken(req.AccessToken))
if errors.Is(err, screen_service.ErrScreenNotFound) {
return nil, status.Error(codes.Unauthenticated, "недействительная сессия экрана")
}
if err != nil || session.GameID == nil {
return nil, status.Error(codes.FailedPrecondition, "экран не привязан к игре")
}
teams, err := s.teamService.GetScreenTeams(ctx, *session.GameID)
if err != nil {
return nil, status.Error(codes.Internal, "не удалось получить команды")
}
result := make([]*proto.ScreenTeam, 0, len(teams))
for _, team := range teams {
result = append(result, &proto.ScreenTeam{ID: team.ID, Name: team.Name})
}
return &proto.GetScreenTeamsRsp{Teams: result}, nil
}
+19
View File
@@ -26,3 +26,22 @@ func TestSetScreenModeRejectsRemovedModes(t *testing.T) {
}
}
}
func TestScreenTeamsRejectsInvalidAccess(t *testing.T) {
tests := []struct {
name string
req *proto.GetScreenTeamsReq
}{
{"missing", nil},
{"missing token", &proto.GetScreenTeamsReq{ScreenID: "1"}},
{"bad ID", &proto.GetScreenTeamsReq{ScreenID: "bad", AccessToken: "token"}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, err := (&server{}).GetScreenTeams(context.Background(), tt.req)
if status.Code(err) != codes.InvalidArgument {
t.Fatalf("unexpected error: %v", err)
}
})
}
}