feat: 品牌抓取历史(brand_crawl_logs)+ 品牌页展示

crawl_tasks 每品牌只有一行(uq_crawl_tasks_brand),入队/完成都是覆盖,
答不了「什么时候爬过、爬了几次、哪次没抓到」。故新增 append-only 历史表:

- 在 Report 的同一事务里写一条:品牌 / 任务 / 状态 / 第几次尝试 /
  场次上限 / 实际上报场次 / 错误 / 执行者 / 领取与回报时间
- 写入后只保留该品牌最近 30 条(CrawlLogKeepPerBrand),
  历史用于排障而非归档,避免无限增长
- 品牌详情页新增「抓取历史」区块(结果 / 明细 / 耗时 / 执行者),
  与「更新记录」上下相邻,构成「抓取一次 → 每场入库结果」的完整链路

量级:每品牌每次抓取 1 行、约 200B(含索引);全站跑一遍 23237 行 ≈ 4.6MB。
This commit is contained in:
toom1996
2026-10-25 11:37:43 +08:00
parent c8a70760cc
commit 64b264ee73
6 changed files with 172 additions and 3 deletions

View File

@ -0,0 +1,30 @@
package model
// CrawlLogKeepPerBrand 每个品牌保留的抓取历史条数。
//
// 抓取历史是「最近发生了什么」的排障材料,不是审计归档;留太多只会让查询变慢。
// 30 条足够覆盖「最近一次批量重爬 + 若干次手动重试」。
const CrawlLogKeepPerBrand = 30
// CrawlLog 品牌抓取历史的一行(append-only)。
//
// 与 ingest_jobs 的分工:
// - CrawlLog 记录「这一次抓取」的进程级结果(抓到几场、报了什么错、谁跑的);
// - ingest_jobs 记录「每一场秀」的入库结果(新建 / 跳过 / 失败)。
// 两者按时间顺序摆在品牌详情页,就是完整的更新链路。
type CrawlLog struct {
ID uint32 `gorm:"primaryKey;column:id" json:"id"`
BrandID uint32 `gorm:"column:brand_id;not null" json:"brand_id"`
TaskID uint32 `gorm:"column:task_id;not null;default:0" json:"task_id"`
Status string `gorm:"column:status;size:16;not null;default:''" json:"status"` // done / failed
Attempt int16 `gorm:"column:attempt;not null;default:1" json:"attempt"`
ShowsRequested int16 `gorm:"column:shows_requested;not null;default:0" json:"shows_requested"`
ShowsSubmitted int32 `gorm:"column:shows_submitted;not null;default:0" json:"shows_submitted"`
Message string `gorm:"column:message;type:text;not null;default:''" json:"message"`
Worker string `gorm:"column:worker;size:64;not null;default:''" json:"worker"`
StartedAt uint32 `gorm:"column:started_at;not null;default:0" json:"started_at"`
FinishedAt uint32 `gorm:"column:finished_at;not null;default:0" json:"finished_at"`
}
// TableName 指定表名。
func (CrawlLog) TableName() string { return "brand_crawl_logs" }