diff --git a/src/main/java/com/example/spot/api/code/status/SuccessStatus.java b/src/main/java/com/example/spot/api/code/status/SuccessStatus.java index d294cea6..b68a066f 100644 --- a/src/main/java/com/example/spot/api/code/status/SuccessStatus.java +++ b/src/main/java/com/example/spot/api/code/status/SuccessStatus.java @@ -31,6 +31,7 @@ public enum SuccessStatus implements BaseCode { _MEMBER_LOGIN_ID_CHECK_COMPLETED(HttpStatus.OK, "MEMBER2012", "회원 아이디 사용 가능 여부 조회 완료"), _MEMBER_EMAIL_CHECK_COMPLETED(HttpStatus.OK, "MEMBER2013", "회원 이메일 사용 가능 여부 조회 완료"), _RSA_PUBLIC_KEY_FOUND(HttpStatus.OK, "MEMBER2014", "RSA Public Key 조회 완료"), + _MEMBER_SIGNUP_CHECK_COMPLETED(HttpStatus.OK, "MEMBER2015", "회원 가입 유무 조회 완료"), //스터디 게시글 관련 응답 _STUDY_POST_CREATED(HttpStatus.CREATED, "STUDYPOST3001", "스터디 게시글 작성 완료"), diff --git a/src/main/java/com/example/spot/domain/mapping/StudyPostImage.java b/src/main/java/com/example/spot/domain/mapping/StudyPostImage.java index afa63038..507251f1 100644 --- a/src/main/java/com/example/spot/domain/mapping/StudyPostImage.java +++ b/src/main/java/com/example/spot/domain/mapping/StudyPostImage.java @@ -16,14 +16,17 @@ @Entity @Getter +@Builder @DynamicUpdate @DynamicInsert +@AllArgsConstructor @NoArgsConstructor(access = AccessLevel.PROTECTED) public class StudyPostImage extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @Setter @Column(nullable = false) private String url; @@ -31,10 +34,4 @@ public class StudyPostImage extends BaseEntity { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "study_post_id", nullable = false) private StudyPost studyPost; - -/* ----------------------------- 생성자 ------------------------------------- */ - - public StudyPostImage(String url) { - this.url = url; - } } diff --git a/src/main/java/com/example/spot/domain/study/StudyPost.java b/src/main/java/com/example/spot/domain/study/StudyPost.java index 57946a27..83da25b1 100644 --- a/src/main/java/com/example/spot/domain/study/StudyPost.java +++ b/src/main/java/com/example/spot/domain/study/StudyPost.java @@ -107,10 +107,6 @@ public void deleteLikedPost(StudyLikedPost likedPost) { likedPosts.remove(likedPost); } - public void updateImage(StudyPostImage studyPostImage) { - images.set(images.indexOf(studyPostImage), studyPostImage); - } - public void updateComment(StudyPostComment studyPostComment) { comments.set(comments.indexOf(studyPostComment), studyPostComment); } @@ -152,4 +148,8 @@ public void updatePost(StudyPostRequestDTO.PostDTO requestDTO) { member.updateStudyPost(this); study.updateStudyPost(this); } + + public void updateImage(StudyPostImage studyPostImage) { + images.set(images.indexOf(studyPostImage), studyPostImage); + } } diff --git a/src/main/java/com/example/spot/service/auth/AuthService.java b/src/main/java/com/example/spot/service/auth/AuthService.java index 6182153b..24574d02 100644 --- a/src/main/java/com/example/spot/service/auth/AuthService.java +++ b/src/main/java/com/example/spot/service/auth/AuthService.java @@ -43,4 +43,6 @@ public interface AuthService { MemberResponseDTO.AvailabilityDTO checkLoginIdAvailability(String loginId); MemberResponseDTO.AvailabilityDTO checkEmailAvailability(String email); + + MemberResponseDTO.CheckMemberDTO checkIsSpotMember(Long loginId); } diff --git a/src/main/java/com/example/spot/service/auth/AuthServiceImpl.java b/src/main/java/com/example/spot/service/auth/AuthServiceImpl.java index aea01c17..bc21b495 100644 --- a/src/main/java/com/example/spot/service/auth/AuthServiceImpl.java +++ b/src/main/java/com/example/spot/service/auth/AuthServiceImpl.java @@ -10,6 +10,7 @@ import com.example.spot.repository.MemberThemeRepository; import com.example.spot.repository.PreferredRegionRepository; import com.example.spot.repository.StudyReasonRepository; +import com.example.spot.web.dto.member.MemberResponseDTO.CheckMemberDTO; import com.example.spot.web.dto.rsa.Rsa; import com.example.spot.domain.auth.RefreshToken; import com.example.spot.domain.auth.VerificationCode; @@ -670,6 +671,16 @@ public MemberResponseDTO.AvailabilityDTO checkEmailAvailability(String email) { return MemberResponseDTO.AvailabilityDTO.toDTO(Boolean.TRUE, null); } + @Override + public CheckMemberDTO checkIsSpotMember(Long loginId) { + Member member = memberRepository.findById(loginId) + .orElseThrow(() -> new MemberHandler(ErrorStatus._MEMBER_NOT_FOUND)); + + boolean memberExistsByCheckList = isMemberExistsByCheckList(member); + + return CheckMemberDTO.toDTO(memberExistsByCheckList); + } + /** * 임시 비밀번호를 발급하는 메서드입니다. * 알파벳 대소문자, 숫자, 특수기호를 혼합하여 13자리 비밀번호를 생성합니다. diff --git a/src/main/java/com/example/spot/service/studypost/StudyPostCommandServiceImpl.java b/src/main/java/com/example/spot/service/studypost/StudyPostCommandServiceImpl.java index eb13ee2b..539d1146 100644 --- a/src/main/java/com/example/spot/service/studypost/StudyPostCommandServiceImpl.java +++ b/src/main/java/com/example/spot/service/studypost/StudyPostCommandServiceImpl.java @@ -26,6 +26,7 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; import java.time.LocalDateTime; import java.util.ArrayList; @@ -96,6 +97,8 @@ public StudyPostResDTO.PostPreviewDTO createPost(Long studyId, StudyPostRequestD .likeNum(0) .hitNum(0) .commentNum(0) + .member(member) + .study(study) .build(); // 공지면 announcedAt 설정 @@ -103,19 +106,19 @@ public StudyPostResDTO.PostPreviewDTO createPost(Long studyId, StudyPostRequestD studyPost.setAnnouncedAt(LocalDateTime.now()); } + studyPost = studyPostRepository.save(studyPost); member.addStudyPost(studyPost); study.addStudyPost(studyPost); - studyPost = studyPostRepository.save(studyPost); - if (postRequestDTO.getImages() != null && !postRequestDTO.getImages().isEmpty()) { - ImageResponse.ImageUploadResponse imageUploadResponse = s3ImageService.uploadImages(postRequestDTO.getImages()); - for (ImageResponse.Images imageDTO : imageUploadResponse.getImageUrls()) { - String imageUrl = imageDTO.getImageUrl(); - StudyPostImage studyPostImage = new StudyPostImage(imageUrl); - studyPost.addImage(studyPostImage); // image id가 저장되지 않음 - studyPostImage = studyPostImageRepository.save(studyPostImage); - studyPost.updateImage(studyPostImage); // image id 저장 - } + // 이미지가 있는 경우 이미지 저장 + if (postRequestDTO.getImage() != null) { + String imageUrl = s3ImageService.upload(postRequestDTO.getImage()); + StudyPostImage studyPostImage = StudyPostImage.builder() + .url(imageUrl) + .studyPost(studyPost) + .build(); + studyPostImage = studyPostImageRepository.save(studyPostImage); + studyPost.addImage(studyPostImage); } if (studyPost.getIsAnnouncement()){ @@ -154,9 +157,9 @@ public StudyPostResDTO.PostPreviewDTO updatePost(Long studyId, Long postId, Stud Long memberId = SecurityUtils.getCurrentUserId(); SecurityUtils.verifyUserId(memberId); - Member member = memberRepository.findById(memberId) + memberRepository.findById(memberId) .orElseThrow(() -> new MemberHandler(ErrorStatus._MEMBER_NOT_FOUND)); - Study study = studyRepository.findById(studyId) + studyRepository.findById(studyId) .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_NOT_FOUND)); StudyPost studyPost = studyPostRepository.findById(postId) .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_POST_NOT_FOUND)); @@ -174,25 +177,28 @@ public StudyPostResDTO.PostPreviewDTO updatePost(Long studyId, Long postId, Stud throw new StudyHandler(ErrorStatus._STUDY_POST_ANNOUNCEMENT_INVALID); } + // 스터디 게시글 이미지 업데이트 + updateStudyPostImage(postDTO, studyPost); + // 스터디 게시글 업데이트 studyPost.updatePost(postDTO); - studyPost = studyPostRepository.save(studyPost); - // 기존 게시글 이미지 삭제 - studyPostImageRepository.deleteAllByStudyPostId(postId); - - if (postDTO.getImages() != null && !postDTO.getImages().isEmpty()) { - ImageResponse.ImageUploadResponse imageUploadResponse = s3ImageService.uploadImages(postDTO.getImages()); - for (ImageResponse.Images imageDTO : imageUploadResponse.getImageUrls()) { - String imageUrl = imageDTO.getImageUrl(); - StudyPostImage studyPostImage = new StudyPostImage(imageUrl); - studyPost.addImage(studyPostImage); // image id가 저장되지 않음 - studyPostImage = studyPostImageRepository.save(studyPostImage); - studyPost.updateImage(studyPostImage); // image id 저장 + return StudyPostResDTO.PostPreviewDTO.toDTO(studyPost); + } + + private void updateStudyPostImage(StudyPostRequestDTO.PostDTO postDTO, StudyPost studyPost) { + List studyPostImages = studyPost.getImages(); + // 기존 이미지가 존재하는 경우 이미지 유지 + if (!StringUtils.hasText(postDTO.getExistingImage())) { + // 기존 이미지가 없고 새로운 이미지를 등록한 경우 이미지 url 변경 + if (postDTO.getImage() != null) { + String imageUrl = s3ImageService.upload(postDTO.getImage()); + studyPostImages.forEach(studyPostImage -> { + studyPostImage.setUrl(imageUrl); + studyPost.updateImage(studyPostImage); + }); } } - - return StudyPostResDTO.PostPreviewDTO.toDTO(studyPost); } /** diff --git a/src/main/java/com/example/spot/web/controller/AuthController.java b/src/main/java/com/example/spot/web/controller/AuthController.java index 45ea453c..a1677450 100644 --- a/src/main/java/com/example/spot/web/controller/AuthController.java +++ b/src/main/java/com/example/spot/web/controller/AuthController.java @@ -2,6 +2,7 @@ import com.example.spot.api.ApiResponse; import com.example.spot.api.code.status.SuccessStatus; +import com.example.spot.security.utils.SecurityUtils; import com.example.spot.web.dto.rsa.Rsa; import com.example.spot.service.auth.AuthService; import com.example.spot.validation.annotation.TextLength; @@ -268,6 +269,19 @@ public ApiResponse login( return ApiResponse.onSuccess(SuccessStatus._MEMBER_SIGNED_IN, memberSignInDTO); } + @Tag(name = "회원 관리 API - 개발 완료", description = "회원 관리 API") + @Operation(summary = "[가입 유무 확인] 서비스 가입 유무 확인 API", + description = """ + ## [가입 유무 확인] 서비스 가입 유무 확인 API입니다. + 해당 유저가 가입 후 체크리스트를 작성 했는지 확인합니다. + * 가입 후 체크리스트를 작성한 경우 true, 작성하지 않은 경우 false를 반환합니다. + """) + @GetMapping("/check") + public ApiResponse checkIsSpotMember() { + MemberResponseDTO.CheckMemberDTO checkMemberDTO = authService.checkIsSpotMember(SecurityUtils.getCurrentUserId()); + return ApiResponse.onSuccess(SuccessStatus._MEMBER_SIGNUP_CHECK_COMPLETED, checkMemberDTO); + } + /* ----------------------------- 로그아웃 API ------------------------------------- */ @Tag(name = "회원 관리 API - 개발 중", description = "회원 관리 API") diff --git a/src/main/java/com/example/spot/web/controller/StudyPostController.java b/src/main/java/com/example/spot/web/controller/StudyPostController.java index 4ed48aff..5e5ce8c6 100644 --- a/src/main/java/com/example/spot/web/controller/StudyPostController.java +++ b/src/main/java/com/example/spot/web/controller/StudyPostController.java @@ -54,9 +54,6 @@ public class StudyPostController { public ApiResponse createPost( @PathVariable @ExistStudy Long studyId, @ModelAttribute(name = "post") @Valid StudyPostRequestDTO.PostDTO postRequestDTO) { - if (postRequestDTO.getImages() == null) { - postRequestDTO.initImages(); - } StudyPostResDTO.PostPreviewDTO postPreviewDTO = studyPostCommandService.createPost(studyId, postRequestDTO); return ApiResponse.onSuccess(SuccessStatus._STUDY_POST_CREATED, postPreviewDTO); } @@ -74,9 +71,6 @@ public ApiResponse updatePost( @PathVariable @ExistStudyPost Long postId, @ModelAttribute(name= "post") @Valid StudyPostRequestDTO.PostDTO postDTO ) { - if (postDTO.getImages() == null) { - postDTO.initImages(); - } StudyPostResDTO.PostPreviewDTO postPreviewDTO = studyPostCommandService.updatePost(studyId, postId, postDTO); return ApiResponse.onSuccess(SuccessStatus._STUDY_POST_UPDATED, postPreviewDTO); } diff --git a/src/main/java/com/example/spot/web/dto/member/MemberResponseDTO.java b/src/main/java/com/example/spot/web/dto/member/MemberResponseDTO.java index 0aa7bc02..af481101 100644 --- a/src/main/java/com/example/spot/web/dto/member/MemberResponseDTO.java +++ b/src/main/java/com/example/spot/web/dto/member/MemberResponseDTO.java @@ -47,6 +47,18 @@ public static SocialLoginSignInDTO toDTO(Boolean isSpotMember, MemberSignInDTO s .build(); } } + @Getter + @RequiredArgsConstructor(access = AccessLevel.PRIVATE) + @Builder(access = AccessLevel.PRIVATE) + public static class CheckMemberDTO { + private final Boolean isSpotMember; + + public static CheckMemberDTO toDTO(Boolean isSpotMember) { + return CheckMemberDTO.builder() + .isSpotMember(isSpotMember) + .build(); + } + } @Getter @RequiredArgsConstructor diff --git a/src/main/java/com/example/spot/web/dto/memberstudy/request/StudyPostRequestDTO.java b/src/main/java/com/example/spot/web/dto/memberstudy/request/StudyPostRequestDTO.java index a3716640..c4837f2a 100644 --- a/src/main/java/com/example/spot/web/dto/memberstudy/request/StudyPostRequestDTO.java +++ b/src/main/java/com/example/spot/web/dto/memberstudy/request/StudyPostRequestDTO.java @@ -38,12 +38,11 @@ public static class PostDTO { @Schema(description = "내용", example = "content") private String content; - @Schema(description = "이미지") - private List images; + @Schema(description = "신규 이미지") + private MultipartFile image; - public void initImages() { - this.images = new ArrayList<>(); - } + @Schema(description = "기존 이미지") + private String existingImage; } } diff --git a/src/test/java/com/example/spot/service/studypost/StudyPostCommandServiceTest.java b/src/test/java/com/example/spot/service/studypost/StudyPostCommandServiceTest.java index 5577640c..3946e925 100644 --- a/src/test/java/com/example/spot/service/studypost/StudyPostCommandServiceTest.java +++ b/src/test/java/com/example/spot/service/studypost/StudyPostCommandServiceTest.java @@ -13,6 +13,7 @@ import com.example.spot.domain.study.StudyPost; import com.example.spot.domain.study.StudyPostComment; import com.example.spot.repository.*; +import com.example.spot.service.s3.S3ImageService; import com.example.spot.web.dto.memberstudy.request.StudyPostCommentRequestDTO; import com.example.spot.web.dto.memberstudy.request.StudyPostRequestDTO; import com.example.spot.web.dto.memberstudy.response.StudyPostCommentResponseDTO; @@ -30,6 +31,7 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.multipart.MultipartFile; import java.util.ArrayList; import java.util.Collections; @@ -70,6 +72,9 @@ class StudyPostCommandServiceTest { @Mock private NotificationRepository notificationRepository; + @Mock + private S3ImageService s3ImageService; + @InjectMocks private StudyPostCommandServiceImpl studyPostCommandService; @@ -125,12 +130,15 @@ void setUp() { when(studyLikedPostRepository.existsByMemberIdAndStudyPostId(3L, 1L)) .thenReturn(true); - // Comment + // Comment when(studyPostCommentRepository.findAllByStudyPostId(1L)) .thenReturn(List.of(studyPost1Comment1, studyPost1Comment2)); when(studyPostCommentRepository.findById(1L)).thenReturn(Optional.of(studyPost1Comment1)); when(studyPostCommentRepository.findById(2L)).thenReturn(Optional.of(studyPost1Comment2)); + // S3 + when(s3ImageService.upload(any(MultipartFile.class))).thenReturn("url"); + } /*-------------------------------------------------------- 게시글 작성 ------------------------------------------------------------------------*/