package mysql import ( "context" "errors" "strings" "time" "gorm.io/gorm" "gorm.io/gorm/clause" "wucher/internal/domain/dul" filemanager "wucher/internal/domain/file_manager" "wucher/internal/shared/pkg/uuidv7" ) type DULRepository struct { db *gorm.DB } func NewDULRepository(db *gorm.DB) *DULRepository { return &DULRepository{db: db} } func (r *DULRepository) WithTransaction(ctx context.Context, fn func(repo dul.Repository) error) error { if fn == nil { return nil } return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { return fn(&DULRepository{db: tx}) }) } func (r *DULRepository) Create(ctx context.Context, row *dul.DUL) error { if actor := actorUserIDFromContext(ctx); len(actor) > 0 { row.CreatedBy = actor row.UpdatedBy = actor } return r.db.WithContext(ctx).Omit(clause.Associations).Create(row).Error } func (r *DULRepository) Update(ctx context.Context, row *dul.DUL) error { if actor := actorUserIDFromContext(ctx); len(actor) > 0 { row.UpdatedBy = actor } return r.db.WithContext(ctx).Omit(clause.Associations).Save(row).Error } func (r *DULRepository) Delete(ctx context.Context, id []byte) error { updates := map[string]any{"deleted_at": time.Now().UTC()} if actor := actorUserIDFromContext(ctx); len(actor) > 0 { updates["deleted_by"] = actor updates["updated_by"] = actor } return r.db.WithContext(ctx).Model(&dul.DUL{}).Where("id = ? AND deleted_at IS NULL", id).Updates(updates).Error } func (r *DULRepository) GetByID(ctx context.Context, id []byte) (*dul.DUL, error) { var row dul.DUL err := r.db.WithContext(ctx). Preload("Base"). Preload("Base.BaseCategory"). Where("id = ? AND deleted_at IS NULL", id). First(&row).Error if errors.Is(err, gorm.ErrRecordNotFound) { return nil, nil } if err != nil { return nil, err } _ = r.loadImages(ctx, []*dul.DUL{&row}) return &row, nil } func (r *DULRepository) List(ctx context.Context, filter, sort string, limit, offset int, baseIDs [][]byte) ([]dul.DUL, int64, error) { var rows []dul.DUL var total int64 query := r.db.WithContext(ctx).Model(&dul.DUL{}).Where("deleted_at IS NULL") if len(baseIDs) > 0 { query = query.Where("base_id IN ?", baseIDs) } if s := strings.TrimSpace(filter); s != "" { like := "%" + s + "%" query = query.Where("name LIKE ? OR info LIKE ?", like, like) } if err := query.Count(&total).Error; err != nil { return nil, 0, err } if strings.TrimSpace(sort) != "" { query = query.Order(sort) } else { query = query.Order("base_id ASC").Order("no ASC") } if limit > 0 { query = query.Limit(limit).Offset(offset) } if err := query.Preload("Base").Preload("Base.BaseCategory").Find(&rows).Error; err != nil { return nil, 0, err } ptrs := make([]*dul.DUL, 0, len(rows)) for i := range rows { ptrs = append(ptrs, &rows[i]) } _ = r.loadImages(ctx, ptrs) return rows, total, nil } func (r *DULRepository) loadImages(ctx context.Context, rows []*dul.DUL) error { if len(rows) == 0 { return nil } refIDs := make([]string, 0, len(rows)) byRef := map[string]*dul.DUL{} for _, row := range rows { if row == nil || len(row.ID) != 16 { continue } refID, _ := uuidv7.BytesToString(row.ID) refIDs = append(refIDs, refID) byRef[refID] = row row.Images = nil } if len(refIDs) == 0 { return nil } var attachments []filemanager.Attachment if err := r.db.WithContext(ctx). Preload("File"). Where("ref_type = ? AND ref_id IN ?", "dul", refIDs). Order("sort_order ASC, created_at ASC"). Find(&attachments).Error; err != nil { return err } for i := range attachments { att := attachments[i] row := byRef[att.RefID] if row == nil { continue } clone := att row.Images = append(row.Images, &clone) } return nil }