Skip to content

Commit

Permalink
Merge pull request #98 from Team-GAJI/feature/#86-study-comments/GAJI…
Browse files Browse the repository at this point in the history
…-115

✨ [feature] #86 - 스터디 댓글 작성, 삭제, 조회 API
  • Loading branch information
spenshark authored Aug 17, 2024
2 parents 07f3368 + d39570b commit d0e3488
Show file tree
Hide file tree
Showing 23 changed files with 325 additions and 104 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@

public interface SelectCategoryRepository extends JpaRepository<SelectCategory, Long>, SelectCategoryQueryDslRepository {

List<SelectCategory> findAllByEntityIdAndType(Long entityId, PostTypeEnum type);
SelectCategory findByEntityIdAndType(Long entityId, PostTypeEnum type);
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,7 @@ public interface CategoryService {
List<SelectCategory> findAllFetchJoinWithCategoryByEntityIdAndPostType(Long entityId, PostTypeEnum postType);
List<CategoryResponseDTO.BaseDTO> findAllCategory();
void saveAllSelectCategory(List<SelectCategory> selectCategoryList);

SelectCategory findByEntityIdAndType(Long entityId, PostTypeEnum type);
}

Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,9 @@ public List<CategoryResponseDTO.BaseDTO> findAllCategory() {
public void saveAllSelectCategory(List<SelectCategory> selectCategoryList) {
selectCategoryRepository.saveAll(selectCategoryList);
}

@Override
public SelectCategory findByEntityIdAndType(Long entityId, PostTypeEnum type) {
return selectCategoryRepository.findByEntityIdAndType(entityId, type);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
public enum RecruitErrorStatus implements BaseErrorCodeInterface {
_RECRUIT_POST_NOT_FOUND(HttpStatus.BAD_REQUEST, "RECRUIT_4001", "모집 게시글을 찾을 수 없습니다."),

_COMMENT_NOT_FOUND(HttpStatus.BAD_REQUEST, "COMMENT_4001", "존재하지 않는 댓글입니다."),
_COMMENT_ALREADY_DELETE(HttpStatus.BAD_REQUEST, "COMMENT_4002", "이미 삭제된 댓글입니다."),
_COMMENT_NOT_OWNER(HttpStatus.BAD_REQUEST, "COMMENT_4003", "댓글 작성자가 아닙니다."),

_ROOM_ALREADY_LIKE(HttpStatus.BAD_REQUEST, "LIKE_4001", "이미 좋아요 된 게시글 입니다."),
_ROOM_ALREADY_NO_LIKE(HttpStatus.BAD_REQUEST, "LIKE_4002", "이미 좋아요 취소된 게시글 입니다."),

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package gaji.service.domain.recruit.converter;

import gaji.service.domain.common.entity.Category;
import gaji.service.domain.common.entity.SelectCategory;
import gaji.service.domain.enums.CategoryEnum;
import gaji.service.domain.recruit.entity.RecruitPostBookmark;
Expand All @@ -14,10 +13,10 @@
import gaji.service.domain.room.entity.Room;
import gaji.service.domain.studyMate.entity.StudyMate;
import gaji.service.global.converter.DateConverter;
import org.springframework.data.domain.Slice;

import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -46,14 +45,8 @@ public static RecruitResponseDTO.CreateRoomDTO toResponseDTO(Room room) {
.build();
}

public static List<CategoryEnum> toCategoryList(List<SelectCategory> selectCategoryList) {
List<CategoryEnum> categoryList = new ArrayList<>();
for (SelectCategory selectCategory : selectCategoryList) {
Category category = selectCategory.getCategory();
categoryList.add(category.getCategory());
}

return categoryList;
public static CategoryEnum toCategory(SelectCategory selectCategory) {
return selectCategory.getCategory().getCategory();
}

public static Material toMaterial(String materialPath, Room room) {
Expand All @@ -71,7 +64,7 @@ public static StudyMate toStudyMate(User user, Room room) {
.build();
}

public static RecruitResponseDTO.studyDetailDTO toStudyDetailDTO(User user, Room room, List<CategoryEnum> categoryList) {
public static RecruitResponseDTO.studyDetailDTO toStudyDetailDTO(User user, Room room, CategoryEnum category) {
return RecruitResponseDTO.studyDetailDTO.builder()
.userNickName(user.getNickname())
.userActive(user.getStatus())
Expand All @@ -80,7 +73,7 @@ public static RecruitResponseDTO.studyDetailDTO toStudyDetailDTO(User user, Room
.name(room.getName())
.imageUrl(room.getThumbnailUrl())
.recruitPostTypeEnum(room.getRecruitPostTypeEnum())
.postCategoryList(categoryList)
.studyCategory(category)
.views(room.getViews())
.likes(room.getLikes())
.bookmarks(room.getBookmarks())
Expand All @@ -98,23 +91,40 @@ private static RecruitResponseDTO.CommentResponseDTO toCommentResponseDTO(StudyC
return RecruitResponseDTO.CommentResponseDTO.builder()
.userImage(comment.getUser().getProfileImagePth())
.userNickName(comment.getUser().getNickname())
.commentCreatedAt(comment.getCreatedAt())
.commentOrder(comment.getCommentOrder())
.depth(comment.getDepth())
.commentId(comment.getId())
.commentWriteDate(DateConverter.convertWriteTimeFormat(LocalDate.from(comment.getCreatedAt()), " 작성"))
.commentBody(comment.getBody())
.build();
}

public static List<RecruitResponseDTO.CommentResponseDTO> toCommentResponseDTOList(List<StudyComment> commentList) {
int toIndex = Math.min(4, commentList.size());
return commentList.subList(0, toIndex).stream().map(RecruitConverter::toCommentResponseDTO).collect(Collectors.toList());
}
public static RecruitResponseDTO.CommentListDTO toCommentListDTO(int commentCount, Slice<StudyComment> commentList) {
List<RecruitResponseDTO.CommentResponseDTO> CommentResponseDTO =
commentList.stream().map(RecruitConverter::toCommentResponseDTO).collect(Collectors.toList());

public static RecruitResponseDTO.CommentListDTO toCommentListDTO(int commentCount, List<RecruitResponseDTO.CommentResponseDTO> CommentResponseDTO) {
return RecruitResponseDTO.CommentListDTO.builder()
.commentCount(commentCount)
.hasNext(commentList.hasNext())
.commentList(CommentResponseDTO)
.build();
}

public static StudyComment toComment(RecruitRequestDTO.WriteCommentDTO request, User user, Room room, StudyComment parentComment) {
return StudyComment.builder()
.user(user)
.room(room)
.parentComment(parentComment)
.body(request.getBody())
.build();
}

public static RecruitResponseDTO.WriteCommentDTO toWriteCommentDTO(StudyComment comment) {
return RecruitResponseDTO.WriteCommentDTO.builder()
.commentId(comment.getId())
.build();
}

public static RecruitPostLikes toRecruitPostLikes(User user, Room room) {
return RecruitPostLikes.builder()
.user(user)
Expand Down Expand Up @@ -150,7 +160,7 @@ public static RecruitResponseDTO.PreviewDTO toPreviewDTO(Room room) {
.deadLine(ChronoUnit.DAYS.between(room.getRecruitEndDay(), LocalDate.now()))
.description(room.getDescription())
.createdAt(DateConverter.convertToRelativeTimeFormat(room.getCreatedAt()))
.recruitCount(room.getPeopleMaximum())
.recruitMaxCount(room.getPeopleMaximum())
.build();
}

Expand Down
21 changes: 19 additions & 2 deletions src/main/java/gaji/service/domain/recruit/entity/StudyComment.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import gaji.service.domain.room.entity.Room;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

Expand Down Expand Up @@ -40,9 +41,25 @@ public class StudyComment extends BaseEntity {
private List<StudyCommentLikes> studyCommentLikes = new ArrayList<>();

private String body;
private Integer commentOrder;
private Integer depth;
private Integer depth; // 댓글의 깊이 (대댓글 여부)
private Integer commentOrder; // 댓글의 순서 (같은 부모면 같은 순서)

@Enumerated(EnumType.STRING)
private CommentStatus status;

@Builder
public StudyComment(User user, Room room, StudyComment parentComment, String body) {
this.user = user;
this.room = room;
this.parent = parentComment;
this.body = body;
this.status = CommentStatus.PUBLIC;
if (parentComment == null) {
this.depth = 0;
this.commentOrder = room.getCommentCount();
} else {
this.depth = 1;
this.commentOrder = parent.getCommentOrder();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,8 @@
import gaji.service.domain.enums.PreviewFilter;
import gaji.service.domain.enums.SortType;
import gaji.service.domain.recruit.web.dto.RecruitResponseDTO;
import gaji.service.domain.room.entity.Room;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.time.LocalDateTime;
import java.util.List;

public interface RecruitCustomRepository {

RecruitResponseDTO.PreviewListDTO findByCategoryOrderBySortType(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package gaji.service.domain.recruit.repository;

import gaji.service.domain.recruit.entity.RecruitPostBookmark;
import gaji.service.domain.recruit.entity.RecruitPostLikes;
import gaji.service.domain.room.entity.Room;
import gaji.service.domain.user.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package gaji.service.domain.recruit.repository;

import gaji.service.domain.recruit.entity.StudyComment;
import gaji.service.domain.room.entity.Room;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;

public interface StudyCommentCustomRepository {
Slice<StudyComment> findByRoomFetchJoinWithUser(
Integer lastCommentOrder, Integer lastDepth, Long lastCommentId, Room room, Pageable pageable);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package gaji.service.domain.recruit.repository;

import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import gaji.service.domain.recruit.entity.StudyComment;
import gaji.service.domain.room.entity.Room;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.SliceImpl;
import org.springframework.stereotype.Repository;
import java.util.List;

import static gaji.service.domain.recruit.entity.QStudyComment.studyComment;
import static gaji.service.domain.user.entity.QUser.user;

@Repository
@RequiredArgsConstructor
public class StudyCommentCustomRepositoryImpl implements StudyCommentCustomRepository {

private final JPAQueryFactory jpaQueryFactory;

@Override
public Slice<StudyComment> findByRoomFetchJoinWithUser(
Integer lastCommentOrder, Integer lastDepth, Long lastCommentId, Room room, Pageable pageable) {
List<StudyComment> commentList = jpaQueryFactory
.select(studyComment)
.from(studyComment)
.leftJoin(studyComment.user, user)
.fetchJoin()
.where(
studyComment.room.eq(room),
gtCommentOrderDepthAndCommentId(lastCommentOrder, lastDepth, lastCommentId)
)
.orderBy(
orderByCommentOrderAndDepth()
)
.limit(pageable.getPageSize() + 1)
.fetch();
return checkLastPage(pageable, commentList);
}

private BooleanExpression gtCommentOrderDepthAndCommentId(
Integer lastCommentOrder, Integer lastDepth, Long lastId) {
if (lastCommentOrder == null || lastDepth == null || lastId == null) {
return null;
}

return studyComment.commentOrder.gt(lastCommentOrder) // 다음 commentOrder로 넘어가는 경우
.or(
studyComment.commentOrder.eq(lastCommentOrder)
.and(
studyComment.depth.gt(lastDepth) // 동일한 commentOrder 내에서 depth가 더 큰 경우
.or(
studyComment.depth.eq(lastDepth)
.and(
studyComment.id.gt(lastId)) // 동일한 depth 내에서 createdAt이 더 큰 경우
)
)
);
}

private Slice<StudyComment> checkLastPage(Pageable pageable, List<StudyComment> commentList) {
boolean hasNext = false;

if (commentList.size() > pageable.getPageSize()) {
hasNext = true;
commentList.remove(pageable.getPageSize());
}

return new SliceImpl<>(commentList, pageable, hasNext);
}

private OrderSpecifier[] orderByCommentOrderAndDepth() {
return new OrderSpecifier[] {
studyComment.commentOrder.asc(),
studyComment.depth.asc(),
studyComment.createdAt.asc()
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@
import gaji.service.domain.room.entity.Room;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

public interface StudyCommentRepository extends JpaRepository<StudyComment, Long> {

List<StudyComment> findByRoomAndDepth(Room room, int depth);
public interface StudyCommentRepository extends JpaRepository<StudyComment, Long>, StudyCommentCustomRepository {

int countByRoom(Room room);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import gaji.service.domain.recruit.web.dto.RecruitRequestDTO;
import gaji.service.domain.recruit.web.dto.RecruitResponseDTO;
import gaji.service.domain.room.entity.Room;

public interface RecruitCommandService {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,14 @@

import gaji.service.domain.common.entity.Category;
import gaji.service.domain.common.entity.SelectCategory;
import gaji.service.domain.common.repository.CategoryRepository;
import gaji.service.domain.enums.CategoryEnum;
import gaji.service.domain.common.service.CategoryService;
import gaji.service.domain.enums.PostTypeEnum;
import gaji.service.domain.recruit.code.RecruitErrorStatus;
import gaji.service.domain.recruit.converter.RecruitConverter;
import gaji.service.domain.recruit.entity.RecruitPostBookmark;
import gaji.service.domain.recruit.entity.RecruitPostLikes;
import gaji.service.domain.recruit.repository.RecruitPostBookmarkRepository;
import gaji.service.domain.recruit.repository.RecruitPostLikesRepository;
import gaji.service.domain.common.repository.SelectCategoryRepository;
import gaji.service.domain.recruit.web.dto.RecruitRequestDTO;
import gaji.service.domain.recruit.web.dto.RecruitResponseDTO;
import gaji.service.domain.room.entity.Material;
Expand All @@ -37,9 +35,8 @@ public class RecruitCommandServiceImpl implements RecruitCommandService {

private final RoomCommandService roomCommandService;
private final UserQueryService userQueryService;
private final CategoryService categoryService;
private final RoomQueryService roomQueryService;
private final SelectCategoryRepository selectCategoryRepository;
private final CategoryRepository categoryRepository;
private final StudyMateRepository studyMateRepository;
private final MaterialCommandService materialCommandService;
private final RecruitPostLikesRepository recruitPostLikesRepository;
Expand All @@ -53,8 +50,6 @@ public RecruitResponseDTO.CreateRoomDTO createRoom(RecruitRequestDTO.CreateRoomD
String thumbnailUrl = DEFAULT_THUMBNAIL_URL;
String inviteCode = null;
int peopleMaximum = 0;
SelectCategory selectCategory;


if (request.getThumbnailUrl() != null && !request.getThumbnailUrl().isEmpty()) {
thumbnailUrl = request.getThumbnailUrl();
Expand Down Expand Up @@ -85,18 +80,17 @@ public RecruitResponseDTO.CreateRoomDTO createRoom(RecruitRequestDTO.CreateRoomD

roomCommandService.saveRoom(room);

for (CategoryEnum categoryEnum : request.getCategoryList()) {
Category category = Category.builder()
.category(categoryEnum)
.build();
categoryRepository.save(category);
selectCategory = SelectCategory.builder()
.category(category)
.entityId(room.getId())
.type(PostTypeEnum.ROOM)
.build();
selectCategoryRepository.save(selectCategory);
}
Category category = Category.builder()
.category(request.getCategory())
.build();
categoryService.saveCategory(category);

SelectCategory selectCategory = SelectCategory.builder()
.category(category)
.entityId(room.getId())
.type(PostTypeEnum.ROOM)
.build();
categoryService.saveSelectCategory(selectCategory);

return RecruitConverter.toResponseDTO(room);
}
Expand Down
Loading

0 comments on commit d0e3488

Please sign in to comment.