update
This commit is contained in:
@ -22,10 +22,17 @@ type AdminRecordCard struct {
|
||||
Status string // pending / published / rejected
|
||||
}
|
||||
|
||||
// DupStat 某记录的图片去重统计(仅统计 is_deleted=0 的存活行)。
|
||||
type DupStat struct {
|
||||
Total int // 存活图片总数(含主图与细节图)
|
||||
Dup int // is_duplicate=1 的张数
|
||||
// AdminRecordStats 是一条记录(图集)在列表上要展示的派生统计:原图字节数之和 + 主图 / 副图行数。
|
||||
//
|
||||
// 为什么主副要分开、而不能直接用 brand_runways.image_count:
|
||||
// 那一列的口径**只数主图**(见 service.countMainImages),并驱动公开卡片的「N 张」角标
|
||||
// 与 FeaturedIDs 的 SUM(image_count) 热度,不能改。但后台「图数」列若照搬它,就会和编辑页
|
||||
// 看到的图片总数对不上(实测 image_count=49 的图集实际有 224 行:49 主图 + 175 细节图)。
|
||||
// 因此列表另取真实行数,主副分别展示。
|
||||
type AdminRecordStats struct {
|
||||
Size int64 // 原图字节数之和(image_size 列,worker 下载时写入)
|
||||
Main int // 主图行数(is_detail = 0)
|
||||
Detail int // 副图(细节图)行数(is_detail = 1)
|
||||
}
|
||||
|
||||
// AdminRepository 文章管理页所需的跨表只读查询。
|
||||
@ -36,12 +43,9 @@ type AdminRepository interface {
|
||||
ListMixed(ctx context.Context, status, kind string, page, size int) ([]AdminRecordCard, int64, error)
|
||||
// CountMixed 统计某状态 + 类型下的记录总数(用于 tab 角标)。
|
||||
CountMixed(ctx context.Context, status, kind string) (int64, error)
|
||||
// DupRateByRecords 批量统计若干记录的图片去重情况(仅存活行)。
|
||||
// 返回 owner id → {Total 存活图片总数, Dup is_duplicate=1 张数};不存在的 id 不出现在 map 中。
|
||||
DupRateByRecords(ctx context.Context, kind string, ids []uint32) (map[uint32]DupStat, error)
|
||||
// ImageSizeByRecords 批量返回若干记录的原图字节数之和(按 kind 分别查对应图片表);
|
||||
// 返回 owner id → 字节数。供文章管理混合列表展示「图集原图总大小」。
|
||||
ImageSizeByRecords(ctx context.Context, kind string, ids []uint32) (map[uint32]int64, error)
|
||||
// ImageSizeByRecords 批量返回若干记录的原图统计(字节数之和 + 主/副图行数,按 kind 分别查对应图片表);
|
||||
// 返回 owner id → 统计,不存在的 id 不出现在 map 中。供文章管理混合列表展示「图数」「原图总大小」。
|
||||
ImageSizeByRecords(ctx context.Context, kind string, ids []uint32) (map[uint32]AdminRecordStats, error)
|
||||
}
|
||||
|
||||
type adminRepository struct {
|
||||
@ -114,11 +118,13 @@ func (r *adminRepository) ListMixed(ctx context.Context, status, kind string, pa
|
||||
return cards, total, nil
|
||||
}
|
||||
|
||||
// DupRateByRecords 批量统计若干记录的图片去重情况。
|
||||
// kind=runway 查 brand_runway_images(外键 runway_id);kind=street 查 street_snap_images(外键 snap_id)。
|
||||
// 只统计 is_deleted=0 的存活行;命中阈值为 phash.DefaultThreshold(当前 4)。
|
||||
func (r *adminRepository) DupRateByRecords(ctx context.Context, kind string, ids []uint32) (map[uint32]DupStat, error) {
|
||||
out := make(map[uint32]DupStat)
|
||||
// ImageSizeByRecords 批量返回若干记录的原图统计(字节数之和 + 主/副图行数,按 kind 分别查对应图片表)。
|
||||
// 返回 owner id → 统计,不存在的 id 不出现在 map 中。
|
||||
//
|
||||
// 字节数、主图数、副图数在**同一次扫描**里算完(都只依赖同一批图片行),避免为「图数」再查一遍库。
|
||||
// 一律过滤 is_deleted = 0:软删的图不参与展示,也不该计入图数。
|
||||
func (r *adminRepository) ImageSizeByRecords(ctx context.Context, kind string, ids []uint32) (map[uint32]AdminRecordStats, error) {
|
||||
out := make(map[uint32]AdminRecordStats)
|
||||
if len(ids) == 0 {
|
||||
return out, nil
|
||||
}
|
||||
@ -132,56 +138,24 @@ func (r *adminRepository) DupRateByRecords(ctx context.Context, kind string, ids
|
||||
return out, nil
|
||||
}
|
||||
type agg struct {
|
||||
Owner uint32 `gorm:"column:owner"`
|
||||
Total int `gorm:"column:total"`
|
||||
Dup int `gorm:"column:dup"`
|
||||
Owner uint32 `gorm:"column:owner"`
|
||||
Total int64 `gorm:"column:total"`
|
||||
Main int `gorm:"column:main"`
|
||||
Detail int `gorm:"column:detail"`
|
||||
}
|
||||
var rows []agg
|
||||
if err := r.db.WithContext(ctx).
|
||||
Table(table).
|
||||
Select(fk + " AS owner, COUNT(*) AS total, COALESCE(SUM(CASE WHEN is_duplicate = 1 THEN 1 ELSE 0 END), 0) AS dup").
|
||||
Select(fk + " AS owner, COALESCE(SUM(image_size), 0) AS total, " +
|
||||
"COUNT(*) FILTER (WHERE is_detail = 0) AS main, " +
|
||||
"COUNT(*) FILTER (WHERE is_detail = 1) AS detail").
|
||||
Where(fk+" IN ? AND is_deleted = 0", ids).
|
||||
Group(fk).
|
||||
Scan(&rows).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, row := range rows {
|
||||
out[row.Owner] = DupStat{Total: row.Total, Dup: row.Dup}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ImageSizeByRecords 批量返回若干记录的原图字节数之和(按 kind 分别查对应图片表)。
|
||||
// 返回 owner id → 字节数;不存在的 id 不出现在 map 中。供文章管理混合列表展示「图集原图总大小」。
|
||||
func (r *adminRepository) ImageSizeByRecords(ctx context.Context, kind string, ids []uint32) (map[uint32]int64, error) {
|
||||
out := make(map[uint32]int64)
|
||||
if len(ids) == 0 {
|
||||
return out, nil
|
||||
}
|
||||
var table, fk string
|
||||
switch kind {
|
||||
case "runway":
|
||||
table, fk = "brand_runway_images", "runway_id"
|
||||
case "street":
|
||||
table, fk = "street_snap_images", "snap_id"
|
||||
default:
|
||||
return out, nil
|
||||
}
|
||||
type agg struct {
|
||||
Owner uint32 `gorm:"column:owner"`
|
||||
Total int64 `gorm:"column:total"`
|
||||
}
|
||||
var rows []agg
|
||||
if err := r.db.WithContext(ctx).
|
||||
Table(table).
|
||||
Select(fk + " AS owner, COALESCE(SUM(image_size), 0) AS total").
|
||||
Where(fk+" IN ? AND is_deleted = 0", ids).
|
||||
Group(fk).
|
||||
Scan(&rows).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, row := range rows {
|
||||
out[row.Owner] = row.Total
|
||||
out[row.Owner] = AdminRecordStats{Size: row.Total, Main: row.Main, Detail: row.Detail}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user