Add initial backend implementation
- Go module with Gin, GORM, JWT, excelize dependencies - Configuration loading from YAML with all settings - GORM models for users, categories, components, configurations, alerts - Repository layer for all entities - Services: auth (JWT), pricing (median/average/weighted), components, quotes, configurations, export (CSV/XLSX), alerts - Middleware: JWT auth, role-based access, CORS - HTTP handlers for all API endpoints - Main server with dependency injection and graceful shutdown Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
127
internal/repository/component.go
Normal file
127
internal/repository/component.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/mchus/quoteforge/internal/models"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type ComponentRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func NewComponentRepository(db *gorm.DB) *ComponentRepository {
|
||||
return &ComponentRepository{db: db}
|
||||
}
|
||||
|
||||
type ComponentFilter struct {
|
||||
Category string
|
||||
Vendor string
|
||||
Search string
|
||||
HasPrice bool
|
||||
}
|
||||
|
||||
func (r *ComponentRepository) List(filter ComponentFilter, offset, limit int) ([]models.LotMetadata, int64, error) {
|
||||
var components []models.LotMetadata
|
||||
var total int64
|
||||
|
||||
query := r.db.Model(&models.LotMetadata{}).
|
||||
Preload("Lot").
|
||||
Preload("Category")
|
||||
|
||||
if filter.Category != "" {
|
||||
query = query.Joins("JOIN qt_categories ON qt_lot_metadata.category_id = qt_categories.id").
|
||||
Where("qt_categories.code = ?", filter.Category)
|
||||
}
|
||||
if filter.Vendor != "" {
|
||||
query = query.Where("vendor = ?", filter.Vendor)
|
||||
}
|
||||
if filter.Search != "" {
|
||||
search := "%" + filter.Search + "%"
|
||||
query = query.Where("lot_name LIKE ? OR model LIKE ?", search, search)
|
||||
}
|
||||
if filter.HasPrice {
|
||||
query = query.Where("current_price IS NOT NULL AND current_price > 0")
|
||||
}
|
||||
|
||||
query.Count(&total)
|
||||
|
||||
// Sort by popularity + freshness, no price goes last
|
||||
err := query.
|
||||
Order("CASE WHEN current_price IS NULL OR current_price = 0 THEN 1 ELSE 0 END").
|
||||
Order("popularity_score DESC").
|
||||
Offset(offset).
|
||||
Limit(limit).
|
||||
Find(&components).Error
|
||||
|
||||
return components, total, err
|
||||
}
|
||||
|
||||
func (r *ComponentRepository) GetByLotName(lotName string) (*models.LotMetadata, error) {
|
||||
var component models.LotMetadata
|
||||
err := r.db.
|
||||
Preload("Lot").
|
||||
Preload("Category").
|
||||
Where("lot_name = ?", lotName).
|
||||
First(&component).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &component, nil
|
||||
}
|
||||
|
||||
func (r *ComponentRepository) GetMultiple(lotNames []string) ([]models.LotMetadata, error) {
|
||||
var components []models.LotMetadata
|
||||
err := r.db.
|
||||
Preload("Lot").
|
||||
Preload("Category").
|
||||
Where("lot_name IN ?", lotNames).
|
||||
Find(&components).Error
|
||||
return components, err
|
||||
}
|
||||
|
||||
func (r *ComponentRepository) Update(component *models.LotMetadata) error {
|
||||
return r.db.Save(component).Error
|
||||
}
|
||||
|
||||
func (r *ComponentRepository) Create(component *models.LotMetadata) error {
|
||||
return r.db.Create(component).Error
|
||||
}
|
||||
|
||||
func (r *ComponentRepository) GetVendors(category string) ([]string, error) {
|
||||
var vendors []string
|
||||
query := r.db.Model(&models.LotMetadata{}).Distinct("vendor")
|
||||
if category != "" {
|
||||
query = query.Joins("JOIN qt_categories ON qt_lot_metadata.category_id = qt_categories.id").
|
||||
Where("qt_categories.code = ?", category)
|
||||
}
|
||||
err := query.Pluck("vendor", &vendors).Error
|
||||
return vendors, err
|
||||
}
|
||||
|
||||
func (r *ComponentRepository) IncrementRequestCount(lotName string) error {
|
||||
now := time.Now()
|
||||
return r.db.Model(&models.LotMetadata{}).
|
||||
Where("lot_name = ?", lotName).
|
||||
Updates(map[string]interface{}{
|
||||
"request_count": gorm.Expr("request_count + 1"),
|
||||
"last_request_date": now,
|
||||
}).Error
|
||||
}
|
||||
|
||||
// GetAllLots returns all lots from the existing lot table
|
||||
func (r *ComponentRepository) GetAllLots() ([]models.Lot, error) {
|
||||
var lots []models.Lot
|
||||
err := r.db.Find(&lots).Error
|
||||
return lots, err
|
||||
}
|
||||
|
||||
// GetLotsWithoutMetadata returns lots that don't have qt_lot_metadata entries
|
||||
func (r *ComponentRepository) GetLotsWithoutMetadata() ([]models.Lot, error) {
|
||||
var lots []models.Lot
|
||||
err := r.db.
|
||||
Where("lot_name NOT IN (SELECT lot_name FROM qt_lot_metadata)").
|
||||
Find(&lots).Error
|
||||
return lots, err
|
||||
}
|
||||
Reference in New Issue
Block a user