ArticleService.java
package io.smartir.smartir.website.service;
import io.smartir.smartir.website.entity.Article;
import io.smartir.smartir.website.entity.Tag;
import io.smartir.smartir.website.exceptions.ArticleWithThisTitleExistsException;
import io.smartir.smartir.website.exceptions.NoTagWithThisIdException;
import io.smartir.smartir.website.exceptions.TagsCantBeNullException;
import io.smartir.smartir.website.helper.HelperFunctions;
import io.smartir.smartir.website.model.ArticleContentsModel;
import io.smartir.smartir.website.repository.ArticleRepository;
import io.smartir.smartir.website.repository.TagRepository;
import io.smartir.smartir.website.requests.ArticleFilterRequest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.nio.file.Path;
import java.time.OffsetDateTime;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@Service
public class ArticleService extends HelperFunctions {
private final FileService fileService;
private final ArticleRepository articleRepository;
private final TagRepository tagRepository;
public ArticleService(FileService fileService, ArticleRepository articleRepository, TagRepository tagRepository) {
this.fileService = fileService;
this.articleRepository = articleRepository;
this.tagRepository = tagRepository;
}
public String addArticle(MultipartFile imageFile, String title, MultipartFile bannerImageFile, List<ArticleContentsModel> articleContentsModel, List<Integer> tagIDs, String summary) throws Exception {
Optional<Article> articleCheck = articleRepository.findByTitleContainsAllIgnoreCase(title);
if (articleCheck.isPresent()) throw new ArticleWithThisTitleExistsException();
tagIDs.forEach(id -> tagRepository.findById(id).orElseThrow(NoTagWithThisIdException::new));
Path imagePath = fileService.moveImgFile(imageFile);
Path bannerImagePath = fileService.moveBannerImageFile(bannerImageFile);
List<Tag> tags = getTags(tagIDs);
Article article = new Article();
article.setImage(imagePath.toString());
article.setBannerImage(bannerImagePath.toString());
article.setTitle(title);
article.setContents(articleContentsModel);
article.setSummary(summary);
article.setTags(tags);
articleRepository.save(article);
return "Article added";
}
public String updateArticle(int id, MultipartFile imageFile, String title, MultipartFile bannerImageFile,
List<ArticleContentsModel> articleContentsModel, List<Integer> tagIDs, String summary) throws Exception {
Optional<Article> oldArticleOptional = articleRepository.findById(id);
if (oldArticleOptional.isPresent()) {
Article oldArticle = oldArticleOptional.get();
Optional<Article> articleCheck = articleRepository.findByTitleContainsAllIgnoreCase(title);
// Check if the new title is unique if changed
if (articleCheck.isPresent() && articleCheck.get().getId() != id) {
return "Another article with the same title exists. Update failed.";
}
if (imageFile != null) {
Path imagePath = fileService.moveImgFile(imageFile);
oldArticle.setImage(imagePath.toString());
}
if (bannerImageFile != null) {
Path bannerImagePath = fileService.moveBannerImageFile(bannerImageFile);
oldArticle.setBannerImage(bannerImagePath.toString());
}
// Set other attributes
oldArticle.setTitle(title);
oldArticle.setContents(articleContentsModel);
oldArticle.setSummary(summary);
// Set tags
List<Tag> tags = getTags(tagIDs);
oldArticle.setTags(tags);
articleRepository.save(oldArticle);
return "Article updated successfully.";
} else {
return "Article with id " + id + " not found.";
}
}
public Page<Article> getArticles(ArticleFilterRequest request) {
Pageable pageable = PageRequest.of(request.getPage(), request.getSize());
var articles = articleRepository.findAll(); // getting all result (deleted and not deleted)
articles = articles.stream().filter(article -> article.deletedAt == null).toList(); //filtering NON deleted results here
articles = filterByTag(articles, request);
articles = filterBySearch(articles, request.getSearch());
return makingPagination(articles, pageable);
}
public Optional<Article> getArticle(int articleId) {
return articleRepository.findById(articleId);
}
public String deleteArticle(int id) {
Optional<Article> article = articleRepository.findById(id);
if (article.isPresent()) {
var articleEntity = article.get();
articleEntity.setDeletedAt(OffsetDateTime.now());
articleRepository.save(articleEntity);
return "Article Deleted";
} else {
return "Article not found";
}
}
private List<Tag> getTags(List<Integer> tagIds) throws Exception {
List<Tag> existingTags = tagRepository.findByIdIn(tagIds);
for (Integer tagId : tagIds) {
boolean tagExists = existingTags.stream().anyMatch(tag -> tag.getId() == tagId);
if (!tagExists) {
throw new Exception("Invalid tag ID: " + tagId);
}
}
return existingTags;
}
public List<Article> filterByTag(List<Article> articles, ArticleFilterRequest request) {
if (request.getTagID() == null) throw new TagsCantBeNullException();
if (!request.getTagID().isEmpty())
return articles.stream().filter(
article -> article.getTags().stream().anyMatch(
tag -> request.getTagID().contains(tag.getId())
)).toList();
return articles;
}
public List<Article> filterBySearch(List<Article> articles, String search) {
if (!search.isEmpty() && !search.isBlank()) {
Predicate<Article> containsName = article ->
article.getTitle().toLowerCase().contains(search.toLowerCase());
Predicate<Article> containsSummary = article ->
article.getSummary() != null && article.getSummary().toLowerCase().contains(search.toLowerCase());
return articles.stream()
.filter(containsName.or(containsSummary))
.collect(Collectors.toList());
}
return articles;
}
public List<Article> getAllArticles() {
return articleRepository.findAll();
}
}