Skip to content

Commit

Permalink
Merge branch 'develop' of https://github.com/Lets-JUPJUP/JUPJUP-Back
Browse files Browse the repository at this point in the history
…into develop
  • Loading branch information
ParkIsComing committed Aug 21, 2024
2 parents 9661d3d + eed5a99 commit f635e34
Show file tree
Hide file tree
Showing 28 changed files with 524 additions and 95 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-security'
testImplementation 'org.springframework.security:spring-security-test'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation 'org.springframework.data:spring-data-jpa'

//springdoc
implementation 'org.springdoc:springdoc-openapi-ui:1.6.12'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package efub.back.jupjup.domain.comment.service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import org.springframework.http.ResponseEntity;
Expand All @@ -13,17 +14,22 @@

import efub.back.jupjup.domain.comment.domain.Comment;
import efub.back.jupjup.domain.comment.dto.CommentDto;
import efub.back.jupjup.domain.comment.dto.CommentPostDto;
import efub.back.jupjup.domain.post.dto.PostResponseDto;
import efub.back.jupjup.domain.post.domain.PostImage;
import efub.back.jupjup.domain.comment.dto.CommentRequestDto;
import efub.back.jupjup.domain.comment.dto.CommentResponseDto;
import efub.back.jupjup.domain.comment.dto.ReplyRequestDto;
import efub.back.jupjup.domain.comment.dto.ReplyResponseDto;
import efub.back.jupjup.domain.comment.exception.NoAuthorityCommentRemoveException;
import efub.back.jupjup.domain.comment.exception.NoCommentExistsException;
import efub.back.jupjup.domain.comment.exception.NoPostExistException;
import efub.back.jupjup.domain.postjoin.repository.PostjoinRepository;
import efub.back.jupjup.domain.score.repository.ScoreRepository;
import efub.back.jupjup.domain.post.repository.PostImageRepository;
import efub.back.jupjup.domain.comment.repository.CommentRepository;
import efub.back.jupjup.domain.member.domain.Member;
import efub.back.jupjup.domain.member.repository.MemberRepository;
import efub.back.jupjup.domain.heart.repository.HeartRepository;
import efub.back.jupjup.domain.notification.domain.NotificationType;
import efub.back.jupjup.domain.notification.service.FirebaseService;
import efub.back.jupjup.domain.notification.service.NotificationService;
Expand All @@ -44,6 +50,10 @@ public class CommentService {
private final CommentRepository commentRepository;
private final PostRepository postRepository;
private final MemberRepository memberRepository;
private final PostImageRepository postImageRepository;
private final ScoreRepository scoreRepository;
private final PostjoinRepository postjoinRepository;
private final HeartRepository heartRepository;
private final NotificationService notificationService;
private final FirebaseService firebaseService;

Expand Down Expand Up @@ -169,18 +179,15 @@ public ResponseEntity<StatusResponse> removeReply(Long replyId, Member member) {
.build());
}

