package store import ( "context" "database/sql" "errors" "time" "github.com/google/uuid" "github.com/jmoiron/sqlx" ) type Round struct { ID string `db:"id" json:"id"` TopicID string `db:"topic_id" json:"topic_id"` RoundNo int `db:"round_no" json:"round_no"` OpenedAt time.Time `db:"opened_at" json:"opened_at"` ClosedAt *time.Time `db:"closed_at" json:"closed_at,omitempty"` } type RoundStore struct { db *sqlx.DB } func NewRoundStore(db *sqlx.DB) *RoundStore { return &RoundStore{db: db} } // Open creates the round-0 row for a topic entering `debating`. Subsequent // rounds (1, 2, ...) are inserted by the round driver as the debate // advances; we leave round-bumping logic outside the store so the policy // (time-based? all-participants-posted?) can evolve without DB churn. func (s *RoundStore) Open(ctx context.Context, topicID string, roundNo int) (*Round, error) { id := uuid.NewString() if _, err := s.db.ExecContext(ctx, `INSERT INTO rounds (id, topic_id, round_no) VALUES (?, ?, ?)`, id, topicID, roundNo); err != nil { return nil, err } var r Round if err := s.db.GetContext(ctx, &r, `SELECT * FROM rounds WHERE id = ?`, id); err != nil { return nil, err } return &r, nil } func (s *RoundStore) Latest(ctx context.Context, topicID string) (*Round, error) { var r Round err := s.db.GetContext(ctx, &r, `SELECT * FROM rounds WHERE topic_id = ? ORDER BY round_no DESC LIMIT 1`, topicID) if errors.Is(err, sql.ErrNoRows) { return nil, ErrNotFound } if err != nil { return nil, err } return &r, nil } func (s *RoundStore) ListByTopic(ctx context.Context, topicID string) ([]Round, error) { var rows []Round if err := s.db.SelectContext(ctx, &rows, `SELECT * FROM rounds WHERE topic_id = ? ORDER BY round_no ASC`, topicID); err != nil { return nil, err } return rows, nil }