diff --git a/goorm/src/main/java/study/goorm/domain/cloth/domain/application/.DS_Store b/goorm/src/main/java/study/goorm/domain/cloth/domain/application/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/goorm/src/main/java/study/goorm/domain/cloth/domain/application/.DS_Store differ diff --git a/goorm/src/main/java/study/goorm/domain/cloth/domain/application/ClothServiceImpl.java b/goorm/src/main/java/study/goorm/domain/cloth/domain/application/ClothServiceImpl.java index 7603be2..44799ae 100644 --- a/goorm/src/main/java/study/goorm/domain/cloth/domain/application/ClothServiceImpl.java +++ b/goorm/src/main/java/study/goorm/domain/cloth/domain/application/ClothServiceImpl.java @@ -48,7 +48,7 @@ public ClothResponseDTO.ClothEditViewResult getClothEditView(Long clothId) { String firstImageUrl = clothImageUrls.stream() .findFirst() .map(ClothImage::getImageUrl) - .orElseThrow(() -> new ClothException(ErrorStatus.NO_ClOTH_IMAGE)); + .orElseThrow(() -> new ClothException(ErrorStatus.NO_CLOTH_IMAGE)); return ClothConverter.toClothEditViewResult(cloth, firstImageUrl); } diff --git a/goorm/src/main/java/study/goorm/domain/cloth/domain/entity/Cloth.java b/goorm/src/main/java/study/goorm/domain/cloth/domain/entity/Cloth.java index dce955c..d6b3c15 100644 --- a/goorm/src/main/java/study/goorm/domain/cloth/domain/entity/Cloth.java +++ b/goorm/src/main/java/study/goorm/domain/cloth/domain/entity/Cloth.java @@ -64,4 +64,9 @@ public class Cloth extends BaseEntity { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "member_id", nullable = false) private Member member; + + public void setWearNum(int wearNum) { + this.wearNum = wearNum; + } + } diff --git a/goorm/src/main/java/study/goorm/domain/history/domain/api/HistoryRestController.java b/goorm/src/main/java/study/goorm/domain/history/domain/api/HistoryRestController.java new file mode 100644 index 0000000..30edaf4 --- /dev/null +++ b/goorm/src/main/java/study/goorm/domain/history/domain/api/HistoryRestController.java @@ -0,0 +1,101 @@ +package study.goorm.domain.history.domain.api; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.http.MediaType; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import study.goorm.domain.history.domain.application.HistoryService; +import study.goorm.domain.history.domain.dto.HistoryRequestDTO; +import study.goorm.domain.history.domain.dto.HistoryResponseDTO; +import study.goorm.domain.member.domain.entity.Member; +import study.goorm.global.common.response.BaseResponse; +import study.goorm.global.error.code.status.SuccessStatus; + +import java.time.YearMonth; +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/history-") +@Validated +public class HistoryRestController { + private final HistoryService historyService; + + @GetMapping("/monthly/") + @Operation( summary = "월별 기록 조회 API", description = "query string으로 clokeyid, month 넣어줘야합니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse( responseCode = "HISTORY_200", description = "월별 기록이 성공적으로 조회되었습니다.") + }) + @Parameters({ + @Parameter(name = "clokey-id", description = "클로키 유저의 clokey id, 입력하지 않으면 본인의 월별 기록을 확인합니다."), + @Parameter(name = "month", description = "날짜 형식은 YYYY-MM(ex.2025-01)과 같은 형태로 입력해야합니다.") + }) + public BaseResponse getMonthlyHistories( + @RequestParam(value = "clokey-id", required = false) String clokeyId, // required false -> 값 안넣어도 되지만 안넣으면 null로 돌어옴 + @RequestParam("month") @DateTimeFormat(pattern = "YYYY-MM") YearMonth month + ){ + HistoryResponseDTO.MonthlyHistoriesResult result = historyService.getMonthlyHistories(clokeyId, month); + + return BaseResponse.onSuccess(SuccessStatus.HISTORY_VIEW_SUCCESS, result); + } + + + @GetMapping("/{historyId}") + @Operation(summary = "일별 기록을 조회하는 API", description = "historyId를 Path Variable입력하면 조회가 가능합니다.") + @Parameter(name = "historyId", description = "조회하고자 하는 기록의 ID", required = true) + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "HISTORY_200", description = "성공적으로 조회되었습니다.") + }) + public BaseResponse getDailyHistory( + @PathVariable(name = "historyId") Long historyId + ){ + HistoryResponseDTO.DailyHistoryResult result = historyService.getDailyHistory(historyId); + + return BaseResponse.onSuccess(SuccessStatus.HISTORY_VIEW_SUCCESS, result); + } + + @PostMapping(value = "", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @Operation(summary = "날짜별 옷 기록 추가 API", description = "해당 날짜에 기록을 추가하는 API입니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "HISTORY_201", description = "CREATED, 성공적으로 생성되었습니다.") + }) + public BaseResponse createHistory( + @RequestPart("historyCreateRequest") @Valid HistoryRequestDTO.HistoryCreateRequest historyCreateRequest, + @RequestPart("imageFile") List imageFiles + ){ + HistoryResponseDTO.HistoryCreateResult result = historyService.createHistory(historyCreateRequest, imageFiles); + return BaseResponse.onSuccess(SuccessStatus.HISTORY_CREATED, result); + } + + + @PatchMapping(value = "/{historyId}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @Operation(summary = "날짜별 옷 기록 수정 API", description = "기록 ID로 기존 기록을 수정합니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "HISTORY_202", description = "PATCHED, 성공적으로 수정되었습니다.") + }) + public BaseResponse updateHistory( + @PathVariable Long historyId, + @RequestPart("metadata") @Valid HistoryRequestDTO.HistoryUpdateRequest metadata, + @RequestPart("image") List imageFiles + ) { + historyService.updateHistory(historyId, metadata, imageFiles); + return BaseResponse.onSuccess(SuccessStatus.HISTORY_DELETED, null); + } + + @DeleteMapping("/{historyId}") + @Operation(summary = "날짜별 옷 기록 삭제 API", description = "해당 ID의 기록을 삭제합니다.") + @ApiResponses({ + @ApiResponse(responseCode = "HISTORY_200", description = "기록이 성공적으로 삭제되었습니다."), + @ApiResponse(responseCode = "HISTORY_4002", description = "존재하지 않는 기록 ID 입니다.") + }) + public BaseResponse deleteHistory(@PathVariable Long historyId) { + historyService.deleteHistory(historyId); + return BaseResponse.onS \ No newline at end of file diff --git a/goorm/src/main/java/study/goorm/domain/history/domain/application/HistoryService.java b/goorm/src/main/java/study/goorm/domain/history/domain/application/HistoryService.java new file mode 100644 index 0000000..123770f --- /dev/null +++ b/goorm/src/main/java/study/goorm/domain/history/domain/application/HistoryService.java @@ -0,0 +1,25 @@ +package study.goorm.domain.history.domain.application; + +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; +import study.goorm.domain.history.domain.dto.HistoryRequestDTO; +import study.goorm.domain.history.domain.dto.HistoryResponseDTO; + +import java.time.YearMonth; +import java.util.List; + +@Service +public interface HistoryService { + HistoryResponseDTO.MonthlyHistoriesResult getMonthlyHistories(String clokeyId, YearMonth month); + + HistoryResponseDTO.DailyHistoryResult getDailyHistory(Long historyId); + + HistoryResponseDTO.HistoryCreateResult createHistory(HistoryRequestDTO.HistoryCreateRequest historyCreateRequest, List imageFiles); + + void updateHistory(Long historyId, + HistoryRequestDTO.HistoryUpdateRequest request, + List imageFiles + ); + void deleteHistory(Long historyId); + + \ No newline at end of file diff --git a/goorm/src/main/java/study/goorm/domain/history/domain/application/HistoryServiceImpl.java b/goorm/src/main/java/study/goorm/domain/history/domain/application/HistoryServiceImpl.java new file mode 100644 index 0000000..f142da9 --- /dev/null +++ b/goorm/src/main/java/study/goorm/domain/history/domain/application/HistoryServiceImpl.java @@ -0,0 +1,463 @@ +package study.goorm.domain.history.domain.application; + +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; +import study.goorm.domain.cloth.domain.dto.ClothRequestDTO; +import study.goorm.domain.cloth.domain.entity.Cloth; +import study.goorm.domain.cloth.domain.exception.ClothException; +import study.goorm.domain.cloth.domain.repository.ClothRepository; +import study.goorm.domain.history.domain.converter.HistoryConverter; +import study.goorm.domain.history.domain.dto.HistoryRequestDTO; +import study.goorm.domain.history.domain.dto.HistoryResponseDTO; +import study.goorm.domain.history.domain.entity.*; +import study.goorm.domain.history.domain.exception.HistoryException; +import study.goorm.domain.history.domain.repository.*; +import study.goorm.domain.member.domain.application.MemberService; +import study.goorm.domain.member.domain.entity.Member; +import study.goorm.domain.member.domain.exception.MemberException; +import study.goorm.domain.member.domain.repository.MemberRepository; +import study.goorm.global.error.code.status.ErrorStatus; + +import java.util.Map; +import java.util.Optional; + +import java.time.LocalDate; +import java.time.YearMonth; +import java.util.HashSet; +import java.util.List; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class HistoryServiceImpl implements HistoryService { + + private final HistoryRepository historyRepository; + private final HistoryImageRepository historyImageRepository; + private final HistoryConverter historyConverter; + private final MemberService memberService; + private final CommentRepository commentRepository; + private final HashtagHistoryRepository hashtagHistoryRepository; + private final HistoryClothRepository historyClothRepository; + private final MemberLikeRepository memberLikeRepository; + private final ClothRepository clothRepository; + private final HashtagRepository hashtagRepository; + private final MemberRepository memberRepository; + + // 상수 필드 생성자 매서드 순서 지켜서 넣기 + @Override + @Transactional(readOnly = true) + public HistoryResponseDTO.MonthlyHistoriesResult getMonthlyHistories(String clokeyId, YearMonth month){ + + Member member = (clokeyId == null) + ? memberService.getCurrentMember() + : memberService.findByClokeyId(clokeyId) + .orElseThrow(() -> new MemberException(ErrorStatus.NO_SUCH_MEMBER)); + + List histories = historyRepository.findByMemberIdAndMonth(member.getId(), month); + + // 이미지 로직 따로 빼는게 더 나을까요? 아니면 그냥 두는게 나을까요 -> 빼는 게 더 좋을 거같음(가독성 안좋음) + List historiesDtos = histories.stream() + .map(history -> { + String imageUrl = historyImageRepository + .findFirstByHistoryIdOrderByCreatedAtAsc(history.getId()) + .map(HistoryImage::getImageUrl) + .orElseThrow(() -> new HistoryException(ErrorStatus.NO_CLOTH_IMAGE)); + return historyConverter.toMonthlyHistoryDto(history, imageUrl); + }) + .collect(Collectors.toList()); + + return historyConverter.toMonthlyHistoriesResult(member, historiesDtos); + } + + + + + @Override + @Transactional(readOnly = true) + public HistoryResponseDTO.DailyHistoryResult getDailyHistory(Long historyId){ + + Member currentMember = memberService.getCurrentMember(); + + History history = historyRepository.findById(historyId) + .orElseThrow(() -> new HistoryException(ErrorStatus.NO_SUCH_HISTORY)); + + if(!history.getMember().getId().equals(currentMember.getId())){ + throw new HistoryException(ErrorStatus.NO_GRANT_HISTORY); + } + + List imageUrls = historyImageRepository.findAllByHistory(history) + .stream() + .map(HistoryImage::getImageUrl) + .toList(); + + List hashtags = hashtagHistoryRepository.findAllByHistory(history) + .stream() + .map(hashtagHistory -> hashtagHistory.getHashtag().getName()) + .toList(); + // N+1 발생 가능성 있음 -> 패치조인 하거나 직접 꺼내오기 + + boolean liked = memberLikeRepository.existsByHistoryAndMember(history, currentMember); + + + List clothDtos = historyClothRepository.findAllByHistory(history) + .stream() + .map(mapping -> historyConverter.toClothDto(mapping.getCloth())) + .toList(); + + + return historyConverter.toDailyHistoryResult(history, imageUrls, hashtags, liked, clothDtos); + } + + // 기록 추가 + @Override + public HistoryResponseDTO.HistoryCreateResult createHistory(HistoryRequestDTO.HistoryCreateRequest historyCreateRequest, List imageFiles){ + + Member member = memberService.getCurrentMember(); + LocalDate date = historyCreateRequest.getDate(); + + // 있으면 에러 없으면 밑에서 만들도록 하는게 가독성이 좋을 듯 + History history = historyRepository.findByMemberAndHistoryDate(member,historyCreateRequest.getDate()) + .orElseGet(() -> History.builder() + .historyDate(date) + .content(historyCreateRequest.getContent()) + .likes(0) + .member(member) + .build()); + // Content 중복 분리하기 + history.setContent(historyCreateRequest.getContent()); + historyRepository.save(history); + + if(imageFiles == null || imageFiles.isEmpty()){ + throw new HistoryException(ErrorStatus.NO_HISTORY_IMAGE); + } + + // S3 연결 안함 + 이미지 10개 이상인거 검증하기 + for (MultipartFile file : imageFiles) { + String imageUrl = "image URL"; + HistoryImage image = HistoryImage.builder() + .imageUrl(imageUrl) + .history(history) + .build(); + historyImageRepository.save(image); + } + + for(String tag : historyCreateRequest.getHashtags()){ + Hashtag hashtag = hashtagRepository.findByName(tag) + .orElseGet(() -> hashtagRepository.save(Hashtag.builder().name(tag).build())); + + + HashtagHistory hashtagHistory = HashtagHistory.builder() + .history(history) + .hashtag(hashtag) + .build(); + hashtagHistoryRepository.save(hashtagHistory); + } + + for(Long clothId : historyCreateRequest.getClothes()){ // for 문법 쓰는거보다 stream 쓰는게 가독성이 좋음 (한번에 찾아오도록) + Cloth cloth = clothRepository.findById(clothId) + .orElseThrow(() -> new ClothException(ErrorStatus.NO_SUCH_CLOTH)); + + cloth.setWearNum(cloth.getWearNum() + 1); + clothRepository.save(cloth); + + HistoryCloth historyCloth = HistoryCloth.builder() + .cloth(cloth) + .history(history) + .build(); + historyClothRepository.save(historyCloth); + } + + + return historyConverter.toHistoryCreateResult(history); + } + + // WearNum을 너무 자유롭게 변경하게 둠 (금준님 코드 참고하기) + // 트렌젝션이 없어서 트렌적션이 없는 거에서 호출을 하면 위험할 수 있음 (에러 가능성) + // 짧아서 분리할 필요도 없을 듯 + private void decrWear(Cloth cloth) { + cloth.setWearNum(Math.max(cloth.getWearNum() - 1, 0)); + clothRepository.save(cloth); + } + + private void incrWear(Cloth cloth) { + cloth.setWearNum(cloth.getWearNum() + 1); + clothRepository.save(cloth); + } + + + @Override + @Transactional + public void updateHistory( + Long historyId, + HistoryRequestDTO.HistoryUpdateRequest req, + List imageFiles + ) { + Member me = memberService.getCurrentMember(); + History history = historyRepository.findById(historyId) + .orElseThrow(() -> new HistoryException(ErrorStatus.NO_SUCH_HISTORY)); + if (!history.getMember().getId().equals(me.getId())) { + throw new HistoryException(ErrorStatus.NO_GRANT_HISTORY); + } + + if (imageFiles == null || imageFiles.isEmpty()) { + throw new HistoryException(ErrorStatus.NO_ENOUGH_IMAGES); + } + if (imageFiles.size() > 10) { + throw new HistoryException(ErrorStatus.NO_ENOUGH_IMAGES); + } + + if (req.getClothes().size() != new HashSet<>(req.getClothes()).size() + || req.getHashtags().size() != new HashSet<>(req.getHashtags()).size()) { + throw new HistoryException(ErrorStatus.NO_ONLY_CLOTH); + } + + History updated = historyConverter.toUpdatedHistory(history, req); + historyRepository.save(updated); // save 안해도 업데이트 됨 (트랜잭션안에있ㅇㅁ) + + historyImageRepository.deleteAllByHistory(updated); + hashtagHistoryRepository.deleteAllByHistory(updated); + List olds = historyClothRepository.findAllByHistory(updated); + for (HistoryCloth hc : olds) { + decrWear(hc.getCloth()); + } + historyClothRepository.deleteAll(olds); + + for (MultipartFile f : imageFiles) { + String imageUrl = "uploaded-url"; // S3 후 변경 예정 + historyImageRepository.save( + historyConverter.toHistoryImage(updated, imageUrl) + ); + } + + for (String tagName : req.getHashtags()) { + Hashtag tag = hashtagRepository.findByName(tagName) + .orElseGet(() -> hashtagRepository.save( + Hashtag.builder().name(tagName).build() + )); + hashtagHistoryRepository.save( + historyConverter.toHashtagHistory(updated, tag) + ); + } + + for (Long clothId : req.getClothes()) { + Cloth cloth = clothRepository.findById(clothId) + .filter(c -> c.getMember().equals(me)) + .orElseThrow(() -> new HistoryException(ErrorStatus.NO_ONLY_CLOTH)); + + incrWear(cloth); + historyClothRepository.save( + historyConverter.toHistoryCloth(updated, cloth) + ); + } + } + + + @Transactional + @Override + public void deleteHistory(Long historyId) { + Member member = memberService.getCurrentMember(); + + History history = historyRepository.findById(historyId) + .orElseThrow(() -> new HistoryException(ErrorStatus.NO_SUCH_HISTORY)); + + if (!history.getMember().getId().equals(member.getId())) { + throw new HistoryException(ErrorStatus.NO_GRANT_HISTORY); + } + + historyImageRepository.deleteAllByHistory(history); + + hashtagHistoryRepository.deleteAllByHistory(history); + + memberLikeRepository.deleteAllByHistory(history); + + commentRepository.deleteAllByHistory(history); + + List mappings = historyClothRepository.findAllByHistory(history); + for (HistoryCloth hc : mappings) { // 이것도 스트림으로 하기 + Cloth cloth = hc.getCloth(); + cloth.setWearNum(Math.max(0, cloth.getWearNum() - 1)); +// clothRepository.save(cloth); // 얘만 없애면 너무 비효율적일거같진않음 + } + historyClothRepository.deleteAll(mappings); + + historyRepository.delete(history); + } + + @Override + @Transactional + public HistoryResponseDTO.LikeResult like(Long memberId, HistoryRequestDTO.LikeRequest request) { + // 기록 존재 확인 + History history = historyRepository.findById(request.getHistoryId()) + .orElseThrow(() -> new HistoryException(ErrorStatus.NO_SUCH_HISTORY)); + + Member member = memberService.getCurrentMember(); + + // 현재 DB 상태 저장 + boolean currentlyLiked = memberLikeRepository.existsByHistoryAndMember(history, member); + + // 요청값과 실제 상태가 같으면 에러 + if (currentlyLiked == request.getLiked()) { + throw new HistoryException(ErrorStatus.NO_STATE_LIKE); + } + + boolean newLiked; + + if (currentlyLiked) { + // 좋아요 취소 + MemberLike memberLike = memberLikeRepository + .findByHistoryAndMember(history, member) + .orElseThrow(() -> new HistoryException(ErrorStatus.NO_STATE_LIKE)); +// memberLikeRepository.delete(memberLike); + history.setLikes(history.getLikes() - 1); + newLiked = false; + } else { + // 좋아요 추가 + MemberLike newMemberLike = MemberLike.builder() + .history(history) + .member(member) + .build(); + history.setLikes(history.getLikes() + 1); + newLiked = true; + } + + // 좋아요 수 반영 + historyRepository.save(history); + + return historyConverter.toLikeResult(history, newLiked); + } + + @Override + @Transactional(readOnly = true) + public HistoryResponseDTO.LikedUsersResult getLikedUsers(Long historyId) { + + History history = historyRepository.findById(historyId) + .orElseThrow(() -> new HistoryException(ErrorStatus.NO_SUCH_HISTORY)); + + // 좋아요 누른 MemberLike 리스트 조회 + List likes = memberLikeRepository.findAllWithMemberByHistory(history); + + // Member 객체만 꺼내 DTO 변환 + List users = likes.stream() + .map(MemberLike::getMember) + .map(historyConverter::toLikedUserDto) + .collect(Collectors.toList()); + + return HistoryResponseDTO.LikedUsersResult.builder() + .likedUsers(users) + .build(); + } + + @Override + @Transactional + public HistoryResponseDTO.CommentResult writeComment( + Long memberId, + Long historyId, + HistoryRequestDTO.CommentRequestDTO request + ) { + Member member = memberRepository.findById(memberId) + .orElseThrow(() -> new HistoryException(ErrorStatus.NO_SUCH_MEMBER)); + + History history = historyRepository.findById(historyId) + .orElseThrow(() -> new HistoryException(ErrorStatus.NO_SUCH_HISTORY)); + + + Comment commentParent = null; + + // 대댓글 경우 + if (request.getCommentId() != null) { + // 부모 댓글 존재 검증 + commentParent = commentRepository.findById(request.getCommentId()) + .orElseThrow(() -> new HistoryException(ErrorStatus.NO_SUCH_HISTORY)); + + // 대대댓글 방지 + if (commentParent.getComment() != null || !commentParent.getHistory().getId().equals(historyId)) { + throw new HistoryException(ErrorStatus.NO_COMMENT_PLUS); // 잘못된 부모 댓글 + } + } + + Comment comment = historyConverter.toEntity(history, member, commentParent, request); + + + return historyConverter.toDto(comment); + + } + + @Override + @Transactional(readOnly = true) + public HistoryResponseDTO.CommentListResult getComments(Long historyId, int page) { + + // 예외처리 + if (page < 1) { + throw new HistoryException(ErrorStatus.PAGE_UNDER_ONE); + } + + History history = historyRepository.findById(historyId) + .orElseThrow(() -> new HistoryException(ErrorStatus.NO_SUCH_HISTORY)); + + // 페이징 조건 생성 + PageRequest pageRequest = PageRequest.of(page - 1, 10); + + // 부모 댓글만 페이징 조회해서 10개씩 가져옴 + Page parentPage = commentRepository.findAllByHistoryAndCommentIsNull(history, pageRequest); + + // 부모 댓글 목록과, 이들에 딸린 대댓글을 한 번에 조회 + List parents = parentPage.getContent(); + List allReplies = + commentRepository.findAllByCommentIn(parents); + + // parentId 별로 그룹핑 + Map> repliesByParent = allReplies.stream() + .collect(Collectors.groupingBy(c -> c.getComment().getId())); + + // DTO 변환: 부모 댓글 하나당 그룹에서 대댓글 리스트 꺼내 붙이기 + List commentDtos = parents.stream() + .map(parent -> { + List replies = + repliesByParent.getOrDefault(parent.getId(), List.of()) + .stream() + .map(historyConverter::toReplyDto) + .collect(Collectors.toList()); + return historyConverter.toCommentDto(parent, replies); + }) + .collect(Collectors.toList()); + + + return historyConverter.toCommentListResult(parentPage, commentDtos); + } + + @Override + @Transactional + public void deleteComment(Long commentId) { + Comment comment = commentRepository.findById(commentId) + .orElseThrow(() -> new HistoryException(ErrorStatus.NO_SUCH_COMMENT)); + + // 대댓글 모두 조회하여 한 번에 삭제 + List replies = commentRepository.findAllByComment(comment); + if (!replies.isEmpty()) { + commentRepository.deleteAll(replies); + } + +// commentRepository.delete(comment); + } + + @Override + @Transactional + public void updateComment( + Long memberId, + Long commentId, + HistoryRequestDTO.CommentRequestDTO request + ) { + Comment comment = commentRepository.findById(commentId) + .orElseThrow(() -> new HistoryException(ErrorStatus.NO_SUCH_COMMENT)); + + if (!comment.getMember().getId().equals(memberId)) { + throw new HistoryException(ErrorStatus.NO_GRANT_HISTORY); + } + + comment.setContent(request.getContent()); + } +} diff --git a/goorm/src/main/java/study/goorm/domain/history/domain/converter/HistoryConverter.java b/goorm/src/main/java/study/goorm/domain/history/domain/converter/HistoryConverter.java new file mode 100644 index 0000000..e13fe28 --- /dev/null +++ b/goorm/src/main/java/study/goorm/domain/history/domain/converter/HistoryConverter.java @@ -0,0 +1,104 @@ +package study.goorm.domain.history.domain.converter; + +import org.springframework.data.domain.Page; +import org.springframework.stereotype.Component; +import study.goorm.domain.cloth.domain.entity.Cloth; +import study.goorm.domain.history.domain.dto.HistoryRequestDTO; +import study.goorm.domain.history.domain.dto.HistoryResponseDTO; +import study.goorm.domain.history.domain.entity.*; +import study.goorm.domain.member.domain.entity.Member; + +import java.util.List; + + +@Component +public class HistoryConverter { + public HistoryResponseDTO.MonthlyHistoriesResult.HistoryDto toMonthlyHistoryDto(History history, String imageUrl){ + return HistoryResponseDTO.MonthlyHistoriesResult.HistoryDto.builder() + .historyId(history.getId()) + .date(history.getHistoryDate()) + .imageUrl(imageUrl) + .build(); + } + + public HistoryResponseDTO.MonthlyHistoriesResult toMonthlyHistoriesResult( + Member member, List historyDtos + ){ + return HistoryResponseDTO.MonthlyHistoriesResult.builder() + .id(member.getId()) + .nickName(member.getNickname()) + .histories(historyDtos) + .build(); + } + + + public HistoryResponseDTO.DailyHistoryResult toDailyHistoryResult( + History history, + List imageUrls, + List hashtags, + boolean liked, + List clothDtos + ){ + Member member = history.getMember(); + + return HistoryResponseDTO.DailyHistoryResult.builder() + .memberId(member.getId()) + .historyId(history.getId()) + .memberImageUrl(member.getProfileImageUrl()) + .nickName(member.getNickname()) + .clokeyId(member.getClokeyId()) + .contents(history.getContent()) + .imageUrls(imageUrls) + .hashtags(hashtags) + .likeCount(history.getLikes()) + .commentCount(0) + .date(history.getHistoryDate().toString()) + .liked(liked) + .cloths(clothDtos) + .build(); + } + + public HistoryResponseDTO.DailyHistoryResult.ClothDto toClothDto(Cloth cloth){ + return HistoryResponseDTO.DailyHistoryResult.ClothDto.builder() + .clothId(cloth.getId()) + .clothImageUrl(cloth.getClothUrl()) + .clothName(cloth.getName()) + .build(); + } + + // 기록 추가 + public static HistoryResponseDTO.HistoryCreateResult toHistoryCreateResult(History history){ + return HistoryResponseDTO.HistoryCreateResult.builder() + .historyId(history.getId()) + .build(); + } + + // History 엔티티 업데이트 + // 컨버터에 로직이 들어가는건 지양하기 + public History toUpdatedHistory(History existing, HistoryRequestDTO.HistoryUpdateRequest req) { + existing.setContent(req.getContent()); + existing.setVisibility(req.getVisibility()); + return existing; + } + + // 이미지 매핑 엔티티 생성 + public HistoryImage toHistoryImage(History history, String imageUrl) { + return HistoryImage.builder() + .history(history) + .imageUrl(imageUrl) + .build(); + } + + // HashtagHistory 매핑 생성 + public HashtagHistory toHashtagHistory(History history, Hashtag hashtag) { + return HashtagHistory.builder() + .history(history) + .hashtag(hashtag) + .build(); + } + + // HistoryCloth 매핑 생성 + public HistoryCloth toHistoryCloth(History history, Cloth cloth) { + return HistoryCloth.builder() + .history(history) + .clot \ No newline at end of file diff --git a/goorm/src/main/java/study/goorm/domain/history/domain/dto/HistoryRequestDTO.java b/goorm/src/main/java/study/goorm/domain/history/domain/dto/HistoryRequestDTO.java new file mode 100644 index 0000000..f3fddac --- /dev/null +++ b/goorm/src/main/java/study/goorm/domain/history/domain/dto/HistoryRequestDTO.java @@ -0,0 +1,59 @@ +package study.goorm.domain.history.domain.dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.*; +import org.springframework.format.annotation.DateTimeFormat; + +import study.goorm.domain.model.enums.Visibility; + +import java.time.LocalDate; +import java.util.List; + +public class HistoryRequestDTO { + // 기록 추가 + @Builder + @Getter + @Setter + @NoArgsConstructor + @AllArgsConstructor + public static class HistoryCreateRequest{ + // 에러 메세지들 설정해줘야함 + @NotNull + @Size(max = 200) + private String content; + + @NotNull + private List clothes; + + @NotNull + private List hashtags; + + @NotNull + @DateTimeFormat(pattern = "YYYY-MM-DD") + private LocalDate date; + } + + @Getter + @Setter + @NoArgsConstructor + @AllArgsConstructor + @Builder + public static class HistoryUpdateRequest { + @NotBlank(message = "내용은 필수입니다.") + @Size(max=200, message = "content는 200자 이하입니다.") + private String content; + + @Size(max = 10, message = "최대 10개의 옷만 허용됩니다.") + private List clothes; + + @Size(max = 10, message = "최대 10개의 해시태그만 허용됩니다.") + private List hashtags; + + @NotNull(message = "visibility는 필수입니다.") + private Visibility visibility; + + } + + \ No newline at end of file diff --git a/goorm/src/main/java/study/goorm/domain/history/domain/dto/HistoryResponseDTO.java b/goorm/src/main/java/study/goorm/domain/history/domain/dto/HistoryResponseDTO.java new file mode 100644 index 0000000..fd91898 --- /dev/null +++ b/goorm/src/main/java/study/goorm/domain/history/domain/dto/HistoryResponseDTO.java @@ -0,0 +1,72 @@ +package study.goorm.domain.history.domain.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; +import java.util.List; + +public class HistoryResponseDTO { + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class MonthlyHistoriesResult{ + private Long id; + private String nickName; + private List histories; + + @Getter + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class HistoryDto { + private Long historyId; + private LocalDate date; + private String imageUrl; + } + } + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class DailyHistoryResult{ + private Long memberId; + private Long historyId; + private String memberImageUrl; + private String nickName; + private String clokeyId; + private String contents; + private List imageUrls; + private List hashtags; + private int likeCount; + private int commentCount; // 응답표에는 있어서 임시로 넣어뒀습니당 + private boolean liked; + private String date; // 여기서 Localdate가 나은지 string이 나은지? + private List cloths; + + @Getter + @Builder + public static class ClothDto{ + private Long clothId; + private String clothImageUrl; + private String clothName; + } + + + + } + + // 기록 추가 + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class HistoryCreateResult { + private Long historyId; + } + diff --git a/goorm/src/main/java/study/goorm/domain/history/domain/entity/Comment.java b/goorm/src/main/java/study/goorm/domain/history/domain/entity/Comment.java index 2b8a337..80d9774 100644 --- a/goorm/src/main/java/study/goorm/domain/history/domain/entity/Comment.java +++ b/goorm/src/main/java/study/goorm/domain/history/domain/entity/Comment.java @@ -30,4 +30,8 @@ public class Comment extends BaseEntity { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "parent_id") private Comment comment; + + public void setContent(String content) { + this.content = content; + } } diff --git a/goorm/src/main/java/study/goorm/domain/history/domain/entity/History.java b/goorm/src/main/java/study/goorm/domain/history/domain/entity/History.java index 0180b18..67946df 100644 --- a/goorm/src/main/java/study/goorm/domain/history/domain/entity/History.java +++ b/goorm/src/main/java/study/goorm/domain/history/domain/entity/History.java @@ -7,6 +7,7 @@ import org.hibernate.annotations.DynamicUpdate; import study.goorm.domain.member.domain.entity.Member; import study.goorm.domain.model.entity.BaseEntity; +import study.goorm.domain.model.enums.Visibility; import java.time.LocalDate; @@ -37,5 +38,17 @@ public class History extends BaseEntity { @JoinColumn(name = "member_id", nullable = false) private Member member; -} + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private Visibility visibility; + + + public void setContent(String content) { + this.content = content; + } + + public void setVisibility(Visibility visibility) { + this.visibility = visibility; + } + \ No newline at end of file diff --git a/goorm/src/main/java/study/goorm/domain/history/domain/exception/HistoryException.java b/goorm/src/main/java/study/goorm/domain/history/domain/exception/HistoryException.java new file mode 100644 index 0000000..b1910a5 --- /dev/null +++ b/goorm/src/main/java/study/goorm/domain/history/domain/exception/HistoryException.java @@ -0,0 +1,10 @@ +package study.goorm.domain.history.domain.exception; + +import study.goorm.global.error.code.status.BaseErrorCode; +import study.goorm.global.exception.GeneralException; + +public class HistoryException extends GeneralException { + public HistoryException(BaseErrorCode code) { + super(code); + } +} diff --git a/goorm/src/main/java/study/goorm/domain/history/domain/repository/CommentRepository.java b/goorm/src/main/java/study/goorm/domain/history/domain/repository/CommentRepository.java index c37745a..c81161f 100644 --- a/goorm/src/main/java/study/goorm/domain/history/domain/repository/CommentRepository.java +++ b/goorm/src/main/java/study/goorm/domain/history/domain/repository/CommentRepository.java @@ -1,7 +1,9 @@ package study.goorm.domain.history.domain.repository; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import study.goorm.domain.history.domain.entity.Comment; +import study.goorm.domain.history.domain.entity.History; -public interface CommentRepository extends JpaRepository{ -} +import java.util.Collection \ No newline at end of file diff --git a/goorm/src/main/java/study/goorm/domain/history/domain/repository/HashtagHistoryRepository.java b/goorm/src/main/java/study/goorm/domain/history/domain/repository/HashtagHistoryRepository.java index a2ae404..8d975fe 100644 --- a/goorm/src/main/java/study/goorm/domain/history/domain/repository/HashtagHistoryRepository.java +++ b/goorm/src/main/java/study/goorm/domain/history/domain/repository/HashtagHistoryRepository.java @@ -2,6 +2,13 @@ import org.springframework.data.jpa.repository.JpaRepository; import study.goorm.domain.history.domain.entity.HashtagHistory; +import study.goorm.domain.history.domain.entity.History; + +import java.util.List; public interface HashtagHistoryRepository extends JpaRepository{ + List findAllByHistory(History history); + + void deleteAllByHistory(History history); + } diff --git a/goorm/src/main/java/study/goorm/domain/history/domain/repository/HashtagRepository.java b/goorm/src/main/java/study/goorm/domain/history/domain/repository/HashtagRepository.java index 31330e5..2ac2ce6 100644 --- a/goorm/src/main/java/study/goorm/domain/history/domain/repository/HashtagRepository.java +++ b/goorm/src/main/java/study/goorm/domain/history/domain/repository/HashtagRepository.java @@ -3,5 +3,8 @@ import org.springframework.data.jpa.repository.JpaRepository; import study.goorm.domain.history.domain.entity.Hashtag; +import java.util.Optional; + public interface HashtagRepository extends JpaRepository{ + Optional findByName(String name); } diff --git a/goorm/src/main/java/study/goorm/domain/history/domain/repository/HistoryClothRepository.java b/goorm/src/main/java/study/goorm/domain/history/domain/repository/HistoryClothRepository.java index ce17a30..7110d7c 100644 --- a/goorm/src/main/java/study/goorm/domain/history/domain/repository/HistoryClothRepository.java +++ b/goorm/src/main/java/study/goorm/domain/history/domain/repository/HistoryClothRepository.java @@ -3,7 +3,13 @@ import org.springframework.data.jpa.repository.JpaRepository; import study.goorm.domain.cloth.domain.entity.Cloth; import study.goorm.domain.history.domain.entity.HistoryCloth; +import study.goorm.domain.history.domain.entity.History; +import java.util.List; + public interface HistoryClothRepository extends JpaRepository{ void deleteAllByCloth(Cloth cloth); + + List findAllByHistory(History history); + } diff --git a/goorm/src/main/java/study/goorm/domain/history/domain/repository/HistoryImageRepository.java b/goorm/src/main/java/study/goorm/domain/history/domain/repository/HistoryImageRepository.java index d07af9b..4ec44a8 100644 --- a/goorm/src/main/java/study/goorm/domain/history/domain/repository/HistoryImageRepository.java +++ b/goorm/src/main/java/study/goorm/domain/history/domain/repository/HistoryImageRepository.java @@ -1,7 +1,17 @@ package study.goorm.domain.history.domain.repository; import org.springframework.data.jpa.repository.JpaRepository; +import study.goorm.domain.history.domain.entity.History; import study.goorm.domain.history.domain.entity.HistoryImage; +import java.util.List; +import java.util.Optional; + public interface HistoryImageRepository extends JpaRepository{ + Optional findFirstByHistoryIdOrderByCreatedAtAsc(Long historyId); + + List findAllByHistory(History history); + + void deleteAllByHistory(History history); + } diff --git a/goorm/src/main/java/study/goorm/domain/history/domain/repository/HistoryRepository.java b/goorm/src/main/java/study/goorm/domain/history/domain/repository/HistoryRepository.java index 47df229..dc1975f 100644 --- a/goorm/src/main/java/study/goorm/domain/history/domain/repository/HistoryRepository.java +++ b/goorm/src/main/java/study/goorm/domain/history/domain/repository/HistoryRepository.java @@ -1,7 +1,25 @@ package study.goorm.domain.history.domain.repository; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import study.goorm.domain.history.domain.entity.History; +import study.goorm.domain.history.domain.entity.MemberLike; +import study.goorm.domain.member.domain.entity.Member; + +import java.time.LocalDate; +import java.time.YearMonth; +import java.util.List; +import java.util.Optional; public interface HistoryRepository extends JpaRepository{ -} + + // 이렇게 쿼리문을 써도 괜찮을까요..? -> 직접 쿼리 작성해야함 ㅇㅇ + @Query("SELECT h FROM History h WHERE h.member.id = :memberId AND FUNCTION('DATE_FORMAT', h.historyDate, '%Y-%m') = :monthStr") + List findByMemberIdAndMonth(@Param("memberId") Long memberId, @Param("monthStr") String monthStr); + + default List findByMemberIdAndMonth(Long memberId, YearMonth month) { + return findByMemberIdAndMonth(memberId, month.toString()); + } + + Optional findByMe \ No newline at end of file diff --git a/goorm/src/main/java/study/goorm/domain/history/domain/repository/MemberLikeRepository.java b/goorm/src/main/java/study/goorm/domain/history/domain/repository/MemberLikeRepository.java index 2fb6291..2edc880 100644 --- a/goorm/src/main/java/study/goorm/domain/history/domain/repository/MemberLikeRepository.java +++ b/goorm/src/main/java/study/goorm/domain/history/domain/repository/MemberLikeRepository.java @@ -1,7 +1,13 @@ package study.goorm.domain.history.domain.repository; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import study.goorm.domain.history.domain.entity.MemberLike; +import study.goorm.domain.history.domain.entity.History; +import study.goorm.domain.member.domain.entity.Member; -public interface MemberLikeRepository extends JpaRepository{ -} +import java.util.List; +import java.util.Optional; + +public interface MemberLikeRepositor \ No newline at end of file diff --git a/goorm/src/main/java/study/goorm/domain/member/domain/application/MemberService.java b/goorm/src/main/java/study/goorm/domain/member/domain/application/MemberService.java new file mode 100644 index 0000000..73f691b --- /dev/null +++ b/goorm/src/main/java/study/goorm/domain/member/domain/application/MemberService.java @@ -0,0 +1,12 @@ +package study.goorm.domain.member.domain.application; + +import study.goorm.domain.member.domain.entity.Member; + +import java.util.Optional; + +public interface MemberService { + Member getCurrentMember(); + + Optional findByClokeyId(String clokeyId); + +} diff --git a/goorm/src/main/java/study/goorm/domain/member/domain/application/MemberServiceImpl.java b/goorm/src/main/java/study/goorm/domain/member/domain/application/MemberServiceImpl.java new file mode 100644 index 0000000..eaf25a7 --- /dev/null +++ b/goorm/src/main/java/study/goorm/domain/member/domain/application/MemberServiceImpl.java @@ -0,0 +1,28 @@ +package study.goorm.domain.member.domain.application; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import study.goorm.domain.member.domain.entity.Member; +import study.goorm.domain.member.domain.exception.MemberException; +import study.goorm.domain.member.domain.repository.MemberRepository; +import study.goorm.global.error.code.status.ErrorStatus; + +import java.util.Optional; + +@Service +@RequiredArgsConstructor +public class MemberServiceImpl implements MemberService { + private final MemberRepository memberRepository; + + @Override + public Member getCurrentMember() { + Long fakeLoginMemberid = 1L; + return memberRepository.findById(fakeLoginMemberid) + .orElseThrow(() -> new MemberException(ErrorStatus.NO_SUCH_MEMBER)); + } + + @Override + public Optional findByClokeyId(String clokeyId) { + return memberRepository.findByClokeyId(clokeyId); + } +} diff --git a/goorm/src/main/java/study/goorm/domain/model/enums/Visibility.java b/goorm/src/main/java/study/goorm/domain/model/enums/Visibility.java new file mode 100644 index 0000000..43e8baa --- /dev/null +++ b/goorm/src/main/java/study/goorm/domain/model/enums/Visibility.java @@ -0,0 +1,5 @@ +package study.goorm.domain.model.enums; + +public enum Visibility { + PUBLIC, PRIVATE +} \ No newline at end of file diff --git a/goorm/src/main/java/study/goorm/global/error/code/status/ErrorStatus.java b/goorm/src/main/java/study/goorm/global/error/code/status/ErrorStatus.java index d6d7321..5d1b9c3 100644 --- a/goorm/src/main/java/study/goorm/global/error/code/status/ErrorStatus.java +++ b/goorm/src/main/java/study/goorm/global/error/code/status/ErrorStatus.java @@ -17,13 +17,23 @@ public enum ErrorStatus implements BaseErrorCode { // Cloth NO_SUCH_CLOTH(HttpStatus.BAD_REQUEST, "CLOTH_4001", "옷이 존재하지 않습니다."), - NO_ClOTH_IMAGE(HttpStatus.BAD_REQUEST,"CLOTH_4002","옷의 사진이 존재하지 않습니다."), + NO_CLOTH_IMAGE(HttpStatus.BAD_REQUEST,"CLOTH_4002","옷의 사진이 존재하지 않습니다."), NO_SUCH_CATEGORY(HttpStatus.BAD_REQUEST, "CLOTH_4003", "카테고리가 존재하지 않습니다."), LOWER_TEMP_BIGGER_THAN_UPPER_TEMP(HttpStatus.BAD_REQUEST,"CLOTH_4004","옷의 하한 온도가 상한 온도 보다 높습니다."), // Member NO_SUCH_MEMBER(HttpStatus.BAD_REQUEST,"MEMBER_4001","멤버가 존재하지 않습니다."), + // History + NO_SUCH_HISTORY(HttpStatus.BAD_REQUEST, "HISTORY_4001", "존재하지 않는 기록입니다."), + NO_GRANT_HISTORY(HttpStatus.BAD_REQUEST, "HISTORY_4002", "기록에 접근 권한이 없습니다."), + NO_HISTORY_IMAGE(HttpStatus.BAD_REQUEST, "HISTORY_4003", "기록에는 사진을 첨부해야합니다."), + NO_ENOUGH_IMAGES(HttpStatus.BAD_REQUEST,"HISTORY_4004","이미지는 1~10장 첨부해야 합니다."), + NO_ONLY_CLOTH(HttpStatus.BAD_REQUEST,"HISTORY_4005","중복된 옷 또는 해시태그가 있습니다."), + NO_OWN_CLOTH(HttpStatus.BAD_REQUEST,"HISTORY_4005","본인 옷이 아닙니다."), + NO_STATE_LIKE(HttpStatus.BAD_REQUEST, "HISTORY_4006", "이미 좋아요가 눌려있거나 취소되어 있습니다."), + NO_COMMENT_PLUS(HttpStatus.BAD_REQUEST, "HISTORY_4006", "대대댓글입니다."), + NO_SUCH_COMMENT(HttpStatus.BAD_REQUEST, "HISTORY_4007", "존재하지 않는 댓글입니다."), // Page PAGE_UNDER_ONE(HttpStatus.BAD_REQUEST,"PAGE_4001","페이지는 1이상으로 입력해야 합니다."), PAGE_SIZE_UNDER_ONE(HttpStatus.BAD_REQUEST,"PAGE_4002","페이지 사이즈는 1이상으로 입력해야 합니다.") diff --git a/goorm/src/main/java/study/goorm/global/error/code/status/SuccessStatus.java b/goorm/src/main/java/study/goorm/global/error/code/status/SuccessStatus.java index cd438a7..ec5ee66 100644 --- a/goorm/src/main/java/study/goorm/global/error/code/status/SuccessStatus.java +++ b/goorm/src/main/java/study/goorm/global/error/code/status/SuccessStatus.java @@ -13,30 +13,21 @@ public enum SuccessStatus implements BaseCode { // Cloth CLOTH_VIEW_SUCCESS(HttpStatus.OK,"CLOTH_200","옷이 성공적으로 조회되었습니다."), CLOTH_CREATED(HttpStatus.CREATED, "CLOTH_201"," 옷이 성공적으로 생성되었습니다."), - CLOTH_DELETED(HttpStatus.NO_CONTENT,"CLOTH_202","옷이 성공적으로 삭제되었습니다") + CLOTH_DELETED(HttpStatus.NO_CONTENT,"CLOTH_202","옷이 성공적으로 삭제되었습니다"), + + // History + HISTORY_VIEW_SUCCESS(HttpStatus.OK, "HISTORY_200", "월별 기록이 성공적으로 조회되었습니다."), + HISTORY_CREATED(HttpStatus.OK, "HISTORY_201", "기록이 성공적으로 추가되었습니다."), + HISTORY_PATCHED(HttpStatus.OK, "HISTORY_202", "기록이 성공적으로 수정되었습니다."), + HISTORY_DELETED(HttpStatus.OK, "HISTORY_204", "기록이 성공적으로 삭제되었습니다."), + + // Comment + COMMENT_VIEW_SUCCESS(HttpStatus.OK, "COMMENT_200", "댓글이 성공적으로 조회되었습니다."), + COMMENT_CREATED(HttpStatus.OK, "COMMENT_201", "댓글이 성공적으로 생성되었습니다."), + COMMENT_PATCHED(HttpStatus.OK, "COMMENT_202", "댓글이 성공적으로 수정되었습니다."), + COMMENT_DELETED(HttpStatus.OK, "COMMENT_204", "댓글이 성공적으로 삭제되었습니다."), ; private final HttpStatus httpStatus; private final String code; - private final String message; - - @Override - public String getCode() { - return code; - } - - @Override - public String getMessage() { - return message; - } - - @Override - public ReasonDTO getReasonHttpStatus() { - return ReasonDTO.builder() - .message(message) - .code(code) - .isSuccess(true) - .httpStatus(httpStatus) - .build(); - } -} + private fi \ No newline at end of file