Files
ai-agent/internal/services/knowledge_document_service.go
T
mlogclub 0b4a1b4594 Refactor error handling in services to use internationalized error messages
- Updated OSSStorage validation errors to use internationalized messages.
- Changed error messages in provider.go for unsupported file storage types.
- Refactored tag_service.go to replace hardcoded error messages with internationalized versions.
- Updated ticket_service.go to use internationalized error messages for various validation checks.
- Refactored ticket_tag_service.go to use internationalized error messages for tag validation.
- Changed ticket_view_service.go to use internationalized error messages for view validation.
- Updated tool_catalog_service.go to use internationalized error messages for tool code validation.
- Refactored user_service.go to replace error messages with internationalized versions.
- Updated ws_service.go to use internationalized error messages for WebSocket handling.
- Refactored wxwork_kf_inbound_service.go to use internationalized error messages for message handling.
- Updated wxwork_kf_outbound_service.go to use internationalized error messages for outbound message handling.
- Refactored wxwork_login_service.go to use internationalized error messages for login handling.
- Updated login.go in wxwork package to use internationalized error messages for login state and ticket validation.
2026-06-02 20:51:13 +08:00

291 lines
9.6 KiB
Go

package services
import (
"context"
"crypto/sha256"
"encoding/hex"
"log/slog"
"time"
"agent-desk/internal/ai/rag"
"agent-desk/internal/models"
"agent-desk/internal/pkg/dto"
"agent-desk/internal/pkg/dto/request"
"agent-desk/internal/pkg/enums"
"agent-desk/internal/pkg/errorsx"
"agent-desk/internal/pkg/utils"
"agent-desk/internal/repositories"
"agent-desk/internal/pkg/httpx/params"
"github.com/mlogclub/simple/common/strs"
"github.com/mlogclub/simple/sqls"
)
var KnowledgeDocumentService = newKnowledgeDocumentService()
func newKnowledgeDocumentService() *knowledgeDocumentService {
return &knowledgeDocumentService{}
}
type knowledgeDocumentService struct {
}
func (s *knowledgeDocumentService) Get(id int64) *models.KnowledgeDocument {
return repositories.KnowledgeDocumentRepository.Get(sqls.DB(), id)
}
func (s *knowledgeDocumentService) Take(where ...interface{}) *models.KnowledgeDocument {
return repositories.KnowledgeDocumentRepository.Take(sqls.DB(), where...)
}
func (s *knowledgeDocumentService) Find(cnd *sqls.Cnd) []models.KnowledgeDocument {
return repositories.KnowledgeDocumentRepository.Find(sqls.DB(), cnd)
}
func (s *knowledgeDocumentService) FindOne(cnd *sqls.Cnd) *models.KnowledgeDocument {
return repositories.KnowledgeDocumentRepository.FindOne(sqls.DB(), cnd)
}
func (s *knowledgeDocumentService) FindPageByParams(params *params.QueryParams) (list []models.KnowledgeDocument, paging *sqls.Paging) {
return repositories.KnowledgeDocumentRepository.FindPageByParams(sqls.DB(), params)
}
func (s *knowledgeDocumentService) FindPageByCnd(cnd *sqls.Cnd) (list []models.KnowledgeDocument, paging *sqls.Paging) {
return repositories.KnowledgeDocumentRepository.FindPageByCnd(sqls.DB(), cnd)
}
func (s *knowledgeDocumentService) FindPageListByCnd(cnd *sqls.Cnd) (list []models.KnowledgeDocument, paging *sqls.Paging) {
return repositories.KnowledgeDocumentRepository.FindPageListByCnd(sqls.DB(), cnd)
}
func (s *knowledgeDocumentService) Count(cnd *sqls.Cnd) int64 {
return repositories.KnowledgeDocumentRepository.Count(sqls.DB(), cnd)
}
func (s *knowledgeDocumentService) Create(t *models.KnowledgeDocument) error {
return repositories.KnowledgeDocumentRepository.Create(sqls.DB(), t)
}
func (s *knowledgeDocumentService) Update(t *models.KnowledgeDocument) error {
return repositories.KnowledgeDocumentRepository.Update(sqls.DB(), t)
}
func (s *knowledgeDocumentService) Updates(id int64, columns map[string]interface{}) error {
return repositories.KnowledgeDocumentRepository.Updates(sqls.DB(), id, columns)
}
func (s *knowledgeDocumentService) UpdateColumn(id int64, name string, value interface{}) error {
return repositories.KnowledgeDocumentRepository.UpdateColumn(sqls.DB(), id, name, value)
}
func (s *knowledgeDocumentService) Delete(id int64) {
repositories.KnowledgeDocumentRepository.Delete(sqls.DB(), id)
}
func (s *knowledgeDocumentService) CreateKnowledgeDocument(req request.CreateKnowledgeDocumentRequest, operator *dto.AuthPrincipal) (*models.KnowledgeDocument, error) {
if operator == nil {
return nil, errorsx.UnauthorizedI18n("error.auth.expired")
}
kb := KnowledgeBaseService.Get(req.KnowledgeBaseID)
if kb == nil {
return nil, errorsx.InvalidParamI18n("error.e0283")
}
if kb.KnowledgeType == string(enums.KnowledgeBaseTypeFAQ) {
return nil, errorsx.InvalidParamI18n("error.e0026")
}
if _, err := KnowledgeDirectoryService.RequireUsableDirectory(req.KnowledgeBaseID, req.DirectoryID); err != nil {
return nil, err
}
item, err := s.buildKnowledgeDocumentModel(req)
if err != nil {
return nil, err
}
item.Status = enums.StatusOk
item.IndexStatus = enums.KnowledgeDocumentIndexStatusPending
item.IndexError = ""
item.IndexedAt = nil
item.AuditFields = utils.BuildAuditFields(operator)
if err := sqls.WithTransaction(func(ctx *sqls.TxContext) error {
if err := ctx.Tx.Create(item).Error; err != nil {
return err
}
return nil
}); err != nil {
return nil, err
}
if err := rag.Index.IndexDocumentByID(context.Background(), item.ID); err != nil {
slog.Error("failed to index created knowledge document", "document_id", item.ID, "error", err)
}
item = s.Get(item.ID)
return item, nil
}
func (s *knowledgeDocumentService) UpdateKnowledgeDocument(req request.UpdateKnowledgeDocumentRequest, operator *dto.AuthPrincipal) error {
if operator == nil {
return errorsx.UnauthorizedI18n("error.auth.expired")
}
current := s.Get(req.ID)
if current == nil {
return errorsx.InvalidParamI18n("error.e0218")
}
kb := KnowledgeBaseService.Get(req.KnowledgeBaseID)
if kb == nil {
return errorsx.InvalidParamI18n("error.e0283")
}
if kb.KnowledgeType == string(enums.KnowledgeBaseTypeFAQ) {
return errorsx.InvalidParamI18n("error.e0026")
}
if _, err := KnowledgeDirectoryService.RequireUsableDirectory(req.KnowledgeBaseID, req.DirectoryID); err != nil {
return err
}
item, err := s.buildKnowledgeDocumentModel(req.CreateKnowledgeDocumentRequest)
if err != nil {
return err
}
oldKnowledgeBaseID := current.KnowledgeBaseID
if err := repositories.KnowledgeDocumentRepository.Updates(sqls.DB(), req.ID, map[string]any{
"knowledge_base_id": item.KnowledgeBaseID,
"directory_id": item.DirectoryID,
"title": item.Title,
"content_type": item.ContentType,
"content_hash": item.ContentHash,
"content": item.Content,
"index_status": enums.KnowledgeDocumentIndexStatusPending,
"indexed_at": nil,
"index_error": "",
"update_user_id": operator.UserID,
"update_user_name": operator.Username,
"updated_at": time.Now(),
}); err != nil {
return err
}
if oldKnowledgeBaseID != item.KnowledgeBaseID {
if err := rag.Index.RemoveDocumentIndex(context.Background(), req.ID); err != nil {
slog.Error("failed to remove old document index after knowledge base change", "document_id", req.ID, "knowledge_base_id", oldKnowledgeBaseID, "error", err)
}
}
if err := rag.Index.IndexDocumentByID(context.Background(), req.ID); err != nil {
slog.Error("failed to reindex updated knowledge document", "document_id", req.ID, "error", err)
}
return nil
}
func (s *knowledgeDocumentService) DeleteKnowledgeDocument(id int64) error {
if err := repositories.KnowledgeDocumentRepository.Updates(sqls.DB(), id, map[string]any{
"status": enums.StatusDeleted,
"updated_at": time.Now(),
}); err != nil {
return err
}
return rag.Index.RemoveDocumentIndex(context.Background(), id)
}
func (s *knowledgeDocumentService) BatchMoveKnowledgeDocuments(req request.BatchMoveKnowledgeDocumentRequest, operator *dto.AuthPrincipal) error {
if operator == nil {
return errorsx.UnauthorizedI18n("error.auth.expired")
}
ids := uniquePositiveIDs(req.IDs)
if len(ids) == 0 {
return errorsx.InvalidParamI18n("error.e0333")
}
kb := KnowledgeBaseService.Get(req.KnowledgeBaseID)
if kb == nil {
return errorsx.InvalidParamI18n("error.e0283")
}
if kb.KnowledgeType == string(enums.KnowledgeBaseTypeFAQ) {
return errorsx.InvalidParamI18n("error.e0026")
}
if _, err := KnowledgeDirectoryService.RequireUsableDirectory(req.KnowledgeBaseID, req.DirectoryID); err != nil {
return err
}
for _, id := range ids {
current := s.Get(id)
if current == nil || current.Status == enums.StatusDeleted {
return errorsx.InvalidParamI18n("error.e0218")
}
if current.KnowledgeBaseID != req.KnowledgeBaseID {
return errorsx.InvalidParamI18n("error.e0139")
}
}
now := time.Now()
if err := sqls.WithTransaction(func(ctx *sqls.TxContext) error {
for _, id := range ids {
if err := repositories.KnowledgeDocumentRepository.Updates(ctx.Tx, id, map[string]any{
"directory_id": req.DirectoryID,
"index_status": enums.KnowledgeDocumentIndexStatusPending,
"indexed_at": nil,
"index_error": "",
"update_user_id": operator.UserID,
"update_user_name": operator.Username,
"updated_at": now,
}); err != nil {
return err
}
}
return nil
}); err != nil {
return err
}
for _, id := range ids {
if err := rag.Index.IndexDocumentByID(context.Background(), id); err != nil {
slog.Error("failed to reindex moved knowledge document", "document_id", id, "error", err)
}
}
return nil
}
func (s *knowledgeDocumentService) BatchDeleteKnowledgeDocuments(req request.BatchDeleteKnowledgeDocumentRequest) error {
ids := uniquePositiveIDs(req.IDs)
if len(ids) == 0 {
return errorsx.InvalidParamI18n("error.e0331")
}
for _, id := range ids {
if err := s.DeleteKnowledgeDocument(id); err != nil {
return err
}
}
return nil
}
func (s *knowledgeDocumentService) buildKnowledgeDocumentModel(req request.CreateKnowledgeDocumentRequest) (*models.KnowledgeDocument, error) {
if strs.IsBlank(string(req.ContentType)) {
req.ContentType = enums.KnowledgeDocumentContentTypeHTML
}
if req.ContentType != enums.KnowledgeDocumentContentTypeHTML && req.ContentType != enums.KnowledgeDocumentContentTypeMarkdown {
return nil, errorsx.InvalidParamI18n("error.e0129")
}
plainText := rag.ExtractPlainText(req.Content, req.ContentType)
item := &models.KnowledgeDocument{
KnowledgeBaseID: req.KnowledgeBaseID,
DirectoryID: req.DirectoryID,
Title: req.Title,
ContentType: req.ContentType,
Content: req.Content,
}
if plainText != "" {
hash := sha256.Sum256([]byte(plainText))
item.ContentHash = hex.EncodeToString(hash[:])
}
return item, nil
}
func uniquePositiveIDs(ids []int64) []int64 {
result := make([]int64, 0, len(ids))
seen := make(map[int64]struct{}, len(ids))
for _, id := range ids {
if id <= 0 {
continue
}
if _, ok := seen[id]; ok {
continue
}
seen[id] = struct{}{}
result = append(result, id)
}
return result
}