diff --git a/src/main/java/com/jiwon/mylog/domain/category/service/CategoryService.java b/src/main/java/com/jiwon/mylog/domain/category/service/CategoryService.java index 1c302a8..0e070db 100644 --- a/src/main/java/com/jiwon/mylog/domain/category/service/CategoryService.java +++ b/src/main/java/com/jiwon/mylog/domain/category/service/CategoryService.java @@ -7,6 +7,7 @@ import com.jiwon.mylog.domain.category.dto.response.CategoryResponse; import com.jiwon.mylog.domain.category.dto.request.CategoryRequest; import com.jiwon.mylog.domain.category.repository.CategoryRepository; +import com.jiwon.mylog.domain.post.repository.PostRepository; import com.jiwon.mylog.domain.user.entity.User; import com.jiwon.mylog.global.common.error.exception.DuplicateException; import com.jiwon.mylog.global.common.error.ErrorCode; @@ -24,6 +25,7 @@ public class CategoryService { private final CategoryRepository categoryRepository; private final UserRepository userRepository; + private final PostRepository postRepository; @Transactional public CategoryResponse createCategory(Long userId, CategoryRequest categoryRequest) { @@ -48,6 +50,7 @@ public CategoryResponse updateCategory(Long userId, Long categoryId, CategoryReq @Transactional public void deleteCategory(Long userId, Long categoryId) { Category category = getUserCategory(userId, categoryId); + postRepository.updatePostCategory(categoryId); categoryRepository.delete(category); } diff --git a/src/main/java/com/jiwon/mylog/domain/event/NotificationEventListener.java b/src/main/java/com/jiwon/mylog/domain/event/NotificationEventListener.java index 5a080e3..e164555 100644 --- a/src/main/java/com/jiwon/mylog/domain/event/NotificationEventListener.java +++ b/src/main/java/com/jiwon/mylog/domain/event/NotificationEventListener.java @@ -1,6 +1,8 @@ package com.jiwon.mylog.domain.event; import com.jiwon.mylog.domain.event.dto.CommentCreatedEvent; +import com.jiwon.mylog.domain.event.dto.FollowCreatedEvent; +import com.jiwon.mylog.domain.event.dto.FollowDeletedEvent; import com.jiwon.mylog.domain.event.dto.LikeCreatedEvent; import com.jiwon.mylog.domain.notification.entity.Notification; import com.jiwon.mylog.domain.notification.repository.NotificationRepository; @@ -29,8 +31,7 @@ public class NotificationEventListener { @EventListener public void handleCommentCreated(CommentCreatedEvent event) { Long receiverId = event.getPostWriterId(); - User receiver = userRepository.findById(receiverId) - .orElseThrow(() -> new NotFoundException(ErrorCode.NOT_FOUND_USER)); + User receiver = getReceiver(receiverId); Notification notification = Notification.create( receiver, @@ -40,19 +41,14 @@ public void handleCommentCreated(CommentCreatedEvent event) { ); notificationRepository.save(notification); - try { - notificationService.sendNotification(receiverId, notification); - } catch(Exception e) { - log.warn("SSE 전송 실패"); - } + sendSSE(receiverId, notification); } @Transactional @EventListener public void handleLikeCreated(LikeCreatedEvent event) { Long receiverId = event.getPostWriterId(); - User receiver = userRepository.findById(receiverId) - .orElseThrow(() -> new NotFoundException(ErrorCode.NOT_FOUND_USER)); + User receiver = getReceiver(receiverId); Notification notification = Notification.create( receiver, @@ -62,6 +58,49 @@ public void handleLikeCreated(LikeCreatedEvent event) { ); notificationRepository.save(notification); + sendSSE(receiverId, notification); + } + + @Transactional + @EventListener + public void handleFollowCreated(FollowCreatedEvent event) { + Long receiverId = event.getReceiverId(); + User receiver = getReceiver(event.getReceiverId()); + + Notification notification = Notification.create( + receiver, + event.getFollowerName() + "왹이 잡 았다! 너 잡혔다!", + "/" + event.getFollowerId(), + NotificationType.FOLLOW + ); + notificationRepository.save(notification); + + sendSSE(receiverId, notification); + } + + @Transactional + @EventListener + public void handleUnFollowCreated(FollowDeletedEvent event) { + Long receiverId = event.getReceiverId(); + User receiver = getReceiver(receiverId); + + Notification notification = Notification.create( + receiver, + "오오자비로운" + event.getFollowerName() + "왹께서널놓아주시니", + "/" + event.getFollowerId(), + NotificationType.FOLLOW + ); + notificationRepository.save(notification); + + sendSSE(receiverId, notification); + } + + private User getReceiver(Long receiverId) { + return userRepository.findById(receiverId) + .orElseThrow(() -> new NotFoundException(ErrorCode.NOT_FOUND_USER)); + } + + private void sendSSE(Long receiverId, Notification notification) { try { notificationService.sendNotification(receiverId, notification); } catch(Exception e) { diff --git a/src/main/java/com/jiwon/mylog/domain/event/dto/FollowCreatedEvent.java b/src/main/java/com/jiwon/mylog/domain/event/dto/FollowCreatedEvent.java new file mode 100644 index 0000000..1fdab18 --- /dev/null +++ b/src/main/java/com/jiwon/mylog/domain/event/dto/FollowCreatedEvent.java @@ -0,0 +1,12 @@ +package com.jiwon.mylog.domain.event.dto; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class FollowCreatedEvent { + private final Long receiverId; + private final Long followerId; + private final String followerName; +} diff --git a/src/main/java/com/jiwon/mylog/domain/event/dto/FollowDeletedEvent.java b/src/main/java/com/jiwon/mylog/domain/event/dto/FollowDeletedEvent.java new file mode 100644 index 0000000..3398860 --- /dev/null +++ b/src/main/java/com/jiwon/mylog/domain/event/dto/FollowDeletedEvent.java @@ -0,0 +1,12 @@ +package com.jiwon.mylog.domain.event.dto; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class FollowDeletedEvent { + private final Long receiverId; + private final Long followerId; + private final String followerName; +} diff --git a/src/main/java/com/jiwon/mylog/domain/follow/entity/Follow.java b/src/main/java/com/jiwon/mylog/domain/follow/entity/Follow.java index eb61f68..09539f3 100644 --- a/src/main/java/com/jiwon/mylog/domain/follow/entity/Follow.java +++ b/src/main/java/com/jiwon/mylog/domain/follow/entity/Follow.java @@ -11,12 +11,14 @@ import jakarta.persistence.ManyToOne; import lombok.AllArgsConstructor; import lombok.Builder; +import lombok.Getter; import lombok.NoArgsConstructor; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import java.time.LocalDateTime; +@Getter @AllArgsConstructor @NoArgsConstructor @Builder diff --git a/src/main/java/com/jiwon/mylog/domain/follow/repository/FollowRepository.java b/src/main/java/com/jiwon/mylog/domain/follow/repository/FollowRepository.java index e398e3a..c6ef458 100644 --- a/src/main/java/com/jiwon/mylog/domain/follow/repository/FollowRepository.java +++ b/src/main/java/com/jiwon/mylog/domain/follow/repository/FollowRepository.java @@ -14,7 +14,10 @@ public interface FollowRepository extends JpaRepository { boolean existsByFromUserIdAndToUserId(Long fromUserId, Long toUserId); - Optional findByFromUserIdAndToUserId(Long fromUserId, Long toUserId); + @Query("select f from Follow f " + + "join fetch f.fromUser " + + "where f.fromUser.id = :fromUserId and f.toUser.id = :toUserId") + Optional findByFromUserIdAndToUserId(@Param("fromUserId") Long fromUserId, @Param("toUserId") Long toUserId); @Query("select " + "sum(case when f.fromUser.id = :userId then 1 else 0 end), " + diff --git a/src/main/java/com/jiwon/mylog/domain/follow/service/FollowService.java b/src/main/java/com/jiwon/mylog/domain/follow/service/FollowService.java index 7f2756b..cf3a285 100644 --- a/src/main/java/com/jiwon/mylog/domain/follow/service/FollowService.java +++ b/src/main/java/com/jiwon/mylog/domain/follow/service/FollowService.java @@ -1,5 +1,7 @@ package com.jiwon.mylog.domain.follow.service; +import com.jiwon.mylog.domain.event.dto.FollowCreatedEvent; +import com.jiwon.mylog.domain.event.dto.FollowDeletedEvent; import com.jiwon.mylog.domain.follow.dto.FollowCountResponse; import com.jiwon.mylog.domain.follow.dto.FollowListResponse; import com.jiwon.mylog.domain.follow.dto.FollowResponse; @@ -10,6 +12,7 @@ import com.jiwon.mylog.global.common.error.ErrorCode; import com.jiwon.mylog.global.common.error.exception.NotFoundException; import lombok.RequiredArgsConstructor; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -19,6 +22,7 @@ @Service public class FollowService { + private final ApplicationEventPublisher eventPublisher; private final UserRepository userRepository; private final FollowRepository followRepository; @@ -33,6 +37,16 @@ public void follow(Long fromUserId, Long toUserId) { .orElseThrow(() -> new NotFoundException(ErrorCode.NOT_FOUND_USER)); followRepository.save(Follow.follow(fromUser, toUser)); + + if (toUser.getId() != null) { + eventPublisher.publishEvent( + new FollowCreatedEvent( + toUser.getId(), + fromUser.getId(), + fromUser.getUsername() + ) + ); + } } @Transactional @@ -44,6 +58,16 @@ public void unfollow(Long fromUserId, Long toUserId) { .orElseThrow(() -> new NotFoundException(ErrorCode.NOT_FOUND)); followRepository.delete(follow); + + if (toUserId != null) { + eventPublisher.publishEvent( + new FollowDeletedEvent( + toUserId, + fromUserId, + follow.getFromUser().getUsername() + ) + ); + } } @Transactional(readOnly = true) diff --git a/src/main/java/com/jiwon/mylog/domain/notification/controller/NotificationController.java b/src/main/java/com/jiwon/mylog/domain/notification/controller/NotificationController.java index 5efeab1..6cd90df 100644 --- a/src/main/java/com/jiwon/mylog/domain/notification/controller/NotificationController.java +++ b/src/main/java/com/jiwon/mylog/domain/notification/controller/NotificationController.java @@ -58,7 +58,7 @@ public SseEmitter subscribe( HttpServletResponse response) { if (accessToken == null || !jwtService.validateToken(accessToken)) { - throw new UnauthorizedException(ErrorCode.UNAUTHORIZED); + throw new UnauthorizedException(ErrorCode.INVALID_TOKEN); } response.setHeader("Cache-Control", "no-cache"); diff --git a/src/main/java/com/jiwon/mylog/domain/notification/repository/NotificationRepository.java b/src/main/java/com/jiwon/mylog/domain/notification/repository/NotificationRepository.java index aded4c6..1cdec55 100644 --- a/src/main/java/com/jiwon/mylog/domain/notification/repository/NotificationRepository.java +++ b/src/main/java/com/jiwon/mylog/domain/notification/repository/NotificationRepository.java @@ -9,11 +9,16 @@ import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import java.time.LocalDateTime; import java.util.List; @Repository public interface NotificationRepository extends JpaRepository { + @Modifying + @Query("delete from Notification n where n.isRead = true and n.createdAt < :date") + void deleteAllByCreatedAtBefore(@Param("date") LocalDateTime date); + @Modifying(clearAutomatically = true) @Query("update Notification n set n.isRead = true where n.receiver.id = :receiverId and n.isRead = false") void updateReadStateByReceiverId(@Param("receiverId") Long receiverId); diff --git a/src/main/java/com/jiwon/mylog/domain/post/controller/AdminNoticeController.java b/src/main/java/com/jiwon/mylog/domain/post/controller/AdminNoticeController.java index 48b009d..799dfe6 100644 --- a/src/main/java/com/jiwon/mylog/domain/post/controller/AdminNoticeController.java +++ b/src/main/java/com/jiwon/mylog/domain/post/controller/AdminNoticeController.java @@ -48,7 +48,7 @@ public ResponseEntity hello(@LoginUser Long userId) { public ResponseEntity createNotice( @LoginUser Long userId, @Valid @RequestBody PostRequest postRequest) { - PostDetailResponse response = postService.createPost(userId, postRequest, true); + PostDetailResponse response = postService.createPost(userId, postRequest); return new ResponseEntity<>(response, HttpStatus.CREATED); } @@ -65,7 +65,7 @@ public ResponseEntity updateNotice( @LoginUser Long userId, @PathVariable("postId") Long postId, @Valid @RequestBody PostRequest postRequest) { - PostDetailResponse response = postService.updatePost(userId, postId, postRequest, true); + PostDetailResponse response = postService.updatePost(userId, postId, postRequest); return new ResponseEntity<>(response, HttpStatus.OK); } } diff --git a/src/main/java/com/jiwon/mylog/domain/post/controller/PostController.java b/src/main/java/com/jiwon/mylog/domain/post/controller/PostController.java index de0ec24..ee2c403 100644 --- a/src/main/java/com/jiwon/mylog/domain/post/controller/PostController.java +++ b/src/main/java/com/jiwon/mylog/domain/post/controller/PostController.java @@ -49,7 +49,7 @@ public class PostController { public ResponseEntity createPost( @LoginUser Long userId, @Valid @RequestBody PostRequest postRequest) { - PostDetailResponse response = postService.createPost(userId, postRequest, false); + PostDetailResponse response = postService.createPost(userId, postRequest); return new ResponseEntity<>(response, HttpStatus.CREATED); } @@ -66,7 +66,7 @@ public ResponseEntity updatePost( @LoginUser Long userId, @PathVariable("postId") Long postId, @Valid @RequestBody PostRequest postRequest) { - PostDetailResponse response = postService.updatePost(userId, postId, postRequest, false); + PostDetailResponse response = postService.updatePost(userId, postId, postRequest); return new ResponseEntity<>(response, HttpStatus.OK); } diff --git a/src/main/java/com/jiwon/mylog/domain/post/dto/request/PostRequest.java b/src/main/java/com/jiwon/mylog/domain/post/dto/request/PostRequest.java index e49d3a3..1ec8260 100644 --- a/src/main/java/com/jiwon/mylog/domain/post/dto/request/PostRequest.java +++ b/src/main/java/com/jiwon/mylog/domain/post/dto/request/PostRequest.java @@ -35,4 +35,7 @@ public class PostRequest { private List<@Valid TagRequest> tagRequests = new ArrayList<>(); private boolean pinned; + + @NotBlank(message = "게시글 타입을 지정해주세요.") + private String type; } \ No newline at end of file diff --git a/src/main/java/com/jiwon/mylog/domain/post/dto/response/PinnedPostResponse.java b/src/main/java/com/jiwon/mylog/domain/post/dto/response/PinnedPostResponse.java index 022a34e..1492880 100644 --- a/src/main/java/com/jiwon/mylog/domain/post/dto/response/PinnedPostResponse.java +++ b/src/main/java/com/jiwon/mylog/domain/post/dto/response/PinnedPostResponse.java @@ -3,6 +3,7 @@ import com.jiwon.mylog.domain.category.dto.response.CategoryResponse; import com.jiwon.mylog.domain.post.entity.Post; import com.jiwon.mylog.domain.tag.dto.response.TagResponse; +import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -13,6 +14,12 @@ public class PinnedPostResponse { private final String title; private final String contentPreview; + public PinnedPostResponse(Long postId, String title, String contentPreview) { + this.postId = postId; + this.title = title; + this.contentPreview = contentPreview; + } + public static PinnedPostResponse fromPost(Post post) { return PinnedPostResponse.builder() .postId(post.getId()) diff --git a/src/main/java/com/jiwon/mylog/domain/post/dto/response/PostDetailResponse.java b/src/main/java/com/jiwon/mylog/domain/post/dto/response/PostDetailResponse.java index 5d81bdc..c358556 100644 --- a/src/main/java/com/jiwon/mylog/domain/post/dto/response/PostDetailResponse.java +++ b/src/main/java/com/jiwon/mylog/domain/post/dto/response/PostDetailResponse.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.annotation.JsonFormat; import com.jiwon.mylog.domain.post.entity.Post; +import com.jiwon.mylog.domain.post.entity.PostType; import com.jiwon.mylog.global.common.enums.Visibility; import com.jiwon.mylog.domain.category.dto.response.CategoryResponse; import com.jiwon.mylog.domain.comment.dto.response.CommentResponse; @@ -30,13 +31,13 @@ public class PostDetailResponse { @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime createdAt; private boolean pinned; - private boolean isNotice; + private PostType type; private UserResponse user; private CategoryResponse category; private List tags; private List comments; - public PostDetailResponse(Long postId, String title, String content, String contentPreview, int views, PostStatus postStatus, Visibility visibility, LocalDateTime createdAt, boolean pinned, boolean isNotice, UserResponse user, CategoryResponse category) { + public PostDetailResponse(Long postId, String title, String content, String contentPreview, int views, PostStatus postStatus, Visibility visibility, LocalDateTime createdAt, boolean pinned, PostType type, UserResponse user, CategoryResponse category) { this.postId = postId; this.title = title; this.content = content; @@ -46,7 +47,7 @@ public PostDetailResponse(Long postId, String title, String content, String cont this.visibility = visibility; this.createdAt = createdAt; this.pinned = pinned; - this.isNotice = isNotice; + this.type = type; this.user = user; this.category = category; } @@ -67,7 +68,7 @@ public static PostDetailResponse fromPost(Post post) { .toList()) .createdAt(post.getCreatedAt()) .pinned(post.isPinned()) - .isNotice(post.isNotice()) + .type(post.getType()) .comments(post.getComments().stream() .map(comment -> CommentResponse.fromComment(comment)) .toList()) diff --git a/src/main/java/com/jiwon/mylog/domain/post/entity/Post.java b/src/main/java/com/jiwon/mylog/domain/post/entity/Post.java index 6a97c22..0454f4c 100644 --- a/src/main/java/com/jiwon/mylog/domain/post/entity/Post.java +++ b/src/main/java/com/jiwon/mylog/domain/post/entity/Post.java @@ -56,8 +56,9 @@ public class Post extends BaseEntity { @Builder.Default private boolean pinned = false; - @Builder.Default - private boolean isNotice = false; + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private PostType type; @Enumerated(value = EnumType.STRING) private PostStatus postStatus; @@ -85,7 +86,7 @@ public class Post extends BaseEntity { @OneToMany(mappedBy = "post") private List comments = new ArrayList<>(); - public static Post create(PostRequest request, boolean isNotice, User user, Category category, List tags) { + public static Post create(PostRequest request, User user, Category category, List tags) { Post post = Post.builder() .title(request.getTitle()) .content(request.getContent()) @@ -95,7 +96,7 @@ public static Post create(PostRequest request, boolean isNotice, User user, Cate .user(user) .category(category) .pinned(request.isPinned()) - .isNotice(isNotice) + .type(PostType.fromString(request.getType())) .build(); post.setTags(tags); return post; diff --git a/src/main/java/com/jiwon/mylog/domain/post/entity/PostType.java b/src/main/java/com/jiwon/mylog/domain/post/entity/PostType.java new file mode 100644 index 0000000..1fe0c7d --- /dev/null +++ b/src/main/java/com/jiwon/mylog/domain/post/entity/PostType.java @@ -0,0 +1,25 @@ +package com.jiwon.mylog.domain.post.entity; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import com.jiwon.mylog.global.common.enums.BaseEnum; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public enum PostType implements BaseEnum { + NORMAL("일반 글"), + NOTICE("공지"); + + private final String label; + + @JsonValue + @Override + public String getStatus() { + return label; + } + + @JsonCreator + public static PostType fromString(String value) { + return BaseEnum.fromString(PostType.class, value); + } +} diff --git a/src/main/java/com/jiwon/mylog/domain/post/repository/PostRepository.java b/src/main/java/com/jiwon/mylog/domain/post/repository/PostRepository.java index 1271eed..18619ac 100644 --- a/src/main/java/com/jiwon/mylog/domain/post/repository/PostRepository.java +++ b/src/main/java/com/jiwon/mylog/domain/post/repository/PostRepository.java @@ -1,5 +1,6 @@ package com.jiwon.mylog.domain.post.repository; +import com.jiwon.mylog.domain.post.dto.response.PinnedPostResponse; import com.jiwon.mylog.domain.post.entity.Post; import java.util.List; @@ -19,16 +20,20 @@ public interface PostRepository extends JpaRepository, PostRepositor @Query("update Post p set p.views = :view where p.id = :postId") void updatePostView(@Param("postId") Long postId, @Param("view") int view); - @Query(value = "select p from Post p where p.deletedAt is null and p.isNotice = true order by p.createdAt desc", - countQuery = "select count(p) from Post p where p.deletedAt is null and p.isNotice = true") + @Modifying + @Query("update Post p set p.category = null where p.category.id = :categoryId") + void updatePostCategory(@Param("categoryId") Long categoryId); + + @Query(value = "select p from Post p where p.deletedAt is null and p.type = 'NOTICE' order by p.createdAt desc", + countQuery = "select count(p) from Post p where p.deletedAt is null and p.type = 'NOTICE'") Page findAllNotice(Pageable pageable); @Query(value = "select p from Post p " + "join fetch p.user u " + "left join fetch u.profileImage " + - "where p.deletedAt is null and p.visibility = 'PUBLIC' and p.isNotice = false " + + "where p.deletedAt is null and p.visibility = 'PUBLIC' and p.type = 'NORMAL' " + "order by p.createdAt desc", - countQuery = "select count(p) from Post p where p.deletedAt is null and p.visibility = 'PUBLIC' and p.isNotice = false") + countQuery = "select count(p) from Post p where p.deletedAt is null and p.visibility = 'PUBLIC' and p.type = 'NORMAL'") Page findAll(Pageable pageable); @Query(value = "select p from Post p left join fetch p.category left join fetch p.postTags pt left join fetch pt.tag t where p.deletedAt is null and p.user.id = :userId", @@ -47,6 +52,8 @@ public interface PostRepository extends JpaRepository, PostRepositor @Query("select p from Post p left join fetch p.postTags pt left join fetch pt.tag where p.id = :postId") Optional findWithTags(@Param("postId") Long postId); - @Query("select p from Post p where p.user.id = :userId and p.pinned = true and p.deletedAt is null") - List findPinnedPostsByUserId(@Param("userId") Long userId); + @Query("select new com.jiwon.mylog.domain.post.dto.response.PinnedPostResponse(p.id, p.title, p.contentPreview) " + + "from Post p " + + "where p.user.id = :userId and p.pinned = true and p.deletedAt is null") + List findPinnedPostsByUserId(@Param("userId") Long userId); } diff --git a/src/main/java/com/jiwon/mylog/domain/post/repository/PostRepositoryCustom.java b/src/main/java/com/jiwon/mylog/domain/post/repository/PostRepositoryCustom.java index 83c4f9f..fc77b1a 100644 --- a/src/main/java/com/jiwon/mylog/domain/post/repository/PostRepositoryCustom.java +++ b/src/main/java/com/jiwon/mylog/domain/post/repository/PostRepositoryCustom.java @@ -2,7 +2,7 @@ import com.jiwon.mylog.domain.post.dto.response.PostDetailResponse; import com.jiwon.mylog.domain.post.entity.Post; -import com.jiwon.mylog.domain.user.dto.response.UserActivitiesResponse; +import com.jiwon.mylog.domain.user.dto.response.UserActivityResponse; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -15,5 +15,5 @@ public interface PostRepositoryCustom { Page findByTags(Long userId, List tagIds, Pageable pageable); Optional findPostDetail(Long postId); - UserActivitiesResponse findUserActivities(Long userId, LocalDate start, LocalDate end); + List findUserActivities(Long userId, LocalDate start, LocalDate end); } diff --git a/src/main/java/com/jiwon/mylog/domain/post/repository/PostRepositoryImpl.java b/src/main/java/com/jiwon/mylog/domain/post/repository/PostRepositoryImpl.java index 345a89a..4f7c507 100644 --- a/src/main/java/com/jiwon/mylog/domain/post/repository/PostRepositoryImpl.java +++ b/src/main/java/com/jiwon/mylog/domain/post/repository/PostRepositoryImpl.java @@ -75,7 +75,7 @@ public Optional findPostDetail(Long postId) { POST.visibility, POST.createdAt, POST.pinned, - POST.isNotice, + POST.type, Projections.constructor(UserResponse.class, USER.id, USER.username, @@ -146,7 +146,7 @@ public Optional findPostDetail(Long postId) { } @Override - public UserActivitiesResponse findUserActivities(Long userId, LocalDate start, LocalDate end) { + public List findUserActivities(Long userId, LocalDate start, LocalDate end) { DateTemplate formattedDate = Expressions.dateTemplate( Date.class, "FUNCTION('DATE', {0})", @@ -171,7 +171,7 @@ public UserActivitiesResponse findUserActivities(Long userId, LocalDate start, L .orderBy(formattedDate.asc()) .fetch(); - return new UserActivitiesResponse(activities); + return activities; } private PageImpl createResult(Pageable pageable, BooleanBuilder builder) { diff --git a/src/main/java/com/jiwon/mylog/domain/post/service/PostService.java b/src/main/java/com/jiwon/mylog/domain/post/service/PostService.java index fd7c024..3b3ca4a 100644 --- a/src/main/java/com/jiwon/mylog/domain/post/service/PostService.java +++ b/src/main/java/com/jiwon/mylog/domain/post/service/PostService.java @@ -47,19 +47,19 @@ public class PostService { @Caching( put = @CachePut(value = "post::detail", key = "#result.postId", unless = "#result.postId == null"), evict = { - @CacheEvict(value = "post::notice", allEntries = true, condition = "#isNotice"), - @CacheEvict(value = "post::main", allEntries = true, condition = "!#isNotice"), - @CacheEvict(value = "post::list", allEntries = true, condition = "!#isNotice"), - @CacheEvict(value = "post::filter", allEntries = true, condition = "!#isNotice") + @CacheEvict(value = "post::notice", allEntries = true, condition = "#postRequest.type.equals('공지')"), + @CacheEvict(value = "post::main", allEntries = true, condition = "#postRequest.type.equals('일반 글')"), + @CacheEvict(value = "post::list", allEntries = true, condition = "#postRequest.type.equals('일반 글')"), + @CacheEvict(value = "post::filter", allEntries = true, condition = "#postRequest.type.equals('일반 글')") } ) @Transactional - public PostDetailResponse createPost(Long userId, PostRequest postRequest, boolean isNotice) { + public PostDetailResponse createPost(Long userId, PostRequest postRequest) { User user = getUserById(userId); Category category = getCategoryById(userId, postRequest.getCategoryId()); List tags = tagService.getTagsById(user, postRequest.getTagRequests()); - Post post = Post.create(postRequest, isNotice, user, category, tags); + Post post = Post.create(postRequest, user, category, tags); Post savedPost = postRepository.save(post); increaseRelatedPostInfo(category, tags); @@ -71,14 +71,14 @@ public PostDetailResponse createPost(Long userId, PostRequest postRequest, boole @Caching( put = @CachePut(value = "post::detail", key = "#postId", unless = "#postId == null"), evict = { - @CacheEvict(value = "post::notice", allEntries = true, condition = "#isNotice"), - @CacheEvict(value = "post::main", allEntries = true, condition = "!#isNotice"), - @CacheEvict(value = "post::list", allEntries = true, condition = "!#isNotice"), - @CacheEvict(value = "post::filter", allEntries = true, condition = "!#isNotice") + @CacheEvict(value = "post::notice", allEntries = true, condition = "#postRequest.type.equals('공지')"), + @CacheEvict(value = "post::main", allEntries = true, condition = "#postRequest.type.equals('일반 글')"), + @CacheEvict(value = "post::list", allEntries = true, condition = "#postRequest.type.equals('일반 글')"), + @CacheEvict(value = "post::filter", allEntries = true, condition = "#postRequest.type.equals('일반 글')") } ) @Transactional - public PostDetailResponse updatePost(Long userId, Long postId, PostRequest postRequest, boolean isNotice) { + public PostDetailResponse updatePost(Long userId, Long postId, PostRequest postRequest) { Post post = getPostWithDetails(postId); validateOwner(post, userId); diff --git a/src/main/java/com/jiwon/mylog/domain/post/service/PostViewService.java b/src/main/java/com/jiwon/mylog/domain/post/service/PostViewService.java index cbc63c8..437995f 100644 --- a/src/main/java/com/jiwon/mylog/domain/post/service/PostViewService.java +++ b/src/main/java/com/jiwon/mylog/domain/post/service/PostViewService.java @@ -11,22 +11,23 @@ public class PostViewService { private final RedisUtil redisUtil; private static final String VIEW_KEY_PREFIX = "post:view:"; - private static final String VIEW_COUNT_KEY_PREFIX = "post:view:count"; + private static final String VIEW_COUNT_KEY_PREFIX = "post:view:count:"; public int incrementPostView(Long postId, int view, String userKey) { String existKey = VIEW_KEY_PREFIX + postId; - String countKey = VIEW_COUNT_KEY_PREFIX; + String countKey = VIEW_COUNT_KEY_PREFIX + postId; boolean exist = redisUtil.existPostViewUser(existKey, userKey); if (!exist) { redisUtil.addPostViewUser(existKey, userKey); - redisUtil.increasePostView(countKey, postId.toString(), String.valueOf(view)); + redisUtil.increasePostView(countKey, String.valueOf(view)); } return getPostView(postId, view); } public int getPostView(Long postId, int view) { - return redisUtil.getPostView(VIEW_COUNT_KEY_PREFIX, postId.toString(), view); + String key = VIEW_COUNT_KEY_PREFIX + postId; + return redisUtil.getPostView(key, view); } } diff --git a/src/main/java/com/jiwon/mylog/domain/readme/controller/ReadmeController.java b/src/main/java/com/jiwon/mylog/domain/readme/controller/ReadmeController.java new file mode 100644 index 0000000..b14997b --- /dev/null +++ b/src/main/java/com/jiwon/mylog/domain/readme/controller/ReadmeController.java @@ -0,0 +1,35 @@ +package com.jiwon.mylog.domain.readme.controller; + +import com.jiwon.mylog.domain.readme.dto.ReadmeRequest; +import com.jiwon.mylog.domain.readme.dto.ReadmeResponse; +import com.jiwon.mylog.domain.readme.service.ReadmeService; +import com.jiwon.mylog.global.security.auth.annotation.LoginUser; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RequiredArgsConstructor +@RequestMapping("/api") +@RestController +public class ReadmeController { + + private final ReadmeService readmeService; + + @PostMapping("/readme") + public ResponseEntity editReadme( + @LoginUser Long userId, @RequestBody @Valid ReadmeRequest request) { + ReadmeResponse response = readmeService.editReadme(userId, request); + return ResponseEntity.ok(response); + } + + @DeleteMapping("/readme") + public ResponseEntity deleteReadme(@LoginUser Long userId) { + readmeService.deleteReadme(userId); + return ResponseEntity.noContent().build(); + } +} diff --git a/src/main/java/com/jiwon/mylog/domain/readme/dto/ReadmeRequest.java b/src/main/java/com/jiwon/mylog/domain/readme/dto/ReadmeRequest.java new file mode 100644 index 0000000..798448b --- /dev/null +++ b/src/main/java/com/jiwon/mylog/domain/readme/dto/ReadmeRequest.java @@ -0,0 +1,12 @@ +package com.jiwon.mylog.domain.readme.dto; + +import jakarta.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public class ReadmeRequest { + @NotBlank + private final String content; +} diff --git a/src/main/java/com/jiwon/mylog/domain/readme/dto/ReadmeResponse.java b/src/main/java/com/jiwon/mylog/domain/readme/dto/ReadmeResponse.java new file mode 100644 index 0000000..7b86901 --- /dev/null +++ b/src/main/java/com/jiwon/mylog/domain/readme/dto/ReadmeResponse.java @@ -0,0 +1,18 @@ +package com.jiwon.mylog.domain.readme.dto; + +import com.jiwon.mylog.domain.readme.entity.Readme; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public class ReadmeResponse { + private final String content; + + public static ReadmeResponse from(Readme readme) { + if (readme == null) { + return null; + } + return new ReadmeResponse(readme.getContent()); + } +} \ No newline at end of file diff --git a/src/main/java/com/jiwon/mylog/domain/readme/entity/Readme.java b/src/main/java/com/jiwon/mylog/domain/readme/entity/Readme.java new file mode 100644 index 0000000..1e0cf02 --- /dev/null +++ b/src/main/java/com/jiwon/mylog/domain/readme/entity/Readme.java @@ -0,0 +1,59 @@ +package com.jiwon.mylog.domain.readme.entity; + +import com.jiwon.mylog.domain.user.entity.User; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToOne; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import java.time.LocalDateTime; + +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Getter +@EntityListeners(AuditingEntityListener.class) +@Entity +public class Readme { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id", unique = true) + private User user; + + @Column(nullable = false, columnDefinition = "LONGTEXT") + private String content; + + @CreatedDate + @Column(nullable = false, updatable = false) + private LocalDateTime createdAt; + + @LastModifiedDate + @Column(nullable = false) + private LocalDateTime updatedAt; + + public static Readme from(User user, String content) { + return Readme.builder() + .user(user) + .content(content) + .build(); + } + + public void updateContent(String content) { + this.content = content; + } +} diff --git a/src/main/java/com/jiwon/mylog/domain/readme/repository/ReadmeRepository.java b/src/main/java/com/jiwon/mylog/domain/readme/repository/ReadmeRepository.java new file mode 100644 index 0000000..15478c5 --- /dev/null +++ b/src/main/java/com/jiwon/mylog/domain/readme/repository/ReadmeRepository.java @@ -0,0 +1,12 @@ +package com.jiwon.mylog.domain.readme.repository; + +import com.jiwon.mylog.domain.readme.entity.Readme; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface ReadmeRepository extends JpaRepository { + Optional findByUserId(Long userId); +} diff --git a/src/main/java/com/jiwon/mylog/domain/readme/service/ReadmeService.java b/src/main/java/com/jiwon/mylog/domain/readme/service/ReadmeService.java new file mode 100644 index 0000000..e52558f --- /dev/null +++ b/src/main/java/com/jiwon/mylog/domain/readme/service/ReadmeService.java @@ -0,0 +1,43 @@ +package com.jiwon.mylog.domain.readme.service; + +import com.jiwon.mylog.domain.readme.repository.ReadmeRepository; +import com.jiwon.mylog.domain.readme.dto.ReadmeRequest; +import com.jiwon.mylog.domain.readme.dto.ReadmeResponse; +import com.jiwon.mylog.domain.readme.entity.Readme; +import com.jiwon.mylog.domain.user.entity.User; +import com.jiwon.mylog.domain.user.repository.UserRepository; +import com.jiwon.mylog.global.common.error.ErrorCode; +import com.jiwon.mylog.global.common.error.exception.NotFoundException; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@RequiredArgsConstructor +@Service +public class ReadmeService { + + private final UserRepository userRepository; + private final ReadmeRepository readmeRepository; + + @Transactional + public ReadmeResponse editReadme(Long userId, ReadmeRequest request) { + Readme readme = readmeRepository.findByUserId(userId).orElse(null); + + if (readme == null) { + User user = userRepository.findById(userId) + .orElseThrow(() -> new NotFoundException(ErrorCode.NOT_FOUND_USER)); + readme = Readme.from(user, request.getContent()); + } else { + readme.updateContent(request.getContent()); + } + + return ReadmeResponse.from(readmeRepository.save(readme)); + } + + @Transactional + public void deleteReadme(Long userId) { + Readme readme = readmeRepository.findByUserId(userId) + .orElseThrow(() -> new NotFoundException(ErrorCode.NOT_FOUND)); + readmeRepository.delete(readme); + } +} \ No newline at end of file diff --git a/src/main/java/com/jiwon/mylog/domain/user/controller/UserController.java b/src/main/java/com/jiwon/mylog/domain/user/controller/UserController.java index 0ac98fb..bf5713c 100644 --- a/src/main/java/com/jiwon/mylog/domain/user/controller/UserController.java +++ b/src/main/java/com/jiwon/mylog/domain/user/controller/UserController.java @@ -1,5 +1,6 @@ package com.jiwon.mylog.domain.user.controller; +import com.jiwon.mylog.domain.user.service.UserBlogService; import com.jiwon.mylog.global.common.entity.PageResponse; import com.jiwon.mylog.domain.user.dto.request.UserProfileRequest; import com.jiwon.mylog.domain.user.dto.response.UserActivitiesResponse; @@ -36,6 +37,7 @@ public class UserController { private final UserService userService; + private final UserBlogService userBlogService; @GetMapping("/me") @Operation( @@ -74,28 +76,11 @@ public ResponseEntity updateMyProfile( @ApiResponse(responseCode = "404", description = "회원 정보를 찾을 수 없음") }) public ResponseEntity getUserMain(@PathVariable("userId") Long userId) { - UserMainResponse response = userService.getUserMain(userId); + UserMainResponse response = userBlogService.getUserMain(userId); return ResponseEntity.ok(response); } - @GetMapping("/{userId}/activity") - @Operation( - summary = "유저 활동 내역 조회 (활동 날짜 - 횟수)", - description = "일정 기간 동안의 유저 활동 일자와 활동 횟수를 조회한다.", - responses = { - @ApiResponse(responseCode = "200", description = "유저 활동 내역 조회 성공"), - @ApiResponse(responseCode = "404", description = "회원 정보를 찾을 수 없음") - } - ) - public ResponseEntity getUserActivity( - @PathVariable("userId") Long userId, - @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate, - @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate) { - UserActivitiesResponse response = userService.getUserActivity(userId, startDate, endDate); - return ResponseEntity.ok(response); - } - @GetMapping("/search") @Operation( summary = "회원 조회 (유저 이름으로 검색)", diff --git a/src/main/java/com/jiwon/mylog/domain/user/dto/response/UserMainResponse.java b/src/main/java/com/jiwon/mylog/domain/user/dto/response/UserMainResponse.java index d5d4a87..e26ff93 100644 --- a/src/main/java/com/jiwon/mylog/domain/user/dto/response/UserMainResponse.java +++ b/src/main/java/com/jiwon/mylog/domain/user/dto/response/UserMainResponse.java @@ -2,6 +2,7 @@ import com.jiwon.mylog.domain.post.dto.response.PinnedPostResponse; import com.jiwon.mylog.domain.post.entity.Post; +import com.jiwon.mylog.domain.readme.dto.ReadmeResponse; import com.jiwon.mylog.domain.user.entity.User; import lombok.Builder; import lombok.Getter; @@ -16,17 +17,24 @@ public class UserMainResponse { private final String username; private final String bio; private final String imageKey; + private final ReadmeResponse readme; private final List pinnedPosts; + private final List activities; - public static UserMainResponse fromUser(User user, List pinnedPosts) { + public static UserMainResponse fromUser( + User user, + ReadmeResponse readme, + List pinnedPosts, + List activities + ) { return UserMainResponse.builder() .userId(user.getId()) .username(user.getUsername()) .bio(user.getBio()) .imageKey(user.getProfileImage() == null ? "" : user.getProfileImage().getFileKey()) - .pinnedPosts(pinnedPosts.stream() - .map(PinnedPostResponse::fromPost) - .collect(Collectors.toList())) + .readme(readme) + .pinnedPosts(pinnedPosts) + .activities(activities) .build(); } } diff --git a/src/main/java/com/jiwon/mylog/domain/user/service/UserBlogService.java b/src/main/java/com/jiwon/mylog/domain/user/service/UserBlogService.java new file mode 100644 index 0000000..720884e --- /dev/null +++ b/src/main/java/com/jiwon/mylog/domain/user/service/UserBlogService.java @@ -0,0 +1,55 @@ +package com.jiwon.mylog.domain.user.service; + +import com.jiwon.mylog.domain.post.dto.response.PinnedPostResponse; +import com.jiwon.mylog.domain.post.repository.PostRepository; +import com.jiwon.mylog.domain.readme.dto.ReadmeResponse; +import com.jiwon.mylog.domain.readme.entity.Readme; +import com.jiwon.mylog.domain.readme.repository.ReadmeRepository; +import com.jiwon.mylog.domain.user.dto.response.UserActivityResponse; +import com.jiwon.mylog.domain.user.dto.response.UserMainResponse; +import com.jiwon.mylog.domain.user.entity.User; +import com.jiwon.mylog.domain.user.repository.UserRepository; +import com.jiwon.mylog.global.common.error.ErrorCode; +import com.jiwon.mylog.global.common.error.exception.NotFoundException; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDate; +import java.util.List; + +@RequiredArgsConstructor +@Service +public class UserBlogService { + + private final UserRepository userRepository; + private final PostRepository postRepository; + private final ReadmeRepository readmeRepository; + + @Transactional(readOnly = true) + public UserMainResponse getUserMain(Long userId) { + User user = userRepository.findUserWithProfileImage(userId) + .orElseThrow(() -> new NotFoundException(ErrorCode.NOT_FOUND_USER)); + + ReadmeResponse readme = getUserReadme(userId); + List pinnedPosts = getPinnedPosts(userId); + List activities = getUserAnnualActivities(userId); + + return UserMainResponse.fromUser(user, readme, pinnedPosts, activities); + } + + private ReadmeResponse getUserReadme(Long userId) { + Readme readme = readmeRepository.findByUserId(userId).orElse(null); + return ReadmeResponse.from(readme); + } + + private List getPinnedPosts(Long userId) { + return postRepository.findPinnedPostsByUserId(userId); + } + + private List getUserAnnualActivities(Long userId) { + LocalDate endDate = LocalDate.now(); + LocalDate startDate = endDate.minusYears(1); + return postRepository.findUserActivities(userId, startDate, endDate); + } +} diff --git a/src/main/java/com/jiwon/mylog/domain/user/service/UserService.java b/src/main/java/com/jiwon/mylog/domain/user/service/UserService.java index e54423c..508f0ba 100644 --- a/src/main/java/com/jiwon/mylog/domain/user/service/UserService.java +++ b/src/main/java/com/jiwon/mylog/domain/user/service/UserService.java @@ -6,11 +6,7 @@ import com.jiwon.mylog.domain.item.repository.UserItemRepository; import com.jiwon.mylog.domain.point.service.PointService; import com.jiwon.mylog.global.common.entity.PageResponse; -import com.jiwon.mylog.domain.post.entity.Post; -import com.jiwon.mylog.domain.post.repository.PostRepository; import com.jiwon.mylog.domain.user.dto.request.UserProfileRequest; -import com.jiwon.mylog.domain.user.dto.response.UserActivitiesResponse; -import com.jiwon.mylog.domain.user.dto.response.UserMainResponse; import com.jiwon.mylog.domain.user.dto.response.UserResponse; import com.jiwon.mylog.domain.user.entity.User; import com.jiwon.mylog.domain.user.repository.UserRepository; @@ -23,7 +19,6 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.time.LocalDate; import java.util.List; @RequiredArgsConstructor @@ -31,7 +26,6 @@ public class UserService { private final UserRepository userRepository; - private final PostRepository postRepository; private final ItemRepository itemRepository; private final UserItemRepository userItemRepository; private final PointService pointService; @@ -54,23 +48,6 @@ public UserResponse updateUserProfile(Long userId, UserProfileRequest userProfil return UserResponse.fromUser(user); } - @Transactional(readOnly = true) - public UserMainResponse getUserMain(Long userId) { - User user = userRepository.findUserWithProfileImage(userId) - .orElseThrow(() -> new NotFoundException(ErrorCode.NOT_FOUND_USER)); - List pinnedPosts = postRepository.findPinnedPostsByUserId(userId); - return UserMainResponse.fromUser(user, pinnedPosts); - - } - - @Transactional(readOnly = true) - public UserActivitiesResponse getUserActivity(Long userId, LocalDate startDate, LocalDate endDate) { - if (!userRepository.existsById(userId)) { - throw new NotFoundException(ErrorCode.NOT_FOUND_USER); - } - return postRepository.findUserActivities(userId, startDate, endDate); - } - @Transactional(readOnly = true) public PageResponse searchWithUsername(String username, Pageable pageable) { Page userPage = userRepository.findByUsernameContaining(username, pageable); diff --git a/src/main/java/com/jiwon/mylog/global/common/config/SecurityConfig.java b/src/main/java/com/jiwon/mylog/global/common/config/SecurityConfig.java index 530f344..80aceff 100644 --- a/src/main/java/com/jiwon/mylog/global/common/config/SecurityConfig.java +++ b/src/main/java/com/jiwon/mylog/global/common/config/SecurityConfig.java @@ -87,7 +87,7 @@ public SecurityFilterChain filterChain(HttpSecurity http, JwtService jwtService) // 단순 조회 (권한X) .requestMatchers(HttpMethod.GET, "/api/users/**", "/api/posts/**", "/api/categories/**", "/api/images/**", "/api/points/**", "/api/items/**", "/api/sse/**", "/api/likes/**").permitAll() // 블로그 사용자 - .requestMatchers("/api/users/**", "/api/posts/**", "/api/categories/**", "/api/comments/**", "/api/images/**", "/api/notifications/**", "/api/likes/**").authenticated() + .requestMatchers("/api/users/**", "/api/posts/**", "/api/categories/**", "/api/comments/**", "/api/images/**", "/api/notifications/**", "/api/likes/**", "/api/readme/**").authenticated() // 관리자 전용 .requestMatchers("/api/admin/**").hasRole("ADMIN")); diff --git a/src/main/java/com/jiwon/mylog/global/common/error/ErrorCode.java b/src/main/java/com/jiwon/mylog/global/common/error/ErrorCode.java index 4bcf56c..e97b9cf 100644 --- a/src/main/java/com/jiwon/mylog/global/common/error/ErrorCode.java +++ b/src/main/java/com/jiwon/mylog/global/common/error/ErrorCode.java @@ -10,7 +10,7 @@ public enum ErrorCode { INVALID_INPUT(HttpStatus.BAD_REQUEST, "입력값이 올바르지 않습니다."), INVALID_ACCOUNT_ID_OR_PASSWORD(HttpStatus.UNAUTHORIZED, "잘못된 아이디 또는 비밀번호입니다."), - INVALID_TOKEN(HttpStatus.BAD_REQUEST, "유효하지 않은 토큰입니다."), + INVALID_TOKEN(HttpStatus.UNAUTHORIZED, "유효하지 않은 토큰입니다."), EXPIRED_TOKEN(HttpStatus.UNAUTHORIZED, "만료된 토큰입니다."), FAIlED_MAIL_SEND(HttpStatus.INTERNAL_SERVER_ERROR, "메일 전송에 실패했습니다."), diff --git a/src/main/java/com/jiwon/mylog/global/redis/RedisUtil.java b/src/main/java/com/jiwon/mylog/global/redis/RedisUtil.java index 128dbc1..24cfb8e 100644 --- a/src/main/java/com/jiwon/mylog/global/redis/RedisUtil.java +++ b/src/main/java/com/jiwon/mylog/global/redis/RedisUtil.java @@ -2,12 +2,15 @@ import java.time.Duration; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; +@Slf4j @RequiredArgsConstructor @Service public class RedisUtil { @@ -42,24 +45,28 @@ public boolean existPostViewUser(String key, String value) { public void addPostViewUser(String key, String value) { redisTemplate.opsForSet().add(key, value); - redisTemplate.expire(key, Duration.ofHours(12L)); + redisTemplate.expire(key, Duration.ofHours(12)); } - public Long increasePostView(String key, String hashKey, String view) { - redisTemplate.opsForHash().putIfAbsent(key, hashKey, view); - return redisTemplate.opsForHash().increment(key, hashKey, 1); + public Long increasePostView(String key, String view) { + Boolean isNew = redisTemplate.opsForValue().setIfAbsent(key, view); + if (Boolean.TRUE.equals(isNew)) { + redisTemplate.expire(key, Duration.ofDays(7)); + } + return redisTemplate.opsForValue().increment(key, 1); } - public int getPostView(String key, String hashKey, int view) { - String value = (String) redisTemplate.opsForHash().get(key, hashKey); + public int getPostView(String key, int view) { + String value = redisTemplate.opsForValue().get(key); return value != null ? Integer.parseInt(value) : view; } - public Map getAllPostView(String key) { - return redisTemplate.opsForHash().entries(key).entrySet().stream() + public Map getAllPostView(String keyPrefix) { + Set keys = redisTemplate.keys(keyPrefix); + return keys.stream() .collect(Collectors.toMap( - e -> Long.parseLong(e.getKey().toString()), - e -> Integer.parseInt(e.getValue().toString()) + key -> Long.parseLong(key.replace(keyPrefix, "")), + key -> getPostView(key, 0) )); } diff --git a/src/main/java/com/jiwon/mylog/global/schedular/NotificationScheduler.java b/src/main/java/com/jiwon/mylog/global/schedular/NotificationScheduler.java new file mode 100644 index 0000000..ca89061 --- /dev/null +++ b/src/main/java/com/jiwon/mylog/global/schedular/NotificationScheduler.java @@ -0,0 +1,29 @@ +package com.jiwon.mylog.global.schedular; + +import com.jiwon.mylog.domain.notification.repository.NotificationRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; + +@Slf4j +@RequiredArgsConstructor +@Component +public class NotificationScheduler { + + private final NotificationRepository notificationRepository; + + @Scheduled(cron = "0 0 3 * * *") + @Transactional + public void deleteOldNotifications() { + LocalDateTime fourteenDaysAgo = LocalDateTime.now().minusDays(14); + try { + notificationRepository.deleteAllByCreatedAtBefore(fourteenDaysAgo); + } catch (Exception e) { + log.error("알림 삭제 실패: {}", e.getMessage()); + } + } +} diff --git a/src/main/java/com/jiwon/mylog/global/schedular/PostViewScheduler.java b/src/main/java/com/jiwon/mylog/global/schedular/PostViewScheduler.java index 34d1548..1454afb 100644 --- a/src/main/java/com/jiwon/mylog/global/schedular/PostViewScheduler.java +++ b/src/main/java/com/jiwon/mylog/global/schedular/PostViewScheduler.java @@ -17,7 +17,7 @@ public class PostViewScheduler { private final PostRepository postRepository; private final RedisUtil redisUtil; - private static final String REDIS_KEY = "post:view:count"; + private static final String REDIS_KEY = "post:view:count:"; @Scheduled(fixedRate = 10 * 60 * 1000L) @Transactional diff --git a/src/main/java/com/jiwon/mylog/global/security/auth/resolver/LoginUserArgumentResolver.java b/src/main/java/com/jiwon/mylog/global/security/auth/resolver/LoginUserArgumentResolver.java index 3756c38..a33077f 100644 --- a/src/main/java/com/jiwon/mylog/global/security/auth/resolver/LoginUserArgumentResolver.java +++ b/src/main/java/com/jiwon/mylog/global/security/auth/resolver/LoginUserArgumentResolver.java @@ -1,5 +1,7 @@ package com.jiwon.mylog.global.security.auth.resolver; +import com.jiwon.mylog.global.common.error.ErrorCode; +import com.jiwon.mylog.global.common.error.exception.UnauthorizedException; import com.jiwon.mylog.global.security.auth.annotation.LoginUser; import com.jiwon.mylog.global.security.jwt.JwtService; import jakarta.servlet.http.HttpServletRequest; @@ -33,7 +35,7 @@ public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer m String token = jwtService.getAccessToken(header); if (token == null || !jwtService.validateToken(token)) { - throw new IllegalArgumentException("잘못된 토큰입니다."); + throw new UnauthorizedException(ErrorCode.INVALID_TOKEN); } return jwtService.getUserId(token); diff --git a/src/main/java/com/jiwon/mylog/global/security/auth/service/AuthService.java b/src/main/java/com/jiwon/mylog/global/security/auth/service/AuthService.java index b9f1426..e718def 100644 --- a/src/main/java/com/jiwon/mylog/global/security/auth/service/AuthService.java +++ b/src/main/java/com/jiwon/mylog/global/security/auth/service/AuthService.java @@ -9,6 +9,7 @@ import com.jiwon.mylog.domain.user.dto.request.auth.UserLoginRequest; import com.jiwon.mylog.global.common.error.exception.CustomException; import com.jiwon.mylog.global.common.error.exception.DuplicateException; +import com.jiwon.mylog.global.common.error.exception.UnauthorizedException; import com.jiwon.mylog.global.mail.dto.request.MailRequest; import com.jiwon.mylog.global.mail.service.MailService; import com.jiwon.mylog.global.security.auth.user.CustomUserDetails; @@ -143,7 +144,7 @@ public void resetPassword(PasswordResetRequest passwordResetRequest) { private void validateToken(String refreshToken) { if (!jwtService.validateToken(refreshToken)) { - throw new IllegalArgumentException(ErrorCode.INVALID_TOKEN.getMessage()); + throw new UnauthorizedException(ErrorCode.INVALID_TOKEN); } } diff --git a/src/test/java/com/jiwon/mylog/controller/PostControllerTest.java b/src/test/java/com/jiwon/mylog/controller/PostControllerTest.java index a25bba2..2d67201 100644 --- a/src/test/java/com/jiwon/mylog/controller/PostControllerTest.java +++ b/src/test/java/com/jiwon/mylog/controller/PostControllerTest.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.jiwon.mylog.domain.post.controller.PostController; +import com.jiwon.mylog.domain.post.entity.PostType; import com.jiwon.mylog.global.common.enums.Visibility; import com.jiwon.mylog.domain.category.dto.response.CategoryResponse; import com.jiwon.mylog.domain.post.entity.PostStatus; @@ -50,7 +51,7 @@ class PostControllerTest { @Test void createPost() throws Exception { // given - PostRequest request = new PostRequest("title", "content", "contentPreview", "공개", 1L, null, false); + PostRequest request = new PostRequest("title", "content", "contentPreview", "공개", 1L, null, false, PostType.NORMAL.getStatus()); UserResponse user = createUser(1L, "testUser"); CategoryResponse category = createCategory(1L, "category"); List tags = List.of( @@ -60,7 +61,7 @@ void createPost() throws Exception { given(resolver.supportsParameter(any())).willReturn(true); given(resolver.resolveArgument(any(), any(), any(), any())).willReturn(1L); - given(postService.createPost(any(Long.class), any(PostRequest.class), any(Boolean.class))).willReturn(response); + given(postService.createPost(any(Long.class), any(PostRequest.class))).willReturn(response); // when & then mockMvc.perform(post("/api/posts") diff --git a/src/test/java/com/jiwon/mylog/domain/category/repository/CategoryRepositoryTest.java b/src/test/java/com/jiwon/mylog/domain/category/repository/CategoryRepositoryTest.java index 136af65..c6b04ec 100644 --- a/src/test/java/com/jiwon/mylog/domain/category/repository/CategoryRepositoryTest.java +++ b/src/test/java/com/jiwon/mylog/domain/category/repository/CategoryRepositoryTest.java @@ -12,8 +12,10 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.context.annotation.Import; +import org.springframework.test.context.ActiveProfiles; import java.util.List; import java.util.Optional; @@ -22,11 +24,14 @@ import static org.assertj.core.api.Assertions.tuple; import static org.junit.jupiter.api.Assertions.*; +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) +@ActiveProfiles("test") @DataJpaTest @Import({QueryDSLConfig.class, JpaAuditingConfiguration.class}) class CategoryRepositoryTest { - private static final List DEFAULT_CATEGORY_NAMES = List.of("category1", "category2", "category3"); + private static final List DEFAULT_CATEGORY_NAMES = + List.of("category1", "category2", "category3"); @Autowired private UserRepository userRepository; @@ -84,10 +89,12 @@ void findAllWithCountByUserId() { User savedUser = userRepository.save(user); List savedCategories = saveCategories(user, DEFAULT_CATEGORY_NAMES); + Category category1 = savedCategories.get(0); + Category category2 = savedCategories.get(1); - Post post1 = TestDataFactory.createPost("title1", "content", user, savedCategories.get(0)); - Post post2 = TestDataFactory.createPost("title2", "content", user, savedCategories.get(0)); - Post post3 = TestDataFactory.createPost("title3", "content", user, savedCategories.get(1)); + Post post1 = createPostWithCategory(user, category1); + Post post2 = createPostWithCategory(user, category1); + Post post3 = createPostWithCategory(user, category2); postRepository.saveAll(List.of(post1, post2, post3)); @@ -113,10 +120,12 @@ void findAllWithCountByUserId_WithUncategorizedPosts() { User savedUser = userRepository.save(user); List savedCategories = saveCategories(user, DEFAULT_CATEGORY_NAMES); + Category category1 = savedCategories.get(0); + Category category2 = savedCategories.get(1); - Post post1 = TestDataFactory.createPost("title1", "content", user, savedCategories.get(0)); - Post post2 = TestDataFactory.createPost("title2", "content", user, savedCategories.get(0)); - Post post3 = TestDataFactory.createPost("title3", "content", user, savedCategories.get(1)); + Post post1 = createPostWithCategory(user, category1); + Post post2 = createPostWithCategory(user, category1); + Post post3 = createPostWithCategory(user, category2); Post post4 = TestDataFactory.createPost("title4", "content", user, null); Post post5 = TestDataFactory.createPost("title5", "content", user, null); @@ -145,15 +154,17 @@ void findAllWithCountByUserId_ExcludeDeletedPosts() { User savedUser = userRepository.save(user); List savedCategories = saveCategories(user, DEFAULT_CATEGORY_NAMES); + Category category1 = savedCategories.get(0); + Category category2 = savedCategories.get(1); - Post post1 = TestDataFactory.createPost("title1", "content", user, savedCategories.get(0)); - Post post2 = TestDataFactory.createPost("title2", "content", user, savedCategories.get(0)); - Post post3 = TestDataFactory.createPost("title3", "content", user, savedCategories.get(1)); + Post post1 = createPostWithCategory(user, category1); + Post post2 = createPostWithCategory(user, category1); + Post post3 = createPostWithCategory(user, category2); Post post4 = TestDataFactory.createPost("title4", "content", user, null); Post post5 = TestDataFactory.createPost("title5", "content", user, null); post1.delete(); - post2.delete(); + post1.getCategory().decrementUsage(); postRepository.saveAll(List.of(post1, post2, post3, post4, post5)); @@ -165,13 +176,21 @@ void findAllWithCountByUserId_ExcludeDeletedPosts() { assertThat(result) .extracting(CategoryCountResponse::getName, CategoryCountResponse::getPostCount) .containsExactly( - tuple("category1", 0L), + tuple("category1", 1L), tuple("category2", 1L), tuple("category3", 0L), tuple("미분류", 2L) ); } + private Post createPostWithCategory(User user, Category category) { + Post post = TestDataFactory.createPost("title1", "content", user, category); + if (category != null) { + category.incrementUsage(); + } + return post; + } + private List saveCategories(User user, List names) { return categoryRepository.saveAll( names.stream() diff --git a/src/test/java/com/jiwon/mylog/service/CategoryServiceTest.java b/src/test/java/com/jiwon/mylog/domain/category/service/CategoryServiceTest.java similarity index 82% rename from src/test/java/com/jiwon/mylog/service/CategoryServiceTest.java rename to src/test/java/com/jiwon/mylog/domain/category/service/CategoryServiceTest.java index e17c262..473aa75 100644 --- a/src/test/java/com/jiwon/mylog/service/CategoryServiceTest.java +++ b/src/test/java/com/jiwon/mylog/domain/category/service/CategoryServiceTest.java @@ -1,13 +1,13 @@ -package com.jiwon.mylog.service; +package com.jiwon.mylog.domain.category.service; -import com.jiwon.mylog.domain.category.service.CategoryService; import com.jiwon.mylog.domain.category.dto.request.CategoryRequest; +import com.jiwon.mylog.domain.category.entity.Category; +import com.jiwon.mylog.domain.category.repository.CategoryRepository; import com.jiwon.mylog.domain.user.entity.User; -import com.jiwon.mylog.global.common.error.exception.DuplicateException; +import com.jiwon.mylog.domain.user.repository.UserRepository; import com.jiwon.mylog.global.common.error.ErrorCode; +import com.jiwon.mylog.global.common.error.exception.DuplicateException; import com.jiwon.mylog.global.common.error.exception.NotFoundException; -import com.jiwon.mylog.domain.category.repository.CategoryRepository; -import com.jiwon.mylog.domain.user.repository.UserRepository; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -18,9 +18,8 @@ import java.util.Optional; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.verify; @ExtendWith(MockitoExtension.class) class CategoryServiceTest { @@ -49,7 +48,6 @@ void create_Duplicate() { assertThatThrownBy(() -> categoryService.createCategory(userId, request)) .isInstanceOf(DuplicateException.class) .hasMessage(ErrorCode.DUPLICATE_CATEGORY.getMessage()); - } @DisplayName("존재하지 않는 카테고리 ID로 수정을 요청할 시 예외가 발생한다.") @@ -84,4 +82,19 @@ void update_Duplicate() { .isInstanceOf(DuplicateException.class) .hasMessage(ErrorCode.DUPLICATE_CATEGORY.getMessage()); } + + @DisplayName("존재하지 않는 카테고리를 삭제할 시 예외가 발생한다.") + @Test + void delete() { + // given + Long userId = 1L; + Long categoryId = 1L; + + given(categoryRepository.findByUserIdAndId(userId, categoryId)).willReturn(Optional.empty()); + + // when & then + assertThatThrownBy(() -> categoryService.deleteCategory(userId, categoryId)) + .isInstanceOf(NotFoundException.class) + .hasMessage(ErrorCode.NOT_FOUND_CATEGORY.getMessage()); + } } \ No newline at end of file diff --git a/src/test/java/com/jiwon/mylog/domain/post/repository/PostRepositoryTest.java b/src/test/java/com/jiwon/mylog/domain/post/repository/PostRepositoryTest.java new file mode 100644 index 0000000..54a6d68 --- /dev/null +++ b/src/test/java/com/jiwon/mylog/domain/post/repository/PostRepositoryTest.java @@ -0,0 +1,67 @@ +package com.jiwon.mylog.domain.post.repository; + +import com.jiwon.mylog.TestDataFactory; +import com.jiwon.mylog.domain.category.entity.Category; +import com.jiwon.mylog.domain.category.repository.CategoryRepository; +import com.jiwon.mylog.domain.post.entity.Post; +import com.jiwon.mylog.domain.user.entity.User; +import com.jiwon.mylog.domain.user.repository.UserRepository; +import com.jiwon.mylog.global.common.config.JpaAuditingConfiguration; +import com.jiwon.mylog.global.common.config.QueryDSLConfig; +import jakarta.persistence.EntityManager; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.ActiveProfiles; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; + +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) +@ActiveProfiles("test") +@DataJpaTest +@Import({QueryDSLConfig.class, JpaAuditingConfiguration.class}) +class PostRepositoryTest { + + @Autowired + private EntityManager em; + + @Autowired + private PostRepository postRepository; + + @Autowired + private UserRepository userRepository; + + @Autowired + private CategoryRepository categoryRepository; + + @BeforeEach + void setUp() { + userRepository.deleteAll(); + postRepository.deleteAll(); + categoryRepository.deleteAll(); + } + + @Test + void updatePostCategory() { + // given + User user = TestDataFactory.createUser("email", "accountId", "name"); + Category category = TestDataFactory.createCategory(user, "name"); + Post post = TestDataFactory.createPost("title", "content", user, category); + + userRepository.save(user); + Category savedCategory = categoryRepository.save(category); + Post savedPost = postRepository.save(post); + + // when + postRepository.updatePostCategory(savedCategory.getId()); + + // then + em.clear(); + Post updatedPost = postRepository.findById(savedPost.getId()).get(); + assertThat(updatedPost.getCategory()).isNull(); + } +} \ No newline at end of file diff --git a/src/test/java/com/jiwon/mylog/domain/post/service/PostServiceCacheTest.java b/src/test/java/com/jiwon/mylog/domain/post/service/PostServiceCacheTest.java index 574e5fc..8b56e45 100644 --- a/src/test/java/com/jiwon/mylog/domain/post/service/PostServiceCacheTest.java +++ b/src/test/java/com/jiwon/mylog/domain/post/service/PostServiceCacheTest.java @@ -2,6 +2,7 @@ import com.jiwon.mylog.domain.post.dto.request.PostRequest; import com.jiwon.mylog.domain.post.dto.response.PostDetailResponse; +import com.jiwon.mylog.domain.post.entity.PostType; import com.jiwon.mylog.domain.post.repository.PostRepository; import com.jiwon.mylog.global.common.error.exception.NotFoundException; import org.assertj.core.api.Assertions; @@ -51,13 +52,13 @@ void createPost() { Long userId = 1L; Long categoryId = 1L; Pageable pageable = PageRequest.of(0, 10); - PostRequest postRequest = new PostRequest("title", "content", "preview", "공개", 1L, List.of(), true); + PostRequest postRequest = new PostRequest("title", "content", "preview", "공개", 1L, List.of(), true, PostType.NORMAL.getStatus()); postService.getUserPosts(userId, pageable); postService.getPostsByCategoryAndTags(userId, categoryId, List.of(), pageable); // when - PostDetailResponse post = postService.createPost(userId, postRequest, false); + PostDetailResponse post = postService.createPost(userId, postRequest); Long postId = post.getPostId(); postService.getPost(postId); @@ -81,13 +82,13 @@ void updatePost() { Long postId = 1L; Long categoryId = 1L; Pageable pageable = PageRequest.of(0, 10); - PostRequest postRequest = new PostRequest("title", "content", "preview", "공개", 1L, List.of(), true); + PostRequest postRequest = new PostRequest("title", "content", "preview", "공개", 1L, List.of(), true, PostType.NORMAL.getStatus()); postService.getUserPosts(userId, pageable); // 캐시 생성 postService.getPostsByCategoryAndTags(userId, categoryId, List.of(), pageable); // 캐시 생성 // when - PostDetailResponse post = postService.updatePost(userId, postId, postRequest, false); + PostDetailResponse post = postService.updatePost(userId, postId, postRequest); postService.getPost(postId); postService.getUserPosts(userId, pageable); // 캐시 다시 생성 postService.getPostsByCategoryAndTags(userId, categoryId, List.of(), pageable); // 캐시 다시 생성 diff --git a/src/test/java/com/jiwon/mylog/domain/post/service/PostServiceTest.java b/src/test/java/com/jiwon/mylog/domain/post/service/PostServiceTest.java index 7cfc857..2b1c6be 100644 --- a/src/test/java/com/jiwon/mylog/domain/post/service/PostServiceTest.java +++ b/src/test/java/com/jiwon/mylog/domain/post/service/PostServiceTest.java @@ -1,11 +1,13 @@ package com.jiwon.mylog.domain.post.service; import com.jiwon.mylog.TestDataFactory; +import com.jiwon.mylog.config.EmbeddedRedisConfig; import com.jiwon.mylog.domain.category.entity.Category; import com.jiwon.mylog.domain.category.repository.CategoryRepository; import com.jiwon.mylog.domain.post.dto.request.PostRequest; import com.jiwon.mylog.domain.post.dto.response.PostDetailResponse; import com.jiwon.mylog.domain.post.entity.Post; +import com.jiwon.mylog.domain.post.entity.PostType; import com.jiwon.mylog.domain.post.repository.PostRepository; import com.jiwon.mylog.domain.tag.dto.request.TagRequest; import com.jiwon.mylog.domain.tag.entity.PostTag; @@ -15,10 +17,12 @@ import com.jiwon.mylog.domain.user.entity.User; import com.jiwon.mylog.domain.user.repository.UserRepository; import jakarta.persistence.EntityManager; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Import; import org.springframework.test.context.ActiveProfiles; import org.springframework.transaction.annotation.Transactional; @@ -27,6 +31,7 @@ import static org.assertj.core.api.Assertions.assertThat; @Transactional +@Import(EmbeddedRedisConfig.class) @SpringBootTest @ActiveProfiles("test") class PostServiceTest { @@ -52,6 +57,12 @@ class PostServiceTest { @Autowired private TagRepository tagRepository; + @BeforeEach + void setUp() { + userRepository.deleteAll(); + postRepository.deleteAll(); + } + @DisplayName("게시글 생성 시 연관된 정보(카테고리, 태그 게시글 수)가 올바르게 업데이트 된다.") @Test void createPost_연관정보() { @@ -63,10 +74,11 @@ class PostServiceTest { PostRequest postRequest = new PostRequest("title", "content", "preview", "공개", savedCategory.getId(), tagRequest, - false); + false, + PostType.NORMAL.getStatus()); // when - PostDetailResponse response = postService.createPost(savedUser.getId(), postRequest, false); + PostDetailResponse response = postService.createPost(savedUser.getId(), postRequest); // then Post post = postRepository.findById(response.getPostId()).get(); @@ -95,9 +107,10 @@ class PostServiceTest { PostRequest oldPostRequest = new PostRequest("title", "content", "preview", "공개", savedOldCategory.getId(), oldTagRequest, - false); + false, + PostType.NORMAL.getStatus()); - PostDetailResponse oldPostResponse = postService.createPost(savedUser.getId(), oldPostRequest, false); + PostDetailResponse oldPostResponse = postService.createPost(savedUser.getId(), oldPostRequest); em.flush(); em.clear(); @@ -108,10 +121,11 @@ class PostServiceTest { PostRequest newPostRequest = new PostRequest("title", "content", "preview", "공개", savedNewCategory.getId(), newTagRequest, - false); + false, + "일반 글"); // when - PostDetailResponse newPostResponse = postService.updatePost(savedUser.getId(), oldPostResponse.getPostId(), newPostRequest, false); + PostDetailResponse newPostResponse = postService.updatePost(savedUser.getId(), oldPostResponse.getPostId(), newPostRequest); em.flush(); em.clear(); @@ -145,8 +159,9 @@ class PostServiceTest { PostRequest postRequest = new PostRequest("title", "content", "preview", "공개", savedCategory.getId(), tagRequest, - false); - PostDetailResponse response = postService.createPost(savedUser.getId(), postRequest, false); + false, + PostType.NORMAL.getStatus()); + PostDetailResponse response = postService.createPost(savedUser.getId(), postRequest); em.flush(); em.clear(); diff --git a/src/test/java/com/jiwon/mylog/entity/post/dto/request/PostRequestTest.java b/src/test/java/com/jiwon/mylog/entity/post/dto/request/PostRequestTest.java index 19992b2..7e5c7ec 100644 --- a/src/test/java/com/jiwon/mylog/entity/post/dto/request/PostRequestTest.java +++ b/src/test/java/com/jiwon/mylog/entity/post/dto/request/PostRequestTest.java @@ -1,6 +1,7 @@ package com.jiwon.mylog.entity.post.dto.request; import com.jiwon.mylog.domain.post.dto.request.PostRequest; +import com.jiwon.mylog.domain.post.entity.PostType; import jakarta.validation.ConstraintViolation; import jakarta.validation.Validation; import jakarta.validation.Validator; @@ -30,7 +31,7 @@ void setUp() { @MethodSource("titles") void validateTitleLength(String title, boolean validated) { // given - PostRequest request = new PostRequest(title, "content", "preview", "visibility", 1L, null, false); + PostRequest request = new PostRequest(title, "content", "preview", "visibility", 1L, null, false, PostType.NORMAL.getStatus()); // when Set> violations = validator.validate(request); @@ -55,7 +56,7 @@ private static Stream titles() { @ValueSource(strings = {"", " "}) void validateTitleIsNotBlank(String title) { // given - PostRequest request = new PostRequest(title, "content", "preview", "visibility", 1L, null, false); + PostRequest request = new PostRequest(title, "content", "preview", "visibility", 1L, null, false, PostType.NORMAL.getStatus()); // when Set> violations = validator.validate(request); @@ -69,7 +70,7 @@ void validateTitleIsNotBlank(String title) { @ValueSource(strings = {"", " "}) void validateContentIsNotBlank(String content) { // given - PostRequest request = new PostRequest("title", content, "preview", "visibility", 1L, null, false); + PostRequest request = new PostRequest("title", content, "preview", "visibility", 1L, null, false, PostType.NORMAL.getStatus()); // when Set> violations = validator.validate(request); @@ -83,7 +84,7 @@ void validateContentIsNotBlank(String content) { @ValueSource(strings = {"", " "}) void validateVisibilityIsNotBlank(String visibility) { // given - PostRequest request = new PostRequest("title", "content", "preview", visibility, 1L, null, false); + PostRequest request = new PostRequest("title", "content", "preview", visibility, 1L, null, false, PostType.NORMAL.getStatus()); // when Set> violations = validator.validate(request); @@ -97,7 +98,7 @@ void validateVisibilityIsNotBlank(String visibility) { @MethodSource("contentPreviews") void validateContentPreviewLength(String contentPreview, boolean validated) { // given - PostRequest request = new PostRequest("title", "content", contentPreview, "visibility", 1L, null, false); + PostRequest request = new PostRequest("title", "content", contentPreview, "visibility", 1L, null, false, PostType.NORMAL.getStatus()); // when Set> violations = validator.validate(request);