Улучшения управления ценами и конфигурациями
- Добавлено отображение последней полученной цены в окне настройки цены - Добавлен функционал переименования конфигураций (PATCH /api/configs/:uuid/rename) - Изменён формат имени файла при экспорте: "YYYY-MM-DD NAME SPEC.ext" - Исправлена сортировка компонентов: перенесена на сервер для корректной работы с пагинацией - Добавлен расчёт popularity_score на основе котировок из lot_log - Исправлена потеря настроек (метод, период, коэффициент) при пересчёте цен Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -16,9 +16,11 @@ func NewComponentRepository(db *gorm.DB) *ComponentRepository {
|
||||
}
|
||||
|
||||
type ComponentFilter struct {
|
||||
Category string
|
||||
Search string
|
||||
HasPrice bool
|
||||
Category string
|
||||
Search string
|
||||
HasPrice bool
|
||||
SortField string
|
||||
SortDir string
|
||||
}
|
||||
|
||||
func (r *ComponentRepository) List(filter ComponentFilter, offset, limit int) ([]models.LotMetadata, int64, error) {
|
||||
@@ -43,10 +45,33 @@ func (r *ComponentRepository) List(filter ComponentFilter, offset, limit int) ([
|
||||
|
||||
query.Count(&total)
|
||||
|
||||
// Sort by popularity + freshness, no price goes last
|
||||
// Apply sorting
|
||||
sortDir := "ASC"
|
||||
if filter.SortDir == "desc" {
|
||||
sortDir = "DESC"
|
||||
}
|
||||
|
||||
switch filter.SortField {
|
||||
case "popularity_score":
|
||||
query = query.Order("popularity_score " + sortDir)
|
||||
case "current_price":
|
||||
query = query.Order("CASE WHEN current_price IS NULL OR current_price = 0 THEN 1 ELSE 0 END").
|
||||
Order("current_price " + sortDir)
|
||||
case "lot_name":
|
||||
query = query.Order("lot_name " + sortDir)
|
||||
case "quote_count":
|
||||
// Sort by quote count from lot_log table
|
||||
query = query.
|
||||
Select("qt_lot_metadata.*, (SELECT COUNT(*) FROM lot_log WHERE lot_log.lot = qt_lot_metadata.lot_name) as quote_count_sort").
|
||||
Order("quote_count_sort " + sortDir)
|
||||
default:
|
||||
// Default: sort by popularity, no price goes last
|
||||
query = query.
|
||||
Order("CASE WHEN current_price IS NULL OR current_price = 0 THEN 1 ELSE 0 END").
|
||||
Order("popularity_score DESC")
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -90,3 +90,26 @@ func (r *StatsRepository) ResetMonthlyCounters() error {
|
||||
Where("1 = 1").
|
||||
Update("quotes_last_30d", 0).Error
|
||||
}
|
||||
|
||||
// UpdatePopularityScores recalculates popularity_score in qt_lot_metadata
|
||||
// based on supplier quotes from lot_log table
|
||||
func (r *StatsRepository) UpdatePopularityScores() error {
|
||||
// Formula: popularity_score = quotes_last_30d * 3 + quotes_last_90d * 1 + quotes_total * 0.1
|
||||
// This gives more weight to recent supplier activity
|
||||
return r.db.Exec(`
|
||||
UPDATE qt_lot_metadata m
|
||||
LEFT JOIN (
|
||||
SELECT
|
||||
lot,
|
||||
COUNT(*) as quotes_total,
|
||||
SUM(CASE WHEN date >= DATE_SUB(NOW(), INTERVAL 30 DAY) THEN 1 ELSE 0 END) as quotes_last_30d,
|
||||
SUM(CASE WHEN date >= DATE_SUB(NOW(), INTERVAL 90 DAY) THEN 1 ELSE 0 END) as quotes_last_90d
|
||||
FROM lot_log
|
||||
GROUP BY lot
|
||||
) s ON m.lot_name = s.lot
|
||||
SET m.popularity_score = COALESCE(
|
||||
s.quotes_last_30d * 3 + s.quotes_last_90d * 1 + s.quotes_total * 0.1,
|
||||
0
|
||||
)
|
||||
`).Error
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user