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() }