feat: manga follow

This commit is contained in:
Rodrigo Verdiani 2026-03-19 11:36:46 -03:00
parent f3def583ae
commit 1d55420d64
8 changed files with 85 additions and 70 deletions

View File

@ -8,10 +8,10 @@ import com.magamochi.catalog.model.dto.MangaListFilterDTO;
import com.magamochi.catalog.model.entity.Manga;
import com.magamochi.catalog.model.repository.MangaRepository;
import com.magamochi.common.exception.NotFoundException;
import com.magamochi.model.repository.UserMangaFollowRepository;
import com.magamochi.model.specification.MangaSpecification;
import com.magamochi.user.service.UserService;
import com.magamochi.userinteraction.model.repository.UserFavoriteMangaRepository;
import com.magamochi.userinteraction.model.repository.UserMangaFollowRepository;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

View File

@ -23,28 +23,4 @@ public class MangaController {
return DefaultResponseDTO.ok().build();
}
@Operation(
summary = "Follow the manga specified by its ID",
description = "Follow the manga specified by its ID.",
tags = {"Manga"},
operationId = "followManga")
@PostMapping("/{mangaId}/followManga")
public DefaultResponseDTO<Void> followManga(@PathVariable Long mangaId) {
oldMangaService.follow(mangaId);
return DefaultResponseDTO.ok().build();
}
@Operation(
summary = "Unfollow the manga specified by its ID",
description = "Unfollow the manga specified by its ID.",
tags = {"Manga"},
operationId = "unfollowManga")
@PostMapping("/{mangaId}/unfollowManga")
public DefaultResponseDTO<Void> unfollowManga(@PathVariable Long mangaId) {
oldMangaService.unfollow(mangaId);
return DefaultResponseDTO.ok().build();
}
}

View File

