129 lines
3.7 KiB
Go
129 lines
3.7 KiB
Go
package v1
|
|
|
|
import (
|
|
"encoding/csv"
|
|
"fmt"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/gochat/gochat/internal/service"
|
|
"github.com/gochat/gochat/pkg/response"
|
|
)
|
|
|
|
// CsatMetricsHandler handles dedicated CSAT metrics and CSV download endpoints.
|
|
// Reference: Chatwoot csat_report controller — enterprise metrics dashboard + export
|
|
type CsatMetricsHandler struct {
|
|
svc *service.CsatMetricsService
|
|
}
|
|
|
|
// NewCsatMetricsHandler creates a new CsatMetricsHandler.
|
|
func NewCsatMetricsHandler(svc *service.CsatMetricsService) *CsatMetricsHandler {
|
|
return &CsatMetricsHandler{svc: svc}
|
|
}
|
|
|
|
// Metrics returns aggregated CSAT metrics (average rating, response rate, trend).
|
|
// GET /api/v1/accounts/:account_id/csat_survey_responses/metrics?since=...&until=...
|
|
func (h *CsatMetricsHandler) Metrics(c *gin.Context) {
|
|
accountID := getAccountID(c)
|
|
if accountID == 0 {
|
|
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "account_id is required")
|
|
return
|
|
}
|
|
|
|
since, until := parseSinceUntilQuery(c)
|
|
|
|
report, err := h.svc.GetMetrics(c.Request.Context(), accountID, since, until)
|
|
if err != nil {
|
|
handleServiceError(c, err)
|
|
return
|
|
}
|
|
|
|
response.OK(c, report)
|
|
}
|
|
|
|
// Download generates a CSV file of CSAT responses and streams it back.
|
|
// GET /api/v1/accounts/:account_id/csat_survey_responses/download?since=...&until=...
|
|
func (h *CsatMetricsHandler) Download(c *gin.Context) {
|
|
accountID := getAccountID(c)
|
|
if accountID == 0 {
|
|
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "account_id is required")
|
|
return
|
|
}
|
|
|
|
since, until := parseSinceUntilQuery(c)
|
|
|
|
rows, err := h.svc.ExportCSV(c.Request.Context(), accountID, since, until)
|
|
if err != nil {
|
|
handleServiceError(c, err)
|
|
return
|
|
}
|
|
|
|
// Set CSV response headers
|
|
filename := fmt.Sprintf("csat_metrics_account_%d.csv", accountID)
|
|
c.Header("Content-Type", "text/csv")
|
|
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename))
|
|
|
|
// Write CSV header + rows directly to the response writer
|
|
writer := csv.NewWriter(c.Writer)
|
|
|
|
// CSV header row
|
|
header := []string{"conversation_id", "contact_id", "assigned_agent_id", "rating", "feedback_message", "created_at"}
|
|
if err := writer.Write(header); err != nil {
|
|
response.AbortWithStatusError(c, http.StatusInternalServerError, response.ErrInternal, "failed to write CSV header")
|
|
return
|
|
}
|
|
|
|
// CSV data rows
|
|
for _, row := range rows {
|
|
record := []string{
|
|
row.ConversationID,
|
|
row.ContactID,
|
|
row.AssignedAgentID,
|
|
row.Rating,
|
|
row.FeedbackMessage,
|
|
row.CreatedAt,
|
|
}
|
|
if err := writer.Write(record); err != nil {
|
|
response.AbortWithStatusError(c, http.StatusInternalServerError, response.ErrInternal, "failed to write CSV row")
|
|
return
|
|
}
|
|
}
|
|
|
|
writer.Flush()
|
|
if err := writer.Error(); err != nil {
|
|
response.AbortWithStatusError(c, http.StatusInternalServerError, response.ErrInternal, "CSV flush error")
|
|
return
|
|
}
|
|
}
|
|
|
|
// parseSinceUntilQuery extracts optional since/until time.Time pointers from query params.
|
|
func parseSinceUntilQuery(c *gin.Context) (*time.Time, *time.Time) {
|
|
var since, until *time.Time
|
|
|
|
if v := c.Query("since"); v != "" {
|
|
t, err := time.Parse(time.RFC3339, v)
|
|
if err == nil {
|
|
since = &t
|
|
}
|
|
}
|
|
|
|
if v := c.Query("until"); v != "" {
|
|
t, err := time.Parse(time.RFC3339, v)
|
|
if err == nil {
|
|
until = &t
|
|
}
|
|
}
|
|
|
|
return since, until
|
|
}
|
|
|
|
// RegisterCsatMetricsRoutes registers CSAT metrics routes on the given router group.
|
|
// Reference: Chatwoot GET /csat_survey_responses/metrics + /csat_survey_responses/download
|
|
func RegisterCsatMetricsRoutes(g *gin.RouterGroup, h *CsatMetricsHandler) {
|
|
responses := g.Group("/csat_survey_responses")
|
|
{
|
|
responses.GET("/metrics", h.Metrics)
|
|
responses.GET("/download", h.Download)
|
|
}
|
|
} |