package app import ( "context" "fmt" "time" "gitea.dwysokinski.me/twhelp/core/internal/domain" ) type PlayerSnapshotRepository interface { // Create persists player snapshots in a store (e.g. Postgres). // Duplicates are ignored. Create(ctx context.Context, params ...domain.CreatePlayerSnapshotParams) error ListWithRelations( ctx context.Context, params domain.ListPlayerSnapshotsParams, ) (domain.ListPlayerSnapshotsWithRelationsResult, error) Delete(ctx context.Context, serverKey string, dateLTE time.Time) error } type PlayerSnapshotService struct { repo PlayerSnapshotRepository playerSvc *PlayerService pub SnapshotPublisher } func NewPlayerSnapshotService( repo PlayerSnapshotRepository, playerSvc *PlayerService, pub SnapshotPublisher, ) *PlayerSnapshotService { return &PlayerSnapshotService{repo: repo, playerSvc: playerSvc, pub: pub} } //nolint:gocyclo func (svc *PlayerSnapshotService) Create( ctx context.Context, createSnapshotsCmdPayload domain.CreateSnapshotsCmdPayload, ) error { serverKey := createSnapshotsCmdPayload.ServerKey() date := createSnapshotsCmdPayload.Date() listPlayersParams := domain.NewListPlayersParams() if err := listPlayersParams.SetServerKeys([]string{serverKey}); err != nil { return fmt.Errorf("%s: %w", serverKey, err) } if err := listPlayersParams.SetDeleted(domain.NullBool{ V: false, Valid: true, }); err != nil { return fmt.Errorf("%s: %w", serverKey, err) } if err := listPlayersParams.SetSort([]domain.PlayerSort{domain.PlayerSortIDASC}); err != nil { return fmt.Errorf("%s: %w", serverKey, err) } if err := listPlayersParams.SetLimit(domain.PlayerListMaxLimit); err != nil { return fmt.Errorf("%s: %w", serverKey, err) } for { res, err := svc.playerSvc.List(ctx, listPlayersParams) if err != nil { return fmt.Errorf("%s: %w", serverKey, err) } players := res.Players() if len(players) == 0 { break } params, err := domain.NewCreatePlayerSnapshotParams(players, date) if err != nil { return fmt.Errorf("%s: %w", serverKey, err) } if err = svc.repo.Create(ctx, params...); err != nil { return fmt.Errorf("%s: %w", serverKey, err) } if res.Next().IsZero() { break } if err = listPlayersParams.SetCursor(res.Next()); err != nil { return fmt.Errorf("%s: %w", serverKey, err) } } payload, err := domain.NewSnapshotsCreatedEventPayload(serverKey, createSnapshotsCmdPayload.VersionCode()) if err != nil { return fmt.Errorf("%s: couldn't construct domain.SnapshotsCreatedEventPayload: %w", serverKey, err) } if err = svc.pub.EventCreated(ctx, payload); err != nil { return fmt.Errorf("%s: %w", serverKey, err) } return nil } func (svc *PlayerSnapshotService) ListWithRelations( ctx context.Context, params domain.ListPlayerSnapshotsParams, ) (domain.ListPlayerSnapshotsWithRelationsResult, error) { return svc.repo.ListWithRelations(ctx, params) } func (svc *PlayerSnapshotService) CleanUp(ctx context.Context, payload domain.CleanUpDataCmdPayload) error { if !payload.CanCleanUpPlayerSnapshots() { return nil } if err := svc.repo.Delete( ctx, payload.Server().Key(), time.Now().Add(-domain.PlayerSnapshotRetentionForClosedServers), ); err != nil { return fmt.Errorf("%s: %w", payload.Server().Key(), err) } return nil }