committtttt

This commit is contained in:
Dev
2025-09-13 06:48:55 +03:00
commit 2d49f69be6
32 changed files with 6478 additions and 0 deletions

View File

@@ -0,0 +1,211 @@
package handlers
import (
"context"
"net/http"
"github.com/gin-gonic/gin"
"customer-support-system/internal/ai"
"customer-support-system/internal/models"
"customer-support-system/pkg/logger"
)
// AIHandler handles AI-related HTTP requests
type AIHandler struct {
aiService *ai.AIService
}
// NewAIHandler creates a new AI handler
func NewAIHandler() *AIHandler {
return &AIHandler{
aiService: ai.NewAIService(),
}
}
// QueryAI handles querying the AI service
func (h *AIHandler) QueryAI(c *gin.Context) {
// Check if user is authenticated
_, exists := c.Get("userID")
if !exists {
logger.Error("Failed to get user ID from context")
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "Unauthorized",
})
return
}
var req struct {
Prompt string `json:"prompt" binding:"required"`
ConversationHistory []models.Message `json:"conversationHistory"`
Complexity int `json:"complexity"`
}
if err := c.ShouldBindJSON(&req); err != nil {
logger.WithError(err).Error("Failed to parse query AI request")
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "Invalid request body",
})
return
}
// If complexity is not provided, analyze it
if req.Complexity == 0 {
req.Complexity = h.aiService.AnalyzeComplexity(req.Prompt)
}
// Query AI
ctx := context.Background()
response, err := h.aiService.Query(ctx, req.Prompt, req.ConversationHistory, req.Complexity)
if err != nil {
logger.WithError(err).Error("Failed to query AI")
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "Failed to query AI",
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "AI query successful",
"data": gin.H{
"response": response,
"complexity": req.Complexity,
},
})
}
// AnalyzeComplexity handles analyzing the complexity of a prompt
func (h *AIHandler) AnalyzeComplexity(c *gin.Context) {
var req struct {
Prompt string `json:"prompt" binding:"required"`
}
if err := c.ShouldBindJSON(&req); err != nil {
logger.WithError(err).Error("Failed to parse analyze complexity request")
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "Invalid request body",
})
return
}
// Analyze complexity
complexity := h.aiService.AnalyzeComplexity(req.Prompt)
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "Complexity analysis successful",
"data": gin.H{
"complexity": complexity,
},
})
}
// GetAvailableModels handles getting available AI models
func (h *AIHandler) GetAvailableModels(c *gin.Context) {
// Get available models
models := h.aiService.GetAvailableModels()
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": gin.H{
"models": models,
},
})
}
// QueryOpenAI handles querying the OpenAI API
func (h *AIHandler) QueryOpenAI(c *gin.Context) {
// Check if user is authenticated
_, exists := c.Get("userID")
if !exists {
logger.Error("Failed to get user ID from context")
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "Unauthorized",
})
return
}
var req struct {
Prompt string `json:"prompt" binding:"required"`
ConversationHistory []models.Message `json:"conversationHistory"`
}
if err := c.ShouldBindJSON(&req); err != nil {
logger.WithError(err).Error("Failed to parse query OpenAI request")
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "Invalid request body",
})
return
}
// Query OpenAI
ctx := context.Background()
response, err := h.aiService.QueryOpenAI(ctx, req.Prompt, req.ConversationHistory)
if err != nil {
logger.WithError(err).Error("Failed to query OpenAI")
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "Failed to query OpenAI",
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "OpenAI query successful",
"data": gin.H{
"response": response,
},
})
}
// QueryOllama handles querying the Ollama API
func (h *AIHandler) QueryOllama(c *gin.Context) {
// Check if user is authenticated
_, exists := c.Get("userID")
if !exists {
logger.Error("Failed to get user ID from context")
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "Unauthorized",
})
return
}
var req struct {
Prompt string `json:"prompt" binding:"required"`
ConversationHistory []models.Message `json:"conversationHistory"`
}
if err := c.ShouldBindJSON(&req); err != nil {
logger.WithError(err).Error("Failed to parse query Ollama request")
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "Invalid request body",
})
return
}
// Query Ollama
ctx := context.Background()
response, err := h.aiService.QueryOllama(ctx, req.Prompt, req.ConversationHistory)
if err != nil {
logger.WithError(err).Error("Failed to query Ollama")
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "Failed to query Ollama",
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "Ollama query successful",
"data": gin.H{
"response": response,
},
})
}

