update backend

This commit is contained in:
toom1996
2026-09-20 01:06:13 +08:00
parent f7ae917603
commit b99dc3dca1
115 changed files with 24732 additions and 10842 deletions

View File

@ -2,7 +2,7 @@ package model
// Brand 品牌表。
//
// 注意:时间戳字段为 int unsigned 存储的 Unix 秒,不是 MySQL datetime,
// 注意:时间戳字段为 int unsigned 存储的 Unix 秒,不是数据库 datetime,
// 迁移时保持原样,避免与既有数据不兼容。
type Brand struct {
ID uint32 `gorm:"primaryKey;column:id" json:"id"`
@ -17,4 +17,4 @@ type Brand struct {
}
// TableName 指定表名。
func (Brand) TableName() string { return "brand" }
func (Brand) TableName() string { return "brands" }

View File

@ -4,9 +4,9 @@ package model
//
// 设计:爬虫 POST 到 :8092 的 ingest 接口(HMAC 验签后)只做「入队 + 立即 202」,
// 真正的去重 / 补分类 / 下载图 / 写正式表 由后台 worker 异步处理,
// 因此接口本身很薄、不会被慢任务(尤其是 OSS 上传)拖垮。
// 因此接口本身很薄、不会被慢任务(尤其是 S4 上传)拖垮。
//
// 并发安全靠 MySQL 的 `SELECT ... FOR UPDATE SKIP LOCKED`:多个 worker 同时
// 并发安全靠 PostgreSQL 的 `SELECT ... FOR UPDATE SKIP LOCKED`:多个 worker 同时
// Claim 不会抢到同一条,未提交的行对其它 worker 不可见。
type IngestJob struct {
ID uint32 `gorm:"primaryKey;column:id" json:"id"`
@ -32,10 +32,10 @@ const (
IngestStatusFailed = "failed"
)
// IngestJob 任务类型(kind 列):区分「爬虫入库」与「清理七牛孤儿图」两条管线。
// IngestJob 任务类型(kind 列):区分「爬虫入库」与「清理S4孤儿图」两条管线。
const (
IngestKindCrawl = "crawl" // 爬虫上报的走秀/街拍入库
IngestKindMediaCleanup = "media_cleanup" // 删除图集时异步清理七牛孤儿文件
IngestKindMediaCleanup = "media_cleanup" // 删除图集时异步清理S4孤儿文件
)
// IngestMaxAttempts 单条任务最大处理次数(含首次)。达上限仍未成功则置 failed,需后台手动重试。

View File

@ -25,11 +25,11 @@ type BrandRunway struct {
}
// TableName 指定表名。
func (BrandRunway) TableName() string { return "brand_runway" }
func (BrandRunway) TableName() string { return "brand_runways" }
// RunwayRow 走秀档案的查询投影行。
//
// 独立于 BrandRunway 定义,是为了承载 JOIN brand 得到的品牌名(en/cn 两列)。
// 独立于 BrandRunway 定义,是为了承载 JOIN brands 得到的品牌名(en/cn 两列)。
type RunwayRow struct {
ID uint32 `gorm:"column:id"`
BrandID uint32 `gorm:"column:brand_id"`

View File

@ -5,8 +5,8 @@ import "database/sql"
// BrandRunwayDraft 走秀草稿(爬虫入库待审核)。
//
// 爬虫 worker 处理完 ingest_jobs 后不直写正式表,而是写入本表并置为 pending;
// 后台审核通过(Approve)后再晋升(SaveRunwayFromDraft)到 brand_runway 正式表。
// 字段与 brand_runway 对齐(含 review 专用字段),便于审核页直接预览/微调。
// 后台审核通过(Approve)后再晋升(SaveRunwayFromDraft)到 brand_runways 正式表。
// 字段与 brand_runways 对齐(含 review 专用字段),便于审核页直接预览/微调。
type BrandRunwayDraft struct {
ID uint32 `gorm:"primaryKey;column:id" json:"id"`
JobID uint32 `gorm:"column:job_id" json:"job_id"` // 关联 ingest_jobs.id(重跑溯源)
@ -30,7 +30,7 @@ type BrandRunwayDraft struct {
}
// TableName 指定草稿主表名。
func (BrandRunwayDraft) TableName() string { return "brand_runway_draft" }
func (BrandRunwayDraft) TableName() string { return "brand_runway_drafts" }
// BrandRunwayDraftImage 草稿图片(对应 brand_runway_draft_images)。
type BrandRunwayDraftImage struct {
@ -46,7 +46,6 @@ type BrandRunwayDraftImage struct {
UpdatedAt uint32 `gorm:"column:updated_at" json:"updated_at"`
// 去重字段:见 BrandRunwayImage 注释。phash 为 vector(64) 二进制向量(NULL=无法解码)。
ContentSha1 string `gorm:"column:content_sha1;type:varchar(64);not null;default:''" json:"content_sha1"`
Phash sql.NullString `gorm:"column:phash;type:vector(64)" json:"phash"`
IsDuplicate uint8 `gorm:"column:is_duplicate;not null;default:0" json:"is_duplicate"`
DupOf string `gorm:"column:dup_of;type:varchar(32);not null;default:''" json:"dup_of"`

View File

@ -16,11 +16,9 @@ type BrandRunwayImage struct {
LookIndex uint32 `gorm:"column:look_index" json:"look_index"` // 细节图归属的主图序号(主图=该 look;细节图=所属主图的 look)
IsDetail uint8 `gorm:"column:is_detail" json:"is_detail"` // 0=主图/look 图(默认展示) 1=细节图
// 去重字段(精确 + 近重复 + 语义 embedding 留痕):
// content_sha1 图片内容 sha1,与存储 key 同源,空串为迁移前遗留行;
// 去重字段(近重复 + 语义 embedding 留痕):
// phash 为 dHash 的 pgvector 二进制向量(vector(64)),NULL 表示无法解码(如 webp),不参与检索;
// is_duplicate / dup_of 仅在命中近重复时标记,供人工审核留痕,不阻断入库。
ContentSha1 string `gorm:"column:content_sha1;type:varchar(64);not null;default:''" json:"content_sha1"`
Phash sql.NullString `gorm:"column:phash;type:vector(64)" json:"phash"`
IsDuplicate uint8 `gorm:"column:is_duplicate;not null;default:0" json:"is_duplicate"`
DupOf string `gorm:"column:dup_of;type:varchar(32);not null;default:''" json:"dup_of"`

View File

@ -2,10 +2,10 @@ package model
import "database/sql"
// StreetSnap 街拍主表(对应 db_dev.street_snap)。
// StreetSnap 街拍主表(对应 db_dev.street_snaps)。
//
// 字段对齐 db_dev 约定:INT UNSIGNED 主键、int unsigned 时间戳、is_deleted 软删、varchar(255)。
// 与 brand_runway 双表同构,但**无 brand_id**(街拍不关联品牌,模块自治)。
// 与 brand_runways 双表同构,但**无 brand_id**(街拍不关联品牌,模块自治)。
type StreetSnap struct {
ID uint32 `gorm:"primaryKey;column:id" json:"id"`
Title string `gorm:"column:title" json:"title"`
@ -19,7 +19,7 @@ type StreetSnap struct {
}
// TableName 指定主表名。
func (StreetSnap) TableName() string { return "street_snap" }
func (StreetSnap) TableName() string { return "street_snaps" }
// StreetSnapImage 街拍图片表(对应 db_dev.street_snap_images)。
//
@ -35,7 +35,6 @@ type StreetSnapImage struct {
UpdatedAt uint32 `gorm:"column:updated_at" json:"updated_at"`
// 去重字段:见 BrandRunwayImage 注释。phash 为 vector(64) 二进制向量(NULL=无法解码)。
ContentSha1 string `gorm:"column:content_sha1;type:varchar(64);not null;default:''" json:"content_sha1"`
Phash sql.NullString `gorm:"column:phash;type:vector(64)" json:"phash"`
IsDuplicate uint8 `gorm:"column:is_duplicate;not null;default:0" json:"is_duplicate"`
DupOf string `gorm:"column:dup_of;type:varchar(32);not null;default:''" json:"dup_of"`

View File

@ -4,10 +4,10 @@ import "database/sql"
// StreetSnapDraft 街拍草稿(爬虫入库待审核)。
//
// 与 brand_runway_draft 同构的审核流程:爬虫 worker 处理完 ingest_jobs 后写入本表并置
// pending;后台审核通过(Approve)后再晋升(SaveStreetSnapFromDraft)到 street_snap 正式表。
// 与 brand_runway_drafts 同构的审核流程:爬虫 worker 处理完 ingest_jobs 后写入本表并置
// pending;后台审核通过(Approve)后再晋升(SaveStreetSnapFromDraft)到 street_snaps 正式表。
//
// 字段对齐 street_snap 正式表(单 Title、有 City、无 brand_id、无 season 系列列),
// 字段对齐 street_snaps 正式表(单 Title、有 City、无 brand_id、无 season 系列列),
// 外加审核专用字段,便于审核页直接预览/微调。
type StreetSnapDraft struct {
ID uint32 `gorm:"primaryKey;column:id" json:"id"`
@ -26,7 +26,7 @@ type StreetSnapDraft struct {
}
// TableName 指定草稿主表名。
func (StreetSnapDraft) TableName() string { return "street_snap_draft" }
func (StreetSnapDraft) TableName() string { return "street_snap_drafts" }
// StreetSnapDraftImage 街拍草稿图片(对应 street_snap_draft_images)。
type StreetSnapDraftImage struct {
@ -40,7 +40,6 @@ type StreetSnapDraftImage struct {
UpdatedAt uint32 `gorm:"column:updated_at" json:"updated_at"`
// 去重字段:见 BrandRunwayImage 注释。phash 为 vector(64) 二进制向量(NULL=无法解码)。
ContentSha1 string `gorm:"column:content_sha1;type:varchar(64);not null;default:''" json:"content_sha1"`
Phash sql.NullString `gorm:"column:phash;type:vector(64)" json:"phash"`
IsDuplicate uint8 `gorm:"column:is_duplicate;not null;default:0" json:"is_duplicate"`
DupOf string `gorm:"column:dup_of;type:varchar(32);not null;default:''" json:"dup_of"`

View File

@ -11,7 +11,7 @@ type User struct {
Email string `gorm:"column:email;size:191;uniqueIndex" json:"email"`
PasswordHash string `gorm:"column:password_hash;size:255" json:"-"`
IsDeleted uint8 `gorm:"column:is_deleted" json:"is_deleted"`
// Tier 用户等级:free(普通/未登录)/ vip(高级/VIP)。详情页据其决定返回普通还是高清图。
// Tier 用户等级:free / vip。仅作账号标记(后台可提级 VIP);图片质量模型已统一,tier 不再影响图片 URL。
Tier string `gorm:"column:tier;size:16" json:"tier"`
}