new file: main_dc/yalarba/api_es/internal/repository/object_repository.go

add object_repository
This commit is contained in:
2025-11-21 16:27:13 +05:00
parent d2f526629e
commit ca44a2e6a5
@@ -0,0 +1,398 @@
package repository
import (
"errors"
"gorm.io/gorm"
"api_es/internal/models"
)
var (
ErrObjectNotFound = errors.New("object not found")
)
type ObjectRepository interface {
// Основные операции
Create(object *models.Object) error
GetByID(id uint) (*models.Object, error)
Update(id uint, updates *models.ObjectUpdateRequest) error
Delete(id uint) error
List(filter *ObjectFilter, pagination *Pagination) ([]models.Object, int64, error)
// Специфичные операции
GetByOwner(ownerID uint, filter *ObjectFilter, pagination *Pagination) ([]models.Object, int64, error)
UpdateStatus(id uint, status models.ObjectStatus) error
IncrementViewCount(id uint) error
UpdateRating(id uint, rating float64, reviewCount int) error
// Работа с изображениями
AddImage(objectID uint, image *models.ObjectImage) error
RemoveImage(objectID uint, imageID uint) error
SetPrimaryImage(objectID uint, imageID uint) error
GetImages(objectID uint) ([]models.ObjectImage, error)
// Работа с удобствами
AddAmenities(objectID uint, amenityIDs []uint) error
RemoveAmenities(objectID uint, amenityIDs []uint) error
GetAmenities(objectID uint) ([]models.Amenity, error)
}
type ObjectFilter struct {
Type []models.ObjectType
City string
Status []models.ObjectStatus
OwnerID uint
MinPrice float64
MaxPrice float64
MinRating float64
AmenityIDs []uint
Search string
}
type Pagination struct {
Page int `form:"page" default:"1"`
PageSize int `form:"page_size" default:"20"`
}
type objectRepository struct {
db *gorm.DB
}
func NewObjectRepository(db *gorm.DB) ObjectRepository {
return &objectRepository{db: db}
}
// Create создает новый объект
func (r *objectRepository) Create(object *models.Object) error {
return r.db.Transaction(func(tx *gorm.DB) error {
// Создаем основной объект
if err := tx.Create(object).Error; err != nil {
return err
}
// Добавляем связи с удобствами, если они есть
if len(object.Amenities) > 0 {
if err := tx.Model(object).Association("Amenities").Append(object.Amenities); err != nil {
return err
}
}
return nil
})
}
// GetByID возвращает объект по ID с связанными данными
func (r *objectRepository) GetByID(id uint) (*models.Object, error) {
var object models.Object
err := r.db.
Preload("Owner", func(db *gorm.DB) *gorm.DB {
return db.Select("id, first_name, last_name, email, phone")
}).
Preload("Images", func(db *gorm.DB) *gorm.DB {
return db.Order("is_primary DESC, order ASC")
}).
Preload("Amenities").
First(&object, id).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrObjectNotFound
}
return nil, err
}
return &object, nil
}
// Update обновляет объект
func (r *objectRepository) Update(id uint, updates *models.ObjectUpdateRequest) error {
return r.db.Transaction(func(tx *gorm.DB) error {
// Обновляем основные поля
updateData := map[string]interface{}{}
if updates.Title != "" {
updateData["title"] = updates.Title
}
if updates.Type != "" {
updateData["type"] = updates.Type
}
if updates.Description != "" {
updateData["description"] = updates.Description
}
if updates.City != "" {
updateData["city"] = updates.City
}
if updates.Address != "" {
updateData["address"] = updates.Address
}
if updates.Latitude != 0 {
updateData["latitude"] = updates.Latitude
}
if updates.Longitude != 0 {
updateData["longitude"] = updates.Longitude
}
if updates.Price != 0 {
updateData["price"] = updates.Price
}
if updates.PricePeriod != "" {
updateData["price_period"] = updates.PricePeriod
}
if updates.Status != "" {
updateData["status"] = updates.Status
}
if len(updateData) > 0 {
if err := tx.Model(&models.Object{}).Where("id = ?", id).Updates(updateData).Error; err != nil {
return err
}
}
// Обновляем удобства, если переданы
if updates.AmenityIDs != nil {
var object models.Object
if err := tx.First(&object, id).Error; err != nil {
return err
}
var amenities []models.Amenity
if err := tx.Where("id IN ?", updates.AmenityIDs).Find(&amenities).Error; err != nil {
return err
}
if err := tx.Model(&object).Association("Amenities").Replace(amenities); err != nil {
return err
}
}
return nil
})
}
// Delete удаляет объект (мягкое удаление)
func (r *objectRepository) Delete(id uint) error {
result := r.db.Delete(&models.Object{}, id)
if result.Error != nil {
return result.Error
}
if result.RowsAffected == 0 {
return ErrObjectNotFound
}
return nil
}
// List возвращает список объектов с фильтрацией и пагинацией
func (r *objectRepository) List(filter *ObjectFilter, pagination *Pagination) ([]models.Object, int64, error) {
var objects []models.Object
var total int64
query := r.db.Model(&models.Object{})
// Применяем фильтры
if filter != nil {
query = r.applyFilters(query, filter)
}
// Считаем общее количество
if err := query.Count(&total).Error; err != nil {
return nil, 0, err
}
// Применяем пагинацию
if pagination != nil {
offset := (pagination.Page - 1) * pagination.PageSize
query = query.Offset(offset).Limit(pagination.PageSize)
}
// Загружаем данные с прелоадами
err := query.
Preload("Images", func(db *gorm.DB) *gorm.DB {
return db.Where("is_primary = ?", true).Limit(1)
}).
Preload("Amenities").
Order("created_at DESC").
Find(&objects).Error
if err != nil {
return nil, 0, err
}
return objects, total, nil
}
// GetByOwner возвращает объекты владельца
func (r *objectRepository) GetByOwner(ownerID uint, filter *ObjectFilter, pagination *Pagination) ([]models.Object, int64, error) {
if filter == nil {
filter = &ObjectFilter{}
}
filter.OwnerID = ownerID
return r.List(filter, pagination)
}
// UpdateStatus обновляет статус объекта
func (r *objectRepository) UpdateStatus(id uint, status models.ObjectStatus) error {
result := r.db.Model(&models.Object{}).Where("id = ?", id).Update("status", status)
if result.Error != nil {
return result.Error
}
if result.RowsAffected == 0 {
return ErrObjectNotFound
}
return nil
}
// IncrementViewCount увеличивает счетчик просмотров
func (r *objectRepository) IncrementViewCount(id uint) error {
return r.db.Model(&models.Object{}).
Where("id = ?", id).
Update("view_count", gorm.Expr("view_count + ?", 1)).
Error
}
// UpdateRating обновляет рейтинг и количество отзывов
func (r *objectRepository) UpdateRating(id uint, rating float64, reviewCount int) error {
return r.db.Model(&models.Object{}).
Where("id = ?", id).
Updates(map[string]interface{}{
"rating": rating,
"review_count": reviewCount,
}).Error
}
// AddImage добавляет изображение к объекту
func (r *objectRepository) AddImage(objectID uint, image *models.ObjectImage) error {
image.ObjectID = objectID
return r.db.Create(image).Error
}
// RemoveImage удаляет изображение объекта
func (r *objectRepository) RemoveImage(objectID uint, imageID uint) error {
result := r.db.Where("object_id = ? AND id = ?", objectID, imageID).Delete(&models.ObjectImage{})
if result.Error != nil {
return result.Error
}
if result.RowsAffected == 0 {
return ErrObjectNotFound
}
return nil
}
// SetPrimaryImage устанавливает основное изображение
func (r *objectRepository) SetPrimaryImage(objectID uint, imageID uint) error {
return r.db.Transaction(func(tx *gorm.DB) error {
// Сбрасываем все is_primary для объекта
if err := tx.Model(&models.ObjectImage{}).
Where("object_id = ?", objectID).
Update("is_primary", false).Error; err != nil {
return err
}
// Устанавливаем новое основное изображение
result := tx.Model(&models.ObjectImage{}).
Where("object_id = ? AND id = ?", objectID, imageID).
Update("is_primary", true)
if result.Error != nil {
return result.Error
}
if result.RowsAffected == 0 {
return ErrObjectNotFound
}
return nil
})
}
// GetImages возвращает изображения объекта
func (r *objectRepository) GetImages(objectID uint) ([]models.ObjectImage, error) {
var images []models.ObjectImage
err := r.db.Where("object_id = ?", objectID).
Order("is_primary DESC, order ASC").
Find(&images).Error
return images, err
}
// AddAmenities добавляет удобства к объекту
func (r *objectRepository) AddAmenities(objectID uint, amenityIDs []uint) error {
return r.db.Transaction(func(tx *gorm.DB) error {
var object models.Object
if err := tx.First(&object, objectID).Error; err != nil {
return err
}
var amenities []models.Amenity
if err := tx.Where("id IN ?", amenityIDs).Find(&amenities).Error; err != nil {
return err
}
return tx.Model(&object).Association("Amenities").Append(amenities)
})
}
// RemoveAmenities удаляет удобства у объекта
func (r *objectRepository) RemoveAmenities(objectID uint, amenityIDs []uint) error {
return r.db.Transaction(func(tx *gorm.DB) error {
var object models.Object
if err := tx.First(&object, objectID).Error; err != nil {
return err
}
var amenities []models.Amenity
if err := tx.Where("id IN ?", amenityIDs).Find(&amenities).Error; err != nil {
return err
}
return tx.Model(&object).Association("Amenities").Delete(amenities)
})
}
// GetAmenities возвращает удобства объекта
func (r *objectRepository) GetAmenities(objectID uint) ([]models.Amenity, error) {
var amenities []models.Amenity
err := r.db.Joins("JOIN object_amenities ON amenities.id = object_amenities.amenity_id").
Where("object_amenities.object_id = ?", objectID).
Find(&amenities).Error
return amenities, err
}
// applyFilters применяет фильтры к запросу
func (r *objectRepository) applyFilters(query *gorm.DB, filter *ObjectFilter) *gorm.DB {
if len(filter.Type) > 0 {
query = query.Where("type IN ?", filter.Type)
}
if filter.City != "" {
query = query.Where("city = ?", filter.City)
}
if len(filter.Status) > 0 {
query = query.Where("status IN ?", filter.Status)
}
if filter.OwnerID != 0 {
query = query.Where("owner_id = ?", filter.OwnerID)
}
if filter.MinPrice > 0 {
query = query.Where("price >= ?", filter.MinPrice)
}
if filter.MaxPrice > 0 {
query = query.Where("price <= ?", filter.MaxPrice)
}
if filter.MinRating > 0 {
query = query.Where("rating >= ?", filter.MinRating)
}
if filter.Search != "" {
search := "%" + filter.Search + "%"
query = query.Where("title ILIKE ? OR description ILIKE ?", search, search)
}
// Фильтр по удобствам
if len(filter.AmenityIDs) > 0 {
query = query.Joins("JOIN object_amenities ON objects.id = object_amenities.object_id").
Where("object_amenities.amenity_id IN ?", filter.AmenityIDs)
}
return query
}