added free agents
This commit is contained in:
342
internal/db/freeagent.go
Normal file
342
internal/db/freeagent.go
Normal file
@@ -0,0 +1,342 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
// SeasonLeagueFreeAgent tracks players registered as free agents in a season_league.
|
||||
type SeasonLeagueFreeAgent struct {
|
||||
bun.BaseModel `bun:"table:season_league_free_agents,alias:slfa"`
|
||||
|
||||
SeasonID int `bun:",pk,notnull"`
|
||||
LeagueID int `bun:",pk,notnull"`
|
||||
PlayerID int `bun:",pk,notnull"`
|
||||
RegisteredAt int64 `bun:",notnull"`
|
||||
RegisteredByUserID int `bun:",notnull"`
|
||||
|
||||
Season *Season `bun:"rel:belongs-to,join:season_id=id"`
|
||||
League *League `bun:"rel:belongs-to,join:league_id=id"`
|
||||
Player *Player `bun:"rel:belongs-to,join:player_id=id"`
|
||||
RegisteredBy *User `bun:"rel:belongs-to,join:registered_by_user_id=id"`
|
||||
}
|
||||
|
||||
// FixtureFreeAgent tracks which free agents are nominated for specific fixtures.
|
||||
type FixtureFreeAgent struct {
|
||||
bun.BaseModel `bun:"table:fixture_free_agents,alias:ffa"`
|
||||
|
||||
FixtureID int `bun:",pk,notnull"`
|
||||
PlayerID int `bun:",pk,notnull"`
|
||||
TeamID int `bun:",notnull"`
|
||||
NominatedByUserID int `bun:",notnull"`
|
||||
NominatedAt int64 `bun:",notnull"`
|
||||
|
||||
Fixture *Fixture `bun:"rel:belongs-to,join:fixture_id=id"`
|
||||
Player *Player `bun:"rel:belongs-to,join:player_id=id"`
|
||||
Team *Team `bun:"rel:belongs-to,join:team_id=id"`
|
||||
NominatedBy *User `bun:"rel:belongs-to,join:nominated_by_user_id=id"`
|
||||
}
|
||||
|
||||
// RegisterFreeAgent registers a player as a free agent in a season_league.
|
||||
func RegisterFreeAgent(
|
||||
ctx context.Context,
|
||||
tx bun.Tx,
|
||||
seasonID, leagueID, playerID int,
|
||||
audit *AuditMeta,
|
||||
) error {
|
||||
user := CurrentUser(ctx)
|
||||
if user == nil {
|
||||
return errors.New("user cannot be nil")
|
||||
}
|
||||
|
||||
entry := &SeasonLeagueFreeAgent{
|
||||
SeasonID: seasonID,
|
||||
LeagueID: leagueID,
|
||||
PlayerID: playerID,
|
||||
RegisteredAt: time.Now().Unix(),
|
||||
RegisteredByUserID: user.ID,
|
||||
}
|
||||
|
||||
info := &AuditInfo{
|
||||
Action: "free_agents.add",
|
||||
ResourceType: "season_league_free_agent",
|
||||
ResourceID: fmt.Sprintf("%d-%d-%d", seasonID, leagueID, playerID),
|
||||
Details: map[string]any{
|
||||
"season_id": seasonID,
|
||||
"league_id": leagueID,
|
||||
"player_id": playerID,
|
||||
},
|
||||
}
|
||||
|
||||
err := Insert(tx, entry).WithAudit(audit, info).Exec(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Insert")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnregisterFreeAgent removes a player's free agent registration and all their nominations.
|
||||
func UnregisterFreeAgent(
|
||||
ctx context.Context,
|
||||
tx bun.Tx,
|
||||
seasonID, leagueID, playerID int,
|
||||
audit *AuditMeta,
|
||||
) error {
|
||||
// First remove all nominations for this player
|
||||
err := RemoveAllFreeAgentNominationsForPlayer(ctx, tx, playerID)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "RemoveAllFreeAgentNominationsForPlayer")
|
||||
}
|
||||
|
||||
// Then remove the registration
|
||||
_, err = tx.NewDelete().
|
||||
Model((*SeasonLeagueFreeAgent)(nil)).
|
||||
Where("season_id = ?", seasonID).
|
||||
Where("league_id = ?", leagueID).
|
||||
Where("player_id = ?", playerID).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "tx.NewDelete")
|
||||
}
|
||||
|
||||
info := &AuditInfo{
|
||||
Action: "free_agents.remove",
|
||||
ResourceType: "season_league_free_agent",
|
||||
ResourceID: fmt.Sprintf("%d-%d-%d", seasonID, leagueID, playerID),
|
||||
Details: map[string]any{
|
||||
"season_id": seasonID,
|
||||
"league_id": leagueID,
|
||||
"player_id": playerID,
|
||||
},
|
||||
}
|
||||
err = LogSuccess(ctx, tx, audit, info)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "LogSuccess")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetFreeAgentsForSeasonLeague returns all players registered as free agents in a season_league.
|
||||
func GetFreeAgentsForSeasonLeague(
|
||||
ctx context.Context,
|
||||
tx bun.Tx,
|
||||
seasonID, leagueID int,
|
||||
) ([]*SeasonLeagueFreeAgent, error) {
|
||||
entries := []*SeasonLeagueFreeAgent{}
|
||||
err := tx.NewSelect().
|
||||
Model(&entries).
|
||||
Where("slfa.season_id = ?", seasonID).
|
||||
Where("slfa.league_id = ?", leagueID).
|
||||
Relation("Player", func(q *bun.SelectQuery) *bun.SelectQuery {
|
||||
return q.Relation("User")
|
||||
}).
|
||||
Relation("RegisteredBy").
|
||||
Order("slfa.registered_at ASC").
|
||||
Scan(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "tx.NewSelect")
|
||||
}
|
||||
return entries, nil
|
||||
}
|
||||
|
||||
// IsFreeAgentRegistered checks if a player is registered as a free agent in a season_league.
|
||||
func IsFreeAgentRegistered(
|
||||
ctx context.Context,
|
||||
tx bun.Tx,
|
||||
seasonID, leagueID, playerID int,
|
||||
) (bool, error) {
|
||||
count, err := tx.NewSelect().
|
||||
Model((*SeasonLeagueFreeAgent)(nil)).
|
||||
Where("season_id = ?", seasonID).
|
||||
Where("league_id = ?", leagueID).
|
||||
Where("player_id = ?", playerID).
|
||||
Count(ctx)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "tx.NewSelect")
|
||||
}
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
// NominateFreeAgent nominates a free agent for a specific fixture on behalf of a team.
|
||||
func NominateFreeAgent(
|
||||
ctx context.Context,
|
||||
tx bun.Tx,
|
||||
fixtureID, playerID, teamID int,
|
||||
audit *AuditMeta,
|
||||
) error {
|
||||
user := CurrentUser(ctx)
|
||||
if user == nil {
|
||||
return errors.New("user cannot be nil")
|
||||
}
|
||||
|
||||
// Check if already nominated by another team
|
||||
existing := new(FixtureFreeAgent)
|
||||
err := tx.NewSelect().
|
||||
Model(existing).
|
||||
Where("ffa.fixture_id = ?", fixtureID).
|
||||
Where("ffa.player_id = ?", playerID).
|
||||
Scan(ctx)
|
||||
if err == nil {
|
||||
// Found existing nomination
|
||||
if existing.TeamID != teamID {
|
||||
return BadRequest("Player already nominated for this fixture by another team")
|
||||
}
|
||||
return BadRequest("Player already nominated for this fixture")
|
||||
}
|
||||
if err.Error() != "sql: no rows in result set" {
|
||||
return errors.Wrap(err, "tx.NewSelect")
|
||||
}
|
||||
|
||||
// Check max 2 free agents per team per fixture
|
||||
count, err := tx.NewSelect().
|
||||
Model((*FixtureFreeAgent)(nil)).
|
||||
Where("fixture_id = ?", fixtureID).
|
||||
Where("team_id = ?", teamID).
|
||||
Count(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "tx.NewSelect count")
|
||||
}
|
||||
if count >= 2 {
|
||||
return BadRequest("Maximum of 2 free agents per team per fixture")
|
||||
}
|
||||
|
||||
entry := &FixtureFreeAgent{
|
||||
FixtureID: fixtureID,
|
||||
PlayerID: playerID,
|
||||
TeamID: teamID,
|
||||
NominatedByUserID: user.ID,
|
||||
NominatedAt: time.Now().Unix(),
|
||||
}
|
||||
|
||||
info := &AuditInfo{
|
||||
Action: "free_agents.nominate",
|
||||
ResourceType: "fixture_free_agent",
|
||||
ResourceID: fmt.Sprintf("%d-%d", fixtureID, playerID),
|
||||
Details: map[string]any{
|
||||
"fixture_id": fixtureID,
|
||||
"player_id": playerID,
|
||||
"team_id": teamID,
|
||||
},
|
||||
}
|
||||
|
||||
err = Insert(tx, entry).WithAudit(audit, info).Exec(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Insert")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetNominatedFreeAgents returns all free agents nominated for a fixture.
|
||||
func GetNominatedFreeAgents(
|
||||
ctx context.Context,
|
||||
tx bun.Tx,
|
||||
fixtureID int,
|
||||
) ([]*FixtureFreeAgent, error) {
|
||||
entries := []*FixtureFreeAgent{}
|
||||
err := tx.NewSelect().
|
||||
Model(&entries).
|
||||
Where("ffa.fixture_id = ?", fixtureID).
|
||||
Relation("Player", func(q *bun.SelectQuery) *bun.SelectQuery {
|
||||
return q.Relation("User")
|
||||
}).
|
||||
Relation("Team").
|
||||
Relation("NominatedBy").
|
||||
Order("ffa.nominated_at ASC").
|
||||
Scan(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "tx.NewSelect")
|
||||
}
|
||||
return entries, nil
|
||||
}
|
||||
|
||||
// GetNominatedFreeAgentsByTeam returns free agents nominated by a specific team for a fixture.
|
||||
func GetNominatedFreeAgentsByTeam(
|
||||
ctx context.Context,
|
||||
tx bun.Tx,
|
||||
fixtureID, teamID int,
|
||||
) ([]*FixtureFreeAgent, error) {
|
||||
entries := []*FixtureFreeAgent{}
|
||||
err := tx.NewSelect().
|
||||
Model(&entries).
|
||||
Where("ffa.fixture_id = ?", fixtureID).
|
||||
Where("ffa.team_id = ?", teamID).
|
||||
Relation("Player", func(q *bun.SelectQuery) *bun.SelectQuery {
|
||||
return q.Relation("User")
|
||||
}).
|
||||
Order("ffa.nominated_at ASC").
|
||||
Scan(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "tx.NewSelect")
|
||||
}
|
||||
return entries, nil
|
||||
}
|
||||
|
||||
// RemoveAllFreeAgentNominationsForPlayer deletes all nominations for a player.
|
||||
// Used for cascade deletion on team join and unregister.
|
||||
func RemoveAllFreeAgentNominationsForPlayer(
|
||||
ctx context.Context,
|
||||
tx bun.Tx,
|
||||
playerID int,
|
||||
) error {
|
||||
_, err := tx.NewDelete().
|
||||
Model((*FixtureFreeAgent)(nil)).
|
||||
Where("player_id = ?", playerID).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "tx.NewDelete")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveFreeAgentNomination removes a specific nomination.
|
||||
func RemoveFreeAgentNomination(
|
||||
ctx context.Context,
|
||||
tx bun.Tx,
|
||||
fixtureID, playerID int,
|
||||
audit *AuditMeta,
|
||||
) error {
|
||||
_, err := tx.NewDelete().
|
||||
Model((*FixtureFreeAgent)(nil)).
|
||||
Where("fixture_id = ?", fixtureID).
|
||||
Where("player_id = ?", playerID).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "tx.NewDelete")
|
||||
}
|
||||
|
||||
info := &AuditInfo{
|
||||
Action: "free_agents.remove_nomination",
|
||||
ResourceType: "fixture_free_agent",
|
||||
ResourceID: fmt.Sprintf("%d-%d", fixtureID, playerID),
|
||||
Details: map[string]any{
|
||||
"fixture_id": fixtureID,
|
||||
"player_id": playerID,
|
||||
},
|
||||
}
|
||||
err = LogSuccess(ctx, tx, audit, info)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "LogSuccess")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveFreeAgentRegistrationForPlayer removes all free agent registrations for a player.
|
||||
// Used on team join.
|
||||
func RemoveFreeAgentRegistrationForPlayer(
|
||||
ctx context.Context,
|
||||
tx bun.Tx,
|
||||
playerID int,
|
||||
) error {
|
||||
_, err := tx.NewDelete().
|
||||
Model((*SeasonLeagueFreeAgent)(nil)).
|
||||
Where("player_id = ?", playerID).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "tx.NewDelete")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user