This commit is contained in:
lfirmin
2026-05-02 03:23:51 +02:00
parent aeb9b3d96f
commit 181b8ed454
12 changed files with 508 additions and 73 deletions
+3
View File
@@ -60,6 +60,9 @@ func migrate(db *sql.DB) error {
count INTEGER NOT NULL DEFAULT 0
);
INSERT OR IGNORE INTO convocation_counter(id,count) VALUES(1,0);`,
// v2: user-supplied ticket title and description (from modal)
`ALTER TABLE tickets ADD COLUMN ticket_title TEXT;
ALTER TABLE tickets ADD COLUMN ticket_description TEXT;`,
}
for i, m := range migrations {
+41 -24
View File
@@ -8,20 +8,22 @@ import (
)
type Ticket struct {
ID int64
TicketNumber int
UserID string
Panel string
Type string
ChannelID string
OpenedAt time.Time
ClaimedBy sql.NullString
ClaimedAt sql.NullTime
ClosedAt sql.NullTime
ClosedBy sql.NullString
Reason sql.NullString
TranscriptPath sql.NullString
Status string
ID int64
TicketNumber int
UserID string
Panel string
Type string
ChannelID string
OpenedAt time.Time
ClaimedBy sql.NullString
ClaimedAt sql.NullTime
ClosedAt sql.NullTime
ClosedBy sql.NullString
Reason sql.NullString
TranscriptPath sql.NullString
Status string
TicketTitle sql.NullString
TicketDescription sql.NullString
}
type TicketRepo struct{ db *sql.DB }
@@ -51,9 +53,10 @@ func (r *TicketRepo) Insert(ctx context.Context, t *Ticket) error {
t.TicketNumber = n
res, err := tx.ExecContext(ctx, `
INSERT INTO tickets(ticket_number,user_id,panel,type,channel_id,opened_at,status)
VALUES(?,?,?,?,?,?,?)`,
INSERT INTO tickets(ticket_number,user_id,panel,type,channel_id,opened_at,status,ticket_title,ticket_description)
VALUES(?,?,?,?,?,?,?,?,?)`,
n, t.UserID, t.Panel, t.Type, t.ChannelID, t.OpenedAt.UTC(), "open",
t.TicketTitle, t.TicketDescription,
)
if err != nil {
return fmt.Errorf("insert ticket: %w", err)
@@ -66,9 +69,10 @@ func (r *TicketRepo) Insert(ctx context.Context, t *Ticket) error {
// InsertWithNumber inserts a ticket where TicketNumber is already set (e.g. convocations).
func (r *TicketRepo) InsertWithNumber(ctx context.Context, t *Ticket) error {
res, err := r.db.ExecContext(ctx, `
INSERT INTO tickets(ticket_number,user_id,panel,type,channel_id,opened_at,status)
VALUES(?,?,?,?,?,?,?)`,
INSERT INTO tickets(ticket_number,user_id,panel,type,channel_id,opened_at,status,ticket_title,ticket_description)
VALUES(?,?,?,?,?,?,?,?,?)`,
t.TicketNumber, t.UserID, t.Panel, t.Type, t.ChannelID, t.OpenedAt.UTC(), "open",
t.TicketTitle, t.TicketDescription,
)
if err != nil {
return fmt.Errorf("insert ticket with number: %w", err)
@@ -81,7 +85,7 @@ func (r *TicketRepo) InsertWithNumber(ctx context.Context, t *Ticket) error {
func (r *TicketRepo) GetByChannelID(ctx context.Context, channelID string) (*Ticket, error) {
row := r.db.QueryRowContext(ctx, `
SELECT id,ticket_number,user_id,panel,type,channel_id,opened_at,
claimed_by,claimed_at,closed_at,closed_by,reason,transcript_path,status
claimed_by,claimed_at,closed_at,closed_by,reason,transcript_path,status,ticket_title,ticket_description
FROM tickets WHERE channel_id=?`, channelID)
return scanTicket(row)
}
@@ -89,7 +93,7 @@ func (r *TicketRepo) GetByChannelID(ctx context.Context, channelID string) (*Tic
func (r *TicketRepo) GetByID(ctx context.Context, id int64) (*Ticket, error) {
row := r.db.QueryRowContext(ctx, `
SELECT id,ticket_number,user_id,panel,type,channel_id,opened_at,
claimed_by,claimed_at,closed_at,closed_by,reason,transcript_path,status
claimed_by,claimed_at,closed_at,closed_by,reason,transcript_path,status,ticket_title,ticket_description
FROM tickets WHERE id=?`, id)
return scanTicket(row)
}
@@ -97,7 +101,7 @@ func (r *TicketRepo) GetByID(ctx context.Context, id int64) (*Ticket, error) {
func (r *TicketRepo) HasOpenTicket(ctx context.Context, userID, ticketType string) (*Ticket, error) {
row := r.db.QueryRowContext(ctx, `
SELECT id,ticket_number,user_id,panel,type,channel_id,opened_at,
claimed_by,claimed_at,closed_at,closed_by,reason,transcript_path,status
claimed_by,claimed_at,closed_at,closed_by,reason,transcript_path,status,ticket_title,ticket_description
FROM tickets WHERE user_id=? AND type=? AND status IN('open','claimed')
LIMIT 1`, userID, ticketType)
t, err := scanTicket(row)
@@ -133,7 +137,7 @@ func (r *TicketRepo) SetClosedByChannel(ctx context.Context, channelID, reason s
func (r *TicketRepo) ListOpen(ctx context.Context) ([]*Ticket, error) {
rows, err := r.db.QueryContext(ctx, `
SELECT id,ticket_number,user_id,panel,type,channel_id,opened_at,
claimed_by,claimed_at,closed_at,closed_by,reason,transcript_path,status
claimed_by,claimed_at,closed_at,closed_by,reason,transcript_path,status,ticket_title,ticket_description
FROM tickets WHERE status='open'`)
if err != nil {
return nil, err
@@ -160,11 +164,24 @@ func (r *TicketRepo) NextConvocationNumber(ctx context.Context) (int, error) {
return n, tx.Commit()
}
// NullStr returns a valid NullString for non-empty strings, invalid (NULL) for empty.
func NullStr(s string) sql.NullString {
return sql.NullString{String: s, Valid: s != ""}
}
// SetTitleDescription updates the title and description of a ticket after modal submission.
func (r *TicketRepo) SetTitleDescription(ctx context.Context, id int64, title, description string) error {
_, err := r.db.ExecContext(ctx,
`UPDATE tickets SET ticket_title=?, ticket_description=? WHERE id=?`,
title, description, id)
return err
}
func scanTicket(row *sql.Row) (*Ticket, error) {
var t Ticket
err := row.Scan(&t.ID, &t.TicketNumber, &t.UserID, &t.Panel, &t.Type, &t.ChannelID,
&t.OpenedAt, &t.ClaimedBy, &t.ClaimedAt, &t.ClosedAt, &t.ClosedBy,
&t.Reason, &t.TranscriptPath, &t.Status)
&t.Reason, &t.TranscriptPath, &t.Status, &t.TicketTitle, &t.TicketDescription)
if err != nil {
return nil, err
}
@@ -177,7 +194,7 @@ func scanTickets(rows *sql.Rows) ([]*Ticket, error) {
var t Ticket
if err := rows.Scan(&t.ID, &t.TicketNumber, &t.UserID, &t.Panel, &t.Type, &t.ChannelID,
&t.OpenedAt, &t.ClaimedBy, &t.ClaimedAt, &t.ClosedAt, &t.ClosedBy,
&t.Reason, &t.TranscriptPath, &t.Status); err != nil {
&t.Reason, &t.TranscriptPath, &t.Status, &t.TicketTitle, &t.TicketDescription); err != nil {
return nil, err
}
result = append(result, &t)