Files
gochat/internal/handler/api/v1/sla_policy_handler.go
T
2026-06-04 15:44:48 +08:00

293 lines
8.9 KiB
Go

package v1
import (
"net/http"
"strconv"
"github.com/gin-gonic/gin"
"github.com/gochat/gochat/internal/service"
applogger "github.com/gochat/gochat/pkg/logger"
"github.com/gochat/gochat/pkg/response"
)
// SlaPolicyHandler handles SLA Policy CRUD + applied SLA metrics/download.
// Reference: Chatwoot app/controllers/api/v1/sla_policies_controller.rb
type SlaPolicyHandler struct {
svc *service.SlaPolicyService
}
// NewSlaPolicyHandler creates a new SlaPolicy handler.
func NewSlaPolicyHandler(svc *service.SlaPolicyService) *SlaPolicyHandler {
return &SlaPolicyHandler{svc: svc}
}
// Create creates a new SLA policy.
// POST /api/v1/accounts/:account_id/sla_policies
func (h *SlaPolicyHandler) Create(c *gin.Context) {
accountID := getAccountID(c)
if accountID == 0 {
response.AbortWithStatusError(c, http.StatusUnauthorized, response.ErrUnauthorized, "account not identified")
return
}
var wrapper service.SlaPolicyCreateWrapper
if err := c.ShouldBindJSON(&wrapper); err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrValidation, err.Error())
return
}
req := wrapper.SlaPolicy
policy, svcErr := h.svc.Create(c.Request.Context(), accountID, &req)
if svcErr != nil {
applogger.L().Errorf("Create SLA policy for account %d: %v", accountID, svcErr)
handleServiceError(c, svcErr)
return
}
response.Created(c, policy)
}
// Get retrieves a SLA policy by ID.
// GET /api/v1/accounts/:account_id/sla_policies/:id
func (h *SlaPolicyHandler) Get(c *gin.Context) {
accountID := getAccountID(c)
if accountID == 0 {
response.AbortWithStatusError(c, http.StatusUnauthorized, response.ErrUnauthorized, "account not identified")
return
}
policyID, err := parseUintParam(c, "id")
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrValidation, "invalid policy id")
return
}
policy, svcErr := h.svc.Get(c.Request.Context(), accountID, policyID)
if svcErr != nil {
applogger.L().Errorf("Get SLA policy %d for account %d: %v", policyID, accountID, svcErr)
handleServiceError(c, svcErr)
return
}
response.OK(c, policy)
}
// List retrieves all SLA policies for an account.
// GET /api/v1/accounts/:account_id/sla_policies
func (h *SlaPolicyHandler) List(c *gin.Context) {
accountID := getAccountID(c)
if accountID == 0 {
response.AbortWithStatusError(c, http.StatusUnauthorized, response.ErrUnauthorized, "account not identified")
return
}
policies, svcErr := h.svc.List(c.Request.Context(), accountID)
if svcErr != nil {
applogger.L().Errorf("List SLA policies for account %d: %v", accountID, svcErr)
handleServiceError(c, svcErr)
return
}
response.OK(c, policies)
}
// Update updates a SLA policy.
// PUT /api/v1/accounts/:account_id/sla_policies/:id
func (h *SlaPolicyHandler) Update(c *gin.Context) {
accountID := getAccountID(c)
if accountID == 0 {
response.AbortWithStatusError(c, http.StatusUnauthorized, response.ErrUnauthorized, "account not identified")
return
}
policyID, err := parseUintParam(c, "id")
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrValidation, "invalid policy id")
return
}
var wrapper service.SlaPolicyUpdateWrapper
if err := c.ShouldBindJSON(&wrapper); err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrValidation, err.Error())
return
}
req := wrapper.SlaPolicy
policy, svcErr := h.svc.Update(c.Request.Context(), accountID, policyID, &req)
if svcErr != nil {
applogger.L().Errorf("Update SLA policy %d for account %d: %v", policyID, accountID, svcErr)
handleServiceError(c, svcErr)
return
}
response.OK(c, policy)
}
// Delete deletes a SLA policy.
// DELETE /api/v1/accounts/:account_id/sla_policies/:id
func (h *SlaPolicyHandler) Delete(c *gin.Context) {
accountID := getAccountID(c)
if accountID == 0 {
response.AbortWithStatusError(c, http.StatusUnauthorized, response.ErrUnauthorized, "account not identified")
return
}
policyID, err := parseUintParam(c, "id")
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrValidation, "invalid policy id")
return
}
svcErr := h.svc.Delete(c.Request.Context(), accountID, policyID)
if svcErr != nil {
applogger.L().Errorf("Delete SLA policy %d for account %d: %v", policyID, accountID, svcErr)
handleServiceError(c, svcErr)
return
}
// Chatwoot returns head :ok (200) on destroy — not 204 NoContent
c.JSON(http.StatusOK, gin.H{"success": true})
}
// GetAppliedSlaMetrics retrieves SLA metrics for a conversation.
// GET /api/v1/accounts/:account_id/applied_slas/metrics
func (h *SlaPolicyHandler) GetAppliedSlaMetrics(c *gin.Context) {
accountID := getAccountID(c)
if accountID == 0 {
response.AbortWithStatusError(c, http.StatusUnauthorized, response.ErrUnauthorized, "account not identified")
return
}
conversationIDStr := c.Query("conversation_id")
if conversationIDStr == "" {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrValidation, "conversation_id is required")
return
}
conversationID, err := strconv.ParseUint(conversationIDStr, 10, 32)
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrValidation, "invalid conversation_id")
return
}
applied, events, svcErr := h.svc.GetAppliedSlaMetrics(c.Request.Context(), accountID, uint(conversationID))
if svcErr != nil {
applogger.L().Errorf("Get SLA metrics for account %d, conversation %d: %v", accountID, conversationID, svcErr)
handleServiceError(c, svcErr)
return
}
response.OK(c, gin.H{
"applied_sla": applied,
"sla_events": events,
})
}
// GetAppliedSlaDownload retrieves all applied SLAs for export.
// GET /api/v1/accounts/:account_id/applied_slas/download
func (h *SlaPolicyHandler) GetAppliedSlaDownload(c *gin.Context) {
accountID := getAccountID(c)
if accountID == 0 {
response.AbortWithStatusError(c, http.StatusUnauthorized, response.ErrUnauthorized, "account not identified")
return
}
applied, svcErr := h.svc.GetAppliedSlaDownload(c.Request.Context(), accountID)
if svcErr != nil {
applogger.L().Errorf("Get SLA download for account %d: %v", accountID, svcErr)
handleServiceError(c, svcErr)
return
}
response.OK(c, applied)
}
// ListInboxes retrieves all inboxes associated with a SLA policy.
// GET /api/v1/accounts/:account_id/sla_policies/:id/inboxes
func (h *SlaPolicyHandler) ListInboxes(c *gin.Context) {
accountID := getAccountID(c)
if accountID == 0 {
response.AbortWithStatusError(c, http.StatusUnauthorized, response.ErrUnauthorized, "account not identified")
return
}
policyID, err := parseUintParam(c, "id")
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrValidation, "invalid policy id")
return
}
inboxes, svcErr := h.svc.ListInboxes(c.Request.Context(), accountID, policyID)
if svcErr != nil {
applogger.L().Errorf("List inboxes for SLA policy %d, account %d: %v", policyID, accountID, svcErr)
handleServiceError(c, svcErr)
return
}
response.OK(c, inboxes)
}
// AddInbox associates an inbox with a SLA policy.
// POST /api/v1/accounts/:account_id/sla_policies/:id/inboxes
func (h *SlaPolicyHandler) AddInbox(c *gin.Context) {
accountID := getAccountID(c)
if accountID == 0 {
response.AbortWithStatusError(c, http.StatusUnauthorized, response.ErrUnauthorized, "account not identified")
return
}
policyID, err := parseUintParam(c, "id")
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrValidation, "invalid policy id")
return
}
var req struct {
InboxID uint `json:"inbox_id" validate:"required"`
}
if err := c.ShouldBindJSON(&req); err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrValidation, err.Error())
return
}
spi, svcErr := h.svc.AddInbox(c.Request.Context(), accountID, policyID, req.InboxID)
if svcErr != nil {
applogger.L().Errorf("Add inbox %d to SLA policy %d, account %d: %v", req.InboxID, policyID, accountID, svcErr)
handleServiceError(c, svcErr)
return
}
response.Created(c, spi)
}
// RemoveInbox removes an inbox association from a SLA policy.
// DELETE /api/v1/accounts/:account_id/sla_policies/:id/inboxes/:inbox_id
func (h *SlaPolicyHandler) RemoveInbox(c *gin.Context) {
accountID := getAccountID(c)
if accountID == 0 {
response.AbortWithStatusError(c, http.StatusUnauthorized, response.ErrUnauthorized, "account not identified")
return
}
policyID, err := parseUintParam(c, "id")
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrValidation, "invalid policy id")
return
}
inboxID, err := parseUintParam(c, "inbox_id")
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrValidation, "invalid inbox id")
return
}
svcErr := h.svc.RemoveInbox(c.Request.Context(), accountID, policyID, inboxID)
if svcErr != nil {
applogger.L().Errorf("Remove inbox %d from SLA policy %d, account %d: %v", inboxID, policyID, accountID, svcErr)
handleServiceError(c, svcErr)
return
}
response.NoContent(c)
}