diff --git a/.DS_Store b/.DS_Store index 281e3db..2b11931 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/build.gradle b/build.gradle index 2ff8e85..89141c4 100644 --- a/build.gradle +++ b/build.gradle @@ -35,8 +35,12 @@ dependencies { // S3 implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' - // h2 db 추가 - implementation "com.h2database:h2" + + //ModelMapper 라이브러리 추가 + implementation 'org.modelmapper:modelmapper:2.4.2' + + //파일 관련 + implementation 'org.apache.pdfbox:pdfbox:2.0.24' } tasks.named('test') { diff --git a/build/libs/BE-ver_230228.jar b/build/libs/BE-ver_230228.jar deleted file mode 100644 index cc38cb3..0000000 Binary files a/build/libs/BE-ver_230228.jar and /dev/null differ diff --git a/build/libs/BE-ver_230301.jar b/build/libs/BE-ver_230301.jar deleted file mode 100644 index 980901d..0000000 Binary files a/build/libs/BE-ver_230301.jar and /dev/null differ diff --git a/src/main/java/Remoa/BE/Member/Controller/FollowController.java b/src/main/java/Remoa/BE/Member/Controller/FollowController.java index 9e20bdd..a16b415 100644 --- a/src/main/java/Remoa/BE/Member/Controller/FollowController.java +++ b/src/main/java/Remoa/BE/Member/Controller/FollowController.java @@ -4,18 +4,13 @@ import Remoa.BE.Member.Service.FollowService; import Remoa.BE.Member.Service.MemberService; import Remoa.BE.exception.CustomMessage; -import Remoa.BE.exception.response.ErrorResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; -import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -27,7 +22,6 @@ @RestController @Slf4j @RequiredArgsConstructor -@CrossOrigin(origins = "*") public class FollowController { private final FollowService followService; diff --git a/src/main/java/Remoa/BE/Member/Controller/KakaoController.java b/src/main/java/Remoa/BE/Member/Controller/KakaoController.java index be74e54..3097c5a 100644 --- a/src/main/java/Remoa/BE/Member/Controller/KakaoController.java +++ b/src/main/java/Remoa/BE/Member/Controller/KakaoController.java @@ -5,21 +5,21 @@ import Remoa.BE.Member.Dto.Res.ResSignupDto; import Remoa.BE.Member.Service.KakaoService; import Remoa.BE.Member.Service.MemberService; +import Remoa.BE.Member.Service.ProfileService; import Remoa.BE.exception.CustomMessage; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; -import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; import java.io.IOException; import java.util.Map; import java.util.Optional; +import java.util.Random; import static Remoa.BE.exception.CustomBody.*; import static Remoa.BE.utill.MemberInfo.*; @@ -28,12 +28,13 @@ @RestController @Slf4j @RequiredArgsConstructor -@CrossOrigin(origins = "*") // 프론트에서 추가 요청 public class KakaoController { + + private final KakaoService ks; private final MemberService memberService; - private final HttpSession httpSession; + private final ProfileService profileService; /** * 카카오 로그인을 통해 code를 query string으로 받아오면, 코드를 통해 토큰, 토큰을 통해 사용자 정보를 얻어와 db에 해당 사용자가 존재하는지 여부를 @@ -62,7 +63,7 @@ public ResponseEntity getCI(@RequestParam String code, HttpServletReques if (member.isPresent()) { securityLoginWithoutLoginForm(member.get(), request); //if문에 걸리지 않았다면 이미 회원가입이 진행돼 db에 kakaoId가 있는 유저이므로 kakaoMember가 존재하므로 LoginController처럼 로그인 처리 하면 됩니다. - return successResponse(CustomMessage.OK, userInfo); + return successResponse(CustomMessage.OK, member.get().getNickname()); } else { //kakaoId가 db에 없으므로 kakaoMember가 null이므로 회원가입하지 않은 회원. 따라서 회원가입이 필요하므로 회원가입하는 uri로 redirect 시켜주어야 함. @@ -70,34 +71,31 @@ public ResponseEntity getCI(@RequestParam String code, HttpServletReques } } - /** - * 카카오 로그인을 우회해 테스트 하기 위한 용도로 추가됨. - * @param kakaoId - * @return ResponseEntity - */ - @PostMapping("/login/kakao/test") - public ResponseEntity testLogin(@RequestBody Integer kakaoId, HttpServletRequest request) { - log.warn("kakaoId = {}", kakaoId); - Optional findMember = memberService.findByKakaoId(Long.valueOf(kakaoId)); - if (findMember.isPresent()) { - Member member = findMember.get(); - securityLoginWithoutLoginForm(member, request); - return successResponse(CustomMessage.OK, member); - } - return failResponse(CustomMessage.VALIDATED, "User Not Exist"); - } - /** * front-end에서 회원가입에 필요한 정보를 넘겨주면 KakaoSignupForm으로 받아 회원가입을 진행시켜줌 */ @PostMapping("/signup/kakao") - public ResponseEntity signupKakaoMember(@RequestBody @Validated ReqSignupDto form, HttpServletRequest request) { + public ResponseEntity signupKakaoMember(@RequestBody @Validated ReqSignupDto form, HttpServletRequest request) throws IOException { Member member = new Member(); + Random random = new Random(); + + //닉네임 사용 가능하면 그대로 진행, 불가능하면 임의 닉네임 "유저-{randomInt}로 지정. + String randomNumber = Integer.toString((random.nextInt(900_000) + 100_000)); // 100_000 ~ 999_999 + boolean nicknameDuplicate = memberService.isNicknameDuplicate("유저-" + randomNumber); + while (nicknameDuplicate) { //특수문자는 닉네임에 사용할 수 없으나 임의로 지정하는 닉네임에는 사용 가능하게 해서 또 다른 중복 문제 없게끔. + randomNumber = Integer.toString((random.nextInt(900_000) + 100_000)); // 100_000 ~ 999_999 + nicknameDuplicate = memberService.isNicknameDuplicate("유저-" + randomNumber); + } + member.setNickname("유저-" + randomNumber); + + + //카카오에서 받은 프로필 사진 url 링크를 토대로 s3에 저장 + if (memberService.findByKakaoId(form.getKakaoId()).isPresent()) { + return failResponse(CustomMessage.VALIDATED, "kakaoId가 이미 가입되어 있습니다."); + } member.setKakaoId(form.getKakaoId()); member.setEmail(form.getEmail()); - member.setNickname(form.getNickname()); - member.setProfileImage(form.getProfileImage()); member.setTermConsent(form.getTermConsent()); memberService.join(member); @@ -114,39 +112,22 @@ public ResponseEntity signupKakaoMember(@RequestBody @Validated ReqSignu return successResponse(CustomMessage.OK,result); } - /** - *자동 로그인 추후에 - */ -/* - @GetMapping("/login") - public ResponseEntity autoLogin(){ - Long kaKaoId = getKaKaoId(); - Optional member = memberService.findByKakaoId(kaKaoId); - if(member.isPresent()){ - return successResponse(CustomMessage.OK,member); - } - else{ - return errorResponse(CustomMessage.UNAUTHORIZED); - } - - } -*/ - /** * 로그아웃 기능 세션무효화, jsession쿠키를 제거, */ - @PostMapping("/user/logout") - public ResponseEntity logout(HttpServletResponse response){ + @PostMapping ("/user/logout") + public ResponseEntity logout(HttpServletRequest request){ + if(authorized(request)) { + + SecurityContextHolder.clearContext(); + request.getSession().invalidate(); - SecurityContextHolder.clearContext(); // 현재 SecurityContext를 제거합니다. - httpSession.invalidate(); // HttpSession을 무효화합니다. + return new ResponseEntity<>(HttpStatus.OK); + + } - Cookie myCookie = new Cookie("JSESSIONID", null); - myCookie.setMaxAge(0); // 쿠키의 expiration 타임을 0으로 하여 없앤다. - myCookie.setPath("/"); // 모든 경로에서 삭제 됬음을 알린다. - response.addCookie(myCookie); - return successResponse(CustomMessage.OK,myCookie); + return errorResponse(CustomMessage.UNAUTHORIZED); } diff --git a/src/main/java/Remoa/BE/Member/Controller/MyFollowingController.java b/src/main/java/Remoa/BE/Member/Controller/MyFollowingController.java new file mode 100644 index 0000000..6c3073d --- /dev/null +++ b/src/main/java/Remoa/BE/Member/Controller/MyFollowingController.java @@ -0,0 +1,55 @@ +package Remoa.BE.Member.Controller; + +import Remoa.BE.Member.Domain.Member; +import Remoa.BE.Member.Dto.Res.ResMypageFollowing; +import Remoa.BE.Member.Service.MemberService; +import Remoa.BE.Member.Service.MyFollowingService; +import Remoa.BE.exception.CustomMessage; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpRequest; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; + +import static Remoa.BE.exception.CustomBody.errorResponse; +import static Remoa.BE.exception.CustomBody.successResponse; +import static Remoa.BE.utill.MemberInfo.authorized; +import static Remoa.BE.utill.MemberInfo.getMemberId; + +@RestController +@Slf4j +@RequiredArgsConstructor +public class MyFollowingController { + + private final MemberService memberService; + + private final MyFollowingService myFollowingService; + + @GetMapping("/mypage/following") // 마이페이지 팔로잉 관리화면 + public ResponseEntity mypageFollowing(HttpServletRequest request){ + if(authorized(request)){ + Long myMemberId = getMemberId(); + Member member = memberService.findOne(myMemberId); + ResMypageFollowing resMypageFollowing = myFollowingService.mypageFollowing(member); + + return successResponse(CustomMessage.OK, resMypageFollowing); + } + return errorResponse(CustomMessage.UNAUTHORIZED); + } + + @GetMapping("/mypage/follower") // 마이페이지 팔로워 관리화면 + public ResponseEntity mypageFollower(HttpServletRequest request){ + if(authorized(request)){ + Long myMemberId = getMemberId(); + Member member = memberService.findOne(myMemberId); + ResMypageFollowing resMypageFollower = myFollowingService.mypageFollower(member); + + return successResponse(CustomMessage.OK, resMypageFollower); + } + return errorResponse(CustomMessage.UNAUTHORIZED); + } + +} diff --git a/src/main/java/Remoa/BE/Member/Controller/ProfileController.java b/src/main/java/Remoa/BE/Member/Controller/ProfileController.java index c8bebb4..1dd741d 100644 --- a/src/main/java/Remoa/BE/Member/Controller/ProfileController.java +++ b/src/main/java/Remoa/BE/Member/Controller/ProfileController.java @@ -1,74 +1,155 @@ package Remoa.BE.Member.Controller; +import Remoa.BE.Member.Domain.AwsS3; import Remoa.BE.Member.Domain.Member; import Remoa.BE.Member.Dto.Req.EditProfileForm; +import Remoa.BE.Member.Dto.Res.ResUserInfoDto; +//import Remoa.BE.Member.Service.ImageService; +import Remoa.BE.Member.Service.AwsS3Service; import Remoa.BE.Member.Service.MemberService; import Remoa.BE.Member.Service.ProfileService; import Remoa.BE.exception.CustomMessage; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; + + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.net.MalformedURLException; + import static Remoa.BE.exception.CustomBody.*; +import static Remoa.BE.utill.FileExtension.fileExtension; +import static Remoa.BE.utill.MemberInfo.authorized; +import static Remoa.BE.utill.MemberInfo.getMemberId; @Slf4j @RestController @RequiredArgsConstructor -@CrossOrigin(origins = "*") public class ProfileController { private final ProfileService profileService; - private final MemberService memberService; + private final AwsS3Service awsS3Service; // 프로필 수정 범위 : 닉네임(중복확인), 핸드폰번호, 대학교, 한줄소개 @GetMapping("/user") - public ResponseEntity userHome(HttpServletRequest request) { + public ResponseEntity userInfo(HttpServletRequest request) { + + if(authorized(request)) { + Long memberId = getMemberId(); + // 로그인된 사용자의 정보를 db에서 다시 불러와 띄워줌. + Member member = memberService.findOne(memberId); + ResUserInfoDto resUserInfoDto = ResUserInfoDto.builder() + .email(member.getEmail()) + .nickname(member.getNickname()) + .phoneNumber(member.getPhoneNumber()) + .university(member.getUniversity()) + .oneLineIntroduction(member.getOneLineIntroduction()) + .build(); + return successResponse(CustomMessage.OK, resUserInfoDto); + } + + return errorResponse(CustomMessage.UNAUTHORIZED); + + } + + @PutMapping("/user") + public ResponseEntity editProfile(@RequestBody EditProfileForm form, HttpServletRequest request) { + + if(authorized(request)){ + Long memberId = getMemberId(); + + Member myMember = memberService.findOne(memberId); + log.info(myMember.getNickname()); + if(memberService.isNicknameDuplicate(myMember.getNickname())) { + + // 사용자의 입력 정보를 DTO에 담아 서비스로 전달 + profileService.editProfile(memberId, form); + ResUserInfoDto resUserInfoDto = ResUserInfoDto.builder() + .email(myMember.getEmail()) + .nickname(myMember.getNickname()) + .phoneNumber(myMember.getPhoneNumber()) + .university(myMember.getUniversity()) + .oneLineIntroduction(myMember.getOneLineIntroduction()) + .build(); + + return successResponse(CustomMessage.OK, resUserInfoDto); + } - HttpSession session = request.getSession(); - // 현재 로그인한 사용자의 세션 가져오기 - Member loginMember = (Member) session.getAttribute("loginMember"); + return errorResponse(CustomMessage.BAD_DUPLICATE); - // 세션이 없으면 로그인 페이지로 이동 - if (loginMember == null) { - return failResponse(CustomMessage.VALIDATED, "redirect:/login/kakao"); } - // 로그인된 사용자의 정보를 db에서 다시 불러와 띄워줌. - Member member = memberService.findOne(loginMember.getMemberId()); - return successResponse(CustomMessage.OK, member); + // 수정이 완료되면 프로필 페이지로 이동 + return errorResponse(CustomMessage.UNAUTHORIZED); } - // RESTful API에서 PUT 매핑은 수정할 리소스를 명확하게 지정해야 하는데 이 경우에는 URL에 리소스 ID를 명시하는 것이 일반적이다. - // 그런데 우리는 수정할 사용자의 정보를 모두 입력받아 수정하는 형태이기 때문에 - // URL에 리소스 ID를 명시할 필요가 없어서 PUT대신 POST 매핑을 사용하였습니다. - @PostMapping("/user") - public ResponseEntity editProfile(@RequestBody EditProfileForm form, HttpServletRequest request) { - HttpSession session = request.getSession(); - Member loginMember = (Member) session.getAttribute("loginMember"); - if (loginMember == null) { - // 로그인되어 있지 않은 경우 로그인 페이지로 이동 - return failResponse(CustomMessage.VALIDATED, "로그인하지 않은 회원입니다. 로 redirect"); + // 프로필 사진 불러오기 + @GetMapping("/user/img") + public ResponseEntity showImage(HttpServletRequest request) { + if(authorized(request)) { + Long memberId = getMemberId(); + Member myMember = memberService.findOne(memberId); + return successResponse(CustomMessage.OK, myMember.getProfileImage()); } + return errorResponse(CustomMessage.UNAUTHORIZED); + } + + // 프로필 사진 업로드 + @PutMapping("/user/img") + public ResponseEntity upload(@RequestPart("file") MultipartFile multipartFile, HttpServletRequest request) throws IOException { + + if(authorized(request)) { + + //jpg랑 png만 가능합니다 + String extension = fileExtension(multipartFile); + if(extension.equals("png") ||extension.equals("jpg")) { + + Long memberId = getMemberId(); + Member myMember = memberService.findOne(memberId); + - if (memberService.isNicknameDuplicate(form.getNickname())) { - return failResponse(CustomMessage.VALIDATED, "닉네임이 중복됩니다."); + String editProfileImg = awsS3Service.editProfileImg(myMember.getProfileImage(), multipartFile); + myMember.setProfileImage(editProfileImg); + memberService.join(myMember); + return new ResponseEntity<>(HttpStatus.OK); + } + else{ + throw new IOException(); + } } + return errorResponse(CustomMessage.UNAUTHORIZED); + } - // 사용자의 입력 정보를 DTO에 담아 서비스로 전달 - EditProfileForm profileInfo = new EditProfileForm( - form.getNickname(), form.getPhoneNumber(), form.getUniversity(), form.getOneLineIntroduction()); - profileService.editProfile(loginMember.getMemberId(), profileInfo); + // 프로필 사진 삭제 + @DeleteMapping("/user/img") + public ResponseEntity remove(HttpServletRequest request) throws MalformedURLException { + if(authorized(request)) { - // 수정이 완료되면 프로필 페이지로 이동 - return successResponse(CustomMessage.OK, "redirect:/user"); + Long memberId = getMemberId(); + Member myMember = memberService.findOne(memberId); + + awsS3Service.removeProfileUrl(myMember.getProfileImage()); + myMember.setProfileImage("https://remoa.s3.ap-northeast-2.amazonaws.com/img/profile_img.png"); + memberService.join(myMember); + return new ResponseEntity<>(HttpStatus.OK); + } + return errorResponse(CustomMessage.UNAUTHORIZED); } + + + /** * 프론트에서 닉네임 중복 검사를 할 때 사용할 메서드 * @param nickname diff --git a/src/main/java/Remoa/BE/Member/Controller/WithdrewController.java b/src/main/java/Remoa/BE/Member/Controller/WithdrewController.java index e6922b7..d133a42 100644 --- a/src/main/java/Remoa/BE/Member/Controller/WithdrewController.java +++ b/src/main/java/Remoa/BE/Member/Controller/WithdrewController.java @@ -11,11 +11,11 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; +import java.util.Objects; @RestController @Slf4j @RequiredArgsConstructor -@CrossOrigin(origins = "*") public class WithdrewController { private final WithdrewService withdrewService; @@ -41,7 +41,7 @@ public ResponseEntity withdrewRemoa(@PathVariable("member_id") Long memb //PathVariable의 id와 로그인된 사용자의 id가 같은지 확인하기 위한 용도. Member loginMember = (Member) session.getAttribute("loginMember"); - if (loginMember.getMemberId() != memberId) { + if (!Objects.equals(loginMember.getMemberId(), memberId)) { return new ResponseEntity<>("회원정보가 일치하지 않습니다.", HttpStatus.UNAUTHORIZED); } @@ -71,19 +71,4 @@ public ResponseEntity withdrewRemoaWithoutPathVariable(HttpServletReques return new ResponseEntity<>("회원 탈퇴가 완료되었습니다.", HttpStatus.OK); } - /** - * 탈퇴 처리가 되어 deleted 필드가 true인 member가 조회되는지 확인하기 위한 테스트 uri. - * @param memberId - * @return 조회 결과가 없으면 418(I am a tea pot), 있으면 200(OK) - */ - @GetMapping("/find/{member_id}") - public ResponseEntity findMemberTest(@PathVariable("member_id") Long memberId) { - Member member = memberService.findOne(memberId); - - if (member == null) { - return new ResponseEntity<>("회원 정보가 없습니다.", HttpStatus.I_AM_A_TEAPOT); - } - - return new ResponseEntity<>("memberId 번호 <" + memberId + ">는 " + member.getName() + "입니다.", HttpStatus.OK); - } } diff --git a/src/main/java/Remoa/BE/Member/Domain/AwsS3.java b/src/main/java/Remoa/BE/Member/Domain/AwsS3.java new file mode 100644 index 0000000..64b5fa8 --- /dev/null +++ b/src/main/java/Remoa/BE/Member/Domain/AwsS3.java @@ -0,0 +1,22 @@ +package Remoa.BE.Member.Domain; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class AwsS3 { + private String key; + private String path; + + public AwsS3() { + + } + + @Builder + public AwsS3(String key, String path) { + this.key = key; + this.path = path; + } +} \ No newline at end of file diff --git a/src/main/java/Remoa/BE/Member/Domain/Comment.java b/src/main/java/Remoa/BE/Member/Domain/Comment.java index d9a1e07..5d79ca8 100644 --- a/src/main/java/Remoa/BE/Member/Domain/Comment.java +++ b/src/main/java/Remoa/BE/Member/Domain/Comment.java @@ -3,11 +3,12 @@ import Remoa.BE.Post.Domain.Post; import lombok.Getter; import lombok.Setter; -import org.hibernate.annotations.SQLDelete; import org.hibernate.annotations.Where; import javax.persistence.*; +import java.time.LocalDateTime; + import static javax.persistence.FetchType.LAZY; @Entity @@ -17,7 +18,7 @@ public class Comment { @Id - @GeneratedValue + @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "comment_id") private Long commentId; @@ -38,19 +39,30 @@ public class Comment { /** * Comment의 내용 */ + @Lob private String comment; + /** + * 대댓글 기능을 위해 부모 댓글과의 연관관계 세팅. 부모댓글인 경우 null. + */ + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "parent_comment_id") + private Comment parentComment = null; + /** * Comment가 작성된 시간 */ @Column(name = "commented_time") - private String commentedTime; + private LocalDateTime commentedTime; /** * Comment의 좋아요 숫자 */ @Column(name = "comment_like_count") - private Integer commentLikeCount; + private Integer commentLikeCount = 0; + + @OneToOne + private CommentFeedback commentFeedback; private Boolean deleted = Boolean.FALSE; } \ No newline at end of file diff --git a/src/main/java/Remoa/BE/Member/Domain/CommentBookmark.java b/src/main/java/Remoa/BE/Member/Domain/CommentBookmark.java index b90ffc6..b888e13 100644 --- a/src/main/java/Remoa/BE/Member/Domain/CommentBookmark.java +++ b/src/main/java/Remoa/BE/Member/Domain/CommentBookmark.java @@ -14,6 +14,7 @@ @Setter @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) +@Deprecated public class CommentBookmark { @Id diff --git a/src/main/java/Remoa/BE/Member/Domain/CommentFeedback.java b/src/main/java/Remoa/BE/Member/Domain/CommentFeedback.java new file mode 100644 index 0000000..16fbc9e --- /dev/null +++ b/src/main/java/Remoa/BE/Member/Domain/CommentFeedback.java @@ -0,0 +1,49 @@ +package Remoa.BE.Member.Domain; + +import Remoa.BE.Post.Domain.Post; +import lombok.*; +import org.hibernate.annotations.Where; + +import javax.persistence.*; +import java.time.LocalDateTime; + +import static javax.persistence.FetchType.LAZY; + +/** + * 마이페이지-내 활동 관리에 쓰이는 Comment와 Feedback을 구분 없이 최신순으로 조회하기 위한 entity. + */ +@Builder +@Entity +@Getter +@Setter +@Where(clause = "deleted = false") +@NoArgsConstructor +@AllArgsConstructor +public class CommentFeedback { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "comment_feedback_id") + private Long commentFeedbackId; + + + private ContentType type; + + @ManyToOne(fetch = LAZY) + @JoinColumn(name = "member_id") + private Member member; + + @ManyToOne(fetch = LAZY) + @JoinColumn(name = "post_id") + private Post post; + + @OneToOne + private Comment comment; + + @OneToOne + private Feedback feedback; + + private LocalDateTime time; + + private Boolean deleted = Boolean.FALSE; +} diff --git a/src/main/java/Remoa/BE/Member/Domain/ContentType.java b/src/main/java/Remoa/BE/Member/Domain/ContentType.java new file mode 100644 index 0000000..687b287 --- /dev/null +++ b/src/main/java/Remoa/BE/Member/Domain/ContentType.java @@ -0,0 +1,14 @@ +package Remoa.BE.Member.Domain; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * CommentFeedback에 comment와 Feedback 구분을 위한 enum 클래스. + */ +@AllArgsConstructor +@Getter +public enum ContentType { + COMMENT, + FEEDBACK; +} diff --git a/src/main/java/Remoa/BE/Member/Domain/Feedback.java b/src/main/java/Remoa/BE/Member/Domain/Feedback.java index 1bcb965..6961e83 100644 --- a/src/main/java/Remoa/BE/Member/Domain/Feedback.java +++ b/src/main/java/Remoa/BE/Member/Domain/Feedback.java @@ -7,6 +7,8 @@ import javax.persistence.*; +import java.time.LocalDateTime; + import static javax.persistence.FetchType.LAZY; @Entity @@ -16,7 +18,7 @@ public class Feedback { @Id - @GeneratedValue + @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "feedback_id") private Long feedbackId; @@ -40,19 +42,31 @@ public class Feedback { /** * Feedback의 내용 */ + @Lob private String feedback; + /** + * 대댓글 기능을 위해 부모 댓글과의 연관관계 세팅. 부모댓글인 경우 null. + */ + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "parent_feedback_id") + private Feedback parentFeedback = null; + + /** * Feedback이 작성된 시간 */ @Column(name = "feedback_time") - private String feedbackTime; + private LocalDateTime feedbackTime; /** * Feedback의 좋아요 숫자 */ @Column(name = "feedback_like_count") - private Integer feedbackLikeCount; + private Integer feedbackLikeCount = 0; + + @OneToOne + private CommentFeedback commentFeedback; private Boolean deleted = Boolean.FALSE; } \ No newline at end of file diff --git a/src/main/java/Remoa/BE/Member/Domain/FeedbackBookmark.java b/src/main/java/Remoa/BE/Member/Domain/FeedbackBookmark.java index be94dfb..cfe1fbe 100644 --- a/src/main/java/Remoa/BE/Member/Domain/FeedbackBookmark.java +++ b/src/main/java/Remoa/BE/Member/Domain/FeedbackBookmark.java @@ -14,6 +14,7 @@ @Setter @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) +@Deprecated public class FeedbackBookmark { @Id diff --git a/src/main/java/Remoa/BE/Member/Domain/Follow.java b/src/main/java/Remoa/BE/Member/Domain/Follow.java index f84b161..dc6dbd3 100644 --- a/src/main/java/Remoa/BE/Member/Domain/Follow.java +++ b/src/main/java/Remoa/BE/Member/Domain/Follow.java @@ -2,7 +2,6 @@ import lombok.Getter; import lombok.Setter; -import org.hibernate.annotations.SQLDelete; import org.hibernate.annotations.Where; import javax.persistence.*; diff --git a/src/main/java/Remoa/BE/Member/Domain/Member.java b/src/main/java/Remoa/BE/Member/Domain/Member.java index 2d71b83..c206133 100644 --- a/src/main/java/Remoa/BE/Member/Domain/Member.java +++ b/src/main/java/Remoa/BE/Member/Domain/Member.java @@ -1,10 +1,8 @@ package Remoa.BE.Member.Domain; import Remoa.BE.Post.Domain.Post; -import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Getter; import lombok.Setter; -import org.hibernate.annotations.SQLDelete; import org.hibernate.annotations.Where; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; @@ -67,6 +65,7 @@ public class Member implements UserDetails { * 한 줄 소개 */ @Column(name = "one_line_introduction") + @Lob private String oneLineIntroduction; /** @@ -79,29 +78,32 @@ public class Member implements UserDetails { * 카카오 api에서 받아오는 카카오 프로필 사진 uri */ @Column(name = "profile_image") - private String profileImage; + private String profileImage="https://remoa.s3.ap-northeast-2.amazonaws.com/img/profile_img.png"; - @OneToMany(mappedBy = "member") + @OneToMany(mappedBy = "member",fetch = FetchType.LAZY) private List posts = new ArrayList<>(); - @OneToMany(mappedBy = "member") + @OneToMany(mappedBy = "member",fetch = FetchType.LAZY) private List comments = new ArrayList<>(); - @OneToMany(mappedBy = "member") + @OneToMany(mappedBy = "member",fetch = FetchType.LAZY) private List feedbacks = new ArrayList<>(); - @OneToMany(mappedBy = "member", cascade = {CascadeType.ALL}) + @OneToMany(mappedBy = "member", cascade = {CascadeType.ALL},fetch = FetchType.LAZY) private List memberCategories = new ArrayList<>(); - @OneToMany(mappedBy = "member", cascade = {CascadeType.ALL}) + @OneToMany(mappedBy = "member", cascade = {CascadeType.ALL},fetch = FetchType.LAZY) private List commentBookmarks = new ArrayList<>(); - @OneToMany(mappedBy = "member", cascade = {CascadeType.ALL}) + @OneToMany(mappedBy = "member", cascade = {CascadeType.ALL},fetch = FetchType.LAZY) private List commentLikes = new ArrayList<>(); - @OneToMany(mappedBy = "fromMember", cascade = {CascadeType.ALL}) + @OneToMany(mappedBy = "fromMember", cascade = {CascadeType.ALL},fetch = FetchType.LAZY) private List follows = new ArrayList<>(); + @OneToMany(mappedBy = "member", cascade = {CascadeType.ALL}, fetch = FetchType.LAZY) + private List commentFeedbacks = new ArrayList<>(); + /** * ADMIN과 일반 USER를 구분하기 위해 존재. Spring Security 이용하기 위함 */ diff --git a/src/main/java/Remoa/BE/Member/Dto/Req/ReqSignupDto.java b/src/main/java/Remoa/BE/Member/Dto/Req/ReqSignupDto.java index d7dfbfe..7db89c2 100644 --- a/src/main/java/Remoa/BE/Member/Dto/Req/ReqSignupDto.java +++ b/src/main/java/Remoa/BE/Member/Dto/Req/ReqSignupDto.java @@ -16,10 +16,6 @@ public class ReqSignupDto { @NotNull(message = "카카오에서 발급받은 id값이 누락되었습니다.") private Long kakaoId; - @NotBlank(message = "이름은 필수값입니다.") - private String nickname; - - private String profileImage; @NotNull(message = "선택 동의사항 값은 필수입니다.") private Boolean termConsent; diff --git a/src/main/java/Remoa/BE/Member/Dto/Res/ResMemberInfoDto.java b/src/main/java/Remoa/BE/Member/Dto/Res/ResMemberInfoDto.java new file mode 100644 index 0000000..8048aa3 --- /dev/null +++ b/src/main/java/Remoa/BE/Member/Dto/Res/ResMemberInfoDto.java @@ -0,0 +1,13 @@ +package Remoa.BE.Member.Dto.Res; + +import lombok.*; + +@Getter +@AllArgsConstructor +@NoArgsConstructor +public class ResMemberInfoDto { + + private Long memberId; + private String nickname; + private String profileImage; +} diff --git a/src/main/java/Remoa/BE/Member/Dto/Res/ResMypageFollowing.java b/src/main/java/Remoa/BE/Member/Dto/Res/ResMypageFollowing.java new file mode 100644 index 0000000..3756f9b --- /dev/null +++ b/src/main/java/Remoa/BE/Member/Dto/Res/ResMypageFollowing.java @@ -0,0 +1,18 @@ +package Remoa.BE.Member.Dto.Res; + +import lombok.Builder; +import lombok.Getter; + +import java.util.List; + +@Getter +@Builder +public class ResMypageFollowing { + + private String userName; + + private int followNum; + + private List resMypageList; + +} diff --git a/src/main/java/Remoa/BE/Member/Dto/Res/ResMypageList.java b/src/main/java/Remoa/BE/Member/Dto/Res/ResMypageList.java new file mode 100644 index 0000000..849293f --- /dev/null +++ b/src/main/java/Remoa/BE/Member/Dto/Res/ResMypageList.java @@ -0,0 +1,16 @@ +package Remoa.BE.Member.Dto.Res; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class ResMypageList { + private String profileImage; + + private String userName; + + private int followingNum; + + private int followerNum; +} diff --git a/src/main/java/Remoa/BE/Member/Dto/Res/ResSignupDto.java b/src/main/java/Remoa/BE/Member/Dto/Res/ResSignupDto.java index b4b390c..4a20807 100644 --- a/src/main/java/Remoa/BE/Member/Dto/Res/ResSignupDto.java +++ b/src/main/java/Remoa/BE/Member/Dto/Res/ResSignupDto.java @@ -3,8 +3,6 @@ import lombok.Builder; import lombok.Getter; -import javax.validation.constraints.NotBlank; - @Builder @Getter public class ResSignupDto { diff --git a/src/main/java/Remoa/BE/Member/Dto/Res/ResUserInfoDto.java b/src/main/java/Remoa/BE/Member/Dto/Res/ResUserInfoDto.java new file mode 100644 index 0000000..58f37df --- /dev/null +++ b/src/main/java/Remoa/BE/Member/Dto/Res/ResUserInfoDto.java @@ -0,0 +1,19 @@ +package Remoa.BE.Member.Dto.Res; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class ResUserInfoDto { + + private String email; + + private String nickname; + private String phoneNumber; + + private String university; + + private String oneLineIntroduction; + +} diff --git a/src/main/java/Remoa/BE/Member/Repository/MemberRepository.java b/src/main/java/Remoa/BE/Member/Repository/MemberRepository.java index 3f07bcd..195dbb1 100644 --- a/src/main/java/Remoa/BE/Member/Repository/MemberRepository.java +++ b/src/main/java/Remoa/BE/Member/Repository/MemberRepository.java @@ -4,16 +4,11 @@ import Remoa.BE.Member.Domain.Member; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.data.jpa.repository.Modifying; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import javax.persistence.EntityManager; -import javax.persistence.EntityNotFoundException; import java.util.List; import java.util.Optional; -import java.util.stream.Collectors; @Repository @Slf4j @@ -100,6 +95,18 @@ public List loadFollows(Member member) { .getResultList(); } + /** + * 팔로워 Member 객체 리스트 반환 + * @param member + * @return List + */ + public List loadFollowers(Member member){ + return em.createQuery("select f.fromMember from Follow f " + + "where f.toMember = :member", Member.class) + .setParameter("member", member) + .getResultList(); + } + /** * 멤버가 팔로우하는 모든멤버의 아이디를 불러와줌 * @param member diff --git a/src/main/java/Remoa/BE/Member/Service/AwsS3Service.java b/src/main/java/Remoa/BE/Member/Service/AwsS3Service.java new file mode 100644 index 0000000..a097e85 --- /dev/null +++ b/src/main/java/Remoa/BE/Member/Service/AwsS3Service.java @@ -0,0 +1,77 @@ +package Remoa.BE.Member.Service; + +import Remoa.BE.Member.Domain.AwsS3; +import Remoa.BE.utill.FileExtension; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.AmazonS3Exception; +import com.amazonaws.services.s3.model.CannedAccessControlList; +import com.amazonaws.services.s3.model.ObjectMetadata; +import com.amazonaws.services.s3.model.PutObjectRequest; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Optional; +import java.util.UUID; + +import static Remoa.BE.utill.FileExtension.fileExtension; + +@Service +@RequiredArgsConstructor +@Slf4j +public class AwsS3Service { + + private final AmazonS3 amazonS3; + + @Value("${cloud.aws.s3.bucket}") + private String bucket; + + public String editProfileImg(String profileImgUrl,MultipartFile multipartFile) throws IOException { + + if(profileImgUrl != null) { + //기존 프로필 사진 s3에서 삭제 + removeProfileUrl(profileImgUrl); + } + + //파일 타입과 사이즈 저장 + ObjectMetadata objectMetadata = new ObjectMetadata(); + objectMetadata.setContentType("image/jpeg"); + log.info(multipartFile.getContentType()); + objectMetadata.setContentLength(multipartFile.getSize()); + + //파일 이름 + String originalFilename = multipartFile.getOriginalFilename(); + + //파일 이름이 겹치지 않게 + String uuid = UUID.randomUUID().toString(); + + //post 폴더에 따로 넣어서 보관 + String s3name = "img/"+uuid+"_"+originalFilename; + + try (InputStream inputStream = multipartFile.getInputStream()) { + amazonS3.putObject(new PutObjectRequest(bucket, s3name, inputStream, objectMetadata) + .withCannedAcl(CannedAccessControlList.PublicRead)); + } catch (IOException e) { + //파일을 제대로 받아오지 못했을때 + //Todo 예외처리 custom 따로 만들기 + throw new RuntimeException(e); + } + + return amazonS3.getUrl(bucket,s3name).toString().replaceAll("\\+", "+"); + + } + + public void removeProfileUrl(String profileImgUrl) throws MalformedURLException { + URL fileUrl = new URL(profileImgUrl); + String objectKey = fileUrl.getPath().replaceAll("^/", ""); + amazonS3.deleteObject(bucket,objectKey); + } +} \ No newline at end of file diff --git a/src/main/java/Remoa/BE/Member/Service/FollowService.java b/src/main/java/Remoa/BE/Member/Service/FollowService.java index a90ae23..93624af 100644 --- a/src/main/java/Remoa/BE/Member/Service/FollowService.java +++ b/src/main/java/Remoa/BE/Member/Service/FollowService.java @@ -9,7 +9,6 @@ import org.springframework.transaction.annotation.Transactional; import java.util.List; -import java.util.Optional; @Service @Slf4j diff --git a/src/main/java/Remoa/BE/Member/Service/KakaoService.java b/src/main/java/Remoa/BE/Member/Service/KakaoService.java index 79a2ed0..d37e100 100644 --- a/src/main/java/Remoa/BE/Member/Service/KakaoService.java +++ b/src/main/java/Remoa/BE/Member/Service/KakaoService.java @@ -1,19 +1,19 @@ package Remoa.BE.Member.Service; -import Remoa.BE.Member.Domain.Member; import Remoa.BE.Member.Repository.MemberRepository; +import com.amazonaws.services.s3.AmazonS3; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.io.*; import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.ProtocolException; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; @@ -24,7 +24,6 @@ public class KakaoService { //카카오 로그인시 접속해야 할 링크 : https://kauth.kakao.com/oauth/authorize?client_id=139febf9e13da4d124d1c1faafcf3f86&redirect_uri=http://localhost:8080/login/kakao&response_type=code - private final MemberRepository MemberRepository; /** * 카카오 인증 서버에 code를 보내고 token을 발급받는 메서드 @@ -104,7 +103,8 @@ public String getToken(String code) throws IOException { * @return 카카오 사용자 정보((kakao)id, nickname, email, profileImage) * @throws IOException */ - public Map getUserInfo(String access_token) throws IOException { + public Map getUserInfo(String access_token) { + //사용자 정보를 받아올 카카오 api 서버. 레모아 서버가 클리아언트로, 카카오 api 서버가 서버로 동작한다고 보면 됩니다. String host = "https://kapi.kakao.com/v2/user/me"; //사용자 정보를 받을 Map 객체 생성 @@ -121,7 +121,7 @@ public Map getUserInfo(String access_token) throws IOException { int responseCode = urlConnection.getResponseCode(); log.debug("responseCode = {}", responseCode); - BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); + BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), StandardCharsets.UTF_8)); String line = ""; String res = ""; //다양한 형식(한 줄 이상의 JSON 데이터)를 받기 위한 작업 @@ -133,18 +133,13 @@ public Map getUserInfo(String access_token) throws IOException { //JSON parsing JSONParser parser = new JSONParser(); JSONObject obj = (JSONObject) parser.parse(res); - JSONObject properties = (JSONObject) obj.get("properties"); String id = obj.get("id").toString(); - String nickname = properties.get("nickname").toString(); - String profileImage = properties.get("profile_image").toString(); JSONObject kakao_account = (JSONObject) obj.get("kakao_account"); String email = kakao_account.get("email").toString(); result.put("id", id); - result.put("nickname", nickname); - result.put("image", profileImage); result.put("email", email); br.close(); @@ -157,6 +152,9 @@ public Map getUserInfo(String access_token) throws IOException { return result; } + + + /** * 사용자의 카카오 api 동의 내역을 확인하는 메서드. * kakao developers 공식문서 <- 참고 @@ -186,28 +184,10 @@ public String getAgreementInfo(String access_token) // result는 json 포멧. br.close(); - } catch (MalformedURLException e) { - e.printStackTrace(); - } catch (ProtocolException e) { - e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return result; } - /** - * kakaoId가 db에 있는지 확인해주는 메서드 - * @param kakaoId - * @return db에 존재 -> Member, 없으면 -> null - */ - public Member distinguishKakaoId(Long kakaoId) { - - if (!MemberRepository.findByKakaoId(kakaoId).isPresent()) { - return null; - } - Member kakaoMember = MemberRepository.findByKakaoId(kakaoId).get(); - - return kakaoMember; - } -} +} \ No newline at end of file diff --git a/src/main/java/Remoa/BE/Member/Service/MemberService.java b/src/main/java/Remoa/BE/Member/Service/MemberService.java index d007af6..f629ee1 100644 --- a/src/main/java/Remoa/BE/Member/Service/MemberService.java +++ b/src/main/java/Remoa/BE/Member/Service/MemberService.java @@ -38,13 +38,10 @@ private void validateDuplicateMember(Member member) { } } + public Boolean isNicknameDuplicate(String nickname) { List findMembers = memberRepository.findByNickname(nickname); - if (findMembers.isEmpty()) { - return false; - } else { - return true; - } + return !(findMembers.size()==0); } diff --git a/src/main/java/Remoa/BE/Member/Service/MyFollowingService.java b/src/main/java/Remoa/BE/Member/Service/MyFollowingService.java new file mode 100644 index 0000000..8cba796 --- /dev/null +++ b/src/main/java/Remoa/BE/Member/Service/MyFollowingService.java @@ -0,0 +1,73 @@ +package Remoa.BE.Member.Service; + +import Remoa.BE.Member.Domain.Member; +import Remoa.BE.Member.Dto.Res.ResMypageFollowing; +import Remoa.BE.Member.Dto.Res.ResMypageList; +import Remoa.BE.Member.Repository.MemberRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; + +@Service +@Slf4j +@RequiredArgsConstructor +public class MyFollowingService { + + private final MemberRepository memberRepository; + + public List findResMypageList(Member member, int isFollowing){ + List resMypageLists = new ArrayList<>(); + List memberList; + + if(isFollowing == 1) { // 마이페이지 팔로잉 관리 화면 + memberList = memberRepository.loadFollows(member); + } else{ // 마이페이지 팔로워 관리 화면 + memberList = memberRepository.loadFollowers(member); + } + + for(int i=0; i postInquiry(@Validated ReqNoticeDto reqNoticeDto){ + inquiryService.registerInquiry(reqNoticeDto); + return new ResponseEntity<>(HttpStatus.OK); + } + + @GetMapping("/inquiry") + public ResponseEntity getInquiry(@RequestParam(required = false, defaultValue = "1", name = "page") int pageNumber){ + pageNumber -= 1; + if (pageNumber < 0) { + return errorResponse(CustomMessage.PAGE_NUM_OVER); + } + Page inquiries =inquiryService.getInquiry(pageNumber); + + if ((inquiries.getContent().isEmpty()) && (inquiries.getTotalElements() > 0)) { + return errorResponse(CustomMessage.PAGE_NUM_OVER); + } + + Map responseData = new HashMap<>(); + + List result = new ArrayList<>(); + + for(Inquiry inquiry:inquiries) { + ResNoticeDto resNoticeDto = ResNoticeDto.builder() + .noticeId(inquiry.getInquiryId()) + .title(inquiry.getTitle()) + .postingTime(inquiry.getPostingTime()) + .view(10) + .build(); + result.add(resNoticeDto); + } + + responseData.put("inquiries", result); //조회한 레퍼런스들 + responseData.put("totalPages", inquiries.getTotalPages()); //전체 페이지의 수 + responseData.put("totalOfAllNotices", inquiries.getTotalElements()); //모든 레퍼런스의 수 + responseData.put("totalOfPageElements", inquiries.getNumberOfElements()); //현 페이지의 레퍼런스 수 + + return successResponse(CustomMessage.OK, responseData); + } +} \ No newline at end of file diff --git a/src/main/java/Remoa/BE/Notice/Controller/NoticeController.java b/src/main/java/Remoa/BE/Notice/Controller/NoticeController.java new file mode 100644 index 0000000..19cd1ef --- /dev/null +++ b/src/main/java/Remoa/BE/Notice/Controller/NoticeController.java @@ -0,0 +1,70 @@ +package Remoa.BE.Notice.Controller; + +import Remoa.BE.Notice.Dto.Req.ReqNoticeDto; +import Remoa.BE.Notice.Dto.Res.ResNoticeDto; +import Remoa.BE.Notice.Service.NoticeService; +import Remoa.BE.Notice.domain.Notice; +import Remoa.BE.exception.CustomMessage; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static Remoa.BE.exception.CustomBody.errorResponse; +import static Remoa.BE.exception.CustomBody.successResponse; + +@RestController +@RequiredArgsConstructor +public class NoticeController { + + private final NoticeService noticeService; + + @PostMapping("/notice") + public ResponseEntity postNotice(ReqNoticeDto reqNoticeDto){ + noticeService.registerNotice(reqNoticeDto); + return new ResponseEntity<>(HttpStatus.OK); + } + + @GetMapping("/notice") + public ResponseEntity getNotice(@RequestParam(required = false, defaultValue = "1", name = "page") int pageNumber){ + pageNumber -= 1; + if (pageNumber < 0) { + return errorResponse(CustomMessage.PAGE_NUM_OVER); + } + Page notices =noticeService.getNotice(pageNumber); + + if ((notices.getContent().isEmpty()) && (notices.getTotalElements() > 0)) { + return errorResponse(CustomMessage.PAGE_NUM_OVER); + } + Map responseData = new HashMap<>(); + + List result = new ArrayList<>(); + + for(Notice notice:notices) { + ResNoticeDto resNoticeDto = ResNoticeDto.builder() + .noticeId(notice.getNoticeId()) + .title(notice.getTitle()) + .postingTime(notice.getPostingTime()) + .view(10) + .build(); + result.add(resNoticeDto); + } + + responseData.put("notices", result); //조회한 레퍼런스들 + responseData.put("totalPages", notices.getTotalPages()); //전체 페이지의 수 + responseData.put("totalOfAllNotices", notices.getTotalElements()); //모든 레퍼런스의 수 + responseData.put("totalOfPageElements", notices.getNumberOfElements()); //현 페이지의 레퍼런스 수 + + return successResponse(CustomMessage.OK, responseData); + } + +} diff --git a/src/main/java/Remoa/BE/Notice/Dto/Req/ReqNoticeDto.java b/src/main/java/Remoa/BE/Notice/Dto/Req/ReqNoticeDto.java new file mode 100644 index 0000000..254be51 --- /dev/null +++ b/src/main/java/Remoa/BE/Notice/Dto/Req/ReqNoticeDto.java @@ -0,0 +1,15 @@ +package Remoa.BE.Notice.Dto.Req; + +import lombok.Data; + +import javax.validation.constraints.NotNull; + +@Data +public class ReqNoticeDto { + + @NotNull + private String title; + + @NotNull + private String content; +} diff --git a/src/main/java/Remoa/BE/Notice/Dto/Res/ResNoticeDto.java b/src/main/java/Remoa/BE/Notice/Dto/Res/ResNoticeDto.java new file mode 100644 index 0000000..ead5126 --- /dev/null +++ b/src/main/java/Remoa/BE/Notice/Dto/Res/ResNoticeDto.java @@ -0,0 +1,14 @@ +package Remoa.BE.Notice.Dto.Res; + +import lombok.Builder; + +import java.time.LocalDateTime; + +@Builder +public class ResNoticeDto { + + private Long noticeId; + private String title; + private LocalDateTime postingTime; + private Integer view ; +} diff --git a/src/main/java/Remoa/BE/Notice/Repository/InquiryRepository.java b/src/main/java/Remoa/BE/Notice/Repository/InquiryRepository.java new file mode 100644 index 0000000..786096f --- /dev/null +++ b/src/main/java/Remoa/BE/Notice/Repository/InquiryRepository.java @@ -0,0 +1,12 @@ +package Remoa.BE.Notice.Repository; + +import Remoa.BE.Notice.domain.Inquiry; +import Remoa.BE.Notice.domain.Notice; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface InquiryRepository extends JpaRepository { +} diff --git a/src/main/java/Remoa/BE/Notice/Repository/NoticeRepository.java b/src/main/java/Remoa/BE/Notice/Repository/NoticeRepository.java new file mode 100644 index 0000000..c7a4975 --- /dev/null +++ b/src/main/java/Remoa/BE/Notice/Repository/NoticeRepository.java @@ -0,0 +1,14 @@ +package Remoa.BE.Notice.Repository; + +import Remoa.BE.Member.Domain.Comment; +import Remoa.BE.Notice.domain.Notice; +import Remoa.BE.Post.Domain.Post; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface NoticeRepository extends JpaRepository { + +} diff --git a/src/main/java/Remoa/BE/Notice/Service/InquiryService.java b/src/main/java/Remoa/BE/Notice/Service/InquiryService.java new file mode 100644 index 0000000..07a5365 --- /dev/null +++ b/src/main/java/Remoa/BE/Notice/Service/InquiryService.java @@ -0,0 +1,35 @@ +package Remoa.BE.Notice.Service; + +import Remoa.BE.Notice.Dto.Req.ReqNoticeDto; +import Remoa.BE.Notice.Repository.InquiryRepository; +import Remoa.BE.Notice.domain.Inquiry; +import Remoa.BE.Notice.domain.Notice; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class InquiryService { + + private final InquiryRepository inquiryRepository; + + @Transactional + public void registerInquiry(ReqNoticeDto reqNoticeDto){ + Inquiry inquiry = new Inquiry(); + inquiry.setTitle(reqNoticeDto.getTitle()); + inquiry.setContent(reqNoticeDto.getContent()); + + inquiryRepository.save(inquiry); + } + + public Page getInquiry(int pageNumber){ + int NOTICE_NUMBER = 5; + Pageable pageable = PageRequest.of(pageNumber-1, NOTICE_NUMBER); + return inquiryRepository.findAll(pageable); + } +} diff --git a/src/main/java/Remoa/BE/Notice/Service/NoticeService.java b/src/main/java/Remoa/BE/Notice/Service/NoticeService.java new file mode 100644 index 0000000..e23b4ba --- /dev/null +++ b/src/main/java/Remoa/BE/Notice/Service/NoticeService.java @@ -0,0 +1,34 @@ +package Remoa.BE.Notice.Service; + +import Remoa.BE.Notice.Dto.Req.ReqNoticeDto; +import Remoa.BE.Notice.Repository.NoticeRepository; +import Remoa.BE.Notice.domain.Notice; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class NoticeService { + + private final NoticeRepository noticeRepository; + + @Transactional + public void registerNotice(ReqNoticeDto reqNoticeDto){ + Notice notice = new Notice(); + notice.setTitle(reqNoticeDto.getTitle()); + notice.setContent(reqNoticeDto.getContent()); + + noticeRepository.save(notice); + } + + public Page getNotice(int pageNumber){ + int NOTICE_NUMBER = 5; + Pageable pageable = PageRequest.of(pageNumber-1, NOTICE_NUMBER); + return noticeRepository.findAll(pageable); + } +} diff --git a/src/main/java/Remoa/BE/Notice/domain/Inquiry.java b/src/main/java/Remoa/BE/Notice/domain/Inquiry.java new file mode 100644 index 0000000..43fcad0 --- /dev/null +++ b/src/main/java/Remoa/BE/Notice/domain/Inquiry.java @@ -0,0 +1,28 @@ +package Remoa.BE.Notice.domain; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import java.time.LocalDateTime; + +@Entity +@Getter +@Setter +@NoArgsConstructor +public class Inquiry { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long inquiryId; + + private String title; + + private String content; + + private LocalDateTime postingTime; + +} diff --git a/src/main/java/Remoa/BE/Notice/domain/Notice.java b/src/main/java/Remoa/BE/Notice/domain/Notice.java new file mode 100644 index 0000000..529cd33 --- /dev/null +++ b/src/main/java/Remoa/BE/Notice/domain/Notice.java @@ -0,0 +1,30 @@ +package Remoa.BE.Notice.domain; + +import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import java.time.LocalDateTime; + +@Entity +@Getter +@Setter +@NoArgsConstructor +public class Notice { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long noticeId; + + private String title; + + private String content; + + private LocalDateTime postingTime; + +} diff --git a/src/main/java/Remoa/BE/Post/Controller/CommentController.java b/src/main/java/Remoa/BE/Post/Controller/CommentController.java new file mode 100644 index 0000000..205b7f1 --- /dev/null +++ b/src/main/java/Remoa/BE/Post/Controller/CommentController.java @@ -0,0 +1,85 @@ +package Remoa.BE.Post.Controller; + +import Remoa.BE.Member.Domain.Comment; +import Remoa.BE.Member.Domain.Member; +import Remoa.BE.Member.Service.MemberService; +import Remoa.BE.Post.Service.CommentService; +import Remoa.BE.exception.CustomMessage; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import java.util.Map; + +import static Remoa.BE.exception.CustomBody.errorResponse; +import static Remoa.BE.exception.CustomBody.successResponse; +import static Remoa.BE.utill.MemberInfo.authorized; +import static Remoa.BE.utill.MemberInfo.getMemberId; + +@Slf4j +@RestController +@RequiredArgsConstructor +public class CommentController { + + private final MemberService memberService; + + private final CommentService commentService; + + @PostMapping("/reference/{reference_id}/comment") + public ResponseEntity registerComment(@RequestBody Map comment, @PathVariable("reference_id") Long postId, HttpServletRequest request){ + String myComment = comment.get("comment"); + if(authorized(request)){ + Long memberId = getMemberId(); + Member myMember = memberService.findOne(memberId); + commentService.registerComment(myMember, myComment, postId, null); + return new ResponseEntity<>(HttpStatus.OK); + } + return errorResponse(CustomMessage.UNAUTHORIZED); + } + + @PostMapping("/reference/{reference_id}/comment/{comment_id}") + public ResponseEntity registerCommentReply(@RequestBody Map comment, @PathVariable("reference_id") Long postId, @PathVariable("comment_id") Long commentId, HttpServletRequest request){ + String myComment = comment.get("comment"); + if(authorized(request)){ + Long memberId = getMemberId(); + Member myMember = memberService.findOne(memberId); + commentService.registerComment(myMember, myComment, postId, commentId); + return new ResponseEntity<>(HttpStatus.OK); + } + return errorResponse(CustomMessage.UNAUTHORIZED); + } + @PutMapping("/reference/comment/{comment_id}") + public ResponseEntity modifyComment(@RequestBody Map comment, @PathVariable("comment_id") Long commentId, HttpServletRequest request){ + String myComment = comment.get("comment"); + + if(authorized(request)){ + commentService.modifyComment(myComment, commentId); + return new ResponseEntity<>(HttpStatus.OK); + } + return errorResponse(CustomMessage.UNAUTHORIZED); + } + + @DeleteMapping("/comment/{comment_id}") + public ResponseEntity deleteComment(@PathVariable("comment_id") Long commentId, HttpServletRequest request){ + if(authorized(request)){ + + commentService.deleteComment(commentId); + return new ResponseEntity<>(HttpStatus.OK); + } + return errorResponse(CustomMessage.UNAUTHORIZED); + } + + @PostMapping("/comment/{comment_id}/like") // 댓글 좋아요 + public ResponseEntity likeComment(@PathVariable("comment_id") Long commentId, HttpServletRequest request){ + if(authorized(request)){ + Long memberId = getMemberId(); + Member myMember = memberService.findOne(memberId); + commentService.likeComment(memberId, myMember, commentId); + return new ResponseEntity<>(HttpStatus.OK); + } + return errorResponse(CustomMessage.UNAUTHORIZED); + } +} \ No newline at end of file diff --git a/src/main/java/Remoa/BE/Post/Controller/FeedbackController.java b/src/main/java/Remoa/BE/Post/Controller/FeedbackController.java new file mode 100644 index 0000000..401e36b --- /dev/null +++ b/src/main/java/Remoa/BE/Post/Controller/FeedbackController.java @@ -0,0 +1,90 @@ +package Remoa.BE.Post.Controller; + +import Remoa.BE.Member.Domain.Member; +import Remoa.BE.Member.Service.MemberService; +import Remoa.BE.Post.Service.FeedbackService; +import Remoa.BE.exception.CustomMessage; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import java.util.Map; + +import static Remoa.BE.exception.CustomBody.errorResponse; +import static Remoa.BE.utill.MemberInfo.authorized; +import static Remoa.BE.utill.MemberInfo.getMemberId; + +@Slf4j +@RestController +@RequiredArgsConstructor +public class FeedbackController { + + private final FeedbackService feedbackService; + private final MemberService memberService; + + @PostMapping("/reference/{reference_id}/{page_number}") // 레퍼런스에 피드백 등록 + public ResponseEntity registerFeedback(@RequestBody Map feedback, + @PathVariable("reference_id") Long postId, + @PathVariable("page_number") Integer pageNumber, + HttpServletRequest request){ + String myFeedback = feedback.get("feedback"); + if(authorized(request)){ + Long memberId = getMemberId(); + Member myMember = memberService.findOne(memberId); + feedbackService.registerFeedback(myMember, myFeedback, postId, pageNumber, null); + return new ResponseEntity<>(HttpStatus.OK); + } + return errorResponse(CustomMessage.UNAUTHORIZED); + } + + @PostMapping("/reference/{reference_id}/feedback/{feedback_id}") // 레퍼런스에 피드백 대댓글 등록 + public ResponseEntity registerFeedbackReply(@RequestBody Map feedback, + @PathVariable("reference_id") Long postId, + @PathVariable("feedback_id") Long feedbackId, + HttpServletRequest request){ + String myFeedback = feedback.get("feedback"); + if(authorized(request)){ + Long memberId = getMemberId(); + Member myMember = memberService.findOne(memberId); + feedbackService.registerFeedback(myMember, myFeedback, postId, null, feedbackId); + return new ResponseEntity<>(HttpStatus.OK); + } + return errorResponse(CustomMessage.UNAUTHORIZED); + } + + @PutMapping("/reference/feedback/{feedback_id}") // 피드백 수정 + public ResponseEntity modifyFeedback(@RequestBody Map feedback, @PathVariable("feedback_id") Long feedbackId, HttpServletRequest request){ + String myFeedback = feedback.get("feedback"); + + if(authorized(request)){ + feedbackService.modifyFeedback(myFeedback, feedbackId); + return new ResponseEntity<>(HttpStatus.OK); + } + return errorResponse(CustomMessage.UNAUTHORIZED); + } + + @DeleteMapping("/reference/feedback/{feedback_id}") + public ResponseEntity deleteFeedback(@PathVariable("feedback_id") Long feedbackId, HttpServletRequest request){ + if(authorized(request)){ + + feedbackService.deleteFeedback(feedbackId); + return new ResponseEntity<>(HttpStatus.OK); + } + return errorResponse(CustomMessage.UNAUTHORIZED); + } + + @PostMapping("/reference/feedback/{feedback_id}/like") // 피드백 좋아요 + public ResponseEntity likeFeedback(@PathVariable("feedback_id") Long feedbackId, HttpServletRequest request){ + if(authorized(request)){ + Long memberId = getMemberId(); + Member myMember = memberService.findOne(memberId); + feedbackService.likeFeedback(memberId, myMember, feedbackId); + return new ResponseEntity<>(HttpStatus.OK); + } + return errorResponse(CustomMessage.UNAUTHORIZED); + + } +} \ No newline at end of file diff --git a/src/main/java/Remoa/BE/Post/Controller/MyActivityController.java b/src/main/java/Remoa/BE/Post/Controller/MyActivityController.java new file mode 100644 index 0000000..ad71d12 --- /dev/null +++ b/src/main/java/Remoa/BE/Post/Controller/MyActivityController.java @@ -0,0 +1,128 @@ +package Remoa.BE.Post.Controller; + +import Remoa.BE.Member.Domain.CommentFeedback; +import Remoa.BE.Member.Domain.Member; +import Remoa.BE.Member.Dto.Res.ResMemberInfoDto; +import Remoa.BE.Member.Service.MemberService; +import Remoa.BE.Post.Domain.PostScarp; +import Remoa.BE.Post.Dto.Response.ResCommentFeedbackDto; +import Remoa.BE.Post.Dto.Response.ResPostDto; +import Remoa.BE.Post.Service.CommentFeedbackService; +import Remoa.BE.Post.Service.PostService; +import Remoa.BE.exception.CustomMessage; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static Remoa.BE.Member.Domain.ContentType.COMMENT; +import static Remoa.BE.Member.Domain.ContentType.FEEDBACK; +import static Remoa.BE.exception.CustomBody.errorResponse; +import static Remoa.BE.exception.CustomBody.successResponse; +import static Remoa.BE.utill.MemberInfo.authorized; +import static Remoa.BE.utill.MemberInfo.getMemberId; + +@RestController +@Slf4j +@RequiredArgsConstructor +public class MyActivityController { + + private final MemberService memberService; + private final CommentFeedbackService commentFeedbackService; + private final PostService postService; + + /** + * 내 활동 관리 + * @param request + * @param commentSize + * @param scrapSize + * @return Map + * "contents" : 내가 작성한 최신 댓글(Comment, Feedback 무관)들의 List. + * "posts" : 내가 스크랩한 post들을 가장 최근 스크랩한 순서의 List. + * 주의사항! : page는 고정해두고 size를 이용하므로 누적 데이터가 return 됨. + */ + @GetMapping("/user/activity") + public ResponseEntity myActivity(HttpServletRequest request, + @RequestParam(name = "comment", defaultValue = "1", required = false) int commentSize, + @RequestParam(name = "scrap", defaultValue = "1", required = false) int scrapSize) { + + if (authorized(request)) { + Long memberId = getMemberId(); + Member myMember = memberService.findOne(memberId); + + Map result = new HashMap<>(); + + Page commentOrFeedback = commentFeedbackService.findNewestCommentOrFeedback(myMember, commentSize); + + /** + * 조회한 가장 최근에 작성한 댓글들을 dto로 mapping + */ + List contents = commentOrFeedback.stream().map(commentFeedback -> { + ResCommentFeedbackDto map = null; + if (commentFeedback.getType().equals(FEEDBACK)) { + map = ResCommentFeedbackDto.builder() + .title(commentFeedback.getPost().getTitle()) + .postId(commentFeedback.getPost().getPostId()) + .thumbnail(commentFeedback.getPost().getThumbnail().getStoreFileUrl()) + .member(new ResMemberInfoDto(commentFeedback.getMember().getMemberId(), + commentFeedback.getMember().getNickname(), + commentFeedback.getMember().getProfileImage())) + .content(commentFeedback.getFeedback().getFeedback()) + .likeCount(commentFeedback.getFeedback().getFeedbackLikeCount()).build(); + } else if (commentFeedback.getType().equals(COMMENT)) { + map = ResCommentFeedbackDto.builder() + .title(commentFeedback.getPost().getTitle()) + .postId(commentFeedback.getPost().getPostId()) + .thumbnail(commentFeedback.getPost().getThumbnail().getStoreFileUrl()) + .member(new ResMemberInfoDto(commentFeedback.getMember().getMemberId(), + commentFeedback.getMember().getNickname(), + commentFeedback.getMember().getProfileImage())) + .content(commentFeedback.getComment().getComment()) + .likeCount(commentFeedback.getComment().getCommentLikeCount()).build(); + } + return map; + }).collect(Collectors.toList()); + + result.put("contents", contents); + + scrapSize *= 12; //스크랩한 post는 12개씩 보여주므로. + + /** + * 조회한 최근에 스크랩한 12개의 post들을 dto로 mapping. + */ + List posts = postService.findScrapedPost(scrapSize, myMember) + .stream() + .map(PostScarp::getPost) + .collect(Collectors.toList()) + .stream() + .map(post -> ResPostDto.builder() + .postId(post.getPostId()) + .postMember(new ResMemberInfoDto(post.getMember().getMemberId(), + post.getMember().getNickname(), + post.getMember().getProfileImage())) + .thumbnail(post.getThumbnail().getStoreFileUrl()) + .title(post.getTitle()) + .likeCount(post.getLikeCount()) + .postingTime(post.getPostingTime().toString()) + .views(post.getViews()) + .scrapCount(post.getScrapCount()) + .categoryName(post.getCategory().getName()).build()) + .collect(Collectors.toList()); + + result.put("posts", posts); + + return successResponse(CustomMessage.OK, result); + } + return errorResponse(CustomMessage.UNAUTHORIZED); + } +} diff --git a/src/main/java/Remoa/BE/Post/Controller/MyFeedbackController.java b/src/main/java/Remoa/BE/Post/Controller/MyFeedbackController.java new file mode 100644 index 0000000..1e6cc60 --- /dev/null +++ b/src/main/java/Remoa/BE/Post/Controller/MyFeedbackController.java @@ -0,0 +1,132 @@ +package Remoa.BE.Post.Controller; + +import Remoa.BE.Member.Domain.Comment; +import Remoa.BE.Member.Domain.Member; +import Remoa.BE.Member.Dto.Res.ResMemberInfoDto; +import Remoa.BE.Member.Service.MemberService; +import Remoa.BE.Post.Domain.Post; +import Remoa.BE.Post.Dto.Response.ResCommentDto; +import Remoa.BE.Post.Dto.Response.ResReceivedCommentDto; +import Remoa.BE.Post.Dto.Response.ResReplyDto; +import Remoa.BE.Post.Service.CommentService; +import Remoa.BE.Post.Service.MyPostService; +import Remoa.BE.exception.CustomMessage; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static Remoa.BE.exception.CustomBody.errorResponse; +import static Remoa.BE.exception.CustomBody.successResponse; +import static Remoa.BE.utill.MemberInfo.authorized; +import static Remoa.BE.utill.MemberInfo.getMemberId; + +@RestController +@RequiredArgsConstructor +@Slf4j +public class MyFeedbackController { + + private final MemberService memberService; + private final MyPostService myPostService; + private final CommentService commentService; + + @GetMapping("/user/feedback") + public ResponseEntity receivedFeedback(HttpServletRequest request, + @RequestParam(required = false, defaultValue = "all") String category, + @RequestParam(required = false, defaultValue = "1", name = "page") int pageNumber) { + + if (authorized(request)) { + Long memberId = getMemberId(); + Member myMember = memberService.findOne(memberId); + + pageNumber -= 1; + if (pageNumber < 0) { + return errorResponse(CustomMessage.PAGE_NUM_OVER); + } + + Page posts; + if (category.equals("idea") || + category.equals("marketing") || + category.equals("design") || + category.equals("video") || + category.equals("etc")) { + + posts = myPostService.getNewestThreePostsSortCategory(pageNumber, myMember, category); + + } else { + posts = myPostService.getNewestThreePosts(pageNumber, myMember); + } + + if ((posts.getContent().isEmpty()) && (posts.getTotalElements() > 0)) { + return errorResponse(CustomMessage.PAGE_NUM_OVER); + } + + Map result = new HashMap<>(); + + int postNumber = 1; + for (Post post : posts) { //조회한 post + Map commentInfo = new HashMap<>(); + + List parentComments = commentService.getRecentThreeCommentsExceptReply(post); + + int commentNumber = 1; + for (Comment parentComment : parentComments) { //조회한 post의 parent comment + List parentCommentsReply = commentService.getParentCommentsReply(parentComment); + + + + List replies = new ArrayList<>(); + for (Comment reply : parentCommentsReply) { + + replies.add(new ResReplyDto( + reply.getCommentId(), + reply.getMember().getMemberId(), + reply.getMember().getNickname(), + reply.getMember().getProfileImage(), + reply.getComment(), + reply.getCommentLikeCount())); + } + + commentInfo.put("comment_" + commentNumber, new ResCommentDto( + parentComment.getCommentId(), + parentComment.getMember().getMemberId(), + parentComment.getMember().getNickname(), + parentComment.getMember().getProfileImage(), + parentComment.getComment(), + parentComment.getCommentLikeCount(), + replies)); + + commentNumber++; + } + + ResReceivedCommentDto map = ResReceivedCommentDto.builder() + .title(post.getTitle()) + .postId(post.getPostId()) + .commentInfo(commentInfo) + .build(); + + result.put("post_" + postNumber, map); + + postNumber++; + } + result.put("totalPages", posts.getTotalPages()); //전체 페이지의 수 + result.put("totalOfAllComments", posts.getTotalElements()); //모든 코멘트의 수 + result.put("totalOfPageElements", posts.getNumberOfElements()); //현 페이지 피드백의 수 + + return successResponse(CustomMessage.OK, result); + + } + return errorResponse(CustomMessage.UNAUTHORIZED); + } + +} diff --git a/src/main/java/Remoa/BE/Post/Controller/MyPostController.java b/src/main/java/Remoa/BE/Post/Controller/MyPostController.java new file mode 100644 index 0000000..7daf9c7 --- /dev/null +++ b/src/main/java/Remoa/BE/Post/Controller/MyPostController.java @@ -0,0 +1,110 @@ +package Remoa.BE.Post.Controller; + +import Remoa.BE.Member.Domain.Member; +import Remoa.BE.Member.Dto.Res.ResMemberInfoDto; +import Remoa.BE.Member.Service.MemberService; +import Remoa.BE.Post.Domain.Post; +import Remoa.BE.Post.Dto.Response.ResPostDto; +import Remoa.BE.Post.Service.CommentService; +import Remoa.BE.Post.Service.MyPostService; +import Remoa.BE.exception.CustomMessage; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.modelmapper.ModelMapper; +import org.springframework.data.domain.Page; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static Remoa.BE.exception.CustomBody.errorResponse; +import static Remoa.BE.exception.CustomBody.successResponse; +import static Remoa.BE.utill.MemberInfo.authorized; +import static Remoa.BE.utill.MemberInfo.getMemberId; + +@Slf4j +@RestController +@RequiredArgsConstructor +public class MyPostController { + + private final MemberService memberService; + private final MyPostService myPostService; + + // Entity <-> DTO 간의 변환을 편리하게 하고자 ModelMapper 사용.(build.gradle에 의존성 주입 완료) +// private final ModelMapper modelMapper = new ModelMapper(); + + + /** + * 내 작업물 목록 페이지 + */ + @GetMapping("/user/reference") + public ResponseEntity myReference(HttpServletRequest request, + @RequestParam(required = false, defaultValue = "all") String category, + @RequestParam(required = false, defaultValue = "1", name = "page") int pageNumber, + @RequestParam(required = false, defaultValue = "newest") String sort, + @RequestParam(required = false, defaultValue = "") String title) { + if (authorized(request)) { + Long memberId = getMemberId(); + Member myMember = memberService.findOne(memberId); + + pageNumber -= 1; + if (pageNumber < 0) { + return errorResponse(CustomMessage.PAGE_NUM_OVER); + } + + Page posts; + + if (category.equals("idea") || + category.equals("marketing") || + category.equals("design") || + category.equals("video") || + category.equals("etc")) { + posts = myPostService.sortAndPaginatePostsByCategoryAndMember(category, pageNumber, sort, myMember,title); + } else { + posts = myPostService.sortAndPaginatePostsByMember(pageNumber, sort, myMember,title); + } + + //조회할 레퍼런스가 db에 있으나, 현재 페이지에 조회할 데이터가 없는 경우 == 페이지 번호를 잘못 입력 + if ((posts.getContent().isEmpty()) && (posts.getTotalElements() > 0)) { + return errorResponse(CustomMessage.PAGE_NUM_OVER); + } + + List result = new ArrayList<>(); + + for (Post post : posts) { + ResPostDto map = ResPostDto.builder() + .postingTime(post.getPostingTime().toString()) + .postMember(new ResMemberInfoDto(post.getMember().getMemberId(), + post.getMember().getNickname(), + post.getMember().getProfileImage())) + .postId(post.getPostId()) + .views(post.getViews()) + .categoryName(post.getCategory().getName()) + .likeCount(post.getLikeCount()) + .thumbnail(post.getThumbnail().getStoreFileUrl()) + .scrapCount(post.getScrapCount()) + .title(post.getTitle()).build(); + result.add(map); + } + //프론트에서 쓰일 조회한 레퍼런스들과 페이지 관련한 값들 map에 담아서 return. + Map referencesAndPageInfo = new HashMap<>(); + referencesAndPageInfo.put("references", result); //조회한 레퍼런스들 + referencesAndPageInfo.put("totalPages", posts.getTotalPages()); //전체 페이지의 수 + referencesAndPageInfo.put("totalOfAllReferences", posts.getTotalElements()); //모든 레퍼런스의 수 + referencesAndPageInfo.put("totalOfPageElements", posts.getNumberOfElements()); //현 페이지의 레퍼런스 수 + + return successResponse(CustomMessage.OK, referencesAndPageInfo); + } + return errorResponse(CustomMessage.UNAUTHORIZED); + } + + + + +} diff --git a/src/main/java/Remoa/BE/Post/Controller/PostController.java b/src/main/java/Remoa/BE/Post/Controller/PostController.java index 8d81335..266e0a5 100644 --- a/src/main/java/Remoa/BE/Post/Controller/PostController.java +++ b/src/main/java/Remoa/BE/Post/Controller/PostController.java @@ -1,23 +1,30 @@ package Remoa.BE.Post.Controller; import Remoa.BE.Member.Domain.Member; +import Remoa.BE.Member.Dto.Res.ResMemberInfoDto; import Remoa.BE.Member.Service.MemberService; import Remoa.BE.Post.Domain.Post; -import Remoa.BE.Post.Service.FileService; +import Remoa.BE.Post.Domain.UploadFile; +import Remoa.BE.Post.Dto.Request.UploadPostForm; +import Remoa.BE.Post.Dto.Response.ResHomeReferenceDto; +import Remoa.BE.Post.Dto.Response.ResReferenceRegisterDto; import Remoa.BE.Post.Service.PostService; -import Remoa.BE.Post.form.Request.UploadPostForm; -import Remoa.BE.exception.CustomBody; import Remoa.BE.exception.CustomMessage; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; import static Remoa.BE.exception.CustomBody.errorResponse; import static Remoa.BE.exception.CustomBody.successResponse; @@ -27,51 +34,113 @@ @Slf4j @RestController @RequiredArgsConstructor -@CrossOrigin(origins = "*") public class PostController { - private final FileService fileService; - private final PostService postService; - private final MemberService memberService; +// private final ModelMapper modelMapper; - //Todo 게시글 작성중 파일 업로드만 작성 - @PostMapping("/post") - public void posting(Post post, @RequestParam("files") List multipartFile){ - fileService.saveUploadFiles(post,multipartFile); - } + @GetMapping("/reference") + public ResponseEntity searchPost(@RequestParam(required = false, defaultValue = "all") String category, + @RequestParam(required = false, defaultValue = "newest") String sort, + @RequestParam(required = false, defaultValue = "1", name = "page") int pageNumber, + @RequestParam(required = false, defaultValue = "") String title) { - /** - * @param fileId file PK - * @return file이 저장된 url 반환 - */ - @GetMapping("/post/file/{fileId}/url") - public String getFileUrl(@PathVariable("fileId") Long fileId ){ - return fileService.getUrl(fileId); - } + Map responseData = new HashMap<>(); + + pageNumber -= 1; + if (pageNumber < 0) { + return errorResponse(CustomMessage.PAGE_NUM_OVER); + } + + Page allPosts; + if (category.equals("idea") || + category.equals("marketing") || + category.equals("design") || + category.equals("video") || + category.equals("etc")) { + //sort -> 최신순 : newest, 좋아요순 : like, 스크랩순 : scrap, 조회순 : view + allPosts = postService.sortAndPaginatePostsByCategory(category, sort, pageNumber,title); + } else { + //sort -> 최신순 : newest, 좋아요순 : like, 스크랩순 : scrap, 조회순 : view + allPosts = postService.sortAndPaginatePosts(sort, pageNumber,title); + } - /** - * @param fileId file PK - * @return file을 바로 다운로드 할 수 있다 - */ - @GetMapping("/post/file/{fileId}") - public ResponseEntity getFileDownload(@PathVariable("fileId") Long fileId ) throws IOException { - return fileService.getObject(fileId); + if ((allPosts.getContent().isEmpty()) && (allPosts.getTotalElements() > 0)) { + return errorResponse(CustomMessage.PAGE_NUM_OVER); + } + + List result = new ArrayList<>(); + + for (Post post : allPosts) { + ResHomeReferenceDto map = ResHomeReferenceDto.builder() + .postThumbnail(post.getThumbnail().getStoreFileUrl()) + .postId(post.getPostId()) + .title(post.getTitle()) + .views(post.getViews()) + .likeCount(post.getLikeCount()) + .scrapCount(post.getScrapCount()) + .postMember(new ResMemberInfoDto(post.getMember().getMemberId(), + post.getMember().getNickname(), + post.getMember().getProfileImage())) + .build(); + + result.add(map); + } + + responseData.put("references", result); //조회한 레퍼런스들 + responseData.put("totalPages", allPosts.getTotalPages()); //전체 페이지의 수 + responseData.put("totalOfAllReferences", allPosts.getTotalElements()); //모든 레퍼런스의 수 + responseData.put("totalOfPageElements", allPosts.getNumberOfElements()); //현 페이지의 레퍼런스 수 + + return successResponse(CustomMessage.OK, responseData); } + @PostMapping("/reference") // 게시물 등록 - public ResponseEntity share(@RequestPart UploadPostForm uploadPostForm, - @RequestPart List uploadFiles, HttpServletRequest request){ - //TODO postingTime 설정. 로그인 여부 거르는 건 Spring Security 설정으로 가능해서 우선 없어도 괜찮을듯함. + public ResponseEntity share(@RequestPart("data") UploadPostForm uploadPostForm, + @RequestPart("thumbnail")MultipartFile thumbnail, + @RequestPart("file") List uploadFiles, HttpServletRequest request) throws IOException { if(authorized(request)){ Long memberId = getMemberId(); - Member myMember = memberService.findOne(memberId); - postService.registPost(uploadPostForm,uploadFiles,myMember); - return successResponse(CustomMessage.OK,myMember); + + Post savePost = postService.registerPost(uploadPostForm,thumbnail,uploadFiles,memberId); + + //잘못된 파일 유형 + if(savePost == null){ + return errorResponse(CustomMessage.BAD_FILE); + } + + Post post = postService.findOne(savePost.getPostId()); + + ResReferenceRegisterDto resReferenceRegisterDto = ResReferenceRegisterDto.builder() + .postId(post.getPostId()) + .title(post.getTitle()) + .category(post.getCategory().getName()) + .contestAwardType(post.getContestAwardType()) + .contestName(post.getContestName()) + .youtubeLink(post.getYoutubeLink()) + .pageCount(post.getPageCount()) + .fileNames(post.getUploadFiles().stream().map(UploadFile::getOriginalFileName).collect(Collectors.toList())) + .build(); + return successResponse(CustomMessage.OK, resReferenceRegisterDto); + + } return errorResponse(CustomMessage.UNAUTHORIZED); } -} + @PostMapping("/reference/scrap/{reference_id}") + public ResponseEntity scrapReference(@PathVariable("reference_id") Long referenceId, HttpServletRequest request){ + if(authorized(request)){ + Long memberId = getMemberId(); + Member myMember = memberService.findOne(memberId); + postService.scrapPost(memberId, myMember, referenceId); + + + return new ResponseEntity<>(HttpStatus.OK); + } + return errorResponse(CustomMessage.UNAUTHORIZED); + } +} \ No newline at end of file diff --git a/src/main/java/Remoa/BE/Post/Controller/ViewerController.java b/src/main/java/Remoa/BE/Post/Controller/ViewerController.java new file mode 100644 index 0000000..fa8c587 --- /dev/null +++ b/src/main/java/Remoa/BE/Post/Controller/ViewerController.java @@ -0,0 +1,48 @@ +package Remoa.BE.Post.Controller; + +import Remoa.BE.Member.Dto.Res.ResMemberInfoDto; +import Remoa.BE.Post.Domain.Post; +import Remoa.BE.Post.Dto.Response.ResReferenceViewerDto; +import Remoa.BE.Post.Service.ViewerService; +import Remoa.BE.exception.CustomMessage; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; + +import static Remoa.BE.exception.CustomBody.successResponse; + +@Slf4j +@RestController +@RequiredArgsConstructor +public class ViewerController { + + private final ViewerService viewerService; + + @GetMapping("reference/{reference_id}") + public ResponseEntity referenceViewer(HttpServletRequest request, + @PathVariable("reference_id") Long referenceId) { + + Post post = viewerService.getPost(referenceId); + + ResReferenceViewerDto result = ResReferenceViewerDto.builder() + .postingTime(post.getPostingTime().toString()) + .postMember(new ResMemberInfoDto(post.getMember().getMemberId(), + post.getMember().getNickname(), + post.getMember().getProfileImage())) + .postId(post.getPostId()) + .views(post.getViews()) + .category(post.getCategory().getName()) + .likeCount(post.getLikeCount()) + .thumbnail(post.getThumbnail().getStoreFileUrl()) + .scrapCount(post.getScrapCount()) + .title(post.getTitle()) + .build(); + + return successResponse(CustomMessage.OK, result); + } +} \ No newline at end of file diff --git a/src/main/java/Remoa/BE/Post/Domain/Post.java b/src/main/java/Remoa/BE/Post/Domain/Post.java index e9753b4..2d0f2e6 100644 --- a/src/main/java/Remoa/BE/Post/Domain/Post.java +++ b/src/main/java/Remoa/BE/Post/Domain/Post.java @@ -1,11 +1,14 @@ package Remoa.BE.Post.Domain; import Remoa.BE.Member.Domain.Comment; +import Remoa.BE.Member.Domain.CommentFeedback; +import Remoa.BE.Member.Domain.Feedback; import Remoa.BE.Member.Domain.Member; import lombok.*; import org.hibernate.annotations.Where; import javax.persistence.*; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; @@ -21,8 +24,7 @@ public class Post { @Id - @GeneratedValue - @Column(name = "post_id") + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long postId; /** @@ -37,6 +39,9 @@ public class Post { */ private String title; + @OneToOne + private UploadFile thumbnail; + /** * 참여 공모전의 이름 */ @@ -44,21 +49,21 @@ public class Post { private String contestName; /** - * 참여한 공모전의 마감 기한 + * 유튜브 링크 */ - private String deadline; + private String youtubeLink; /** - * 참여한 공모전의 수상 내역 + * 참여한 공모전의 마감 기한 */ - @Column(name = "contest_award") - private Boolean ContestAward; + private String deadline; + /** * pm쪽에 문의해야할듯. */ @Column(name = "contest_aware_type") - private String contestAwareType; + private String contestAwardType; /** * Post에 대한 좋아요 수 @@ -70,24 +75,34 @@ public class Post { * Post가 작성된 시간 */ @Column(name = "posting_time") - private String postingTime; + private LocalDateTime postingTime; /** * Post의 조회수 */ private Integer views = 0; + private Integer scrapCount = 0; + + private Integer pageCount; + /** * Post에 작성되어진 Comment */ @OneToMany(mappedBy = "post") private List comments = new ArrayList<>(); + @OneToMany(mappedBy = "post") + private List feedbacks = new ArrayList<>(); + + @OneToMany(mappedBy = "post") + private List commentFeedbacks = new ArrayList<>(); + /** * Post에서 쓰인 files */ @OneToMany(mappedBy = "post") - private List uploadFiles = new ArrayList<>(); + private List uploadFiles; @OneToMany(mappedBy = "post", cascade = CascadeType.ALL) private List postScarps = new ArrayList<>(); diff --git a/src/main/java/Remoa/BE/Post/Domain/PostLike.java b/src/main/java/Remoa/BE/Post/Domain/PostLike.java index d2d112e..b7ee5dd 100644 --- a/src/main/java/Remoa/BE/Post/Domain/PostLike.java +++ b/src/main/java/Remoa/BE/Post/Domain/PostLike.java @@ -5,7 +5,6 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import org.hibernate.annotations.SQLDelete; import org.hibernate.annotations.Where; import javax.persistence.*; diff --git a/src/main/java/Remoa/BE/Post/Domain/PostScarp.java b/src/main/java/Remoa/BE/Post/Domain/PostScarp.java index 49fc0ac..c5d4f11 100644 --- a/src/main/java/Remoa/BE/Post/Domain/PostScarp.java +++ b/src/main/java/Remoa/BE/Post/Domain/PostScarp.java @@ -5,10 +5,10 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import org.hibernate.annotations.SQLDelete; import org.hibernate.annotations.Where; import javax.persistence.*; +import java.time.LocalDateTime; @Getter @Setter @@ -30,11 +30,15 @@ public class PostScarp { @JoinColumn(name = "post_id") private Post post; + @Column(name = "scrap_time") + private LocalDateTime scrapTime; + private Boolean deleted = Boolean.FALSE; public static PostScarp createPostScrap(Member member, Post post) { PostScarp postScrap = new PostScarp(); postScrap.setPost(post); + postScrap.setScrapTime(LocalDateTime.now()); postScrap.setMember(member); return postScrap; diff --git a/src/main/java/Remoa/BE/Post/Domain/UploadFile.java b/src/main/java/Remoa/BE/Post/Domain/UploadFile.java index eeb155f..6337ff5 100644 --- a/src/main/java/Remoa/BE/Post/Domain/UploadFile.java +++ b/src/main/java/Remoa/BE/Post/Domain/UploadFile.java @@ -2,7 +2,6 @@ import lombok.Getter; import lombok.Setter; -import org.hibernate.annotations.SQLDelete; import org.hibernate.annotations.Where; import javax.persistence.*; @@ -16,12 +15,8 @@ public class UploadFile { @Id @GeneratedValue - @Column(name = "file_id") private Long uploadFileId; - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "post_id") - private Post post; /** * 업로드된 파일의 원본 이름 @@ -48,6 +43,10 @@ public class UploadFile { @Column(name = "store_file_url") private String storeFileUrl; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "post_id") + private Post post; + private Boolean deleted = Boolean.FALSE; //이후 업로드 날짜 및 시간, 컨텐츠 타입, 사이즈 등의 필드등이 필요할 때 손봐야할듯. diff --git a/src/main/java/Remoa/BE/Post/Dto/Request/UploadPostForm.java b/src/main/java/Remoa/BE/Post/Dto/Request/UploadPostForm.java new file mode 100644 index 0000000..3067840 --- /dev/null +++ b/src/main/java/Remoa/BE/Post/Dto/Request/UploadPostForm.java @@ -0,0 +1,21 @@ +package Remoa.BE.Post.Dto.Request; + +import lombok.*; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Builder +public class UploadPostForm { + + private String title; // Post name + + private String contestName; + + private String contestAwardType; + + private String category; // Category name + + private String youtubeLink; // 유튜브 링크 +} diff --git a/src/main/java/Remoa/BE/Post/Dto/Response/ResCommentDto.java b/src/main/java/Remoa/BE/Post/Dto/Response/ResCommentDto.java new file mode 100644 index 0000000..6567908 --- /dev/null +++ b/src/main/java/Remoa/BE/Post/Dto/Response/ResCommentDto.java @@ -0,0 +1,20 @@ +package Remoa.BE.Post.Dto.Response; + +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.util.List; + +@Data +@AllArgsConstructor +public class ResCommentDto { + + private Long commentId; + private Long memberId; + private String nickname; + private String profileImage; + private String comment; + private Integer likeCount; + private List replies; + +} \ No newline at end of file diff --git a/src/main/java/Remoa/BE/Post/Dto/Response/ResCommentFeedbackDto.java b/src/main/java/Remoa/BE/Post/Dto/Response/ResCommentFeedbackDto.java new file mode 100644 index 0000000..1a7d0fe --- /dev/null +++ b/src/main/java/Remoa/BE/Post/Dto/Response/ResCommentFeedbackDto.java @@ -0,0 +1,21 @@ +package Remoa.BE.Post.Dto.Response; + +import Remoa.BE.Member.Dto.Res.ResMemberInfoDto; +import lombok.Builder; +import lombok.Data; + +/** + * 내 활동 관리에 쓰이는 Comment와 Feedback을 구분 없이 최신순으로 볼러오는 데 쓰이는 dto. + */ +@Data +@Builder +public class ResCommentFeedbackDto { + + private String title; + private Long postId; + private String thumbnail; + private ResMemberInfoDto member; + private String content; + private Integer likeCount; + +} diff --git a/src/main/java/Remoa/BE/Post/Dto/Response/ResHomeReferenceDto.java b/src/main/java/Remoa/BE/Post/Dto/Response/ResHomeReferenceDto.java new file mode 100644 index 0000000..96345b1 --- /dev/null +++ b/src/main/java/Remoa/BE/Post/Dto/Response/ResHomeReferenceDto.java @@ -0,0 +1,18 @@ +package Remoa.BE.Post.Dto.Response; + +import Remoa.BE.Member.Dto.Res.ResMemberInfoDto; +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +public class ResHomeReferenceDto { + + private String postThumbnail; + private Long postId; + private String title; + private int views; + private int likeCount; + private int scrapCount; + private ResMemberInfoDto postMember; +} diff --git a/src/main/java/Remoa/BE/Post/Dto/Response/ResPostDto.java b/src/main/java/Remoa/BE/Post/Dto/Response/ResPostDto.java new file mode 100644 index 0000000..1b02c5b --- /dev/null +++ b/src/main/java/Remoa/BE/Post/Dto/Response/ResPostDto.java @@ -0,0 +1,24 @@ +package Remoa.BE.Post.Dto.Response; + +import Remoa.BE.Member.Dto.Res.ResMemberInfoDto; +import lombok.*; + +/** + * 작업물 목록을 보여줄 때 쓰일 Post의 간단한 정보만을 담은 Dto. + */ +@Setter +@Getter +@Builder +public class ResPostDto { + + public Long postId; + public ResMemberInfoDto postMember; + public String thumbnail; + public String title; + public Integer likeCount; + public String postingTime; + public Integer views; + public Integer scrapCount; + public String categoryName; + +} diff --git a/src/main/java/Remoa/BE/Post/Dto/Response/ResReceivedCommentDto.java b/src/main/java/Remoa/BE/Post/Dto/Response/ResReceivedCommentDto.java new file mode 100644 index 0000000..674c122 --- /dev/null +++ b/src/main/java/Remoa/BE/Post/Dto/Response/ResReceivedCommentDto.java @@ -0,0 +1,18 @@ +package Remoa.BE.Post.Dto.Response; + +import Remoa.BE.Member.Dto.Res.ResMemberInfoDto; +import lombok.Builder; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Data +@Builder +public class ResReceivedCommentDto { + + private Long postId; + private Map commentInfo; + private String title; + +} diff --git a/src/main/java/Remoa/BE/Post/Dto/Response/ResReferenceRegisterDto.java b/src/main/java/Remoa/BE/Post/Dto/Response/ResReferenceRegisterDto.java new file mode 100644 index 0000000..0419c41 --- /dev/null +++ b/src/main/java/Remoa/BE/Post/Dto/Response/ResReferenceRegisterDto.java @@ -0,0 +1,29 @@ +package Remoa.BE.Post.Dto.Response; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Builder +@Getter +@Setter +public class ResReferenceRegisterDto { + + private Long postId; + + private String title; // Post name + + private String contestName; + + private String category; // Category name + + private String contestAwardType; + + private String youtubeLink; + + private Integer pageCount; + + private List fileNames; +} diff --git a/src/main/java/Remoa/BE/Post/Dto/Response/ResReferenceViewerDto.java b/src/main/java/Remoa/BE/Post/Dto/Response/ResReferenceViewerDto.java new file mode 100644 index 0000000..0c7eeb3 --- /dev/null +++ b/src/main/java/Remoa/BE/Post/Dto/Response/ResReferenceViewerDto.java @@ -0,0 +1,28 @@ +package Remoa.BE.Post.Dto.Response; + +import Remoa.BE.Member.Dto.Res.ResMemberInfoDto; +import lombok.Builder; +import lombok.Data; + +import java.util.List; + +@Data +@Builder +public class ResReferenceViewerDto { + + // 임시 + public Long postId; + public ResMemberInfoDto postMember; + public String thumbnail; + private String contestName; + private String contestAwardType; + private String category; + public String title; + public Integer likeCount; + public Integer scrapCount; + public String postingTime; + public Integer views; + private Integer pageCount; + private List fileNames; + +} diff --git a/src/main/java/Remoa/BE/Post/Dto/Response/ResReplyDto.java b/src/main/java/Remoa/BE/Post/Dto/Response/ResReplyDto.java new file mode 100644 index 0000000..ce35568 --- /dev/null +++ b/src/main/java/Remoa/BE/Post/Dto/Response/ResReplyDto.java @@ -0,0 +1,18 @@ +package Remoa.BE.Post.Dto.Response; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; + +@Data +@AllArgsConstructor +@Builder +public class ResReplyDto { + + private Long commentId; + private Long memberId; + private String nickname; + private String profileImage; + private String comment; + private Integer likeCount; +} diff --git a/src/main/java/Remoa/BE/Post/Repository/CommentFeedbackRepository.java b/src/main/java/Remoa/BE/Post/Repository/CommentFeedbackRepository.java new file mode 100644 index 0000000..958879c --- /dev/null +++ b/src/main/java/Remoa/BE/Post/Repository/CommentFeedbackRepository.java @@ -0,0 +1,13 @@ +package Remoa.BE.Post.Repository; + +import Remoa.BE.Member.Domain.CommentFeedback; +import Remoa.BE.Member.Domain.Member; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface CommentFeedbackRepository extends JpaRepository { + + Page findByMemberOrderByTimeDesc(Pageable pageable, Member member); + +} diff --git a/src/main/java/Remoa/BE/Post/Repository/CommentLikeRepository.java b/src/main/java/Remoa/BE/Post/Repository/CommentLikeRepository.java new file mode 100644 index 0000000..4cba625 --- /dev/null +++ b/src/main/java/Remoa/BE/Post/Repository/CommentLikeRepository.java @@ -0,0 +1,12 @@ +package Remoa.BE.Post.Repository; + +import Remoa.BE.Member.Domain.Comment; +import Remoa.BE.Member.Domain.CommentLike; +import Remoa.BE.Member.Domain.FeedbackLike; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface CommentLikeRepository extends JpaRepository { + CommentLike findByMemberMemberIdAndCommentCommentId(Long memberId, Long commentId); +} \ No newline at end of file diff --git a/src/main/java/Remoa/BE/Post/Repository/CommentPagingRepository.java b/src/main/java/Remoa/BE/Post/Repository/CommentPagingRepository.java new file mode 100644 index 0000000..d2d177e --- /dev/null +++ b/src/main/java/Remoa/BE/Post/Repository/CommentPagingRepository.java @@ -0,0 +1,15 @@ +package Remoa.BE.Post.Repository; + +import Remoa.BE.Member.Domain.Comment; +import Remoa.BE.Post.Domain.Post; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.repository.PagingAndSortingRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface CommentPagingRepository extends PagingAndSortingRepository { + + Page findByParentCommentIsNullAndPost(Pageable pageable, Post post); + +} diff --git a/src/main/java/Remoa/BE/Post/Repository/CommentRepository.java b/src/main/java/Remoa/BE/Post/Repository/CommentRepository.java index cf7049d..ce958bd 100644 --- a/src/main/java/Remoa/BE/Post/Repository/CommentRepository.java +++ b/src/main/java/Remoa/BE/Post/Repository/CommentRepository.java @@ -19,13 +19,15 @@ public class CommentRepository { private final EntityManager em; - + public Optional findOne(Long id) { + return Optional.ofNullable(em.find(Comment.class, id)); + } public void saveComment(Comment comment) { em.persist(comment); } - public Comment findByCommentId(Long commentId) { - return em.find(Comment.class, commentId); + public Optional findByCommentId(Long commentId) { + return Optional.ofNullable(em.find(Comment.class, commentId)); } /** @@ -87,4 +89,19 @@ public Integer findCommentBookmark(Comment comment) { .getResultList() .size(); }*/ + + public List findRepliesOfParentComment(Comment parentComment) { + return em.createQuery("select c from Comment c " + + "where c.parentComment = :comment order by c.commentedTime desc", Comment.class) + .setParameter("comment", parentComment) + .getResultList(); + } + + public void updateComment(Comment newComment){ + em.merge(newComment); + } + + public void deleteComment(Comment comment){ + em.remove(comment); + } } diff --git a/src/main/java/Remoa/BE/Post/Repository/FeedbackLikeRepository.java b/src/main/java/Remoa/BE/Post/Repository/FeedbackLikeRepository.java new file mode 100644 index 0000000..852efdc --- /dev/null +++ b/src/main/java/Remoa/BE/Post/Repository/FeedbackLikeRepository.java @@ -0,0 +1,10 @@ +package Remoa.BE.Post.Repository; + +import Remoa.BE.Member.Domain.FeedbackLike; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface FeedbackLikeRepository extends JpaRepository { + FeedbackLike findByMemberMemberIdAndFeedbackFeedbackId(Long memberId, Long feedbackId); +} \ No newline at end of file diff --git a/src/main/java/Remoa/BE/Post/Repository/FeedbackRepository.java b/src/main/java/Remoa/BE/Post/Repository/FeedbackRepository.java index 703c21c..a86ebd9 100644 --- a/src/main/java/Remoa/BE/Post/Repository/FeedbackRepository.java +++ b/src/main/java/Remoa/BE/Post/Repository/FeedbackRepository.java @@ -19,13 +19,15 @@ public class FeedbackRepository { private final EntityManager em; - + public Optional findOne(Long id){ + return Optional.ofNullable(em.find(Feedback.class, id)); + } public void saveFeedback(Feedback feedback) { em.persist(feedback); } - public Feedback findByFeedbackId(Long feedbackId) { - return em.find(Feedback.class, feedbackId); + public Optional findByFeedbackId(Long feedbackId) { + return Optional.ofNullable(em.find(Feedback.class, feedbackId)); } /** @@ -80,6 +82,10 @@ public Optional findMemberCommendBookmark(Member member, Feedb .findAny(); } + public void updateFeedback(Feedback newFeedback){ + em.merge(newFeedback); + } + /* //필요없을 거 같아서 주석처리... public Integer findFeedbackBookmark(Feedback feedback) { return em.createQuery("select cb from FeedbackBookmark cb where cb.feedback = :feedback", FeedbackBookmark.class) @@ -87,4 +93,8 @@ public Integer findFeedbackBookmark(Feedback feedback) { .getResultList() .size(); }*/ + + public void deleteFeedback(Feedback feedback){ + em.remove(feedback); + } } diff --git a/src/main/java/Remoa/BE/Post/Repository/PostPagingRepository.java b/src/main/java/Remoa/BE/Post/Repository/PostPagingRepository.java new file mode 100644 index 0000000..0b1ac3c --- /dev/null +++ b/src/main/java/Remoa/BE/Post/Repository/PostPagingRepository.java @@ -0,0 +1,39 @@ +package Remoa.BE.Post.Repository; + +import Remoa.BE.Member.Domain.Member; +import Remoa.BE.Post.Domain.Category; +import Remoa.BE.Post.Domain.Post; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.repository.PagingAndSortingRepository; +import org.springframework.stereotype.Repository; + +import java.time.LocalDateTime; + +/** + * JPA Query Creation을 사용해서 post data sorting & slicing + */ +@Repository +public interface PostPagingRepository extends PagingAndSortingRepository { + + Page findByMemberAndTitleContainingOrderByPostingTimeDesc(Pageable pageable, Member member,String title); + + Page findByMemberAndTitleContainingOrderByViewsDesc(Pageable pageable, Member member,String title); + + Page findByMemberAndTitleContainingOrderByLikeCountDesc(Pageable pageable, Member member,String title); + + Page findByMemberAndTitleContainingOrderByScrapCountDesc(Pageable pageable, Member member,String title); + + Page findByMemberAndCategoryAndTitleContaining(Pageable pageable, Member member, Category category,String title); + + //"IsNotEmpty" 부분에 "Cannot resolve property 'isNotEmpty'"경고가 나오는 건 JPA의 isNotEmpty 예약어를 intellij가 인식하지 못하고 자바의 프로퍼티로 인식하기 때문. 즉, 무시해도 됨. + Page findByMemberAndCategoryAndCommentsIsNotEmpty(Pageable pageable, Member member, Category category); + + //"IsNotEmpty" 부분에 "Cannot resolve property 'isNotEmpty'"경고가 나오는 건 JPA의 isNotEmpty 예약어를 intellij가 인식하지 못하고 자바의 프로퍼티로 인식하기 때문. 즉, 무시해도 됨. + Page findByMemberAndCommentsIsNotEmpty(Pageable pageable, Member member); + + Page findByTitleContaining(Pageable pageable,String title); + + Page findByCategoryAndTitleContaining(Pageable pageable, Category category,String title); + +} diff --git a/src/main/java/Remoa/BE/Post/Repository/PostRepository.java b/src/main/java/Remoa/BE/Post/Repository/PostRepository.java index a31258a..83816ac 100644 --- a/src/main/java/Remoa/BE/Post/Repository/PostRepository.java +++ b/src/main/java/Remoa/BE/Post/Repository/PostRepository.java @@ -1,5 +1,6 @@ package Remoa.BE.Post.Repository; +import Remoa.BE.Member.Domain.Comment; import Remoa.BE.Member.Domain.Member; import Remoa.BE.Post.Domain.Category; import Remoa.BE.Post.Domain.Post; @@ -10,6 +11,10 @@ import org.springframework.stereotype.Repository; import javax.persistence.EntityManager; +import javax.persistence.TypedQuery; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Root; import java.util.List; import java.util.Optional; @@ -24,15 +29,27 @@ public void savePost(Post post) { em.persist(post); } - public Post findByPostId(Long postId) { - return em.find(Post.class, postId); + public List findAll(){ + return em.createQuery("SELECT p FROM Post p", Post.class).getResultList(); } - public Optional findByMemberId(Member member) { + + public Optional findOne(Long postId) { // 위 findByPostId와 거의 같음 반환형을 Optional로 하기 위함 + return Optional.ofNullable(em.find(Post.class, postId)); + } + + public List findByMember(Member member) { return em.createQuery("select p from Post p where p.member = :member", Post.class) .setParameter("member", member) - .getResultStream() - .findAny(); + .getResultList(); + } + + public List findByTitleContaining(String name){ + CriteriaBuilder cb = em.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(Post.class); + Root root = query.from(Post.class); + query.select(root).where(cb.like(root.get("title"), "%" + name + "%")); + return em.createQuery(query).getResultList(); } public void savePostScrap(PostScarp postScarp) { @@ -63,6 +80,8 @@ public List findPostsByCategory(Category category) { .getResultList(); } - + public void saveComment(Comment comment) { + em.persist(comment); + } } diff --git a/src/main/java/Remoa/BE/Post/Repository/PostScrapRepository.java b/src/main/java/Remoa/BE/Post/Repository/PostScrapRepository.java new file mode 100644 index 0000000..ff319d9 --- /dev/null +++ b/src/main/java/Remoa/BE/Post/Repository/PostScrapRepository.java @@ -0,0 +1,14 @@ +package Remoa.BE.Post.Repository; + +import Remoa.BE.Member.Domain.Member; +import Remoa.BE.Post.Domain.PostScarp; +import Remoa.BE.Post.Service.PostService; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface PostScrapRepository extends JpaRepository { + PostScarp findByMemberMemberIdAndPostPostId(Long memberId, Long postId); + + Page findByMemberOrderByScrapTimeDesc(Pageable pageable, Member member); +} diff --git a/src/main/java/Remoa/BE/Post/Service/CommentFeedbackService.java b/src/main/java/Remoa/BE/Post/Service/CommentFeedbackService.java new file mode 100644 index 0000000..5251e28 --- /dev/null +++ b/src/main/java/Remoa/BE/Post/Service/CommentFeedbackService.java @@ -0,0 +1,42 @@ +package Remoa.BE.Post.Service; + +import Remoa.BE.Member.Domain.*; +import Remoa.BE.Post.Domain.Post; +import Remoa.BE.Post.Repository.CommentFeedbackRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class CommentFeedbackService { + + private final CommentFeedbackRepository commentFeedbackRepository; + + @Transactional + public CommentFeedback saveCommentFeedback(Comment comment, Feedback feedback, ContentType type, + Member member, Post post, LocalDateTime time) { + + CommentFeedback commentFeedback = CommentFeedback.builder() + .type(type) + .member(member) + .post(post) + .comment(comment) + .feedback(feedback) + .time(time) + .deleted(false).build(); + return commentFeedbackRepository.save(commentFeedback); + } + + public Page findNewestCommentOrFeedback(Member member, int size) { + Pageable pageable = PageRequest.of(0, size); + return commentFeedbackRepository.findByMemberOrderByTimeDesc(pageable, member); + } + +} diff --git a/src/main/java/Remoa/BE/Post/Service/CommentService.java b/src/main/java/Remoa/BE/Post/Service/CommentService.java index 93744ed..5967ab4 100644 --- a/src/main/java/Remoa/BE/Post/Service/CommentService.java +++ b/src/main/java/Remoa/BE/Post/Service/CommentService.java @@ -1,17 +1,23 @@ package Remoa.BE.Post.Service; -import Remoa.BE.Member.Domain.Comment; -import Remoa.BE.Member.Domain.CommentBookmark; -import Remoa.BE.Member.Domain.CommentLike; -import Remoa.BE.Member.Domain.Member; +import Remoa.BE.Member.Domain.*; import Remoa.BE.Post.Domain.Post; +import Remoa.BE.Post.Repository.CommentPagingRepository; +import Remoa.BE.Post.Repository.CommentLikeRepository; import Remoa.BE.Post.Repository.CommentRepository; -import Remoa.BE.Post.Repository.PostRepository; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.server.ResponseStatusException; +import java.time.LocalDateTime; import java.util.List; +import java.util.Optional; + +import static Remoa.BE.Member.Domain.ContentType.COMMENT; @Service @Transactional(readOnly = true) @@ -19,7 +25,11 @@ public class CommentService { private final CommentRepository commentRepository; - private final PostRepository postRepository; + + private final CommentLikeRepository commentLikeRepository; + private final PostService postService; + private final CommentPagingRepository commentPagingRepository; + private final CommentFeedbackService commentFeedbackService; @Transactional public Long writeComment(Comment comment) { @@ -27,10 +37,7 @@ public Long writeComment(Comment comment) { return comment.getCommentId(); } - public List loadCommentsByPostId(Long postId) { - Post post = postRepository.findByPostId(postId); - return commentRepository.findByPost(post); - } + @Transactional public Long commentLikeAction(Comment comment, Member member) { @@ -39,10 +46,91 @@ public Long commentLikeAction(Comment comment, Member member) { return commentLike.getCommentLikeId(); } + public Comment findOne(Long commentId) { + Optional comment = commentRepository.findOne(commentId); + return comment.orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST, "Comment not found")); + } + @Transactional public Long commentBookmarkAction(Comment comment, Member member) { CommentBookmark commentBookmark = CommentBookmark.createCommentBookmark(member, comment); commentRepository.saveCommentBookmark(commentBookmark); return commentBookmark.getCommentBookmarkId(); } -} + + @Transactional + public Comment registerComment(Member member, String comment, Long postId, Long commentId){ + + Comment parentComment = null; + + if (commentId != null) { + parentComment = commentRepository.findByCommentId(commentId) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST, "Comment not found")); + } + + Comment commentObj = new Comment(); +// String formatDate = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + Post post = postService.findOne(postId); + LocalDateTime time = LocalDateTime.now(); + + commentObj.setPost(post); + commentObj.setMember(member); + commentObj.setParentComment(parentComment); //대댓글인 경우 원 댓글의 Feedback, 댓글인 경우 null + commentObj.setComment(comment); + commentObj.setCommentLikeCount(0); + commentObj.setCommentedTime(time); + commentRepository.saveComment(commentObj); + + if (parentComment == null) { + commentFeedbackService.saveCommentFeedback(commentObj, null, COMMENT, member, post, time); + } + + return commentObj; + + } + + @Transactional + public void modifyComment(String comment, Long commentId){ + Comment commentObj = findOne(commentId); + commentObj.setComment(comment); + commentRepository.updateComment(commentObj); + } + + + @Transactional + public void deleteComment(Long commentId){ + Comment commentObj = findOne(commentId); + commentRepository.deleteComment(commentObj); + } + + public List getRecentThreeCommentsExceptReply(Post post) { + PageRequest pageable = PageRequest.of(0, 3, Sort.by("commentedTime").descending()); + return commentPagingRepository.findByParentCommentIsNullAndPost(pageable, post).getContent(); + } + + public List getParentCommentsReply(Comment parentComment) { + return commentRepository.findRepliesOfParentComment(parentComment); + } + @Transactional + public CommentLike getCommentLikeByMemberIdAndCommentId(Long memberId, Long commentId) { + return commentLikeRepository.findByMemberMemberIdAndCommentCommentId(memberId, commentId); + } + + @Transactional + public void likeComment(Long memberId, Member myMember, Long commentId){ + Comment commentObj = findOne(commentId); + Integer commentLikeCount = commentObj.getCommentLikeCount(); + + // CommentLike를 db에서 조회해보고 조회 결과가 null이면 like+=1, CommentLike 엔티티 생성 + // null이 아니면 like -= 1, 조회결과인 해당 CommentLike 엔티티 삭제 + CommentLike commentLike = getCommentLikeByMemberIdAndCommentId(memberId, commentId); + if(commentLike == null){ + commentObj.setCommentLikeCount(commentLikeCount + 1); // 좋아요 수 1 증가 + CommentLike commentLikeObj = CommentLike.createCommentLike(myMember, commentObj); + commentLikeRepository.save(commentLikeObj); + }else{ + commentObj.setCommentLikeCount(commentLikeCount - 1); // 좋아요 수 1 차감 + commentLikeRepository.deleteById(commentLike.getCommentLikeId()); // db에서 삭제 + } + } +} \ No newline at end of file diff --git a/src/main/java/Remoa/BE/Post/Service/FeedbackService.java b/src/main/java/Remoa/BE/Post/Service/FeedbackService.java new file mode 100644 index 0000000..cf37810 --- /dev/null +++ b/src/main/java/Remoa/BE/Post/Service/FeedbackService.java @@ -0,0 +1,109 @@ +package Remoa.BE.Post.Service; + +import Remoa.BE.Member.Domain.*; +import Remoa.BE.Member.Service.MemberService; +import Remoa.BE.Post.Domain.Post; +import Remoa.BE.Post.Domain.UploadFile; +import Remoa.BE.Post.Repository.FeedbackLikeRepository; +import Remoa.BE.Post.Repository.FeedbackRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.server.ResponseStatusException; + +import javax.persistence.EntityNotFoundException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +import static Remoa.BE.Member.Domain.ContentType.*; + +@Slf4j +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class FeedbackService { + private final FeedbackRepository feedbackRepository; + private final FeedbackLikeRepository feedbackLikeRepository; + private final PostService postService; + private final CommentFeedbackService commentFeedbackService; + + @Transactional + public Feedback findOne(Long feedbackId){ + Optional feedback = feedbackRepository.findOne(feedbackId); + return feedback.orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST, "Feedback not found")); + } + + @Transactional + public FeedbackLike getFeedbackLikeByMemberIdAndFeedbackId(Long memberId, Long feedbackId) { + FeedbackLike feedbackLike = feedbackLikeRepository.findByMemberMemberIdAndFeedbackFeedbackId(memberId, feedbackId); + return feedbackLike; + } + + @Transactional + public void registerFeedback(Member member, String feedback, Long postId, Integer pageNumber, Long feedbackId){ + + Feedback parentFeedback = null; + if (feedbackId != null) { + parentFeedback = feedbackRepository.findByFeedbackId(feedbackId) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST, "Feedback not found")); + } + + + Feedback feedbackObj = new Feedback(); + Post post = postService.findOne(postId); + + LocalDateTime time = LocalDateTime.now(); + + feedbackObj.setPost(post); + feedbackObj.setMember(member); + feedbackObj.setParentFeedback(parentFeedback); //대댓글인 경우 원 댓글의 Feedback, 댓글인 경우 null + feedbackObj.setPageNumber(pageNumber); //대댓글인 경우 null. parentFeedback.getPageNumber()통헤서 값 넣어도 됩니다. + feedbackObj.setFeedback(feedback); + feedbackObj.setFeedbackLikeCount(0); + feedbackObj.setFeedbackTime(time); + feedbackRepository.saveFeedback(feedbackObj); + + if (parentFeedback == null) { + commentFeedbackService.saveCommentFeedback(null, feedbackObj, FEEDBACK, member, post, time); + } + + } + + @Transactional + public void modifyFeedback(String feedback, Long feedbackId){ + Feedback feedbackObj = findOne(feedbackId); + feedbackObj.setFeedback(feedback); + feedbackRepository.updateFeedback(feedbackObj); + } + + @Transactional + public void deleteFeedback(Long feedbackId){ + Feedback feedbackObj = findOne(feedbackId); + feedbackRepository.deleteFeedback(feedbackObj); + } + + @Transactional + public void likeFeedback(Long memberId, Member myMember, Long feedbackId){ + Feedback feedbackObj = findOne(feedbackId); + Integer feedbackLikeCount = feedbackObj.getFeedbackLikeCount(); + + //FeedbackLike를 db에서 조회해보고 조회 결과가 null이면 like+=1, FeedbackLike 엔티티 추가 + // null이 아니면 like -=1, 조회결과인 해당 FeedbackLike 엔티티 삭제 + FeedbackLike feedbackLike = getFeedbackLikeByMemberIdAndFeedbackId(memberId, feedbackId); + if(feedbackLike == null){ + feedbackObj.setFeedbackLikeCount(feedbackLikeCount + 1); // 좋아요 수 1 증가 + FeedbackLike feedbackLikeObj = FeedbackLike.createFeedbackLike(myMember, feedbackObj); + feedbackLikeRepository.save(feedbackLikeObj); + } else{ + feedbackObj.setFeedbackLikeCount(feedbackLikeCount - 1); // 좋아요 수 1 차감 + feedbackLikeRepository.deleteById(feedbackLike.getFeedbackLikeId()); // db에서 삭제 + } + } +} \ No newline at end of file diff --git a/src/main/java/Remoa/BE/Post/Service/FileService.java b/src/main/java/Remoa/BE/Post/Service/FileService.java index 14d7553..6fa26ae 100644 --- a/src/main/java/Remoa/BE/Post/Service/FileService.java +++ b/src/main/java/Remoa/BE/Post/Service/FileService.java @@ -22,6 +22,7 @@ import java.io.InputStream; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.UUID; @@ -47,11 +48,18 @@ public class FileService { * 파일들을 저장해준다 */ @Transactional - public void saveUploadFiles(Post post,List multipartFile){ + public void saveUploadFiles(Post post,MultipartFile thumbnail, List multipartFile){ - multipartFile.forEach(item -> saveUploadFile(post,item)); + //썸네일 파일 저장 추가 + saveUploadFile(thumbnail,post,"thumbnail"); + multipartFile.forEach(file -> saveUploadFile(file, post,"post")); - post.setUploadFiles(uploadFileList); + + //새로운 인스턴스 만들어서 set하지 않으면 clear 되면서 null이 계속 저장됨. + UploadFile uploadFile = uploadFileList.get(0); + post.setThumbnail(uploadFile); + + post.setUploadFiles(new ArrayList<>(uploadFileList.subList(1, uploadFileList.size()))); postRepository.savePost(post); uploadFileList.clear(); @@ -59,18 +67,18 @@ public void saveUploadFiles(Post post,List multipartFile){ /** * - * @param post 게시글 * @param multipartFile 파일 - * saveUploadFiles 에서 파일 하나씩 가져와서 s3에 넣는다 */ @Transactional - public void saveUploadFile(Post post, MultipartFile multipartFile){ + public void saveUploadFile(MultipartFile multipartFile, Post post,String folderName){ //파일 타입과 사이즈 저장 ObjectMetadata objectMetadata = new ObjectMetadata(); objectMetadata.setContentType(multipartFile.getContentType()); objectMetadata.setContentLength(multipartFile.getSize()); + log.info(multipartFile.getContentType()); + //파일 이름 String originalFilename = multipartFile.getOriginalFilename(); @@ -82,8 +90,8 @@ public void saveUploadFile(Post post, MultipartFile multipartFile){ //파일 이름이 겹치지 않게 String uuid = UUID.randomUUID().toString(); - //postId 폴더에 따로 넣어서 보관 - String s3name = uuid+"_"+originalFilename; + //post 폴더에 따로 넣어서 보관 + String s3name = folderName+"/"+uuid+"_"+originalFilename; try (InputStream inputStream = multipartFile.getInputStream()) { amazonS3.putObject(new PutObjectRequest(bucket, s3name, inputStream, objectMetadata) @@ -97,14 +105,17 @@ public void saveUploadFile(Post post, MultipartFile multipartFile){ //파일 보관 url String storeFileUrl = amazonS3.getUrl(bucket,s3name).toString().replaceAll("\\+", "+"); UploadFile uploadFile = new UploadFile(); - uploadFile.setPost(post); uploadFile.setOriginalFileName(originalFilename); uploadFile.setSaveFileName(s3name); uploadFile.setStoreFileUrl(storeFileUrl); uploadFile.setExtension(ext); + uploadFile.setPost(post); + uploadFileList.add(uploadFile); - log.info(storeFileUrl); + uploadFileRepository.saveFile(uploadFile); + + log.info(storeFileUrl); } public String getUrl(Long fileId){ @@ -132,7 +143,8 @@ public ResponseEntity getObject(Long fileId) throws IOException { String fileOriginalName = file.get().getOriginalFileName(); //encode 메서드에 두 번째 파라메터에 StandardCharsets.UTF_8만 쓰면 오류가 나서 뒤에 name을 임사방편으로 붙임. 기능상 문제는 없을듯 함 - String fileNameFix = URLEncoder.encode(fileOriginalName, StandardCharsets.UTF_8.name()).replaceAll("\\+", "%20"); + String fileNameFix = URLEncoder.encode(fileOriginalName, StandardCharsets.UTF_8.name()) + .replaceAll("\\+", "%20"); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM); httpHeaders.setContentLength(bytes.length); diff --git a/src/main/java/Remoa/BE/Post/Service/MyPostService.java b/src/main/java/Remoa/BE/Post/Service/MyPostService.java new file mode 100644 index 0000000..1d44545 --- /dev/null +++ b/src/main/java/Remoa/BE/Post/Service/MyPostService.java @@ -0,0 +1,104 @@ +package Remoa.BE.Post.Service; + +import Remoa.BE.Member.Domain.Member; +import Remoa.BE.Post.Domain.Post; +import Remoa.BE.Post.Repository.CategoryRepository; +import Remoa.BE.Post.Repository.PostPagingRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Slf4j +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class MyPostService { + + private final PostPagingRepository postPagingRepository; + private final CategoryRepository categoryRepository; + private static final int PAGE_SIZE = 12; + private static final int RECEIVED_COMMENT_PAGE_SIZE = 3; + + public Page sortAndPaginatePostsByMember(int pageNumber, String sort, Member myMember,String title) { + Page posts; + PageRequest pageable = PageRequest.of(pageNumber, PAGE_SIZE); + //switch문을 통해 각 옵션에 맞게 sorting + switch (sort) { + case "view": + posts = postPagingRepository.findByMemberAndTitleContainingOrderByViewsDesc(pageable, myMember,title); + break; + case "like": + posts = postPagingRepository.findByMemberAndTitleContainingOrderByLikeCountDesc(pageable, myMember,title); + break; + case "scrap": + posts = postPagingRepository.findByMemberAndTitleContainingOrderByScrapCountDesc(pageable, myMember,title); + break; + default: + //sort 문자열이 잘못됐을 경우 default인 최신순으로 정렬 + posts = postPagingRepository.findByMemberAndTitleContainingOrderByPostingTimeDesc(pageable, myMember,title); + break; + } + return posts; + } + + public Page sortAndPaginatePostsByCategoryAndMember(String category, int pageNumber, String sort, Member myMember,String title) { + Page posts; + PageRequest pageable; + switch (sort) { + case "view": + pageable = PageRequest.of(pageNumber, PAGE_SIZE, Sort.by("views").descending()); + break; + case "like": + pageable = PageRequest.of(pageNumber, PAGE_SIZE, Sort.by("likeCount").descending()); + break; + case "scrap": + pageable = PageRequest.of(pageNumber, PAGE_SIZE, Sort.by("scrapCount").descending()); + break; + default: + //sort 문자열이 잘못됐을 경우 default인 최신순으로 정렬 + pageable = PageRequest.of(pageNumber, PAGE_SIZE, Sort.by("postingTime").descending()); + break; + } + posts = postPagingRepository.findByMemberAndCategoryAndTitleContaining(pageable, myMember, categoryRepository.findByCategoryName(category),title); + return posts; + } + + /** + * 받은 피드백 관리에서 쓰이는 최신 3개순 포스트 + * @param page + * @param member + * @param category + * @return member가 작성한 최신 3개의 Post. + */ + public Page getNewestThreePostsSortCategory(int page, Member member, String category) { + PageRequest pageable = PageRequest.of(page, RECEIVED_COMMENT_PAGE_SIZE, Sort.by("postingTime").descending()); + return postPagingRepository.findByMemberAndCategoryAndCommentsIsNotEmpty(pageable, member, categoryRepository.findByCategoryName(category)); + } + + /** + * 받은 피드백 관리에서 쓰이는 최신 3개순 포스트 + * @param page + * @param member + * @return member가 작성한 최신 3개의 Post. + */ + public Page getNewestThreePosts(int page, Member member) { + PageRequest pageable = PageRequest.of(page, RECEIVED_COMMENT_PAGE_SIZE, Sort.by("postingTime").descending()); + return postPagingRepository.findByMemberAndCommentsIsNotEmpty(pageable, member); + } + + /** + * 내 활동 관리에 쓰이는 코멘트 및 피드백을 단 작업물 + * @param size + * @param member + * @return Post + */ + public Page getCommentedPost(int size, Member member) { + PageRequest pageable = PageRequest.of(0, size, Sort.by("postingTime").descending()); + return postPagingRepository.findByMemberAndCommentsIsNotEmpty(pageable, member); + } + +} \ No newline at end of file diff --git a/src/main/java/Remoa/BE/Post/Service/PostService.java b/src/main/java/Remoa/BE/Post/Service/PostService.java index 9f962fa..2d8ded3 100644 --- a/src/main/java/Remoa/BE/Post/Service/PostService.java +++ b/src/main/java/Remoa/BE/Post/Service/PostService.java @@ -1,56 +1,175 @@ package Remoa.BE.Post.Service; import Remoa.BE.Member.Domain.Member; +import Remoa.BE.Member.Service.MemberService; import Remoa.BE.Post.Domain.Category; import Remoa.BE.Post.Domain.Post; -import Remoa.BE.Post.Domain.UploadFile; -import Remoa.BE.Post.Repository.PostRepository; -import Remoa.BE.Post.Repository.UploadFileRepository; +import Remoa.BE.Post.Domain.PostScarp; +import Remoa.BE.Post.Dto.Request.UploadPostForm; import Remoa.BE.Post.Repository.CategoryRepository; -import Remoa.BE.Post.form.Request.UploadPostForm; +import Remoa.BE.Post.Repository.PostPagingRepository; +import Remoa.BE.Post.Repository.PostRepository; +import Remoa.BE.Post.Repository.PostScrapRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.server.ResponseStatusException; +import java.io.IOException; +import java.time.LocalDateTime; import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import static Remoa.BE.utill.FileExtension.fileExtension; @Slf4j @Service @RequiredArgsConstructor +@Transactional(readOnly = true) public class PostService { - private final UploadFileRepository uploadFileRepository; + + private final MemberService memberService; private final PostRepository postRepository; private final CategoryRepository categoryRepository; - private final List uploadFileList; - private final FileService fileService; - public Post dtoToEntity(UploadPostForm uploadPostForm, Member member){ // dto를 db에 저장하기 위해 entity로 변환 + private final PostPagingRepository postPagingRepository; + + private final PostScrapRepository postScrapRepository; + + private static final int HOME_PAGE_SIZE = 12; + + public Post findOne(Long postId) { + Optional post = postRepository.findOne(postId); + return post.orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST, "Post not found")); + } + + @Transactional + public Post registerPost(UploadPostForm uploadPostForm, MultipartFile thumbnail, List uploadFiles, Long memberId) throws IOException { - // String category를 이용해 Category 엔티티를 찾기 Category category = categoryRepository.findByCategoryName(uploadPostForm.getCategory()); - return Post.builder() - .title(uploadPostForm.getTitle()) - .member(member) - .contestName(uploadPostForm.getContestName()) - .category(category) - .contestAwareType(uploadPostForm.getContestAward()) - .build(); + Member member = memberService.findOne(memberId); + + + //확장자 확인 + String extension = fileExtension(uploadFiles.get(0)); + if (extension.equals("pdf") || extension.equals("jpg") || extension.equals("png")) { + int pageCount; + if (extension.equals("pdf")) { + PDDocument document = PDDocument.load(uploadFiles.get(0).getInputStream()); + pageCount = document.getNumberOfPages(); + } else { + pageCount = uploadFiles.size(); + } + Post post = Post.builder() + .title(uploadPostForm.getTitle()) + .member(member) + .contestName(uploadPostForm.getContestName()) + .category(category) + .youtubeLink(uploadPostForm.getYoutubeLink()) + .contestAwardType(uploadPostForm.getContestAwardType()) + .pageCount(pageCount) + .postingTime(LocalDateTime.now()) + .likeCount(0) + .views(0) + .scrapCount(0) + .deleted(false) + .build(); + + fileService.saveUploadFiles(post, thumbnail, uploadFiles); + return post; + } else { + throw new IOException(); + } + + + } + + public Page sortAndPaginatePosts(String sort, int pageNumber, String title) { + Page Posts; + Pageable pageable; + switch (sort) { + case "like": + pageable = PageRequest.of(pageNumber, HOME_PAGE_SIZE, Sort.by("likeCount").descending()); + Posts = postPagingRepository.findByTitleContaining(pageable, title); + break; + case "scrap": + pageable = PageRequest.of(pageNumber, HOME_PAGE_SIZE, Sort.by("scrapCount").descending()); + Posts = postPagingRepository.findByTitleContaining(pageable, title); + break; + case "view": + pageable = PageRequest.of(pageNumber, HOME_PAGE_SIZE, Sort.by("views").descending()); + Posts = postPagingRepository.findByTitleContaining(pageable, title); + break; + default: + //sort 문자열이 잘못됐을 경우 default인 최신순으로 정렬 + pageable = PageRequest.of(pageNumber, HOME_PAGE_SIZE, Sort.by("postingTime").descending()); + Posts = postPagingRepository.findByTitleContaining(pageable, title); + break; + } + return Posts; + } + + public Page sortAndPaginatePostsByCategory(String category, String sort, int pageNumber, String title) { + Page Posts; + Pageable pageable; + switch (sort) { + case "like": + pageable = PageRequest.of(pageNumber, HOME_PAGE_SIZE, Sort.by("likeCount").descending()); + break; + case "scrap": + pageable = PageRequest.of(pageNumber, HOME_PAGE_SIZE, Sort.by("scrapCount").descending()); + break; + case "view": + pageable = PageRequest.of(pageNumber, HOME_PAGE_SIZE, Sort.by("views").descending()); + break; + default: + //sort 문자열이 잘못됐을 경우 default인 최신순으로 정렬 + pageable = PageRequest.of(pageNumber, HOME_PAGE_SIZE, Sort.by("postingTime").descending()); + break; + } + Posts = postPagingRepository.findByCategoryAndTitleContaining(pageable, categoryRepository.findByCategoryName(category), title); + return Posts; + } + + public PostScarp getPostScrapByMemberIdAndPostId(Long memberId, Long postId) { + return postScrapRepository.findByMemberMemberIdAndPostPostId(memberId, postId); } @Transactional - public void registPost(UploadPostForm uploadPostForm, List uploadFiles, Member member){ + public void scrapPost(Long memberId, Member myMember, Long referenceId) { + Post post = findOne(referenceId); + Integer postScrapCount = post.getScrapCount(); // 이 게시물을 스크랩한 수 - Post post = dtoToEntity(uploadPostForm, member); - postRepository.savePost(post); - fileService.saveUploadFiles(post, uploadFiles); + // scrapPost를 db에서 조회해보고 조회 결과가 null이면 scrapCount += 1, PostScrap 생성 + // null이 아니면 scrapCount -= 1, 조회결과인 해당 PostScrap 삭제 + PostScarp postScarp = getPostScrapByMemberIdAndPostId(memberId, referenceId); + if (postScarp == null) { + post.setScrapCount(postScrapCount + 1); // 스크랩 수 1 증가 + PostScarp postScrapObj = PostScarp.createPostScrap(myMember, post); + postScrapRepository.save(postScrapObj); + } else { + post.setScrapCount(post.getScrapCount() - 1); // 스크랩 수 1 차감 + postScrapRepository.deleteById(postScarp.getPostScrapId()); // db에서 삭제 + } } -} + public Page findScrapedPost(int size, Member member) { + Pageable pageable = PageRequest.of(0, size); + return postScrapRepository.findByMemberOrderByScrapTimeDesc(pageable, member); + } +} \ No newline at end of file diff --git a/src/main/java/Remoa/BE/Post/Service/ViewerService.java b/src/main/java/Remoa/BE/Post/Service/ViewerService.java new file mode 100644 index 0000000..5ea5ab2 --- /dev/null +++ b/src/main/java/Remoa/BE/Post/Service/ViewerService.java @@ -0,0 +1,42 @@ +package Remoa.BE.Post.Service; + + +import Remoa.BE.Post.Domain.Post; +import Remoa.BE.Post.Repository.PostRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + + +@Slf4j +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class ViewerService { + + private final PostRepository postRepository; + + @Transactional + public Post getPost(Long postId) { + // 게시물 정보 검색 + Post post = postRepository.findOne(postId) + .orElseThrow(() -> new IllegalArgumentException("Invalid postId: " + postId)); + + // 조회수 업데이트 + post.setViews(post.getViews() + 1); + + return post; + } + + @Transactional + public void updateScrapCount(Long postId) { + // 게시물 정보 검색 + Post post = postRepository.findOne(postId) + .orElseThrow(() -> new IllegalArgumentException("Invalid postId: " + postId)); + + // 스크랩수 업데이트 + int scrapCount = post.getPostScarps().size(); + post.setScrapCount(scrapCount); + } +} \ No newline at end of file diff --git a/src/main/java/Remoa/BE/Post/form/Request/UploadPostForm.java b/src/main/java/Remoa/BE/Post/form/Request/UploadPostForm.java deleted file mode 100644 index bcecf18..0000000 --- a/src/main/java/Remoa/BE/Post/form/Request/UploadPostForm.java +++ /dev/null @@ -1,27 +0,0 @@ -package Remoa.BE.Post.form.Request; - -import Remoa.BE.Post.Domain.Category; -import Remoa.BE.Post.Domain.Post; -import lombok.Builder; -import lombok.Getter; -import lombok.Setter; -import org.springframework.web.multipart.MultipartFile; - -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.Size; -import java.util.List; - -@Getter -@Setter -public class UploadPostForm { - - private String title; // Post name - - private String contestName; - - private String category; // Category name - - private String contestAward; - - //private List uploadFiles; -} diff --git a/src/main/java/Remoa/BE/config/AppConfig.java b/src/main/java/Remoa/BE/config/AppConfig.java new file mode 100644 index 0000000..d930292 --- /dev/null +++ b/src/main/java/Remoa/BE/config/AppConfig.java @@ -0,0 +1,13 @@ +package Remoa.BE.config; + +import org.modelmapper.ModelMapper; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class AppConfig { + @Bean + public ModelMapper modelMapper() { + return new ModelMapper(); + } +} \ No newline at end of file diff --git a/src/main/java/Remoa/BE/config/CorsConfig.java b/src/main/java/Remoa/BE/config/CorsConfig.java index a369d88..ade0a09 100644 --- a/src/main/java/Remoa/BE/config/CorsConfig.java +++ b/src/main/java/Remoa/BE/config/CorsConfig.java @@ -11,14 +11,16 @@ public class CorsConfig { @Bean public CorsFilter corsFilter() { + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration config = new CorsConfiguration(); + config.setAllowCredentials(true); - config.addAllowedOrigin("*"); + config.addAllowedOriginPattern("*"); // addAllowedOriginPattern("*") 대신 사용 config.addAllowedHeader("*"); config.addAllowedMethod("*"); + source.registerCorsConfiguration("/**", config); - source.registerCorsConfiguration("*", config); return new CorsFilter(source); } } \ No newline at end of file diff --git a/src/main/java/Remoa/BE/config/DbInit.java b/src/main/java/Remoa/BE/config/DbInit.java index e1350be..98462bf 100644 --- a/src/main/java/Remoa/BE/config/DbInit.java +++ b/src/main/java/Remoa/BE/config/DbInit.java @@ -1,6 +1,5 @@ package Remoa.BE.config; -import Remoa.BE.Member.Domain.Role; import Remoa.BE.Post.Domain.Category; import Remoa.BE.Member.Domain.Member; import Remoa.BE.Post.Service.CategoryService; diff --git a/src/main/java/Remoa/BE/config/SecureConfig.java b/src/main/java/Remoa/BE/config/SecureConfig.java index 8bff517..2c60b10 100644 --- a/src/main/java/Remoa/BE/config/SecureConfig.java +++ b/src/main/java/Remoa/BE/config/SecureConfig.java @@ -15,7 +15,7 @@ @Configuration public class SecureConfig { - private final CorsFilter corsFilter; + /** * Spring Security에서 사용할 password encoder로 BCryptPasswordEncoder 지정 @@ -31,7 +31,6 @@ public PasswordEncoder passwordEncoder() { @Bean public SecurityFilterChain configure(HttpSecurity http) throws Exception { - http.addFilter(corsFilter); http.formLogin().disable(); diff --git a/src/main/java/Remoa/BE/exception/CustomMessage.java b/src/main/java/Remoa/BE/exception/CustomMessage.java index e1da644..e333f0c 100644 --- a/src/main/java/Remoa/BE/exception/CustomMessage.java +++ b/src/main/java/Remoa/BE/exception/CustomMessage.java @@ -2,13 +2,16 @@ import lombok.Getter; import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; @Getter public enum CustomMessage { //200 정상처리 상태코드 OK(HttpStatus.OK,"올바른 요청","정상적으로 처리되었습니다"), + OK_DUPLICATE(HttpStatus.OK,"올바른 요청","닉네임이 중복되었습니다"), + + OK_UN_DUPLICATE(HttpStatus.OK,"올바른 요청","사용가능한 닉네임 입니다"), + OK_UNFOLLOW(HttpStatus.OK,"올바른 요청","회원을 언팔로잉 합니다"), //201 한 api에서 정상처리상 구분이 필요할떄 사용 @@ -18,13 +21,21 @@ public enum CustomMessage { //400 잘못된 요청 VALIDATED(HttpStatus.BAD_REQUEST,"잘못된 요청","요청한 값이 유효성검사를 통과하지 못했습니다"), - NO_MEMBER(HttpStatus.BAD_REQUEST,"잘못된 요청","요청한 memberId가 존재하지 않습니다"), + NO_ID(HttpStatus.BAD_REQUEST,"잘못된 요청","요청한 Id가 존재하지 않습니다"), - //401권한오류 - UNAUTHORIZED(HttpStatus.UNAUTHORIZED,"권한이 없습니다","인증에 필요한 쿠키 정보가 없습니다"), + FOLLOW_ME(HttpStatus.BAD_REQUEST,"서비스 로직상 오류","자신을 팔로우할 수 없습니다"), + + BAD_DUPLICATE(HttpStatus.BAD_REQUEST,"서비스 로직상 오류","닉네임이 중복되었습니다"), - //409 상태 충돌 - FOLLOW_ME(HttpStatus.CONFLICT,"서비스 로직상 오류","자신을 팔로우할 수 없습니다"); + BAD_PROFILE_IMG(HttpStatus.BAD_REQUEST,"서비스 로직상 오류","해당 멤버의 프로필사진이 존재하지 않습니다"), + + BAD_FILE(HttpStatus.BAD_REQUEST,"서비스 로직상 오류","해당 파일 유형은 지원하지 않습니다"), + + BAD_PAGE_NUM(HttpStatus.BAD_REQUEST, "피드백 등록시 페이지 넘버 오류", "존재하지 않는 페이지에 피드백을 등록하려 합니다"), + + PAGE_NUM_OVER(HttpStatus.BAD_REQUEST, "레퍼런스 조회시 페이지 넘버 오류", "올바르지 않는 페이지 번호입니다."), + //401권한오류 + UNAUTHORIZED(HttpStatus.UNAUTHORIZED,"권한이 없습니다","인증에 필요한 쿠키 정보가 없습니다"); private final HttpStatus httpStatus; diff --git a/src/main/java/Remoa/BE/exception/CustomizedExceptionHandler.java b/src/main/java/Remoa/BE/exception/CustomizedExceptionHandler.java index 922f7f7..3fc7279 100644 --- a/src/main/java/Remoa/BE/exception/CustomizedExceptionHandler.java +++ b/src/main/java/Remoa/BE/exception/CustomizedExceptionHandler.java @@ -1,6 +1,5 @@ package Remoa.BE.exception; -import Remoa.BE.exception.response.FailResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.MethodArgumentNotValidException; @@ -8,6 +7,7 @@ import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.server.ResponseStatusException; +import java.io.IOException; import java.util.HashMap; import java.util.Map; @@ -31,6 +31,12 @@ public ResponseEntity methodValidException(MethodArgumentNotValidExcepti @ExceptionHandler public ResponseEntity responseStatusException(ResponseStatusException ex){ - return errorResponse(CustomMessage.NO_MEMBER); + return errorResponse(CustomMessage.NO_ID); + } + + //파일 유형 에러 + @ExceptionHandler + public ResponseEntity ioException(IOException ex){ + return errorResponse(CustomMessage.BAD_FILE); } } diff --git a/src/main/java/Remoa/BE/utill/FileExtension.java b/src/main/java/Remoa/BE/utill/FileExtension.java new file mode 100644 index 0000000..ffcc1df --- /dev/null +++ b/src/main/java/Remoa/BE/utill/FileExtension.java @@ -0,0 +1,13 @@ +package Remoa.BE.utill; + +import org.springframework.web.multipart.MultipartFile; + +public class FileExtension { + public static String fileExtension(MultipartFile multipartFile){ + String fileName = multipartFile.getOriginalFilename(); + assert fileName != null; + int lastIndex = fileName.lastIndexOf("."); + return fileName.substring(lastIndex + 1); + } + +} diff --git a/src/main/java/Remoa/BE/utill/MemberInfo.java b/src/main/java/Remoa/BE/utill/MemberInfo.java index b04ff16..4ae2b6b 100644 --- a/src/main/java/Remoa/BE/utill/MemberInfo.java +++ b/src/main/java/Remoa/BE/utill/MemberInfo.java @@ -1,6 +1,7 @@ package Remoa.BE.utill; import Remoa.BE.Member.Domain.Member; +import lombok.extern.slf4j.Slf4j; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; @@ -16,6 +17,7 @@ import java.util.List; import java.util.Optional; +@Slf4j public class MemberInfo { public static Long getMemberId() { Member member = (Member) SecurityContextHolder.getContext().getAuthentication().getPrincipal();