feat: sync_log таблица и список прайслистов в Support Bundle
- Добавлена таблица sync_log (до 100 записей на тип): фиксирует каждый запуск синхронизации с типом, статусом, ошибкой, кол-вом и временем - AppendSyncLog вызывается из SyncComponents, SyncPricelists (service и handler), SyncAll и SyncComponentsIfEmpty - Bundle теперь включает sync_log.json (200 последних записей) и pricelists.json (все скачанные прайслисты, сгруппированные по source) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -229,6 +229,7 @@ func autoMigrateLocalSchema(db *gorm.DB) error {
|
||||
&LocalSyncGuardState{},
|
||||
&PendingChange{},
|
||||
&LocalPartnumberBook{},
|
||||
&SyncLogEntry{},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1153,6 +1154,37 @@ func (l *LocalDB) SetPricelistSyncResult(status, errorText string, attemptedAt t
|
||||
})
|
||||
}
|
||||
|
||||
const syncLogMaxPerType = 100
|
||||
|
||||
// AppendSyncLog writes a sync result and prunes old entries beyond the per-type cap.
|
||||
func (l *LocalDB) AppendSyncLog(syncType, status, errorText string, syncedCount int, startedAt time.Time, durationMs int64) {
|
||||
entry := SyncLogEntry{
|
||||
SyncType: syncType,
|
||||
Status: status,
|
||||
ErrorText: errorText,
|
||||
SyncedCount: syncedCount,
|
||||
StartedAt: startedAt,
|
||||
DurationMs: durationMs,
|
||||
}
|
||||
if err := l.db.Create(&entry).Error; err != nil {
|
||||
return
|
||||
}
|
||||
// Prune: keep only the most recent N entries for this sync_type
|
||||
l.db.Exec(`
|
||||
DELETE FROM sync_log
|
||||
WHERE sync_type = ? AND id NOT IN (
|
||||
SELECT id FROM sync_log WHERE sync_type = ? ORDER BY started_at DESC LIMIT ?
|
||||
)
|
||||
`, syncType, syncType, syncLogMaxPerType)
|
||||
}
|
||||
|
||||
// GetSyncLog returns the most recent sync log entries, newest first.
|
||||
func (l *LocalDB) GetSyncLog(limit int) ([]SyncLogEntry, error) {
|
||||
var entries []SyncLogEntry
|
||||
err := l.db.Order("started_at DESC").Limit(limit).Find(&entries).Error
|
||||
return entries, err
|
||||
}
|
||||
|
||||
func (l *LocalDB) GetLastComponentSyncAttemptAt() *time.Time {
|
||||
value, ok := l.getAppSettingValue("last_component_sync_attempt_at")
|
||||
if !ok {
|
||||
|
||||
@@ -317,6 +317,19 @@ type VendorSpecLotMapping struct {
|
||||
QuantityPerPN int `json:"quantity_per_pn"`
|
||||
}
|
||||
|
||||
// SyncLogEntry records the outcome of a single sync operation for diagnostics.
|
||||
type SyncLogEntry struct {
|
||||
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
|
||||
SyncType string `gorm:"not null;index;size:32" json:"sync_type"` // components | pricelists | push | full
|
||||
Status string `gorm:"not null;size:16" json:"status"` // ok | error | skipped
|
||||
ErrorText string `gorm:"size:1000" json:"error_text,omitempty"`
|
||||
SyncedCount int `gorm:"default:0" json:"synced_count"`
|
||||
StartedAt time.Time `gorm:"not null;index" json:"started_at"`
|
||||
DurationMs int64 `gorm:"default:0" json:"duration_ms"`
|
||||
}
|
||||
|
||||
func (SyncLogEntry) TableName() string { return "sync_log" }
|
||||
|
||||
// VendorSpec is a JSON-encodable slice of VendorSpecItem
|
||||
type VendorSpec []VendorSpecItem
|
||||
|
||||
|
||||
Reference in New Issue
Block a user