package services import ( "context" "errors" "fmt" "gogs.fxtmmsk.ru/foxtime/photoplaces/backend/internal/models" "gogs.fxtmmsk.ru/foxtime/photoplaces/backend/internal/repository" ) var ( ErrNotYourPlace = errors.New("not your place") ErrPlaceNotFound = errors.New("place not found") ) type PlaceRepo interface { Create(ctx context.Context, place *models.Place) error GetByID(ctx context.Context, id string) (*models.Place, error) List(ctx context.Context, filter models.PlaceFilter) ([]*models.Place, error) Update(ctx context.Context, place *models.Place) error UpdateStatus(ctx context.Context, id, status, comment string) error SoftDelete(ctx context.Context, id string) error SetTags(ctx context.Context, placeID string, tags []models.Tag) error SetFeatures(ctx context.Context, placeID string, features []models.Feature) error GetTags(ctx context.Context, placeID string) ([]models.Tag, error) GetFeatures(ctx context.Context, placeID string) ([]models.Feature, error) } type PlaceService struct { placeRepo PlaceRepo } func NewPlaceService(placeRepo PlaceRepo) *PlaceService { return &PlaceService{placeRepo: placeRepo} } type CreatePlaceInput struct { OwnerID string Type string Title string Description *string Address *string Lat float64 Lng float64 CoverImage *string AccessInfo *string HourlyRate *int Currency string MinHours int Tags []models.Tag Features []models.Feature } func (s *PlaceService) Create(ctx context.Context, input CreatePlaceInput) (*models.Place, error) { place := &models.Place{ Type: input.Type, OwnerID: input.OwnerID, Title: input.Title, Description: input.Description, Address: input.Address, Lat: input.Lat, Lng: input.Lng, CoverImage: input.CoverImage, AccessInfo: input.AccessInfo, Status: "pending_moderation", HourlyRate: input.HourlyRate, Currency: input.Currency, MinHours: input.MinHours, Tags: input.Tags, Features: input.Features, } if err := s.placeRepo.Create(ctx, place); err != nil { return nil, fmt.Errorf("create place: %w", err) } return place, nil } func (s *PlaceService) GetByID(ctx context.Context, id string, fetchTags bool) (*models.Place, error) { place, err := s.placeRepo.GetByID(ctx, id) if err != nil { return nil, err } if place == nil { return nil, nil } if fetchTags { tags, err := s.placeRepo.GetTags(ctx, id) if err != nil { return nil, err } place.Tags = tags features, err := s.placeRepo.GetFeatures(ctx, id) if err != nil { return nil, err } place.Features = features } return place, nil } func (s *PlaceService) List(ctx context.Context, filter models.PlaceFilter) ([]*models.Place, error) { return s.placeRepo.List(ctx, filter) } type UpdatePlaceInput struct { ID string OwnerID string Title *string Description *string Address *string Lat *float64 Lng *float64 CoverImage *string AccessInfo *string HourlyRate *int Currency *string MinHours *int } func (s *PlaceService) Update(ctx context.Context, input UpdatePlaceInput, isModerator bool) (*models.Place, error) { place, err := s.placeRepo.GetByID(ctx, input.ID) if err != nil { return nil, err } if place == nil { return nil, nil } if !isModerator && place.OwnerID != input.OwnerID { return nil, fmt.Errorf("%w: user %s tried to update place %s", ErrNotYourPlace, input.OwnerID, input.ID) } if input.Title != nil { place.Title = *input.Title } if input.Description != nil { place.Description = input.Description } if input.Address != nil { place.Address = input.Address } if input.Lat != nil { place.Lat = *input.Lat } if input.Lng != nil { place.Lng = *input.Lng } if input.CoverImage != nil { place.CoverImage = input.CoverImage } if input.AccessInfo != nil { place.AccessInfo = input.AccessInfo } if input.HourlyRate != nil { place.HourlyRate = input.HourlyRate } if input.Currency != nil { place.Currency = *input.Currency } if input.MinHours != nil { place.MinHours = *input.MinHours } if !isModerator { place.Status = "pending_moderation" } if err := s.placeRepo.Update(ctx, place); err != nil { return nil, err } return place, nil } func (s *PlaceService) Moderate(ctx context.Context, id, action, comment, moderatorID string) error { var status string switch action { case "approve": status = "published" case "reject": status = "rejected" default: return fmt.Errorf("unknown action: %s", action) } return s.placeRepo.UpdateStatus(ctx, id, status, comment) } func (s *PlaceService) Delete(ctx context.Context, id string) error { return s.placeRepo.SoftDelete(ctx, id) }