feat: user-interaction content read
This commit is contained in:
parent
1827e39471
commit
5da02723cb
@ -2,8 +2,8 @@ package com.magamochi.content.controller;
|
||||
|
||||
import com.magamochi.common.model.dto.DefaultResponseDTO;
|
||||
import com.magamochi.content.model.dto.MangaContentDTO;
|
||||
import com.magamochi.content.model.dto.MangaContentImagesDTO;
|
||||
import com.magamochi.content.service.ContentService;
|
||||
import com.magamochi.model.dto.MangaContentImagesDTO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
|
||||
@ -10,12 +10,12 @@ public record MangaContentDTO(
|
||||
@NotNull Boolean downloaded,
|
||||
@NotNull Boolean isRead,
|
||||
LanguageDTO language) {
|
||||
public static MangaContentDTO from(MangaContent mangaContent) {
|
||||
public static MangaContentDTO from(MangaContent mangaContent, boolean isRead) {
|
||||
return new MangaContentDTO(
|
||||
mangaContent.getId(),
|
||||
mangaContent.getTitle(),
|
||||
mangaContent.getDownloaded(),
|
||||
false,
|
||||
isRead,
|
||||
LanguageDTO.from(mangaContent.getLanguage()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package com.magamochi.model.dto;
|
||||
package com.magamochi.content.model.dto;
|
||||
|
||||
import com.magamochi.content.model.entity.MangaContent;
|
||||
import com.magamochi.content.model.entity.MangaContentImage;
|
||||
@ -3,9 +3,10 @@ package com.magamochi.content.service;
|
||||
import com.magamochi.catalog.service.MangaContentProviderService;
|
||||
import com.magamochi.common.exception.NotFoundException;
|
||||
import com.magamochi.content.model.dto.MangaContentDTO;
|
||||
import com.magamochi.content.model.dto.MangaContentImagesDTO;
|
||||
import com.magamochi.content.model.entity.MangaContent;
|
||||
import com.magamochi.content.model.repository.MangaContentRepository;
|
||||
import com.magamochi.model.dto.MangaContentImagesDTO;
|
||||
import com.magamochi.userinteraction.service.UserMangaContentReadService;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
@ -16,6 +17,7 @@ import org.springframework.stereotype.Service;
|
||||
@RequiredArgsConstructor
|
||||
public class ContentService {
|
||||
private final MangaContentProviderService mangaContentProviderService;
|
||||
private final UserMangaContentReadService userMangaContentReadService;
|
||||
|
||||
private final MangaContentRepository mangaContentRepository;
|
||||
|
||||
@ -24,7 +26,11 @@ public class ContentService {
|
||||
|
||||
return mangaContentProvider.getMangaContents().stream()
|
||||
.sorted(Comparator.comparing(MangaContent::getId))
|
||||
.map(MangaContentDTO::from)
|
||||
.map(
|
||||
mangaContent -> {
|
||||
var isRead = userMangaContentReadService.isRead(mangaContent.getId());
|
||||
return MangaContentDTO.from(mangaContent, isRead);
|
||||
})
|
||||
.toList();
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
package com.magamochi.controller;
|
||||
|
||||
import com.magamochi.common.model.dto.DefaultResponseDTO;
|
||||
import com.magamochi.model.enumeration.ArchiveFileType;
|
||||
import com.magamochi.service.MangaChapterService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
@ -20,18 +19,6 @@ import org.springframework.web.bind.annotation.*;
|
||||
public class MangaChapterController {
|
||||
private final MangaChapterService mangaChapterService;
|
||||
|
||||
@Operation(
|
||||
summary = "Mark a chapter as read",
|
||||
description = "Mark a chapter as read by its ID.",
|
||||
tags = {"Manga Chapter"},
|
||||
operationId = "markAsRead")
|
||||
@PostMapping("/{chapterId}/mark-as-read")
|
||||
public DefaultResponseDTO<Void> markAsRead(@PathVariable Long chapterId) {
|
||||
mangaChapterService.markAsRead(chapterId);
|
||||
|
||||
return DefaultResponseDTO.ok().build();
|
||||
}
|
||||
|
||||
@Operation(
|
||||
summary = "Download chapter archive",
|
||||
description = "Download a chapter as a compressed file by its ID.",
|
||||
|
||||
@ -27,14 +27,6 @@ public class MangaChapterService {
|
||||
|
||||
private final OldImageService oldImageService;
|
||||
|
||||
public void markAsRead(Long chapterId) {
|
||||
// TODO: implement this
|
||||
// var chapter = getMangaChapterThrowIfNotFound(chapterId);
|
||||
// chapter.setRead(true);
|
||||
//
|
||||
// mangaChapterRepository.save(chapter);
|
||||
}
|
||||
|
||||
public MangaChapterArchiveDTO downloadChapter(Long chapterId, ArchiveFileType archiveFileType)
|
||||
throws IOException {
|
||||
var chapter = getMangaChapterThrowIfNotFound(chapterId);
|
||||
|
||||
@ -2,6 +2,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 io.swagger.v3.oas.annotations.Operation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
@ -14,6 +15,7 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
@RequiredArgsConstructor
|
||||
public class UserInteractionController {
|
||||
private final UserFavoriteMangaService userFavoriteMangaService;
|
||||
private final UserMangaContentReadService userMangaContentReadService;
|
||||
|
||||
@Operation(
|
||||
summary = "Favorite a manga",
|
||||
@ -38,4 +40,16 @@ public class UserInteractionController {
|
||||
|
||||
return DefaultResponseDTO.ok().build();
|
||||
}
|
||||
|
||||
@Operation(
|
||||
summary = "Mark content as read",
|
||||
description = "Mark content as read by its ID.",
|
||||
tags = {"User Interaction"},
|
||||
operationId = "markContentAsRead")
|
||||
@PostMapping("/content/{mangaContentId}/read")
|
||||
public DefaultResponseDTO<Void> markContentAsRead(@PathVariable Long mangaContentId) {
|
||||
userMangaContentReadService.setRead(mangaContentId);
|
||||
|
||||
return DefaultResponseDTO.ok().build();
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,31 @@
|
||||
package com.magamochi.userinteraction.model.entity;
|
||||
|
||||
import com.magamochi.content.model.entity.MangaContent;
|
||||
import com.magamochi.user.model.entity.User;
|
||||
import jakarta.persistence.*;
|
||||
import java.time.Instant;
|
||||
import lombok.*;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
|
||||
@Entity
|
||||
@Table(name = "user_manga_content_read")
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
public class UserMangaContentRead {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "user_id")
|
||||
private User user;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "manga_content_id")
|
||||
private MangaContent mangaContent;
|
||||
|
||||
@CreationTimestamp private Instant createdAt;
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package com.magamochi.userinteraction.model.repository;
|
||||
|
||||
import com.magamochi.content.model.entity.MangaContent;
|
||||
import com.magamochi.user.model.entity.User;
|
||||
import com.magamochi.userinteraction.model.entity.UserMangaContentRead;
|
||||
import java.util.Optional;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface UserMangaContentReadRepository extends JpaRepository<UserMangaContentRead, Long> {
|
||||
boolean existsByUserAndMangaContent(User user, MangaContent mangaContent);
|
||||
|
||||
Optional<UserMangaContentRead> findByUserAndMangaContent(User user, MangaContent mangaContent);
|
||||
}
|
||||
@ -0,0 +1,63 @@
|
||||
package com.magamochi.userinteraction.service;
|
||||
|
||||
import com.magamochi.common.exception.NotFoundException;
|
||||
import com.magamochi.content.model.entity.MangaContent;
|
||||
import com.magamochi.content.model.repository.MangaContentRepository;
|
||||
import com.magamochi.user.service.UserService;
|
||||
import com.magamochi.userinteraction.model.entity.UserMangaContentRead;
|
||||
import com.magamochi.userinteraction.model.repository.UserMangaContentReadRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Log4j2
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class UserMangaContentReadService {
|
||||
private final UserService userService;
|
||||
|
||||
private final UserMangaContentReadRepository userMangaContentReadRepository;
|
||||
private final MangaContentRepository mangaContentRepository;
|
||||
|
||||
public void setRead(Long id) {
|
||||
try {
|
||||
var user = userService.getLoggedUserThrowIfNotFound();
|
||||
var mangaContent = findMangaContent(id);
|
||||
|
||||
if (userMangaContentReadRepository.existsByUserAndMangaContent(user, mangaContent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
userMangaContentReadRepository.save(
|
||||
UserMangaContentRead.builder().user(user).mangaContent(mangaContent).build());
|
||||
} catch (NotFoundException _) {
|
||||
}
|
||||
}
|
||||
|
||||
public void setUnread(Long id) {
|
||||
var user = userService.getLoggedUserThrowIfNotFound();
|
||||
var mangaContent = findMangaContent(id);
|
||||
|
||||
var mangaContentRead =
|
||||
userMangaContentReadRepository.findByUserAndMangaContent(user, mangaContent);
|
||||
|
||||
mangaContentRead.ifPresent(userMangaContentReadRepository::delete);
|
||||
}
|
||||
|
||||
public boolean isRead(Long id) {
|
||||
try {
|
||||
var user = userService.getLoggedUserThrowIfNotFound();
|
||||
var mangaContent = findMangaContent(id);
|
||||
|
||||
return userMangaContentReadRepository.existsByUserAndMangaContent(user, mangaContent);
|
||||
} catch (NotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private MangaContent findMangaContent(Long id) {
|
||||
return mangaContentRepository
|
||||
.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException("MangaContent not found for ID: " + id));
|
||||
}
|
||||
}
|
||||
8
src/main/resources/db/migration/V0003__CONTENT_READ.sql
Normal file
8
src/main/resources/db/migration/V0003__CONTENT_READ.sql
Normal file
@ -0,0 +1,8 @@
|
||||
CREATE TABLE user_manga_content_read
|
||||
(
|
||||
id BIGSERIAL NOT NULL PRIMARY KEY,
|
||||
manga_content_id BIGINT NOT NULL REFERENCES manga_contents (id) ON DELETE CASCADE,
|
||||
user_id BIGINT NOT NULL REFERENCES users (id) ON DELETE CASCADE,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE (user_id, manga_content_id)
|
||||
);
|
||||
Loading…
x
Reference in New Issue
Block a user