Files
goods/api/internal/adminstore/bulk.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

81 lines
2.2 KiB
Go

package adminstore
import (
"context"
"errors"
)
// Bulk-operation errors.
var (
// ErrNoTargets is returned when a bulk request selects no products.
ErrNoTargets = errors.New("no products selected")
// ErrInvalidStatus is returned for an unknown product status value.
ErrInvalidStatus = errors.New("invalid status")
)
var validStatus = map[string]bool{"active": true, "merged": true, "deprecated": true}
// BulkSetStatus updates the status of every selected product in one statement.
func (s *Store) BulkSetStatus(ctx context.Context, actor string, ids []string, status string) (int, error) {
if len(ids) == 0 {
return 0, ErrNoTargets
}
if !validStatus[status] {
return 0, ErrInvalidStatus
}
ct, err := s.pool.Exec(ctx,
"UPDATE product SET status = $1 WHERE id = ANY($2)", status, ids)
if err != nil {
return 0, err
}
n := int(ct.RowsAffected())
_ = s.writeAudit(ctx, actor, "bulk_status", "product", nil,
[]string{"status"}, map[string]any{"ids": ids}, map[string]any{"status": status})
return n, nil
}
// BulkSetCategory reassigns the category of every selected product, syncing the
// GPC brick code and recomputing quality for each one.
func (s *Store) BulkSetCategory(ctx context.Context, actor string, ids []string, categoryID *string) (int, error) {
if len(ids) == 0 {
return 0, ErrNoTargets
}
tx, err := s.pool.Begin(ctx)
if err != nil {
return 0, err
}
defer tx.Rollback(ctx)
var gpc *string
if categoryID != nil && *categoryID != "" {
if err := tx.QueryRow(ctx, "SELECT gpc_brick_code FROM category WHERE id = $1", *categoryID).Scan(&gpc); err != nil {
return 0, ErrInvalidParent
}
} else {
categoryID = nil
}
ct, err := tx.Exec(ctx,
"UPDATE product SET category_id = $1, gpc_brick_code = $2 WHERE id = ANY($3)",
categoryID, gpc, ids)
if err != nil {
return 0, err
}
for _, id := range ids {
if _, err := s.recomputeQualityTx(ctx, tx, id); err != nil {
return 0, err
}
}
if err := tx.Commit(ctx); err != nil {
return 0, err
}
n := int(ct.RowsAffected())
cat := ""
if categoryID != nil {
cat = *categoryID
}
_ = s.writeAudit(ctx, actor, "bulk_category", "product", nil,
[]string{"category"}, map[string]any{"ids": ids}, map[string]any{"category_id": cat})
return n, nil
}