@ -1,30 +1,23 @@
package com.magamochi.service;
import com.magamochi.catalog.model.entity.Manga;
import com.magamochi.catalog.model.repository.MangaContentProviderRepository;
import com.magamochi.catalog.model.repository.MangaRepository;
import com.magamochi.common.exception.NotFoundException;
import com.magamochi.content.model.entity.MangaContent;
import com.magamochi.model.dto.*;
import com.magamochi.model.entity.UserMangaFollow;
import com.magamochi.model.repository.*;
import com.magamochi.queue.MangaChapterDownloadProducer;
import com.magamochi.user.service.UserService;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Log4j2
@Service
@RequiredArgsConstructor
public class OldMangaService {
private final UserService userService;
private final MangaRepository mangaRepository;
private final MangaContentProviderRepository mangaContentProviderRepository;
private final UserMangaFollowRepository userMangaFollowRepository;
private final MangaContentProviderRepository mangaContentProviderRepository;
private final MangaChapterDownloadProducer mangaChapterDownloadProducer;
@ -81,36 +74,4 @@ public class OldMangaService {
// mangaProvider.getContentProvider().getName())));
// }
public Manga findMangaByIdThrowIfNotFound(Long mangaId) {
return mangaRepository
.findById(mangaId)
.orElseThrow(() -> new NotFoundException("Manga not found for ID: " + mangaId));
}
@Transactional
public void follow(Long mangaId) {
var user = userService.getLoggedUserThrowIfNotFound();
var manga = findMangaByIdThrowIfNotFound(mangaId);
manga.setFollow(true);
if (userMangaFollowRepository.existsByUserAndManga(user, manga)) {
return;
}
userMangaFollowRepository.save(UserMangaFollow.builder().user(user).manga(manga).build());
}
@Transactional
public void unfollow(Long mangaId) {
var user = userService.getLoggedUserThrowIfNotFound();
var manga = findMangaByIdThrowIfNotFound(mangaId);
var userMangaFollow = userMangaFollowRepository.findByUserAndManga(user, manga);
userMangaFollow.ifPresent(userMangaFollowRepository::delete);
if (!userMangaFollowRepository.existsByManga(manga)) {
manga.setFollow(false);
}
}
}

View File

@ -7,7 +7,6 @@ import com.magamochi.queue.UpdateMangaFollowChapterListProducer;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@ -22,7 +21,7 @@ public class MangaFollowUpdateTask {
private final UpdateMangaFollowChapterListProducer producer;
@Scheduled(cron = "${manga-follow.cron-expression}")
// @Scheduled(cron = "${manga-follow.cron-expression}")
@Transactional
public void updateMangaFollow() {
if (!updateEnabled) {

View File

@ -3,6 +3,7 @@ package com.magamochi.userinteraction.controller;
import com.magamochi.common.model.dto.DefaultResponseDTO;
import com.magamochi.userinteraction.service.UserFavoriteMangaService;
import com.magamochi.userinteraction.service.UserMangaContentReadService;
import com.magamochi.userinteraction.service.UserMangaFollowService;
import io.swagger.v3.oas.annotations.Operation;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PathVariable;
@ -16,6 +17,7 @@ import org.springframework.web.bind.annotation.RestController;
public class UserInteractionController {
private final UserFavoriteMangaService userFavoriteMangaService;
private final UserMangaContentReadService userMangaContentReadService;
private final UserMangaFollowService userMangaFollowService;
@Operation(
summary = "Favorite a manga",
@ -52,4 +54,28 @@ public class UserInteractionController {
return DefaultResponseDTO.ok().build();
}
@Operation(
summary = "Follow the manga specified by its ID",
description = "Follow the manga specified by its ID.",
tags = {"User Interaction"},
operationId = "followManga")
@PostMapping("/manga/{mangaId}/follow")
public DefaultResponseDTO<Void> followManga(@PathVariable Long mangaId) {
userMangaFollowService.follow(mangaId);
return DefaultResponseDTO.ok().build();
}
@Operation(
summary = "Unfollow the manga specified by its ID",
description = "Unfollow the manga specified by its ID.",
tags = {"User Interaction"},
operationId = "unfollowManga")
@PostMapping("/manga/{mangaId}/unfollow")
public DefaultResponseDTO<Void> unfollowManga(@PathVariable Long mangaId) {
userMangaFollowService.unfollow(mangaId);
return DefaultResponseDTO.ok().build();
}
}

View File

@ -1,4 +1,4 @@
package com.magamochi.model.entity;
package com.magamochi.userinteraction.model.entity;
import com.magamochi.catalog.model.entity.Manga;
import com.magamochi.user.model.entity.User;

View File

@ -1,8 +1,8 @@
package com.magamochi.model.repository;
package com.magamochi.userinteraction.model.repository;
import com.magamochi.catalog.model.entity.Manga;
import com.magamochi.model.entity.UserMangaFollow;
import com.magamochi.user.model.entity.User;
import com.magamochi.userinteraction.model.entity.UserMangaFollow;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;

View File

@ -0,0 +1,53 @@
package com.magamochi.userinteraction.service;
import com.magamochi.catalog.model.entity.Manga;
import com.magamochi.catalog.model.repository.MangaRepository;
import com.magamochi.common.exception.NotFoundException;
import com.magamochi.user.service.UserService;
import com.magamochi.userinteraction.model.entity.UserMangaFollow;
import com.magamochi.userinteraction.model.repository.UserMangaFollowRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@RequiredArgsConstructor
public class UserMangaFollowService {
private final UserService userService;
private final MangaRepository mangaRepository;
private final UserMangaFollowRepository userMangaFollowRepository;
@Transactional
public void follow(Long mangaId) {
var user = userService.getLoggedUserThrowIfNotFound();
var manga = findMangaByIdThrowIfNotFound(mangaId);
manga.setFollow(true);
if (userMangaFollowRepository.existsByUserAndManga(user, manga)) {
return;
}
userMangaFollowRepository.save(UserMangaFollow.builder().user(user).manga(manga).build());
}
@Transactional
public void unfollow(Long mangaId) {
var user = userService.getLoggedUserThrowIfNotFound();
var manga = findMangaByIdThrowIfNotFound(mangaId);
var userMangaFollow = userMangaFollowRepository.findByUserAndManga(user, manga);
userMangaFollow.ifPresent(userMangaFollowRepository::delete);
if (!userMangaFollowRepository.existsByManga(manga)) {
manga.setFollow(false);
}
}
public Manga findMangaByIdThrowIfNotFound(Long mangaId) {
return mangaRepository
.findById(mangaId)
.orElseThrow(() -> new NotFoundException("Manga not found for ID: " + mangaId));
}
}