package store import ( "context" "database/sql" "errors" "fmt" "strings" "github.com/google/uuid" "github.com/jmoiron/sqlx" "git.hangman-lab.top/hzhang/Dialectic.Backend/internal/models" ) var ErrNotFound = errors.New("not found") type TopicStore struct { db *sqlx.DB } func NewTopicStore(db *sqlx.DB) *TopicStore { return &TopicStore{db: db} } type CreateTopicInput struct { Title string Summary string Visibility models.Visibility VerdictSchemaID string SignupOpenAt string // RFC3339; parsed by SQL SignupCloseAt string DebateStartAt string DebateEndAt string CreatorUserID string } func (s *TopicStore) Create(ctx context.Context, in CreateTopicInput) (*models.Topic, error) { id := uuid.NewString() _, err := s.db.ExecContext(ctx, ` INSERT INTO topics (id, title, summary, visibility, verdict_schema_id, signup_open_at, signup_close_at, debate_start_at, debate_end_at, creator_user_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, id, in.Title, in.Summary, in.Visibility, in.VerdictSchemaID, in.SignupOpenAt, in.SignupCloseAt, in.DebateStartAt, in.DebateEndAt, in.CreatorUserID) if err != nil { return nil, fmt.Errorf("insert topic: %w", err) } return s.GetByID(ctx, id) } func (s *TopicStore) GetByID(ctx context.Context, id string) (*models.Topic, error) { var t models.Topic err := s.db.GetContext(ctx, &t, `SELECT * FROM topics WHERE id = ?`, id) if errors.Is(err, sql.ErrNoRows) { return nil, ErrNotFound } if err != nil { return nil, err } return &t, nil } type ListFilter struct { Status string // empty = all Visibility string // empty = all Limit int // 0 = default 50 Offset int } func (s *TopicStore) List(ctx context.Context, f ListFilter) ([]models.Topic, error) { if f.Limit <= 0 || f.Limit > 200 { f.Limit = 50 } q := "SELECT * FROM topics" args := []any{} var clauses []string if f.Status != "" { clauses = append(clauses, "status = ?") args = append(args, f.Status) } if f.Visibility != "" { clauses = append(clauses, "visibility = ?") args = append(args, f.Visibility) } if len(clauses) > 0 { q += " WHERE " + strings.Join(clauses, " AND ") } q += " ORDER BY created_at DESC LIMIT ? OFFSET ?" args = append(args, f.Limit, f.Offset) var rows []models.Topic if err := s.db.SelectContext(ctx, &rows, q, args...); err != nil { return nil, err } return rows, nil } // SetVisibility flips public/private; records who/when. Returns updated row. func (s *TopicStore) SetVisibility(ctx context.Context, id string, v models.Visibility, byUserID string) (*models.Topic, error) { _, err := s.db.ExecContext(ctx, ` UPDATE topics SET visibility = ?, visibility_changed_by = ?, visibility_changed_at = CURRENT_TIMESTAMP WHERE id = ?`, v, byUserID, id) if err != nil { return nil, err } return s.GetByID(ctx, id) }