Files
rmser/internal/infrastructure/repository/catalog/postgres.go
SERTY 4e4571b3db Настройки работают
Иерархия групп работает
Полностью завязано на пользователя и серверы
2025-12-18 07:21:31 +03:00

207 lines
6.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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, rootGroupID *uuid.UUID) ([]catalog.Product, error) {
var products []catalog.Product
db := r.db.Preload("MainUnit").Preload("Containers").
Where("rms_server_id = ? AND is_deleted = ? AND type IN ?", serverID, false, []string{"GOODS"})
if rootGroupID != nil && *rootGroupID != uuid.Nil {
// Используем Recursive CTE для поиска всех дочерних элементов папки
subQuery := r.db.Raw(`
WITH RECURSIVE subnodes AS (
SELECT id FROM products WHERE id = ?
UNION ALL
SELECT p.id FROM products p INNER JOIN subnodes s ON p.parent_id = s.id
)
SELECT id FROM subnodes
`, rootGroupID)
db = db.Where("id IN (?)", subQuery)
}
err := db.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, rootGroupID *uuid.UUID) ([]catalog.Product, error) {
var products []catalog.Product
q := "%" + query + "%"
db := 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)
if rootGroupID != nil && *rootGroupID != uuid.Nil {
subQuery := r.db.Raw(`
WITH RECURSIVE subnodes AS (
SELECT id FROM products WHERE id = ?
UNION ALL
SELECT p.id FROM products p INNER JOIN subnodes s ON p.parent_id = s.id
)
SELECT id FROM subnodes
`, rootGroupID)
db = db.Where("id IN (?)", subQuery)
}
err := db.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
}
func (r *pgRepository) GetGroups(serverID uuid.UUID) ([]catalog.Product, error) {
var groups []catalog.Product
// iiko присылает группы с типом "GROUP"
err := r.db.Where("rms_server_id = ? AND type = ? AND is_deleted = ?", serverID, "GROUP", false).
Order("name ASC").
Find(&groups).Error
return groups, err
}