221 lines
5.8 KiB
Go
221 lines
5.8 KiB
Go
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 (
|
|
tribeMetaColumns = []string{"id", "name", "tag", "profile_url"}
|
|
)
|
|
|
|
type Tribe struct {
|
|
db *bun.DB
|
|
}
|
|
|
|
func NewTribe(db *bun.DB) *Tribe {
|
|
return &Tribe{db: db}
|
|
}
|
|
|
|
func (t *Tribe) CreateOrUpdate(ctx context.Context, params ...domain.CreateTribeParams) error {
|
|
if len(params) == 0 {
|
|
return nil
|
|
}
|
|
|
|
tribes := make([]model.Tribe, 0, len(params))
|
|
for _, p := range params {
|
|
tribes = append(tribes, model.NewTribe(p))
|
|
}
|
|
if _, err := t.db.NewInsert().
|
|
Model(&tribes).
|
|
On("CONFLICT ON CONSTRAINT tribes_pkey DO UPDATE").
|
|
Set("name = EXCLUDED.name").
|
|
Set("tag = EXCLUDED.tag").
|
|
Set("num_members = EXCLUDED.num_members").
|
|
Set("num_villages = EXCLUDED.num_villages").
|
|
Set("points = EXCLUDED.points").
|
|
Set("all_points = EXCLUDED.all_points").
|
|
Set("rank = EXCLUDED.rank").
|
|
Set("profile_url = EXCLUDED.profile_url").
|
|
Set("best_rank = EXCLUDED.best_rank").
|
|
Set("best_rank_at = EXCLUDED.best_rank_at").
|
|
Set("most_villages = EXCLUDED.most_villages").
|
|
Set("most_villages_at = EXCLUDED.most_villages_at").
|
|
Set("most_points = EXCLUDED.most_points").
|
|
Set("most_points_at = EXCLUDED.most_points_at").
|
|
Set("deleted_at = null").
|
|
Apply(appendODSetClauses).
|
|
Returning("NULL").
|
|
Exec(ctx); err != nil {
|
|
return fmt.Errorf("something went wrong while inserting tribes into the db: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (t *Tribe) Delete(ctx context.Context, serverKey string, ids ...int64) error {
|
|
if len(ids) == 0 {
|
|
return nil
|
|
}
|
|
|
|
if _, err := t.db.NewUpdate().
|
|
Model(&model.Tribe{}).
|
|
Where("deleted_at IS NULL").
|
|
Where("id IN (?)", bun.In(ids)).
|
|
Where("server_key = ?", serverKey).
|
|
Set("deleted_at = ?", time.Now()).
|
|
Returning("NULL").
|
|
Exec(ctx); err != nil {
|
|
return fmt.Errorf("couldn't delete tribes: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (t *Tribe) UpdateDominance(ctx context.Context, serverKey string, numPlayerVillages int64) error {
|
|
q := t.db.NewUpdate().
|
|
Model(&model.Tribe{}).
|
|
Returning("NULL").
|
|
Where("server_key = ?", serverKey).
|
|
Where("deleted_at IS NULL")
|
|
if numPlayerVillages > 0 {
|
|
q = q.Set("dominance = num_villages::double precision / ? * 100", numPlayerVillages)
|
|
} else {
|
|
q = q.Set("dominance = 0")
|
|
}
|
|
if _, err := q.Exec(ctx); err != nil {
|
|
return fmt.Errorf("couldn't update dominance (server=%s): %w", serverKey, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (t *Tribe) List(ctx context.Context, params domain.ListTribesParams) ([]domain.Tribe, error) {
|
|
var tribes []model.Tribe
|
|
|
|
if err := t.buildListQuery(&tribes, params).Scan(ctx); err != nil && !errors.Is(err, sql.ErrNoRows) {
|
|
return nil, fmt.Errorf("couldn't select tribes from the db: %w", err)
|
|
}
|
|
|
|
result := make([]domain.Tribe, 0, len(tribes))
|
|
for _, tribe := range tribes {
|
|
result = append(result, tribe.ToDomain())
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func (t *Tribe) ListCount(ctx context.Context, params domain.ListTribesParams) ([]domain.Tribe, int64, error) {
|
|
var tribes []model.Tribe
|
|
|
|
q := t.buildListQuery(&tribes, params)
|
|
count, err := scanAndCount(ctx, q, q)
|
|
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
|
return nil, 0, fmt.Errorf("couldn't select tribes from the db: %w", err)
|
|
}
|
|
|
|
result := make([]domain.Tribe, 0, len(tribes))
|
|
for _, tribe := range tribes {
|
|
result = append(result, tribe.ToDomain())
|
|
}
|
|
|
|
return result, int64(count), nil
|
|
}
|
|
|
|
func (t *Tribe) buildListQuery(tribes *[]model.Tribe, params domain.ListTribesParams) *bun.SelectQuery {
|
|
return t.db.NewSelect().
|
|
Model(tribes).
|
|
Order("tribe.server_key ASC").
|
|
Apply(listTribesParamsApplier{params}.apply)
|
|
}
|
|
|
|
type listTribesParamsApplier struct {
|
|
params domain.ListTribesParams
|
|
}
|
|
|
|
func (l listTribesParamsApplier) apply(q *bun.SelectQuery) *bun.SelectQuery {
|
|
return q.Apply(l.applySort).Apply(l.applyFilters).Apply(l.applyPagination)
|
|
}
|
|
|
|
func (l listTribesParamsApplier) applyFilters(q *bun.SelectQuery) *bun.SelectQuery {
|
|
if len(l.params.IDs) > 0 {
|
|
q = q.Where("tribe.id IN (?)", bun.In(l.params.IDs))
|
|
}
|
|
|
|
if l.params.IDGT.Valid {
|
|
q = q.Where("tribe.id > ?", l.params.IDGT.Int64)
|
|
}
|
|
|
|
if len(l.params.Tags) > 0 {
|
|
q = q.Where("tribe.tag IN (?)", bun.In(l.params.Tags))
|
|
}
|
|
|
|
if len(l.params.ServerKeys) > 0 {
|
|
q = q.Where("tribe.server_key IN (?)", bun.In(l.params.ServerKeys))
|
|
}
|
|
|
|
if l.params.Deleted.Valid {
|
|
if l.params.Deleted.Bool {
|
|
q = q.Where("tribe.deleted_at IS NOT NULL")
|
|
} else {
|
|
q = q.Where("tribe.deleted_at IS NULL")
|
|
}
|
|
}
|
|
|
|
return q
|
|
}
|
|
|
|
func (l listTribesParamsApplier) 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 := tribeSortByToColumn(s.By)
|
|
if err != nil {
|
|
return q.Err(fmt.Errorf("tribeSortByToColumn (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 listTribesParamsApplier) applyPagination(q *bun.SelectQuery) *bun.SelectQuery {
|
|
return (paginationApplier{pagination: l.params.Pagination}).apply(q)
|
|
}
|
|
|
|
func tribeSortByToColumn(sortBy domain.TribeSortBy) (string, error) {
|
|
switch sortBy {
|
|
case domain.TribeSortByID:
|
|
return "tribe.id", nil
|
|
case domain.TribeSortByScoreAtt:
|
|
return "tribe.score_att", nil
|
|
case domain.TribeSortByScoreDef:
|
|
return "tribe.score_def", nil
|
|
case domain.TribeSortByScoreTotal:
|
|
return "tribe.score_total", nil
|
|
case domain.TribeSortByPoints:
|
|
return "tribe.points", nil
|
|
case domain.TribeSortByDominance:
|
|
return "tribe.dominance", nil
|
|
case domain.TribeSortByDeletedAt:
|
|
return "tribe.deleted_at", nil
|
|
}
|
|
return "", fmt.Errorf("%w: %d", domain.ErrUnsupportedSortBy, sortBy)
|
|
}
|