一、后台只留一个「爬虫」页(/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 分钟才回收。
62 lines
3.7 KiB
Go
62 lines
3.7 KiB
Go
package model
|
||
|
||
// BrandRunway 走秀档案(对外称"文章")。
|
||
type BrandRunway struct {
|
||
ID uint32 `gorm:"primaryKey;column:id" json:"id"`
|
||
// i18n 列(方案 A):*_en 为规范英文(当前数据全英文),*_cn 为中文;
|
||
// 服务端按 locale 选列,缺翻译回落另一语言。
|
||
// (原通用 title / description 列已删除,统一由 *_en/*_cn 取代。)
|
||
TitleEn string `gorm:"column:title_en" json:"title_en"`
|
||
TitleCn string `gorm:"column:title_cn" json:"title_cn"`
|
||
DescriptionEn string `gorm:"column:description_en" json:"description_en"`
|
||
DescriptionCn string `gorm:"column:description_cn" json:"description_cn"`
|
||
CreatedAt uint32 `gorm:"column:created_at" json:"created_at"`
|
||
UpdatedAt uint32 `gorm:"column:updated_at" json:"updated_at"`
|
||
IsDeleted uint8 `gorm:"column:is_deleted" json:"is_deleted"`
|
||
ImageCount uint16 `gorm:"column:image_count" json:"image_count"`
|
||
// ExpectedImages 该场秀应有的图片总数(spider 上报)。0 = 未知(本字段上线前的历史数据)。
|
||
//
|
||
// 用途:重爬前的覆盖率判定——实际张数接近应有张数才算「抓全了」。
|
||
// 只看「有没有图」会把当初只抓到十几张的场次永久判成已完成,缺的图再也补不上。
|
||
ExpectedImages int `gorm:"column:expected_images;not null;default:0" json:"expected_images"`
|
||
BrandID uint32 `gorm:"column:brand_id" json:"brand_id"`
|
||
Year uint16 `gorm:"column:year" json:"year"`
|
||
Cover string `gorm:"column:cover" json:"cover"`
|
||
|
||
// 分类字段:由 title 解析回填(历史数据迁移一次性写入,现已由外部 SQL 迁移维护)
|
||
CollectionType string `gorm:"column:collection_type;size:16;not null;default:''" json:"collection_type"` // rtw/menswear/couture/resort/pre_fall
|
||
Season string `gorm:"column:season;size:8" json:"season"` // spring/fall/null
|
||
SeasonCode string `gorm:"column:season_code;size:8;not null;default:''" json:"season_code"` // SS26/FW25/RES26/PF25
|
||
PublishedAt uint32 `gorm:"column:published_at" json:"published_at"` // 发布时间 Unix 时间戳(秒);0=未抽取。Vogue datePublished;标题缺年份时用于矫正 year
|
||
|
||
// 审核态与溯源(单表发布模型;列由 db/migrations/2026-09-22-01-single-table-publish.sql 添加)。
|
||
Status string `gorm:"column:status;size:16;not null;default:'pending'" json:"status"`
|
||
JobID uint32 `gorm:"column:job_id;not null;default:0" json:"job_id"` // 关联 ingest_jobs.id(重跑溯源)
|
||
Reviewer string `gorm:"column:reviewer;size:64;not null;default:''" json:"reviewer"` // 审核人(admin 账号)
|
||
}
|
||
|
||
// TableName 指定表名。
|
||
func (BrandRunway) TableName() string { return "brand_runways" }
|
||
|
||
// RunwayRow 走秀档案的查询投影行。
|
||
//
|
||
// 独立于 BrandRunway 定义,是为了承载 JOIN brands 得到的品牌名(en/cn 两列)。
|
||
type RunwayRow struct {
|
||
ID uint32 `gorm:"column:id"`
|
||
BrandID uint32 `gorm:"column:brand_id"`
|
||
TitleEn string `gorm:"column:title_en"`
|
||
TitleCn string `gorm:"column:title_cn"`
|
||
DescriptionEn string `gorm:"column:description_en"`
|
||
DescriptionCn string `gorm:"column:description_cn"`
|
||
Cover string `gorm:"column:cover"`
|
||
Year uint16 `gorm:"column:year"`
|
||
ImageCount uint16 `gorm:"column:image_count"`
|
||
CollectionType string `gorm:"column:collection_type"`
|
||
Season string `gorm:"column:season"`
|
||
SeasonCode string `gorm:"column:season_code"`
|
||
Status string `gorm:"column:status"`
|
||
CreatedAt uint32 `gorm:"column:created_at"`
|
||
BrandNameEn string `gorm:"column:brand_name_en"`
|
||
BrandNameCn string `gorm:"column:brand_name_cn"`
|
||
}
|