generated from VLADIMIR/template
update
This commit is contained in:
@@ -34,6 +34,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_service"
|
||||
"evening_detective_server/internal/services/team_service"
|
||||
"evening_detective_server/internal/services/ticket_service"
|
||||
"evening_detective_server/internal/services/ui_service"
|
||||
@@ -137,6 +138,7 @@ func main() {
|
||||
applicationsRepo := applications_repo.NewApplicationsRepo(dbpool)
|
||||
answersRepo := team_answers_repo.NewTeamAnswersRepo(dbpool)
|
||||
screenRepo := screen_sessions_repo.NewScreenSessionsRepo(dbpool)
|
||||
screenService := screen_service.NewScreenService(screenRepo)
|
||||
mapRepo := game_map_repo.New(dbpool)
|
||||
reviewsRepo := game_reviews_repo.New(dbpool)
|
||||
gameService := game_service.NewGameService(
|
||||
@@ -217,7 +219,7 @@ func main() {
|
||||
teamService,
|
||||
ticketService,
|
||||
participationService,
|
||||
screenRepo,
|
||||
screenService,
|
||||
mapRepo,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -186,6 +186,7 @@ SQL держи рядом с методом, которому он нужен.
|
||||
`make generate`.
|
||||
- Преобразование `proto ↔ domain/storage` находится в `app` или пакете,
|
||||
которому принадлежат обе модели; бизнес-правила в mapping не прячь.
|
||||
- Поля структур в mapping-литералах располагай вертикально: одно поле на строку.
|
||||
- Любой mapping, принадлежащий сервису, находится в отдельной функции и файле
|
||||
`*_mapper.go`, а не внутри метода сервиса.
|
||||
- Модель, которой владеет один пакет, объявляй в этом пакете. Не создавай
|
||||
@@ -270,6 +271,7 @@ go test ./...
|
||||
- Аббревиатуры пишутся единообразно: `ID`, `URL`, `HTTP`, `JWT`, `SQL`.
|
||||
- Используй префикс `I` в именах интерфейсов: `IFileStorage`, `IEmailSender`.
|
||||
- Не прячь побочные эффекты в функциях с именами `Get`, `Map`, `Parse`.
|
||||
- Сервисы и конструкторы называй по предметной области: `ScreenService` и `NewScreenService`; generic-имена `Service` и `New` запрещены.
|
||||
- `New...` создаёт значение и возвращает ошибку только если создание реально
|
||||
может завершиться ошибкой.
|
||||
- Форматируй код `gofmt`; не добавляй ручное форматирование, генераторы и
|
||||
|
||||
@@ -33,7 +33,7 @@ func (s *server) GetGameMap(ctx context.Context, req *proto.GetGameMapReq) (*pro
|
||||
return nil, status.Error(codes.Internal, "не удалось сохранить карту")
|
||||
}
|
||||
}
|
||||
screens, err := s.screenRepo.ListByGame(ctx, int(req.GameID))
|
||||
screens, err := s.screenService.ListByGame(ctx, int(req.GameID))
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, "не удалось получить экраны")
|
||||
}
|
||||
|
||||
+20
-20
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
"evening_detective_server/internal/modules/processor_jwt"
|
||||
"evening_detective_server/internal/modules/roles"
|
||||
"evening_detective_server/internal/repos/screen_sessions_repo"
|
||||
"evening_detective_server/internal/services/screen_service"
|
||||
proto "evening_detective_server/proto"
|
||||
|
||||
"github.com/google/uuid"
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
|
||||
func (s *server) CreateScreenSession(ctx context.Context, _ *proto.CreateScreenSessionReq) (*proto.CreateScreenSessionRsp, error) {
|
||||
token := uuid.New().String()
|
||||
id, err := s.screenRepo.Create(ctx, hashScreenToken(token))
|
||||
id, err := s.screenService.Create(ctx, hashScreenToken(token))
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, "не удалось создать сессию экрана")
|
||||
}
|
||||
@@ -37,8 +37,8 @@ func (s *server) GetScreenState(ctx context.Context, req *proto.GetScreenStateRe
|
||||
if err != nil || id <= 0 {
|
||||
return nil, status.Error(codes.InvalidArgument, "некорректный идентификатор экрана")
|
||||
}
|
||||
session, err := s.screenRepo.Get(ctx, id, hashScreenToken(req.AccessToken))
|
||||
if errors.Is(err, screen_sessions_repo.ErrScreenNotFound) {
|
||||
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 {
|
||||
@@ -59,7 +59,7 @@ func (s *server) GetGameScreens(ctx context.Context, req *proto.GetGameScreensRe
|
||||
if _, err := s.gameService.GetGame(ctx, int(req.GameID)); err != nil {
|
||||
return nil, status.Error(codes.NotFound, "игра не найдена")
|
||||
}
|
||||
sessions, err := s.screenRepo.ListByGame(ctx, int(req.GameID))
|
||||
sessions, err := s.screenService.ListByGame(ctx, int(req.GameID))
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, "не удалось получить экраны игры")
|
||||
}
|
||||
@@ -87,13 +87,13 @@ func (s *server) BindScreen(ctx context.Context, req *proto.BindScreenReq) (*pro
|
||||
return nil, status.Error(codes.NotFound, "игра не найдена")
|
||||
}
|
||||
hash := hashScreenToken(req.AccessToken)
|
||||
if err = s.screenRepo.Bind(ctx, id, int(req.GameID), hash); err != nil {
|
||||
if errors.Is(err, screen_sessions_repo.ErrScreenNotFound) {
|
||||
if err = s.screenService.Bind(ctx, id, int(req.GameID), hash); err != nil {
|
||||
if errors.Is(err, screen_service.ErrScreenNotFound) {
|
||||
return nil, status.Error(codes.Unauthenticated, "недействительная сессия экрана")
|
||||
}
|
||||
return nil, status.Error(codes.Internal, "не удалось привязать экран")
|
||||
}
|
||||
session, err := s.screenRepo.Get(ctx, id, hash)
|
||||
session, err := s.screenService.Get(ctx, id, hash)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, "не удалось получить состояние экрана")
|
||||
}
|
||||
@@ -113,8 +113,8 @@ func (s *server) UnbindScreen(ctx context.Context, req *proto.UnbindScreenReq) (
|
||||
if err != nil || id <= 0 {
|
||||
return nil, status.Error(codes.InvalidArgument, "некорректный идентификатор экрана")
|
||||
}
|
||||
if err = s.screenRepo.Unbind(ctx, id); err != nil {
|
||||
if errors.Is(err, screen_sessions_repo.ErrScreenNotFound) {
|
||||
if err = s.screenService.Unbind(ctx, id); err != nil {
|
||||
if errors.Is(err, screen_service.ErrScreenNotFound) {
|
||||
return nil, status.Error(codes.NotFound, "экран не найден")
|
||||
}
|
||||
return nil, status.Error(codes.Internal, "не удалось отвязать экран")
|
||||
@@ -133,8 +133,8 @@ func (s *server) SetScreenSound(ctx context.Context, req *proto.SetScreenSoundRe
|
||||
if err != nil || id <= 0 {
|
||||
return nil, status.Error(codes.InvalidArgument, "некорректный идентификатор экрана")
|
||||
}
|
||||
if err = s.screenRepo.SetSound(ctx, id, req.SoundEnabled); err != nil {
|
||||
if errors.Is(err, screen_sessions_repo.ErrScreenNotFound) {
|
||||
if err = s.screenService.SetSound(ctx, id, req.SoundEnabled); err != nil {
|
||||
if errors.Is(err, screen_service.ErrScreenNotFound) {
|
||||
return nil, status.Error(codes.NotFound, "экран не найден")
|
||||
}
|
||||
return nil, status.Error(codes.Internal, "не удалось назначить экран со звуком")
|
||||
@@ -155,7 +155,7 @@ func hashScreenToken(token string) string {
|
||||
return base64.RawURLEncoding.EncodeToString(sum[:])
|
||||
}
|
||||
|
||||
func screenState(session *screen_sessions_repo.ScreenSession) *proto.ScreenState {
|
||||
func screenState(session *screen_service.ScreenSession) *proto.ScreenState {
|
||||
timerState := proto.ScreenTimerState(session.TimerState)
|
||||
timerRemaining := session.TimerRemainingSeconds
|
||||
if timerState == proto.ScreenTimerState_SCREEN_TIMER_STATE_RUNNING {
|
||||
@@ -193,7 +193,7 @@ func screenState(session *screen_sessions_repo.ScreenSession) *proto.ScreenState
|
||||
return state
|
||||
}
|
||||
|
||||
func (s *server) enrichScreenState(ctx context.Context, session *screen_sessions_repo.ScreenSession, state *proto.ScreenState) {
|
||||
func (s *server) enrichScreenState(ctx context.Context, session *screen_service.ScreenSession, state *proto.ScreenState) {
|
||||
if session.GameID == nil {
|
||||
return
|
||||
}
|
||||
@@ -229,7 +229,7 @@ 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 err := s.screenRepo.SetMode(ctx, int(req.GameID), int32(req.Mode)); err != nil {
|
||||
if err := s.screenService.SetMode(ctx, int(req.GameID), int32(req.Mode)); err != nil {
|
||||
return nil, status.Error(codes.Internal, "не удалось изменить режим экранов")
|
||||
}
|
||||
return &proto.SetScreenModeRsp{}, nil
|
||||
@@ -252,7 +252,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.screenRepo.SetTimer(ctx, int(req.GameID), req.DurationSeconds, label); err != nil {
|
||||
if err := s.screenService.SetTimer(ctx, int(req.GameID), req.DurationSeconds, label); err != nil {
|
||||
return nil, status.Error(codes.Internal, "не удалось настроить таймер экранов")
|
||||
}
|
||||
return &proto.SetScreenTimerRsp{}, nil
|
||||
@@ -269,7 +269,7 @@ func (s *server) SendScreenCommand(ctx context.Context, req *proto.SendScreenCom
|
||||
return nil, status.Error(codes.NotFound, "игра не найдена")
|
||||
}
|
||||
if isTimerCommand(req.Command) {
|
||||
if err := s.screenRepo.SetTimerCommand(ctx, int(req.GameID), int32(req.Command)); err != nil {
|
||||
if err := s.screenService.SetTimerCommand(ctx, int(req.GameID), int32(req.Command)); err != nil {
|
||||
return nil, status.Error(codes.Internal, "не удалось выполнить команду таймера")
|
||||
}
|
||||
return &proto.SendScreenCommandRsp{}, nil
|
||||
@@ -280,7 +280,7 @@ func (s *server) SendScreenCommand(ctx context.Context, req *proto.SendScreenCom
|
||||
if req.Volume < 0 || req.Volume > 100 {
|
||||
return nil, status.Error(codes.InvalidArgument, "некорректная громкость")
|
||||
}
|
||||
if err := s.screenRepo.SetCommand(ctx, int(req.GameID), int32(req.Command), req.PositionMs, req.Volume); err != nil {
|
||||
if err := s.screenService.SetCommand(ctx, int(req.GameID), int32(req.Command), req.PositionMs, req.Volume); err != nil {
|
||||
return nil, status.Error(codes.Internal, "не удалось сохранить позицию экранов")
|
||||
}
|
||||
return &proto.SendScreenCommandRsp{}, nil
|
||||
@@ -298,8 +298,8 @@ func (s *server) GetScreenResults(ctx context.Context, req *proto.GetScreenResul
|
||||
if err != nil || id <= 0 {
|
||||
return nil, status.Error(codes.InvalidArgument, "некорректный идентификатор экрана")
|
||||
}
|
||||
session, err := s.screenRepo.Get(ctx, id, hashScreenToken(req.AccessToken))
|
||||
if errors.Is(err, screen_sessions_repo.ErrScreenNotFound) {
|
||||
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 {
|
||||
|
||||
@@ -4,12 +4,12 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"evening_detective_server/internal/repos/screen_sessions_repo"
|
||||
"evening_detective_server/internal/services/screen_service"
|
||||
proto "evening_detective_server/proto"
|
||||
)
|
||||
|
||||
func TestScreenStateTimer(t *testing.T) {
|
||||
session := &screen_sessions_repo.ScreenSession{
|
||||
session := &screen_service.ScreenSession{
|
||||
ID: 1,
|
||||
LastSeen: time.Now(),
|
||||
TimerLabel: "Перерыв",
|
||||
|
||||
@@ -2,7 +2,6 @@ package app
|
||||
|
||||
import (
|
||||
"evening_detective_server/internal/repos/game_map_repo"
|
||||
"evening_detective_server/internal/repos/screen_sessions_repo"
|
||||
"evening_detective_server/internal/services/answer_service"
|
||||
"evening_detective_server/internal/services/file_service"
|
||||
"evening_detective_server/internal/services/game_review_service"
|
||||
@@ -10,6 +9,7 @@ import (
|
||||
"evening_detective_server/internal/services/gameplay_service"
|
||||
"evening_detective_server/internal/services/participation_service"
|
||||
"evening_detective_server/internal/services/scenarios_service"
|
||||
"evening_detective_server/internal/services/screen_service"
|
||||
"evening_detective_server/internal/services/team_service"
|
||||
"evening_detective_server/internal/services/ticket_service"
|
||||
"evening_detective_server/internal/services/ui_service"
|
||||
@@ -31,7 +31,7 @@ type server struct {
|
||||
teamService *team_service.TeamService
|
||||
ticketService *ticket_service.Service
|
||||
participationService *participation_service.ParticipationService
|
||||
screenRepo *screen_sessions_repo.ScreenSessionsRepo
|
||||
screenService *screen_service.ScreenService
|
||||
mapRepo *game_map_repo.Repo
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ func NewServer(
|
||||
teamService *team_service.TeamService,
|
||||
ticketService *ticket_service.Service,
|
||||
participationService *participation_service.ParticipationService,
|
||||
screenRepo *screen_sessions_repo.ScreenSessionsRepo,
|
||||
screenService *screen_service.ScreenService,
|
||||
mapRepo *game_map_repo.Repo,
|
||||
) proto.EveningDetectiveServerServer {
|
||||
return &server{
|
||||
@@ -62,7 +62,7 @@ func NewServer(
|
||||
teamService: teamService,
|
||||
ticketService: ticketService,
|
||||
participationService: participationService,
|
||||
screenRepo: screenRepo,
|
||||
screenService: screenService,
|
||||
mapRepo: mapRepo,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package screen_service
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"evening_detective_server/internal/repos/screen_sessions_repo"
|
||||
)
|
||||
|
||||
type ScreenSession struct {
|
||||
ID int
|
||||
GameID *int
|
||||
Sound bool
|
||||
LastSeen time.Time
|
||||
Mode int32
|
||||
Version int64
|
||||
Position int64
|
||||
Command int32
|
||||
Volume int32
|
||||
TimerLabel string
|
||||
TimerDurationSeconds int64
|
||||
TimerRemainingSeconds int64
|
||||
TimerState int32
|
||||
TimerEndsAt *time.Time
|
||||
}
|
||||
|
||||
func mapSession(session *screen_sessions_repo.ScreenSession) *ScreenSession {
|
||||
return &ScreenSession{
|
||||
ID: session.ID,
|
||||
GameID: session.GameID,
|
||||
Sound: session.Sound,
|
||||
LastSeen: session.LastSeen,
|
||||
Mode: session.Mode,
|
||||
Version: session.Version,
|
||||
Position: session.Position,
|
||||
Command: session.Command,
|
||||
Volume: session.Volume,
|
||||
TimerLabel: session.TimerLabel,
|
||||
TimerDurationSeconds: session.TimerDurationSeconds,
|
||||
TimerRemainingSeconds: session.TimerRemainingSeconds,
|
||||
TimerState: session.TimerState,
|
||||
TimerEndsAt: session.TimerEndsAt,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package screen_service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"evening_detective_server/internal/repos/screen_sessions_repo"
|
||||
)
|
||||
|
||||
var ErrScreenNotFound = errors.New("Экран не найден")
|
||||
|
||||
type ScreenService struct {
|
||||
repo *screen_sessions_repo.ScreenSessionsRepo
|
||||
}
|
||||
|
||||
func NewScreenService(repo *screen_sessions_repo.ScreenSessionsRepo) *ScreenService {
|
||||
return &ScreenService{repo: repo}
|
||||
}
|
||||
|
||||
func (s *ScreenService) Create(ctx context.Context, tokenHash string) (int, error) {
|
||||
return s.repo.Create(ctx, tokenHash)
|
||||
}
|
||||
|
||||
func (s *ScreenService) Get(ctx context.Context, id int, tokenHash string) (*ScreenSession, error) {
|
||||
session, err := s.repo.Get(ctx, id, tokenHash)
|
||||
if err != nil {
|
||||
return nil, mapError(err)
|
||||
}
|
||||
return mapSession(session), nil
|
||||
}
|
||||
|
||||
func (s *ScreenService) ListByGame(ctx context.Context, gameID int) ([]*ScreenSession, error) {
|
||||
sessions, err := s.repo.ListByGame(ctx, gameID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make([]*ScreenSession, 0, len(sessions))
|
||||
for _, session := range sessions {
|
||||
result = append(result, mapSession(session))
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *ScreenService) SetMode(ctx context.Context, gameID int, mode int32) error {
|
||||
return s.repo.SetMode(ctx, gameID, mode)
|
||||
}
|
||||
|
||||
func (s *ScreenService) SetTimer(ctx context.Context, gameID int, duration int64, label string) error {
|
||||
return s.repo.SetTimer(ctx, gameID, duration, label)
|
||||
}
|
||||
|
||||
func (s *ScreenService) SetTimerCommand(ctx context.Context, gameID int, command int32) error {
|
||||
return s.repo.SetTimerCommand(ctx, gameID, command)
|
||||
}
|
||||
|
||||
func (s *ScreenService) SetCommand(ctx context.Context, gameID int, command int32, position int64, volume int32) error {
|
||||
return s.repo.SetCommand(ctx, gameID, command, position, volume)
|
||||
}
|
||||
|
||||
func (s *ScreenService) Bind(ctx context.Context, id, gameID int, tokenHash string) error {
|
||||
return mapError(s.repo.Bind(ctx, id, gameID, tokenHash))
|
||||
}
|
||||
|
||||
func (s *ScreenService) Unbind(ctx context.Context, id int) error {
|
||||
return mapError(s.repo.Unbind(ctx, id))
|
||||
}
|
||||
|
||||
func (s *ScreenService) SetSound(ctx context.Context, id int, enabled bool) error {
|
||||
return mapError(s.repo.SetSound(ctx, id, enabled))
|
||||
}
|
||||
|
||||
func mapError(err error) error {
|
||||
if errors.Is(err, screen_sessions_repo.ErrScreenNotFound) {
|
||||
return ErrScreenNotFound
|
||||
}
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user