Files
backend_v2/internal/handler/spider_render_test.go
toom1996 e930ac41c8 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 分钟才回收。
2026-10-25 23:59:54 +08:00

77 lines
2.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package handler
import (
"bytes"
"strings"
"testing"
"github.com/gin-gonic/gin"
)
// TestSpiderPageRenders 守住「爬虫」页的三块内容与轮询锚点。
//
// 这类模板错误不会编译失败,只会在打开页面时 500 或空白,
// 所以用一个渲染测试把「有任务 / 卡住 / 有入库结果」这几种情况固定下来。
func TestSpiderPageRenders(t *testing.T) {
tpl, ok := pageTemplates["spider"]
if !ok {
t.Fatal("spider 模板未注册")
}
data := gin.H{
"NavKey": "spider",
"PendingReviews": 0,
"State": "可能卡住",
"StateClass": "failed",
"RunningCount": 1,
"QueuedCount": 2,
"PendingIngest": 3,
"Running": []spiderRunView{{
ID: 85,
BrandName: "Amiri",
Progress: "12/47 场 · Fall 2021 Menswear(24/38 张)",
Beat: "11 分钟",
Stale: true,
Worker: "vm-1-74218-0",
}},
"Queued": []spiderQueueView{
{BrandName: "Celine", Since: "10-25 23:30", Waiting: "8 分钟"},
{BrandName: "Gucci", Since: "10-25 23:35", Waiting: "3 分钟"},
},
"Results": []spiderResultView{
{At: "10-25 23:12", BrandName: "Amiri", Title: "Fall 2019 Menswear", Result: "新建", Note: "新建 34 张图"},
{At: "10-25 23:08", BrandName: "Gucci", Title: "Fall 2020 RTW", Result: "失败", Note: "图片下载超时", Bad: true},
},
}
var buf bytes.Buffer
if err := tpl.ExecuteTemplate(&buf, "layout", data); err != nil {
t.Fatalf("渲染爬虫页失败: %v", err)
}
out := buf.String()
for _, want := range []string{
`id="running-body"`, // 轮询重建的目标容器
"/admin/spider/live", // 轮询地址
"12/47 场 · Fall 2021 Menswear(24/38 张)", // 进度文案原样输出
"Celine", // 待抓队列
"新建 34 张图", // 入库结果说明
} {
if !strings.Contains(out, want) {
t.Errorf("页面缺少 %q", want)
}
}
// 卡住要能看出来:进度单元格带 stale 样式。
if !strings.Contains(out, "progress stale") {
t.Error("疑似卡住的任务没有 stale 样式")
}
// 失败结果要走 failed 徽标,正常结果走 done 徽标。
if !strings.Contains(out, `class="badge failed"`) {
t.Error("失败结果没有 failed 徽标")
}
if !strings.Contains(out, `class="badge done"`) {
t.Error("正常结果没有 done 徽标")
}
}