// 내가 쓴 댓글의 게시글 모아보기
// 내가 쓴 댓글의 게시글 모아보기 (수정됨)
public ResponseEntity<StatusResponse> getCommentedPosts(Member member) {
List<Comment> comments = commentRepository.findByWriter(member);
LocalDateTime now = LocalDateTime.now();

List<CommentPostDto> commentedPosts = comments.stream()
.collect(Collectors.toMap(
comment -> comment.getPost().getId(),
CommentPostDto::of,
(existing, replacement) -> existing,
LinkedHashMap::new
))
.values().stream()
List<PostResponseDto> commentedPosts = comments.stream()
.map(Comment::getPost)
.distinct()
.map(post -> createPostResponseDto(post, member, now))
.collect(Collectors.toList());

return ResponseEntity.ok(StatusResponse.builder()
Expand All @@ -189,4 +196,21 @@ public ResponseEntity<StatusResponse> getCommentedPosts(Member member) {
.data(commentedPosts)
.build());
}

private PostResponseDto createPostResponseDto(Post post, Member member, LocalDateTime now) {
List<String> urlList = postImageRepository.findAllByPost(post)
.stream()
.map(PostImage::getFileUrl)
.collect(Collectors.toList());

boolean isAuthor = member.getId().equals(post.getAuthor().getId());
boolean hasJoined = postjoinRepository.existsByMemberAndPost(member, post);
boolean isJoined = isAuthor || hasJoined;
boolean isHearted = heartRepository.existsByMemberAndPost(member, post);
boolean isEnded = now.isAfter(post.getDueDate());
boolean isReviewed = scoreRepository.existsByParticipantAndPost(member, post);
Long joinedMemberCount = postjoinRepository.countByPost(post) + 1;

return PostResponseDto.of(post, urlList, Optional.of(isJoined), Optional.of(isHearted), isEnded, isAuthor, isReviewed, joinedMemberCount);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ public class HeartController {
// 게시글 찜하기
@PostMapping("/{postId}")
public ResponseEntity<StatusResponse> heartPost(@PathVariable Long postId, @AuthUser Member member) {
Post post = postRepository.findById(postId).orElseThrow(() -> new IllegalArgumentException("해당 게시글을 찾을 수 없습니다."));
return heartService.heartPost(member, postId);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package efub.back.jupjup.domain.heart.exception;

import efub.back.jupjup.global.exception.custom.ApplicationException;
import org.springframework.http.HttpStatus;

public class DuplicateHeartException extends ApplicationException {
public DuplicateHeartException() {
super(HttpStatus.BAD_REQUEST);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import efub.back.jupjup.domain.heart.domain.Heart;
import efub.back.jupjup.domain.heart.dto.HeartResponseDto;
import efub.back.jupjup.domain.heart.exception.DuplicateHeartException;
import efub.back.jupjup.domain.heart.repository.HeartRepository;
import efub.back.jupjup.domain.member.domain.Member;
import efub.back.jupjup.domain.post.domain.Post;
Expand Down Expand Up @@ -44,6 +45,11 @@ public ResponseEntity<StatusResponse> heartPost(Member member, Long postId) {
Post post = postRepository.findById(postId)
.orElseThrow(() -> new IllegalArgumentException("해당 게시글을 찾을 수 없습니다."));

// 이미 좋아요를 눌렀는지 확인
if (heartRepository.existsByMemberAndPost(member, post)) {
throw new DuplicateHeartException();
}

Heart heart = new Heart(member, post);
heartRepository.save(heart);

Expand Down Expand Up @@ -72,13 +78,12 @@ public ResponseEntity<StatusResponse> unheartPost(Member member, Long postId) {
.build());
}

// 찜한 글 모아보기
// 찜한 글 모아보기 (수정됨)
@Transactional(readOnly = true)
public ResponseEntity<StatusResponse> findHeartPostList(Member member) {
List<Heart> heartList = heartRepository.findAllByMemberOrderByIdDesc(member);

List<PostResponseDto> postList = new ArrayList<>();
for (Heart heart : heartList) {
List<PostResponseDto> postList = heartList.stream().map(heart -> {
Post post = postRepository.findById(heart.getPost().getId())
.orElseThrow(() -> new PostNotFoundException(heart.getPost().getId()));

Expand All @@ -92,19 +97,15 @@ public ResponseEntity<StatusResponse> findHeartPostList(Member member) {
boolean isEnded = LocalDateTime.now().isAfter(post.getDueDate());
boolean isAuthor = post.getAuthor().getId().equals(member.getId());
boolean isReviewed = scoreRepository.existsByParticipantAndPost(member, post);
Long joinedMemberCount = postjoinRepository.countByPost(post);

return PostResponseDto.of(post, imgUrlList, Optional.of(isJoined), Optional.of(isHearted), isEnded, isAuthor, isReviewed, joinedMemberCount);
}).collect(Collectors.toList());

PostResponseDto postResponseDto = PostResponseDto.of(post, imgUrlList, Optional.of(isJoined), Optional.of(isHearted), isEnded, isAuthor, isReviewed);
postList.add(postResponseDto);
}

Long count = heartRepository.countByMember(member);
HeartResponseDto response = new HeartResponseDto(postList, member.getId(), count);

return ResponseEntity.ok(StatusResponse.builder()
.status(StatusEnum.OK.getStatusCode())
.message(StatusEnum.OK.getCode())
.data(response)
.build());
return ResponseEntity.ok(StatusResponse.builder()
.status(StatusEnum.OK.getStatusCode())
.message(StatusEnum.OK.getCode())
.data(postList)
.build());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import efub.back.jupjup.domain.member.domain.Member;
import efub.back.jupjup.domain.member.dto.request.MemberReqDto;
import efub.back.jupjup.domain.member.dto.request.NicknameCheckReqDto;
import efub.back.jupjup.domain.member.service.MemberProfileFacade;
import efub.back.jupjup.domain.member.service.MemberService;
import efub.back.jupjup.domain.security.userInfo.AuthUser;
import efub.back.jupjup.global.response.StatusResponse;
Expand All @@ -23,6 +24,7 @@
@RequiredArgsConstructor
public class MemberController {
private final MemberService memberService;
private final MemberProfileFacade memberProfileFacade;

@GetMapping("/{memberId}")
public ResponseEntity<StatusResponse> readProfile(@PathVariable Long memberId) {
Expand All @@ -46,4 +48,10 @@ public ResponseEntity<StatusResponse> checkDuplicateNickname(@RequestBody Nickna
@AuthUser Member member) {
return memberService.checkDuplicateNickname(nicknameCheckReqDto, member);
}

@GetMapping("/stats/{memberId}")
public ResponseEntity<StatusResponse> getUserProfileStats(@PathVariable Long memberId) {
return memberProfileFacade.getUserProfileStats(memberId);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package efub.back.jupjup.domain.member.dto.response;

import java.math.BigDecimal;

import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class ProfileStatResDto {
private int userHeartsCount;
private BigDecimal averageScore; // 평균 평점
private int postCount;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package efub.back.jupjup.domain.member.service;

import java.math.BigDecimal;

import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import efub.back.jupjup.domain.member.domain.Member;
import efub.back.jupjup.domain.member.dto.response.ProfileStatResDto;
import efub.back.jupjup.domain.member.exception.MemberNotFoundException;
import efub.back.jupjup.domain.member.repository.MemberRepository;
import efub.back.jupjup.domain.post.repository.PostRepository;
import efub.back.jupjup.domain.postjoin.repository.PostjoinRepository;
import efub.back.jupjup.domain.score.domain.AverageScore;
import efub.back.jupjup.domain.score.repository.AverageScoreRepository;
import efub.back.jupjup.domain.userHeart.repository.UserHeartRepository;
import efub.back.jupjup.global.response.StatusEnum;
import efub.back.jupjup.global.response.StatusResponse;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Service
public class MemberProfileFacade {
private final MemberRepository memberRepository;
private final PostRepository postRepository;
private final PostjoinRepository postjoinRepository;
private final UserHeartRepository userHeartRepository;
private final AverageScoreRepository averageScoreRepository;

@Transactional(readOnly = true)
public ResponseEntity<StatusResponse> getUserProfileStats(Long memberId) {
Member member = memberRepository.findById(memberId).orElseThrow(MemberNotFoundException::new);
// 주최한 플로깅 개수
int hostCount = (int)postRepository.countByAuthor(member);
int joinCount = (int)postjoinRepository.countByMember(member);
int postCount = hostCount + joinCount;
int userHeartCount = userHeartRepository.countUserHeartsByMemberId(memberId).intValue();
BigDecimal averageScore = averageScoreRepository.findById(memberId)
.map(AverageScore::getAverageScore)
.orElse(BigDecimal.ZERO);

ProfileStatResDto profileStatResDto = ProfileStatResDto.builder()
.averageScore(averageScore)
.postCount(postCount)
.userHeartsCount(userHeartCount)
.build();

return ResponseEntity.ok()
.body(StatusResponse.builder()
.status(StatusEnum.OK.getStatusCode())
.message(StatusEnum.OK.getCode())
.data(profileStatResDto)
.build());
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
package efub.back.jupjup.domain.post.controller;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import efub.back.jupjup.domain.member.domain.Member;
import efub.back.jupjup.domain.post.domain.District;
import efub.back.jupjup.domain.post.dto.PostFilterDto;
import efub.back.jupjup.domain.post.dto.PostRequestDto;
import efub.back.jupjup.domain.post.service.PostService;
import efub.back.jupjup.domain.security.userInfo.AuthUser;
Expand Down Expand Up @@ -96,4 +102,28 @@ public ResponseEntity<StatusResponse> getSuccessfulRecruitmentPosts(@AuthUser Me
public ResponseEntity<StatusResponse> getCompletedPosts(@AuthUser Member member) {
return postService.getCompletedPosts(member);
}

// 필터링된 게시글 리스트 조회
@GetMapping("/filter")
public ResponseEntity<StatusResponse> getFilteredPosts(
@ModelAttribute PostFilterDto filterDto,
@AuthUser Member member,
@RequestParam(required = false) List<String> districts) {

if (districts != null && !districts.isEmpty()) {
List<District> districtList = districts.stream()
.map(District::from)
.collect(Collectors.toList());
filterDto.setDistricts(districtList);
}

filterDto.setUserGender(member.getGender());
return postService.getFilteredPosts(filterDto, member);
}

// 가장 최근에 완료한 플로깅 게시글 1개 조회하기
@GetMapping("/latest-completed")
public ResponseEntity<StatusResponse> getLatestCompletedPost(@AuthUser Member member) {
return postService.getLatestCompletedPost(member);
}
}
52 changes: 47 additions & 5 deletions src/main/java/efub/back/jupjup/domain/post/domain/District.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,54 @@
package efub.back.jupjup.domain.post.domain;

import lombok.AllArgsConstructor;
import com.fasterxml.jackson.annotation.JsonCreator;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum District {
강남구, 강동구, 강북구, 강서구, 관악구, 광진구, 구로구, 금천구, 노원구, 도봉구,
동대문구, 동작구, 마포구, 서대문구, 서초구, 성동구, 성북구, 송파구, 양천구, 영등포구,
용산구, 은평구, 종로구, 중구, 중랑구
GANGNAM("강남구"),
GANGDONG("강동구"),
GANGBUK("강북구"),
GANGSEO("강서구"),
GWANAK("관악구"),
GWANGJIN("광진구"),
GURO("구로구"),
GEUMCHEON("금천구"),
NOWON("노원구"),
DOBONG("도봉구"),
DONGDAEMUN("동대문구"),
DONGJAK("동작구"),
MAPO("마포구"),
SEODAEMUN("서대문구"),
SEOCHO("서초구"),
SEONGDONG("성동구"),
SEONGBUK("성북구"),
SONGPA("송파구"),
YANGCHEON("양천구"),
YEONGDEUNGPO("영등포구"),
YONGSAN("용산구"),
EUNPYEONG("은평구"),
JONGNO("종로구"),
JUNG("중구"),
JUNGNANG("중랑구");

private final String koreanName;

District(String koreanName) {
this.koreanName = koreanName;
}

@JsonCreator
public static District from(String value) {
for (District district : District.values()) {
if (district.getKoreanName().equals(value) || district.name().equalsIgnoreCase(value)) {
return district;
}
}
throw new IllegalArgumentException("Invalid district: " + value);
}

@Override
public String toString() {
return this.koreanName;
}
}
Loading

0 comments on commit f635e34

Please sign in to comment.