package bundb import ( "context" "database/sql" "errors" "fmt" "time" "gitea.dwysokinski.me/twhelp/core/internal/bundb/internal/model" "gitea.dwysokinski.me/twhelp/core/internal/domain" "github.com/uptrace/bun" ) var ( ennoblementOrders = []string{"ennoblement.server_key ASC"} ) type Ennoblement struct { db *bun.DB } func NewEnnoblement(db *bun.DB) *Ennoblement { return &Ennoblement{db: db} } func (e *Ennoblement) Create(ctx context.Context, params ...domain.CreateEnnoblementParams) error { if len(params) == 0 { return nil } ennoblements := make([]model.Ennoblement, 0, len(params)) for _, p := range params { ennoblements = append(ennoblements, model.NewEnnoblement(p)) } if _, err := e.db.NewInsert(). Model(&ennoblements). Returning("NULL"). Ignore(). Exec(ctx); err != nil { return fmt.Errorf("something went wrong while inserting ennoblements into the db: %w", err) } return nil } func (e *Ennoblement) List(ctx context.Context, params domain.ListEnnoblementsParams) ([]domain.Ennoblement, error) { var ennoblements []model.Ennoblement if err := e.db.NewSelect(). Model(&ennoblements). Order(ennoblementOrders...). Apply(listEnnoblementsParamsApplier{params}.apply). Scan(ctx); err != nil && !errors.Is(err, sql.ErrNoRows) { return nil, fmt.Errorf("couldn't select ennoblements from the db: %w", err) } result := make([]domain.Ennoblement, 0, len(ennoblements)) for _, ennoblement := range ennoblements { result = append(result, ennoblement.ToDomain()) } return result, nil } func (e *Ennoblement) ListCountWithRelations( ctx context.Context, params domain.ListEnnoblementsParams, ) ([]domain.EnnoblementWithRelations, int64, error) { var ennoblements []model.Ennoblement paramsApplier := listEnnoblementsParamsApplier{params} cntQ := e.db.NewSelect(). Model(&model.Ennoblement{}). Apply(paramsApplier.applyFilters) subQ := e.db.NewSelect(). Column("id"). Model(&model.Ennoblement{}). Order(ennoblementOrders...). Apply(paramsApplier.apply) q := e.db.NewSelect(). Model(&ennoblements). Where("ennoblement.id IN (?)", subQ). Order(ennoblementOrders...). Apply(paramsApplier.applySort). Relation("OldOwner", func(q *bun.SelectQuery) *bun.SelectQuery { return q.Column(playerMetaColumns...) }). Relation("OldTribe", func(q *bun.SelectQuery) *bun.SelectQuery { return q.Column(tribeMetaColumns...) }). Relation("NewOwner", func(q *bun.SelectQuery) *bun.SelectQuery { return q.Column(playerMetaColumns...) }). Relation("NewTribe", func(q *bun.SelectQuery) *bun.SelectQuery { return q.Column(tribeMetaColumns...) }). Relation("Village", func(q *bun.SelectQuery) *bun.SelectQuery { return q.Column(villageMetaColumns...) }) count, err := scanAndCount(ctx, cntQ, q) if err != nil && !errors.Is(err, sql.ErrNoRows) { return nil, 0, fmt.Errorf("couldn't select ennoblements from the db: %w", err) } result := make([]domain.EnnoblementWithRelations, 0, len(ennoblements)) for _, ennoblement := range ennoblements { result = append(result, ennoblement.ToDomainWithRelations()) } return result, int64(count), nil } func (e *Ennoblement) Delete(ctx context.Context, serverKey string, createdAtLTE time.Time) error { if _, err := e.db.NewDelete(). Model(&model.Ennoblement{}). Where("server_key = ?", serverKey). Where("created_at <= ?", createdAtLTE). Returning("NULL"). Exec(ctx); err != nil { return fmt.Errorf("couldn't delete ennoblements: %w", err) } return nil } type listEnnoblementsParamsApplier struct { params domain.ListEnnoblementsParams } func (l listEnnoblementsParamsApplier) apply(q *bun.SelectQuery) *bun.SelectQuery { return q.Apply(l.applySort).Apply(l.applyFilters).Apply(l.applyPagination) } func (l listEnnoblementsParamsApplier) applyFilters(q *bun.SelectQuery) *bun.SelectQuery { if len(l.params.ServerKeys) > 0 { q = q.Where("ennoblement.server_key IN (?)", bun.In(l.params.ServerKeys)) } if l.params.CreatedAtGTE.Valid { q = q.Where("ennoblement.created_at >= ?", l.params.CreatedAtGTE.Time) } if l.params.CreatedAtLTE.Valid { q = q.Where("ennoblement.created_at <= ?", l.params.CreatedAtLTE.Time) } if l.params.PlayerID > 0 { q = q.WhereGroup(" AND ", func(q *bun.SelectQuery) *bun.SelectQuery { return q.WhereOr("ennoblement.new_owner_id = ?", l.params.PlayerID). WhereOr("ennoblement.old_owner_id = ?", l.params.PlayerID) }) } if l.params.TribeID > 0 { q = q.WhereGroup(" AND ", func(q *bun.SelectQuery) *bun.SelectQuery { return q.WhereOr("ennoblement.new_tribe_id = ?", l.params.TribeID). WhereOr("ennoblement.old_tribe_id = ?", l.params.TribeID) }) } if l.params.VillageID > 0 { q = q.Where("village_id = ?", l.params.VillageID) } return q } func (l listEnnoblementsParamsApplier) applySort(q *bun.SelectQuery) *bun.SelectQuery { if len(l.params.Sort) == 0 { return q } orders := make([]string, 0, len(l.params.Sort)) for i, s := range l.params.Sort { column, err := ennoblementSortByToColumn(s.By) if err != nil { return q.Err(fmt.Errorf("ennoblementSortByToColumn (index=%d): %w", i, err)) } direction, err := sortDirectionToString(s.Direction) if err != nil { return q.Err(fmt.Errorf("sortDirectionToString (index=%d): %w", i, err)) } orders = append(orders, column+" "+direction) } return q.Order(orders...) } func (l listEnnoblementsParamsApplier) applyPagination(q *bun.SelectQuery) *bun.SelectQuery { return (paginationApplier{pagination: l.params.Pagination}).apply(q) } func ennoblementSortByToColumn(sortBy domain.EnnoblementSortBy) (string, error) { switch sortBy { case domain.EnnoblementSortByCreatedAt: return "ennoblement.created_at", nil default: return "", fmt.Errorf("%w: %d", domain.ErrUnsupportedSortBy, sortBy) } }