package model // IngestJob 爬虫上报的走秀入库任务队列。 // // 设计:爬虫 POST 到 :8092 的 ingest 接口(HMAC 验签后)只做「入队 + 立即 202」, // 真正的去重 / 补分类 / 下载图 / 写正式表 由后台 worker 异步处理, // 因此接口本身很薄、不会被慢任务(尤其是 OSS 上传)拖垮。 // // 并发安全靠 MySQL 的 `SELECT ... FOR UPDATE SKIP LOCKED`:多个 worker 同时 // Claim 不会抢到同一条,未提交的行对其它 worker 不可见。 type IngestJob struct { ID uint32 `gorm:"primaryKey;column:id" json:"id"` CreatedAt uint32 `gorm:"column:created_at" json:"created_at"` UpdatedAt uint32 `gorm:"column:updated_at" json:"updated_at"` LockedAt uint32 `gorm:"column:locked_at" json:"locked_at"` // worker 领取时间;用于超时兜底回收 SourceURL string `gorm:"column:source_url" json:"source_url"` Status string `gorm:"column:status;size:16;not null;default:'pending'" json:"status"` // pending|processing|done|failed Attempts uint8 `gorm:"column:attempts;not null;default:0" json:"attempts"` Payload string `gorm:"column:payload;type:text" json:"payload"` // 原始 JSON(RunwayIngest 或 MediaCleanupPayload) LastError string `gorm:"column:last_error;type:text" json:"last_error"` Kind string `gorm:"column:kind;size:16;not null;default:'crawl'" json:"kind"` // crawl|media_cleanup NextAttemptAt uint32 `gorm:"column:next_attempt_at;not null;default:0" json:"next_attempt_at"` // 重试退避:到达该 unix 时间才可被领取 } // TableName 指定队列表名。 func (IngestJob) TableName() string { return "ingest_jobs" } // IngestJob 状态常量。 const ( IngestStatusPending = "pending" IngestStatusProcessing = "processing" IngestStatusDone = "done" IngestStatusFailed = "failed" ) // IngestJob 任务类型(kind 列):区分「爬虫入库」与「清理七牛孤儿图」两条管线。 const ( IngestKindCrawl = "crawl" // 爬虫上报的走秀/街拍入库 IngestKindMediaCleanup = "media_cleanup" // 删除图集时异步清理七牛孤儿文件 ) // IngestMaxAttempts 单条任务最大处理次数(含首次)。达上限仍未成功则置 failed,需后台手动重试。 const IngestMaxAttempts = 3 // IngestStuckTimeoutSec 任务「卡死」判定阈值:worker 领取后 processing 状态持续超过该秒数, // 视为 worker 崩溃/被杀遗留,由 Claim 兜底回收为 pending 重领。下载图/传图正常远低于此值。 const IngestStuckTimeoutSec = 300 // IngestRetryBackoff 返回第 attempts 次失败后的退避秒数(指数退避,封顶 600s)。 // attempts 从 1 起算:30s / 60s / 120s。用于失败任务的自动重试调度。 func IngestRetryBackoff(attempts int) int { d := 30 * (1 << uint(attempts-1)) if d > 600 { d = 600 } return d }