package db import ( "context" "fmt" "github.com/pkg/errors" "github.com/uptrace/bun" ) type TeamRoster struct { bun.BaseModel `bun:"table:team_rosters,alias:tr"` TeamID int `bun:",pk,notnull" json:"team_id"` SeasonID int `bun:",pk,notnull,unique:player" json:"season_id"` LeagueID int `bun:",pk,notnull,unique:player" json:"league_id"` PlayerID int `bun:",pk,notnull,unique:player" json:"player_id"` IsManager bool `bun:"is_manager,default:'false'" json:"is_manager"` Team *Team `bun:"rel:belongs-to,join:team_id=id" json:"-"` Player *Player `bun:"rel:belongs-to,join:player_id=id" json:"-"` Season *Season `bun:"rel:belongs-to,join:season_id=id" json:"-"` League *League `bun:"rel:belongs-to,join:league_id=id" json:"-"` } type TeamWithRoster struct { Team *Team Season *Season League *League Manager *Player Players []*Player } func GetTeamRoster(ctx context.Context, tx bun.Tx, seasonShortName, leagueShortName string, teamID int) (*TeamWithRoster, error) { tr := []*TeamRoster{} err := tx.NewSelect(). Model(&tr). Relation("Team", func(q *bun.SelectQuery) *bun.SelectQuery { return q.Where("team.id = ?", teamID) }). Relation("Season", func(q *bun.SelectQuery) *bun.SelectQuery { return q.Where("season.short_name = ?", seasonShortName) }). Relation("League", func(q *bun.SelectQuery) *bun.SelectQuery { return q.Where("league.short_name = ?", leagueShortName) }). Relation("Player").Scan(ctx) if err != nil { return nil, errors.Wrap(err, "tx.NewSelect") } team, err := GetTeam(ctx, tx, teamID) if err != nil { return nil, errors.Wrap(err, "GetTeam") } sl, err := GetSeasonLeague(ctx, tx, seasonShortName, leagueShortName) if err != nil { return nil, errors.Wrap(err, "GetSeasonLeague") } var manager *Player players := []*Player{} for _, tp := range tr { if tp.IsManager { manager = tp.Player } else { players = append(players, tp.Player) } } players = append([]*Player{manager}, players...) twr := &TeamWithRoster{ team, sl.Season, sl.League, manager, players, } return twr, nil } // GetManagersByTeam returns a map of teamID -> manager Player for all teams in a season/league func GetManagersByTeam(ctx context.Context, tx bun.Tx, seasonID, leagueID int) (map[int]*Player, error) { rosters := []*TeamRoster{} err := tx.NewSelect(). Model(&rosters). Where("tr.season_id = ?", seasonID). Where("tr.league_id = ?", leagueID). Where("tr.is_manager = true"). Relation("Player"). Scan(ctx) if err != nil { return nil, errors.Wrap(err, "tx.NewSelect") } result := make(map[int]*Player, len(rosters)) for _, r := range rosters { result[r.TeamID] = r.Player } return result, nil } func AddPlayerToTeam(ctx context.Context, tx bun.Tx, seasonID, leagueID, teamID, playerID int, manager bool, audit *AuditMeta) error { season, err := GetByID[Season](tx, seasonID).Get(ctx) if err != nil { return errors.Wrap(err, "GetSeason") } league, err := GetByID[League](tx, leagueID).Get(ctx) if err != nil { return errors.Wrap(err, "GetLeague") } team, err := GetByID[Team](tx, teamID).Get(ctx) if err != nil { return errors.Wrap(err, "GetTeam") } player, err := GetByID[Player](tx, playerID).Get(ctx) if err != nil { return errors.Wrap(err, "GetPlayer") } tr := &TeamRoster{ SeasonID: season.ID, LeagueID: league.ID, TeamID: team.ID, PlayerID: player.ID, IsManager: manager, } err = Insert(tx, tr).WithAudit(audit, nil).Exec(ctx) if err != nil { return errors.Wrap(err, "Insert") } return nil } // ManageTeamRoster replaces the entire roster for a team in a season/league. // It deletes all existing roster entries and inserts the new ones. func ManageTeamRoster(ctx context.Context, tx bun.Tx, seasonID, leagueID, teamID, managerID int, playerIDs []int, audit *AuditMeta) error { // Delete all existing roster entries for this team/season/league _, err := tx.NewDelete(). Model((*TeamRoster)(nil)). Where("season_id = ?", seasonID). Where("league_id = ?", leagueID). Where("team_id = ?", teamID). Exec(ctx) if err != nil { return errors.Wrap(err, "delete existing roster") } // Insert manager if provided if managerID > 0 { tr := &TeamRoster{ SeasonID: seasonID, LeagueID: leagueID, TeamID: teamID, PlayerID: managerID, IsManager: true, } err = Insert(tx, tr).Exec(ctx) if err != nil { return errors.Wrap(err, "Insert manager") } } // Insert players for _, playerID := range playerIDs { if playerID == managerID { continue // Already inserted as manager } tr := &TeamRoster{ SeasonID: seasonID, LeagueID: leagueID, TeamID: teamID, PlayerID: playerID, IsManager: false, } err = Insert(tx, tr).Exec(ctx) if err != nil { return errors.Wrap(err, "Insert player") } } // Log the roster change details := map[string]any{ "season_id": seasonID, "league_id": leagueID, "team_id": teamID, "manager_id": managerID, "player_ids": playerIDs, } info := &AuditInfo{ "teams.manage_players", "team_roster", fmt.Sprintf("%d-%d-%d", seasonID, leagueID, teamID), details, } err = LogSuccess(ctx, tx, audit, info) if err != nil { return errors.Wrap(err, "LogSuccess") } return nil }