package app import ( "context" "fmt" "gitea.dwysokinski.me/twhelp/corev3/internal/domain" ) type VillageRepository interface { CreateOrUpdate(ctx context.Context, params ...domain.CreateVillageParams) error List(ctx context.Context, params domain.ListVillagesParams) (domain.Villages, error) Delete(ctx context.Context, serverKey string, ids ...int) error } type VillageService struct { repo VillageRepository twSvc TWService pub VillagePublisher } func NewVillageService(repo VillageRepository, twSvc TWService, pub VillagePublisher) *VillageService { return &VillageService{repo: repo, twSvc: twSvc, pub: pub} } func (svc *VillageService) Sync(ctx context.Context, serverSyncedPayload domain.ServerSyncedEventPayload) error { serverKey := serverSyncedPayload.Key() serverURL := serverSyncedPayload.URL() villages, err := svc.twSvc.GetVillages(ctx, serverURL) if err != nil { return fmt.Errorf("%s: couldn't get villages: %w", serverKey, err) } if err = svc.createOrUpdate(ctx, serverKey, villages); err != nil { return fmt.Errorf("%s: couldn't create/update villages: %w", serverKey, err) } if err = svc.delete(ctx, serverKey, villages); err != nil { return fmt.Errorf("%s: couldn't delete villages: %w", serverKey, err) } villagesSyncedPayload, err := domain.NewVillagesSyncedEventPayloadFromVillages( serverKey, serverURL, serverSyncedPayload.VersionCode(), villages, ) if err != nil { return fmt.Errorf("%s: couldn't construct domain.PlayersSyncedEventPayload: %w", serverKey, err) } if err = svc.pub.EventSynced(ctx, villagesSyncedPayload); err != nil { return fmt.Errorf("%s: %w", serverKey, err) } return nil } const villageCreateOrUpdateChunkSize = domain.VillageListMaxLimit func (svc *VillageService) createOrUpdate(ctx context.Context, serverKey string, villages domain.BaseVillages) error { for i := 0; i < len(villages); i += villageCreateOrUpdateChunkSize { end := i + villageCreateOrUpdateChunkSize if end > len(villages) { end = len(villages) } createParams, err := domain.NewCreateVillageParams(serverKey, villages[i:end]) if err != nil { return err } if err = svc.repo.CreateOrUpdate(ctx, createParams...); err != nil { return err } } return nil } func (svc *VillageService) delete(ctx context.Context, serverKey string, villages domain.BaseVillages) error { listParams := domain.NewListVillagesParams() if err := listParams.SetServerKeys([]string{serverKey}); err != nil { return err } if err := listParams.SetSort([]domain.VillageSort{domain.VillageSortIDASC}); err != nil { return err } if err := listParams.SetLimit(domain.VillageListMaxLimit); err != nil { return err } var toDelete []int for { storedVillages, err := svc.repo.List(ctx, listParams) if err != nil { return err } if len(storedVillages) == 0 { break } toDelete = append(toDelete, storedVillages.Delete(villages)...) if err = listParams.SetIDGT(domain.NullInt{ Value: storedVillages[len(storedVillages)-1].ID(), Valid: true, }); err != nil { return err } } return svc.repo.Delete(ctx, serverKey, toDelete...) }