core/internal/app/service_tribe.go

189 lines
4.9 KiB
Go

package app
import (
"context"
"fmt"
"gitea.dwysokinski.me/twhelp/corev3/internal/domain"
)
type TribeRepository interface {
CreateOrUpdate(ctx context.Context, params ...domain.CreateTribeParams) error
UpdateDominance(ctx context.Context, serverKey string, numPlayerVillages int) error
List(ctx context.Context, params domain.ListTribesParams) (domain.ListTribesResult, error)
// Delete marks players with the given serverKey and ids as deleted (sets deleted at to now).
//
// https://en.wiktionary.org/wiki/soft_deletion
Delete(ctx context.Context, serverKey string, ids ...int) error
}
type TribeService struct {
repo TribeRepository
twSvc TWService
pub TribePublisher
}
func NewTribeService(repo TribeRepository, twSvc TWService, pub TribePublisher) *TribeService {
return &TribeService{repo: repo, twSvc: twSvc, pub: pub}
}
func (svc *TribeService) Sync(ctx context.Context, serverSyncedPayload domain.ServerSyncedEventPayload) error {
serverKey := serverSyncedPayload.Key()
serverURL := serverSyncedPayload.URL()
tribes, err := svc.twSvc.GetTribes(ctx, serverURL)
if err != nil {
return fmt.Errorf("%s: couldn't get tribes: %w", serverKey, err)
}
if err = svc.createOrUpdate(ctx, serverKey, tribes); err != nil {
return fmt.Errorf("%s: couldn't create/update tribes: %w", serverKey, err)
}
if err = svc.delete(ctx, serverKey, tribes); err != nil {
return fmt.Errorf("%s: couldn't delete tribes: %w", serverKey, err)
}
payload, err := domain.NewTribesSyncedEventPayload(
serverKey,
serverURL,
serverSyncedPayload.VersionCode(),
len(tribes),
)
if err != nil {
return fmt.Errorf("%s: couldn't construct domain.TribesSyncedEventPayload: %w", serverKey, err)
}
if err = svc.pub.EventSynced(ctx, payload); err != nil {
return fmt.Errorf("%s: %w", serverKey, err)
}
return nil
}
const tribeCreateOrUpdateChunkSize = domain.TribeListMaxLimit
func (svc *TribeService) createOrUpdate(ctx context.Context, serverKey string, tribes domain.BaseTribes) error {
for i := 0; i < len(tribes); i += tribeCreateOrUpdateChunkSize {
end := i + tribeCreateOrUpdateChunkSize
if end > len(tribes) {
end = len(tribes)
}
if err := svc.createOrUpdateChunk(ctx, serverKey, tribes[i:end]); err != nil {
return err
}
}
return nil
}
func (svc *TribeService) createOrUpdateChunk(ctx context.Context, serverKey string, tribes domain.BaseTribes) error {
ids := make([]int, 0, len(tribes))
for _, t := range tribes {
ids = append(ids, t.ID())
}
listParams := domain.NewListTribesParams()
if err := listParams.SetServerKeys([]string{serverKey}); err != nil {
return err
}
if err := listParams.SetIDs(ids); err != nil {
return err
}
if err := listParams.SetSort([]domain.TribeSort{domain.TribeSortIDASC}); err != nil {
return err
}
if err := listParams.SetLimit(domain.TribeListMaxLimit); err != nil {
return err
}
res, err := svc.repo.List(ctx, listParams)
if err != nil {
return err
}
createParams, err := domain.NewCreateTribeParams(serverKey, tribes, res.Tribes())
if err != nil {
return err
}
return svc.repo.CreateOrUpdate(ctx, createParams...)
}
func (svc *TribeService) delete(ctx context.Context, serverKey string, tribes domain.BaseTribes) error {
listParams := domain.NewListTribesParams()
if err := listParams.SetServerKeys([]string{serverKey}); err != nil {
return err
}
if err := listParams.SetDeleted(domain.NullBool{
Value: false,
Valid: true,
}); err != nil {
return err
}
if err := listParams.SetSort([]domain.TribeSort{domain.TribeSortIDASC}); err != nil {
return err
}
if err := listParams.SetLimit(domain.TribeListMaxLimit); err != nil {
return err
}
var toDelete []int
for {
res, err := svc.repo.List(ctx, listParams)
if err != nil {
return err
}
toDelete = append(toDelete, res.Tribes().Delete(serverKey, tribes)...)
if res.Next().IsZero() {
break
}
if err = listParams.SetCursor(res.Next()); err != nil {
return err
}
}
return svc.repo.Delete(ctx, serverKey, toDelete...)
}
func (svc *TribeService) UpdateDominance(ctx context.Context, payload domain.VillagesSyncedEventPayload) error {
return svc.repo.UpdateDominance(ctx, payload.ServerKey(), payload.NumPlayerVillages())
}
func (svc *TribeService) List(ctx context.Context, params domain.ListTribesParams) (domain.ListTribesResult, error) {
return svc.repo.List(ctx, params)
}
func (svc *TribeService) Get(
ctx context.Context,
id int,
serverKey string,
) (domain.Tribe, error) {
params := domain.NewListTribesParams()
if err := params.SetIDs([]int{id}); err != nil {
return domain.Tribe{}, err
}
if err := params.SetServerKeys([]string{serverKey}); err != nil {
return domain.Tribe{}, err
}
res, err := svc.repo.List(ctx, params)
if err != nil {
return domain.Tribe{}, err
}
tribes := res.Tribes()
if len(tribes) == 0 {
return domain.Tribe{}, domain.TribeNotFoundError{
ID: id,
ServerKey: serverKey,
}
}
return tribes[0], nil
}