feat(ops): ECR-007 行为分析与 ECR-008 内容运营后台
ci / h5 (push) Canceled after 0s
ci / api (push) Canceled after 0s
ci / ess-docs (push) Canceled after 0s

落地埋点 ingest/数据看板、首页宫格 CMS 与测评上下架;含账号引导、问答流式与免责声明去重,以及 review P1 审计同事务修复。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
jackyu66git
2026-08-07 02:26:16 +08:00
co-authored by Cursor
parent 7e9023f0a8
commit 7ab9add5dd
132 changed files with 8276 additions and 491 deletions
+75 -4
View File
@@ -5,6 +5,7 @@ import (
"encoding/json"
"github.com/google/uuid"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"
)
@@ -62,13 +63,13 @@ func (r *ScaleRepo) ListPublished(ctx context.Context) ([]ScaleListItem, error)
return out, rows.Err()
}
// GetBySlug loads scale with questions.
// GetBySlug loads a published scale with questions.
func (r *ScaleRepo) GetBySlug(ctx context.Context, slug string) (*ScaleDetail, error) {
d := &ScaleDetail{Slug: slug}
var scaleID uuid.UUID
err := r.Pool.QueryRow(ctx, `
SELECT id, title, description FROM scales
WHERE slug=$1 AND deleted_at IS NULL`, slug,
WHERE slug=$1 AND status='published' AND deleted_at IS NULL`, slug,
).Scan(&scaleID, &d.Title, &d.Description)
if err != nil {
return nil, err
@@ -90,6 +91,75 @@ func (r *ScaleRepo) GetBySlug(ctx context.Context, slug string) (*ScaleDetail, e
return d, rows.Err()
}
// ScaleAdminItem is a scale row for ops.
type ScaleAdminItem struct {
ID uuid.UUID `json:"id"`
Slug string `json:"slug"`
Title string `json:"title"`
Description string `json:"description"`
Status string `json:"status"`
}
// ListAllAdmin returns all non-deleted scales.
func (r *ScaleRepo) ListAllAdmin(ctx context.Context) ([]ScaleAdminItem, error) {
rows, err := r.Pool.Query(ctx, `
SELECT id, slug, title, description, status FROM scales
WHERE deleted_at IS NULL ORDER BY created_at`)
if err != nil {
return nil, err
}
defer rows.Close()
var out []ScaleAdminItem
for rows.Next() {
var it ScaleAdminItem
if err := rows.Scan(&it.ID, &it.Slug, &it.Title, &it.Description, &it.Status); err != nil {
return nil, err
}
out = append(out, it)
}
return out, rows.Err()
}
// UpdateStatus sets published|draft.
func (r *ScaleRepo) UpdateStatus(ctx context.Context, id uuid.UUID, status string) error {
return r.UpdateStatusWithAudit(ctx, id, status, uuid.Nil, nil)
}
// UpdateStatusWithAudit updates status and optionally writes audit in one tx.
func (r *ScaleRepo) UpdateStatusWithAudit(
ctx context.Context,
id uuid.UUID,
status string,
adminID uuid.UUID,
meta json.RawMessage,
) error {
tx, err := r.Pool.Begin(ctx)
if err != nil {
return err
}
defer tx.Rollback(ctx)
tag, err := tx.Exec(ctx, `
UPDATE scales SET status=$2, updated_at=now()
WHERE id=$1 AND deleted_at IS NULL`, id, status)
if err != nil {
return err
}
if tag.RowsAffected() == 0 {
return pgx.ErrNoRows
}
if adminID != uuid.Nil {
if meta == nil {
meta = json.RawMessage(`{}`)
}
if _, err := tx.Exec(ctx, `
INSERT INTO admin_audit_logs(admin_id, action, target_type, target_id, meta)
VALUES ($1,'scale.status','scale',$2,$3)`, adminID, id.String(), meta); err != nil {
return err
}
}
return tx.Commit(ctx)
}
// SaveResult stores scoring output.
func (r *ScaleRepo) SaveResult(ctx context.Context, userID, scaleID, profileID uuid.UUID, answers, result json.RawMessage) (uuid.UUID, error) {
var id uuid.UUID
@@ -101,10 +171,11 @@ func (r *ScaleRepo) SaveResult(ctx context.Context, userID, scaleID, profileID u
return id, err
}
// ScaleIDBySlug resolves id.
// ScaleIDBySlug resolves id for a published scale.
func (r *ScaleRepo) ScaleIDBySlug(ctx context.Context, slug string) (uuid.UUID, error) {
var id uuid.UUID
err := r.Pool.QueryRow(ctx, `
SELECT id FROM scales WHERE slug=$1 AND deleted_at IS NULL`, slug).Scan(&id)
SELECT id FROM scales
WHERE slug=$1 AND status='published' AND deleted_at IS NULL`, slug).Scan(&id)
return id, err
}