package catalog import ( "rmser/internal/domain/catalog" "github.com/google/uuid" "gorm.io/gorm" "gorm.io/gorm/clause" ) type pgRepository struct { db *gorm.DB } func NewRepository(db *gorm.DB) catalog.Repository { return &pgRepository{db: db} } // --- Запись (Save) --- // При сохранении мы предполагаем, что serverID уже проставлен в Entity в слое Service. // Но для надежности можно передавать serverID в метод Save, однако Service должен это контролировать. // Оставим контракт Save(products []Product), где внутри products уже заполнен RMSServerID. func (r *pgRepository) SaveMeasureUnits(units []catalog.MeasureUnit) error { if len(units) == 0 { return nil } return r.db.Clauses(clause.OnConflict{ Columns: []clause.Column{{Name: "id"}}, // ID глобально уникален (UUID), конфликтов между серверами не будет UpdateAll: true, }).CreateInBatches(units, 100).Error } func (r *pgRepository) SaveProducts(products []catalog.Product) error { sorted := sortProductsByHierarchy(products) return r.db.Transaction(func(tx *gorm.DB) error { // 1. Продукты if err := tx.Omit("Containers").Clauses(clause.OnConflict{ Columns: []clause.Column{{Name: "id"}}, UpdateAll: true, }).CreateInBatches(sorted, 100).Error; err != nil { return err } // 2. Контейнеры var allContainers []catalog.ProductContainer for _, p := range products { allContainers = append(allContainers, p.Containers...) } if len(allContainers) > 0 { if err := tx.Clauses(clause.OnConflict{ Columns: []clause.Column{{Name: "id"}}, UpdateAll: true, }).CreateInBatches(allContainers, 100).Error; err != nil { return err } } return nil }) } func (r *pgRepository) SaveContainer(container catalog.ProductContainer) error { return r.db.Clauses(clause.OnConflict{ Columns: []clause.Column{{Name: "id"}}, UpdateAll: true, }).Create(&container).Error } func (r *pgRepository) SaveStores(stores []catalog.Store) error { if len(stores) == 0 { return nil } return r.db.Clauses(clause.OnConflict{ Columns: []clause.Column{{Name: "id"}}, UpdateAll: true, }).CreateInBatches(stores, 100).Error } // --- Чтение (Read) с фильтрацией по ServerID --- func (r *pgRepository) GetAll() ([]catalog.Product, error) { // Этот метод был legacy и грузил всё. Теперь он опасен без serverID. // Оставляем заглушку или удаляем. Лучше удалить из интерфейса, но пока вернем пустой список // чтобы не ломать сборку, пока не почистим вызовы. return nil, nil } func (r *pgRepository) GetActiveGoods(serverID uuid.UUID) ([]catalog.Product, error) { var products []catalog.Product err := r.db. Preload("MainUnit"). Preload("Containers"). Where("rms_server_id = ? AND is_deleted = ? AND type IN ?", serverID, false, []string{"GOODS"}). Order("name ASC"). Find(&products).Error return products, err } func (r *pgRepository) GetActiveStores(serverID uuid.UUID) ([]catalog.Store, error) { var stores []catalog.Store err := r.db.Where("rms_server_id = ? AND is_deleted = ?", serverID, false).Order("name ASC").Find(&stores).Error return stores, err } func (r *pgRepository) Search(serverID uuid.UUID, query string) ([]catalog.Product, error) { var products []catalog.Product q := "%" + query + "%" err := r.db. Preload("MainUnit"). Preload("Containers"). Where("rms_server_id = ? AND is_deleted = ? AND type = ?", serverID, false, "GOODS"). Where("name ILIKE ? OR code ILIKE ? OR num ILIKE ?", q, q, q). Order("name ASC"). Limit(20). Find(&products).Error return products, err } // sortProductsByHierarchy - вспомогательная функция, оставляем как есть (копипаст из старого файла) func sortProductsByHierarchy(products []catalog.Product) []catalog.Product { if len(products) == 0 { return products } childrenMap := make(map[uuid.UUID][]catalog.Product) var roots []catalog.Product allIDs := make(map[uuid.UUID]struct{}, len(products)) for _, p := range products { allIDs[p.ID] = struct{}{} } for _, p := range products { if p.ParentID == nil { roots = append(roots, p) } else { if _, exists := allIDs[*p.ParentID]; exists { childrenMap[*p.ParentID] = append(childrenMap[*p.ParentID], p) } else { roots = append(roots, p) } } } result := make([]catalog.Product, 0, len(products)) queue := roots for len(queue) > 0 { current := queue[0] queue = queue[1:] result = append(result, current) if children, ok := childrenMap[current.ID]; ok { queue = append(queue, children...) delete(childrenMap, current.ID) } } for _, remaining := range childrenMap { result = append(result, remaining...) } return result } func (r *pgRepository) CountGoods(serverID uuid.UUID) (int64, error) { var count int64 err := r.db.Model(&catalog.Product{}). Where("rms_server_id = ? AND type IN ? AND is_deleted = ?", serverID, []string{"GOODS"}, false). Count(&count).Error return count, err } func (r *pgRepository) CountStores(serverID uuid.UUID) (int64, error) { var count int64 err := r.db.Model(&catalog.Store{}). Where("rms_server_id = ? AND is_deleted = ?", serverID, false). Count(&count).Error return count, err }