gohorsejobs/backend/internal/services/ticket_service.go

137 lines
3.6 KiB
Go

package services
import (
"context"
"database/sql"
"errors"
"github.com/rede5/gohorsejobs/backend/internal/models"
)
type TicketService struct {
DB *sql.DB
}
func NewTicketService(db *sql.DB) *TicketService {
return &TicketService{DB: db}
}
func (s *TicketService) CreateTicket(ctx context.Context, userID int, subject, priority string) (*models.Ticket, error) {
if priority == "" {
priority = "medium"
}
query := `
INSERT INTO tickets (user_id, subject, status, priority, created_at, updated_at)
VALUES ($1, $2, 'open', $3, NOW(), NOW())
RETURNING id, user_id, subject, status, priority, created_at, updated_at
`
var t models.Ticket
err := s.DB.QueryRowContext(ctx, query, userID, subject, priority).Scan(
&t.ID, &t.UserID, &t.Subject, &t.Status, &t.Priority, &t.CreatedAt, &t.UpdatedAt,
)
if err != nil {
return nil, err
}
return &t, nil
}
func (s *TicketService) ListTickets(ctx context.Context, userID int) ([]models.Ticket, error) {
query := `
SELECT id, user_id, subject, status, priority, created_at, updated_at
FROM tickets
WHERE user_id = $1
ORDER BY updated_at DESC
`
rows, err := s.DB.QueryContext(ctx, query, userID)
if err != nil {
return nil, err
}
defer rows.Close()
var tickets []models.Ticket
for rows.Next() {
var t models.Ticket
if err := rows.Scan(
&t.ID, &t.UserID, &t.Subject, &t.Status, &t.Priority, &t.CreatedAt, &t.UpdatedAt,
); err != nil {
return nil, err
}
tickets = append(tickets, t)
}
return tickets, nil
}
func (s *TicketService) GetTicket(ctx context.Context, ticketID string, userID int) (*models.Ticket, []models.TicketMessage, error) {
// 1. Get Ticket
queryTicket := `
SELECT id, user_id, subject, status, priority, created_at, updated_at
FROM tickets
WHERE id = $1 AND user_id = $2
`
var t models.Ticket
err := s.DB.QueryRowContext(ctx, queryTicket, ticketID, userID).Scan(
&t.ID, &t.UserID, &t.Subject, &t.Status, &t.Priority, &t.CreatedAt, &t.UpdatedAt,
)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, nil, errors.New("ticket not found")
}
return nil, nil, err
}
// 2. Get Messages
queryMsgs := `
SELECT id, ticket_id, user_id, message, created_at
FROM ticket_messages
WHERE ticket_id = $1
ORDER BY created_at ASC
`
rows, err := s.DB.QueryContext(ctx, queryMsgs, ticketID)
if err != nil {
return nil, nil, err
}
defer rows.Close()
var messages []models.TicketMessage
for rows.Next() {
var m models.TicketMessage
if err := rows.Scan(
&m.ID, &m.TicketID, &m.UserID, &m.Message, &m.CreatedAt,
); err != nil {
return nil, nil, err
}
messages = append(messages, m)
}
return &t, messages, nil
}
func (s *TicketService) AddMessage(ctx context.Context, ticketID string, userID int, message string) (*models.TicketMessage, error) {
// Verify ticket ownership first (or admin access, but keeping simple for now)
var count int
err := s.DB.QueryRowContext(ctx, "SELECT COUNT(*) FROM tickets WHERE id = $1 AND user_id = $2", ticketID, userID).Scan(&count)
if err != nil {
return nil, err
}
if count == 0 {
return nil, errors.New("ticket not found")
}
query := `
INSERT INTO ticket_messages (ticket_id, user_id, message, created_at)
VALUES ($1, $2, $3, NOW())
RETURNING id, ticket_id, user_id, message, created_at
`
var m models.TicketMessage
err = s.DB.QueryRowContext(ctx, query, ticketID, userID, message).Scan(
&m.ID, &m.TicketID, &m.UserID, &m.Message, &m.CreatedAt,
)
if err != nil {
return nil, err
}
// Update ticket updated_at
_, _ = s.DB.ExecContext(ctx, "UPDATE tickets SET updated_at = NOW() WHERE id = $1", ticketID)
return &m, nil
}