Files
goods/api/internal/adminstore/stats.go
T
sulaimaannaasif6866 f382c27200
CI / Go (api) (pull_request) Successful in 13s
CI / Python (ingestion) (pull_request) Successful in 9s
CI / Migrations (postgres) (pull_request) Successful in 14s
feat: 数据概览/操作日志/批量操作 + 首页合格档案数
后台新增「数据概览」(商品/合格/按状态/品牌/分类/待审核) 与「操作日志」(全局审计分页);商品列表支持多选批量改状态/分类。公开首页标题改为「天工」并展示合格档案数;新增公开接口 /api/v1/stats 与后台 /api/stats、/api/audit、/api/products/bulk。合格口径=quality_score≥0.6 且在用。

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-06-21 01:45:28 +00:00

62 lines
1.9 KiB
Go

package adminstore
import "context"
// QualifiedMinScore is the quality_score threshold at or above which a product
// counts as "qualified" (合格) in admin and public stats.
const QualifiedMinScore = 0.6
// AdminStats summarizes the catalog for the admin overview dashboard.
type AdminStats struct {
Products int `json:"products"`
Qualified int `json:"qualified"`
MinScore float64 `json:"min_score"`
ByStatus map[string]int `json:"by_status"`
Brands int `json:"brands"`
Categories int `json:"categories"`
Pending int `json:"pending_submissions"`
AvgQuality float64 `json:"avg_quality"`
}
// Stats gathers the dashboard counters in a handful of aggregate queries.
func (s *Store) Stats(ctx context.Context) (*AdminStats, error) {
out := &AdminStats{MinScore: QualifiedMinScore, ByStatus: map[string]int{}}
if err := s.pool.QueryRow(ctx, `
SELECT count(*),
count(*) FILTER (WHERE quality_score >= $1 AND status = 'active'),
COALESCE(avg(quality_score), 0)
FROM product`, QualifiedMinScore).Scan(&out.Products, &out.Qualified, &out.AvgQuality); err != nil {
return nil, err
}
rows, err := s.pool.Query(ctx, "SELECT status, count(*) FROM product GROUP BY status")
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
var st string
var n int
if err := rows.Scan(&st, &n); err != nil {
return nil, err
}
out.ByStatus[st] = n
}
if err := rows.Err(); err != nil {
return nil, err
}
if err := s.pool.QueryRow(ctx, "SELECT count(*) FROM brand").Scan(&out.Brands); err != nil {
return nil, err
}
if err := s.pool.QueryRow(ctx, "SELECT count(*) FROM category").Scan(&out.Categories); err != nil {
return nil, err
}
if err := s.pool.QueryRow(ctx,
"SELECT count(*) FROM submission WHERE status = 'pending'").Scan(&out.Pending); err != nil {
return nil, err
}
return out, nil
}