nquest/pkg/service/game.go

131 lines
2.8 KiB
Go

package service
import (
"context"
"errors"
"time"
"github.com/jackc/pgx/v5/pgconn"
"gitrepo.ru/neonxp/nquest/pkg/models"
"gorm.io/gorm"
)
var (
ErrGameNotStarted = errors.New("game not started")
)
type Game struct {
DB *gorm.DB
}
// NewGame returns new Game.
func NewGame(db *gorm.DB) *Game {
return &Game{
DB: db,
}
}
func (gs *Game) GetByID(ctx context.Context, id uint) (*models.Game, error) {
g := &models.Game{}
return g, gs.DB.
WithContext(ctx).
Preload("FirstTask").
First(g, id).
Error
}
func (gs *Game) List(ctx context.Context) ([]*models.Game, error) {
games := make([]*models.Game, 0)
return games, gs.DB.
WithContext(ctx).
Preload("Teams").
Preload("Teams.Team").
Order("start_at ASC").
Find(&games, "visible = true").
Limit(20).
Error
}
func (gs *Game) GetTaskID(ctx context.Context, id uint) (*models.Task, error) {
t := &models.Task{}
return t, gs.DB.WithContext(ctx).Preload("Next").First(t, id).Error
}
func (gs *Game) GetHistory(ctx context.Context, game *models.Game, team *models.Team) ([]*models.GamePassing, error) {
db := gs.DB.WithContext(ctx)
history := []*models.GamePassing{}
return history, db.Where(`team_id = ? and game_id = ?`, team.ID, game.ID).Find(&history).Error
}
func (gs *Game) GetState(ctx context.Context, game *models.Game, team *models.Team) (*models.GamePassing, error) {
db := gs.DB.WithContext(ctx)
if !game.StartAt.Before(time.Now()) {
return nil, ErrGameNotStarted
}
// Пытаемся получить GamePassing
gamepass := &models.GamePassing{
Team: team,
Game: game,
Task: game.FirstTask,
Status: models.PassStarted,
Codes: []*models.Code{},
CreatedAt: game.StartAt,
Deadline: game.StartAt.Add(time.Minute * time.Duration(game.FirstTask.MaxTime)),
}
err := db.
Where(`team_id = ? and game_id = ? and status = ?`, team.ID, game.ID, models.PassStarted).
Preload("Task").
FirstOrCreate(gamepass).
Error
if err != nil {
if err, ok := err.(*pgconn.PgError); ok {
if err.Code == "23505" {
return nil, nil
}
}
return nil, err
}
for {
if !gamepass.Timeouted() {
break
}
gamepass.Status = models.PassFailed
gamepass.FinishedAt = &gamepass.Deadline
if err := db.Save(gamepass).Error; err != nil {
return nil, err
}
taskID := gamepass.TaskID
task, err := gs.GetTaskID(ctx, taskID)
if err != nil {
return nil, err
}
if task.Next == nil {
return nil, nil
}
gamepass = &models.GamePassing{
Team: team,
Game: game,
Task: task.Next,
CreatedAt: gamepass.Deadline,
Deadline: gamepass.Deadline.Add(time.Minute * time.Duration(task.Next.MaxTime)),
Status: models.PassStarted,
Codes: []*models.Code{},
}
if err := db.Create(gamepass).Error; err != nil {
return nil, err
}
}
return gamepass, nil
}