feat: 爬虫页取代抓取任务/入库任务两页,抓取改全量+覆盖率跳过
一、后台只留一个「爬虫」页(/admin/spider) 抓取与入库本就是同一条流水线的上下游,分两页看谁都看不全。 新页三块:状态条(在线/卡住 + 队列数 + 待入库数)、正在抓(含进度与心跳)、 最近入库结果(品牌/场次/结果/说明)。只读,取消与重试一并下线。 二、抓取一律全量 删除品牌页的场次上限下拉与 hidden maxShows=3,入队固定 0(全量)。 crawl_tasks.max_shows 列保留,历史任务照常显示。 三、跳过判定改覆盖率 - brand_runways 增加 expected_images(spider 上报的应有张数) - /shows/covered 返回 images + expected - 实际张数 >= 应有 - 5 才跳过;expected=0 时回落 10 张阈值 - 实体已存在时不再直接跳过:按 key 比对把缺的图补进库(repaired) 否则「判定不完整→重爬→后端跳过」会形成死循环,带宽白花、缺图永远补不上 四、租约 900→300 秒,且心跳算存活 只看 locked_at 会把跑了几小时的全量抓取误判成卡死; 加上 progress_at 后,真卡住 5 分钟才回收。
This commit is contained in:
@ -132,19 +132,12 @@ func NewBackstage(opt BackstageOptions) *gin.Engine {
|
||||
admin.POST("/street-snaps/:id/images/:img/delete", opt.Backstage.StreetSnapImageDelete)
|
||||
admin.POST("/street-snaps/:id/cover", opt.Backstage.StreetSnapSetCover)
|
||||
admin.POST("/street-snaps/:id/toggle", opt.Backstage.StreetSnapToggleDeleted)
|
||||
// 品牌抓取任务控制面:后台决定爬哪些品牌,spider 服务来领取执行。
|
||||
// 列表 + 批量按前缀入队 + 单品牌入队 + 取消/重试/删除。
|
||||
admin.GET("/crawl", opt.Backstage.CrawlList)
|
||||
// 进度轮询(JSON):页面每 4 秒拉一次,只刷新状态/已提交/进度三列。
|
||||
admin.GET("/crawl/live", opt.Backstage.CrawlLive)
|
||||
admin.POST("/crawl/bulk", opt.Backstage.CrawlBulk)
|
||||
admin.POST("/crawl/brands/:uid", opt.Backstage.CrawlEnqueue)
|
||||
admin.POST("/crawl/tasks/:id/retry", opt.Backstage.CrawlRetry)
|
||||
admin.POST("/crawl/tasks/:id/cancel", opt.Backstage.CrawlCancel)
|
||||
admin.POST("/crawl/tasks/:id/delete", opt.Backstage.CrawlDelete)
|
||||
// 爬虫入库任务监控:列出处理进度 + 失败重试
|
||||
admin.GET("/ingest-jobs", opt.Backstage.IngestJobs)
|
||||
admin.POST("/ingest-jobs/:id/retry", opt.Backstage.IngestJobRetry)
|
||||
// 爬虫页:把「抓取」与「入库」两条队列拼成一条流水线(只读)。
|
||||
// 入队仍在品牌页,两个 POST 是被那里的按钮调用的。
|
||||
admin.GET("/spider", opt.Backstage.SpiderBoard)
|
||||
admin.GET("/spider/live", opt.Backstage.SpiderLive)
|
||||
admin.POST("/spider/bulk", opt.Backstage.SpiderBulk)
|
||||
admin.POST("/spider/brands/:uid", opt.Backstage.SpiderEnqueue)
|
||||
// 用户管理:列出用户 + 提级 / 降为 VIP
|
||||
admin.GET("/users", opt.Backstage.Users)
|
||||
admin.POST("/users/:id/tier", opt.Backstage.UserSetTier)
|
||||
|
||||
@ -664,7 +664,7 @@ func TestBackstageNavStatsOnEveryPage(t *testing.T) {
|
||||
t.Fatalf("品牌页导航应带待审计数角标(2):\n%s", body)
|
||||
}
|
||||
// 导航已在共享 partial 里,各模块入口都应在
|
||||
for _, label := range []string{"文章", "品牌", "入库任务", "用户"} {
|
||||
for _, label := range []string{"文章", "品牌", "爬虫", "用户"} {
|
||||
if !strings.Contains(body, ">"+label) {
|
||||
t.Fatalf("导航应包含入口 %q", label)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user