132 lines
2.8 KiB
Go
132 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
|
||
|
}
|