View File

@@ -0,0 +1,558 @@
package handlers
import (
"net/http"
"strconv"
"github.com/gin-gonic/gin"
"customer-support-system/internal/conversation"
"customer-support-system/internal/models"
"customer-support-system/pkg/logger"
)
// ConversationHandler handles conversation-related HTTP requests
type ConversationHandler struct {
conversationService *conversation.ConversationService
}
// NewConversationHandler creates a new conversation handler
func NewConversationHandler() *ConversationHandler {
return &ConversationHandler{
conversationService: conversation.NewConversationService(),
}
}
// CreateConversation handles creating a new conversation
func (h *ConversationHandler) CreateConversation(c *gin.Context) {
// Get user ID from context
userID, exists := c.Get("userID")
if !exists {
logger.Error("Failed to get user ID from context")
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "Unauthorized",
})
return
}
var req models.CreateConversationRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.WithError(err).Error("Failed to parse create conversation request")
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "Invalid request body",
})
return
}
// Create conversation
conv, err := h.conversationService.CreateConversation(userID.(uint), &req)
if err != nil {
logger.WithError(err).Error("Failed to create conversation")
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "Failed to create conversation",
})
return
}
c.JSON(http.StatusCreated, gin.H{
"success": true,
"message": "Conversation created successfully",
"data": gin.H{
"conversation": conv,
},
})
}
// GetConversation handles getting a conversation by ID
func (h *ConversationHandler) GetConversation(c *gin.Context) {
// Get user ID from context
userID, exists := c.Get("userID")
if !exists {
logger.Error("Failed to get user ID from context")
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "Unauthorized",
})
return
}
// Get conversation ID from URL params
conversationID, err := strconv.ParseUint(c.Param("id"), 10, 32)
if err != nil {
logger.WithError(err).Error("Failed to parse conversation ID")
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "Invalid conversation ID",
})
return
}
// Get conversation
conv, err := h.conversationService.GetConversation(uint(conversationID), userID.(uint))
if err != nil {
logger.WithError(err).Error("Failed to get conversation")
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "Failed to get conversation",
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": gin.H{
"conversation": conv,
},
})
}
// ListConversations handles listing conversations for a user
func (h *ConversationHandler) ListConversations(c *gin.Context) {
// Get user ID from context
userID, exists := c.Get("userID")
if !exists {
logger.Error("Failed to get user ID from context")
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "Unauthorized",
})
return
}
// Parse query parameters
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "10"))
status := c.DefaultQuery("status", "")
if page < 1 {
page = 1
}
if pageSize < 1 || pageSize > 100 {
pageSize = 10
}
// List conversations
response, err := h.conversationService.ListConversations(userID.(uint), page, pageSize, status)
if err != nil {
logger.WithError(err).Error("Failed to list conversations")
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "Failed to list conversations",
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": gin.H{
"conversations": response.Conversations,
"total": response.Total,
"page": response.Page,
"pageSize": response.PageSize,
},
})
}
// UpdateConversation handles updating a conversation
func (h *ConversationHandler) UpdateConversation(c *gin.Context) {
// Get user ID from context
userID, exists := c.Get("userID")
if !exists {
logger.Error("Failed to get user ID from context")
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "Unauthorized",
})
return
}
// Get conversation ID from URL params
conversationID, err := strconv.ParseUint(c.Param("id"), 10, 32)
if err != nil {
logger.WithError(err).Error("Failed to parse conversation ID")
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "Invalid conversation ID",
})
return
}
var req models.UpdateConversationRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.WithError(err).Error("Failed to parse update conversation request")
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "Invalid request body",
})
return
}
// Update conversation
conv, err := h.conversationService.UpdateConversation(uint(conversationID), userID.(uint), &req)
if err != nil {
logger.WithError(err).Error("Failed to update conversation")
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "Failed to update conversation",
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "Conversation updated successfully",
"data": gin.H{
"conversation": conv,
},
})
}
// DeleteConversation handles deleting a conversation
func (h *ConversationHandler) DeleteConversation(c *gin.Context) {
// Get user ID from context
userID, exists := c.Get("userID")
if !exists {
logger.Error("Failed to get user ID from context")
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "Unauthorized",
})
return
}
// Get conversation ID from URL params
conversationID, err := strconv.ParseUint(c.Param("id"), 10, 32)
if err != nil {
logger.WithError(err).Error("Failed to parse conversation ID")
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "Invalid conversation ID",
})
return
}
// Delete conversation
err = h.conversationService.DeleteConversation(uint(conversationID), userID.(uint))
if err != nil {
logger.WithError(err).Error("Failed to delete conversation")
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "Failed to delete conversation",
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "Conversation deleted successfully",
})
}
// CreateMessage handles creating a new message in a conversation
func (h *ConversationHandler) CreateMessage(c *gin.Context) {
// Get user ID from context
userID, exists := c.Get("userID")
if !exists {
logger.Error("Failed to get user ID from context")
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "Unauthorized",
})
return
}
// Get conversation ID from URL params
conversationID, err := strconv.ParseUint(c.Param("id"), 10, 32)
if err != nil {
logger.WithError(err).Error("Failed to parse conversation ID")
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "Invalid conversation ID",
})
return
}
var req models.CreateMessageRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.WithError(err).Error("Failed to parse create message request")
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "Invalid request body",
})
return
}
// Create message
message, err := h.conversationService.CreateMessage(uint(conversationID), userID.(uint), &req)
if err != nil {
logger.WithError(err).Error("Failed to create message")
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "Failed to create message",
})
return
}
c.JSON(http.StatusCreated, gin.H{
"success": true,
"message": "Message created successfully",
"data": gin.H{
"message": message,
},
})
}
// GetMessages handles getting messages in a conversation
func (h *ConversationHandler) GetMessages(c *gin.Context) {
// Get user ID from context
userID, exists := c.Get("userID")
if !exists {
logger.Error("Failed to get user ID from context")
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "Unauthorized",
})
return
}
// Get conversation ID from URL params
conversationID, err := strconv.ParseUint(c.Param("id"), 10, 32)
if err != nil {
logger.WithError(err).Error("Failed to parse conversation ID")
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "Invalid conversation ID",
})
return
}
// Parse query parameters
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "20"))
if page < 1 {
page = 1
}
if pageSize < 1 || pageSize > 100 {
pageSize = 20
}
// Get messages
response, err := h.conversationService.GetMessages(uint(conversationID), userID.(uint), page, pageSize)
if err != nil {
logger.WithError(err).Error("Failed to get messages")
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "Failed to get messages",
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": gin.H{
"messages": response.Messages,
"total": response.Total,
"page": response.Page,
"pageSize": response.PageSize,
},
})
}
// UpdateMessage handles updating a message
func (h *ConversationHandler) UpdateMessage(c *gin.Context) {
// Get user ID from context
userID, exists := c.Get("userID")
if !exists {
logger.Error("Failed to get user ID from context")
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "Unauthorized",
})
return
}
// Get message ID from URL params
messageID, err := strconv.ParseUint(c.Param("messageId"), 10, 32)
if err != nil {
logger.WithError(err).Error("Failed to parse message ID")
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "Invalid message ID",
})
return
}
var req models.UpdateMessageRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.WithError(err).Error("Failed to parse update message request")
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "Invalid request body",
})
return
}
// Update message
message, err := h.conversationService.UpdateMessage(uint(messageID), userID.(uint), &req)
if err != nil {
logger.WithError(err).Error("Failed to update message")
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "Failed to update message",
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "Message updated successfully",
"data": gin.H{
"message": message,
},
})
}
// DeleteMessage handles deleting a message
func (h *ConversationHandler) DeleteMessage(c *gin.Context) {
// Get user ID from context
userID, exists := c.Get("userID")
if !exists {
logger.Error("Failed to get user ID from context")
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "Unauthorized",
})
return
}
// Get message ID from URL params
messageID, err := strconv.ParseUint(c.Param("messageId"), 10, 32)
if err != nil {
logger.WithError(err).Error("Failed to parse message ID")
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "Invalid message ID",
})
return
}
// Delete message
err = h.conversationService.DeleteMessage(uint(messageID), userID.(uint))
if err != nil {
logger.WithError(err).Error("Failed to delete message")
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "Failed to delete message",
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "Message deleted successfully",
})
}
// SendMessageWithAI handles sending a message and getting an AI response
func (h *ConversationHandler) SendMessageWithAI(c *gin.Context) {
// Get user ID from context
userID, exists := c.Get("userID")
if !exists {
logger.Error("Failed to get user ID from context")
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "Unauthorized",
})
return
}
// Get conversation ID from URL params
conversationID, err := strconv.ParseUint(c.Param("id"), 10, 32)
if err != nil {
logger.WithError(err).Error("Failed to parse conversation ID")
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "Invalid conversation ID",
})
return
}
var req struct {
Content string `json:"content" binding:"required"`
}
if err := c.ShouldBindJSON(&req); err != nil {
logger.WithError(err).Error("Failed to parse send message request")
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "Invalid request body",
})
return
}
// Send message and get AI response
userMessage, aiMessage, err := h.conversationService.SendMessageWithAI(uint(conversationID), userID.(uint), req.Content)
if err != nil {
logger.WithError(err).Error("Failed to send message with AI")
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "Failed to send message with AI",
})
return
}
c.JSON(http.StatusCreated, gin.H{
"success": true,
"message": "Message sent and AI response received",
"data": gin.H{
"userMessage": userMessage,
"aiMessage": aiMessage,
},
})
}
// GetConversationStats handles getting statistics for a conversation
func (h *ConversationHandler) GetConversationStats(c *gin.Context) {
// Get user ID from context
userID, exists := c.Get("userID")
if !exists {
logger.Error("Failed to get user ID from context")
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "Unauthorized",
})
return
}
// Get conversation ID from URL params
conversationID, err := strconv.ParseUint(c.Param("id"), 10, 32)
if err != nil {
logger.WithError(err).Error("Failed to parse conversation ID")
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "Invalid conversation ID",
})
return
}
// Get conversation stats
stats, err := h.conversationService.GetConversationStats(uint(conversationID), userID.(uint))
if err != nil {
logger.WithError(err).Error("Failed to get conversation stats")
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "Failed to get conversation stats",
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": gin.H{
"stats": stats,
},
})
}

