package service import ( "context" "errors" "strings" "github.com/jackc/pgx/v5/pgconn" "gitrepo.ru/neonxp/nquest/pkg/models" "gorm.io/gorm" ) var ( ErrGameNotStarted = errors.New("game not started") ErrInvalidCode = errors.New("invalid code") ErrOldCode = errors.New("old code") ErrGameFinished = errors.New("game finished") ) type Engine struct { DB *gorm.DB } func NewEngine(db *gorm.DB) *Engine { return &Engine{ DB: db, } } func (e *Engine) GetState(ctx context.Context, game *models.Game, user *models.User) (*models.GameCursor, error) { db := e.DB.WithContext(ctx) // Пытаемся получить GamePassing cursor := &models.GameCursor{ User: user, Game: game, Task: game.Tasks[0], Status: models.TaskStarted, Codes: []*models.Code{}, } err := db. Where(`user_id = ? and game_id = ? and status = ?`, user.ID, game.ID, models.TaskStarted). Preload("Task"). Preload("Task.Codes"). Preload("Task.Next"). Preload("Task.Next.Codes"). Preload("Codes"). FirstOrCreate(cursor). Error if err != nil { if err, ok := err.(*pgconn.PgError); ok { if err.Code == "23505" { return nil, ErrGameNotStarted } } return nil, err } return cursor, nil } func (e *Engine) EnterCode(ctx context.Context, game *models.Game, user *models.User, code string) (*models.GameCursor, error) { db := e.DB.WithContext(ctx) st, err := e.GetState(ctx, game, user) if err != nil { return nil, err } code = strings.Trim(code, " \n\t") code = strings.ToLower(code) var currentCode *models.Code for _, c := range st.Task.Codes { if c.Code == code { currentCode = c break } } if currentCode == nil { return nil, ErrInvalidCode } for _, c := range st.Codes { if c.ID == currentCode.ID { return nil, ErrOldCode } } st.Codes = append(st.Codes, currentCode) if err := db.Save(st).Error; err != nil { return nil, err } if len(st.Codes) != len(st.Task.Codes) { return st, nil } // Уровень пройден. Выдаем следующий st.Status = models.TaskFinished if err := db.Save(st).Error; err != nil { return nil, err } if st.Task.Next == nil { user.Experience += st.Game.Points if err := db.Save(user).Error; err != nil { return nil, err } return nil, ErrGameFinished } newState := &models.GameCursor{ User: user, Game: game, Task: st.Task.Next, Status: models.TaskStarted, Codes: []*models.Code{}, } return newState, db.Create(newState).Error }