feat: optimize background tasks and fix warehouse pricelist workflow
Optimize task retention from 5 minutes to 30 seconds to reduce polling overhead since toast notifications are shown only once. Add conditional warehouse pricelist creation via checkbox. Fix category storage in warehouse pricelists to properly load from lot table. Replace SSE with task polling for all long operations. Add comprehensive logging for debugging while minimizing noise from polling endpoints. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
85
CLAUDE.md
85
CLAUDE.md
@@ -39,6 +39,91 @@ make build-release
|
||||
## Tech Stack
|
||||
Go 1.22+ | Gin | GORM | MariaDB 11 | htmx + Tailwind CDN | excelize
|
||||
|
||||
## Pricelist Sources
|
||||
|
||||
### Estimate
|
||||
- Создается из snapshot текущих цен компонентов (`qt_lot_metadata`)
|
||||
- Сохраняет все настройки ценообразования: `price_period_days`, `price_coefficient`, `manual_price`, `meta_prices`
|
||||
- Это основной прайслист для расчетов и оценок
|
||||
|
||||
### Warehouse
|
||||
- Создается из складских справок (`stock_log`)
|
||||
- **ВАЖНО**: НЕ должен загружать настройки цен из `lot_metadata`
|
||||
- Использует только weighted median (взвешенная медиана по количеству) из stock_log
|
||||
- Поле `price_method` всегда содержит "weighted_median"
|
||||
- Не содержит `price_period_days`, `price_coefficient`, `manual_price`, `meta_prices`
|
||||
- Это фактические складские цены без дополнительной обработки
|
||||
|
||||
### Competitor
|
||||
- Зарезервировано для будущих интеграций с API B2B площадок
|
||||
|
||||
## Background Tasks (Фоновые задачи)
|
||||
|
||||
**ВАЖНО**: Все долгие операции выполняются через Task Manager, НЕ через SSE (Server-Sent Events).
|
||||
|
||||
### Принцип работы:
|
||||
1. **Backend**: Хендлер создает фоновую задачу через `taskManager.Submit()` и возвращает `task_id`
|
||||
2. **Frontend**: Получает `task_id` и делает polling через `/api/tasks/:id` каждые 500ms
|
||||
3. **Уведомления**: Система автоматически показывает toast-уведомления при завершении задачи
|
||||
|
||||
### Типы задач:
|
||||
- `TaskTypeRecalculate` - пересчет всех цен компонентов
|
||||
- `TaskTypeStockImport` - импорт складской справки из .mxl/.xlsx
|
||||
- `TaskTypePricelistCreate` - создание прайслиста (estimate/warehouse/competitor)
|
||||
|
||||
### Структура задачи:
|
||||
```go
|
||||
type Task struct {
|
||||
ID string // UUID задачи
|
||||
Type TaskType // Тип задачи
|
||||
Status TaskStatus // running | completed | error
|
||||
Progress int // 0-100
|
||||
Message string // Текущее сообщение для UI
|
||||
Result map[string]interface{} // Результат при completed
|
||||
Error string // Текст ошибки при error
|
||||
CreatedAt time.Time
|
||||
DoneAt *time.Time
|
||||
}
|
||||
```
|
||||
|
||||
### Пример использования:
|
||||
```go
|
||||
taskID := h.taskManager.Submit(tasks.TaskTypeStockImport, func(ctx context.Context, progressCb func(int, string)) (map[string]interface{}, error) {
|
||||
// Выполнение задачи
|
||||
progressCb(50, "Половина выполнена")
|
||||
|
||||
// Возврат результата
|
||||
return map[string]interface{}{
|
||||
"rows_total": 100,
|
||||
"inserted": 95,
|
||||
}, nil
|
||||
})
|
||||
c.JSON(http.StatusOK, gin.H{"task_id": taskID})
|
||||
```
|
||||
|
||||
## Категории товаров (lot_category)
|
||||
|
||||
### Источник данных:
|
||||
Категории берутся из таблицы `lot` (поле `lot_category`), НЕ из `qt_lot_metadata`.
|
||||
|
||||
### При создании прайслистов:
|
||||
1. **Estimate**: Загружаем категории из `lot.lot_category` для всех компонентов
|
||||
2. **Warehouse**: Загружаем категории из `lot.lot_category` для всех позиций
|
||||
3. Сохраняем в поле `lot_category` таблицы `qt_pricelist_items`
|
||||
|
||||
### В модели:
|
||||
```go
|
||||
type PricelistItem struct {
|
||||
LotCategory *string `gorm:"column:lot_category;size:50" json:"category,omitempty"`
|
||||
// JSON поле называется "category" для фронтенда
|
||||
}
|
||||
```
|
||||
|
||||
### ВАЖНО:
|
||||
- Категория НЕ виртуальное поле - она сохраняется в БД при создании прайслиста
|
||||
- JOIN с таблицей `lot` нужен только для `lot_description`, категория уже есть в `qt_pricelist_items`
|
||||
|
||||
## Notes
|
||||
- Главная страница должна вести на `/admin/pricing`.
|
||||
- В UI не должно быть ссылок на конфигуратор, проекты и экспорт.
|
||||
- **НЕ использовать SSE** - только фоновые задачи через Task Manager.
|
||||
|
||||
Reference in New Issue
Block a user