View File

@@ -0,0 +1,475 @@
package handlers
import (
"net/http"
"strconv"
"github.com/gin-gonic/gin"
"customer-support-system/internal/knowledge"
"customer-support-system/internal/models"
"customer-support-system/pkg/logger"
)
// KnowledgeHandler handles knowledge base-related HTTP requests
type KnowledgeHandler struct {
knowledgeService *knowledge.KnowledgeService
}
// NewKnowledgeHandler creates a new knowledge handler
func NewKnowledgeHandler() *KnowledgeHandler {
return &KnowledgeHandler{
knowledgeService: knowledge.NewKnowledgeService(),
}
}
// CreateKnowledgeEntry handles creating a new knowledge base entry
func (h *KnowledgeHandler) CreateKnowledgeEntry(c *gin.Context) {
// Get user ID from context
userID, exists := c.Get("userID")
if !exists {
logger.Error("Failed to get user ID from context")
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "Unauthorized",
})
return
}
var req models.CreateKnowledgeBaseRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.WithError(err).Error("Failed to parse create knowledge entry request")
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "Invalid request body",
})
return
}
// Create knowledge entry
entry, err := h.knowledgeService.CreateKnowledgeEntry(userID.(uint), &req)
if err != nil {
logger.WithError(err).Error("Failed to create knowledge entry")
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "Failed to create knowledge entry",
})
return
}
c.JSON(http.StatusCreated, gin.H{
"success": true,
"message": "Knowledge entry created successfully",
"data": gin.H{
"entry": entry,
},
})
}
// GetKnowledgeEntry handles getting a knowledge base entry by ID
func (h *KnowledgeHandler) GetKnowledgeEntry(c *gin.Context) {
// Get knowledge entry ID from URL params
entryID, err := strconv.ParseUint(c.Param("id"), 10, 32)
if err != nil {
logger.WithError(err).Error("Failed to parse knowledge entry ID")
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "Invalid knowledge entry ID",
})
return
}
// Get knowledge entry
entry, err := h.knowledgeService.GetKnowledgeEntry(uint(entryID))
if err != nil {
logger.WithError(err).Error("Failed to get knowledge entry")
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "Failed to get knowledge entry",
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": gin.H{
"entry": entry,
},
})
}
// ListKnowledgeEntries handles listing knowledge base entries
func (h *KnowledgeHandler) ListKnowledgeEntries(c *gin.Context) {
// Parse query parameters
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "10"))
category := c.DefaultQuery("category", "")
activeStr := c.DefaultQuery("active", "true")
if page < 1 {
page = 1
}
if pageSize < 1 || pageSize > 100 {
pageSize = 10
}
// Parse active parameter
var active *bool
if activeStr != "" {
activeVal := activeStr == "true"
active = &activeVal
}
// List knowledge entries
response, err := h.knowledgeService.ListKnowledgeEntries(page, pageSize, category, active)
if err != nil {
logger.WithError(err).Error("Failed to list knowledge entries")
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "Failed to list knowledge entries",
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": gin.H{
"entries": response.Entries,
"total": response.Total,
"page": response.Page,
"pageSize": response.PageSize,
},
})
}
// UpdateKnowledgeEntry handles updating a knowledge base entry
func (h *KnowledgeHandler) UpdateKnowledgeEntry(c *gin.Context) {
// Get user ID from context
userID, exists := c.Get("userID")
if !exists {
logger.Error("Failed to get user ID from context")
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "Unauthorized",
})
return
}
// Get knowledge entry ID from URL params
entryID, err := strconv.ParseUint(c.Param("id"), 10, 32)
if err != nil {
logger.WithError(err).Error("Failed to parse knowledge entry ID")
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "Invalid knowledge entry ID",
})
return
}
var req models.UpdateKnowledgeBaseRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.WithError(err).Error("Failed to parse update knowledge entry request")
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "Invalid request body",
})
return
}
// Update knowledge entry
entry, err := h.knowledgeService.UpdateKnowledgeEntry(uint(entryID), userID.(uint), &req)
if err != nil {
logger.WithError(err).Error("Failed to update knowledge entry")
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "Failed to update knowledge entry",
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "Knowledge entry updated successfully",
"data": gin.H{
"entry": entry,
},
})
}
// DeleteKnowledgeEntry handles deleting a knowledge base entry
func (h *KnowledgeHandler) DeleteKnowledgeEntry(c *gin.Context) {
// Get user ID from context
userID, exists := c.Get("userID")
if !exists {
logger.Error("Failed to get user ID from context")
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "Unauthorized",
})
return
}
// Get knowledge entry ID from URL params
entryID, err := strconv.ParseUint(c.Param("id"), 10, 32)
if err != nil {
logger.WithError(err).Error("Failed to parse knowledge entry ID")
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "Invalid knowledge entry ID",
})
return
}
// Delete knowledge entry
err = h.knowledgeService.DeleteKnowledgeEntry(uint(entryID), userID.(uint))
if err != nil {
logger.WithError(err).Error("Failed to delete knowledge entry")
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "Failed to delete knowledge entry",
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "Knowledge entry deleted successfully",
})
}
// SearchKnowledge handles searching knowledge base entries
func (h *KnowledgeHandler) SearchKnowledge(c *gin.Context) {
// Parse query parameters
query := c.DefaultQuery("query", "")
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "10"))
category := c.DefaultQuery("category", "")
if page < 1 {
page = 1
}
if pageSize < 1 || pageSize > 100 {
pageSize = 10
}
// Search knowledge entries
response, err := h.knowledgeService.SearchKnowledge(query, page, pageSize, category)
if err != nil {
logger.WithError(err).Error("Failed to search knowledge entries")
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "Failed to search knowledge entries",
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": gin.H{
"results": response.Results,
"total": response.Total,
"query": response.Query,
"page": response.Page,
"pageSize": response.PageSize,
},
})
}
// GetCategories handles getting all unique categories in the knowledge base
func (h *KnowledgeHandler) GetCategories(c *gin.Context) {
// Get categories
categories, err := h.knowledgeService.GetCategories()
if err != nil {
logger.WithError(err).Error("Failed to get knowledge categories")
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "Failed to get knowledge categories",
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": gin.H{
"categories": categories,
},
})
}
// GetTags handles getting all unique tags in the knowledge base
func (h *KnowledgeHandler) GetTags(c *gin.Context) {
// Get tags
tags, err := h.knowledgeService.GetTags()
if err != nil {
logger.WithError(err).Error("Failed to get knowledge tags")
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "Failed to get knowledge tags",
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": gin.H{
"tags": tags,
},
})
}
// RateKnowledgeEntry handles rating a knowledge base entry as helpful or not
func (h *KnowledgeHandler) RateKnowledgeEntry(c *gin.Context) {
// Get user ID from context
userID, exists := c.Get("userID")
if !exists {
logger.Error("Failed to get user ID from context")
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "Unauthorized",
})
return
}
// Get knowledge entry ID from URL params
entryID, err := strconv.ParseUint(c.Param("id"), 10, 32)
if err != nil {
logger.WithError(err).Error("Failed to parse knowledge entry ID")
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "Invalid knowledge entry ID",
})
return
}
var req models.CreateKnowledgeBaseFeedbackRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.WithError(err).Error("Failed to parse rate knowledge entry request")
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "Invalid request body",
})
return
}
// Rate knowledge entry
err = h.knowledgeService.RateKnowledgeEntry(uint(entryID), userID.(uint), req.Helpful, req.Comment)
if err != nil {
logger.WithError(err).Error("Failed to rate knowledge entry")
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "Failed to rate knowledge entry",
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "Knowledge entry rated successfully",
})
}
// GetPopularKnowledge handles getting popular knowledge base entries
func (h *KnowledgeHandler) GetPopularKnowledge(c *gin.Context) {
// Parse query parameters
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "10"))
if limit < 1 || limit > 100 {
limit = 10
}
// Get popular knowledge entries
entries, err := h.knowledgeService.GetPopularKnowledge(limit)
if err != nil {
logger.WithError(err).Error("Failed to get popular knowledge entries")
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "Failed to get popular knowledge entries",
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": gin.H{
"entries": entries,
},
})
}
// GetRecentKnowledge handles getting recent knowledge base entries
func (h *KnowledgeHandler) GetRecentKnowledge(c *gin.Context) {
// Parse query parameters
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "10"))
if limit < 1 || limit > 100 {
limit = 10
}
// Get recent knowledge entries
entries, err := h.knowledgeService.GetRecentKnowledge(limit)
if err != nil {
logger.WithError(err).Error("Failed to get recent knowledge entries")
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "Failed to get recent knowledge entries",
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": gin.H{
"entries": entries,
},
})
}
// FindBestMatch handles finding the best matching knowledge base entry for a query
func (h *KnowledgeHandler) FindBestMatch(c *gin.Context) {
// Parse query parameters
query := c.DefaultQuery("query", "")
if query == "" {
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "Query parameter is required",
})
return
}
// Find best match
entry, err := h.knowledgeService.FindBestMatch(query)
if err != nil {
logger.WithError(err).Error("Failed to find best match")
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "Failed to find best match",
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": gin.H{
"entry": entry,
},
})
}
// GetKnowledgeStats handles getting statistics for the knowledge base
func (h *KnowledgeHandler) GetKnowledgeStats(c *gin.Context) {
// Get knowledge stats
stats, err := h.knowledgeService.GetKnowledgeStats()
if err != nil {
logger.WithError(err).Error("Failed to get knowledge stats")
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "Failed to get knowledge stats",
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": gin.H{
"stats": stats,
},
})
}

