Files
digital-psychology/apps/api/internal/repository/ask_ops_repo.go
T
jackyu66gitandCursor 00a798bd85 feat(ECR-017): AskOperations 问答会话只读并 Closed
新增 admin.ask.read、GET /admin/ask/threads*(AskSessionView)与 admin-h5「问答」页;禁改消息/UGC/真支付。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-07 18:39:37 +08:00

105 lines
3.0 KiB
Go

package repository
import (
"context"
"errors"
"time"
"github.com/google/uuid"
"github.com/jackc/pgx/v5"
)
// AskSessionView is ops read meta for one ask thread.
type AskSessionView struct {
ID uuid.UUID `json:"id"`
UserID uuid.UUID `json:"user_id"`
ProfileID uuid.UUID `json:"profile_id"`
Scene *string `json:"scene,omitempty"`
MessageCount int `json:"message_count"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// AskMessageView is a read-only message row for ops.
type AskMessageView struct {
ID uuid.UUID `json:"id"`
Role string `json:"role"`
Content string `json:"content"`
CreatedAt time.Time `json:"created_at"`
}
// ListAskSessions returns recent ask threads (optional user filter).
func (r *AdminRepo) ListAskSessions(ctx context.Context, userID *uuid.UUID, limit, offset int) ([]AskSessionView, error) {
if limit <= 0 || limit > 100 {
limit = 20
}
if offset < 0 {
offset = 0
}
rows, err := r.Pool.Query(ctx, `
SELECT t.id, t.user_id, t.profile_id, t.scene, t.created_at, t.updated_at,
(SELECT count(*)::int FROM ask_messages m
WHERE m.thread_id=t.id AND m.deleted_at IS NULL) AS message_count
FROM ask_threads t
WHERE t.deleted_at IS NULL
AND ($1::uuid IS NULL OR t.user_id=$1)
ORDER BY t.updated_at DESC
LIMIT $2 OFFSET $3`, userID, limit, offset)
if err != nil {
return nil, err
}
defer rows.Close()
var out []AskSessionView
for rows.Next() {
var s AskSessionView
if err := rows.Scan(
&s.ID, &s.UserID, &s.ProfileID, &s.Scene, &s.CreatedAt, &s.UpdatedAt, &s.MessageCount,
); err != nil {
return nil, err
}
out = append(out, s)
}
return out, rows.Err()
}
// GetAskSession loads one thread meta or ErrNoRows.
func (r *AdminRepo) GetAskSession(ctx context.Context, threadID uuid.UUID) (*AskSessionView, error) {
var s AskSessionView
err := r.Pool.QueryRow(ctx, `
SELECT t.id, t.user_id, t.profile_id, t.scene, t.created_at, t.updated_at,
(SELECT count(*)::int FROM ask_messages m
WHERE m.thread_id=t.id AND m.deleted_at IS NULL) AS message_count
FROM ask_threads t
WHERE t.id=$1 AND t.deleted_at IS NULL`, threadID,
).Scan(&s.ID, &s.UserID, &s.ProfileID, &s.Scene, &s.CreatedAt, &s.UpdatedAt, &s.MessageCount)
if errors.Is(err, pgx.ErrNoRows) {
return nil, err
}
if err != nil {
return nil, err
}
return &s, nil
}
// ListAskMessagesForAdmin returns messages oldest-first.
func (r *AdminRepo) ListAskMessagesForAdmin(ctx context.Context, threadID uuid.UUID) ([]AskMessageView, error) {
rows, err := r.Pool.Query(ctx, `
SELECT id, role, content, created_at
FROM ask_messages
WHERE thread_id=$1 AND deleted_at IS NULL
ORDER BY created_at ASC`, threadID)
if err != nil {
return nil, err
}
defer rows.Close()
var out []AskMessageView
for rows.Next() {
var m AskMessageView
if err := rows.Scan(&m.ID, &m.Role, &m.Content, &m.CreatedAt); err != nil {
return nil, err
}
out = append(out, m)
}
return out, rows.Err()
}