package service import ( "errors" "web-qudo-be/app/database/entity" articlesRepository "web-qudo-be/app/module/articles/repository" "web-qudo-be/app/module/bookmarks/mapper" "web-qudo-be/app/module/bookmarks/repository" "web-qudo-be/app/module/bookmarks/request" "web-qudo-be/app/module/bookmarks/response" usersRepository "web-qudo-be/app/module/users/repository" "web-qudo-be/utils/paginator" utilSvc "web-qudo-be/utils/service" "github.com/google/uuid" "github.com/rs/zerolog" ) // BookmarksService type bookmarksService struct { Repo repository.BookmarksRepository ArticlesRepo articlesRepository.ArticlesRepository UsersRepo usersRepository.UsersRepository Log zerolog.Logger } // BookmarksService define interface of IBookmarksService type BookmarksService interface { All(clientId *uuid.UUID, authToken string, req request.BookmarksQueryRequest) (bookmarks []*response.BookmarksResponse, paging paginator.Pagination, err error) Show(clientId *uuid.UUID, id uint) (bookmark *response.BookmarksResponse, err error) Save(clientId *uuid.UUID, req request.BookmarksCreateRequest, authToken string) (bookmark *entity.Bookmarks, err error) Delete(clientId *uuid.UUID, id uint) error GetByUserId(clientId *uuid.UUID, authToken string, req request.BookmarksQueryRequest) (bookmarks []*response.BookmarksResponse, paging paginator.Pagination, err error) ToggleBookmark(clientId *uuid.UUID, authToken string, articleId uint) (isBookmarked bool, err error) GetBookmarkSummary(clientId *uuid.UUID, authToken string) (summary *response.BookmarksSummaryResponse, err error) } // NewBookmarksService init BookmarksService func NewBookmarksService( repo repository.BookmarksRepository, articlesRepo articlesRepository.ArticlesRepository, usersRepo usersRepository.UsersRepository, log zerolog.Logger, ) BookmarksService { return &bookmarksService{ Repo: repo, ArticlesRepo: articlesRepo, UsersRepo: usersRepo, Log: log, } } // implement interface of IBookmarksService func (_i *bookmarksService) All(clientId *uuid.UUID, authToken string, req request.BookmarksQueryRequest) (bookmarks []*response.BookmarksResponse, paging paginator.Pagination, err error) { user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) if user == nil { _i.Log.Error().Msg("User not found from auth token") return nil, paging, errors.New("user not found") } req.UserId = &user.ID bookmarksEntity, paging, err := _i.Repo.GetAll(clientId, req) if err != nil { _i.Log.Error().Err(err).Msg("Failed to get all bookmarks") return nil, paging, err } // Convert []*entity.Bookmarks to []entity.Bookmarks var bookmarksSlice []entity.Bookmarks for _, b := range bookmarksEntity { bookmarksSlice = append(bookmarksSlice, *b) } bookmarks = mapper.ToBookmarksResponseList(bookmarksSlice) return bookmarks, paging, nil } func (_i *bookmarksService) Show(clientId *uuid.UUID, id uint) (bookmark *response.BookmarksResponse, err error) { bookmarkEntity, err := _i.Repo.FindOne(clientId, id) if err != nil { _i.Log.Error().Err(err).Msg("Failed to show bookmark") return nil, err } bookmark = mapper.ToBookmarksResponse(bookmarkEntity) return bookmark, nil } func (_i *bookmarksService) Save(clientId *uuid.UUID, req request.BookmarksCreateRequest, authToken string) (bookmark *entity.Bookmarks, err error) { // Extract user info from auth token user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) if user == nil { _i.Log.Error().Msg("User not found from auth token") return nil, errors.New("user not found") } // Check if article exists _, err = _i.ArticlesRepo.FindOne(clientId, req.ArticleId) if err != nil { _i.Log.Error().Err(err).Msg("Article not found") return nil, errors.New("article not found") } // Check if bookmark already exists existingBookmark, err := _i.Repo.FindByUserAndArticle(clientId, user.ID, req.ArticleId) if err == nil && existingBookmark != nil { _i.Log.Error().Msg("Bookmark already exists") return nil, errors.New("article already bookmarked") } // Create new bookmark bookmarkEntity := req.ToEntity(user.ID) bookmark, err = _i.Repo.Create(clientId, bookmarkEntity) if err != nil { _i.Log.Error().Err(err).Msg("Failed to create bookmark") return nil, err } return bookmark, nil } func (_i *bookmarksService) Delete(clientId *uuid.UUID, id uint) error { err := _i.Repo.Delete(clientId, id) if err != nil { _i.Log.Error().Err(err).Msg("Failed to delete bookmark") return err } return nil } func (_i *bookmarksService) GetByUserId(clientId *uuid.UUID, authToken string, req request.BookmarksQueryRequest) (bookmarks []*response.BookmarksResponse, paging paginator.Pagination, err error) { // Extract user info from auth token user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) if user == nil { _i.Log.Error().Msg("User not found from auth token") return nil, paging, errors.New("user not found") } bookmarksEntity, paging, err := _i.Repo.GetByUserId(clientId, user.ID, req) if err != nil { _i.Log.Error().Err(err).Msg("Failed to get bookmarks by user ID") return nil, paging, err } // Convert []*entity.Bookmarks to []entity.Bookmarks var bookmarksSlice []entity.Bookmarks for _, b := range bookmarksEntity { bookmarksSlice = append(bookmarksSlice, *b) } bookmarks = mapper.ToBookmarksResponseList(bookmarksSlice) return bookmarks, paging, nil } func (_i *bookmarksService) ToggleBookmark(clientId *uuid.UUID, authToken string, articleId uint) (isBookmarked bool, err error) { // Extract user info from auth token user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) if user == nil { _i.Log.Error().Msg("User not found from auth token") return false, errors.New("user not found") } // Check if article exists _, err = _i.ArticlesRepo.FindOne(clientId, articleId) if err != nil { _i.Log.Error().Err(err).Msg("Article not found") return false, errors.New("article not found") } // Check if bookmark already exists existingBookmark, err := _i.Repo.FindByUserAndArticle(clientId, user.ID, articleId) if err == nil && existingBookmark != nil { // Bookmark exists, delete it err = _i.Repo.Delete(clientId, existingBookmark.ID) if err != nil { _i.Log.Error().Err(err).Msg("Failed to delete existing bookmark") return false, err } return false, nil // Bookmark removed } // Bookmark doesn't exist, create it bookmarkEntity := &entity.Bookmarks{ UserId: user.ID, ArticleId: articleId, IsActive: boolPtr(true), } _, err = _i.Repo.Create(clientId, bookmarkEntity) if err != nil { _i.Log.Error().Err(err).Msg("Failed to create bookmark") return false, err } return true, nil // Bookmark added } func (_i *bookmarksService) GetBookmarkSummary(clientId *uuid.UUID, authToken string) (summary *response.BookmarksSummaryResponse, err error) { // Extract user info from auth token user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) if user == nil { _i.Log.Error().Msg("User not found from auth token") return nil, errors.New("user not found") } // Get total count totalCount, err := _i.Repo.CountByUserId(clientId, user.ID) if err != nil { _i.Log.Error().Err(err).Msg("Failed to count user bookmarks") return nil, err } // Get recent bookmarks (last 5) req := request.BookmarksQueryRequest{ Pagination: &paginator.Pagination{ Page: 1, Limit: 5, }, } recentBookmarksEntity, _, err := _i.Repo.GetByUserId(clientId, user.ID, req) if err != nil { _i.Log.Error().Err(err).Msg("Failed to get recent bookmarks") return nil, err } // Convert []*entity.Bookmarks to []entity.Bookmarks var bookmarksSlice []entity.Bookmarks for _, b := range recentBookmarksEntity { bookmarksSlice = append(bookmarksSlice, *b) } recentBookmarks := mapper.ToBookmarksResponseList(bookmarksSlice) summary = &response.BookmarksSummaryResponse{ TotalBookmarks: int(totalCount), RecentBookmarks: recentBookmarks, } return summary, nil } // Helper function to create bool pointer func boolPtr(b bool) *bool { return &b }