View File

@@ -0,0 +1,453 @@
package handlers
import (
"fmt"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
"customer-support-system/internal/auth"
"customer-support-system/internal/database"
"customer-support-system/internal/models"
"customer-support-system/pkg/logger"
)
// UserHandler handles user-related HTTP requests
type UserHandler struct {
authService *auth.AuthService
}
// NewUserHandler creates a new user handler
func NewUserHandler() *UserHandler {
return &UserHandler{
authService: auth.NewAuthService(database.GetDB()),
}
}
// Register handles user registration
func (h *UserHandler) Register(c *gin.Context) {
var req models.CreateUserRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.WithError(err).Error("Failed to parse register request")
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "Invalid request body",
})
return
}
// Validate request
if err := validateRegisterRequest(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": err.Error(),
})
return
}
// Create user
user, err := h.authService.Register(&req)
if err != nil {
logger.WithError(err).Error("Failed to register user")
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "Failed to register user",
})
return
}
c.JSON(http.StatusCreated, gin.H{
"success": true,
"message": "User registered successfully",
"data": gin.H{
"user": user,
},
})
}
// Login handles user login
func (h *UserHandler) Login(c *gin.Context) {
var req models.LoginRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.WithError(err).Error("Failed to parse login request")
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "Invalid request body",
})
return
}
// Validate request
if err := validateLoginRequest(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": err.Error(),
})
return
}
// Get client IP
clientIP := c.ClientIP()
// Authenticate user
response, err := h.authService.Login(req.Username, req.Password, clientIP)
if err != nil {
logger.WithError(err).Error("Failed to login user")
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "Invalid username or password",
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "User logged in successfully",
"data": gin.H{
"user": response.User,
"token": response.Token,
},
})
}
// GetProfile handles getting user profile
func (h *UserHandler) GetProfile(c *gin.Context) {
// Get user ID from context
userID, exists := c.Get("userID")
if !exists {
logger.Error("Failed to get user ID from context")
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "Unauthorized",
})
return
}
// Get user profile
user, err := h.authService.GetUserByID(userID.(uint))
if err != nil {
logger.WithError(err).Error("Failed to get user profile")
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "Failed to get user profile",
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": gin.H{
"user": user.ToSafeUser(),
},
})
}
// UpdateProfile handles updating user profile
func (h *UserHandler) UpdateProfile(c *gin.Context) {
// Get user ID from context
userID, exists := c.Get("userID")
if !exists {
logger.Error("Failed to get user ID from context")
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "Unauthorized",
})
return
}
var req models.UpdateUserRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.WithError(err).Error("Failed to parse update profile request")
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "Invalid request body",
})
return
}
// Update user profile
user, err := h.authService.UpdateUser(userID.(uint), &req)
if err != nil {
logger.WithError(err).Error("Failed to update user profile")
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "Failed to update user profile",
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "User profile updated successfully",
"data": gin.H{
"user": user,
},
})
}
// ChangePassword handles changing user password
func (h *UserHandler) ChangePassword(c *gin.Context) {
// Get user ID from context
userID, exists := c.Get("userID")
if !exists {
logger.Error("Failed to get user ID from context")
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "Unauthorized",
})
return
}
var req models.ChangePasswordRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.WithError(err).Error("Failed to parse change password request")
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "Invalid request body",
})
return
}
// Validate request
if err := validateChangePasswordRequest(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": err.Error(),
})
return
}
// Change password
err := h.authService.ChangePassword(userID.(uint), &req)
if err != nil {
logger.WithError(err).Error("Failed to change password")
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "Failed to change password",
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "Password changed successfully",
})
}
// validateRegisterRequest validates the register request
func validateRegisterRequest(req *models.CreateUserRequest) error {
if req.Username == "" {
return fmt.Errorf("username is required")
}
if req.Password == "" {
return fmt.Errorf("password is required")
}
if req.Email == "" {
return fmt.Errorf("email is required")
}
return nil
}
// validateLoginRequest validates the login request
func validateLoginRequest(req *models.LoginRequest) error {
if req.Username == "" {
return fmt.Errorf("username is required")
}
if req.Password == "" {
return fmt.Errorf("password is required")
}
return nil
}
// validateChangePasswordRequest validates the change password request
func validateChangePasswordRequest(req *models.ChangePasswordRequest) error {
if req.CurrentPassword == "" {
return fmt.Errorf("current password is required")
}
if req.NewPassword == "" {
return fmt.Errorf("new password is required")
}
return nil
}
// AdminGetUsers handles getting all users (admin only)
func (h *UserHandler) AdminGetUsers(c *gin.Context) {
// Parse query parameters
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "10"))
if page < 1 {
page = 1
}
if pageSize < 1 || pageSize > 100 {
pageSize = 10
}
// Get users
var users []models.User
var total int64
db := database.GetDB()
// Get total count
if err := db.Model(&models.User{}).Count(&total).Error; err != nil {
logger.WithError(err).Error("Failed to count users")
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "Failed to count users",
})
return
}
// Get paginated results
offset := (page - 1) * pageSize
if err := db.Offset(offset).Limit(pageSize).Find(&users).Error; err != nil {
logger.WithError(err).Error("Failed to get users")
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "Failed to get users",
})
return
}
// Convert to safe users
safeUsers := make([]models.SafeUser, len(users))
for i, user := range users {
safeUsers[i] = user.ToSafeUser()
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": gin.H{
"users": safeUsers,
"total": total,
"page": page,
"pageSize": pageSize,
},
})
}
// AdminGetUser handles getting a user by ID (admin only)
func (h *UserHandler) AdminGetUser(c *gin.Context) {
// Get target user ID from URL params
targetUserID, err := strconv.ParseUint(c.Param("id"), 10, 32)
if err != nil {
logger.WithError(err).Error("Failed to parse user ID")
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "Invalid user ID",
})
return
}
// Get user
user, err := h.authService.GetUserByID(uint(targetUserID))
if err != nil {
logger.WithError(err).Error("Failed to get user")
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "Failed to get user",
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": gin.H{
"user": user.ToSafeUser(),
},
})
}
// AdminUpdateUser handles updating a user (admin only)
func (h *UserHandler) AdminUpdateUser(c *gin.Context) {
// Get target user ID from URL params
targetUserID, err := strconv.ParseUint(c.Param("id"), 10, 32)
if err != nil {
logger.WithError(err).Error("Failed to parse user ID")
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "Invalid user ID",
})
return
}
var req models.UpdateUserRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.WithError(err).Error("Failed to parse update user request")
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "Invalid request body",
})
return
}
// Update user
updatedUser, err := h.authService.UpdateUser(uint(targetUserID), &req)
if err != nil {
logger.WithError(err).Error("Failed to update user")
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "Failed to update user",
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "User updated successfully",
"data": gin.H{
"user": updatedUser,
},
})
}
// AdminDeleteUser handles deleting a user (admin only)
func (h *UserHandler) AdminDeleteUser(c *gin.Context) {
// Get target user ID from URL params
targetUserID, err := strconv.ParseUint(c.Param("id"), 10, 32)
if err != nil {
logger.WithError(err).Error("Failed to parse user ID")
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "Invalid user ID",
})
return
}
// Get current user ID from context
currentUserID, exists := c.Get("userID")
if !exists {
logger.Error("Failed to get current user ID from context")
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "Unauthorized",
})
return
}
// Prevent self-deletion
if currentUserID.(uint) == uint(targetUserID) {
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "Cannot delete your own account",
})
return
}
// Delete user
db := database.GetDB()
if err := db.Delete(&models.User{}, uint(targetUserID)).Error; err != nil {
logger.WithError(err).Error("Failed to delete user")
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "Failed to delete user",
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "User deleted successfully",
})
}