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

255 lines
8.7 KiB
Go

package v1
import (
"net/http"
"strconv"
"github.com/gin-gonic/gin"
"github.com/gochat/gochat/internal/model"
"github.com/gochat/gochat/internal/service"
applogger "github.com/gochat/gochat/pkg/logger"
"github.com/gochat/gochat/pkg/pagination"
"github.com/gochat/gochat/pkg/response"
)
// CaptainAssistantHandler handles CaptainAssistant REST API endpoints.
// Reference: Chatwoot enterprise/app/controllers/api/v1/captain/assistants_controller.rb
type CaptainAssistantHandler struct {
svc *service.CaptainAssistantService
}
// NewCaptainAssistantHandler creates a new CaptainAssistantHandler.
func NewCaptainAssistantHandler(svc *service.CaptainAssistantService) *CaptainAssistantHandler {
return &CaptainAssistantHandler{svc: svc}
}
// Create creates a new captain assistant.
// POST /api/v1/accounts/:account_id/captain_assistants
func (h *CaptainAssistantHandler) Create(c *gin.Context) {
accountID, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid account_id")
return
}
var req service.CreateAssistantRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrValidation, err.Error())
return
}
assistant, err := h.svc.Create(c.Request.Context(), uint(accountID), &req)
if err != nil {
applogger.L().Errorf("Create captain assistant: %v", err)
response.AbortWithStatusError(c, http.StatusInternalServerError, response.ErrInternal, "failed to create assistant")
return
}
response.Created(c, assistant)
}
// Get retrieves a captain assistant by ID.
// GET /api/v1/accounts/:account_id/captain_assistants/:id
func (h *CaptainAssistantHandler) Get(c *gin.Context) {
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid id")
return
}
assistant, err := h.svc.Get(c.Request.Context(), uint(id))
if err != nil {
applogger.L().Errorf("Get captain assistant: %v", err)
response.AbortWithStatusError(c, http.StatusNotFound, response.ErrNotFound, "assistant not found")
return
}
response.OK(c, assistant)
}
// Update updates an existing captain assistant.
// PUT /api/v1/accounts/:account_id/captain_assistants/:id
func (h *CaptainAssistantHandler) Update(c *gin.Context) {
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid id")
return
}
var req service.UpdateAssistantRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrValidation, err.Error())
return
}
assistant, err := h.svc.Update(c.Request.Context(), uint(id), &req)
if err != nil {
applogger.L().Errorf("Update captain assistant: %v", err)
response.AbortWithStatusError(c, http.StatusInternalServerError, response.ErrInternal, "failed to update assistant")
return
}
response.OK(c, assistant)
}
// Delete deletes a captain assistant.
// DELETE /api/v1/accounts/:account_id/captain_assistants/:id
func (h *CaptainAssistantHandler) Delete(c *gin.Context) {
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid id")
return
}
if err := h.svc.Delete(c.Request.Context(), uint(id)); err != nil {
applogger.L().Errorf("Delete captain assistant: %v", err)
response.AbortWithStatusError(c, http.StatusInternalServerError, response.ErrInternal, "failed to delete assistant")
return
}
response.NoContent(c)
}
// List retrieves captain assistants for an account.
// GET /api/v1/accounts/:account_id/captain_assistants
func (h *CaptainAssistantHandler) List(c *gin.Context) {
accountID, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid account_id")
return
}
p := pagination.Parse(c)
assistants, count, err := h.svc.List(c.Request.Context(), uint(accountID), p.Offset, p.PerPage)
if err != nil {
applogger.L().Errorf("List captain assistants: %v", err)
response.AbortWithStatusError(c, http.StatusInternalServerError, response.ErrInternal, "failed to list assistants")
return
}
response.OKWithMeta(c, assistants, p.Page, p.PerPage, count)
}
// GetConfig retrieves the assistant's JSONB config.
// GET /api/v1/accounts/:account_id/captain_assistants/:id/config
func (h *CaptainAssistantHandler) GetConfig(c *gin.Context) {
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid id")
return
}
cfg, err := h.svc.GetConfig(c.Request.Context(), uint(id))
if err != nil {
applogger.L().Errorf("GetConfig captain assistant: %v", err)
response.AbortWithStatusError(c, http.StatusNotFound, response.ErrNotFound, "assistant not found")
return
}
response.OK(c, cfg)
}
// SetConfig updates the assistant's JSONB config.
// PUT /api/v1/accounts/:account_id/captain_assistants/:id/config
func (h *CaptainAssistantHandler) SetConfig(c *gin.Context) {
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid id")
return
}
var cfg model.AssistantConfig
if err := c.ShouldBindJSON(&cfg); err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrValidation, err.Error())
return
}
if err := h.svc.SetConfig(c.Request.Context(), uint(id), &cfg); err != nil {
applogger.L().Errorf("SetConfig captain assistant: %v", err)
response.AbortWithStatusError(c, http.StatusInternalServerError, response.ErrInternal, "failed to update config")
return
}
response.OK(c, cfg)
}
// AssociateInbox binds an assistant to an inbox.
// POST /api/v1/accounts/:account_id/captain_assistants/:id/inboxes
func (h *CaptainAssistantHandler) AssociateInbox(c *gin.Context) {
assistantID, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid assistant id")
return
}
accountID, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid account_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
}
if err := h.svc.AssociateInbox(c.Request.Context(), uint(assistantID), req.InboxID, uint(accountID)); err != nil {
applogger.L().Errorf("AssociateInbox: %v", err)
response.AbortWithStatusError(c, http.StatusInternalServerError, response.ErrInternal, "failed to associate inbox")
return
}
response.OK(c, gin.H{"associated": true})
}
// DissociateInbox unbinds an assistant from an inbox.
// DELETE /api/v1/accounts/:account_id/captain_assistants/:id/inboxes/:inbox_id
func (h *CaptainAssistantHandler) DissociateInbox(c *gin.Context) {
assistantID, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid assistant id")
return
}
inboxID, err := strconv.ParseUint(c.Param("inbox_id"), 10, 64)
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid inbox_id")
return
}
if err := h.svc.DissociateInbox(c.Request.Context(), uint(assistantID), uint(inboxID)); err != nil {
applogger.L().Errorf("DissociateInbox: %v", err)
response.AbortWithStatusError(c, http.StatusInternalServerError, response.ErrInternal, "failed to dissociate inbox")
return
}
response.OK(c, gin.H{"dissociated": true})
}
// GenerateResponse generates an AI response via RAG.
// POST /api/v1/accounts/:account_id/captain_assistants/:id/generate_response
func (h *CaptainAssistantHandler) GenerateResponse(c *gin.Context) {
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid id")
return
}
var req struct {
Query string `json:"query" validate:"required"`
}
if err := c.ShouldBindJSON(&req); err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrValidation, err.Error())
return
}
result, err := h.svc.GenerateResponse(c.Request.Context(), uint(id), req.Query)
if err != nil {
applogger.L().Errorf("GenerateResponse: %v", err)
response.AbortWithStatusError(c, http.StatusInternalServerError, response.ErrInternal, "failed to generate response")
return
}
response.OK(c, gin.H{"response": result})
}