narasiahli-be/app/module/knowledge_base/service/knowledge_base.service.go

338 lines
7.9 KiB
Go
Raw Normal View History

2026-01-14 01:29:35 +00:00
package service
import (
"context"
"fmt"
"io"
"mime"
"mime/multipart"
"narasi-ahli-be/app/module/knowledge_base/mapper"
"narasi-ahli-be/app/module/knowledge_base/repository"
"narasi-ahli-be/app/module/knowledge_base/request"
"narasi-ahli-be/app/module/knowledge_base/response"
config "narasi-ahli-be/config/config"
minioStorage "narasi-ahli-be/config/config"
"narasi-ahli-be/utils/paginator"
"path/filepath"
"strings"
"time"
"github.com/gofiber/fiber/v2"
"github.com/minio/minio-go/v7"
"github.com/rs/zerolog"
)
type KnowledgeBaseService struct {
Repo repository.KnowledgeBaseRepositoryInterface
Log zerolog.Logger
Cfg *config.Config
MinioStorage *minioStorage.MinioStorage
}
type KnowledgeBaseServiceInterface interface {
All(req request.KnowledgeBaseQueryRequest) (data []*response.KnowledgeBaseResponse, paging paginator.Pagination, err error)
Show(id uint) (data *response.KnowledgeBaseResponse, err error)
Create(c *fiber.Ctx) (data *response.KnowledgeBaseResponse, err error)
Update(id uint, req request.KnowledgeBaseUpdateRequest) (data *response.KnowledgeBaseResponse, err error)
Delete(id uint) error
Viewer(c *fiber.Ctx) error
UpdateStatus(id uint, status int) (data *response.KnowledgeBaseResponse, err error)
}
func NewKnowledgeBaseService(
repo repository.KnowledgeBaseRepositoryInterface,
log zerolog.Logger,
cfg *config.Config,
minioStorage *minioStorage.MinioStorage,
) KnowledgeBaseServiceInterface {
return &KnowledgeBaseService{
Repo: repo,
Log: log,
Cfg: cfg,
MinioStorage: minioStorage,
}
}
func (s *KnowledgeBaseService) uploadFileToMinio(
ctx context.Context,
minioClient *minio.Client,
bucketName string,
agentId string,
folder string,
fileHeader *multipart.FileHeader,
) (*string, error) {
if fileHeader == nil {
return nil, nil
}
src, err := fileHeader.Open()
if err != nil {
return nil, err
}
defer src.Close()
filename := filepath.Base(fileHeader.Filename)
filename = strings.ReplaceAll(filename, " ", "")
ext := filepath.Ext(filename)
filenameWithoutExt := strings.TrimSuffix(filename, ext)
now := time.Now()
newFilename := fmt.Sprintf("%s_%d%s", filenameWithoutExt, now.UnixNano(), ext)
objectName := fmt.Sprintf(
"knowledge-base/%s/%s/%s",
agentId,
folder,
newFilename,
)
_, err = minioClient.PutObject(
ctx,
bucketName,
objectName,
src,
fileHeader.Size,
minio.PutObjectOptions{},
)
if err != nil {
return nil, err
}
return &newFilename, nil
}
func (s *KnowledgeBaseService) All(req request.KnowledgeBaseQueryRequest) (data []*response.KnowledgeBaseResponse, paging paginator.Pagination, err error) {
results, paging, err := s.Repo.GetAll(req)
if err != nil {
return nil, paging, err
}
host := s.Cfg.App.Domain
for _, item := range results {
data = append(data, mapper.KnowledgeBaseResponseMapper(item, host))
}
return data, paging, nil
}
func getFileExtension(filename string) string {
parts := strings.Split(filename, ".")
if len(parts) == 1 || (len(parts) == 2 && parts[0] == "") {
return ""
}
return parts[len(parts)-1]
}
func (s *KnowledgeBaseService) Viewer(c *fiber.Ctx) (err error) {
filename := c.Params("filename")
result, folder, err := s.Repo.FindByFilename(filename)
if err != nil {
return err
}
ctx := context.Background()
bucketName := s.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName
objectName := fmt.Sprintf("knowledge-base/%s/%s/%s", *result.AgentId, folder, filename)
s.Log.Info().
Str("timestamp", time.Now().Format(time.RFC3339)).
Str("Service:Resource", "KnowledgeBase:Viewer").
Interface("objectName", objectName).
Msg("")
minioClient, err := s.MinioStorage.ConnectMinio()
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": true,
"msg": err.Error(),
})
}
fileContent, err := minioClient.GetObject(ctx, bucketName, objectName, minio.GetObjectOptions{})
if err != nil {
return err
}
defer fileContent.Close()
contentType := mime.TypeByExtension("." + getFileExtension(objectName))
if contentType == "" {
contentType = "application/octet-stream"
}
c.Set("Content-Type", contentType)
if _, err := io.Copy(c.Response().BodyWriter(), fileContent); err != nil {
return err
}
return nil
}
func (s *KnowledgeBaseService) Show(id uint) (data *response.KnowledgeBaseResponse, err error) {
result, err := s.Repo.FindOne(id)
if err != nil {
return nil, err
}
host := s.Cfg.App.Domain
return mapper.KnowledgeBaseResponseMapper(result, host), nil
}
func (s *KnowledgeBaseService) Create(c *fiber.Ctx) (data *response.KnowledgeBaseResponse, err error) {
req := new(request.KnowledgeBaseCreateRequest)
if err := c.BodyParser(req); err != nil {
return nil, err
}
if req.AgentId == nil || *req.AgentId == "" {
return nil, fiber.NewError(fiber.StatusBadRequest, "agentId is required")
}
if req.AgentName == nil || *req.AgentName == "" {
return nil, fiber.NewError(fiber.StatusBadRequest, "agentName is required")
}
if req.Title == nil || *req.Title == "" {
return nil, fiber.NewError(fiber.StatusBadRequest, "title is required")
}
fileJournal, _ := c.FormFile("fileJournal")
fileAudio, _ := c.FormFile("fileAudio")
fileVideo, _ := c.FormFile("fileVideo")
minioClient, err := s.MinioStorage.ConnectMinio()
if err != nil {
return nil, err
}
ctx := context.Background()
bucketName := s.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName
journalFilename, err := s.uploadFileToMinio(ctx, minioClient, bucketName, *req.AgentId, "journal", fileJournal)
if err != nil {
return nil, err
}
audioFilename, err := s.uploadFileToMinio(ctx, minioClient, bucketName, *req.AgentId, "audio", fileAudio)
if err != nil {
return nil, err
}
videoFilename, err := s.uploadFileToMinio(ctx, minioClient, bucketName, *req.AgentId, "video", fileVideo)
if err != nil {
return nil, err
}
entity := req.ToEntity()
viewerBase := s.Cfg.App.Domain + "/knowledge-base/viewer/"
if journalFilename != nil {
journalUrl := viewerBase + *journalFilename
entity.FileJournalUrl = &journalUrl
}
if audioFilename != nil {
audioUrl := viewerBase + *audioFilename
entity.FileAudioUrl = &audioUrl
}
if videoFilename != nil {
videoUrl := viewerBase + *videoFilename
entity.FileVideoUrl = &videoUrl
}
if req.Status < 0 {
entity.Status = 0
}
err = s.Repo.Create(entity)
if err != nil {
return nil, err
}
result, err := s.Repo.FindOne(entity.ID)
if err != nil {
return nil, err
}
host := s.Cfg.App.Domain
return mapper.KnowledgeBaseResponseMapper(result, host), nil
}
func (s *KnowledgeBaseService) Update(id uint, req request.KnowledgeBaseUpdateRequest) (data *response.KnowledgeBaseResponse, err error) {
old, err := s.Repo.FindOne(id)
if err != nil {
return nil, err
}
old.AgentId = req.AgentId
old.AgentName = req.AgentName
old.Title = req.Title
old.Status = req.Status
old.FileJournalUrl = req.FileJournalUrl
old.FileAudioUrl = req.FileAudioUrl
old.FileVideoUrl = req.FileVideoUrl
if req.IsActive != nil {
old.IsActive = *req.IsActive
}
old.UpdatedAt = time.Now()
err = s.Repo.Update(id, old)
if err != nil {
return nil, err
}
updated, err := s.Repo.FindOne(id)
if err != nil {
return nil, err
}
host := s.Cfg.App.Domain
return mapper.KnowledgeBaseResponseMapper(updated, host), nil
}
func (s *KnowledgeBaseService) Delete(id uint) error {
result, err := s.Repo.FindOne(id)
if err != nil {
return err
}
result.IsActive = false
result.UpdatedAt = time.Now()
return s.Repo.Update(id, result)
}
func (s *KnowledgeBaseService) UpdateStatus(id uint, status int) (data *response.KnowledgeBaseResponse, err error) {
old, err := s.Repo.FindOne(id)
if err != nil {
return nil, err
}
old.Status = status
old.UpdatedAt = time.Now()
err = s.Repo.Update(id, old)
if err != nil {
return nil, err
}
updated, err := s.Repo.FindOne(id)
if err != nil {
return nil, err
}
host := s.Cfg.App.Domain
return mapper.KnowledgeBaseResponseMapper(updated, host), nil
}