234 lines
7.0 KiB
Go
234 lines
7.0 KiB
Go
package handler
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"knowlege-lsxd/internal/application/service"
|
|
"knowlege-lsxd/internal/errors"
|
|
"knowlege-lsxd/internal/logger"
|
|
"knowlege-lsxd/internal/types"
|
|
"knowlege-lsxd/internal/types/interfaces"
|
|
)
|
|
|
|
// ChunkHandler defines HTTP handlers for chunk operations
|
|
type ChunkHandler struct {
|
|
service interfaces.ChunkService
|
|
}
|
|
|
|
// NewChunkHandler creates a new chunk handler
|
|
func NewChunkHandler(service interfaces.ChunkService) *ChunkHandler {
|
|
return &ChunkHandler{service: service}
|
|
}
|
|
|
|
// ListKnowledgeChunks lists all chunks for a given knowledge ID
|
|
func (h *ChunkHandler) ListKnowledgeChunks(c *gin.Context) {
|
|
ctx := c.Request.Context()
|
|
logger.Info(ctx, "Start retrieving knowledge chunks list")
|
|
|
|
knowledgeID := c.Param("knowledge_id")
|
|
if knowledgeID == "" {
|
|
logger.Error(ctx, "Knowledge ID is empty")
|
|
c.Error(errors.NewBadRequestError("Knowledge ID cannot be empty"))
|
|
return
|
|
}
|
|
|
|
// Parse pagination parameters
|
|
var pagination types.Pagination
|
|
if err := c.ShouldBindQuery(&pagination); err != nil {
|
|
logger.Error(ctx, "Failed to parse pagination parameters", err)
|
|
c.Error(errors.NewBadRequestError(err.Error()))
|
|
return
|
|
}
|
|
|
|
logger.Infof(ctx, "Retrieving knowledge chunks list, knowledge ID: %s, page: %d, page size: %d",
|
|
knowledgeID, pagination.Page, pagination.PageSize)
|
|
|
|
// Use pagination for query
|
|
result, err := h.service.ListPagedChunksByKnowledgeID(ctx, knowledgeID, &pagination)
|
|
if err != nil {
|
|
logger.ErrorWithFields(ctx, err, nil)
|
|
c.Error(errors.NewInternalServerError(err.Error()))
|
|
return
|
|
}
|
|
|
|
logger.Infof(
|
|
ctx, "Successfully retrieved knowledge chunks list, knowledge ID: %s, total: %d",
|
|
knowledgeID, result.Total,
|
|
)
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": true,
|
|
"data": result.Data,
|
|
"total": result.Total,
|
|
"page": result.Page,
|
|
"page_size": result.PageSize,
|
|
})
|
|
}
|
|
|
|
// UpdateChunkRequest defines the request structure for updating a chunk
|
|
type UpdateChunkRequest struct {
|
|
Content string `json:"content"`
|
|
Embedding []float32 `json:"embedding"`
|
|
ChunkIndex int `json:"chunk_index"`
|
|
IsEnabled bool `json:"is_enabled"`
|
|
StartAt int `json:"start_at"`
|
|
EndAt int `json:"end_at"`
|
|
ImageInfo string `json:"image_info"`
|
|
}
|
|
|
|
// validateAndGetChunk validates request parameters and retrieves the chunk
|
|
// Returns chunk information, knowledge ID, and error
|
|
func (h *ChunkHandler) validateAndGetChunk(c *gin.Context) (*types.Chunk, string, error) {
|
|
ctx := c.Request.Context()
|
|
|
|
// Validate knowledge ID
|
|
knowledgeID := c.Param("knowledge_id")
|
|
if knowledgeID == "" {
|
|
logger.Error(ctx, "Knowledge ID is empty")
|
|
return nil, "", errors.NewBadRequestError("Knowledge ID cannot be empty")
|
|
}
|
|
|
|
// Validate chunk ID
|
|
id := c.Param("id")
|
|
if id == "" {
|
|
logger.Error(ctx, "Chunk ID is empty")
|
|
return nil, knowledgeID, errors.NewBadRequestError("Chunk ID cannot be empty")
|
|
}
|
|
|
|
// Get tenant ID from context
|
|
tenantID, exists := c.Get(types.TenantIDContextKey.String())
|
|
if !exists {
|
|
logger.Error(ctx, "Failed to get tenant ID")
|
|
return nil, knowledgeID, errors.NewUnauthorizedError("Unauthorized")
|
|
}
|
|
|
|
logger.Infof(ctx, "Retrieving knowledge chunk information, knowledge ID: %s, chunk ID: %s", knowledgeID, id)
|
|
|
|
// Get existing chunk
|
|
chunk, err := h.service.GetChunkByID(ctx, knowledgeID, id)
|
|
if err != nil {
|
|
if err == service.ErrChunkNotFound {
|
|
logger.Warnf(ctx, "Chunk not found, knowledge ID: %s, chunk ID: %s", knowledgeID, id)
|
|
return nil, knowledgeID, errors.NewNotFoundError("Chunk not found")
|
|
}
|
|
logger.ErrorWithFields(ctx, err, nil)
|
|
return nil, knowledgeID, errors.NewInternalServerError(err.Error())
|
|
}
|
|
|
|
// Validate tenant ID
|
|
if chunk.TenantID != tenantID.(uint) || chunk.KnowledgeID != knowledgeID {
|
|
logger.Warnf(
|
|
ctx,
|
|
"Tenant has no permission to access chunk, knowledge ID: %s, chunk ID: %s, req tenant: %d, chunk tenant: %d",
|
|
knowledgeID, id, tenantID.(uint), chunk.TenantID,
|
|
)
|
|
return nil, knowledgeID, errors.NewForbiddenError("No permission to access this chunk")
|
|
}
|
|
|
|
return chunk, knowledgeID, nil
|
|
}
|
|
|
|
// UpdateChunk updates a chunk's properties
|
|
func (h *ChunkHandler) UpdateChunk(c *gin.Context) {
|
|
ctx := c.Request.Context()
|
|
logger.Info(ctx, "Start updating knowledge chunk")
|
|
|
|
// Validate parameters and get chunk
|
|
chunk, knowledgeID, err := h.validateAndGetChunk(c)
|
|
if err != nil {
|
|
c.Error(err)
|
|
return
|
|
}
|
|
var req UpdateChunkRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
logger.Error(ctx, "Failed to parse request parameters", err)
|
|
c.Error(errors.NewBadRequestError(err.Error()))
|
|
return
|
|
}
|
|
|
|
// Update chunk properties
|
|
if req.Content != "" {
|
|
chunk.Content = req.Content
|
|
}
|
|
|
|
chunk.IsEnabled = req.IsEnabled
|
|
|
|
logger.Infof(ctx, "Updating knowledge chunk, knowledge ID: %s, chunk ID: %s", knowledgeID, chunk.ID)
|
|
|
|
if err := h.service.UpdateChunk(ctx, chunk); err != nil {
|
|
logger.ErrorWithFields(ctx, err, nil)
|
|
c.Error(errors.NewInternalServerError(err.Error()))
|
|
return
|
|
}
|
|
|
|
logger.Infof(ctx, "Knowledge chunk updated successfully, knowledge ID: %s, chunk ID: %s", knowledgeID, chunk.ID)
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": true,
|
|
"data": chunk,
|
|
})
|
|
}
|
|
|
|
// DeleteChunk deletes a specific chunk
|
|
func (h *ChunkHandler) DeleteChunk(c *gin.Context) {
|
|
ctx := c.Request.Context()
|
|
logger.Info(ctx, "Start deleting knowledge chunk")
|
|
|
|
// Validate parameters and get chunk
|
|
chunk, knowledgeID, err := h.validateAndGetChunk(c)
|
|
if err != nil {
|
|
c.Error(err)
|
|
return
|
|
}
|
|
|
|
logger.Infof(ctx, "Deleting knowledge chunk, knowledge ID: %s, chunk ID: %s", knowledgeID, chunk.ID)
|
|
|
|
if err := h.service.DeleteChunk(ctx, chunk.ID); err != nil {
|
|
logger.ErrorWithFields(ctx, err, nil)
|
|
c.Error(errors.NewInternalServerError(err.Error()))
|
|
return
|
|
}
|
|
|
|
logger.Infof(ctx, "Knowledge chunk deleted successfully, knowledge ID: %s, chunk ID: %s", knowledgeID, chunk.ID)
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": true,
|
|
"message": "Chunk deleted",
|
|
})
|
|
}
|
|
|
|
// DeleteChunksByKnowledgeID deletes all chunks for a given knowledge ID
|
|
func (h *ChunkHandler) DeleteChunksByKnowledgeID(c *gin.Context) {
|
|
ctx := c.Request.Context()
|
|
logger.Info(ctx, "Start deleting all chunks under knowledge")
|
|
|
|
knowledgeID := c.Param("knowledge_id")
|
|
if knowledgeID == "" {
|
|
logger.Error(ctx, "Knowledge ID is empty")
|
|
c.Error(errors.NewBadRequestError("Knowledge ID cannot be empty"))
|
|
return
|
|
}
|
|
|
|
// Get tenant ID from context
|
|
tenantID, exists := c.Get(types.TenantIDContextKey.String())
|
|
if !exists {
|
|
logger.Error(ctx, "Failed to get tenant ID")
|
|
c.Error(errors.NewUnauthorizedError("Unauthorized"))
|
|
return
|
|
}
|
|
|
|
logger.Infof(ctx, "Deleting all chunks under knowledge, knowledge ID: %s, tenant ID: %d", knowledgeID, tenantID.(uint))
|
|
|
|
// Delete all chunks under the knowledge
|
|
err := h.service.DeleteChunksByKnowledgeID(ctx, knowledgeID)
|
|
if err != nil {
|
|
logger.ErrorWithFields(ctx, err, nil)
|
|
c.Error(errors.NewInternalServerError(err.Error()))
|
|
return
|
|
}
|
|
|
|
logger.Infof(ctx, "All chunks under knowledge deleted successfully, knowledge ID: %s", knowledgeID)
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": true,
|
|
"message": "All chunks under knowledge deleted",
|
|
})
|
|
}
|