diff --git a/src/main/java/com/example/spot/auth/application/legacy/AuthServiceImpl.java b/src/main/java/com/example/spot/auth/application/legacy/AuthServiceImpl.java index 4ef406ac..03eab3b2 100644 --- a/src/main/java/com/example/spot/auth/application/legacy/AuthServiceImpl.java +++ b/src/main/java/com/example/spot/auth/application/legacy/AuthServiceImpl.java @@ -1,43 +1,46 @@ package com.example.spot.auth.application.legacy; +import com.example.spot.auth.domain.RefreshToken; +import com.example.spot.auth.domain.RefreshTokenRepository; +import com.example.spot.auth.domain.RsaKey; +import com.example.spot.auth.domain.VerificationCode; +import com.example.spot.auth.domain.rsa.RSAKeyRepository; +import com.example.spot.auth.domain.verification.VerificationCodeRepository; +import com.example.spot.auth.presentation.dto.naver.NaverCallback; +import com.example.spot.auth.presentation.dto.naver.NaverMember; +import com.example.spot.auth.presentation.dto.naver.NaverOAuthToken; +import com.example.spot.auth.presentation.dto.rsa.Rsa; +import com.example.spot.auth.presentation.dto.token.TokenResponseDTO; +import com.example.spot.auth.presentation.dto.token.TokenResponseDTO.TokenDTO; import com.example.spot.common.api.code.status.ErrorStatus; import com.example.spot.common.api.exception.GeneralException; import com.example.spot.common.api.exception.handler.MemberHandler; +import com.example.spot.common.application.message.MailService; +import com.example.spot.common.security.utils.JwtTokenProvider; +import com.example.spot.common.security.utils.MemberUtils; +import com.example.spot.common.security.utils.RSAUtils; +import com.example.spot.common.security.utils.SecurityUtils; import com.example.spot.member.domain.Member; -import com.example.spot.auth.domain.RsaKey; -import com.example.spot.study.domain.repository.StudyMemberRepository; -import com.example.spot.member.domain.association.MemberThemeRepository; -import com.example.spot.member.domain.association.PreferredRegionRepository; -import com.example.spot.member.domain.association.StudyJoinReasonRepository; -import com.example.spot.member.presentation.dto.MemberRequestDTO.SignUpDetailDTO; -import com.example.spot.member.presentation.dto.MemberResponseDTO.CheckMemberDTO; -import com.example.spot.member.presentation.dto.MemberResponseDTO.NicknameDuplicateDTO; -import com.example.spot.auth.presentation.dto.rsa.Rsa; -import com.example.spot.auth.domain.RefreshToken; -import com.example.spot.auth.domain.VerificationCode; import com.example.spot.member.domain.enums.Carrier; import com.example.spot.member.domain.enums.Gender; import com.example.spot.member.domain.enums.LoginType; import com.example.spot.member.domain.enums.Status; -import com.example.spot.member.domain.MemberRepository; -import com.example.spot.auth.domain.RefreshTokenRepository; -import com.example.spot.auth.domain.rsa.RSAKeyRepository; -import com.example.spot.auth.domain.verification.VerificationCodeRepository; -import com.example.spot.common.security.utils.JwtTokenProvider; -import com.example.spot.common.security.utils.MemberUtils; -import com.example.spot.common.security.utils.RSAUtils; +import com.example.spot.member.infrastructure.MemberRepository; +import com.example.spot.member.infrastructure.PreferredRegionRepository; +import com.example.spot.member.infrastructure.PreferredThemeRepository; +import com.example.spot.member.infrastructure.StudyJoinReasonRepository; import com.example.spot.member.presentation.dto.MemberRequestDTO; +import com.example.spot.member.presentation.dto.MemberRequestDTO.SignUpDetailDTO; import com.example.spot.member.presentation.dto.MemberResponseDTO; -import com.example.spot.common.security.utils.SecurityUtils; -import com.example.spot.common.application.message.MailService; +import com.example.spot.member.presentation.dto.MemberResponseDTO.CheckMemberDTO; +import com.example.spot.member.presentation.dto.MemberResponseDTO.NicknameDuplicateDTO; import com.example.spot.member.presentation.dto.MemberResponseDTO.SocialLoginSignInDTO; -import com.example.spot.auth.presentation.dto.naver.NaverCallback; -import com.example.spot.auth.presentation.dto.naver.NaverMember; -import com.example.spot.auth.presentation.dto.naver.NaverOAuthToken; -import com.example.spot.auth.presentation.dto.token.TokenResponseDTO; -import com.example.spot.auth.presentation.dto.token.TokenResponseDTO.TokenDTO; - +import com.example.spot.study.domain.repository.StudyMemberRepository; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.security.PrivateKey; import java.security.SecureRandom; import java.time.LocalDate; @@ -48,11 +51,6 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.IntStream; - -import jakarta.persistence.EntityManager; -import jakarta.persistence.PersistenceContext; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; @@ -62,7 +60,7 @@ @Service @RequiredArgsConstructor @Transactional -public class AuthServiceImpl implements AuthService{ +public class AuthServiceImpl implements AuthService { @PersistenceContext private EntityManager entityManager; @@ -73,7 +71,7 @@ public class AuthServiceImpl implements AuthService{ private final RefreshTokenRepository refreshTokenRepository; private final VerificationCodeRepository verificationCodeRepository; - private final MemberThemeRepository memberThemeRepository; + private final PreferredThemeRepository preferredThemeRepository; private final PreferredRegionRepository preferredRegionRepository; private final StudyJoinReasonRepository studyJoinReasonRepository; @@ -87,7 +85,7 @@ public class AuthServiceImpl implements AuthService{ private String DEFAULT_PROFILE_IMAGE_URL; -/* ----------------------------- 공통 회원 관리 API ------------------------------------- */ + /* ----------------------------- 공통 회원 관리 API ------------------------------------- */ @Override public MemberResponseDTO.MemberInfoCreationDTO signUpAndPartialUpdate(SignUpDetailDTO request) { @@ -129,11 +127,12 @@ public MemberResponseDTO.InactiveMemberDTO withdraw() { return MemberResponseDTO.InactiveMemberDTO.toDTO(member); } -/* ----------------------------- 네이버 소셜로그인 API ------------------------------------- */ + /* ----------------------------- 네이버 소셜로그인 API ------------------------------------- */ /** * 네이버 로그인 인증 요청 URL로 실제 요청을 전송하고 로그인 페이지로 리디렉션하는 메서드입니다. - * @param request : HTTPServletRequest + * + * @param request : HTTPServletRequest * @param response : HttpServletResponse */ @Override @@ -147,38 +146,39 @@ public void authorizeWithNaver(HttpServletRequest request, HttpServletResponse r } /** - * SPOT 서비스에 네이버를 통해 로그인과 회원가입을 수행하는 함수입니다. - * 로그인 Callback 성공시 반환되는 naverCallback을 바탕으로 액세스 토큰을 발급받고 프로필에 접근합니다. - * 현재 SPOT에 가입되지 않은 회원이라면, 반환된 프로필 정보를 기반으로 회원 정보를 생성하여 DB에 저장합니다. - * 현재 SPOT에 가입되어 있는 회원이라면, 소셜로그인 후 토큰 정보를 반환합니다. - * @param request : HttpServletRequest - * @param response : HttpServletResponse + * SPOT 서비스에 네이버를 통해 로그인과 회원가입을 수행하는 함수입니다. 로그인 Callback 성공시 반환되는 naverCallback을 바탕으로 액세스 토큰을 발급받고 프로필에 접근합니다. 현재 + * SPOT에 가입되지 않은 회원이라면, 반환된 프로필 정보를 기반으로 회원 정보를 생성하여 DB에 저장합니다. 현재 SPOT에 가입되어 있는 회원이라면, 소셜로그인 후 토큰 정보를 반환합니다. + * + * @param request : HttpServletRequest + * @param response : HttpServletResponse * @param naverCallback : Callback 함수 성공시 반환되는 요소(code, state, error, error_description) - * @return SocialLoginSignInDTO(isSpotMember, signInDTO-토큰정보) + * @return SocialLoginSignInDTO(isSpotMember, signInDTO - 토큰정보) */ @Override - public SocialLoginSignInDTO signInWithNaver(HttpServletRequest request, HttpServletResponse response, NaverCallback naverCallback) throws Exception { + public SocialLoginSignInDTO signInWithNaver(HttpServletRequest request, HttpServletResponse response, + NaverCallback naverCallback) throws Exception { NaverMember.ResponseDTO responseDTO = naverOAuthService.getNaverMember(request, response, naverCallback); return getSocialLoginSignInDTO(responseDTO); } /** - * SPOT 서비스에 네이버를 통해 로그인과 회원가입을 수행하는 함수입니다. - * 클라이언트로부터 전달받은 액세스 토큰을 통해 프로필에 접근합니다. - * 현재 SPOT에 가입되지 않은 회원이라면, 반환된 프로필 정보를 기반으로 회원 정보를 생성하여 DB에 저장합니다. - * 현재 SPOT에 가입되어 있는 회원이라면, 소셜로그인 후 토큰 정보를 반환합니다. - * @param request : HttpServletRequest + * SPOT 서비스에 네이버를 통해 로그인과 회원가입을 수행하는 함수입니다. 클라이언트로부터 전달받은 액세스 토큰을 통해 프로필에 접근합니다. 현재 SPOT에 가입되지 않은 회원이라면, 반환된 프로필 정보를 + * 기반으로 회원 정보를 생성하여 DB에 저장합니다. 현재 SPOT에 가입되어 있는 회원이라면, 소셜로그인 후 토큰 정보를 반환합니다. + * + * @param request : HttpServletRequest * @param response : HttpServletResponse - * @return SocialLoginSignInDTO(isSpotMember, signInDTO-토큰정보) + * @return SocialLoginSignInDTO(isSpotMember, signInDTO - 토큰정보) */ @Override - public SocialLoginSignInDTO signInWithNaver(HttpServletRequest request, HttpServletResponse response, NaverOAuthToken.NaverTokenIssuanceDTO naverTokenDTO) throws Exception { + public SocialLoginSignInDTO signInWithNaver(HttpServletRequest request, HttpServletResponse response, + NaverOAuthToken.NaverTokenIssuanceDTO naverTokenDTO) throws Exception { NaverMember.ResponseDTO responseDTO = naverOAuthService.getNaverMember(request, response, naverTokenDTO); return getSocialLoginSignInDTO(responseDTO); } /** * 네이버 회원 프로필을 통해 SocialLoginSignInDTO를 생성하는 함수입니다. + * * @param responseDTO : 네이버 회원 프로필 DTO * @return SocialLoginSignInDTO (SPOT 회원 정보 및 토큰 정보) */ @@ -195,8 +195,7 @@ private SocialLoginSignInDTO getSocialLoginSignInDTO(NaverMember.ResponseDTO res refreshTokenRepository.deleteByMemberId(member.getId()); memberRepository.deleteById(member.getId()); entityManager.flush(); - } - else { + } else { throw new MemberHandler(ErrorStatus._MEMBER_EMAIL_ALREADY_EXISTS); } } @@ -215,8 +214,7 @@ private SocialLoginSignInDTO getSocialLoginSignInDTO(NaverMember.ResponseDTO res isSpotMember = Boolean.FALSE; signUpWithNaver(responseDTO); } - } - else { + } else { isSpotMember = Boolean.FALSE; signUpWithNaver(responseDTO); } @@ -244,13 +242,14 @@ private SocialLoginSignInDTO getSocialLoginSignInDTO(NaverMember.ResponseDTO res public boolean isMemberExistsByCheckList(Member member) { Long memberId = member.getId(); - return memberThemeRepository.existsByMemberId(memberId) && + return preferredThemeRepository.existsByMemberId(memberId) && preferredRegionRepository.existsByMemberId(memberId) && studyJoinReasonRepository.existsByMemberId(memberId); } /** * 현재 SPOT에 가입되어 있지 않은 회원에 한해 회원 정보를 생성하여 DB에 저장합니다. + * * @param memberDTO : naverCallback을 바탕으로 생성된 프로필 객체 */ private void signUpWithNaver(NaverMember.ResponseDTO memberDTO) { @@ -293,11 +292,12 @@ private void signUpWithNaver(NaverMember.ResponseDTO memberDTO) { memberRepository.save(member); } -/* ----------------------------- 일반 로그인/회원가입 API ------------------------------------- */ + /* ----------------------------- 일반 로그인/회원가입 API ------------------------------------- */ /** * 일반 로그인을 위한 메서드입니다. 아이디와 비밀번호를 확인한 후 토큰을 발급하는 로직을 수행합니다. - * @param signInDTO 로그인할 회원의 아이디와 비밀번호를 입력 받습니다. + * + * @param signInDTO 로그인할 회원의 아이디와 비밀번호를 입력 받습니다. * @return 로그인한 회원의 토큰 정보(액세스 & 리프레시 토큰 & 만료기간), 이메일과 회원 아이디(정수)가 반환됩니다. */ @Override @@ -358,11 +358,11 @@ public Rsa.RSAPublicKey getRSAPublicKey() throws Exception { } /** - * 인증 코드를 전송하는 메서드입니다. - * 일반 회원가입, 아이디 찾기, 비밀번호 찾기에 공통으로 적용되는 인증 메일 전송 로직입니다. - * @param request 클라이언트의 요청 정보 객체를 입력 받습니다. + * 인증 코드를 전송하는 메서드입니다. 일반 회원가입, 아이디 찾기, 비밀번호 찾기에 공통으로 적용되는 인증 메일 전송 로직입니다. + * + * @param request 클라이언트의 요청 정보 객체를 입력 받습니다. * @param response 서버의 응답 정보 객체를 입력 받습니다. - * @param email 인증 코드를 전송할 이메일을 입력 받습니다. + * @param email 인증 코드를 전송할 이메일을 입력 받습니다. */ @Override public void sendVerificationCode(HttpServletRequest request, HttpServletResponse response, String email) { @@ -379,6 +379,7 @@ public void sendVerificationCode(HttpServletRequest request, HttpServletResponse /** * 인증 코드를 생성하는 메서드입니다. + * * @return 1 ~ 9999 사이의 랜덤한 정수를 반환합니다. */ private String createCode() { @@ -388,9 +389,9 @@ private String createCode() { } /** - * 이메일로 전송된 인증 코드를 메모리에 저장된 인증 코드 객체 정보와 비교 검증하는 메서드입니다. - * 인증이 완료되면 일반 회원가입, 아이디 찾기, 비밀번호 찾기에 필요한 임시 토큰을 발급합니다. - * @param code 이메일로 전달된 인증 코드를 입력 받습니다. + * 이메일로 전송된 인증 코드를 메모리에 저장된 인증 코드 객체 정보와 비교 검증하는 메서드입니다. 인증이 완료되면 일반 회원가입, 아이디 찾기, 비밀번호 찾기에 필요한 임시 토큰을 발급합니다. + * + * @param code 이메일로 전달된 인증 코드를 입력 받습니다. * @param email 인증 코드가 전송된 이메일을 입력 받습니다. * @return 발급한 임시 토큰 정보(토큰 & 만료기간)를 반환합니다. */ @@ -418,14 +419,8 @@ public TokenResponseDTO.TempTokenDTO verifyEmail(String code, String email) { * 일반 회원가입에 사용되는 메서드입니다. * * @param rsaId - * @param signUpDTO 회원의 기본 정보를 입력 받습니다. - * name : 이름 - * frontRID : 주민번호 앞자리 - * backRID : 주민번호 뒷자리 첫 글자 - * email : 이메일 - * loginId : 아이디 - * password : 비밀번호 (RSA Key로 암호화한 값) - * pwCheck : 비밀번호 확인 + * @param signUpDTO 회원의 기본 정보를 입력 받습니다. name : 이름 frontRID : 주민번호 앞자리 backRID : 주민번호 뒷자리 첫 글자 email : 이메일 loginId : + * 아이디 password : 비밀번호 (RSA Key로 암호화한 값) pwCheck : 비밀번호 확인 * @return 가입한 회원은 자동으로 로그인되며, 회원의 토큰 정보(액세스 & 리프레시 토큰 & 만료기간), 이메일과 회원 아이디(정수)가 반환됩니다. */ @Override @@ -442,8 +437,7 @@ public MemberResponseDTO.MemberSignInDTO signUp(Long rsaId, MemberRequestDTO.Sig refreshTokenRepository.deleteByMemberId(member.getId()); memberRepository.deleteById(member.getId()); entityManager.flush(); - } - else { + } else { throw new MemberHandler(ErrorStatus._MEMBER_EMAIL_ALREADY_EXISTS); } } @@ -503,13 +497,15 @@ public MemberResponseDTO.MemberSignInDTO signUp(Long rsaId, MemberRequestDTO.Sig /** * 생성된 리프레시 토큰을 DB에 저장하는 메서드입니다. + * * @param member 리프레시 토큰을 발급한 회원을 입력 받습니다. - * @param token 저장할 토큰 정보(액세스 & 리프레시 토큰, 만료기간)를 입력 받습니다. + * @param token 저장할 토큰 정보(액세스 & 리프레시 토큰, 만료기간)를 입력 받습니다. */ private void saveRefreshToken(Member member, TokenDTO token) { - if (refreshTokenRepository.existsByMemberId(member.getId())) + if (refreshTokenRepository.existsByMemberId(member.getId())) { refreshTokenRepository.deleteAllByMemberId(member.getId()); + } RefreshToken refreshToken = RefreshToken.builder() .memberId(member.getId()) @@ -521,6 +517,7 @@ private void saveRefreshToken(Member member, TokenDTO token) { /** * 아이디 찾기에 사용되는 메서드입니다. 임시 토큰을 검증한 후 이메일로 가입된 회원 정보를 확인합니다. + * * @return 아이디/이메일, 로그인 타입, 계정 생성일시가 반환합니다. */ @Override @@ -543,6 +540,7 @@ public MemberResponseDTO.FindIdDTO findId() { /** * 비밀번호 찾기에 사용되는 메서드입니다. 임시 토큰을 검증한 후 아이디 & 이메일로 가입된 회원 정보를 확인합니다. + * * @param loginId 비밀번호를 찾고자 하는 회원의 아이디를 입력 받습니다. * @return 닉네임, 아이디, 발급된 임시 비밀번호를 반환합니다. */ @@ -641,8 +639,8 @@ public NicknameDuplicateDTO checkNicknameAvailability(String nickname) { } /** - * 임시 비밀번호를 발급하는 메서드입니다. - * 알파벳 대소문자, 숫자, 특수기호를 혼합하여 13자리 비밀번호를 생성합니다. + * 임시 비밀번호를 발급하는 메서드입니다. 알파벳 대소문자, 숫자, 특수기호를 혼합하여 13자리 비밀번호를 생성합니다. + * * @return 생성된 임시 비밀번호를 반환합니다. */ private String generateTempPassword() { @@ -662,7 +660,7 @@ private String generateTempPassword() { .collect(Collectors.joining()); } -/* ----------------------------- 로그아웃 API ------------------------------------- */ + /* ----------------------------- 로그아웃 API ------------------------------------- */ } diff --git a/src/main/java/com/example/spot/auth/application/refactor/impl/JwtTokenServiceImpl.java b/src/main/java/com/example/spot/auth/application/refactor/impl/JwtTokenServiceImpl.java index d840e56d..06fc8076 100644 --- a/src/main/java/com/example/spot/auth/application/refactor/impl/JwtTokenServiceImpl.java +++ b/src/main/java/com/example/spot/auth/application/refactor/impl/JwtTokenServiceImpl.java @@ -1,10 +1,5 @@ package com.example.spot.auth.application.refactor.impl; -import java.util.Objects; - -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - import com.example.spot.auth.application.refactor.JwtTokenService; import com.example.spot.auth.domain.RefreshToken; import com.example.spot.auth.domain.RefreshTokenRepository; @@ -13,66 +8,71 @@ import com.example.spot.common.api.exception.GeneralException; import com.example.spot.common.security.utils.JwtTokenProvider; import com.example.spot.member.domain.Member; -import com.example.spot.member.domain.MemberRepository; - +import com.example.spot.member.infrastructure.MemberRepository; +import java.util.Objects; import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Service @Transactional @RequiredArgsConstructor public class JwtTokenServiceImpl implements JwtTokenService { - private final MemberRepository memberRepository; - private final JwtTokenProvider jwtTokenProvider; - private final RefreshTokenRepository refreshTokenRepository; - - /** - * 토큰을 재발급 합니다. - * @param refreshToken 리프레시 토큰 - * @return 새로운 토큰을 생성하여 반환합니다. - * @throws GeneralException 토큰이 만료되었거나, 잘못된 토큰일 경우 발생합니다. - */ - @Override - public TokenResponseDTO.TokenDTO reissueToken(String refreshToken) { - - // 리프레시 토큰 조회 및 검증 - RefreshToken tokenInDB = refreshTokenRepository.findByToken(refreshToken) - .orElseThrow(() -> new GeneralException(ErrorStatus._INVALID_REFRESH_TOKEN)); - - // 리프레시 토큰 만료 확인 - if (jwtTokenProvider.isTokenExpired(tokenInDB.getToken())) { - refreshTokenRepository.delete(tokenInDB); - throw new GeneralException(ErrorStatus._EXPIRED_REFRESH_TOKEN); - } - - // 리프레시 토큰에서 memberId 추출 - Long memberIdByToken = jwtTokenProvider.getMemberIdByToken(refreshToken); - - // memberId로 회원 조회 - Member member = memberRepository.findById(memberIdByToken) - .orElseThrow(() -> new GeneralException(ErrorStatus._MEMBER_NOT_FOUND)); - - // 회원의 리프레시 토큰과 요청된 리프레시 토큰 비교 - if (!Objects.equals(member.getId(), memberIdByToken)) - throw new GeneralException(ErrorStatus._INVALID_JWT); - - // 토큰 재발급 - TokenResponseDTO.TokenDTO tokenDTO = jwtTokenProvider.reissueToken(refreshToken); - - // 리프레시 토큰 저장 - RefreshToken token = RefreshToken.builder() - .memberId(member.getId()) - .token(tokenDTO.getRefreshToken()) - .build(); - - // 기존 리프레시 토큰 삭제 - if (refreshTokenRepository.existsByMemberId(member.getId())) - refreshTokenRepository.deleteByMemberId(member.getId()); - - // 새로운 리프레시 토큰 저장 - refreshTokenRepository.save(token); - - // 토큰 재발급 - return tokenDTO; - } + private final MemberRepository memberRepository; + private final JwtTokenProvider jwtTokenProvider; + private final RefreshTokenRepository refreshTokenRepository; + + /** + * 토큰을 재발급 합니다. + * + * @param refreshToken 리프레시 토큰 + * @return 새로운 토큰을 생성하여 반환합니다. + * @throws GeneralException 토큰이 만료되었거나, 잘못된 토큰일 경우 발생합니다. + */ + @Override + public TokenResponseDTO.TokenDTO reissueToken(String refreshToken) { + + // 리프레시 토큰 조회 및 검증 + RefreshToken tokenInDB = refreshTokenRepository.findByToken(refreshToken) + .orElseThrow(() -> new GeneralException(ErrorStatus._INVALID_REFRESH_TOKEN)); + + // 리프레시 토큰 만료 확인 + if (jwtTokenProvider.isTokenExpired(tokenInDB.getToken())) { + refreshTokenRepository.delete(tokenInDB); + throw new GeneralException(ErrorStatus._EXPIRED_REFRESH_TOKEN); + } + + // 리프레시 토큰에서 memberId 추출 + Long memberIdByToken = jwtTokenProvider.getMemberIdByToken(refreshToken); + + // memberId로 회원 조회 + Member member = memberRepository.findById(memberIdByToken) + .orElseThrow(() -> new GeneralException(ErrorStatus._MEMBER_NOT_FOUND)); + + // 회원의 리프레시 토큰과 요청된 리프레시 토큰 비교 + if (!Objects.equals(member.getId(), memberIdByToken)) { + throw new GeneralException(ErrorStatus._INVALID_JWT); + } + + // 토큰 재발급 + TokenResponseDTO.TokenDTO tokenDTO = jwtTokenProvider.reissueToken(refreshToken); + + // 리프레시 토큰 저장 + RefreshToken token = RefreshToken.builder() + .memberId(member.getId()) + .token(tokenDTO.getRefreshToken()) + .build(); + + // 기존 리프레시 토큰 삭제 + if (refreshTokenRepository.existsByMemberId(member.getId())) { + refreshTokenRepository.deleteByMemberId(member.getId()); + } + + // 새로운 리프레시 토큰 저장 + refreshTokenRepository.save(token); + + // 토큰 재발급 + return tokenDTO; + } } diff --git a/src/main/java/com/example/spot/auth/application/refactor/impl/KakaoAuthServiceImpl.java b/src/main/java/com/example/spot/auth/application/refactor/impl/KakaoAuthServiceImpl.java index 76f9159e..5788b49e 100644 --- a/src/main/java/com/example/spot/auth/application/refactor/impl/KakaoAuthServiceImpl.java +++ b/src/main/java/com/example/spot/auth/application/refactor/impl/KakaoAuthServiceImpl.java @@ -1,11 +1,5 @@ package com.example.spot.auth.application.refactor.impl; -import java.io.IOException; - -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - import com.example.spot.auth.application.refactor.KakaoAuthService; import com.example.spot.auth.domain.RefreshToken; import com.example.spot.auth.domain.RefreshTokenRepository; @@ -18,19 +12,22 @@ import com.example.spot.common.api.exception.handler.MemberHandler; import com.example.spot.common.security.utils.JwtTokenProvider; import com.example.spot.member.domain.Member; -import com.example.spot.member.domain.MemberRepository; -import com.example.spot.member.domain.association.MemberThemeRepository; -import com.example.spot.member.domain.association.PreferredRegionRepository; -import com.example.spot.member.domain.association.StudyJoinReasonRepository; import com.example.spot.member.domain.enums.LoginType; +import com.example.spot.member.infrastructure.MemberRepository; +import com.example.spot.member.infrastructure.PreferredRegionRepository; +import com.example.spot.member.infrastructure.PreferredThemeRepository; +import com.example.spot.member.infrastructure.StudyJoinReasonRepository; import com.example.spot.member.presentation.dto.MemberResponseDTO; import com.fasterxml.jackson.core.JsonProcessingException; - import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Slf4j @Service @@ -38,223 +35,224 @@ @RequiredArgsConstructor public class KakaoAuthServiceImpl implements KakaoAuthService { - @PersistenceContext - private EntityManager entityManager; - - private final KakaoOAuthClient client; - - private final JwtTokenProvider jwtTokenProvider; - - private final HttpServletResponse response; - - private final MemberRepository memberRepository; - private final RefreshTokenRepository refreshTokenRepository; - private final MemberThemeRepository memberThemeRepository; - private final PreferredRegionRepository preferredRegionRepository; - private final StudyJoinReasonRepository studyJoinReasonRepository; - - /** - * 카카오 로그인을 통해 회원 가입 또는 로그인을 수행합니다. - * @param accessToken 카카오 OAuth 액세스 토큰 - * @return SPOT 서버에서 발급한 JWT 토큰 및 회원 정보 - * @throws JsonProcessingException 카카오 사용자 정보 파싱 중 발생하는 예외 - */ - @Override - public MemberResponseDTO.SocialLoginSignInDTO signUpByKAKAO(String accessToken) throws JsonProcessingException { - // 액세스 토큰을 사용하여 사용자 정보 요청 - ResponseEntity userInfoResponse = client.requestUserInfo(accessToken); - - // 응답에서 사용자 정보를 파싱 - KaKaoUser kaKaoUser = client.getUserInfo(userInfoResponse); - - if (memberRepository.existsByEmailAndLoginTypeNot(kaKaoUser.toMember().getEmail(), LoginType.KAKAO)){ - log.info(kaKaoUser.toMember().getEmail()); - throw new GeneralException(ErrorStatus._MEMBER_EMAIL_EXIST); - } - - Boolean isSpotMember = false; - // 사용자가 이미 존재하는지 확인 - if (memberRepository.existsByEmail(kaKaoUser.toMember().getEmail())) { - // 존재하는 경우, 사용자 정보를 가져옴 - Member member = memberRepository.findByEmail(kaKaoUser.toMember().getEmail()) - .orElseThrow(() -> new GeneralException(ErrorStatus._MEMBER_NOT_FOUND)); - - updateMemberProfileImage(member, kaKaoUser); - - if (isMemberExistsByCheckList(member)) { - isSpotMember = true; - } - - // JWT 토큰 생성 - TokenResponseDTO.TokenDTO token = jwtTokenProvider.createToken(member.getId()); - - saveRefreshToken(member, token); - - // 로그인 DTO 반환 - MemberResponseDTO.MemberSignInDTO dto = MemberResponseDTO.MemberSignInDTO.builder() - .tokens(token) - .memberId(member.getId()) - .loginType(member.getLoginType()) - .email(member.getEmail()) - .build(); - return MemberResponseDTO.SocialLoginSignInDTO.toDTO(isSpotMember, dto); - } - - - // 존재하지 않는 경우, 새로운 회원 정보 저장 - Member member = memberRepository.save(kaKaoUser.toMember()); - - // JWT 토큰 생성 - TokenResponseDTO.TokenDTO token = jwtTokenProvider.createToken(member.getId()); - - saveRefreshToken(member, token); - - // 회원 가입 DTO 반환 - MemberResponseDTO.MemberSignInDTO dto = MemberResponseDTO.MemberSignInDTO.builder() - .tokens(token) - .memberId(member.getId()) - .loginType(member.getLoginType()) - .email(member.getEmail()) - .build(); - return MemberResponseDTO.SocialLoginSignInDTO.toDTO(isSpotMember, dto); - } - - private void updateMemberProfileImage(Member member, KaKaoUser kaKaoUser) { - if (!member.getProfileImage().equals(kaKaoUser.getProperties().getProfile_image())) { - member.updateProfileImage(kaKaoUser.getProperties().getProfile_image()); - } - } - - /** - * 카카오 로그인을 테스트용으로 수행합니다. 카카오 auth accessToken 발급을 포함한 모든 내부 로직이 구현 되어 있습니다. - * @param code 카카오 로그인 요청 시 발급받은 코드 - * @return SPOT 서버에서 발급한 JWT 토큰 및 회원 정보 - * @throws JsonProcessingException 카카오 사용자 정보 파싱 중 발생하는 예외 - * @throws MemberHandler 이메일로 가입된 내역이 존재하지만, 실제로는 회원이 존재하지 않을 경우 - */ - @Override - public MemberResponseDTO.SocialLoginSignInDTO signUpByKAKAOForTest(String code) throws JsonProcessingException { - // 카카오 OAuth 서비스에서 액세스 토큰 요청 - ResponseEntity accessTokenResponse = client.requestAccessToken(code); - - // 응답에서 액세스 토큰을 파싱 - KaKaoOAuthToken.KaKaoOAuthTokenDTO oAuthToken = client.getAccessToken(accessTokenResponse); - System.out.println(oAuthToken.getAccess_token()); - - // 액세스 토큰을 사용하여 사용자 정보 요청 - ResponseEntity userInfoResponse = client.requestUserInfo(oAuthToken.getAccess_token()); - - // 응답에서 사용자 정보를 파싱 - KaKaoUser kaKaoUser = client.getUserInfo(userInfoResponse); - - // 다른 로그인 방식을 사용한 계정이 있는지 확인 - if (memberRepository.existsByEmailAndLoginTypeNot(kaKaoUser.toMember().getEmail(), LoginType.KAKAO)) { - Member member = memberRepository.findByEmail(kaKaoUser.toMember().getEmail()) - .orElseThrow(() -> new MemberHandler(ErrorStatus._MEMBER_NOT_FOUND)); - - // 탈퇴한(inactive) 회원이면 기존 정보 삭제 - if (member.getInactive() != null) { - refreshTokenRepository.deleteByMemberId(member.getId()); - memberRepository.deleteById(member.getId()); - entityManager.flush(); - } - else { - throw new GeneralException(ErrorStatus._MEMBER_EMAIL_EXIST); - } - } - - - // 사용자가 이미 존재하는지 확인 - Boolean isSpotMember = false; - if (memberRepository.existsByEmail(kaKaoUser.toMember().getEmail())) { - // 존재하는 경우, 사용자 정보를 가져옴 - Member member = memberRepository.findByEmail(kaKaoUser.toMember().getEmail()) - .orElseThrow(() -> new MemberHandler(ErrorStatus._MEMBER_NOT_FOUND)); - - updateMemberProfileImage(member, kaKaoUser); - - // 탈퇴한(inactive) 회원이면 기존 정보 삭제 - if (member.getInactive() != null) { - refreshTokenRepository.deleteByMemberId(member.getId()); - memberRepository.deleteById(member.getId()); - entityManager.flush(); - } - else { - // JWT 토큰 생성 - TokenResponseDTO.TokenDTO token = jwtTokenProvider.createToken(member.getId()); - - saveRefreshToken(member, token); - - if (isMemberExistsByCheckList(member)) { - isSpotMember = true; - } - - // 로그인 DTO 반환 - MemberResponseDTO.MemberSignInDTO memberSignInDto = MemberResponseDTO.MemberSignInDTO.builder() - .tokens(token) - .memberId(member.getId()) - .loginType(member.getLoginType()) - .email(member.getEmail()) - .build(); - - return MemberResponseDTO.SocialLoginSignInDTO.toDTO(isSpotMember, memberSignInDto); - } - } - - // 존재하지 않는 경우, 새로운 회원 정보 저장 - Member member = memberRepository.save(kaKaoUser.toMember()); - - // JWT 토큰 생성 - TokenResponseDTO.TokenDTO token = jwtTokenProvider.createToken(member.getId()); - - saveRefreshToken(member, token); - - // 회원 가입 DTO 반환 - MemberResponseDTO.MemberSignInDTO dto = MemberResponseDTO.MemberSignInDTO.builder() - .tokens(token) - .memberId(member.getId()) - .loginType(member.getLoginType()) - .email(member.getEmail()) - .build(); - return MemberResponseDTO.SocialLoginSignInDTO.toDTO(isSpotMember, dto); - } - - - /** - * redirectURL을 반환합니다. - * @throws IOException URL 리다이렉트 중 발생하는 예외 - */ - @Override - public void redirectURL() throws IOException { - // 카카오 OAuth 서비스에서 리다이렉트 URL 반환 - response.sendRedirect(client.getOauthRedirectURL()); - } - - /** - * 리프레시 토큰을 DB에 저장합니다. - * @param member 리프레시 토큰을 발급한 회원 정보 - * @param token 발급된 토큰 정보 - */ - private void saveRefreshToken(Member member, TokenResponseDTO.TokenDTO token) { - // 기존 리프레시 토큰 삭제 - if (refreshTokenRepository.existsByMemberId(member.getId())) - refreshTokenRepository.deleteAllByMemberId(member.getId()); - - // DB에 저장하기 위한 새로운 리프레시 토큰 객체 생성 - RefreshToken refreshToken = RefreshToken.builder() - .memberId(member.getId()) - .token(token.getRefreshToken()) - .build(); - - // 리프레시 토큰 저장 - refreshTokenRepository.save(refreshToken); - } - - public boolean isMemberExistsByCheckList(Member member) { - Long memberId = member.getId(); - return memberThemeRepository.existsByMemberId(memberId) && - preferredRegionRepository.existsByMemberId(memberId) && - studyJoinReasonRepository.existsByMemberId(memberId); - } + @PersistenceContext + private EntityManager entityManager; + + private final KakaoOAuthClient client; + + private final JwtTokenProvider jwtTokenProvider; + + private final HttpServletResponse response; + + private final MemberRepository memberRepository; + private final RefreshTokenRepository refreshTokenRepository; + private final PreferredThemeRepository preferredThemeRepository; + private final PreferredRegionRepository preferredRegionRepository; + private final StudyJoinReasonRepository studyJoinReasonRepository; + + /** + * 카카오 로그인을 통해 회원 가입 또는 로그인을 수행합니다. + * + * @param accessToken 카카오 OAuth 액세스 토큰 + * @return SPOT 서버에서 발급한 JWT 토큰 및 회원 정보 + * @throws JsonProcessingException 카카오 사용자 정보 파싱 중 발생하는 예외 + */ + @Override + public MemberResponseDTO.SocialLoginSignInDTO signUpByKAKAO(String accessToken) throws JsonProcessingException { + // 액세스 토큰을 사용하여 사용자 정보 요청 + ResponseEntity userInfoResponse = client.requestUserInfo(accessToken); + + // 응답에서 사용자 정보를 파싱 + KaKaoUser kaKaoUser = client.getUserInfo(userInfoResponse); + + if (memberRepository.existsByEmailAndLoginTypeNot(kaKaoUser.toMember().getEmail(), LoginType.KAKAO)) { + log.info(kaKaoUser.toMember().getEmail()); + throw new GeneralException(ErrorStatus._MEMBER_EMAIL_EXIST); + } + + Boolean isSpotMember = false; + // 사용자가 이미 존재하는지 확인 + if (memberRepository.existsByEmail(kaKaoUser.toMember().getEmail())) { + // 존재하는 경우, 사용자 정보를 가져옴 + Member member = memberRepository.findByEmail(kaKaoUser.toMember().getEmail()) + .orElseThrow(() -> new GeneralException(ErrorStatus._MEMBER_NOT_FOUND)); + + updateMemberProfileImage(member, kaKaoUser); + + if (isMemberExistsByCheckList(member)) { + isSpotMember = true; + } + + // JWT 토큰 생성 + TokenResponseDTO.TokenDTO token = jwtTokenProvider.createToken(member.getId()); + + saveRefreshToken(member, token); + + // 로그인 DTO 반환 + MemberResponseDTO.MemberSignInDTO dto = MemberResponseDTO.MemberSignInDTO.builder() + .tokens(token) + .memberId(member.getId()) + .loginType(member.getLoginType()) + .email(member.getEmail()) + .build(); + return MemberResponseDTO.SocialLoginSignInDTO.toDTO(isSpotMember, dto); + } + + // 존재하지 않는 경우, 새로운 회원 정보 저장 + Member member = memberRepository.save(kaKaoUser.toMember()); + + // JWT 토큰 생성 + TokenResponseDTO.TokenDTO token = jwtTokenProvider.createToken(member.getId()); + + saveRefreshToken(member, token); + + // 회원 가입 DTO 반환 + MemberResponseDTO.MemberSignInDTO dto = MemberResponseDTO.MemberSignInDTO.builder() + .tokens(token) + .memberId(member.getId()) + .loginType(member.getLoginType()) + .email(member.getEmail()) + .build(); + return MemberResponseDTO.SocialLoginSignInDTO.toDTO(isSpotMember, dto); + } + + private void updateMemberProfileImage(Member member, KaKaoUser kaKaoUser) { + if (!member.getProfileImage().equals(kaKaoUser.getProperties().getProfile_image())) { + member.updateProfileImage(kaKaoUser.getProperties().getProfile_image()); + } + } + + /** + * 카카오 로그인을 테스트용으로 수행합니다. 카카오 auth accessToken 발급을 포함한 모든 내부 로직이 구현 되어 있습니다. + * + * @param code 카카오 로그인 요청 시 발급받은 코드 + * @return SPOT 서버에서 발급한 JWT 토큰 및 회원 정보 + * @throws JsonProcessingException 카카오 사용자 정보 파싱 중 발생하는 예외 + * @throws MemberHandler 이메일로 가입된 내역이 존재하지만, 실제로는 회원이 존재하지 않을 경우 + */ + @Override + public MemberResponseDTO.SocialLoginSignInDTO signUpByKAKAOForTest(String code) throws JsonProcessingException { + // 카카오 OAuth 서비스에서 액세스 토큰 요청 + ResponseEntity accessTokenResponse = client.requestAccessToken(code); + + // 응답에서 액세스 토큰을 파싱 + KaKaoOAuthToken.KaKaoOAuthTokenDTO oAuthToken = client.getAccessToken(accessTokenResponse); + System.out.println(oAuthToken.getAccess_token()); + + // 액세스 토큰을 사용하여 사용자 정보 요청 + ResponseEntity userInfoResponse = client.requestUserInfo(oAuthToken.getAccess_token()); + + // 응답에서 사용자 정보를 파싱 + KaKaoUser kaKaoUser = client.getUserInfo(userInfoResponse); + + // 다른 로그인 방식을 사용한 계정이 있는지 확인 + if (memberRepository.existsByEmailAndLoginTypeNot(kaKaoUser.toMember().getEmail(), LoginType.KAKAO)) { + Member member = memberRepository.findByEmail(kaKaoUser.toMember().getEmail()) + .orElseThrow(() -> new MemberHandler(ErrorStatus._MEMBER_NOT_FOUND)); + + // 탈퇴한(inactive) 회원이면 기존 정보 삭제 + if (member.getInactive() != null) { + refreshTokenRepository.deleteByMemberId(member.getId()); + memberRepository.deleteById(member.getId()); + entityManager.flush(); + } else { + throw new GeneralException(ErrorStatus._MEMBER_EMAIL_EXIST); + } + } + + // 사용자가 이미 존재하는지 확인 + Boolean isSpotMember = false; + if (memberRepository.existsByEmail(kaKaoUser.toMember().getEmail())) { + // 존재하는 경우, 사용자 정보를 가져옴 + Member member = memberRepository.findByEmail(kaKaoUser.toMember().getEmail()) + .orElseThrow(() -> new MemberHandler(ErrorStatus._MEMBER_NOT_FOUND)); + + updateMemberProfileImage(member, kaKaoUser); + + // 탈퇴한(inactive) 회원이면 기존 정보 삭제 + if (member.getInactive() != null) { + refreshTokenRepository.deleteByMemberId(member.getId()); + memberRepository.deleteById(member.getId()); + entityManager.flush(); + } else { + // JWT 토큰 생성 + TokenResponseDTO.TokenDTO token = jwtTokenProvider.createToken(member.getId()); + + saveRefreshToken(member, token); + + if (isMemberExistsByCheckList(member)) { + isSpotMember = true; + } + + // 로그인 DTO 반환 + MemberResponseDTO.MemberSignInDTO memberSignInDto = MemberResponseDTO.MemberSignInDTO.builder() + .tokens(token) + .memberId(member.getId()) + .loginType(member.getLoginType()) + .email(member.getEmail()) + .build(); + + return MemberResponseDTO.SocialLoginSignInDTO.toDTO(isSpotMember, memberSignInDto); + } + } + + // 존재하지 않는 경우, 새로운 회원 정보 저장 + Member member = memberRepository.save(kaKaoUser.toMember()); + + // JWT 토큰 생성 + TokenResponseDTO.TokenDTO token = jwtTokenProvider.createToken(member.getId()); + + saveRefreshToken(member, token); + + // 회원 가입 DTO 반환 + MemberResponseDTO.MemberSignInDTO dto = MemberResponseDTO.MemberSignInDTO.builder() + .tokens(token) + .memberId(member.getId()) + .loginType(member.getLoginType()) + .email(member.getEmail()) + .build(); + return MemberResponseDTO.SocialLoginSignInDTO.toDTO(isSpotMember, dto); + } + + + /** + * redirectURL을 반환합니다. + * + * @throws IOException URL 리다이렉트 중 발생하는 예외 + */ + @Override + public void redirectURL() throws IOException { + // 카카오 OAuth 서비스에서 리다이렉트 URL 반환 + response.sendRedirect(client.getOauthRedirectURL()); + } + + /** + * 리프레시 토큰을 DB에 저장합니다. + * + * @param member 리프레시 토큰을 발급한 회원 정보 + * @param token 발급된 토큰 정보 + */ + private void saveRefreshToken(Member member, TokenResponseDTO.TokenDTO token) { + // 기존 리프레시 토큰 삭제 + if (refreshTokenRepository.existsByMemberId(member.getId())) { + refreshTokenRepository.deleteAllByMemberId(member.getId()); + } + + // DB에 저장하기 위한 새로운 리프레시 토큰 객체 생성 + RefreshToken refreshToken = RefreshToken.builder() + .memberId(member.getId()) + .token(token.getRefreshToken()) + .build(); + + // 리프레시 토큰 저장 + refreshTokenRepository.save(refreshToken); + } + + public boolean isMemberExistsByCheckList(Member member) { + Long memberId = member.getId(); + return preferredThemeRepository.existsByMemberId(memberId) && + preferredRegionRepository.existsByMemberId(memberId) && + studyJoinReasonRepository.existsByMemberId(memberId); + } } diff --git a/src/main/java/com/example/spot/common/application/admin/AdminServiceImpl.java b/src/main/java/com/example/spot/common/application/admin/AdminServiceImpl.java index ef0d6fed..a3533ee4 100644 --- a/src/main/java/com/example/spot/common/application/admin/AdminServiceImpl.java +++ b/src/main/java/com/example/spot/common/application/admin/AdminServiceImpl.java @@ -1,19 +1,18 @@ package com.example.spot.common.application.admin; +import com.example.spot.auth.domain.RefreshTokenRepository; import com.example.spot.common.api.code.status.ErrorStatus; import com.example.spot.common.api.exception.handler.MemberHandler; -import com.example.spot.member.domain.Member; -import com.example.spot.member.domain.MemberRepository; -import com.example.spot.auth.domain.RefreshTokenRepository; -import com.example.spot.common.security.utils.SecurityUtils; import com.example.spot.common.presentation.dto.admin.AdminResponseDTO; +import com.example.spot.common.security.utils.SecurityUtils; +import com.example.spot.member.domain.Member; +import com.example.spot.member.infrastructure.MemberRepository; +import java.time.LocalDateTime; +import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.time.LocalDateTime; -import java.util.List; - @Service @RequiredArgsConstructor @Transactional @@ -22,7 +21,7 @@ public class AdminServiceImpl implements AdminService { private final MemberRepository memberRepository; private final RefreshTokenRepository refreshTokenRepository; -/* ----------------------------- 회원 정보 관리 API ------------------------------------- */ + /* ----------------------------- 회원 정보 관리 API ------------------------------------- */ @Override public boolean getIsAdmin() { @@ -41,7 +40,8 @@ public AdminResponseDTO.DeletedMemberListDTO deleteInactiveMembers() { // 회원 삭제 List deletedMembers = memberRepository.findAllByInactiveBefore(stdTime); List deletedMemberIds = deletedMembers.stream().map(Member::getId).toList(); - AdminResponseDTO.DeletedMemberListDTO deletedMemberListDTO = AdminResponseDTO.DeletedMemberListDTO.toDTO(deletedMembers); + AdminResponseDTO.DeletedMemberListDTO deletedMemberListDTO = AdminResponseDTO.DeletedMemberListDTO.toDTO( + deletedMembers); // Token 정리 refreshTokenRepository.deleteAllByMemberIdIn(deletedMemberIds); @@ -52,6 +52,6 @@ public AdminResponseDTO.DeletedMemberListDTO deleteInactiveMembers() { return deletedMemberListDTO; } -/* ----------------------------- 신고 내역 관리 API ------------------------------------- */ + /* ----------------------------- 신고 내역 관리 API ------------------------------------- */ } diff --git a/src/main/java/com/example/spot/common/security/oauth/CustomOAuth2UserService.java b/src/main/java/com/example/spot/common/security/oauth/CustomOAuth2UserService.java index d5aaa382..969d65ec 100644 --- a/src/main/java/com/example/spot/common/security/oauth/CustomOAuth2UserService.java +++ b/src/main/java/com/example/spot/common/security/oauth/CustomOAuth2UserService.java @@ -3,14 +3,17 @@ import com.example.spot.common.api.code.status.ErrorStatus; import com.example.spot.common.api.exception.GeneralException; import com.example.spot.common.api.exception.handler.MemberHandler; +import com.example.spot.common.security.oauth.adpter.CustomOAuth2User; +import com.example.spot.common.security.oauth.adpter.OAuth2UserInfo; +import com.example.spot.common.security.utils.MemberUtils; +import com.example.spot.member.application.legacy.MemberService; import com.example.spot.member.domain.Member; import com.example.spot.member.domain.enums.Carrier; import com.example.spot.member.domain.enums.LoginType; -import com.example.spot.member.domain.MemberRepository; -import com.example.spot.common.security.utils.MemberUtils; -import com.example.spot.member.application.legacy.MemberService; -import com.example.spot.common.security.oauth.adpter.CustomOAuth2User; -import com.example.spot.common.security.oauth.adpter.OAuth2UserInfo; +import com.example.spot.member.infrastructure.MemberRepository; +import java.time.LocalDate; +import java.util.Map; +import java.util.Optional; import lombok.RequiredArgsConstructor; import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService; import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; @@ -18,17 +21,13 @@ import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.stereotype.Service; -import java.time.LocalDate; -import java.util.Map; -import java.util.Optional; - /** * CustomOAuth2UserService는 OAuth2UserService를 확장하여, 액세스 토큰을 사용하여 사용자 정보를 가져오는 역할을 합니다. - * - * loadUser() 메서드: 이 메서드는 액세스 토큰을 사용하여 구글 API에서 사용자 정보를 가져옵니다. - * 사용자 정보 처리: 가져온 사용자 정보를 사용하여 새로운 사용자를 생성하거나 기존 사용자와 연동합니다. - * + *

+ * loadUser() 메서드: 이 메서드는 액세스 토큰을 사용하여 구글 API에서 사용자 정보를 가져옵니다. 사용자 정보 처리: 가져온 사용자 정보를 사용하여 새로운 사용자를 생성하거나 기존 사용자와 + * 연동합니다. + *

* 정리하면, accessToken으로 Oauth에게 받아온 사용자 정보를 가져오고 처리하는 역할을합니다. */ @@ -52,8 +51,9 @@ public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2Authentic OAuth2UserInfo oAuthUserInfo = OAuthUserInfoFactory.getOAuthUserInfo(provider, attributes); String oauthEmail = oAuthUserInfo.getEmail(); - if (memberRepository.existsByEmailAndLoginTypeNot(oauthEmail, LoginType.GOOGLE)) + if (memberRepository.existsByEmailAndLoginTypeNot(oauthEmail, LoginType.GOOGLE)) { throw new GeneralException(ErrorStatus._MEMBER_EMAIL_EXIST); + } // Optional optionalMember = memberRepository.findByEmail(oauthEmail); diff --git a/src/main/java/com/example/spot/common/security/oauth/CustomOAuthSuccessHandler.java b/src/main/java/com/example/spot/common/security/oauth/CustomOAuthSuccessHandler.java index 777c0c49..0edfd18b 100644 --- a/src/main/java/com/example/spot/common/security/oauth/CustomOAuthSuccessHandler.java +++ b/src/main/java/com/example/spot/common/security/oauth/CustomOAuthSuccessHandler.java @@ -1,32 +1,31 @@ package com.example.spot.common.security.oauth; +import com.example.spot.auth.presentation.dto.token.TokenResponseDTO; import com.example.spot.common.api.ApiResponse; import com.example.spot.common.api.code.status.ErrorStatus; import com.example.spot.common.api.code.status.SuccessStatus; import com.example.spot.common.api.exception.handler.MemberHandler; -import com.example.spot.member.domain.Member; -import com.example.spot.member.domain.MemberRepository; +import com.example.spot.common.security.oauth.adpter.CustomOAuth2User; +import com.example.spot.common.security.oauth.adpter.google.GoogleUserInfo; import com.example.spot.common.security.utils.JwtTokenProvider; +import com.example.spot.member.domain.Member; +import com.example.spot.member.infrastructure.MemberRepository; import com.example.spot.member.presentation.dto.MemberResponseDTO; import com.example.spot.member.presentation.dto.MemberResponseDTO.SocialLoginSignInDTO; -import com.example.spot.common.security.oauth.adpter.CustomOAuth2User; -import com.example.spot.common.security.oauth.adpter.google.GoogleUserInfo; -import com.example.spot.auth.presentation.dto.token.TokenResponseDTO; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Optional; import lombok.RequiredArgsConstructor; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; import org.springframework.stereotype.Component; -import java.io.IOException; -import java.util.Optional; - /** - * Spring Security가 CustomOAuth2UserService.loadUser()에서 성공한 사용자 정보를 자동으로 SecurityContext Principle에 등록합니다. - * 따라서, OAuth로 로그인 성공 후 브라우저 재 진입시 Principle이 살아있는한 다시 로그인할 필요가 없습니다. (JwtToken과는 별개) + * Spring Security가 CustomOAuth2UserService.loadUser()에서 성공한 사용자 정보를 자동으로 SecurityContext Principle에 등록합니다. 따라서, OAuth로 + * 로그인 성공 후 브라우저 재 진입시 Principle이 살아있는한 다시 로그인할 필요가 없습니다. (JwtToken과는 별개) */ @Component @@ -37,7 +36,8 @@ public class CustomOAuthSuccessHandler extends SimpleUrlAuthenticationSuccessHan private final ObjectMapper objectMapper; @Override - public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, + Authentication authentication) throws IOException, ServletException { // CustomOAuth2UserService.loadUser()에서 저장한 Security Context에서 Principle 추출 // 추후에 Spring Security에서 제공하는 OAuth 방식을 사용할까봐, OAuth2UserInfo로 추상화했습니다. @@ -63,7 +63,7 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo // OAuth 로그인 성공한 후 응답 방식으로 포멧팅 ApiResponse apiResponse = ApiResponse.onSuccess( SuccessStatus._OK, SocialLoginSignInDTO.toDTO( - customOAuth2User.getIsSpotMember(),memberSignInDTO)); + customOAuth2User.getIsSpotMember(), memberSignInDTO)); response.setContentType("application/json"); response.setCharacterEncoding("UTF-8"); diff --git a/src/main/java/com/example/spot/member/application/legacy/MemberServiceImpl.java b/src/main/java/com/example/spot/member/application/legacy/MemberServiceImpl.java index 6e630924..02533627 100644 --- a/src/main/java/com/example/spot/member/application/legacy/MemberServiceImpl.java +++ b/src/main/java/com/example/spot/member/application/legacy/MemberServiceImpl.java @@ -1,9 +1,8 @@ package com.example.spot.member.application.legacy; -import com.example.spot.member.domain.Member; -import com.example.spot.member.domain.MemberRepository; import com.example.spot.auth.domain.CustomUserDetails; - +import com.example.spot.member.domain.Member; +import com.example.spot.member.infrastructure.MemberRepository; import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -11,11 +10,9 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; - import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; - // TODO 추후 삭제 예정 -> 구글 로그인 관련 로직이 남아있음 @Service @@ -29,6 +26,7 @@ public class MemberServiceImpl implements MemberService { /** * 회원의 정보를 조회합니다. + * * @param username 회원 식별자(ID) * @return 회원 정보 * @throws UsernameNotFoundException 회원을 찾을 수 없을 경우 @@ -40,25 +38,26 @@ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundEx // 회원 조회 Member member = memberRepository.findById(memberId) - .orElseThrow(() -> new UsernameNotFoundException("사용자를 찾을 수 없습니다.")); + .orElseThrow(() -> new UsernameNotFoundException("사용자를 찾을 수 없습니다.")); // 권한 설정 -> ROLE_USER 또는 ROLE_ADMIN List authorities = List.of( - new SimpleGrantedAuthority("ROLE_" + (member.getIsAdmin() ? "ADMIN" : "USER")) + new SimpleGrantedAuthority("ROLE_" + (member.getIsAdmin() ? "ADMIN" : "USER")) ); // CustomUserDetails 객체 생성 return CustomUserDetails.builder() - .email(member.getEmail()) - .memberId(member.getId()) - .password(member.getPassword()) - .enabled(true) - .authorities(authorities) - .build(); + .email(member.getEmail()) + .memberId(member.getId()) + .password(member.getPassword()) + .enabled(true) + .authorities(authorities) + .build(); } /** * 문자열로 입력된 회원 ID를 Long 타입으로 파싱합니다. + * * @param username 회원 ID 문자열 * @return 회원 ID * @throws UsernameNotFoundException 회원 ID 형식이 잘못된 경우 diff --git a/src/main/java/com/example/spot/member/application/refactor/impl/MemberInfoServiceImpl.java b/src/main/java/com/example/spot/member/application/refactor/impl/MemberInfoServiceImpl.java index dd597980..63b1dab1 100644 --- a/src/main/java/com/example/spot/member/application/refactor/impl/MemberInfoServiceImpl.java +++ b/src/main/java/com/example/spot/member/application/refactor/impl/MemberInfoServiceImpl.java @@ -1,18 +1,15 @@ package com.example.spot.member.application.refactor.impl; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import com.example.spot.common.api.code.status.ErrorStatus; import com.example.spot.common.api.exception.handler.MemberHandler; import com.example.spot.member.application.refactor.MemberInfoService; import com.example.spot.member.domain.Member; -import com.example.spot.member.domain.MemberRepository; -import com.example.spot.member.presentation.dto.MemberRequestDTO; +import com.example.spot.member.infrastructure.MemberRepository; +import com.example.spot.member.presentation.dto.MemberRequestDTO.MemberUpdateDTO; import com.example.spot.member.presentation.dto.MemberResponseDTO; - import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Slf4j @Service @@ -20,30 +17,39 @@ @RequiredArgsConstructor public class MemberInfoServiceImpl implements MemberInfoService { - private final MemberRepository memberRepository; - /** - * 회원의 프로필을 업데이트 합니다. - * @param memberId 변경할 회원 ID - * @param requestDTO 변경할 회원 정보 - * @return 변경 된 회원 ID와 변경 시간 - * @throws MemberHandler 회원을 찾을 수 없을 경우 - */ - @Override - public MemberResponseDTO.MemberUpdateDTO updateProfile(Long memberId, MemberRequestDTO.MemberUpdateDTO requestDTO) { - // 회원 조회 - Member member = memberRepository.findById(memberId) - .orElseThrow(() -> new MemberHandler(ErrorStatus._MEMBER_NOT_FOUND)); - - // 회원 정보 업데이트 - member.updateInfo(requestDTO); - - // 회원 정보 저장 - memberRepository.save(member); - - // 업데이트된 회원 정보 반환 - return MemberResponseDTO.MemberUpdateDTO.builder() - .memberId(member.getId()) - .updatedAt(member.getUpdatedAt()) - .build(); - } + private final MemberRepository memberRepository; + + /** + * 회원의 프로필을 업데이트 합니다. + * + * @param memberId 변경할 회원 ID + * @param requestDTO 변경할 회원 정보 + * @return 변경 된 회원 ID와 변경 시간 + * @throws MemberHandler 회원을 찾을 수 없을 경우 + */ + @Override + public MemberResponseDTO.MemberUpdateDTO updateProfile(Long memberId, MemberUpdateDTO requestDTO) { + // 회원 조회 + Member member = memberRepository.getById(memberId); + + // 회원 정보 업데이트 + member.updateInfo( + requestDTO.getName(), + requestDTO.getPhone(), + requestDTO.getBirth(), + requestDTO.getCarrier(), + requestDTO.isIdInfo(), + requestDTO.isPersonalInfo(), + requestDTO.getProfileImage()); + + // 업데이트된 회원 정보 반환 + return toUpdateDTO(member); + } + + private MemberResponseDTO.MemberUpdateDTO toUpdateDTO(Member member) { + return MemberResponseDTO.MemberUpdateDTO.builder() + .memberId(member.getId()) + .updatedAt(member.getUpdatedAt()) + .build(); + } } diff --git a/src/main/java/com/example/spot/member/application/refactor/impl/MemberPreferenceServiceImpl.java b/src/main/java/com/example/spot/member/application/refactor/impl/MemberPreferenceServiceImpl.java index e3e84aaa..d1b9253b 100644 --- a/src/main/java/com/example/spot/member/application/refactor/impl/MemberPreferenceServiceImpl.java +++ b/src/main/java/com/example/spot/member/application/refactor/impl/MemberPreferenceServiceImpl.java @@ -1,33 +1,32 @@ package com.example.spot.member.application.refactor.impl; -import java.util.List; - -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; +import static com.example.spot.member.presentation.dto.MemberResponseDTO.MemberRegionDTO.RegionDTO; import com.example.spot.common.api.code.status.ErrorStatus; import com.example.spot.common.api.exception.GeneralException; import com.example.spot.common.api.exception.handler.MemberHandler; import com.example.spot.member.application.refactor.MemberPreferenceService; import com.example.spot.member.domain.Member; -import com.example.spot.member.domain.MemberRepository; -import com.example.spot.member.domain.association.MemberTheme; -import com.example.spot.member.domain.association.MemberThemeRepository; import com.example.spot.member.domain.association.PreferredRegion; -import com.example.spot.member.domain.association.PreferredRegionRepository; +import com.example.spot.member.domain.association.PreferredTheme; import com.example.spot.member.domain.association.StudyJoinReason; -import com.example.spot.member.domain.association.StudyJoinReasonRepository; import com.example.spot.member.domain.enums.Reason; +import com.example.spot.member.infrastructure.MemberRepository; +import com.example.spot.member.infrastructure.PreferredRegionRepository; +import com.example.spot.member.infrastructure.PreferredThemeRepository; +import com.example.spot.member.infrastructure.StudyJoinReasonRepository; import com.example.spot.member.presentation.dto.MemberRequestDTO; import com.example.spot.member.presentation.dto.MemberResponseDTO; -import com.example.spot.study.domain.association.Region; +import com.example.spot.member.presentation.dto.MemberResponseDTO.MemberRegionDTO; import com.example.spot.study.domain.association.Theme; import com.example.spot.study.domain.enums.ThemeType; import com.example.spot.study.domain.repository.RegionRepository; import com.example.spot.study.domain.repository.ThemeRepository; - +import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Slf4j @Service @@ -35,247 +34,234 @@ @RequiredArgsConstructor public class MemberPreferenceServiceImpl implements MemberPreferenceService { - private final MemberRepository memberRepository; - - private final RegionRepository regionRepository; - private final ThemeRepository themeRepository; - - private final MemberThemeRepository memberThemeRepository; - private final PreferredRegionRepository preferredRegionRepository; - private final StudyJoinReasonRepository studyJoinReasonRepository; - - /** - * 회원의 테마 정보를 업데이트합니다. - * @param memberId 회원 ID - * @param requestDTO 업데이트할 테마 정보 - * @return 업데이트된 회원 정보와 업데이트 시간 - * @throws MemberHandler 회원이 존재하지 않을 경우 - * @throws GeneralException 테마가 존재하지 않을 경우 - */ - @Override - public MemberResponseDTO.MemberUpdateDTO updateTheme(Long memberId, MemberRequestDTO.MemberThemeDTO requestDTO) { - // 회원 조회 - Member member = memberRepository.findById(memberId) - .orElseThrow(() -> new MemberHandler(ErrorStatus._MEMBER_NOT_FOUND)); - - // 테마 정보 조회 - List themes = requestDTO.getThemes().stream() - .map(themeType -> themeRepository.findByThemeType(themeType).orElseThrow(() -> new GeneralException(ErrorStatus._THEME_NOT_FOUND))) - .toList(); - - // MemberTheme 객체 생성 - List memberThemes = themes.stream() - .map(theme -> MemberTheme.builder().member(member).theme(theme).build()) - .toList(); - - // 기존의 MemberTheme 삭제 - if (memberThemeRepository.existsByMemberId(member.getId())) - memberThemeRepository.deleteByMemberId(member.getId()); - - // 새로운 MemberTheme과 PreferredRegion을 저장 - memberThemeRepository.saveAll(memberThemes); - - // 회원 정보 업데이트 - member.updateThemes(memberThemes); - - // 회원 정보 저장 - memberRepository.save(member); - - // 업데이트된 회원 정보 반환 - return MemberResponseDTO.MemberUpdateDTO.builder() - .memberId(member.getId()) - .updatedAt(member.getUpdatedAt()) - .build(); - } - - /** - * 회원의 지역 정보를 업데이트합니다. - * @param memberId 회원 ID - * @param requestDTO 업데이트할 지역 정보 - * @return 업데이트된 회원 정보와 업데이트 시간 - * @throws MemberHandler 회원이 존재하지 않을 경우 - * @throws GeneralException 지역이 존재하지 않을 경우 - * - */ - @Override - public MemberResponseDTO.MemberUpdateDTO updateRegion(Long memberId, MemberRequestDTO.MemberRegionDTO requestDTO) { - // 회원 조회 - Member member = memberRepository.findById(memberId) - .orElseThrow(() -> new MemberHandler(ErrorStatus._MEMBER_NOT_FOUND)); - - // 지역 정보 조회 - List regions = requestDTO.getRegions().stream() - .map(regionCode -> regionRepository.findByCode(regionCode).orElseThrow(() -> new GeneralException(ErrorStatus._REGION_NOT_FOUND))) - .toList(); - - // PreferredRegion 객체 생성 - List preferredRegions = regions.stream() - .map(region -> PreferredRegion.builder().member(member).region(region).build()) - .toList(); - - // 기존의 MemberTheme과 PreferredRegion 삭제 - if (preferredRegionRepository.existsByMemberId(member.getId())) - preferredRegionRepository.deleteByMemberId(member.getId()); - - // 새로운 PreferredRegion을 저장 - preferredRegionRepository.saveAll(preferredRegions); - - // 회원 정보 업데이트 - member.updateRegions(preferredRegions); - - // 회원 정보 저장 - memberRepository.save(member); - - // 업데이트된 회원 정보 반환 - return MemberResponseDTO.MemberUpdateDTO.builder() - .memberId(member.getId()) - .updatedAt(member.getUpdatedAt()) - .build(); - } - - /** - * 회원의 스터디 참여 이유를 변경합니다. - * @param memberId 변경할 회원 ID - * @param requestDTO 변경할 이유 정보 - * @return 변경 된 회원 ID와 변경 시간 - * @throws MemberHandler 회원을 찾을 수 없을 경우 - */ - @Override - public MemberResponseDTO.MemberUpdateDTO updateStudyReason(Long memberId, MemberRequestDTO.MemberReasonDTO requestDTO) { - // 회원 조회 - Member member = memberRepository.findById(memberId) - .orElseThrow(() -> new MemberHandler(ErrorStatus._MEMBER_NOT_FOUND)); - - // 이유 정보 조회 - List reasons = requestDTO.getReasons().stream() - .map(Reason::fromCode) - .toList(); - - // StudyReason 객체 생성 - List studyJoinReasons = reasons.stream() - .map(reason -> StudyJoinReason.builder().member(member).reason(reason.getCode()).build()) - .toList(); - - // 기존의 StudyReason 삭제 - if (studyJoinReasonRepository.existsByMemberId(member.getId())) - studyJoinReasonRepository.deleteByMemberId(member.getId()); - - // 새로운 StudyReason 저장 - studyJoinReasonRepository.saveAll(studyJoinReasons); - - // 회원 정보 업데이트 - member.updateReasons(studyJoinReasons); - - // 회원 정보 저장 - memberRepository.save(member); - - // 업데이트된 회원 정보 반환 - return MemberResponseDTO.MemberUpdateDTO.builder() - .memberId(member.getId()) - .updatedAt(member.getUpdatedAt()) - .build(); - } - - /** - * 회원의 테마 정보를 조회합니다. - * @param memberId 조회할 회원 ID - * @return 회원의 테마 정보 및 회원 ID - * @throws MemberHandler 회원을 찾을 수 없을 경우 - * @throws MemberHandler 회원 테마 정보를 찾을 수 없을 경우 - */ - @Override - public MemberResponseDTO.MemberThemeDTO getThemes(Long memberId) { - Member member = memberRepository.findById(memberId) - .orElseThrow(() -> new MemberHandler(ErrorStatus._MEMBER_NOT_FOUND)); - - if (member.getMemberThemeList().isEmpty()) - throw new MemberHandler(ErrorStatus._MEMBER_THEME_NOT_FOUND); - - List themes = member.getMemberThemeList().stream() - .map(MemberTheme::getTheme) - .toList(); - - List themeTypes = themes.stream() - .map(Theme::getThemeType) - .toList(); - - return MemberResponseDTO.MemberThemeDTO.builder() - .memberId(member.getId()) - .themes(themeTypes) - .build(); - } - - - /** - * 회원의 지역 정보를 조회합니다. - * @param memberId 조회할 회원 ID - * @return 회원의 지역 정보 및 회원 ID - * @throws MemberHandler 회원을 찾을 수 없을 경우 - * @throws MemberHandler 회원 지역 정보를 찾을 수 없을 경우 - */ - @Override - public MemberResponseDTO.MemberRegionDTO getRegions(Long memberId) { - // 회원 조회 - Member member = memberRepository.findById(memberId) - .orElseThrow(() -> new MemberHandler(ErrorStatus._MEMBER_NOT_FOUND)); - - // 회원의 지역 정보가 없을 경우 - if (member.getRegions().isEmpty()) - throw new MemberHandler(ErrorStatus._MEMBER_REGION_NOT_FOUND); - - // 회원의 지역 정보 조회 - List regions = member.getPreferredRegionList().stream() - .map(PreferredRegion::getRegion) - .toList(); - - // 지역 정보 DTO로 변환 - List codes = regions.stream() - .map(region -> MemberResponseDTO.MemberRegionDTO.RegionDTO.builder() - .province(region.getProvince()) - .district(region.getDistrict()) - .neighborhood(region.getNeighborhood()) - .code(region.getCode()) - .build()) - .toList(); - - // 회원의 지역 정보 반환 - return MemberResponseDTO.MemberRegionDTO.builder() - .memberId(member.getId()) - .regions(codes) - .build(); - } - - /** - * 회원의 스터디 참여 이유를 조회합니다. - * @param memberId 조회할 회원 ID - * @return 회원의 스터디 참여 이유 및 회원 ID - * @throws MemberHandler 회원을 찾을 수 없을 경우 - * @throws MemberHandler 회원 스터디 참여 이유를 찾을 수 없을 경우 - */ - @Override - public MemberResponseDTO.MemberStudyReasonDTO getStudyReasons(Long memberId) { - // 회원 조회 - Member member = memberRepository.findById(memberId) - .orElseThrow(() -> new MemberHandler(ErrorStatus._MEMBER_NOT_FOUND)); - - // 회원의 스터디 참여 이유가 없을 경우 - if (member.getStudyJoinReasonList().isEmpty()) - throw new MemberHandler(ErrorStatus._MEMBER_STUDY_REASON_NOT_FOUND); - - // 회원의 스터디 참여 이유 ID 조회 - List reasonNums = member.getStudyJoinReasonList().stream() - .map(StudyJoinReason::getReason) - .toList(); - - // 이유 ID를 이유 객체로 변환 - List reasons = reasonNums.stream() - .map(Reason::fromCode) - .toList(); - - // 회원의 스터디 참여 이유 반환 - return MemberResponseDTO.MemberStudyReasonDTO.builder() - .memberId(member.getId()) - .reasons(reasons) - .build(); - } + private final MemberRepository memberRepository; + + private final RegionRepository regionRepository; + private final ThemeRepository themeRepository; + + private final PreferredThemeRepository preferredThemeRepository; + private final PreferredRegionRepository preferredRegionRepository; + private final StudyJoinReasonRepository studyJoinReasonRepository; + + /** + * 회원의 테마 정보를 업데이트합니다. + * + * @param memberId 회원 ID + * @param requestDTO 업데이트할 테마 정보 + * @return 업데이트된 회원 정보와 업데이트 시간 + * @throws MemberHandler 회원이 존재하지 않을 경우 + * @throws GeneralException 테마가 존재하지 않을 경우 + */ + @Override + public MemberResponseDTO.MemberUpdateDTO updateTheme(Long memberId, MemberRequestDTO.MemberThemeDTO requestDTO) { + // 회원 조회 + Member member = findMember(memberId); + + // 테마 정보 조회 + List preferredThemes = requestDTO.getThemes().stream() + .map(themeRepository::getByThemeType) + .map(theme -> PreferredTheme.of(member, theme)) + .toList(); + + persistPreferredThemes(member, preferredThemes); + + // 업데이트된 회원 정보 반환 + return toUpdateDTO(member); + } + + /** + * 회원의 지역 정보를 업데이트합니다. + * + * @param memberId 회원 ID + * @param requestDTO 업데이트할 지역 정보 + * @return 업데이트된 회원 정보와 업데이트 시간 + * @throws MemberHandler 회원이 존재하지 않을 경우 + * @throws GeneralException 지역이 존재하지 않을 경우 + */ + @Override + public MemberResponseDTO.MemberUpdateDTO updateRegion(Long memberId, MemberRequestDTO.MemberRegionDTO requestDTO) { + // 회원 조회 + Member member = findMember(memberId); + + // 지역 정보 조회 + List preferredRegions = requestDTO.getRegions().stream() + .map(regionRepository::getByCode) + .map(region -> PreferredRegion.of(member, region)) + .toList(); + + persistPreferredRegions(member, preferredRegions); + + // 업데이트된 회원 정보 반환 + return toUpdateDTO(member); + } + + /** + * 회원의 스터디 참여 이유를 변경합니다. + * + * @param memberId 변경할 회원 ID + * @param requestDTO 변경할 이유 정보 + * @return 변경 된 회원 ID와 변경 시간 + * @throws MemberHandler 회원을 찾을 수 없을 경우 + */ + @Override + public MemberResponseDTO.MemberUpdateDTO updateStudyReason(Long memberId, + MemberRequestDTO.MemberReasonDTO requestDTO) { + // 회원 조회 + Member member = findMember(memberId); + + // 이유 정보 조회 + List studyJoinReasons = requestDTO.getReasons().stream() + .map(Reason::fromCode) + .map(reason -> StudyJoinReason.of(member, reason.getCode())) + .toList(); + + persistStudyJoinReasons(member, studyJoinReasons); + + // 업데이트된 회원 정보 반환 + return toUpdateDTO(member); + } + + /** + * 회원의 테마 정보를 조회합니다. + * + * @param memberId 조회할 회원 ID + * @return 회원의 테마 정보 및 회원 ID + * @throws MemberHandler 회원을 찾을 수 없을 경우 + * @throws MemberHandler 회원 테마 정보를 찾을 수 없을 경우 + */ + @Override + public MemberResponseDTO.MemberThemeDTO getThemes(Long memberId) { + Member member = findMember(memberId); + + List preferredThemes = preferredThemeRepository.findAllByMemberId(member.getId()); + validatePreferredThemeIsEmpty(preferredThemes); + + List themeTypes = preferredThemes.stream() + .map(PreferredTheme::getTheme) + .map(Theme::getThemeType) + .toList(); + + return MemberResponseDTO.MemberThemeDTO.builder() + .memberId(member.getId()) + .themes(themeTypes) + .build(); + } + + + /** + * 회원의 지역 정보를 조회합니다. + * + * @param memberId 조회할 회원 ID + * @return 회원의 지역 정보 및 회원 ID + * @throws MemberHandler 회원을 찾을 수 없을 경우 + * @throws MemberHandler 회원 지역 정보를 찾을 수 없을 경우 + */ + @Override + public MemberResponseDTO.MemberRegionDTO getRegions(Long memberId) { + // 회원 조회 + Member member = findMember(memberId); + + List preferredRegions = preferredRegionRepository.findAllByMemberId(member.getId()); + validatePreferredRegionIsEmpty(preferredRegions); + + // 회원의 지역 정보 조회 + List codes = preferredRegions.stream() + .map(PreferredRegion::getRegion) + .map(region -> + RegionDTO.builder() + .province(region.getProvince()) + .district(region.getDistrict()) + .neighborhood(region.getNeighborhood()) + .code(region.getCode()) + .build()) + .toList(); + + // 회원의 지역 정보 반환 + return MemberRegionDTO.builder() + .memberId(member.getId()) + .regions(codes) + .build(); + } + + /** + * 회원의 스터디 참여 이유를 조회합니다. + * + * @param memberId 조회할 회원 ID + * @return 회원의 스터디 참여 이유 및 회원 ID + * @throws MemberHandler 회원을 찾을 수 없을 경우 + * @throws MemberHandler 회원 스터디 참여 이유를 찾을 수 없을 경우 + */ + @Override + public MemberResponseDTO.MemberStudyReasonDTO getStudyReasons(Long memberId) { + // 회원 조회 + Member member = findMember(memberId); + + List studyJoinReasons = studyJoinReasonRepository.findAllByMemberId(member.getId()); + validateStudyJoinReasonIsEmpty(studyJoinReasons); + + // 회원의 스터디 참여 이유 ID 조회 + List reasons = studyJoinReasons.stream() + .map(StudyJoinReason::getReason) + .map(Reason::fromCode) + .toList(); + + // 회원의 스터디 참여 이유 반환 + return MemberResponseDTO.MemberStudyReasonDTO.builder() + .memberId(member.getId()) + .reasons(reasons) + .build(); + } + + private Member findMember(Long memberId) { + return memberRepository.getById(memberId); + } + + private MemberResponseDTO.MemberUpdateDTO toUpdateDTO(Member member) { + return MemberResponseDTO.MemberUpdateDTO.builder() + .memberId(member.getId()) + .updatedAt(member.getUpdatedAt()) + .build(); + } + + private void persistPreferredThemes(Member member, List preferredThemes) { + if (preferredThemeRepository.existsByMemberId(member.getId())) { + preferredThemeRepository.deleteByMemberId(member.getId()); + } + preferredThemeRepository.saveAll(preferredThemes); + } + + private void persistPreferredRegions(Member member, List preferredRegions) { + if (preferredRegionRepository.existsByMemberId(member.getId())) { + preferredRegionRepository.deleteByMemberId(member.getId()); + } + preferredRegionRepository.saveAll(preferredRegions); + } + + private void persistStudyJoinReasons(Member member, List studyJoinReasons) { + if (studyJoinReasonRepository.existsByMemberId(member.getId())) { + studyJoinReasonRepository.deleteByMemberId(member.getId()); + } + studyJoinReasonRepository.saveAll(studyJoinReasons); + } + + private void validatePreferredThemeIsEmpty(List preferredThemes) { + if (preferredThemes.isEmpty()) { + throw new MemberHandler(ErrorStatus._MEMBER_THEME_NOT_FOUND); + } + } + + private void validatePreferredRegionIsEmpty(List preferredRegions) { + // 회원의 지역 정보가 없을 경우 + if (preferredRegions.isEmpty()) { + throw new MemberHandler(ErrorStatus._MEMBER_REGION_NOT_FOUND); + } + } + + private void validateStudyJoinReasonIsEmpty(List studyJoinReasons) { + // 회원의 스터디 참여 이유가 없을 경우 + if (studyJoinReasons.isEmpty()) { + throw new MemberHandler(ErrorStatus._MEMBER_STUDY_REASON_NOT_FOUND); + } + } + } diff --git a/src/main/java/com/example/spot/member/application/refactor/impl/MemberTestSupportServiceImpl.java b/src/main/java/com/example/spot/member/application/refactor/impl/MemberTestSupportServiceImpl.java index c5fe4d34..565f467b 100644 --- a/src/main/java/com/example/spot/member/application/refactor/impl/MemberTestSupportServiceImpl.java +++ b/src/main/java/com/example/spot/member/application/refactor/impl/MemberTestSupportServiceImpl.java @@ -1,10 +1,5 @@ package com.example.spot.member.application.refactor.impl; -import java.util.UUID; - -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - import com.example.spot.auth.domain.RefreshToken; import com.example.spot.auth.domain.RefreshTokenRepository; import com.example.spot.auth.presentation.dto.token.TokenResponseDTO; @@ -13,119 +8,125 @@ import com.example.spot.common.security.utils.JwtTokenProvider; import com.example.spot.member.application.refactor.MemberTestSupportService; import com.example.spot.member.domain.Member; -import com.example.spot.member.domain.MemberRepository; import com.example.spot.member.domain.enums.Gender; import com.example.spot.member.domain.enums.LoginType; import com.example.spot.member.domain.enums.Status; +import com.example.spot.member.infrastructure.MemberRepository; import com.example.spot.member.presentation.dto.MemberRequestDTO; import com.example.spot.member.presentation.dto.MemberResponseDTO; - +import java.util.UUID; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Slf4j @Service @Transactional @RequiredArgsConstructor public class MemberTestSupportServiceImpl implements MemberTestSupportService { - // JWT - private final JwtTokenProvider jwtTokenProvider; - - - // Repository - private final MemberRepository memberRepository; - private final RefreshTokenRepository refreshTokenRepository; - - /** - * 테스트 회원을 생성합니다. - * @param memberInfoListDTO 생성할 회원 정보 - * @return 생성된 회원 개인 정보와 토큰 - */ - @Override - public MemberResponseDTO.MemberTestDTO testMember(MemberRequestDTO.MemberInfoListDTO memberInfoListDTO) { - - // 회원 생성 - Member member = Member.builder() - .name(memberInfoListDTO.getName()) - .nickname(memberInfoListDTO.getNickname()) - .birth(memberInfoListDTO.getBirth()) - .gender(Gender.UNKNOWN) - .email(memberInfoListDTO.getEmail()) - .carrier(memberInfoListDTO.getCarrier()) - .phone(memberInfoListDTO.getPhone()) - .password(UUID.randomUUID().toString()) - .profileImage(memberInfoListDTO.getProfileImage()) - .personalInfo(memberInfoListDTO.isPersonalInfo()) - .idInfo(memberInfoListDTO.isIdInfo()) - .loginType(LoginType.NORMAL) - .status(Status.ON) - .build(); - - // 회원 저장 - memberRepository.save(member); - - // // 테마 정보 저장 - // updateTheme(member.getId(), memberInfoListDTO.getThemes()); - // // 지역 정보 저장 - // updateRegion(member.getId(), memberInfoListDTO.getRegions()); - - // 토큰 생성 - TokenResponseDTO.TokenDTO token = jwtTokenProvider.createToken(member.getId()); - - // 리프레시 토큰 저장 - saveRefreshToken(member, token); - - // 회원 정보 반환 - return MemberResponseDTO.MemberTestDTO.builder() - .memberId(member.getId()) - .email(member.getEmail()) - .tokens(token) - .build(); - } - - /** - * 회원에게 관리자 권한을 부여합니다. - * @param memberId 관리자로 변경할 회원 ID - * @return 변경 된 회원 ID와 변경 시간 - * @throws MemberHandler 회원을 찾을 수 없을 경우 - */ - @Override - public MemberResponseDTO.MemberUpdateDTO toAdmin(Long memberId) { - // 회원 조회 - Member member = memberRepository.findById(memberId) - .orElseThrow(() -> new MemberHandler(ErrorStatus._MEMBER_NOT_FOUND)); - - // 관리자 권한 부여 - member.toAdmin(); - - // 회원 정보 저장 - memberRepository.save(member); - - // 변경 된 회원 정보 반환 - return MemberResponseDTO.MemberUpdateDTO.builder() - .memberId(member.getId()) - .updatedAt(member.getUpdatedAt()) - .build(); - } - - - /** - * 리프레시 토큰을 DB에 저장합니다. - * @param member 리프레시 토큰을 발급한 회원 정보 - * @param token 발급된 토큰 정보 - */ - private void saveRefreshToken(Member member, TokenResponseDTO.TokenDTO token) { - // 기존 리프레시 토큰 삭제 - if (refreshTokenRepository.existsByMemberId(member.getId())) + // JWT + private final JwtTokenProvider jwtTokenProvider; + + + // Repository + private final MemberRepository memberRepository; + private final RefreshTokenRepository refreshTokenRepository; + + /** + * 테스트 회원을 생성합니다. + * + * @param memberInfoListDTO 생성할 회원 정보 + * @return 생성된 회원 개인 정보와 토큰 + */ + @Override + public MemberResponseDTO.MemberTestDTO testMember(MemberRequestDTO.MemberInfoListDTO memberInfoListDTO) { + + // 회원 생성 + Member member = Member.builder() + .name(memberInfoListDTO.getName()) + .nickname(memberInfoListDTO.getNickname()) + .birth(memberInfoListDTO.getBirth()) + .gender(Gender.UNKNOWN) + .email(memberInfoListDTO.getEmail()) + .carrier(memberInfoListDTO.getCarrier()) + .phone(memberInfoListDTO.getPhone()) + .password(UUID.randomUUID().toString()) + .profileImage(memberInfoListDTO.getProfileImage()) + .personalInfo(memberInfoListDTO.isPersonalInfo()) + .idInfo(memberInfoListDTO.isIdInfo()) + .loginType(LoginType.NORMAL) + .status(Status.ON) + .build(); + + // 회원 저장 + memberRepository.save(member); + + // // 테마 정보 저장 + // updateTheme(member.getId(), memberInfoListDTO.getThemes()); + // // 지역 정보 저장 + // updateRegion(member.getId(), memberInfoListDTO.getRegions()); + + // 토큰 생성 + TokenResponseDTO.TokenDTO token = jwtTokenProvider.createToken(member.getId()); + + // 리프레시 토큰 저장 + saveRefreshToken(member, token); + + // 회원 정보 반환 + return MemberResponseDTO.MemberTestDTO.builder() + .memberId(member.getId()) + .email(member.getEmail()) + .tokens(token) + .build(); + } + + /** + * 회원에게 관리자 권한을 부여합니다. + * + * @param memberId 관리자로 변경할 회원 ID + * @return 변경 된 회원 ID와 변경 시간 + * @throws MemberHandler 회원을 찾을 수 없을 경우 + */ + @Override + public MemberResponseDTO.MemberUpdateDTO toAdmin(Long memberId) { + // 회원 조회 + Member member = memberRepository.findById(memberId) + .orElseThrow(() -> new MemberHandler(ErrorStatus._MEMBER_NOT_FOUND)); + + // 관리자 권한 부여 + member.toAdmin(); + + // 회원 정보 저장 + memberRepository.save(member); + + // 변경 된 회원 정보 반환 + return MemberResponseDTO.MemberUpdateDTO.builder() + .memberId(member.getId()) + .updatedAt(member.getUpdatedAt()) + .build(); + } + + + /** + * 리프레시 토큰을 DB에 저장합니다. + * + * @param member 리프레시 토큰을 발급한 회원 정보 + * @param token 발급된 토큰 정보 + */ + private void saveRefreshToken(Member member, TokenResponseDTO.TokenDTO token) { + // 기존 리프레시 토큰 삭제 + if (refreshTokenRepository.existsByMemberId(member.getId())) { refreshTokenRepository.deleteAllByMemberId(member.getId()); + } - // DB에 저장하기 위한 새로운 리프레시 토큰 객체 생성 - RefreshToken refreshToken = RefreshToken.builder() - .memberId(member.getId()) - .token(token.getRefreshToken()) - .build(); + // DB에 저장하기 위한 새로운 리프레시 토큰 객체 생성 + RefreshToken refreshToken = RefreshToken.builder() + .memberId(member.getId()) + .token(token.getRefreshToken()) + .build(); - // 리프레시 토큰 저장 - refreshTokenRepository.save(refreshToken); - } + // 리프레시 토큰 저장 + refreshTokenRepository.save(refreshToken); + } } diff --git a/src/main/java/com/example/spot/member/domain/Member.java b/src/main/java/com/example/spot/member/domain/Member.java index da9c3cba..c3834090 100644 --- a/src/main/java/com/example/spot/member/domain/Member.java +++ b/src/main/java/com/example/spot/member/domain/Member.java @@ -1,43 +1,25 @@ package com.example.spot.member.domain; -import com.example.spot.comment.domain.PostComment; -import com.example.spot.comment.domain.association.LikedPostComment; -import com.example.spot.notification.domain.Notification; -import com.example.spot.post.domain.Post; -import com.example.spot.post.domain.association.LikedPost; -import com.example.spot.report.domain.MemberReport; -import com.example.spot.report.domain.PostReport; -import com.example.spot.schedule.domain.Schedule; -import com.example.spot.schedule.domain.association.Quiz; -import com.example.spot.story.domain.Story; -import com.example.spot.story.domain.association.LikedStoryComment; -import com.example.spot.story.domain.association.LikedStory; -import com.example.spot.member.domain.association.StudyJoinReason; import com.example.spot.common.entity.BaseEntity; import com.example.spot.member.domain.enums.Carrier; import com.example.spot.member.domain.enums.Gender; import com.example.spot.member.domain.enums.LoginType; import com.example.spot.member.domain.enums.Status; -import com.example.spot.schedule.domain.association.QuizSubmission; -import com.example.spot.post.domain.association.MemberScrap; -import com.example.spot.study.domain.association.StudyMember; -import com.example.spot.member.domain.association.MemberTheme; -import com.example.spot.todo.domain.ToDo; -import com.example.spot.vote.domain.Vote; -import com.example.spot.vote.domain.association.VoteParticipant; -import com.example.spot.member.domain.association.PreferredRegion; -import com.example.spot.member.domain.association.PreferredStudy; -import com.example.spot.story.domain.association.StoryComment; -import com.example.spot.member.presentation.dto.MemberRequestDTO.MemberUpdateDTO; -import jakarta.persistence.*; -import java.util.ArrayList; - -import lombok.*; - +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; import java.time.LocalDate; import java.time.LocalDateTime; -import java.util.List; - +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; import org.hibernate.annotations.DynamicInsert; import org.hibernate.annotations.DynamicUpdate; @@ -111,286 +93,31 @@ public class Member extends BaseEntity { @Enumerated(EnumType.STRING) private Status status; - //== 스터디 희망사유 ==// - @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) - @Builder.Default - private List studyJoinReasonList = new ArrayList<>(); - - //== 알림 ==// - @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) - @Builder.Default - private List notificationList = new ArrayList<>(); - - //== 해당 회원에 대한 신고 내역 ==// - @OneToMany(mappedBy = "member", cascade = CascadeType.ALL) - @Builder.Default - private List memberReportList = new ArrayList<>(); - - //== 회원이 선호하는 테마 ==// - @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) - @Builder.Default - private List memberThemeList = new ArrayList<>(); - - //== 회원의 출석 목록 ==// - @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) - @Builder.Default - private List quizSubmissionList = new ArrayList<>(); - - //== 회원이 참여하는 스터디 목록 ==// - @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) - @Builder.Default - private List studyMemberList = new ArrayList<>(); - - //== 회원이 찜한 스터디 목록 ==// - @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) - @Builder.Default - private List preferredStudyList = new ArrayList<>(); - - //== 회원이 선호하는 지역 목록 ==// - @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) - @Builder.Default - private List preferredRegionList = new ArrayList<>(); - - ////== 회원이 작성한 게시글 목록 ==// - @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) - @Builder.Default - private List postList = new ArrayList<>(); - - ////== 회원이 좋아요한 게시글 목록 ==// - @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) - @Builder.Default - private List likedPostList = new ArrayList<>(); - - ////== 회원이 선호하는 지역 목록 ==// - @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) - @Builder.Default - private List postReportList = new ArrayList<>(); - - ////== 회원이 스크랩한 게시글 목록 ==// - @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) - @Builder.Default - private List memberScrapList = new ArrayList<>(); - - ////== 회원이 작성한 게시글 댓글 목록 ==// - @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) - @Builder.Default - private List postCommentList = new ArrayList<>(); - - ////== 회원이 좋아요한 게시글 댓글 목록 ==// - @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) - @Builder.Default - private List likedCommentList = new ArrayList<>(); - - //== 회원이 작성한 스터디 게시글 목록 ==// - @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) - @Builder.Default - private List storyList = new ArrayList<>(); - - //== 회원이 좋아요한 스터디 게시글 목록 ==// - @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) - @Builder.Default - private List likedStoryList = new ArrayList<>(); - - //== 회원이 작성한 스터디 게시글 댓글 목록 ==// - @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) - @Builder.Default - private List storyCommentList = new ArrayList<>(); - - //== 회원이 좋아요한 게시글 댓글 목록 ==// - @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) - @Builder.Default - private List likedStoryCommentList = new ArrayList<>(); - - //== 회원이 생성한 투표 목록 ==// - @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) - @Builder.Default - private List voteList = new ArrayList<>(); - - //== 회원이 투표한 항목 목록 ==// - @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) - @Builder.Default - private List voteParticipantList = new ArrayList<>(); - - //== 회원이 선호하는 지역 목록 ==// - @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) - @Builder.Default - private List regions = new ArrayList<>(); - - //== 회원이 생성한 스터디 퀴즈 목록 ==// - @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) - @Builder.Default - private List quizList = new ArrayList<>(); - - //== 회원이 생성한 스터디 일정 목록 ==// - @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) - @Builder.Default - private List scheduleList = new ArrayList<>(); - - @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) - @Builder.Default - private List toDos = new ArrayList<>(); - -/* ----------------------------- 연관관계 메소드 ------------------------------------- */ - - public void addMemberStudy(StudyMember studyMember) { - studyMemberList.add(studyMember); - studyMember.setMember(this); - } - - public void addPreferredRegion(PreferredRegion preferredRegion) { - if (this.regions == null) { - this.regions = new ArrayList<>(); // 재초기화 (안정성 추가) - } - this.regions.add(preferredRegion); - preferredRegion.setMember(this); // 양방향 관계 설정 - } - - public void addMemberTheme(MemberTheme memberTheme) { - if (this.memberThemeList == null) { - this.memberThemeList = new ArrayList<>(); // 재초기화 (안정성 추가) - } - this.memberThemeList.add(memberTheme); - memberTheme.setMember(this); // 양방향 관계 설정 - } - - public void addMemberAttendance(QuizSubmission quizSubmission) { - if (this.quizSubmissionList == null) { - this.quizSubmissionList = new ArrayList<>(); - } - this.quizSubmissionList.add(quizSubmission); - quizSubmission.setMember(this); - } - - public void addVote(Vote vote) { - if (this.voteList == null) { - this.voteList = new ArrayList<>(); - } - this.voteList.add(vote); - vote.setMember(this); - } - - public void updateThemes(List memberThemes) { - this.memberThemeList.clear(); - this.memberThemeList.addAll(memberThemes); - } - public void updateRegions(List preferredRegions) { - this.preferredRegionList.clear(); - this.preferredRegionList.addAll(preferredRegions); - } - - public void updateReasons(List studyJoinReasons) { - this.studyJoinReasonList.clear(); - this.studyJoinReasonList.addAll(studyJoinReasons); - } public void updateTerm(Boolean personalInfo, Boolean idInfo) { this.personalInfo = personalInfo; this.idInfo = idInfo; } - public void updateInfo(MemberUpdateDTO req) { - this.name = req.getName(); - this.phone = req.getPhone(); - this.birth = req.getBirth(); - this.carrier = req.getCarrier(); - this.idInfo = req.isIdInfo(); - this.personalInfo = req.isPersonalInfo(); - this.profileImage = req.getProfileImage(); + public void updateInfo( + String name, String phone, LocalDate birth, + Carrier carrier, Boolean idInfo, Boolean personalInfo, String profileImage) { + this.name = name; + this.phone = phone; + this.birth = birth; + this.carrier = carrier; + this.idInfo = idInfo; + this.personalInfo = personalInfo; + this.profileImage = profileImage; } public void updateProfileImage(String profileImage) { this.profileImage = profileImage; } - public void addStudyPost(Story story) { - if (this.storyList == null) { - this.storyList = new ArrayList<>(); - } - this.storyList.add(story); - story.setMember(this); - } - - public void addStudyLikedComment(LikedStoryComment likedStoryComment) { - if (this.likedCommentList == null) { - this.likedCommentList = new ArrayList<>(); - } - this.likedStoryCommentList.add(likedStoryComment); - likedStoryComment.setMember(this); - } - - public void addMemberVote(VoteParticipant voteParticipant) { - if (this.voteParticipantList == null) { - this.voteParticipantList = new ArrayList<>(); - } - this.voteParticipantList.add(voteParticipant); - voteParticipant.setMember(this); - } - - public void updateVote(Vote vote) { - voteList.set(voteList.indexOf(vote), vote); - } - - public void deleteStudyPost(Story story) { - this.storyList.remove(story); - } - - public void updateStudyPost(Story story) { - storyList.set(storyList.indexOf(story), story); - } - - public void updateComment(StoryComment storyComment) { - storyCommentList.set(storyCommentList.indexOf(storyComment), storyComment); - } - - public void addStudyLikedPost(LikedStory likedStory) { - if (this.likedStoryList == null) { - this.likedStoryList = new ArrayList<>(); - } - this.likedStoryList.add(likedStory); - likedStory.setMember(this); - } - - public void deleteStudyLikedPost(LikedStory likedStory) { - this.likedStoryList.remove(likedStory); - } - - public void deleteStudyLikedComment(LikedStoryComment likedStoryComment) { - this.likedStoryCommentList.remove(likedStoryComment); - } - - public void addComment(StoryComment storyComment) { - this.storyCommentList.add(storyComment); - } - - public void deleteVote(Vote vote) { - this.voteList.remove(vote); - } - - public void addQuiz(Quiz quiz) { - this.quizList.add(quiz); - quiz.setMember(this); - } - - public void addSchedule(Schedule schedule) { - this.scheduleList.add(schedule); - schedule.setMember(this); - - } - - public void updateSchedule(Schedule schedule) { - scheduleList.set(scheduleList.indexOf(schedule), schedule); - } - public void toAdmin() { this.isAdmin = true; } - public void addMemberReport(MemberReport memberReport) { - this.memberReportList.add(memberReport); - } - - public void addToDoList(ToDo toDo) { - this.toDos.add(toDo); - } } diff --git a/src/main/java/com/example/spot/member/domain/association/MemberThemeRepository.java b/src/main/java/com/example/spot/member/domain/association/MemberThemeRepository.java deleted file mode 100644 index 41008620..00000000 --- a/src/main/java/com/example/spot/member/domain/association/MemberThemeRepository.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.example.spot.member.domain.association; - -import java.util.List; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -// 관심 분야 -@Repository -public interface MemberThemeRepository extends JpaRepository { - List findAllByMemberId(Long memberId); - MemberTheme findByMemberId(Long memberId); - - void deleteByMemberId(Long memberId); - boolean existsByMemberId(Long memberId); - -} diff --git a/src/main/java/com/example/spot/member/domain/association/PreferredRegion.java b/src/main/java/com/example/spot/member/domain/association/PreferredRegion.java index 625a0553..f4735553 100644 --- a/src/main/java/com/example/spot/member/domain/association/PreferredRegion.java +++ b/src/main/java/com/example/spot/member/domain/association/PreferredRegion.java @@ -1,15 +1,22 @@ package com.example.spot.member.domain.association; -import com.example.spot.study.domain.association.Region; -import com.example.spot.member.domain.Member; import com.example.spot.common.entity.BaseEntity; -import jakarta.persistence.*; +import com.example.spot.member.domain.Member; +import com.example.spot.study.domain.association.Region; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; -import lombok.Setter; import lombok.NoArgsConstructor; +import lombok.Setter; import org.hibernate.annotations.DynamicInsert; import org.hibernate.annotations.DynamicUpdate; @@ -19,7 +26,7 @@ @DynamicUpdate @DynamicInsert @NoArgsConstructor(access = AccessLevel.PROTECTED) -@AllArgsConstructor +@AllArgsConstructor(access = AccessLevel.PRIVATE) public class PreferredRegion extends BaseEntity { @Id @@ -37,5 +44,11 @@ public class PreferredRegion extends BaseEntity { @JoinColumn(name = "region_id", nullable = false) private Region region; + public static PreferredRegion of(Member member, Region region) { + return PreferredRegion.builder() + .member(member) + .region(region) + .build(); + } } diff --git a/src/main/java/com/example/spot/member/domain/association/MemberTheme.java b/src/main/java/com/example/spot/member/domain/association/PreferredTheme.java similarity index 62% rename from src/main/java/com/example/spot/member/domain/association/MemberTheme.java rename to src/main/java/com/example/spot/member/domain/association/PreferredTheme.java index b8696024..e8b65ec6 100644 --- a/src/main/java/com/example/spot/member/domain/association/MemberTheme.java +++ b/src/main/java/com/example/spot/member/domain/association/PreferredTheme.java @@ -1,15 +1,22 @@ package com.example.spot.member.domain.association; +import com.example.spot.common.entity.BaseEntity; import com.example.spot.member.domain.Member; import com.example.spot.study.domain.association.Theme; -import com.example.spot.common.entity.BaseEntity; -import jakarta.persistence.*; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; -import lombok.Setter; import lombok.NoArgsConstructor; +import lombok.Setter; import org.hibernate.annotations.DynamicInsert; import org.hibernate.annotations.DynamicUpdate; @@ -19,8 +26,8 @@ @DynamicUpdate @DynamicInsert @NoArgsConstructor(access = AccessLevel.PROTECTED) -@AllArgsConstructor -public class MemberTheme extends BaseEntity { +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class PreferredTheme extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -39,4 +46,10 @@ public class MemberTheme extends BaseEntity { @JoinColumn(name = "theme_id", nullable = false) private Theme theme; + public static PreferredTheme of(Member member, Theme theme) { + return PreferredTheme.builder() + .member(member) + .theme(theme) + .build(); + } } diff --git a/src/main/java/com/example/spot/member/domain/association/StudyJoinReason.java b/src/main/java/com/example/spot/member/domain/association/StudyJoinReason.java index 9edf69d3..b6e8fb24 100644 --- a/src/main/java/com/example/spot/member/domain/association/StudyJoinReason.java +++ b/src/main/java/com/example/spot/member/domain/association/StudyJoinReason.java @@ -2,8 +2,14 @@ import com.example.spot.common.entity.BaseEntity; import com.example.spot.member.domain.Member; - -import jakarta.persistence.*; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Builder; @@ -18,7 +24,7 @@ @DynamicUpdate @DynamicInsert @NoArgsConstructor(access = AccessLevel.PROTECTED) -@AllArgsConstructor +@AllArgsConstructor(access = AccessLevel.PRIVATE) public class StudyJoinReason extends BaseEntity { @Id @@ -33,4 +39,11 @@ public class StudyJoinReason extends BaseEntity { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "member_id", nullable = false) private Member member; + + public static StudyJoinReason of(Member member, Long reason) { + return StudyJoinReason.builder() + .member(member) + .reason(reason) + .build(); + } } diff --git a/src/main/java/com/example/spot/member/domain/validation/validator/ExistMemberValidator.java b/src/main/java/com/example/spot/member/domain/validation/validator/ExistMemberValidator.java index 03d8d300..a73fa1e3 100644 --- a/src/main/java/com/example/spot/member/domain/validation/validator/ExistMemberValidator.java +++ b/src/main/java/com/example/spot/member/domain/validation/validator/ExistMemberValidator.java @@ -1,9 +1,8 @@ package com.example.spot.member.domain.validation.validator; import com.example.spot.common.api.code.status.ErrorStatus; -import com.example.spot.member.domain.MemberRepository; import com.example.spot.member.domain.validation.annotation.ExistMember; - +import com.example.spot.member.infrastructure.MemberRepository; import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidatorContext; import lombok.RequiredArgsConstructor; @@ -21,6 +20,7 @@ public class ExistMemberValidator implements ConstraintValidator { Optional findByEmailAndLoginType(String email, LoginType loginType); List findAllByInactiveBefore(LocalDateTime stdTime); + + default Member getById(Long memberId) { + return findById(memberId).orElseThrow(() -> new MemberHandler(ErrorStatus._MEMBER_NOT_FOUND)); + } } diff --git a/src/main/java/com/example/spot/member/domain/association/PreferredRegionRepository.java b/src/main/java/com/example/spot/member/infrastructure/PreferredRegionRepository.java similarity index 77% rename from src/main/java/com/example/spot/member/domain/association/PreferredRegionRepository.java rename to src/main/java/com/example/spot/member/infrastructure/PreferredRegionRepository.java index bf37c2a5..980227c1 100644 --- a/src/main/java/com/example/spot/member/domain/association/PreferredRegionRepository.java +++ b/src/main/java/com/example/spot/member/infrastructure/PreferredRegionRepository.java @@ -1,5 +1,6 @@ -package com.example.spot.member.domain.association; +package com.example.spot.member.infrastructure; +import com.example.spot.member.domain.association.PreferredRegion; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; diff --git a/src/main/java/com/example/spot/member/domain/association/PreferredStudyRepository.java b/src/main/java/com/example/spot/member/infrastructure/PreferredStudyRepository.java similarity index 75% rename from src/main/java/com/example/spot/member/domain/association/PreferredStudyRepository.java rename to src/main/java/com/example/spot/member/infrastructure/PreferredStudyRepository.java index d6d8979b..6b276abb 100644 --- a/src/main/java/com/example/spot/member/domain/association/PreferredStudyRepository.java +++ b/src/main/java/com/example/spot/member/infrastructure/PreferredStudyRepository.java @@ -1,7 +1,7 @@ -package com.example.spot.member.domain.association; +package com.example.spot.member.infrastructure; +import com.example.spot.member.domain.association.PreferredStudy; import com.example.spot.study.domain.enums.StudyLikeStatus; - import java.util.List; import java.util.Optional; import org.springframework.data.domain.Pageable; @@ -11,7 +11,8 @@ public interface PreferredStudyRepository extends JpaRepository { List findByMemberIdAndStudyLikeStatusOrderByCreatedAtDesc( - Long memberId, StudyLikeStatus studyLikeStatus, Pageable pageable); + Long memberId, StudyLikeStatus studyLikeStatus, Pageable pageable); + Optional findByMemberIdAndStudyId(Long memberId, Long studyId); long countByMemberIdAndStudyLikeStatus(Long memberId, StudyLikeStatus studyLikeStatus); diff --git a/src/main/java/com/example/spot/member/infrastructure/PreferredThemeRepository.java b/src/main/java/com/example/spot/member/infrastructure/PreferredThemeRepository.java new file mode 100644 index 00000000..982b0d6c --- /dev/null +++ b/src/main/java/com/example/spot/member/infrastructure/PreferredThemeRepository.java @@ -0,0 +1,19 @@ +package com.example.spot.member.infrastructure; + +import com.example.spot.member.domain.association.PreferredTheme; +import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +// 관심 분야 +@Repository +public interface PreferredThemeRepository extends JpaRepository { + List findAllByMemberId(Long memberId); + + PreferredTheme findByMemberId(Long memberId); + + void deleteByMemberId(Long memberId); + + boolean existsByMemberId(Long memberId); + +} diff --git a/src/main/java/com/example/spot/member/domain/association/StudyJoinReasonRepository.java b/src/main/java/com/example/spot/member/infrastructure/StudyJoinReasonRepository.java similarity index 55% rename from src/main/java/com/example/spot/member/domain/association/StudyJoinReasonRepository.java rename to src/main/java/com/example/spot/member/infrastructure/StudyJoinReasonRepository.java index 7d93ab4a..14fdc8f8 100644 --- a/src/main/java/com/example/spot/member/domain/association/StudyJoinReasonRepository.java +++ b/src/main/java/com/example/spot/member/infrastructure/StudyJoinReasonRepository.java @@ -1,10 +1,15 @@ -package com.example.spot.member.domain.association; +package com.example.spot.member.infrastructure; +import com.example.spot.member.domain.association.StudyJoinReason; +import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; public interface StudyJoinReasonRepository extends JpaRepository { + List findAllByMemberId(Long memberId); + boolean existsByMemberId(Long memberId); + void deleteByMemberId(Long memberId); } diff --git a/src/main/java/com/example/spot/post/application/command/impl/LikePostCommentUseCaseImpl.java b/src/main/java/com/example/spot/post/application/command/impl/LikePostCommentUseCaseImpl.java index 806bdb26..18bb71c4 100644 --- a/src/main/java/com/example/spot/post/application/command/impl/LikePostCommentUseCaseImpl.java +++ b/src/main/java/com/example/spot/post/application/command/impl/LikePostCommentUseCaseImpl.java @@ -9,10 +9,9 @@ import com.example.spot.common.api.exception.handler.MemberHandler; import com.example.spot.common.api.exception.handler.PostHandler; import com.example.spot.member.domain.Member; -import com.example.spot.member.domain.MemberRepository; +import com.example.spot.member.infrastructure.MemberRepository; import com.example.spot.post.application.command.LikePostCommentUseCase; import com.example.spot.post.application.query.GetLikedPostCommentUseCase; -import com.example.spot.post.application.query.GetLikedPostUseCase; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -30,12 +29,13 @@ public class LikePostCommentUseCaseImpl implements LikePostCommentUseCase { /** * 게시글 댓글에 좋아요를 합니다. + * * @param commentId 좋아요할 댓글 ID - * @param memberId 회원 ID + * @param memberId 회원 ID * @return 좋아요한 댓글 ID와 게시글의 현재 좋아요와 싫어요 수 - * @throws PostHandler 댓글을 찾을 수 없는 경우 + * @throws PostHandler 댓글을 찾을 수 없는 경우 * @throws MemberHandler 회원을 찾을 수 없는 경우 - * @throws PostHandler 이미 해당 댓글에 좋아요를 한 경우 + * @throws PostHandler 이미 해당 댓글에 좋아요를 한 경우 */ @Transactional @Override @@ -74,12 +74,13 @@ public CommentLikeResponse likeComment(Long commentId, Long memberId) { /** * 게시글 댓글에 좋아요를 취소합니다. + * * @param commentId 좋아요 취소할 댓글 ID - * @param memberId 회원 ID + * @param memberId 회원 ID * @return 좋아요 취소한 댓글 ID와 게시글의 현재 좋아요와 싫어요 수 - * @throws PostHandler 댓글을 찾을 수 없는 경우 + * @throws PostHandler 댓글을 찾을 수 없는 경우 * @throws MemberHandler 회원을 찾을 수 없는 경우 - * @throws PostHandler 좋아요를 하지 않은 댓글일 경우 + * @throws PostHandler 좋아요를 하지 않은 댓글일 경우 */ @Transactional @Override @@ -93,7 +94,8 @@ public CommentLikeResponse cancelCommentLike(Long commentId, Long memberId) { .orElseThrow(() -> new MemberHandler(ErrorStatus._MEMBER_NOT_FOUND)); // 댓글 좋아요 여부 확인 - LikedPostComment likedPostComment = likedPostCommentRepository.findByMemberIdAndPostCommentIdAndIsLikedTrue(memberId, commentId) + LikedPostComment likedPostComment = likedPostCommentRepository.findByMemberIdAndPostCommentIdAndIsLikedTrue( + memberId, commentId) .orElseThrow(() -> new PostHandler(ErrorStatus._POST_COMMENT_NOT_LIKED)); // 댓글 좋아요 객체 삭제 및 즉시 반영 @@ -112,12 +114,13 @@ public CommentLikeResponse cancelCommentLike(Long commentId, Long memberId) { /** * 게시글 댓글에 싫어요를 합니다. + * * @param commentId 싫어요할 댓글 ID - * @param memberId 회원 ID + * @param memberId 회원 ID * @return 싫어요한 댓글 ID와 게시글의 현재 좋아요와 싫어요 수 - * @throws PostHandler 댓글을 찾을 수 없는 경우 + * @throws PostHandler 댓글을 찾을 수 없는 경우 * @throws MemberHandler 회원을 찾을 수 없는 경우 - * @throws PostHandler 이미 해당 댓글에 싫어요를 한 경우 + * @throws PostHandler 이미 해당 댓글에 싫어요를 한 경우 */ @Transactional @Override @@ -156,12 +159,13 @@ public CommentLikeResponse dislikeComment(Long commentId, Long memberId) { /** * 게시글 댓글에 싫어요를 취소합니다. + * * @param commentId 싫어요 취소할 댓글 ID - * @param memberId 회원 ID + * @param memberId 회원 ID * @return 싫어요 취소한 댓글 ID와 게시글의 현재 좋아요와 싫어요 수 - * @throws PostHandler 댓글을 찾을 수 없는 경우 + * @throws PostHandler 댓글을 찾을 수 없는 경우 * @throws MemberHandler 회원을 찾을 수 없는 경우 - * @throws PostHandler 싫어요를 하지 않은 댓글일 경우 + * @throws PostHandler 싫어요를 하지 않은 댓글일 경우 */ @Transactional @Override @@ -175,7 +179,8 @@ public CommentLikeResponse cancelCommentDislike(Long commentId, Long memberId) { .orElseThrow(() -> new MemberHandler(ErrorStatus._MEMBER_NOT_FOUND)); // 싫어요 여부 확인 - LikedPostComment dislikedPostComment = likedPostCommentRepository.findByMemberIdAndPostCommentIdAndIsLikedFalse(memberId, commentId) + LikedPostComment dislikedPostComment = likedPostCommentRepository.findByMemberIdAndPostCommentIdAndIsLikedFalse( + memberId, commentId) .orElseThrow(() -> new PostHandler(ErrorStatus._POST_COMMENT_NOT_DISLIKED)); // 싫어요 객체 삭제 및 즉시 반영 diff --git a/src/main/java/com/example/spot/post/application/command/impl/LikePostUseCaseImpl.java b/src/main/java/com/example/spot/post/application/command/impl/LikePostUseCaseImpl.java index bc798a5c..b17d16c3 100644 --- a/src/main/java/com/example/spot/post/application/command/impl/LikePostUseCaseImpl.java +++ b/src/main/java/com/example/spot/post/application/command/impl/LikePostUseCaseImpl.java @@ -4,7 +4,7 @@ import com.example.spot.common.api.exception.handler.MemberHandler; import com.example.spot.common.api.exception.handler.PostHandler; import com.example.spot.member.domain.Member; -import com.example.spot.member.domain.MemberRepository; +import com.example.spot.member.infrastructure.MemberRepository; import com.example.spot.post.application.command.LikePostUseCase; import com.example.spot.post.application.query.GetLikedPostUseCase; import com.example.spot.post.domain.Post; @@ -29,12 +29,13 @@ public class LikePostUseCaseImpl implements LikePostUseCase { /** * 게시글에 좋아요를 합니다. - * @param postId 좋아요할 게시글 ID + * + * @param postId 좋아요할 게시글 ID * @param memberId 회원 ID * @return 좋아요한 게시글 ID와 게시글의 현재 좋아요 수 * @throws MemberHandler 회원을 찾을 수 없는 경우 - * @throws PostHandler 게시글을 찾을 수 없는 경우 - * @throws PostHandler 이미 해당 게시글에 좋아요를 누른 경우 + * @throws PostHandler 게시글을 찾을 수 없는 경우 + * @throws PostHandler 이미 해당 게시글에 좋아요를 누른 경우 */ @Transactional @Override @@ -70,12 +71,13 @@ public PostLikeResponse likePost(Long postId, Long memberId) { /** * 게시글 좋아요를 취소합니다. - * @param postId 좋아요를 취소할 게시글 ID + * + * @param postId 좋아요를 취소할 게시글 ID * @param memberId 회원 ID * @return 좋아요를 취소한 게시글 ID와 게시글의 현재 좋아요 수 * @throws MemberHandler 회원을 찾을 수 없는 경우 - * @throws PostHandler 게시글을 찾을 수 없는 경우 - * @throws PostHandler 좋아요 하지 않은 게시글일 경우 + * @throws PostHandler 게시글을 찾을 수 없는 경우 + * @throws PostHandler 좋아요 하지 않은 게시글일 경우 */ @Transactional @Override diff --git a/src/main/java/com/example/spot/post/application/command/impl/ManagePostCommentUseCaseImpl.java b/src/main/java/com/example/spot/post/application/command/impl/ManagePostCommentUseCaseImpl.java index 7b215ca3..b19896c4 100644 --- a/src/main/java/com/example/spot/post/application/command/impl/ManagePostCommentUseCaseImpl.java +++ b/src/main/java/com/example/spot/post/application/command/impl/ManagePostCommentUseCaseImpl.java @@ -8,7 +8,7 @@ import com.example.spot.common.api.exception.handler.MemberHandler; import com.example.spot.common.api.exception.handler.PostHandler; import com.example.spot.member.domain.Member; -import com.example.spot.member.domain.MemberRepository; +import com.example.spot.member.infrastructure.MemberRepository; import com.example.spot.post.application.command.ManagePostCommentUseCase; import com.example.spot.post.domain.Post; import com.example.spot.post.domain.PostRepository; @@ -27,11 +27,12 @@ public class ManagePostCommentUseCaseImpl implements ManagePostCommentUseCase { /** * 게시글에 댓글을 생성합니다. - * @param postId 댓글을 작성할 게시글 ID + * + * @param postId 댓글을 작성할 게시글 ID * @param memberId 회원 ID - * @param request 작성할 댓글 정보 + * @param request 작성할 댓글 정보 * @return 작성된 댓글 정보와 익명여부 반환 - * @throws PostHandler 게시글을 찾을 수 없는 경우 + * @throws PostHandler 게시글을 찾을 수 없는 경우 * @throws MemberHandler 회원을 찾을 수 없는 경우 */ @Transactional diff --git a/src/main/java/com/example/spot/post/application/command/impl/ManagePostUseCaseImpl.java b/src/main/java/com/example/spot/post/application/command/impl/ManagePostUseCaseImpl.java index 13c4ee57..ceba4a5f 100644 --- a/src/main/java/com/example/spot/post/application/command/impl/ManagePostUseCaseImpl.java +++ b/src/main/java/com/example/spot/post/application/command/impl/ManagePostUseCaseImpl.java @@ -7,7 +7,7 @@ import com.example.spot.common.presentation.dto.util.response.ImageResponse.ImageUploadResponse; import com.example.spot.common.presentation.dto.util.response.ImageResponse.Images; import com.example.spot.member.domain.Member; -import com.example.spot.member.domain.MemberRepository; +import com.example.spot.member.infrastructure.MemberRepository; import com.example.spot.post.application.command.ManagePostUseCase; import com.example.spot.post.domain.Post; import com.example.spot.post.domain.PostRepository; @@ -33,11 +33,12 @@ public class ManagePostUseCaseImpl implements ManagePostUseCase { /** * 게시글을 생성합니다. - * @param memberId 게시글을 작성하는 회원 ID + * + * @param memberId 게시글을 작성하는 회원 ID * @param postCreateRequest 생성할 게시글 정보 * @return 생성된 게시글 정보 * @throws MemberHandler 회원을 찾을 수 없는 경우 - * @throws PostHandler 관리자 권한이 없는 경우 (관리자만 공지글 작성 가능) + * @throws PostHandler 관리자 권한이 없는 경우 (관리자만 공지글 작성 가능) */ @Transactional @Override @@ -77,8 +78,9 @@ private List getImageUrls(MultipartFile imageFile) { /** * 게시글 객체를 생성합니다. + * * @param postCreateRequest 생성할 게시글 정보 - * @param currentMember 게시글을 작성하는 회원 정보 + * @param currentMember 게시글을 작성하는 회원 정보 * @return 생성된 게시글 객체 */ private Post createPostEntity(PostCreateRequest postCreateRequest, Member currentMember, List images) { @@ -101,14 +103,15 @@ private Post createPostEntity(PostCreateRequest postCreateRequest, Member curren /** * 게시글을 수정합니다. - * @param memberId 게시글을 수정하는 회원 ID - * @param postId 변경할 게시글 ID + * + * @param memberId 게시글을 수정하는 회원 ID + * @param postId 변경할 게시글 ID * @param postUpdateRequest 수정할 게시글 정보 * @return 수정된 게시글 정보 * @throws MemberHandler 회원을 찾을 수 없는 경우 - * @throws PostHandler 게시글을 찾을 수 없는 경우 - * @throws PostHandler 현재 수정하는 회원과 게시글 작성자가 일치하지 않을 경우 - * @throws PostHandler 관리자 권한이 없는 경우 (관리자만 공지글 수정 가능) + * @throws PostHandler 게시글을 찾을 수 없는 경우 + * @throws PostHandler 현재 수정하는 회원과 게시글 작성자가 일치하지 않을 경우 + * @throws PostHandler 관리자 권한이 없는 경우 (관리자만 공지글 수정 가능) */ @Transactional @Override @@ -141,11 +144,12 @@ public PostCreateResponse updatePost(Long memberId, Long postId, PostUpdateReque /** * 게시글을 삭제합니다. + * * @param memberId 게시글을 수정하는 회원 ID - * @param postId 변경할 게시글 ID + * @param postId 변경할 게시글 ID * @throws MemberHandler 회원을 찾을 수 없는 경우 - * @throws PostHandler 게시글을 찾을 수 없는 경우 - * @throws PostHandler 현재 삭제하는 회원과 게시글 작성자가 일치하지 않을 경우 + * @throws PostHandler 게시글을 찾을 수 없는 경우 + * @throws PostHandler 현재 삭제하는 회원과 게시글 작성자가 일치하지 않을 경우 */ @Transactional @Override diff --git a/src/main/java/com/example/spot/post/application/command/impl/ScrapPostUseCaseImpl.java b/src/main/java/com/example/spot/post/application/command/impl/ScrapPostUseCaseImpl.java index df35c7fe..b89b3024 100644 --- a/src/main/java/com/example/spot/post/application/command/impl/ScrapPostUseCaseImpl.java +++ b/src/main/java/com/example/spot/post/application/command/impl/ScrapPostUseCaseImpl.java @@ -6,7 +6,7 @@ import com.example.spot.common.api.exception.handler.MemberHandler; import com.example.spot.common.api.exception.handler.PostHandler; import com.example.spot.member.domain.Member; -import com.example.spot.member.domain.MemberRepository; +import com.example.spot.member.infrastructure.MemberRepository; import com.example.spot.post.application.command.ScrapPostUseCase; import com.example.spot.post.domain.Post; import com.example.spot.post.domain.PostRepository; @@ -31,12 +31,13 @@ public class ScrapPostUseCaseImpl implements ScrapPostUseCase { /** * 게시글을 스크랩 합니다. - * @param postId 스크랩할 게시글 ID + * + * @param postId 스크랩할 게시글 ID * @param memberId 회원 ID * @return 스크랩한 게시글 ID와 스크랩 수 - * @throws PostHandler 게시글을 찾을 수 없는 경우 + * @throws PostHandler 게시글을 찾을 수 없는 경우 * @throws MemberHandler 회원을 찾을 수 없는 경우 - * @throws PostHandler 이미 해당 게시글을 스크랩 한 경우 + * @throws PostHandler 이미 해당 게시글을 스크랩 한 경우 */ @Override public ScrapPostResponse scrapPost(Long postId, Long memberId) { @@ -73,12 +74,13 @@ public ScrapPostResponse scrapPost(Long postId, Long memberId) { /** * 게시글 스크랩을 취소합니다. - * @param postId 스크랩 취소할 게시글 ID + * + * @param postId 스크랩 취소할 게시글 ID * @param memberId 회원 ID * @return 스크랩 취소한 게시글 ID와 스크랩 수 - * @throws PostHandler 게시글을 찾을 수 없는 경우 + * @throws PostHandler 게시글을 찾을 수 없는 경우 * @throws MemberHandler 회원을 찾을 수 없는 경우 - * @throws PostHandler 스크랩하지 않은 게시글인 경우 + * @throws PostHandler 스크랩하지 않은 게시글인 경우 */ @Override public ScrapPostResponse cancelPostScrap(Long postId, Long memberId) { @@ -110,6 +112,7 @@ public ScrapPostResponse cancelPostScrap(Long postId, Long memberId) { /** * 게시글 스크랩 여러개를 한번에 취소합니다. + * * @param request 취소할 스크랩 ID 리스트 * @return 스크랩 취소 결과 반환 */ diff --git a/src/main/java/com/example/spot/report/application/ReportCommandServiceImpl.java b/src/main/java/com/example/spot/report/application/ReportCommandServiceImpl.java index 855f78ac..caf15c9b 100644 --- a/src/main/java/com/example/spot/report/application/ReportCommandServiceImpl.java +++ b/src/main/java/com/example/spot/report/application/ReportCommandServiceImpl.java @@ -6,13 +6,19 @@ import com.example.spot.common.api.exception.handler.StudyHandler; import com.example.spot.common.security.utils.SecurityUtils; import com.example.spot.member.domain.Member; -import com.example.spot.member.domain.MemberRepository; +import com.example.spot.member.infrastructure.MemberRepository; import com.example.spot.member.presentation.dto.MemberResponseDTO; import com.example.spot.post.domain.Post; import com.example.spot.post.domain.PostRepository; import com.example.spot.post.domain.enums.PostStatus; -import com.example.spot.report.domain.*; +import com.example.spot.report.domain.MemberReport; +import com.example.spot.report.domain.MemberReportRepository; +import com.example.spot.report.domain.PostReport; +import com.example.spot.report.domain.PostReportRepository; +import com.example.spot.report.domain.StoryReport; +import com.example.spot.report.domain.StoryReportRepository; import com.example.spot.report.presentation.dto.PostReportDTO; +import com.example.spot.report.presentation.dto.StudyMemberReportDTO; import com.example.spot.story.domain.Story; import com.example.spot.story.domain.StoryRepository; import com.example.spot.story.web.dto.response.StoryResponseDTO; @@ -20,7 +26,6 @@ import com.example.spot.study.domain.StudyRepository; import com.example.spot.study.domain.enums.StudyApplicationStatus; import com.example.spot.study.domain.repository.StudyMemberRepository; -import com.example.spot.report.presentation.dto.StudyMemberReportDTO; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -70,13 +75,15 @@ public PostReportDTO reportPost(Long postId, Long memberId) { /** * 스터디원을 신고하고 신고 내역을 저장하는 메서드입니다. - * @param studyId 타겟 스터디의 아이디를 입력 받습니다. - * @param memberId 신고할 회원의 아이디를 입력 받습니다. + * + * @param studyId 타겟 스터디의 아이디를 입력 받습니다. + * @param memberId 신고할 회원의 아이디를 입력 받습니다. * @param studyMemberReportDTO 신고 사유를 입력 받습니다. * @return 신고를 당한 회원의 아이디와 이름을 반환합니다. */ @Override - public MemberResponseDTO.ReportedMemberDTO reportStudyMember(Long studyId, Long memberId, StudyMemberReportDTO studyMemberReportDTO) { + public MemberResponseDTO.ReportedMemberDTO reportStudyMember(Long studyId, Long memberId, + StudyMemberReportDTO studyMemberReportDTO) { //=== Exception ===// Long reporterId = SecurityUtils.getCurrentUserId(); @@ -102,7 +109,6 @@ public MemberResponseDTO.ReportedMemberDTO reportStudyMember(Long studyId, Long throw new StudyHandler(ErrorStatus._STUDY_MEMBER_REPORT_INVALID); } - //=== Feature ===// MemberReport memberReport = MemberReport.builder() .content(studyMemberReportDTO.getContent()) @@ -110,15 +116,15 @@ public MemberResponseDTO.ReportedMemberDTO reportStudyMember(Long studyId, Long .build(); memberReport = memberReportRepository.save(memberReport); - member.addMemberReport(memberReport); return MemberResponseDTO.ReportedMemberDTO.toDTO(member); } /** * 스터디 게시글을 신고하고 신고 내역을 저장하는 메서드입니다. + * * @param studyId 타겟 스터디의 아이디를 입력합니다. - * @param postId 신고할 게시글의 아이디를 입력합니다. + * @param postId 신고할 게시글의 아이디를 입력합니다. * @return 신고를 당한 스터디 게시글의 아이디와 제목을 반환합니다. */ @Override diff --git a/src/main/java/com/example/spot/schedule/application/ScheduleCommandServiceImpl.java b/src/main/java/com/example/spot/schedule/application/ScheduleCommandServiceImpl.java index a81f802a..342690b3 100644 --- a/src/main/java/com/example/spot/schedule/application/ScheduleCommandServiceImpl.java +++ b/src/main/java/com/example/spot/schedule/application/ScheduleCommandServiceImpl.java @@ -5,7 +5,7 @@ import com.example.spot.common.api.exception.handler.StudyHandler; import com.example.spot.common.security.utils.SecurityUtils; import com.example.spot.member.domain.Member; -import com.example.spot.member.domain.MemberRepository; +import com.example.spot.member.infrastructure.MemberRepository; import com.example.spot.notification.domain.Notification; import com.example.spot.notification.domain.NotificationRepository; import com.example.spot.notification.domain.enums.NotifyType; @@ -15,22 +15,21 @@ import com.example.spot.schedule.domain.association.QuizSubmission; import com.example.spot.schedule.domain.repository.QuizRepository; import com.example.spot.schedule.domain.repository.QuizSubmissionRepository; +import com.example.spot.schedule.presentation.dto.request.QuizRequestDTO; +import com.example.spot.schedule.presentation.dto.request.ScheduleRequestDTO; +import com.example.spot.schedule.presentation.dto.response.QuizResponseDTO; +import com.example.spot.schedule.presentation.dto.response.ScheduleResponseDTO; import com.example.spot.study.domain.Study; import com.example.spot.study.domain.StudyRepository; import com.example.spot.study.domain.association.StudyMember; import com.example.spot.study.domain.enums.StudyApplicationStatus; import com.example.spot.study.domain.repository.StudyMemberRepository; -import com.example.spot.schedule.presentation.dto.request.ScheduleRequestDTO; -import com.example.spot.schedule.presentation.dto.request.QuizRequestDTO; -import com.example.spot.schedule.presentation.dto.response.ScheduleResponseDTO; -import com.example.spot.schedule.presentation.dto.response.QuizResponseDTO; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - import java.time.LocalDate; import java.time.LocalDateTime; import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Service @RequiredArgsConstructor @@ -49,12 +48,14 @@ public class ScheduleCommandServiceImpl implements ScheduleCommandService { /** * 스터디 일정을 추가하는 메서드입니다. - * @param studyId 타겟 스터디의 아이디를 입력 받습니다. + * + * @param studyId 타겟 스터디의 아이디를 입력 받습니다. * @param scheduleRequestDTO 생성할 일정의 제목, 위치, 시작 일시, 종료 일시, 종일 진행 여부, 반복 여부를 입력 받습니다. * @return 스터디 아이디와 생성된 일정의 아이디, 제목을 반환합니다. */ @Override - public ScheduleResponseDTO.ScheduleDTO addSchedule(Long studyId, ScheduleRequestDTO.ScheduleDTO scheduleRequestDTO) { + public ScheduleResponseDTO.ScheduleDTO addSchedule(Long studyId, + ScheduleRequestDTO.ScheduleDTO scheduleRequestDTO) { //=== Exception ===// Long memberId = SecurityUtils.getCurrentUserId(); @@ -88,12 +89,14 @@ public ScheduleResponseDTO.ScheduleDTO addSchedule(Long studyId, ScheduleRequest // 알림 생성 // 스터디에 참여중인 회원들에게 알림 전송 위해 회원 조회 - List members = studyMemberRepository.findAllByStudyIdAndStatus(studyId, StudyApplicationStatus.APPROVED).stream() + List members = studyMemberRepository.findAllByStudyIdAndStatus(studyId, StudyApplicationStatus.APPROVED) + .stream() .map(StudyMember::getMember) .toList(); - if (members.isEmpty()) + if (members.isEmpty()) { throw new StudyHandler(ErrorStatus._STUDY_MEMBER_NOT_FOUND); + } members.forEach(studyMember -> { Notification notification = Notification.builder() @@ -108,20 +111,21 @@ public ScheduleResponseDTO.ScheduleDTO addSchedule(Long studyId, ScheduleRequest scheduleRepository.save(schedule); study.addSchedule(schedule); - member.addSchedule(schedule); return ScheduleResponseDTO.ScheduleDTO.toDTO(schedule); } /** * 스터디 일정을 변경하는 메서드입니다. - * @param studyId 타겟 스터디의 아이디를 입력 받습니다. - * @param scheduleId 변경할 일정의 아이디를 입력 받습니다. + * + * @param studyId 타겟 스터디의 아이디를 입력 받습니다. + * @param scheduleId 변경할 일정의 아이디를 입력 받습니다. * @param scheduleModDTO 변경된 일정의 제목, 위치, 시작 일시, 종료 일시, 종일 진행 여부, 반복 여부를 입력 받습니다. * @return 스터디 아이디와 변경된 일정의 아이디, 제목을 반환합니다. */ @Override - public ScheduleResponseDTO.ScheduleDTO modSchedule(Long studyId, Long scheduleId, ScheduleRequestDTO.ScheduleDTO scheduleModDTO) { + public ScheduleResponseDTO.ScheduleDTO modSchedule(Long studyId, Long scheduleId, + ScheduleRequestDTO.ScheduleDTO scheduleModDTO) { //=== Exception ===// Long memberId = SecurityUtils.getCurrentUserId(); @@ -155,7 +159,6 @@ public ScheduleResponseDTO.ScheduleDTO modSchedule(Long studyId, Long scheduleId schedule = scheduleRepository.save(schedule); study.updateSchedule(schedule); - member.updateSchedule(schedule); return ScheduleResponseDTO.ScheduleDTO.toDTO(schedule); } @@ -166,25 +169,25 @@ private static void checkStartAndFinishDate(ScheduleRequestDTO.ScheduleDTO sched System.out.println(startDate); System.out.println(finishDate); switch (scheduleRequestDTO.getSchedulePeriod()) { - case DAILY : + case DAILY: // 시작일과 종료일이 일치해야 함 if (finishDate.equals(startDate.plusDays(1)) || finishDate.isAfter(startDate.plusDays(1))) { throw new StudyHandler(ErrorStatus._STUDY_SCHEDULE_WRONG_FORMAT); } - case WEEKLY : + case WEEKLY: // 시작일과 종료일이 일주일 이상 차이나지 않아야 함 if (finishDate.equals(startDate.plusWeeks(1)) || finishDate.isAfter(startDate.plusWeeks(1))) { throw new StudyHandler(ErrorStatus._STUDY_SCHEDULE_WRONG_FORMAT); } - case BIWEEKLY : + case BIWEEKLY: // 시작일과 종료일이 2주 이상 차이나지 않아야 함 if (finishDate.equals(startDate.plusWeeks(2)) || finishDate.isAfter(startDate.plusWeeks(2))) { throw new StudyHandler(ErrorStatus._STUDY_SCHEDULE_WRONG_FORMAT); } - case MONTHLY : + case MONTHLY: // 시작일과 종료일이 한 달 이상 차이나지 않아야 함 if (finishDate.equals(startDate.plusMonths(1)) || finishDate.isAfter(startDate.plusMonths(1))) { @@ -198,13 +201,15 @@ private static void checkStartAndFinishDate(ScheduleRequestDTO.ScheduleDTO sched /** * 출석 퀴즈를 생성하는 메서드입니다. + * * @param studyId 타겟 스터디의 아이디를 입력 받습니다. * @param scheduleId 타겟 일정의 아이디를 입력 받습니다. * @param quizRequestDTO 출석 퀴즈에 담길 질문과 정답을 입력 받습니다. * @return 생성된 퀴즈의 아이디와 질문이 반환됩니다. */ @Override - public QuizResponseDTO.QuestionDTO createAttendanceQuiz(Long studyId, Long scheduleId, QuizRequestDTO.QuizDTO quizRequestDTO) { + public QuizResponseDTO.QuestionDTO createAttendanceQuiz(Long studyId, Long scheduleId, + QuizRequestDTO.QuizDTO quizRequestDTO) { //=== Exception ===// Long memberId = SecurityUtils.getCurrentUserId(); @@ -228,8 +233,10 @@ public QuizResponseDTO.QuestionDTO createAttendanceQuiz(Long studyId, Long sched // 요청한 날짜에 이미 출석 퀴즈가 생성되었는지 확인 LocalDateTime startOfDay = quizRequestDTO.getCreatedAt().withHour(0).withMinute(0).withSecond(0).withNano(0); - LocalDateTime endOfDay = quizRequestDTO.getCreatedAt().withHour(23).withMinute(59).withSecond(59).withNano(999_999_000); - List todayQuizzes = quizRepository.findAllByScheduleIdAndCreatedAtBetween(scheduleId, startOfDay, endOfDay); + LocalDateTime endOfDay = quizRequestDTO.getCreatedAt().withHour(23).withMinute(59).withSecond(59) + .withNano(999_999_000); + List todayQuizzes = quizRepository.findAllByScheduleIdAndCreatedAtBetween(scheduleId, startOfDay, + endOfDay); if (!todayQuizzes.isEmpty()) { throw new StudyHandler(ErrorStatus._STUDY_QUIZ_ALREADY_EXIST); } @@ -245,21 +252,21 @@ public QuizResponseDTO.QuestionDTO createAttendanceQuiz(Long studyId, Long sched quiz = quizRepository.save(quiz); schedule.addQuiz(quiz); - member.addQuiz(quiz); return QuizResponseDTO.QuestionDTO.toDTO(quiz); } /** - * 출석 체크에 사용되는 메서드입니다. - * 메서드 내에서 퀴즈의 제한 시간과 시도 횟수를 확인하며, 조건을 충족하는 경우 회원 출석 정보를 저장합니다. - * @param studyId 타겟 스터디의 아이디를 입력 받습니다. - * @param scheduleId 출석을 체크할 일정을 입력 받습니다. + * 출석 체크에 사용되는 메서드입니다. 메서드 내에서 퀴즈의 제한 시간과 시도 횟수를 확인하며, 조건을 충족하는 경우 회원 출석 정보를 저장합니다. + * + * @param studyId 타겟 스터디의 아이디를 입력 받습니다. + * @param scheduleId 출석을 체크할 일정을 입력 받습니다. * @param attendanceRequestDTO 퀴즈에 대한 회원의 답변을 입력 받습니다. * @return 회원 아이디, 퀴즈 아이디, 출석 아이디, 정답 여부, 시도 횟수, 출석 정보 생성 시각을 반환합니다. */ @Override - public QuizResponseDTO.AttendanceDTO attendantStudy(Long studyId, Long scheduleId, QuizRequestDTO.AttendanceDTO attendanceRequestDTO) { + public QuizResponseDTO.AttendanceDTO attendantStudy(Long studyId, Long scheduleId, + QuizRequestDTO.AttendanceDTO attendanceRequestDTO) { //=== Exception ===// Long memberId = SecurityUtils.getCurrentUserId(); @@ -271,8 +278,10 @@ public QuizResponseDTO.AttendanceDTO attendantStudy(Long studyId, Long scheduleI .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_NOT_FOUND)); // 요청한 날짜에 생성된 출석 퀴즈 조회 - LocalDateTime startOfDay = attendanceRequestDTO.getDateTime().withHour(0).withMinute(0).withSecond(0).withNano(0); - LocalDateTime endOfDay = attendanceRequestDTO.getDateTime().withHour(23).withMinute(59).withSecond(59).withNano(999_999_000); + LocalDateTime startOfDay = attendanceRequestDTO.getDateTime().withHour(0).withMinute(0).withSecond(0) + .withNano(0); + LocalDateTime endOfDay = attendanceRequestDTO.getDateTime().withHour(23).withMinute(59).withSecond(59) + .withNano(999_999_000); List quizzes = quizRepository.findAllByScheduleIdAndCreatedAtBetween(scheduleId, startOfDay, endOfDay); if (quizzes.isEmpty()) { throw new StudyHandler(ErrorStatus._STUDY_QUIZ_NOT_FOUND); @@ -280,7 +289,8 @@ public QuizResponseDTO.AttendanceDTO attendantStudy(Long studyId, Long scheduleI Quiz quiz = quizzes.get(0); // 로그인한 회원이 스터디 회원인지 확인 - studyMemberRepository.findByMemberIdAndStudyIdAndStatus(member.getId(), study.getId(), StudyApplicationStatus.APPROVED) + studyMemberRepository.findByMemberIdAndStudyIdAndStatus(member.getId(), study.getId(), + StudyApplicationStatus.APPROVED) .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_MEMBER_NOT_FOUND)); // 퀴즈 제한시간 확인 @@ -289,13 +299,15 @@ public QuizResponseDTO.AttendanceDTO attendantStudy(Long studyId, Long scheduleI } // 이미 출석이 완료되었거나 시도 횟수를 초과하였는지 확인 - List attendanceList = quizSubmissionRepository.findByQuizIdAndMemberId(quiz.getId(), member.getId()); + List attendanceList = quizSubmissionRepository.findByQuizIdAndMemberId(quiz.getId(), + member.getId()); int try_num = 0; for (QuizSubmission attendance : attendanceList) { - if (attendance.getIsCorrect()) + if (attendance.getIsCorrect()) { throw new StudyHandler(ErrorStatus._STUDY_ATTENDANCE_ALREADY_EXIST); - else + } else { try_num++; + } } if (try_num >= 3) { throw new StudyHandler(ErrorStatus._STUDY_ATTENDANCE_ATTEMPT_LIMIT_EXCEEDED); @@ -310,11 +322,10 @@ public QuizResponseDTO.AttendanceDTO attendantStudy(Long studyId, Long scheduleI } QuizSubmission quizSubmission = new QuizSubmission(isCorrect); - member.addMemberAttendance(quizSubmission); quiz.addMemberAttendance(quizSubmission); quizSubmission = quizSubmissionRepository.save(quizSubmission); - return QuizResponseDTO.AttendanceDTO.toDTO(quizSubmission, try_num+1); + return QuizResponseDTO.AttendanceDTO.toDTO(quizSubmission, try_num + 1); } /** @@ -340,14 +351,16 @@ public QuizResponseDTO.QuestionDTO deleteAttendanceQuiz(Long studyId, Long sched // 요청한 날짜에 생성된 출석 퀴즈 조회 LocalDateTime startOfDay = date.atStartOfDay(); LocalDateTime endOfDay = date.atStartOfDay().plusDays(1); - List todayQuizzes = quizRepository.findAllByScheduleIdAndCreatedAtBetween(scheduleId, startOfDay, endOfDay); + List todayQuizzes = quizRepository.findAllByScheduleIdAndCreatedAtBetween(scheduleId, startOfDay, + endOfDay); if (todayQuizzes.isEmpty()) { throw new StudyHandler(ErrorStatus._STUDY_QUIZ_NOT_FOUND); } Quiz quiz = todayQuizzes.get(0); // 로그인한 회원이 스터디 회원인지 확인 - studyMemberRepository.findByMemberIdAndStudyIdAndStatus(member.getId(), study.getId(), StudyApplicationStatus.APPROVED) + studyMemberRepository.findByMemberIdAndStudyIdAndStatus(member.getId(), study.getId(), + StudyApplicationStatus.APPROVED) .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_MEMBER_NOT_FOUND)); // 로그인한 회원이 스터디장인지 확인 diff --git a/src/main/java/com/example/spot/schedule/application/ScheduleQueryServiceImpl.java b/src/main/java/com/example/spot/schedule/application/ScheduleQueryServiceImpl.java index 423aaf9b..b3898ba2 100644 --- a/src/main/java/com/example/spot/schedule/application/ScheduleQueryServiceImpl.java +++ b/src/main/java/com/example/spot/schedule/application/ScheduleQueryServiceImpl.java @@ -5,7 +5,7 @@ import com.example.spot.common.api.exception.handler.StudyHandler; import com.example.spot.common.security.utils.SecurityUtils; import com.example.spot.member.domain.Member; -import com.example.spot.member.domain.MemberRepository; +import com.example.spot.member.infrastructure.MemberRepository; import com.example.spot.schedule.domain.Schedule; import com.example.spot.schedule.domain.ScheduleRepository; import com.example.spot.schedule.domain.association.Quiz; @@ -13,23 +13,22 @@ import com.example.spot.schedule.domain.enums.SchedulePeriod; import com.example.spot.schedule.domain.repository.QuizRepository; import com.example.spot.schedule.domain.repository.QuizSubmissionRepository; +import com.example.spot.schedule.presentation.dto.response.QuizResponseDTO; +import com.example.spot.schedule.presentation.dto.response.ScheduleResponseDTO; import com.example.spot.study.domain.Study; import com.example.spot.study.domain.StudyRepository; import com.example.spot.study.domain.enums.StudyApplicationStatus; import com.example.spot.study.domain.repository.StudyMemberRepository; -import com.example.spot.schedule.presentation.dto.response.ScheduleResponseDTO; -import com.example.spot.schedule.presentation.dto.response.QuizResponseDTO; -import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.PageImpl; -import org.springframework.data.domain.Pageable; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - import java.time.LocalDate; import java.time.LocalDateTime; import java.time.YearMonth; import java.util.ArrayList; import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Service @Transactional(readOnly = true) @@ -47,9 +46,10 @@ public class ScheduleQueryServiceImpl implements ScheduleQueryService { /** * 특정 연/월의 일정을 불러오는 메서드입니다. + * * @param studyId 일정을 불러올 스터디의 아이디를 입력 받습니다. - * @param year 일정을 불러올 기준 연도를 입력 받습니다. - * @param month 일정을 불러올 달을 입력 받습니다. + * @param year 일정을 불러올 기준 연도를 입력 받습니다. + * @param month 일정을 불러올 달을 입력 받습니다. * @return 스터디 아이디와 해당 스터디의 월별 일정 목록을 반환합니다. */ @Override @@ -65,7 +65,8 @@ public ScheduleResponseDTO.MonthlyScheduleListDTO getMonthlySchedules(Long study // 로그인한 회원이 스터디 회원인지 확인 boolean isStudyMember; - if (studyMemberRepository.existsByMemberIdAndStudyIdAndStatus(memberId, studyId, StudyApplicationStatus.APPROVED)) { + if (studyMemberRepository.existsByMemberIdAndStudyIdAndStatus(memberId, studyId, + StudyApplicationStatus.APPROVED)) { isStudyMember = true; } else { isStudyMember = false; @@ -86,7 +87,8 @@ public ScheduleResponseDTO.MonthlyScheduleListDTO getMonthlySchedules(Long study /** * 하나의 일정에 대한 상세 정보를 불러오는 메서드입니다. - * @param studyId 일정을 불러올 스터디의 아이디를 입력 받습니다. + * + * @param studyId 일정을 불러올 스터디의 아이디를 입력 받습니다. * @param scheduleId 상세 정보를 물러올 일정의 아이디를 입력 받습니다. * @return 일정 아이디, 제목, 위치, 시작 일시, 종료 일시, 매일 진행 여부, 주기를 반환합니다. */ @@ -106,7 +108,8 @@ public ScheduleResponseDTO.MonthlyScheduleDTO getSchedule(Long studyId, Long sch // 로그인한 회원이 스터디 회원인지 확인 boolean isStudyMember; - if (studyMemberRepository.existsByMemberIdAndStudyIdAndStatus(memberId, studyId, StudyApplicationStatus.APPROVED)) { + if (studyMemberRepository.existsByMemberIdAndStudyIdAndStatus(memberId, studyId, + StudyApplicationStatus.APPROVED)) { isStudyMember = true; } else { isStudyMember = false; @@ -121,7 +124,8 @@ public ScheduleResponseDTO.MonthlyScheduleDTO getSchedule(Long studyId, Long sch /** * 로그인한 회원이 참여하는 특정 스터디의 다가오는 모임 목록을 페이징 조회 합니다. - * @param studyId 스터디 ID + * + * @param studyId 스터디 ID * @param pageable 페이징 정보 * @return 다가오는 모임 목록을 반환합니다. * @throws GeneralException 스터디 일정이 존재하지 않는 경우 @@ -131,67 +135,75 @@ public ScheduleResponseDTO.MonthlyScheduleDTO getSchedule(Long studyId, Long sch public ScheduleResponseDTO.SchedulePageDTO findStudySchedule(Long studyId, Pageable pageable) { // 로그인한 회원이 해당 스터디 회원인지 확인 - if (!isMember(SecurityUtils.getCurrentUserId(), studyId)) + if (!isMember(SecurityUtils.getCurrentUserId(), studyId)) { throw new GeneralException(ErrorStatus._ONLY_STUDY_MEMBER_CAN_ACCESS_SCHEDULE); + } // 스터디 일정 조회 List schedules = scheduleRepository.findAllByStudyId(studyId, pageable); // 스터디 일정이 존재하지 않는 경우 - if (schedules.isEmpty()) + if (schedules.isEmpty()) { throw new GeneralException(ErrorStatus._STUDY_SCHEDULE_NOT_FOUND); + } // DTO로 변환하여 반환 - List scheduleDTOS = schedules.stream().map(schedule -> ScheduleResponseDTO.SchedulePreviewDTO.builder() - .title(schedule.getTitle()) - .location(schedule.getLocation()) - .startedAt(schedule.getStartedAt()) - .finishedAt(schedule.getFinishedAt()) - .build()).toList(); + List scheduleDTOS = schedules.stream() + .map(schedule -> ScheduleResponseDTO.SchedulePreviewDTO.builder() + .title(schedule.getTitle()) + .location(schedule.getLocation()) + .startedAt(schedule.getStartedAt()) + .finishedAt(schedule.getFinishedAt()) + .build()).toList(); // 페이징 처리 - return new ScheduleResponseDTO.SchedulePageDTO(new PageImpl<>(scheduleDTOS, pageable, schedules.size()), scheduleDTOS, schedules.size()); + return new ScheduleResponseDTO.SchedulePageDTO(new PageImpl<>(scheduleDTOS, pageable, schedules.size()), + scheduleDTOS, schedules.size()); } /** * 회원이 스터디 구성원인지 확인합니다. + * * @param memberId 확인 하려는 회원 ID - * @param studyId 확인 하려는 스터디 ID + * @param studyId 확인 하려는 스터디 ID * @return 스터디 참여 여부를 반환합니다. */ private boolean isMember(Long memberId, Long studyId) { - return studyMemberRepository.findByMemberIdAndStudyIdAndStatus(memberId, studyId, StudyApplicationStatus.APPROVED).isPresent(); + return studyMemberRepository.findByMemberIdAndStudyIdAndStatus(memberId, studyId, + StudyApplicationStatus.APPROVED).isPresent(); } /** - * 월별 일정 리스트에 주기가 정해져 있지 않은 일정을 추가하기 위한 메서드입니다. - * 일정의 시작일이 기준 연월과 일치하는 경우 월별 일정 리스트에 추가합니다. - * getMonthlySchedules API에서 호출되는 내부 메서드입니다. - * @param schedule 리스트에 추가할 일정 정보를 입력 받습니다. - * @param year 기준 연도를 입력 받습니다. - * @param month 기준 월을 입력 받습니다. + * 월별 일정 리스트에 주기가 정해져 있지 않은 일정을 추가하기 위한 메서드입니다. 일정의 시작일이 기준 연월과 일치하는 경우 월별 일정 리스트에 추가합니다. getMonthlySchedules API에서 + * 호출되는 내부 메서드입니다. + * + * @param schedule 리스트에 추가할 일정 정보를 입력 받습니다. + * @param year 기준 연도를 입력 받습니다. + * @param month 기준 월을 입력 받습니다. * @param monthlyScheduleDTOS 일정을 추가할 월별 일정 리스트를 입력 받습니다. - * @param isStudyMember 스터디 회원 여부를 입력 받습니다. + * @param isStudyMember 스터디 회원 여부를 입력 받습니다. */ - private void addSchedule(Schedule schedule, int year, int month, List monthlyScheduleDTOS, boolean isStudyMember) { + private void addSchedule(Schedule schedule, int year, int month, + List monthlyScheduleDTOS, boolean isStudyMember) { if (schedule.getStartedAt().getYear() == year && schedule.getStartedAt().getMonthValue() == month) { monthlyScheduleDTOS.add(ScheduleResponseDTO.MonthlyScheduleDTO.toDTO(schedule, isStudyMember)); } } /** - * 월별 일정 리스트에 반복적인 일정을 추가하기 위한 메서드입니다. - * 일정의 시작일이 기준 연월 내에 있는 경우에만 일정을 추가하며, 주기에 따라 하나의 일정이라도 여러 번 추가될 수 있습니다. - * 예를 들어 기준 연월이 2024년 8월이고, 2024년 8월 2일부터 시작되는 WEEKLY 일정이 있다고 가정 - * 1. 이 일정은 기준 연월 내에서 2024년 8월 2일, 8월 9일, 8월 16일, 8월 23일, 8월 30일에 시행 - * 2. 따라서 monthlyScheduleDTOS에 추가되는 일정은 총 5개 - * @param schedule 리스트에 추가할 일정 정보를 입력 받습니다. - * @param year 기준 연도를 입력 받습니다. - * @param month 기준 월을 입력 받습니다. + * 월별 일정 리스트에 반복적인 일정을 추가하기 위한 메서드입니다. 일정의 시작일이 기준 연월 내에 있는 경우에만 일정을 추가하며, 주기에 따라 하나의 일정이라도 여러 번 추가될 수 있습니다. 예를 들어 + * 기준 연월이 2024년 8월이고, 2024년 8월 2일부터 시작되는 WEEKLY 일정이 있다고 가정 1. 이 일정은 기준 연월 내에서 2024년 8월 2일, 8월 9일, 8월 16일, 8월 23일, 8월 + * 30일에 시행 2. 따라서 monthlyScheduleDTOS에 추가되는 일정은 총 5개 + * + * @param schedule 리스트에 추가할 일정 정보를 입력 받습니다. + * @param year 기준 연도를 입력 받습니다. + * @param month 기준 월을 입력 받습니다. * @param monthlyScheduleDTOS 일정을 추가할 월별 일정 리스트를 입력 받습니다. - * @param isStudyMember 스터디 회원 여부를 입력 받습니다. + * @param isStudyMember 스터디 회원 여부를 입력 받습니다. */ - private void addPeriodSchedules(Schedule schedule, int year, int month, List monthlyScheduleDTOS, boolean isStudyMember) { + private void addPeriodSchedules(Schedule schedule, int year, int month, + List monthlyScheduleDTOS, + boolean isStudyMember) { LocalDateTime startedAt = schedule.getStartedAt(); LocalDateTime finishedAt = schedule.getFinishedAt(); @@ -203,7 +215,9 @@ private void addPeriodSchedules(Schedule schedule, int year, int month, List todayQuizzes = quizRepository.findAllByScheduleIdAndCreatedAtBetween(scheduleId, startOfDay, endOfDay); + List todayQuizzes = quizRepository.findAllByScheduleIdAndCreatedAtBetween(scheduleId, startOfDay, + endOfDay); if (todayQuizzes.isEmpty()) { throw new StudyHandler(ErrorStatus._STUDY_QUIZ_NOT_FOUND); } @@ -257,13 +273,16 @@ public QuizResponseDTO.AttendanceListDTO getAllAttendances(Long studyId, Long sc .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_MEMBER_NOT_FOUND)); //=== Feature ===// - List studyMembers = studyMemberRepository.findAllByStudyIdAndStatus(studyId, StudyApplicationStatus.APPROVED).stream() + List studyMembers = studyMemberRepository.findAllByStudyIdAndStatus(studyId, + StudyApplicationStatus.APPROVED).stream() .map(memberStudy -> { - List attendanceList = quizSubmissionRepository.findByQuizIdAndMemberId(quiz.getId(), memberStudy.getMember().getId()); + List attendanceList = quizSubmissionRepository.findByQuizIdAndMemberId(quiz.getId(), + memberStudy.getMember().getId()); for (QuizSubmission attendance : attendanceList) { // MemberAttendance에 퀴즈에 대한 정답이 저장되어 있으면 금일 출석 성공 - if (attendance.getIsCorrect()) + if (attendance.getIsCorrect()) { return QuizResponseDTO.AttendingMemberDTO.toDTO(memberStudy, Boolean.TRUE); + } } // 퀴즈를 풀지 않았거나 MemberAttendance에 오답만 저장되어 있으면 금일 출석 실패 return QuizResponseDTO.AttendingMemberDTO.toDTO(memberStudy, Boolean.FALSE); @@ -300,7 +319,8 @@ public QuizResponseDTO.QuestionDTO getAttendanceQuiz(Long studyId, Long schedule // 해당 날짜에 생성된 스터디 퀴즈 조회 LocalDateTime startOfDay = date.atStartOfDay(); LocalDateTime endOfDay = date.atStartOfDay().plusDays(1); - List todayQuizzes = quizRepository.findAllByScheduleIdAndCreatedAtBetween(scheduleId, startOfDay, endOfDay); + List todayQuizzes = quizRepository.findAllByScheduleIdAndCreatedAtBetween(scheduleId, startOfDay, + endOfDay); if (todayQuizzes.isEmpty()) { throw new StudyHandler(ErrorStatus._STUDY_QUIZ_NOT_FOUND); } diff --git a/src/main/java/com/example/spot/story/application/StoryCommandServiceImpl.java b/src/main/java/com/example/spot/story/application/StoryCommandServiceImpl.java index 6e0f0d66..25ffecf5 100644 --- a/src/main/java/com/example/spot/story/application/StoryCommandServiceImpl.java +++ b/src/main/java/com/example/spot/story/application/StoryCommandServiceImpl.java @@ -3,8 +3,14 @@ import com.example.spot.common.api.code.status.ErrorStatus; import com.example.spot.common.api.exception.handler.MemberHandler; import com.example.spot.common.api.exception.handler.StudyHandler; +import com.example.spot.common.application.s3.S3ImageService; +import com.example.spot.common.security.utils.SecurityUtils; import com.example.spot.member.domain.Member; +import com.example.spot.member.infrastructure.MemberRepository; import com.example.spot.notification.domain.Notification; +import com.example.spot.notification.domain.NotificationRepository; +import com.example.spot.notification.domain.enums.NotifyType; +import com.example.spot.report.domain.StoryReportRepository; import com.example.spot.story.domain.Story; import com.example.spot.story.domain.StoryRepository; import com.example.spot.story.domain.association.LikedStory; @@ -15,31 +21,23 @@ import com.example.spot.story.domain.repository.LikedStoryRepository; import com.example.spot.story.domain.repository.StoryCommentRepository; import com.example.spot.story.domain.repository.StoryImageRepository; -import com.example.spot.report.domain.StoryReportRepository; -import com.example.spot.study.domain.association.StudyMember; -import com.example.spot.study.domain.enums.StudyApplicationStatus; -import com.example.spot.notification.domain.enums.NotifyType; -import com.example.spot.study.domain.Study; -import com.example.spot.member.domain.MemberRepository; -import com.example.spot.study.domain.repository.StudyMemberRepository; -import com.example.spot.notification.domain.NotificationRepository; -import com.example.spot.study.domain.StudyRepository; -import com.example.spot.common.security.utils.SecurityUtils; -import com.example.spot.common.application.s3.S3ImageService; import com.example.spot.story.web.dto.request.StoryCommentRequestDTO; import com.example.spot.story.web.dto.request.StoryRequestDTO; import com.example.spot.story.web.dto.response.StoryCommentResponseDTO; import com.example.spot.story.web.dto.response.StoryResponseDTO; - +import com.example.spot.study.domain.Study; +import com.example.spot.study.domain.StudyRepository; +import com.example.spot.study.domain.association.StudyMember; +import com.example.spot.study.domain.enums.StudyApplicationStatus; +import com.example.spot.study.domain.repository.StudyMemberRepository; +import java.time.LocalDateTime; +import java.util.List; import lombok.RequiredArgsConstructor; 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.List; - @Service @Transactional @RequiredArgsConstructor @@ -63,11 +61,12 @@ public class StoryCommandServiceImpl implements StoryCommandService { // S3 Service private final S3ImageService s3ImageService; -/* ----------------------------- 스터디 게시글 관련 API ------------------------------------- */ + /* ----------------------------- 스터디 게시글 관련 API ------------------------------------- */ /** * 스터디 내부 게시판에 게시글을 작성하는 메서드입니다. - * @param studyId 게시글을 작성할 타겟 스터디의 아이디를 입력 받습니다. + * + * @param studyId 게시글을 작성할 타겟 스터디의 아이디를 입력 받습니다. * @param postRequestDTO 게시글의 입력 형식(StudyPostRequestDTO.PostDTO)에 맞추어 게시글 정보를 입력 받습니다. * @return 작성된 스터디 게시글의 Preview(게시글 아이디, 제목)를 반환합니다. */ @@ -82,15 +81,18 @@ public StoryResponseDTO.StoryPreviewDTO createPost(Long studyId, StoryRequestDTO .orElseThrow(() -> new MemberHandler(ErrorStatus._MEMBER_NOT_FOUND)); Study study = studyRepository.findById(studyId) .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_NOT_FOUND)); - studyMemberRepository.findByMemberIdAndStudyIdAndStatus(member.getId(), studyId, StudyApplicationStatus.APPROVED) + studyMemberRepository.findByMemberIdAndStudyIdAndStatus(member.getId(), studyId, + StudyApplicationStatus.APPROVED) .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_MEMBER_NOT_FOUND)); // 로그인한 회원이 스터디 회원인지 확인 - studyMemberRepository.findByMemberIdAndStudyIdAndStatus(member.getId(), studyId, StudyApplicationStatus.APPROVED) + studyMemberRepository.findByMemberIdAndStudyIdAndStatus(member.getId(), studyId, + StudyApplicationStatus.APPROVED) .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_MEMBER_NOT_FOUND)); // 스터디장만 공지 가능 - StudyMember memberStudy = studyMemberRepository.findByMemberIdAndStudyIdAndStatus(member.getId(), studyId, StudyApplicationStatus.APPROVED) + StudyMember memberStudy = studyMemberRepository.findByMemberIdAndStudyIdAndStatus(member.getId(), studyId, + StudyApplicationStatus.APPROVED) .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_MEMBER_NOT_FOUND)); if (!memberStudy.getIsOwned() && postRequestDTO.getIsAnnouncement()) { throw new StudyHandler(ErrorStatus._STUDY_POST_ANNOUNCEMENT_INVALID); @@ -115,7 +117,6 @@ public StoryResponseDTO.StoryPreviewDTO createPost(Long studyId, StoryRequestDTO } story = storyRepository.save(story); - member.addStudyPost(story); study.addStudyPost(story); // 이미지가 있는 경우 이미지 저장 @@ -129,16 +130,17 @@ public StoryResponseDTO.StoryPreviewDTO createPost(Long studyId, StoryRequestDTO story.addImage(storyImage); } - if (story.getIsAnnouncement()){ + if (story.getIsAnnouncement()) { // 스터디에 참여중인 회원들에게 알림 전송 위해 회원 조회 List members = studyMemberRepository.findAllByStudyIdAndStatus( - story.getStudy().getId(), StudyApplicationStatus.APPROVED).stream() + story.getStudy().getId(), StudyApplicationStatus.APPROVED).stream() .map(StudyMember::getMember) - .toList(); + .toList(); - if (members.isEmpty()) + if (members.isEmpty()) { throw new StudyHandler(ErrorStatus._STUDY_MEMBER_NOT_FOUND); + } // 알림 생성 for (Member studyMember : members) { @@ -154,7 +156,6 @@ public StoryResponseDTO.StoryPreviewDTO createPost(Long studyId, StoryRequestDTO } } - member.updateStudyPost(story); study.updateStudyPost(story); return StoryResponseDTO.StoryPreviewDTO.toDTO(story); @@ -174,7 +175,8 @@ public StoryResponseDTO.StoryPreviewDTO updatePost(Long studyId, Long postId, St .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_POST_NOT_FOUND)); // 로그인한 회원이 스터디 회원인지 확인 - StudyMember studyMember = studyMemberRepository.findByMemberIdAndStudyIdAndStatus(memberId, studyId, StudyApplicationStatus.APPROVED) + StudyMember studyMember = studyMemberRepository.findByMemberIdAndStudyIdAndStatus(memberId, studyId, + StudyApplicationStatus.APPROVED) .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_MEMBER_NOT_FOUND)); // 로그인한 회원이 게시글 작성자인지 확인 @@ -212,8 +214,9 @@ private void updateStudyPostImage(StoryRequestDTO.StoryDTO storyDTO, Story story /** * 스터디 내부 게시판에 작성된 게시글을 삭제합니다. + * * @param studyId 게시글을 삭제할 타겟 스터디의 아이디를 입력 받습니다. - * @param postId 삭제할 스터디 게시글의 아이디를 입력 받습니다. + * @param postId 삭제할 스터디 게시글의 아이디를 입력 받습니다. * @return 삭제된 스터디 게시글의 Preview(게시글 아이디, 제목)를 반환합니다. */ @Override @@ -231,7 +234,8 @@ public StoryResponseDTO.StoryPreviewDTO deletePost(Long studyId, Long postId) { .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_POST_NOT_FOUND)); // 로그인한 회원이 스터디 회원인지 확인 - studyMemberRepository.findByMemberIdAndStudyIdAndStatus(member.getId(), studyId, StudyApplicationStatus.APPROVED) + studyMemberRepository.findByMemberIdAndStudyIdAndStatus(member.getId(), studyId, + StudyApplicationStatus.APPROVED) .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_MEMBER_NOT_FOUND)); // 해당 스터디의 게시글인지 확인 @@ -246,14 +250,13 @@ public StoryResponseDTO.StoryPreviewDTO deletePost(Long studyId, Long postId) { .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_OWNER_NOT_FOUND)); if (story.getMember().getId().equals(memberId) || - memberId.equals(ownerId)) { + memberId.equals(ownerId)) { storyImageRepository.deleteAllByStoryId(postId); storyCommentRepository.deleteAllByStoryId(postId); likedStoryRepository.deleteAllByStoryId(postId); storyReportRepository.deleteAllByStoryId(postId); - member.deleteStudyPost(story); study.deleteStudyPost(story); storyRepository.delete(story); @@ -265,10 +268,10 @@ public StoryResponseDTO.StoryPreviewDTO deletePost(Long studyId, Long postId) { } /** - * 스터디 내부 게시판에 작성된 게시글에 좋아요를 누르는 메서드입니다. - * 게시글에 좋아요를 누른 회원의 정보가 StudyLikedPost에 저장되고 스터디 게시글의 좋아요 개수가 업데이트 됩니다. + * 스터디 내부 게시판에 작성된 게시글에 좋아요를 누르는 메서드입니다. 게시글에 좋아요를 누른 회원의 정보가 StudyLikedPost에 저장되고 스터디 게시글의 좋아요 개수가 업데이트 됩니다. + * * @param studyId 게시글이 작성된 타겟 스터디의 아이디를 입력 받습니다. - * @param postId 좋아요를 누를 타겟 게시글의 아이디를 입력 받습니다. + * @param postId 좋아요를 누를 타겟 게시글의 아이디를 입력 받습니다. * @return 게시글의 Preview(게시글 아이디, 제목)와 함께 좋아요 개수가 반환됩니다. */ @Override @@ -286,7 +289,8 @@ public StoryResponseDTO.StoryLikeNumDTO likePost(Long studyId, Long postId) { .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_POST_NOT_FOUND)); // 로그인한 회원이 스터디 회원인지 확인 - studyMemberRepository.findByMemberIdAndStudyIdAndStatus(member.getId(), studyId, StudyApplicationStatus.APPROVED) + studyMemberRepository.findByMemberIdAndStudyIdAndStatus(member.getId(), studyId, + StudyApplicationStatus.APPROVED) .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_MEMBER_NOT_FOUND)); // 해당 스터디의 게시글인지 확인 @@ -305,7 +309,6 @@ public StoryResponseDTO.StoryLikeNumDTO likePost(Long studyId, Long postId) { .build(); likedStory = likedStoryRepository.save(likedStory); - member.addStudyLikedPost(likedStory); story.addLikedPost(likedStory); story.plusLikeNum(); @@ -315,10 +318,10 @@ public StoryResponseDTO.StoryLikeNumDTO likePost(Long studyId, Long postId) { } /** - * 스터디 내부 게시판에 작성된 게시글에 누른 좋아요를 취소하는 메서드입니다. - * 게시글에 좋아요를 누른 회원의 정보가 StudyLikedPost에서 삭제되고 스터디 게시글의 좋아요 개수가 업데이트 됩니다. + * 스터디 내부 게시판에 작성된 게시글에 누른 좋아요를 취소하는 메서드입니다. 게시글에 좋아요를 누른 회원의 정보가 StudyLikedPost에서 삭제되고 스터디 게시글의 좋아요 개수가 업데이트 됩니다. + * * @param studyId 게시글이 작성된 타겟 스터디의 아이디를 입력 받습니다. - * @param postId 좋아요를 취소할 타겟 게시글의 아이디를 입력 받습니다. + * @param postId 좋아요를 취소할 타겟 게시글의 아이디를 입력 받습니다. * @return 게시글의 Preview(게시글 아이디, 제목)와 함께 좋아요 개수가 반환됩니다. */ @Override @@ -336,7 +339,8 @@ public StoryResponseDTO.StoryLikeNumDTO cancelPostLike(Long studyId, Long postId .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_POST_NOT_FOUND)); // 로그인한 회원이 스터디 회원인지 확인 - studyMemberRepository.findByMemberIdAndStudyIdAndStatus(member.getId(), studyId, StudyApplicationStatus.APPROVED) + studyMemberRepository.findByMemberIdAndStudyIdAndStatus(member.getId(), studyId, + StudyApplicationStatus.APPROVED) .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_MEMBER_NOT_FOUND)); // 해당 스터디의 게시글인지 확인 @@ -347,7 +351,6 @@ public StoryResponseDTO.StoryLikeNumDTO cancelPostLike(Long studyId, Long postId LikedStory likedStory = likedStoryRepository.findByMemberIdAndStoryId(memberId, postId) .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_LIKED_POST_NOT_FOUND)); - member.deleteStudyLikedPost(likedStory); story.deleteLikedPost(likedStory); story.minusLikeNum(); likedStoryRepository.delete(likedStory); @@ -356,17 +359,19 @@ public StoryResponseDTO.StoryLikeNumDTO cancelPostLike(Long studyId, Long postId return StoryResponseDTO.StoryLikeNumDTO.toDTO(story); } -/* ----------------------------- 스터디 게시글 댓글 관련 API ------------------------------------- */ + /* ----------------------------- 스터디 게시글 댓글 관련 API ------------------------------------- */ /** * 스터디 게시글에 댓글을 추가하는 메서드입니다. 답글 추가 메서드는 하단에 별도로 구현되어 있습니다. - * @param studyId 스터디 게시글이 작성된 타겟 스터디를 입력 받습니다. - * @param postId 댓글을 추가할 스터디 게시글의 아이디를 입력 받습니다. + * + * @param studyId 스터디 게시글이 작성된 타겟 스터디를 입력 받습니다. + * @param postId 댓글을 추가할 스터디 게시글의 아이디를 입력 받습니다. * @param commentRequestDTO 추가할 댓글(내용, 익명 여부)을 입력 받습니다. * @return 댓글 아이디와 작성자, 내용, 좋아요와 싫어요 개수를 함께 반환합니다. */ @Override - public StoryCommentResponseDTO.CommentDTO createComment(Long studyId, Long postId, StoryCommentRequestDTO.CommentDTO commentRequestDTO) { + public StoryCommentResponseDTO.CommentDTO createComment(Long studyId, Long postId, + StoryCommentRequestDTO.CommentDTO commentRequestDTO) { //=== Exception ===// Long memberId = SecurityUtils.getCurrentUserId(); @@ -380,7 +385,8 @@ public StoryCommentResponseDTO.CommentDTO createComment(Long studyId, Long postI .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_POST_NOT_FOUND)); // 로그인한 회원이 스터디 회원인지 확인 - studyMemberRepository.findByMemberIdAndStudyIdAndStatus(member.getId(), studyId, StudyApplicationStatus.APPROVED) + studyMemberRepository.findByMemberIdAndStudyIdAndStatus(member.getId(), studyId, + StudyApplicationStatus.APPROVED) .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_MEMBER_NOT_FOUND)); // 해당 스터디의 게시글인지 확인 @@ -408,20 +414,21 @@ public StoryCommentResponseDTO.CommentDTO createComment(Long studyId, Long postI storyRepository.save(story); story.addComment(storyComment); - member.addComment(storyComment); - return StoryCommentResponseDTO.CommentDTO.toDTO(storyComment, "익명"+anonymousNum, defaultImage); + return StoryCommentResponseDTO.CommentDTO.toDTO(storyComment, "익명" + anonymousNum, defaultImage); } /** * 스터디 게시글에 답글을 추가하는 메서드입니다. 댓글 추가 메서드는 상단에 별도로 구현되어 있습니다. - * @param studyId 스터디 게시글이 작성된 타겟 스터디를 입력 받습니다. - * @param postId 댓글을 추가할 스터디 게시글의 아이디를 입력 받습니다. + * + * @param studyId 스터디 게시글이 작성된 타겟 스터디를 입력 받습니다. + * @param postId 댓글을 추가할 스터디 게시글의 아이디를 입력 받습니다. * @param commentRequestDTO 추가할 댓글(내용, 익명 여부)을 입력 받습니다. * @return 댓글 아이디와 작성자, 내용, 좋아요와 싫어요 개수를 함께 반환합니다. */ @Override - public StoryCommentResponseDTO.CommentDTO createReply(Long studyId, Long postId, Long commentId, StoryCommentRequestDTO.CommentDTO commentRequestDTO) { + public StoryCommentResponseDTO.CommentDTO createReply(Long studyId, Long postId, Long commentId, + StoryCommentRequestDTO.CommentDTO commentRequestDTO) { //=== Exception ===// Long memberId = SecurityUtils.getCurrentUserId(); @@ -435,7 +442,8 @@ public StoryCommentResponseDTO.CommentDTO createReply(Long studyId, Long postId, .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_POST_NOT_FOUND)); // 로그인한 회원이 스터디 회원인지 확인 - studyMemberRepository.findByMemberIdAndStudyIdAndStatus(member.getId(), studyId, StudyApplicationStatus.APPROVED) + studyMemberRepository.findByMemberIdAndStudyIdAndStatus(member.getId(), studyId, + StudyApplicationStatus.APPROVED) .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_MEMBER_NOT_FOUND)); // 해당 스터디의 게시글인지 확인 @@ -467,20 +475,18 @@ public StoryCommentResponseDTO.CommentDTO createReply(Long studyId, Long postId, storyRepository.save(story); story.addComment(storyComment); - member.addComment(storyComment); parentComment.addChildrenComment(storyComment); - return StoryCommentResponseDTO.CommentDTO.toDTO(storyComment, "익명"+anonymousNum, defaultImage); + return StoryCommentResponseDTO.CommentDTO.toDTO(storyComment, "익명" + anonymousNum, defaultImage); } /** - * 스터디 게시글 댓글마다 익명 번호를 부여하는 메서드입니다. - * 회원이 이미 타겟 스터디 게시글에 익명으로 댓글을 작성한 이력이 있는 경우 해당 번호를 반환합니다. - * @param postId 댓글을 작성할 타겟 스터디 게시글의 아이디를 입력 받습니다. + * 스터디 게시글 댓글마다 익명 번호를 부여하는 메서드입니다. 회원이 이미 타겟 스터디 게시글에 익명으로 댓글을 작성한 이력이 있는 경우 해당 번호를 반환합니다. + * + * @param postId 댓글을 작성할 타겟 스터디 게시글의 아이디를 입력 받습니다. * @param commentRequestDTO 추가할 댓글(내용, 익명 여부)을 입력 받습니다. - * @param member 댓글 작성자를 입력 받습니다. - * @return 댓글 작성자의 익명 번호를 반환합니다. - * 회원이 이미 타겟 스터디 게시글에 익명으로 댓글을 작성한 이력이 있는 경우 해당 번호를 반환합니다. + * @param member 댓글 작성자를 입력 받습니다. + * @return 댓글 작성자의 익명 번호를 반환합니다. 회원이 이미 타겟 스터디 게시글에 익명으로 댓글을 작성한 이력이 있는 경우 해당 번호를 반환합니다. */ private Integer getAnonymousNum(Long postId, StoryCommentRequestDTO.CommentDTO commentRequestDTO, Member member) { Integer anonymousNum = null; @@ -497,7 +503,7 @@ private Integer getAnonymousNum(Long postId, StoryCommentRequestDTO.CommentDTO c maxAnonymousNum = storyComment.getAnonymousNum(); } } - anonymousNum = maxAnonymousNum+1; + anonymousNum = maxAnonymousNum + 1; // 회원의 댓글 이력이 존재하는 경우 익명 작성 여부 확인 // 해당 post에 익명으로 댓글을 남긴 이력이 있으면 해당 번호를 가져옴 if (!myStoryComments.isEmpty()) { @@ -514,8 +520,9 @@ private Integer getAnonymousNum(Long postId, StoryCommentRequestDTO.CommentDTO c /** * 스터디 게시글에 작성한 댓글을 삭제하는 메서드입니다. 댓글 삭제와 답글 삭제 모두 해당 메서드를 활용합니다. - * @param studyId 스터디 게시글이 작성된 타겟 스터디의 아이디를 입력 받습니다. - * @param postId 댓글을 삭제할 타겟 스터디 게시글의 아이디를 입력 받습니다. + * + * @param studyId 스터디 게시글이 작성된 타겟 스터디의 아이디를 입력 받습니다. + * @param postId 댓글을 삭제할 타겟 스터디 게시글의 아이디를 입력 받습니다. * @param commentId 삭제할 댓글의 아이디를 입력 받습니다. * @return 삭제한 댓글의 아이디를 반환합니다. */ @@ -544,7 +551,7 @@ public StoryCommentResponseDTO.CommentIdDTO deleteComment(Long studyId, Long pos .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_POST_COMMENT_NOT_FOUND)); // 댓글 작성자인지 확인 - if(!storyComment.getMember().equals(member)) { + if (!storyComment.getMember().equals(member)) { throw new StudyHandler(ErrorStatus._STUDY_POST_COMMENT_DELETE_INVALID); } @@ -555,17 +562,17 @@ public StoryCommentResponseDTO.CommentIdDTO deleteComment(Long studyId, Long pos } storyComment.deleteComment(); story.updateComment(storyComment); - member.updateComment(storyComment); storyCommentRepository.save(storyComment); return new StoryCommentResponseDTO.CommentIdDTO(commentId); } /** - * 댓글에 좋아요를 누르는 메서드입니다. 댓글 좋아요와 답글 좋아요 모두 해당 메서드를 활용합니다. - * 댓글에 좋아요를 누른 회원의 정보가 StudyLikedComment에 저장되고 타겟 댓글의 좋아요 개수가 업데이트 됩니다. - * @param studyId 스터디 게시글이 작성된 타겟 스터디의 아이디를 입력 받습니다. - * @param postId 타겟이 되는 스터디 게시글의 아이디를 입력 받습니다. + * 댓글에 좋아요를 누르는 메서드입니다. 댓글 좋아요와 답글 좋아요 모두 해당 메서드를 활용합니다. 댓글에 좋아요를 누른 회원의 정보가 StudyLikedComment에 저장되고 타겟 댓글의 좋아요 개수가 + * 업데이트 됩니다. + * + * @param studyId 스터디 게시글이 작성된 타겟 스터디의 아이디를 입력 받습니다. + * @param postId 타겟이 되는 스터디 게시글의 아이디를 입력 받습니다. * @param commentId 좋아요를 누를 타겟 댓글의 아이디를 입력 받습니다. * @return 댓글 아이디와 타겟 댓글의 좋아요 수와 싫어요 수가 반환됩니다. */ @@ -576,10 +583,11 @@ public StoryCommentResponseDTO.CommentPreviewDTO likeComment(Long studyId, Long } /** - * 댓글에 싫어요를 누르는 메서드입니다. 댓글 싫어요와 답글 싫어요 모두 해당 메서드를 활용합니다. - * 댓글에 싫어요를 누른 회원의 정보가 StudyLikedComment에 저장되고 타겟 댓글의 싫어요 개수가 업데이트 됩니다. - * @param studyId 스터디 게시글이 작성된 타겟 스터디의 아이디를 입력 받습니다. - * @param postId 타겟이 되는 스터디 게시글의 아이디를 입력 받습니다. + * 댓글에 싫어요를 누르는 메서드입니다. 댓글 싫어요와 답글 싫어요 모두 해당 메서드를 활용합니다. 댓글에 싫어요를 누른 회원의 정보가 StudyLikedComment에 저장되고 타겟 댓글의 싫어요 개수가 + * 업데이트 됩니다. + * + * @param studyId 스터디 게시글이 작성된 타겟 스터디의 아이디를 입력 받습니다. + * @param postId 타겟이 되는 스터디 게시글의 아이디를 입력 받습니다. * @param commentId 싫어요를 누를 타겟 댓글의 아이디를 입력 받습니다. * @return 댓글 아이디와 타겟 댓글의 좋아요 수와 싫어요 수가 반환됩니다. */ @@ -590,12 +598,12 @@ public StoryCommentResponseDTO.CommentPreviewDTO dislikeComment(Long studyId, Lo } /** - * 댓글 좋아요/싫어요 메서드에서 사용되는 내부 메서드입니다. - * isLiked = true면 좋아요 정보를, isLiked = false면 싫어요 정보를 DB에 저장합니다. - * @param studyId 스터디 게시글이 작성된 타겟 스터디의 아이디를 입력 받습니다. - * @param postId 타겟이 되는 스터디 게시글의 아이디를 입력 받습니다. + * 댓글 좋아요/싫어요 메서드에서 사용되는 내부 메서드입니다. isLiked = true면 좋아요 정보를, isLiked = false면 싫어요 정보를 DB에 저장합니다. + * + * @param studyId 스터디 게시글이 작성된 타겟 스터디의 아이디를 입력 받습니다. + * @param postId 타겟이 되는 스터디 게시글의 아이디를 입력 받습니다. * @param commentId 좋아요 혹은 싫어요를 누를 타겟 댓글의 아이디를 입력 받습니다. - * @param isLiked 좋아요 혹은 싫어요 어부를 입력 받습니다. + * @param isLiked 좋아요 혹은 싫어요 어부를 입력 받습니다. * @return SavePostComment 객체를 반환합니다. */ private StoryComment saveStudyPostComment(Long studyId, Long postId, Long commentId, Boolean isLiked) { @@ -613,7 +621,8 @@ private StoryComment saveStudyPostComment(Long studyId, Long postId, Long commen .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_POST_COMMENT_NOT_FOUND)); // 로그인한 회원이 스터디 회원인지 확인 - studyMemberRepository.findByMemberIdAndStudyIdAndStatus(member.getId(), studyId, StudyApplicationStatus.APPROVED) + studyMemberRepository.findByMemberIdAndStudyIdAndStatus(member.getId(), studyId, + StudyApplicationStatus.APPROVED) .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_MEMBER_NOT_FOUND)); // 해당 스터디의 게시글인지 확인 @@ -621,10 +630,12 @@ private StoryComment saveStudyPostComment(Long studyId, Long postId, Long commen .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_POST_NOT_FOUND)); // 이미 좋아요나 싫어요를 눌렀다면 싫어요 할 수 없음 - if (likedStoryCommentRepository.findByMemberIdAndStoryCommentIdAndIsLiked(memberId, commentId, Boolean.TRUE).isPresent()) { + if (likedStoryCommentRepository.findByMemberIdAndStoryCommentIdAndIsLiked(memberId, commentId, Boolean.TRUE) + .isPresent()) { throw new StudyHandler(ErrorStatus._STUDY_POST_COMMENT_ALREADY_LIKED); } - if (likedStoryCommentRepository.findByMemberIdAndStoryCommentIdAndIsLiked(memberId, commentId, Boolean.FALSE).isPresent()) { + if (likedStoryCommentRepository.findByMemberIdAndStoryCommentIdAndIsLiked(memberId, commentId, Boolean.FALSE) + .isPresent()) { throw new StudyHandler(ErrorStatus._STUDY_POST_COMMENT_ALREADY_DISLIKED); } @@ -636,7 +647,6 @@ private StoryComment saveStudyPostComment(Long studyId, Long postId, Long commen .build(); likedStoryComment = likedStoryCommentRepository.save(likedStoryComment); - member.addStudyLikedComment(likedStoryComment); storyComment.addLikedComment(likedStoryComment); if (likedStoryComment.getIsLiked()) { @@ -650,10 +660,11 @@ private StoryComment saveStudyPostComment(Long studyId, Long postId, Long commen } /** - * 댓글 좋아요를 취소하는 메서드입니다. 댓글 좋아요와 답글 좋아요 모두 해당 메서드를 활용합니다. - * 댓글 좋아요를 취소한 회원의 정보가 StudyLikedComment에서 삭제되고 타겟 댓글의 싫어요 개수가 업데이트 됩니다. - * @param studyId 스터디 게시글이 작성된 타겟 스터디의 아이디를 입력 받습니다. - * @param postId 타겟이 되는 스터디 게시글의 아이디를 입력 받습니다. + * 댓글 좋아요를 취소하는 메서드입니다. 댓글 좋아요와 답글 좋아요 모두 해당 메서드를 활용합니다. 댓글 좋아요를 취소한 회원의 정보가 StudyLikedComment에서 삭제되고 타겟 댓글의 싫어요 개수가 + * 업데이트 됩니다. + * + * @param studyId 스터디 게시글이 작성된 타겟 스터디의 아이디를 입력 받습니다. + * @param postId 타겟이 되는 스터디 게시글의 아이디를 입력 받습니다. * @param commentId 싫어요를 취소할 타겟 댓글의 아이디를 입력 받습니다. * @return 댓글 아이디와 타겟 댓글의 좋아요 수와 싫어요 수가 반환됩니다. */ @@ -663,7 +674,8 @@ public StoryCommentResponseDTO.CommentPreviewDTO cancelCommentLike(Long studyId, Long memberId = SecurityUtils.getCurrentUserId(); SecurityUtils.verifyUserId(memberId); - LikedStoryComment likedStoryComment = likedStoryCommentRepository.findByMemberIdAndStoryCommentIdAndIsLiked(memberId, commentId, Boolean.TRUE) + LikedStoryComment likedStoryComment = likedStoryCommentRepository.findByMemberIdAndStoryCommentIdAndIsLiked( + memberId, commentId, Boolean.TRUE) .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_LIKED_COMMENT_NOT_FOUND)); StoryComment storyComment = deleteStudyLikedComment(studyId, postId, commentId, memberId, likedStoryComment); @@ -671,10 +683,11 @@ public StoryCommentResponseDTO.CommentPreviewDTO cancelCommentLike(Long studyId, } /** - * 댓글 싫어요를 취소하는 메서드입니다. 댓글 싫어요와 답글 싫어요 모두 해당 메서드를 활용합니다. - * 댓글 싫어요를 취소한 회원의 정보가 StudyLikedComment에서 삭제되고 타겟 댓글의 싫어요 개수가 업데이트 됩니다. - * @param studyId 스터디 게시글이 작성된 타겟 스터디의 아이디를 입력 받습니다. - * @param postId 타겟이 되는 스터디 게시글의 아이디를 입력 받습니다. + * 댓글 싫어요를 취소하는 메서드입니다. 댓글 싫어요와 답글 싫어요 모두 해당 메서드를 활용합니다. 댓글 싫어요를 취소한 회원의 정보가 StudyLikedComment에서 삭제되고 타겟 댓글의 싫어요 개수가 + * 업데이트 됩니다. + * + * @param studyId 스터디 게시글이 작성된 타겟 스터디의 아이디를 입력 받습니다. + * @param postId 타겟이 되는 스터디 게시글의 아이디를 입력 받습니다. * @param commentId 싫어요를 취소할 타겟 댓글의 아이디를 입력 받습니다. * @return 댓글 아이디와 타겟 댓글의 좋아요 수와 싫어요 수가 반환됩니다. */ @@ -684,7 +697,8 @@ public StoryCommentResponseDTO.CommentPreviewDTO cancelCommentDislike(Long study Long memberId = SecurityUtils.getCurrentUserId(); SecurityUtils.verifyUserId(memberId); - LikedStoryComment likedStoryComment = likedStoryCommentRepository.findByMemberIdAndStoryCommentIdAndIsLiked(memberId, commentId, Boolean.FALSE) + LikedStoryComment likedStoryComment = likedStoryCommentRepository.findByMemberIdAndStoryCommentIdAndIsLiked( + memberId, commentId, Boolean.FALSE) .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_DISLIKED_COMMENT_NOT_FOUND)); StoryComment storyComment = deleteStudyLikedComment(studyId, postId, commentId, memberId, likedStoryComment); @@ -693,14 +707,16 @@ public StoryCommentResponseDTO.CommentPreviewDTO cancelCommentDislike(Long study /** * 댓글 좋아요/싫어요 취소 메서드에서 사용되는 내부 메서드입니다. - * @param studyId 스터디 게시글이 작성된 타겟 스터디의 아이디를 입력 받습니다. - * @param postId 타겟이 되는 스터디 게시글의 아이디를 입력 받습니다. - * @param commentId 좋아요 혹은 싫어요를 취소할 타겟 댓글의 아이디를 입력 받습니다. - * @param memberId 댓글에 좋아요 혹은 싫어요를 누른 회원의 아이디를 입력 받습니다. + * + * @param studyId 스터디 게시글이 작성된 타겟 스터디의 아이디를 입력 받습니다. + * @param postId 타겟이 되는 스터디 게시글의 아이디를 입력 받습니다. + * @param commentId 좋아요 혹은 싫어요를 취소할 타겟 댓글의 아이디를 입력 받습니다. + * @param memberId 댓글에 좋아요 혹은 싫어요를 누른 회원의 아이디를 입력 받습니다. * @param likedStoryComment DB에서 삭제할 StudyLikedComment 객체를 입력 받습니다. * @return 삭제된 StudyLikedComment 객체를 반환합니다. */ - private StoryComment deleteStudyLikedComment(Long studyId, Long postId, Long commentId, Long memberId, LikedStoryComment likedStoryComment) { + private StoryComment deleteStudyLikedComment(Long studyId, Long postId, Long commentId, Long memberId, + LikedStoryComment likedStoryComment) { //=== Exception ===// Member member = memberRepository.findById(memberId) @@ -713,7 +729,8 @@ private StoryComment deleteStudyLikedComment(Long studyId, Long postId, Long com .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_POST_COMMENT_NOT_FOUND)); // 로그인한 회원이 스터디 회원인지 확인 - studyMemberRepository.findByMemberIdAndStudyIdAndStatus(member.getId(), studyId, StudyApplicationStatus.APPROVED) + studyMemberRepository.findByMemberIdAndStudyIdAndStatus(member.getId(), studyId, + StudyApplicationStatus.APPROVED) .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_MEMBER_NOT_FOUND)); // 로그인한 회원이 댓글에 반응한 사람인지 확인 @@ -722,7 +739,6 @@ private StoryComment deleteStudyLikedComment(Long studyId, Long postId, Long com } //=== Feature ===// - member.deleteStudyLikedComment(likedStoryComment); storyComment.deleteLikedComment(likedStoryComment); if (likedStoryComment.getIsLiked()) { diff --git a/src/main/java/com/example/spot/story/application/StoryQueryServiceImpl.java b/src/main/java/com/example/spot/story/application/StoryQueryServiceImpl.java index c718b2b5..aa175b6e 100644 --- a/src/main/java/com/example/spot/story/application/StoryQueryServiceImpl.java +++ b/src/main/java/com/example/spot/story/application/StoryQueryServiceImpl.java @@ -4,32 +4,31 @@ import com.example.spot.common.api.exception.GeneralException; import com.example.spot.common.api.exception.handler.MemberHandler; import com.example.spot.common.api.exception.handler.StudyHandler; +import com.example.spot.common.security.utils.SecurityUtils; import com.example.spot.member.domain.Member; +import com.example.spot.member.infrastructure.MemberRepository; import com.example.spot.story.domain.Story; +import com.example.spot.story.domain.StoryRepository; +import com.example.spot.story.domain.association.StoryComment; import com.example.spot.story.domain.enums.StoryCategory; -import com.example.spot.study.domain.enums.StudyApplicationStatus; import com.example.spot.story.domain.enums.StoryCategoryQuery; -import com.example.spot.study.domain.Study; -import com.example.spot.story.domain.association.StoryComment; -import com.example.spot.member.domain.MemberRepository; -import com.example.spot.study.domain.repository.StudyMemberRepository; import com.example.spot.story.domain.repository.LikedStoryRepository; import com.example.spot.story.domain.repository.StoryCommentRepository; -import com.example.spot.story.domain.StoryRepository; -import com.example.spot.study.domain.StudyRepository; -import com.example.spot.common.security.utils.SecurityUtils; import com.example.spot.story.web.dto.response.StoryCommentResponseDTO; import com.example.spot.story.web.dto.response.StoryResponseDTO; +import com.example.spot.study.domain.Study; +import com.example.spot.study.domain.StudyRepository; +import com.example.spot.study.domain.enums.StudyApplicationStatus; +import com.example.spot.study.domain.repository.StudyMemberRepository; import com.example.spot.study.presentation.dto.response.StudyImageResponseDTO; +import java.util.Comparator; +import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.Comparator; -import java.util.List; - @Service @Transactional(readOnly = true) @RequiredArgsConstructor @@ -45,20 +44,20 @@ public class StoryQueryServiceImpl implements StoryQueryService { private final StoryRepository storyRepository; private final StudyMemberRepository studyMemberRepository; -/* ----------------------------- 스터디 게시글 관련 API ------------------------------------- */ + /* ----------------------------- 스터디 게시글 관련 API ------------------------------------- */ /** - * 특정 테마(카테고리)에 속한 스터디 게시글 목록을 조회하는 메서드입니다. - * 오프셋 기반 페이징이 적용되어 있습니다. - * @param pageRequest 페이징에 필요한 페이지 번호와 페이지 사이즈 정보를 입력 받습니다. - * @param studyId 게시글 목록을 조회할 타겟 스터디의 아이디를 입력 받습니다. + * 특정 테마(카테고리)에 속한 스터디 게시글 목록을 조회하는 메서드입니다. 오프셋 기반 페이징이 적용되어 있습니다. + * + * @param pageRequest 페이징에 필요한 페이지 번호와 페이지 사이즈 정보를 입력 받습니다. + * @param studyId 게시글 목록을 조회할 타겟 스터디의 아이디를 입력 받습니다. * @param storyCategoryQuery 게시글 테마를 입력 받습니다. themeQuery는 null일 수 있습니다. - * @return 조건에 맞는 스터디 게시글 목록을 반환합니다. - * 1. themeQuery가 있는 경우 해당 테마의 게시글 목록을 반환합니다. - * 2. themeQuery가 null인 경우 필터링 없이 게시글 목록을 반환합니다. + * @return 조건에 맞는 스터디 게시글 목록을 반환합니다. 1. themeQuery가 있는 경우 해당 테마의 게시글 목록을 반환합니다. 2. themeQuery가 null인 경우 필터링 없이 게시글 + * 목록을 반환합니다. */ @Override - public StoryResponseDTO.StoryListDTO getAllPosts(PageRequest pageRequest, Long studyId, StoryCategoryQuery storyCategoryQuery) { + public StoryResponseDTO.StoryListDTO getAllPosts(PageRequest pageRequest, Long studyId, + StoryCategoryQuery storyCategoryQuery) { Long memberId = SecurityUtils.getCurrentUserId(); SecurityUtils.verifyUserId(memberId); @@ -168,8 +167,9 @@ public StoryResponseDTO.StoryDetailDTO getPost(Long studyId, Long postId, Boolea public StoryResponseDTO.StoryContentDTO findStudyAnnouncementPost(Long studyId) { // 로그인한 회원이 해당 스터디 회원인지 확인 - if (!isMember(SecurityUtils.getCurrentUserId(), studyId)) + if (!isMember(SecurityUtils.getCurrentUserId(), studyId)) { throw new GeneralException(ErrorStatus._ONLY_STUDY_MEMBER_CAN_ACCESS_ANNOUNCEMENT_POST); + } // 스터디 공지사항 조회 Story story = storyRepository.findByStudyIdAndIsAnnouncement( @@ -190,15 +190,17 @@ public StoryResponseDTO.StoryContentDTO findStudyAnnouncementPost(Long studyId) * @return 스터디 참여 여부를 반환합니다. */ private boolean isMember(Long memberId, Long studyId) { - return studyMemberRepository.findByMemberIdAndStudyIdAndStatus(memberId, studyId, StudyApplicationStatus.APPROVED).isPresent(); + return studyMemberRepository.findByMemberIdAndStudyIdAndStatus(memberId, studyId, + StudyApplicationStatus.APPROVED).isPresent(); } /* ----------------------------- 스터디 게시글 댓글 관련 API ------------------------------------- */ /** * 특정 스터디 게시글의 모든 댓글을 조회하는 메서드입니다. + * * @param studyId 게시글이 작성된 타겟 스터디의 아이디를 입력 받습니다. - * @param postId 댓글이 작성된 게시글의 아이디를 입력 받습니다. + * @param postId 댓글이 작성된 게시글의 아이디를 입력 받습니다. * @return 스터디 게시글에 작성된 댓글의 목록을 반환합니다. 하나의 댓글에는 해당 댓글에 대한 답글 목록이 포함되어 있습니다. */ @Override @@ -233,11 +235,12 @@ public StoryCommentResponseDTO.ReplyListDTO getAllComments(Long studyId, Long po } -/* ----------------------------- 스터디 갤러리 관련 API ------------------------------------- */ + /* ----------------------------- 스터디 갤러리 관련 API ------------------------------------- */ /** * 스터디 게시판에 업로드한 이미지 목록을 불러오는 메서드입니다. - * @param studyId 타겟 스터디의 아이디를 입력 받습니다. + * + * @param studyId 타겟 스터디의 아이디를 입력 받습니다. * @param pageRequest 페이징에 필요한 페이지 번호와 크기를 입력 받습니다. * @return 스터디 아이디와 해당 스터디에 업로드된 이미지 목록을 반환합니다. */ diff --git a/src/main/java/com/example/spot/story/domain/Story.java b/src/main/java/com/example/spot/story/domain/Story.java index dcc9d829..f0b1ac18 100644 --- a/src/main/java/com/example/spot/story/domain/Story.java +++ b/src/main/java/com/example/spot/story/domain/Story.java @@ -1,21 +1,35 @@ package com.example.spot.story.domain; -import com.example.spot.member.domain.Member; import com.example.spot.common.entity.BaseEntity; +import com.example.spot.member.domain.Member; +import com.example.spot.report.domain.StoryReport; import com.example.spot.story.domain.association.LikedStory; import com.example.spot.story.domain.association.StoryComment; import com.example.spot.story.domain.association.StoryImage; -import com.example.spot.report.domain.StoryReport; import com.example.spot.story.domain.enums.StoryCategory; -import com.example.spot.study.domain.Study; import com.example.spot.story.web.dto.request.StoryRequestDTO; -import jakarta.persistence.*; - +import com.example.spot.study.domain.Study; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; - -import lombok.*; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; @Entity @Getter @@ -81,7 +95,7 @@ public class Story extends BaseEntity { @OneToMany(mappedBy = "story", cascade = CascadeType.ALL) private List storyReports = new ArrayList<>(); -/* ----------------------------- 연관관계 메소드 ------------------------------------- */ + /* ----------------------------- 연관관계 메소드 ------------------------------------- */ public void addImage(StoryImage image) { images.add(image); @@ -116,19 +130,16 @@ public void updateComment(StoryComment storyComment) { public void plusHitNum() { hitNum++; - member.updateStudyPost(this); study.updateStudyPost(this); } public void plusLikeNum() { likeNum++; - member.updateStudyPost(this); study.updateStudyPost(this); } public void minusLikeNum() { likeNum--; - member.updateStudyPost(this); study.updateStudyPost(this); } @@ -148,7 +159,6 @@ public void updatePost(StoryRequestDTO.StoryDTO requestDTO) { announcedAt = null; } - member.updateStudyPost(this); study.updateStudyPost(this); } diff --git a/src/main/java/com/example/spot/study/application/StudyCommandServiceImpl.java b/src/main/java/com/example/spot/study/application/StudyCommandServiceImpl.java index 4e1de468..13e30089 100644 --- a/src/main/java/com/example/spot/study/application/StudyCommandServiceImpl.java +++ b/src/main/java/com/example/spot/study/application/StudyCommandServiceImpl.java @@ -1,34 +1,35 @@ package com.example.spot.study.application; -import com.example.spot.study.domain.association.StudyRegion; import com.example.spot.common.api.code.status.ErrorStatus; import com.example.spot.common.api.exception.handler.MemberHandler; import com.example.spot.common.api.exception.handler.StudyHandler; +import com.example.spot.common.security.utils.SecurityUtils; import com.example.spot.member.domain.Member; +import com.example.spot.member.domain.association.PreferredStudy; +import com.example.spot.member.domain.enums.Status; +import com.example.spot.member.infrastructure.MemberRepository; +import com.example.spot.member.infrastructure.PreferredStudyRepository; +import com.example.spot.study.domain.Study; +import com.example.spot.study.domain.StudyRepository; import com.example.spot.study.domain.association.Region; import com.example.spot.study.domain.association.StudyMember; +import com.example.spot.study.domain.association.StudyRegion; +import com.example.spot.study.domain.association.StudyTheme; import com.example.spot.study.domain.association.Theme; import com.example.spot.study.domain.enums.StudyApplicationStatus; -import com.example.spot.member.domain.enums.Status; import com.example.spot.study.domain.enums.StudyLikeStatus; import com.example.spot.study.domain.enums.StudyState; -import com.example.spot.member.domain.association.PreferredStudy; -import com.example.spot.study.domain.association.StudyTheme; -import com.example.spot.study.domain.Study; -import com.example.spot.member.domain.MemberRepository; -import com.example.spot.study.domain.repository.StudyMemberRepository; -import com.example.spot.member.domain.association.PreferredStudyRepository; import com.example.spot.study.domain.repository.RegionRepository; +import com.example.spot.study.domain.repository.StudyMemberRepository; import com.example.spot.study.domain.repository.StudyRegionRepository; -import com.example.spot.study.domain.StudyRepository; import com.example.spot.study.domain.repository.StudyThemeRepository; import com.example.spot.study.domain.repository.ThemeRepository; -import com.example.spot.common.security.utils.SecurityUtils; import com.example.spot.study.presentation.dto.request.StudyMemberRequestDTO; import com.example.spot.study.presentation.dto.request.StudyMemberRequestDTO.RegisterDTO; import com.example.spot.study.presentation.dto.response.StudyMemberResponseDTO; import com.example.spot.study.presentation.dto.response.StudyResponseDTO; import jakarta.validation.Valid; +import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -36,8 +37,6 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.List; - @Service @Slf4j @Transactional @@ -64,7 +63,8 @@ public class StudyCommandServiceImpl implements StudyCommandService { // [스터디 생성/참여] 참여 신청하기 @Transactional - public StudyMemberResponseDTO.JoinDTO applyToStudy(Long studyId, StudyMemberRequestDTO.@Valid JoinDTO studyJoinRequestDTO) { + public StudyMemberResponseDTO.JoinDTO applyToStudy(Long studyId, + StudyMemberRequestDTO.@Valid JoinDTO studyJoinRequestDTO) { // Authorization Long memberId = SecurityUtils.getCurrentUserId(); @@ -81,12 +81,14 @@ public StudyMemberResponseDTO.JoinDTO applyToStudy(Long studyId, StudyMemberRequ throw new StudyHandler(ErrorStatus._STUDY_NOT_RECRUITING); } - if (study.getMaxPeople() <= studyMemberRepository.countByStatusAndStudyId(StudyApplicationStatus.APPROVED, studyId)) + if (study.getMaxPeople() <= studyMemberRepository.countByStatusAndStudyId(StudyApplicationStatus.APPROVED, + studyId)) { throw new StudyHandler(ErrorStatus._STUDY_IS_FULL); - + } // 이미 신청한 스터디에 다시 신청할 수 없음 - List studyMemberList = studyMemberRepository.findByMemberIdAndStatusNot(memberId, StudyApplicationStatus.REJECTED).stream() + List studyMemberList = studyMemberRepository.findByMemberIdAndStatusNot(memberId, + StudyApplicationStatus.REJECTED).stream() .filter(memberStudy -> study.equals(memberStudy.getStudy())) .toList(); @@ -107,7 +109,6 @@ public StudyMemberResponseDTO.JoinDTO applyToStudy(Long studyId, StudyMemberRequ .status(StudyApplicationStatus.APPLIED) .build(); - member.addMemberStudy(studyMember); study.addMemberStudy(studyMember); studyMemberRepository.save(studyMember); @@ -157,7 +158,8 @@ public StudyResponseDTO.RegisterDTO registerStudy(StudyMemberRequestDTO.Register /** * 스터디 정보를 수정합니다. - * @param studyId 수정할 스터디 ID + * + * @param studyId 수정할 스터디 ID * @param studyInfoDTO 수정할 스터디 정보 * @return 수정된 스터디 정보를 반환합니다. */ @@ -177,7 +179,8 @@ public StudyResponseDTO.RegisterDTO updateStudyInfo(Long studyId, RegisterDTO st Study study = studyRepository.findById(studyId) .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_NOT_FOUND)); - study.updateStudyInfo(studyInfoDTO.getTitle(), studyInfoDTO.getIntroduction(), studyInfoDTO.getGoal(), studyInfoDTO.getIsOnline(), + study.updateStudyInfo(studyInfoDTO.getTitle(), studyInfoDTO.getIntroduction(), studyInfoDTO.getGoal(), + studyInfoDTO.getIsOnline(), studyInfoDTO.isHasFee(), studyInfoDTO.getFee(), studyInfoDTO.getMinAge(), studyInfoDTO.getMaxAge(), studyInfoDTO.getGender(), studyInfoDTO.getMaxPeople(), studyInfoDTO.getProfileImage()); @@ -197,10 +200,11 @@ public StudyResponseDTO.RegisterDTO updateStudyInfo(Long studyId, RegisterDTO st /** * 특정 스터디에 좋아요를 누르거나 취소합니다. 이미 좋아요가 눌려있다면 취소하고, 아니라면 좋아요를 누릅니다. + * * @param memberId 회원 ID - * @param studyId 스터디 ID + * @param studyId 스터디 ID * @return 스터디 제목과 좋아요 상태를 반환합니다. - * @throws StudyHandler 스터디가 존재하지 않는 경우 + * @throws StudyHandler 스터디가 존재하지 않는 경우 * @throws MemberHandler 회원이 존재하지 않는 경우 */ @Override @@ -208,18 +212,18 @@ public StudyResponseDTO.LikeDTO likeStudy(Long memberId, Long studyId) { // 회원과 스터디 조회 Study study = studyRepository.findById(studyId) - .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_NOT_FOUND)); + .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_NOT_FOUND)); Member member = memberRepository.findById(memberId) - .orElseThrow(() -> new MemberHandler(ErrorStatus._MEMBER_NOT_FOUND)); + .orElseThrow(() -> new MemberHandler(ErrorStatus._MEMBER_NOT_FOUND)); // 현재 좋아요 상태 확인 -> 만약 없다면, 객체 하나 생성 PreferredStudy preferredStudy = preferredStudyRepository - .findByMemberIdAndStudyId(memberId, studyId) - .orElse(PreferredStudy.builder() - .member(member) - .study(study) - .studyLikeStatus(StudyLikeStatus.DISLIKE) - .build()); + .findByMemberIdAndStudyId(memberId, studyId) + .orElse(PreferredStudy.builder() + .member(member) + .study(study) + .studyLikeStatus(StudyLikeStatus.DISLIKE) + .build()); // 상태에 따라 변경 if (preferredStudy.getStudyLikeStatus() == StudyLikeStatus.LIKE) { @@ -245,7 +249,6 @@ private void createMemberStudy(Member member, Study study) { .status(StudyApplicationStatus.APPROVED) .build(); - member.addMemberStudy(studyMember); study.addMemberStudy(studyMember); studyMemberRepository.save(studyMember); @@ -300,10 +303,11 @@ private void createStudyTheme(Study study, StudyMemberRequestDTO.RegisterDTO stu /** * 검색어를 인기 검색어(Redis)에 추가합니다. 이미 존재하는 검색어라면 score를 1 증가시킵니다. + * * @param keyword 검색어 */ @Override public void addHotKeyword(String keyword) { - redisTemplate.opsForZSet().incrementScore(KEYWORD, keyword, 1); + redisTemplate.opsForZSet().incrementScore(KEYWORD, keyword, 1); } } diff --git a/src/main/java/com/example/spot/study/application/StudyMemberCommandServiceImpl.java b/src/main/java/com/example/spot/study/application/StudyMemberCommandServiceImpl.java index 5e308594..a345987c 100644 --- a/src/main/java/com/example/spot/study/application/StudyMemberCommandServiceImpl.java +++ b/src/main/java/com/example/spot/study/application/StudyMemberCommandServiceImpl.java @@ -4,26 +4,25 @@ import com.example.spot.common.api.exception.GeneralException; import com.example.spot.common.api.exception.handler.MemberHandler; import com.example.spot.common.api.exception.handler.StudyHandler; +import com.example.spot.common.application.s3.S3ImageService; +import com.example.spot.common.security.utils.SecurityUtils; import com.example.spot.member.domain.Member; +import com.example.spot.member.domain.enums.Status; +import com.example.spot.member.infrastructure.MemberRepository; import com.example.spot.notification.domain.Notification; -import com.example.spot.study.domain.enums.StudyApplicationStatus; +import com.example.spot.notification.domain.NotificationRepository; import com.example.spot.notification.domain.enums.NotifyType; -import com.example.spot.member.domain.enums.Status; -import com.example.spot.study.domain.association.StudyMember; -import com.example.spot.study.domain.Study; import com.example.spot.report.domain.MemberReportRepository; -import com.example.spot.member.domain.MemberRepository; -import com.example.spot.study.domain.repository.StudyMemberRepository; -import com.example.spot.notification.domain.NotificationRepository; import com.example.spot.report.domain.StoryReportRepository; import com.example.spot.story.domain.StoryRepository; +import com.example.spot.study.domain.Study; import com.example.spot.study.domain.StudyRepository; +import com.example.spot.study.domain.association.StudyMember; +import com.example.spot.study.domain.enums.StudyApplicationStatus; +import com.example.spot.study.domain.repository.StudyMemberRepository; import com.example.spot.study.presentation.dto.request.StudyMemberRequestDTO; import com.example.spot.study.presentation.dto.response.StudyMemberResponseDTO; import com.example.spot.study.presentation.dto.response.StudyResponseDTO; -import com.example.spot.common.security.utils.SecurityUtils; -import com.example.spot.common.application.s3.S3ImageService; - import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -46,11 +45,10 @@ public class StudyMemberCommandServiceImpl implements StudyMemberCommandService // S3 Service private final S3ImageService s3ImageService; -/* ----------------------------- 진행중인 스터디 관련 API ------------------------------------- */ + /* ----------------------------- 진행중인 스터디 관련 API ------------------------------------- */ /** - * 진행중인 스터디에서 탈퇴하기 위한 메서드입니다. - * 스터디장은 스터디를 탈퇴할 수 없으며 스터디를 종료하고자 하는 경우 스터디 terminateStudy API를 호출해야 합니다. + * 진행중인 스터디에서 탈퇴하기 위한 메서드입니다. 스터디장은 스터디를 탈퇴할 수 없으며 스터디를 종료하고자 하는 경우 스터디 terminateStudy API를 호출해야 합니다. * * @param studyId 타겟 회원이 탈퇴하고자 하는 스터디의 아이디를 입력 받습니다. * @return 탈퇴한 스터디의 아이디와 이름, 탈퇴한 회원의 아이디와 이름이 반환됩니다. @@ -67,7 +65,8 @@ public StudyMemberResponseDTO.WithdrawalDTO withdrawFromStudy(Long studyId) { Study study = studyRepository.findById(studyId) .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_NOT_FOUND)); - StudyMember studyMember = studyMemberRepository.findByMemberIdAndStudyIdAndStatus(memberId, studyId, StudyApplicationStatus.APPROVED) + StudyMember studyMember = studyMemberRepository.findByMemberIdAndStudyIdAndStatus(memberId, studyId, + StudyApplicationStatus.APPROVED) .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_MEMBER_NOT_FOUND)); // 참여가 승인되지 않은 스터디는 탈퇴할 수 없음 @@ -85,19 +84,22 @@ public StudyMemberResponseDTO.WithdrawalDTO withdrawFromStudy(Long studyId) { } @Override - public StudyMemberResponseDTO.WithdrawalDTO withdrawHostFromStudy(Long studyId, StudyMemberRequestDTO.HostWithdrawDTO hostWithdrawDTO) { + public StudyMemberResponseDTO.WithdrawalDTO withdrawHostFromStudy(Long studyId, + StudyMemberRequestDTO.HostWithdrawDTO hostWithdrawDTO) { // Authorization Long hostId = SecurityUtils.getCurrentUserId(); SecurityUtils.verifyUserId(hostId); - StudyMember studyMember = studyMemberRepository.findByMemberIdAndStudyIdAndStatus(hostId, studyId, StudyApplicationStatus.APPROVED) + StudyMember studyMember = studyMemberRepository.findByMemberIdAndStudyIdAndStatus(hostId, studyId, + StudyApplicationStatus.APPROVED) .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_MEMBER_NOT_FOUND)); if (!studyMember.getIsOwned()) { throw new StudyHandler(ErrorStatus._STUDY_OWNER_ONLY_CAN_WITHDRAW); } - StudyMember newHostStudy = studyMemberRepository.findByMemberIdAndStudyIdAndStatus(hostWithdrawDTO.getNewHostId(), studyId, StudyApplicationStatus.APPROVED) + StudyMember newHostStudy = studyMemberRepository.findByMemberIdAndStudyIdAndStatus( + hostWithdrawDTO.getNewHostId(), studyId, StudyApplicationStatus.APPROVED) .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_MEMBER_NOT_EXIST)); studyMemberRepository.delete(studyMember); @@ -109,11 +111,12 @@ public StudyMemberResponseDTO.WithdrawalDTO withdrawHostFromStudy(Long studyId, return StudyMemberResponseDTO.WithdrawalDTO.toDTO(newHostStudy.getMember(), newHostStudy.getStudy()); } + /** * 운영중인 스터디를 종료하는 메서드입니다. 스터디장만 호출 가능합니다. * - * @param studyId 종료할 스터디의 아이디를 입력 받습니다. - * @param performance 종료할 스터디의 성과를 입력 받습니다. + * @param studyId 종료할 스터디의 아이디를 입력 받습니다. + * @param performance 종료할 스터디의 성과를 입력 받습니다. * @return 종료된 스터디의 아이디, 이름, 상태를 반환합니다. */ public StudyResponseDTO.TerminationDTO terminateStudy(Long studyId, String performance) { @@ -124,7 +127,8 @@ public StudyResponseDTO.TerminationDTO terminateStudy(Long studyId, String perfo .orElseThrow(() -> new MemberHandler(ErrorStatus._MEMBER_NOT_FOUND)); Study study = studyRepository.findById(studyId) .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_NOT_FOUND)); - StudyMember studyMember = studyMemberRepository.findByMemberIdAndStudyIdAndStatus(memberId, studyId, StudyApplicationStatus.APPROVED) + StudyMember studyMember = studyMemberRepository.findByMemberIdAndStudyIdAndStatus(memberId, studyId, + StudyApplicationStatus.APPROVED) .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_MEMBER_NOT_FOUND)); // 스터디장이 아니면 스터디를 종료할 수 없음 @@ -144,41 +148,45 @@ public StudyResponseDTO.TerminationDTO terminateStudy(Long studyId, String perfo } /** - * 스터디 신청을 처리합니다. isAccept가 true이면 승인, false이면 거절합니다. - * 이후 관련 알림을 생성합니다. 알림을 통해 최종 참여 승인을 해야 스터디에 참여할 수 있습니다. + * 스터디 신청을 처리합니다. isAccept가 true이면 승인, false이면 거절합니다. 이후 관련 알림을 생성합니다. 알림을 통해 최종 참여 승인을 해야 스터디에 참여할 수 있습니다. + * * @param memberId 스터디에 신청한 회원 ID - * @param studyId 스터디 ID + * @param studyId 스터디 ID * @param isAccept 승인 여부 * @return 스터디 신청 처리 결과 및 처리 시간 * @throws GeneralException 스터디 신청을 처리하는 회원이 스터디 소유자가 아닐 때 * @throws GeneralException 스터디 소유자가 신청한 경우 - * @throws StudyHandler 스터디 신청자를 찾을 수 없을 때 - * @throws StudyHandler 스터디 신청이 이미 처리되었을 때 - * @throws MemberHandler 스터디 장을 찾을 수 없을 때 + * @throws StudyHandler 스터디 신청자를 찾을 수 없을 때 + * @throws StudyHandler 스터디 신청이 이미 처리되었을 때 + * @throws MemberHandler 스터디 장을 찾을 수 없을 때 */ @Override public StudyMemberResponseDTO.ApplicationStatusDTO acceptAndRejectStudyApply(Long memberId, Long studyId, - boolean isAccept) { + boolean isAccept) { // 신청을 처리하는 회원이 스터디 소유자인지 확인 - if (!isOwner(SecurityUtils.getCurrentUserId(), studyId)) + if (!isOwner(SecurityUtils.getCurrentUserId(), studyId)) { throw new GeneralException(ErrorStatus._ONLY_STUDY_OWNER_CAN_ACCESS_APPLICANTS); + } // 스터디 신청자 조회 - StudyMember studyMember = studyMemberRepository.findByMemberIdAndStudyIdAndStatus(memberId, studyId, StudyApplicationStatus.APPLIED) - .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_APPLICANT_NOT_FOUND)); + StudyMember studyMember = studyMemberRepository.findByMemberIdAndStudyIdAndStatus(memberId, studyId, + StudyApplicationStatus.APPLIED) + .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_APPLICANT_NOT_FOUND)); // 스터디 소유자가 스터디 신청한 경우 - if (studyMember.getIsOwned()) + if (studyMember.getIsOwned()) { throw new GeneralException(ErrorStatus._STUDY_OWNER_CANNOT_APPLY); + } // 스터디 신청이 이미 처리되었을 때 - if (studyMember.getStatus() != StudyApplicationStatus.APPLIED) + if (studyMember.getStatus() != StudyApplicationStatus.APPLIED) { throw new GeneralException(ErrorStatus._STUDY_APPLY_ALREADY_PROCESSED); + } // 스터디 장 조회 Member owner = memberRepository.findById(SecurityUtils.getCurrentUserId()) - .orElseThrow(() -> new MemberHandler(ErrorStatus._MEMBER_NOT_FOUND)); + .orElseThrow(() -> new MemberHandler(ErrorStatus._MEMBER_NOT_FOUND)); // 승인인 경우 if (isAccept) { @@ -187,85 +195,87 @@ public StudyMemberResponseDTO.ApplicationStatusDTO acceptAndRejectStudyApply(Lon // 알림 생성 Notification notification = Notification.builder() - .member(studyMember.getMember()) // 신청자 - .study(studyMember.getStudy()) - .notifierName(owner.getName()) // 스터디장 이름 - .type(NotifyType.STUDY_APPLY) - .isChecked(Boolean.FALSE) - .build(); + .member(studyMember.getMember()) // 신청자 + .study(studyMember.getStudy()) + .notifierName(owner.getName()) // 스터디장 이름 + .type(NotifyType.STUDY_APPLY) + .isChecked(Boolean.FALSE) + .build(); notificationRepository.save(notification); - } - else { // 거절인 경우 + } else { // 거절인 경우 studyMember.setStatus(StudyApplicationStatus.REJECTED); studyMemberRepository.delete(studyMember); } // 스터디 신청 처리 결과 반환 return StudyMemberResponseDTO.ApplicationStatusDTO.builder() - .status(studyMember.getStatus()) - .updatedAt(studyMember.getUpdatedAt()) - .build(); + .status(studyMember.getStatus()) + .updatedAt(studyMember.getUpdatedAt()) + .build(); } /** - * 스터디 신청을 처리합니다. isAccept가 true이면 승인, false이면 거절합니다. - * 이 메서드를 사용하면 알림 처리 없이 바로 스터디에 참여할 수 있습니다. + * 스터디 신청을 처리합니다. isAccept가 true이면 승인, false이면 거절합니다. 이 메서드를 사용하면 알림 처리 없이 바로 스터디에 참여할 수 있습니다. + * * @param memberId 스터디에 신청한 회원 ID - * @param studyId 스터디 ID + * @param studyId 스터디 ID * @param isAccept 승인 여부 * @return 스터디 신청 처리 결과 및 처리 시간 * @throws GeneralException 스터디 신청을 처리하는 회원이 스터디 소유자가 아닐 때 * @throws GeneralException 스터디 소유자가 신청한 경우 - * @throws StudyHandler 스터디 신청자를 찾을 수 없을 때 - * @throws StudyHandler 스터디 신청이 이미 처리되었을 때 - * @throws MemberHandler 스터디 장을 찾을 수 없을 때 - * + * @throws StudyHandler 스터디 신청자를 찾을 수 없을 때 + * @throws StudyHandler 스터디 신청이 이미 처리되었을 때 + * @throws MemberHandler 스터디 장을 찾을 수 없을 때 */ @Override public StudyMemberResponseDTO.ApplicationStatusDTO acceptAndRejectStudyApplyForTest(Long memberId, Long studyId, - boolean isAccept) { + boolean isAccept) { // 스터디 신청을 처리하는 회원이 스터디 소유자인지 확인 - if (!isOwner(SecurityUtils.getCurrentUserId(), studyId)) + if (!isOwner(SecurityUtils.getCurrentUserId(), studyId)) { throw new GeneralException(ErrorStatus._ONLY_STUDY_OWNER_CAN_ACCESS_APPLICANTS); + } // 스터디 신청자 조회 - StudyMember studyMember = studyMemberRepository.findByMemberIdAndStudyIdAndStatus(memberId, studyId, StudyApplicationStatus.APPLIED) - .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_APPLICANT_NOT_FOUND)); + StudyMember studyMember = studyMemberRepository.findByMemberIdAndStudyIdAndStatus(memberId, studyId, + StudyApplicationStatus.APPLIED) + .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_APPLICANT_NOT_FOUND)); // 스터디 소유자가 스터디 신청한 경우 - if (studyMember.getIsOwned()) + if (studyMember.getIsOwned()) { throw new GeneralException(ErrorStatus._STUDY_OWNER_CANNOT_APPLY); + } // 스터디 신청이 이미 처리되었을 때 - if (studyMember.getStatus() != StudyApplicationStatus.APPLIED) + if (studyMember.getStatus() != StudyApplicationStatus.APPLIED) { throw new GeneralException(ErrorStatus._STUDY_APPLY_ALREADY_PROCESSED); + } // 스터디 장 조회 Member owner = memberRepository.findById(SecurityUtils.getCurrentUserId()) - .orElseThrow(() -> new MemberHandler(ErrorStatus._MEMBER_NOT_FOUND)); + .orElseThrow(() -> new MemberHandler(ErrorStatus._MEMBER_NOT_FOUND)); // 승인인 경우 if (isAccept) { studyMember.setStatus(StudyApplicationStatus.APPROVED); - } - else { + } else { studyMember.setStatus(StudyApplicationStatus.REJECTED); studyMemberRepository.delete(studyMember); } // 스터디 신청 처리 결과 반환 return StudyMemberResponseDTO.ApplicationStatusDTO.builder() - .status(studyMember.getStatus()) - .updatedAt(studyMember.getUpdatedAt()) - .build(); + .status(studyMember.getStatus()) + .updatedAt(studyMember.getUpdatedAt()) + .build(); } /** * 회원이 스터디 장인지 확인합니다. + * * @param memberId 확인 하려는 회원 ID - * @param studyId 확인 하려는 스터디 ID + * @param studyId 확인 하려는 스터디 ID * @return 스터디 장 여부를 반환합니다. */ private boolean isOwner(Long memberId, Long studyId) { diff --git a/src/main/java/com/example/spot/study/application/StudyMemberQueryServiceImpl.java b/src/main/java/com/example/spot/study/application/StudyMemberQueryServiceImpl.java index 412f5e7d..40b705e6 100644 --- a/src/main/java/com/example/spot/study/application/StudyMemberQueryServiceImpl.java +++ b/src/main/java/com/example/spot/study/application/StudyMemberQueryServiceImpl.java @@ -1,26 +1,23 @@ package com.example.spot.study.application; import com.example.spot.common.api.code.status.ErrorStatus; +import com.example.spot.common.api.exception.GeneralException; import com.example.spot.common.api.exception.handler.MemberHandler; import com.example.spot.common.api.exception.handler.StudyHandler; +import com.example.spot.common.security.utils.SecurityUtils; import com.example.spot.member.domain.Member; +import com.example.spot.member.infrastructure.MemberRepository; +import com.example.spot.story.domain.StoryRepository; +import com.example.spot.study.domain.StudyRepository; import com.example.spot.study.domain.association.StudyMember; import com.example.spot.study.domain.enums.StudyApplicationStatus; -import com.example.spot.member.domain.MemberRepository; import com.example.spot.study.domain.repository.StudyMemberRepository; -import com.example.spot.story.domain.StoryRepository; -import com.example.spot.study.domain.StudyRepository; import com.example.spot.study.presentation.dto.response.StudyMemberResponseDTO; - -import com.example.spot.common.security.utils.SecurityUtils; - +import java.util.List; import lombok.RequiredArgsConstructor; -import com.example.spot.common.api.exception.GeneralException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.List; - @Service @Transactional(readOnly = true) @@ -44,18 +41,21 @@ public class StudyMemberQueryServiceImpl implements StudyMemberQueryService { public StudyMemberResponseDTO.StudyMemberListDTO findStudyMembers(Long studyId) { // 스터디 멤버 조회 - List memberStudies = studyMemberRepository.findAllByStudyIdAndStatus(studyId, StudyApplicationStatus.APPROVED); + List memberStudies = studyMemberRepository.findAllByStudyIdAndStatus(studyId, + StudyApplicationStatus.APPROVED); // 스터디 멤버가 존재하지 않는 경우 - if (memberStudies.isEmpty()) + if (memberStudies.isEmpty()) { throw new GeneralException(ErrorStatus._STUDY_MEMBER_NOT_FOUND); + } // DTO로 변환하여 반환 - List memberDTOS = memberStudies.stream().map(memberStudy -> StudyMemberResponseDTO.StudyMemberDTO.builder() - .memberId(memberStudy.getMember().getId()) - .nickname(memberStudy.getMember().getName()) - .profileImage(memberStudy.getMember().getProfileImage()) - .build()).toList(); + List memberDTOS = memberStudies.stream() + .map(memberStudy -> StudyMemberResponseDTO.StudyMemberDTO.builder() + .memberId(memberStudy.getMember().getId()) + .nickname(memberStudy.getMember().getName()) + .profileImage(memberStudy.getMember().getProfileImage()) + .build()).toList(); // DTO로 변환하여 반환 return new StudyMemberResponseDTO.StudyMemberListDTO(memberDTOS); } @@ -73,22 +73,26 @@ public StudyMemberResponseDTO.StudyMemberListDTO findStudyMembers(Long studyId) public StudyMemberResponseDTO.StudyMemberListDTO findStudyApplicants(Long studyId) { // 로그인한 회원이 해당 스터디 장인지 확인 - if (!isOwner(SecurityUtils.getCurrentUserId(), studyId)) + if (!isOwner(SecurityUtils.getCurrentUserId(), studyId)) { throw new GeneralException(ErrorStatus._ONLY_STUDY_OWNER_CAN_ACCESS_APPLICANTS); + } // 스터디 신청자 조회 - List memberStudies = studyMemberRepository.findAllByStudyIdAndStatus(studyId, StudyApplicationStatus.APPLIED); + List memberStudies = studyMemberRepository.findAllByStudyIdAndStatus(studyId, + StudyApplicationStatus.APPLIED); // 스터디 신청자가 존재하지 않는 경우 - if (memberStudies.isEmpty()) + if (memberStudies.isEmpty()) { throw new GeneralException(ErrorStatus._STUDY_APPLICANT_NOT_FOUND); + } // DTO로 변환하여 반환 - List memberDTOS = memberStudies.stream().map(memberStudy -> StudyMemberResponseDTO.StudyMemberDTO.builder() - .memberId(memberStudy.getMember().getId()) - .nickname(memberStudy.getMember().getName()) - .profileImage(memberStudy.getMember().getProfileImage()) - .build()).toList(); + List memberDTOS = memberStudies.stream() + .map(memberStudy -> StudyMemberResponseDTO.StudyMemberDTO.builder() + .memberId(memberStudy.getMember().getId()) + .nickname(memberStudy.getMember().getName()) + .profileImage(memberStudy.getMember().getProfileImage()) + .build()).toList(); // DTO로 변환하여 반환 return new StudyMemberResponseDTO.StudyMemberListDTO(memberDTOS); @@ -128,16 +132,19 @@ public StudyMemberResponseDTO.HostDTO getStudyHost(Long studyId) { public StudyMemberResponseDTO.ApplyingMemberDTO findStudyApplication(Long studyId, Long memberId) { // 로그인한 회원이 해당 스터디 장인지 확인 - if (!isOwner(SecurityUtils.getCurrentUserId(), studyId)) + if (!isOwner(SecurityUtils.getCurrentUserId(), studyId)) { throw new GeneralException(ErrorStatus._ONLY_STUDY_OWNER_CAN_ACCESS_APPLICANTS); + } // 스터디 신청자 조회 - StudyMember studyMember = studyMemberRepository.findByMemberIdAndStudyIdAndStatus(memberId, studyId, StudyApplicationStatus.APPLIED) + StudyMember studyMember = studyMemberRepository.findByMemberIdAndStudyIdAndStatus(memberId, studyId, + StudyApplicationStatus.APPLIED) .orElseThrow(() -> new GeneralException(ErrorStatus._STUDY_APPLICANT_NOT_FOUND)); // 스터디 장은 스터디에 신청할 수 없음 - if (studyMember.getIsOwned()) + if (studyMember.getIsOwned()) { throw new GeneralException(ErrorStatus._STUDY_OWNER_CANNOT_APPLY); + } // DTO로 변환하여 반환 return StudyMemberResponseDTO.ApplyingMemberDTO.builder() @@ -162,12 +169,14 @@ public StudyMemberResponseDTO.AppliedStudyDTO isApplied(Long studyId) { Long currentUserId = SecurityUtils.getCurrentUserId(); // 이미 스터디 멤버인 경우 - if (isMember(currentUserId, studyId)) + if (isMember(currentUserId, studyId)) { throw new GeneralException(ErrorStatus._ALREADY_STUDY_MEMBER); + } // DTO로 변환하여 반환 return StudyMemberResponseDTO.AppliedStudyDTO.builder() - .isApplied(studyMemberRepository.existsByMemberIdAndStudyIdAndStatus(currentUserId, studyId, StudyApplicationStatus.APPLIED)) + .isApplied(studyMemberRepository.existsByMemberIdAndStudyIdAndStatus(currentUserId, studyId, + StudyApplicationStatus.APPLIED)) .studyId(studyId) .build(); @@ -192,7 +201,8 @@ private boolean isOwner(Long memberId, Long studyId) { * @return 스터디 참여 여부를 반환합니다. */ private boolean isMember(Long memberId, Long studyId) { - return studyMemberRepository.findByMemberIdAndStudyIdAndStatus(memberId, studyId, StudyApplicationStatus.APPROVED).isPresent(); + return studyMemberRepository.findByMemberIdAndStudyIdAndStatus(memberId, studyId, + StudyApplicationStatus.APPROVED).isPresent(); } } diff --git a/src/main/java/com/example/spot/study/application/StudyQueryServiceImpl.java b/src/main/java/com/example/spot/study/application/StudyQueryServiceImpl.java index 1c9b4415..b137cf0e 100644 --- a/src/main/java/com/example/spot/study/application/StudyQueryServiceImpl.java +++ b/src/main/java/com/example/spot/study/application/StudyQueryServiceImpl.java @@ -4,33 +4,33 @@ import com.example.spot.common.api.exception.GeneralException; import com.example.spot.common.api.exception.handler.MemberHandler; import com.example.spot.common.api.exception.handler.StudyHandler; +import com.example.spot.common.security.utils.SecurityUtils; import com.example.spot.member.domain.Member; +import com.example.spot.member.domain.association.PreferredRegion; +import com.example.spot.member.domain.association.PreferredStudy; +import com.example.spot.member.domain.association.PreferredTheme; +import com.example.spot.member.domain.enums.Gender; +import com.example.spot.member.domain.enums.Status; +import com.example.spot.member.infrastructure.MemberRepository; +import com.example.spot.member.infrastructure.PreferredRegionRepository; +import com.example.spot.member.infrastructure.PreferredStudyRepository; +import com.example.spot.member.infrastructure.PreferredThemeRepository; +import com.example.spot.study.domain.Study; +import com.example.spot.study.domain.StudyRepository; import com.example.spot.study.domain.association.Region; import com.example.spot.study.domain.association.StudyMember; +import com.example.spot.study.domain.association.StudyRegion; +import com.example.spot.study.domain.association.StudyTheme; import com.example.spot.study.domain.association.Theme; import com.example.spot.study.domain.enums.StudyApplicationStatus; -import com.example.spot.member.domain.enums.Gender; -import com.example.spot.member.domain.enums.Status; import com.example.spot.study.domain.enums.StudyLikeStatus; import com.example.spot.study.domain.enums.StudySortBy; import com.example.spot.study.domain.enums.ThemeType; -import com.example.spot.member.domain.association.MemberTheme; -import com.example.spot.member.domain.association.PreferredRegion; -import com.example.spot.member.domain.association.PreferredStudy; -import com.example.spot.study.domain.association.StudyRegion; -import com.example.spot.study.domain.association.StudyTheme; -import com.example.spot.study.domain.Study; -import com.example.spot.member.domain.MemberRepository; -import com.example.spot.study.domain.repository.StudyMemberRepository; -import com.example.spot.member.domain.association.MemberThemeRepository; -import com.example.spot.member.domain.association.PreferredRegionRepository; -import com.example.spot.member.domain.association.PreferredStudyRepository; import com.example.spot.study.domain.repository.RegionRepository; +import com.example.spot.study.domain.repository.StudyMemberRepository; import com.example.spot.study.domain.repository.StudyRegionRepository; -import com.example.spot.study.domain.StudyRepository; import com.example.spot.study.domain.repository.StudyThemeRepository; import com.example.spot.study.domain.repository.ThemeRepository; -import com.example.spot.common.security.utils.SecurityUtils; import com.example.spot.study.presentation.dto.request.StudySearchRequestDTO; import com.example.spot.study.presentation.dto.request.StudySearchRequestWithThemeDTO; import com.example.spot.study.presentation.dto.response.SearchResponseDTO; @@ -38,7 +38,7 @@ import com.example.spot.study.presentation.dto.response.SearchResponseDTO.MyPageDTO; import com.example.spot.study.presentation.dto.response.SearchResponseDTO.SearchStudyDTO; import com.example.spot.study.presentation.dto.response.SearchResponseDTO.StudyPreviewDTO; - +import com.example.spot.study.presentation.dto.response.StudyResponseDTO; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -48,8 +48,6 @@ import java.util.Map; import java.util.Set; import java.util.stream.Collectors; - -import com.example.spot.study.presentation.dto.response.StudyResponseDTO; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -85,7 +83,7 @@ public class StudyQueryServiceImpl implements StudyQueryService { // 관심사 관련 조회 private final ThemeRepository themeRepository; private final StudyThemeRepository studyThemeRepository; - private final MemberThemeRepository memberThemeRepository; + private final PreferredThemeRepository preferredThemeRepository; // 지역 관련 조회 private final PreferredRegionRepository preferredRegionRepository; @@ -95,8 +93,8 @@ public class StudyQueryServiceImpl implements StudyQueryService { private final RedisTemplate redisTemplate; /** - * 인기 검색어를 조회하는 메서드입니다. 인기 검색어는 매일 13시, 18시에 총 2번 업데이트 됩니다. - * 인기 검색어는 검색된 횟수 순으로 5개까지 조회 가능합니다. + * 인기 검색어를 조회하는 메서드입니다. 인기 검색어는 매일 13시, 18시에 총 2번 업데이트 됩니다. 인기 검색어는 검색된 횟수 순으로 5개까지 조회 가능합니다. + * * @return 인기 검색어 목록 및 업데이트 시간을 반환합니다. */ @Override @@ -107,8 +105,9 @@ public HotKeywordDTO getHotKeyword() { Set> typedTuples = zSetOperations.reverseRangeWithScores(HOT_KEYWORD, 0, 4); // 캐시된 검색어가 없을 경우 - if (typedTuples.isEmpty()) + if (typedTuples.isEmpty()) { throw new GeneralException(ErrorStatus._HOT_KEYWORD_NOT_FOUND); + } // 순서를 보장하는 LinkedHashSet을 사용합니다. Set keywordDTOS = new LinkedHashSet<>(); @@ -116,22 +115,23 @@ public HotKeywordDTO getHotKeyword() { // DTO로 변환 for (TypedTuple tuple : typedTuples) { keywordDTOS.add(HotKeywordDTO.KeywordDTO.builder() - .keyword(tuple.getValue()) - .point(tuple.getScore()) - .build()); + .keyword(tuple.getValue()) + .point(tuple.getScore()) + .build()); } // 마지막 업데이트 시간 가져오기 String updatedAt = redisTemplate.opsForValue().get(LAST_UPDATED); return HotKeywordDTO.builder() - .keyword(keywordDTOS) - .updatedAt(updatedAt) // 업데이트 시간 설정 - .build(); + .keyword(keywordDTOS) + .updatedAt(updatedAt) // 업데이트 시간 설정 + .build(); } /** * 스터디의 상세 정보를 조회하는 메서드입니다 + * * @param studyId 스터디의 아이디를 입력 받습니다. * @return 스터디의 상세 정보를 반환합니다. * @throws StudyHandler 스터디가 존재하지 않을 경우 Exception을 발생시킵니다. @@ -157,7 +157,8 @@ public StudyResponseDTO.StudyInfoDTO getStudyInfo(Long studyId) { /** * 마이페이지에 들어갈 스터디 갯수 관련 정보를 조회 하는 메서드입니다. - * @param memberId 회원의 아이디를 입력 받습니다. + * + * @param memberId 회원의 아이디를 입력 받습니다. * @return 지원한 스터디, 참여중인 스터디, 모집중인 스터디 갯수를 반환합니다. * @throws MemberHandler 회원이 존재하지 않을 경우 Exception을 발생시킵니다. */ @@ -165,31 +166,32 @@ public StudyResponseDTO.StudyInfoDTO getStudyInfo(Long studyId) { public MyPageDTO getMyPageStudyCount(Long memberId) { // 회원 조회 Member member = memberRepository.findById(memberId) - .orElseThrow(() -> new MemberHandler(ErrorStatus._MEMBER_NOT_FOUND)); + .orElseThrow(() -> new MemberHandler(ErrorStatus._MEMBER_NOT_FOUND)); // 내가 신청한 스터디 수 - long appliedStudies = studyMemberRepository.countByMemberIdAndStatusAndStudy_Status(memberId, StudyApplicationStatus.APPLIED, Status.ON); + long appliedStudies = studyMemberRepository.countByMemberIdAndStatusAndStudy_Status(memberId, + StudyApplicationStatus.APPLIED, Status.ON); // 내가 참여중인 스터디 수 - long ongoingStudies = studyMemberRepository.countByMemberIdAndStatusAndStudy_Status(memberId, StudyApplicationStatus.APPROVED, Status.ON); + long ongoingStudies = studyMemberRepository.countByMemberIdAndStatusAndStudy_Status(memberId, + StudyApplicationStatus.APPROVED, Status.ON); // 내가 모집중인 스터디 수 - long myRecruitingStudies = studyMemberRepository.countByMemberIdAndIsOwnedAndStudy_Status(memberId, true, Status.ON); + long myRecruitingStudies = studyMemberRepository.countByMemberIdAndIsOwnedAndStudy_Status(memberId, true, + Status.ON); return MyPageDTO.builder() - .name(member.getName()) - .appliedStudies(appliedStudies) - .ongoingStudies(ongoingStudies) - .myRecruitingStudies(myRecruitingStudies) - .build(); + .name(member.getName()) + .appliedStudies(appliedStudies) + .ongoingStudies(ongoingStudies) + .myRecruitingStudies(myRecruitingStudies) + .build(); } /** * 검색 조건 없이 전체 스터디를 조회하는 메서드입니다. * * @param pageable 페이지 정보를 입력 받습니다. - * @param sortBy 정렬 기준을 입력 받습니다. - * + * @param sortBy 정렬 기준을 입력 받습니다. * @return 입력한 조건에 맞는 스터디 목록과 조회된 스터디 갯수를 함께 반환합니다. - * * @throws StudyHandler 조회된 스터디가 없을 경우 Exception을 발생시킵니다. */ @Override @@ -198,8 +200,9 @@ public StudyPreviewDTO findStudies(Pageable pageable, StudySortBy sortBy) { List studies = studyRepository.findAllStudy(sortBy, pageable); // 조회된 스터디가 없을 경우 - if (studies.isEmpty()) + if (studies.isEmpty()) { throw new StudyHandler(ErrorStatus._STUDY_IS_NOT_MATCH); + } // 전체 스터디 수 long totalElements = studyRepository.count(); @@ -210,14 +213,14 @@ public StudyPreviewDTO findStudies(Pageable pageable, StudySortBy sortBy) { * 검색 조건을 통해 전체 스터디를 조회하는 메서드입니다. * * @param pageable 페이지 정보를 입력 받습니다. - * @param sortBy 정렬 기준을 입력 받습니다. - * @param request 검색 조건을 입력 받습니다. + * @param sortBy 정렬 기준을 입력 받습니다. + * @param request 검색 조건을 입력 받습니다. * @return 입력한 조건에 맞는 스터디 목록과 조회된 스터디 갯수를 함께 반환합니다. * @throws StudyHandler 조회된 스터디가 없을 경우 Exception을 발생시킵니다. */ @Override public StudyPreviewDTO findStudiesByConditions(Pageable pageable, StudySearchRequestDTO request, - StudySortBy sortBy) { + StudySortBy sortBy) { // 검색 조건 맵 생성 Map conditions = getSearchConditions(request); @@ -225,8 +228,9 @@ public StudyPreviewDTO findStudiesByConditions(Pageable pageable, StudySearchReq List studies = studyRepository.findAllStudyByConditions(conditions, sortBy, pageable); // 조회된 스터디가 없을 경우 - if (studies.isEmpty()) + if (studies.isEmpty()) { throw new StudyHandler(ErrorStatus._STUDY_IS_NOT_MATCH); + } // 전체 스터디 수 long totalElements = studyRepository.countStudyByConditions(conditions, sortBy); @@ -239,8 +243,8 @@ public StudyPreviewDTO findStudiesByConditions(Pageable pageable, StudySearchReq * @param memberId 회원의 아이디를 입력 받습니다. * @return 입력한 조건에 맞는 스터디 목록과 조회된 스터디 갯수를 함께 반환합니다. * @throws MemberHandler 회원의 관심사가 존재하지 않는 경우 Exception을 발생시킵니다. - * @throws StudyHandler 조회된 스터디가 없을 경우 Exception을 발생시킵니다. - * @throws StudyHandler 회원의 관심사에 해당하는 스터디가 존재하지 않을 경우 Exception을 발생시킵니다. + * @throws StudyHandler 조회된 스터디가 없을 경우 Exception을 발생시킵니다. + * @throws StudyHandler 회원의 관심사에 해당하는 스터디가 존재하지 않을 경우 Exception을 발생시킵니다. */ @Override public StudyPreviewDTO findRecommendStudies(Long memberId) { @@ -249,20 +253,22 @@ public StudyPreviewDTO findRecommendStudies(Long memberId) { List memberOngoingStudyIds = getOngoingStudyIds(memberId); // 회원 관심사 조회 - List memberThemes = memberThemeRepository.findAllByMemberId(memberId); + List preferredThemes = preferredThemeRepository.findAllByMemberId(memberId); List preferredRegions = preferredRegionRepository.findAllByMemberId(memberId); // 회원 관심사가 없을 경우 - if (memberThemes.isEmpty()) + if (preferredThemes.isEmpty()) { throw new MemberHandler(ErrorStatus._STUDY_THEME_IS_INVALID); + } - if (preferredRegions.isEmpty()) + if (preferredRegions.isEmpty()) { throw new MemberHandler(ErrorStatus._STUDY_REGION_IS_INVALID); + } // MemberId로 회원 관심사 및 관심 지역 전체 조회 - List themes = memberThemes.stream() - .map(MemberTheme::getTheme) - .toList(); + List themes = preferredThemes.stream() + .map(PreferredTheme::getTheme) + .toList(); List regions = preferredRegions.stream() .map(PreferredRegion::getRegion) @@ -270,8 +276,8 @@ public StudyPreviewDTO findRecommendStudies(Long memberId) { // 회원 관심사로 스터디 테마 조회 List studyThemes = themes.stream() - .flatMap(theme -> studyThemeRepository.findAllByTheme(theme).stream()) - .toList(); + .flatMap(theme -> studyThemeRepository.findAllByTheme(theme).stream()) + .toList(); // 회원 관심 지역으로 스터디 지역 조회 List regionStudies = regions.stream() @@ -279,22 +285,27 @@ public StudyPreviewDTO findRecommendStudies(Long memberId) { .toList(); // 해당 관심사에 해당하는 스터디가 존재하지 않을 경우 - if (studyThemes.isEmpty()) + if (studyThemes.isEmpty()) { throw new StudyHandler(ErrorStatus._STUDY_THEME_NOT_EXIST); + } // 해당 관심 지역에 해당하는 스터디가 존재하지 않을 경우 - if (regionStudies.isEmpty()) + if (regionStudies.isEmpty()) { throw new StudyHandler(ErrorStatus._STUDY_REGION_NOT_EXIST); + } // 회원 관심사로 추천 스터디 조회 - List preferThemeStudies = studyRepository.findByStudyThemeAndNotInIds(studyThemes, memberOngoingStudyIds); + List preferThemeStudies = studyRepository.findByStudyThemeAndNotInIds(studyThemes, + memberOngoingStudyIds); // 회원 관심 지역으로 추천 스터디 조회 - List preferRegionStudies = studyRepository.findByRegionStudyAndNotInIds(regionStudies, memberOngoingStudyIds); + List preferRegionStudies = studyRepository.findByRegionStudyAndNotInIds(regionStudies, + memberOngoingStudyIds); // 추천 스터디가 없을 경우 - if (preferRegionStudies.isEmpty() || preferThemeStudies.isEmpty()) + if (preferRegionStudies.isEmpty() || preferThemeStudies.isEmpty()) { throw new StudyHandler(ErrorStatus._STUDY_IS_NOT_MATCH); + } // 두 리스트를 합쳐서 중복 제거 Set combinedStudies = new HashSet<>(preferThemeStudies); @@ -324,12 +335,13 @@ public StudyPreviewDTO findRecommendStudies(Long memberId) { public StudyPreviewDTO findInterestedStudies(Long memberId) { // 회원의 관심 Best 스터디 ID 가져오기 List studies = studyRepository.findAllStudyByConditions(new HashMap<>(), - StudySortBy.LIKED, - PageRequest.of(0, 3)); + StudySortBy.LIKED, + PageRequest.of(0, 3)); // 추천 스터디가 없을 경우 - if (studies.isEmpty()) + if (studies.isEmpty()) { throw new StudyHandler(ErrorStatus._STUDY_IS_NOT_MATCH); + } return getDTOs(studies, Pageable.unpaged(), studies.size(), memberId); } @@ -339,54 +351,54 @@ public StudyPreviewDTO findInterestedStudies(Long memberId) { * * @param pageable 페이지 정보를 입력 받습니다. * @param memberId 회원의 아이디를 입력 받습니다. - * @param sortBy 정렬 기준을 입력 받습니다. - * @param request 검색 조건을 입력 받습니다. - * + * @param sortBy 정렬 기준을 입력 받습니다. + * @param request 검색 조건을 입력 받습니다. * @return 입력한 조건에 맞는 스터디 목록과 조회된 스터디 갯수를 함께 반환합니다. - * * @throws MemberHandler 회원의 관심사가 존재하지 않는 경우 Exception을 발생시킵니다. - * @throws StudyHandler 조회된 스터디가 없을 경우 Exception을 발생시킵니다. - * @throws StudyHandler 회원의 관심사에 해당하는 스터디가 존재하지 않을 경우 Exception을 발생시킵니다. - * + * @throws StudyHandler 조회된 스터디가 없을 경우 Exception을 발생시킵니다. + * @throws StudyHandler 회원의 관심사에 해당하는 스터디가 존재하지 않을 경우 Exception을 발생시킵니다. */ @Override public StudyPreviewDTO findInterestStudiesByConditionsAll(Pageable pageable, Long memberId, - StudySearchRequestDTO request, StudySortBy sortBy) { + StudySearchRequestDTO request, StudySortBy sortBy) { // 회원이 참가하고 있는 스터디 ID 가져오기 List memberOngoingStudyIds = getOngoingStudyIds(memberId); // 회원 관심사 조회 - List themes = memberThemeRepository.findAllByMemberId(memberId).stream() - .map(MemberTheme::getTheme) - .toList(); + List themes = preferredThemeRepository.findAllByMemberId(memberId).stream() + .map(PreferredTheme::getTheme) + .toList(); // 회원의 관심사가 없을 경우 - if (themes.isEmpty()) + if (themes.isEmpty()) { throw new MemberHandler(ErrorStatus._STUDY_THEME_IS_INVALID); + } List studyThemes = themes.stream() - .flatMap(theme -> studyThemeRepository.findAllByTheme(theme).stream()) - .toList(); + .flatMap(theme -> studyThemeRepository.findAllByTheme(theme).stream()) + .toList(); // 해당 관심사에 해당하는 스터디가 존재하지 않을 경우 - if (studyThemes.isEmpty()) + if (studyThemes.isEmpty()) { throw new StudyHandler(ErrorStatus._STUDY_THEME_NOT_EXIST); + } // 검색 조건 맵 생성 Map conditions = getSearchConditions(request); // 검색 조건에 맞는 스터디 갯수 조회 long totalElements = studyRepository.countStudyByConditionsAndThemeTypesAndNotInIds( - conditions, studyThemes, sortBy, memberOngoingStudyIds); + conditions, studyThemes, sortBy, memberOngoingStudyIds); // 검색 조건에 맞는 스터디 조회 List studies = studyRepository.findStudyByConditionsAndThemeTypesAndNotInIds( - conditions, sortBy, - pageable, studyThemes, memberOngoingStudyIds); + conditions, sortBy, + pageable, studyThemes, memberOngoingStudyIds); // 조회된 스터디가 없을 경우 - if (studies.isEmpty()) + if (studies.isEmpty()) { throw new StudyHandler(ErrorStatus._STUDY_IS_NOT_MATCH); + } return getDTOs(studies, pageable, totalElements, memberId); } @@ -395,68 +407,69 @@ public StudyPreviewDTO findInterestStudiesByConditionsAll(Pageable pageable, Lon /** * 회원의 특정 관심 분야에 해당 되는 모든 스터디를 조회 합니다. 회원이 현재 진행중인 스터디는 제외합니다. * - * @param pageable 페이지 정보를 입력 받습니다. - * @param memberId 회원의 아이디를 입력 받습니다. - * @param request 검색 조건을 입력 받습니다. + * @param pageable 페이지 정보를 입력 받습니다. + * @param memberId 회원의 아이디를 입력 받습니다. + * @param request 검색 조건을 입력 받습니다. * @param themeType 관심 분야를 입력 받습니다. - * @param sortBy 정렬 기준을 입력 받습니다. - * + * @param sortBy 정렬 기준을 입력 받습니다. * @return 입력한 조건에 맞는 스터디 목록과 조회된 스터디 갯수를 함께 반환합니다. - * * @throws MemberHandler 회원의 관심사가 존재하지 않는 경우 Exception을 발생시킵니다. * @throws MemberHandler 회원이 조회하려는 관심사가 서비스에 등록된 관심사와 일치하지 않는 경우 Exception을 발생시킵니다. - * @throws StudyHandler 회원의 관심사에 해당하는 스터디가 존재하지 않을 경우 Exception을 발생시킵니다. - * @throws StudyHandler 조회된 스터디가 없을 경우 Exception을 발생시킵니다. - * + * @throws StudyHandler 회원의 관심사에 해당하는 스터디가 존재하지 않을 경우 Exception을 발생시킵니다. + * @throws StudyHandler 조회된 스터디가 없을 경우 Exception을 발생시킵니다. */ @Override public StudyPreviewDTO findInterestStudiesByConditionsSpecific(Pageable pageable, - Long memberId, StudySearchRequestDTO request, ThemeType themeType, StudySortBy sortBy) { + Long memberId, StudySearchRequestDTO request, + ThemeType themeType, StudySortBy sortBy) { // 회원이 참가하고 있는 스터디 ID 가져오기 List memberOngoingStudyIds = getOngoingStudyIds(memberId); // 회원 관심사 조회 - List themes = memberThemeRepository.findAllByMemberId(memberId) - .stream() - .map(MemberTheme::getTheme) - .collect(Collectors.toList()); + List themes = preferredThemeRepository.findAllByMemberId(memberId) + .stream() + .map(PreferredTheme::getTheme) + .collect(Collectors.toList()); // 회원의 관심사가 없을 경우 - if (themes.isEmpty()) + if (themes.isEmpty()) { throw new MemberHandler(ErrorStatus._STUDY_THEME_IS_INVALID); + } // 회원이 입력한 관심사가 등록된 관심사와 다른 경우 - if (themes.stream().noneMatch(theme -> theme.getThemeType().equals(themeType))) + if (themes.stream().noneMatch(theme -> theme.getThemeType().equals(themeType))) { throw new MemberHandler(ErrorStatus._BAD_REQUEST); - + } // 회원 관심사로 스터디 테마 조회 Theme theme = findThemeByType(themes, themeType); // 스터디 테마 조회 List studyThemes = themes.stream() - .flatMap(studytheme -> studyThemeRepository.findAllByTheme(theme).stream()) - .toList(); + .flatMap(studytheme -> studyThemeRepository.findAllByTheme(theme).stream()) + .toList(); // 해당 관심사에 해당하는 스터디가 존재하지 않을 경우 - if (studyThemes.isEmpty()) + if (studyThemes.isEmpty()) { throw new StudyHandler(ErrorStatus._STUDY_THEME_NOT_EXIST); + } // 검색 조건 맵 생성 Map conditions = getSearchConditions(request); // 검색 조건에 맞는 스터디 갯수 조회 long totalElements = studyRepository.countStudyByConditionsAndThemeTypesAndNotInIds( - conditions, studyThemes, sortBy, memberOngoingStudyIds); + conditions, studyThemes, sortBy, memberOngoingStudyIds); // 검색 조건에 맞는 스터디 조회 List studies = studyRepository.findStudyByConditionsAndThemeTypesAndNotInIds( - conditions, sortBy, pageable, studyThemes, memberOngoingStudyIds); + conditions, sortBy, pageable, studyThemes, memberOngoingStudyIds); // 조회된 스터디가 없을 경우 - if (studies.isEmpty()) + if (studies.isEmpty()) { throw new StudyHandler(ErrorStatus._STUDY_IS_NOT_MATCH); + } return getDTOs(studies, pageable, totalElements, memberId); } @@ -467,15 +480,12 @@ public StudyPreviewDTO findInterestStudiesByConditionsSpecific(Pageable pageable * * @param pageable 페이지 정보를 입력 받습니다. * @param memberId 회원의 아이디를 입력 받습니다. - * @param sortBy 정렬 기준을 입력 받습니다. - * @param request 검색 조건을 입력 받습니다. - * + * @param sortBy 정렬 기준을 입력 받습니다. + * @param request 검색 조건을 입력 받습니다. * @return 입력한 조건에 맞는 스터디 목록과 조회된 스터디 갯수를 함께 반환합니다. - * * @throws MemberHandler 회원의 관심 지역이 존재하지 않는 경우 Exception을 발생시킵니다. - * @throws StudyHandler 회원의 관심 지역에 해당하는 스터디가 존재하지 않을 경우 Exception을 발생시킵니다. - * @throws StudyHandler 조회된 스터디가 없을 경우 Exception을 발생시킵니다. - * + * @throws StudyHandler 회원의 관심 지역에 해당하는 스터디가 존재하지 않을 경우 Exception을 발생시킵니다. + * @throws StudyHandler 조회된 스터디가 없을 경우 Exception을 발생시킵니다. */ @Override public StudyPreviewDTO findInterestRegionStudiesByConditionsAll( @@ -484,41 +494,42 @@ public StudyPreviewDTO findInterestRegionStudiesByConditionsAll( // 회원이 참가하고 있는 스터디 ID 가져오기 List memberOngoingStudyIds = getOngoingStudyIds(memberId); - // 회원 관심 지역 조회 List regions = preferredRegionRepository.findAllByMemberId(memberId).stream() - .map(PreferredRegion::getRegion) - .toList(); + .map(PreferredRegion::getRegion) + .toList(); // 회원의 관심 지역이 없을 경우이 - if (regions.isEmpty()) + if (regions.isEmpty()) { throw new MemberHandler(ErrorStatus._STUDY_REGION_IS_INVALID); + } // 회원 관심 지역으로 스터디 지역 조회 List regionStudies = regions.stream() - .flatMap(region -> studyRegionRepository.findAllByRegion(region).stream()) - .toList(); + .flatMap(region -> studyRegionRepository.findAllByRegion(region).stream()) + .toList(); // 해당 관심 지역에 해당하는 스터디가 존재하지 않을 경우 - if (regionStudies.isEmpty()) + if (regionStudies.isEmpty()) { throw new StudyHandler(ErrorStatus._STUDY_REGION_NOT_EXIST); + } // 검색 조건 맵 생성 Map conditions = getSearchConditionsWithTheme(request); - // 검색 조건에 맞는 스터디 갯수 조회 long totalElements = studyRepository.countStudyByConditionsAndRegionStudiesAndNotInIds( - conditions, regionStudies, sortBy, memberOngoingStudyIds); + conditions, regionStudies, sortBy, memberOngoingStudyIds); // 검색 조건에 맞는 스터디 조회 List studies = studyRepository.findStudyByConditionsAndRegionStudiesAndNotInIds( - conditions, sortBy, - pageable, regionStudies, memberOngoingStudyIds); + conditions, sortBy, + pageable, regionStudies, memberOngoingStudyIds); // - if (studies.isEmpty()) + if (studies.isEmpty()) { throw new StudyHandler(ErrorStatus._STUDY_IS_NOT_MATCH); + } return getDTOs(studies, pageable, totalElements, memberId); } @@ -526,67 +537,69 @@ public StudyPreviewDTO findInterestRegionStudiesByConditionsAll( /** * 회원의 특정 관심 지역에 해당 되는 모든 스터디를 조회 합니다. 회원이 현재 진행중인 스터디는 제외합니다. * - * @param pageable 페이지 정보를 입력 받습니다. - * @param memberId 회원의 아이디를 입력 받습니다. - * @param request 검색 조건을 입력 받습니다. + * @param pageable 페이지 정보를 입력 받습니다. + * @param memberId 회원의 아이디를 입력 받습니다. + * @param request 검색 조건을 입력 받습니다. * @param regionCode 관심 지역 코드를 입력 받습니다. - * @param sortBy 정렬 기준을 입력 받습니다. - * + * @param sortBy 정렬 기준을 입력 받습니다. * @return 입력한 조건에 맞는 스터디 목록과 조회된 스터디 갯수를 함께 반환합니다. - * * @throws MemberHandler 회원의 관심 지역이 존재하지 않는 경우 Exception을 발생시킵니다. * @throws MemberHandler 회원이 조회하려는 관심 지역이 서비스에 등록된 관심 지역과 일치하지 않는 경우 Exception을 발생시킵니다. - * @throws StudyHandler 회원의 관심 지역에 해당하는 스터디가 존재하지 않을 경우 Exception을 발생시킵니다. - * @throws StudyHandler 조회된 스터디가 없을 경우 Exception을 발생시킵니다. - * + * @throws StudyHandler 회원의 관심 지역에 해당하는 스터디가 존재하지 않을 경우 Exception을 발생시킵니다. + * @throws StudyHandler 조회된 스터디가 없을 경우 Exception을 발생시킵니다. */ @Override public StudyPreviewDTO findInterestRegionStudiesByConditionsSpecific( - Pageable pageable, Long memberId, StudySearchRequestWithThemeDTO request, String regionCode, StudySortBy sortBy) { + Pageable pageable, Long memberId, StudySearchRequestWithThemeDTO request, String regionCode, + StudySortBy sortBy) { // 회원이 참가하고 있는 스터디 ID 가져오기 List memberOngoingStudyIds = getOngoingStudyIds(memberId); // 회원 관심 지역 조회 List regions = preferredRegionRepository.findAllByMemberId(memberId) - .stream() - .map(PreferredRegion::getRegion) - .toList(); + .stream() + .map(PreferredRegion::getRegion) + .toList(); // 회원의 관심 지역이 없을 경우 - if (regions.isEmpty()) + if (regions.isEmpty()) { throw new StudyHandler(ErrorStatus._STUDY_REGION_IS_INVALID); + } // 회원이 입력한 관심 지역이 등록된 지역과 다른 경우 - if (regions.stream().noneMatch(region -> region.getCode().equals(regionCode))) + if (regions.stream().noneMatch(region -> region.getCode().equals(regionCode))) { throw new StudyHandler(ErrorStatus._STUDY_REGION_IS_NOT_MATCH); + } // 회원 관심 지역으로 스터디 지역 조회 Region region = findRegionByCode(regions, regionCode); // 스터디 지역 조회 List regionStudies = regions.stream() - .flatMap(regionStudy -> studyRegionRepository.findAllByRegion(region).stream()) - .toList(); + .flatMap(regionStudy -> studyRegionRepository.findAllByRegion(region).stream()) + .toList(); // 해당 관심 지역에 해당하는 스터디가 존재하지 않을 경우 - if (regionStudies.isEmpty()) + if (regionStudies.isEmpty()) { throw new StudyHandler(ErrorStatus._STUDY_REGION_NOT_EXIST); + } // 검색 조건 맵 생성 Map conditions = getSearchConditionsWithTheme(request); // 검색 조건에 맞는 스터디 갯수 조회 long totalElements = studyRepository.countStudyByConditionsAndRegionStudiesAndNotInIds( - conditions, regionStudies, sortBy, memberOngoingStudyIds); + conditions, regionStudies, sortBy, memberOngoingStudyIds); // 검색 조건에 맞는 스터디 조회 List studies = studyRepository.findStudyByConditionsAndRegionStudiesAndNotInIds( - conditions, sortBy, pageable, regionStudies, memberOngoingStudyIds); + conditions, sortBy, pageable, regionStudies, memberOngoingStudyIds); // 조회된 스터디가 없을 경우 - if (studies.isEmpty()) + if (studies.isEmpty()) { throw new StudyHandler(ErrorStatus._STUDY_IS_NOT_MATCH); + } return getDTOs(studies, pageable, totalElements, memberId); } @@ -595,13 +608,10 @@ public StudyPreviewDTO findInterestRegionStudiesByConditionsSpecific( * 모집중인 스터디를 조회합니다. * * @param pageable 페이지 정보를 입력 받습니다. - * @param request 검색 조건을 입력 받습니다. - * @param sortBy 정렬 기준을 입력 받습니다. - * + * @param request 검색 조건을 입력 받습니다. + * @param sortBy 정렬 기준을 입력 받습니다. * @return 입력한 조건에 맞는 스터디 목록과 조회된 스터디 갯수를 함께 반환합니다. - * * @throws StudyHandler 조회된 스터디가 없을 경우 Exception을 발생시킵니다. - * */ @Override public StudyPreviewDTO findRecruitingStudiesByConditions( @@ -612,11 +622,12 @@ public StudyPreviewDTO findRecruitingStudiesByConditions( // 검색 조건(모집 중)에 맞는 스터디 조회 List studies = studyRepository.findRecruitingStudyByConditions(conditions, - sortBy, pageable); + sortBy, pageable); // 조회된 스터디가 없을 경우 - if (studies.isEmpty()) + if (studies.isEmpty()) { throw new StudyHandler(ErrorStatus._STUDY_IS_NOT_MATCH); + } // 전체 스터디 수 long totalElements = studyRepository.countStudyByConditions(conditions, sortBy); @@ -628,31 +639,30 @@ public StudyPreviewDTO findRecruitingStudiesByConditions( * * @param memberId 회원의 아이디를 입력 받습니다. * @param pageable 페이지 정보를 입력 받습니다. - * * @return 입력한 조건에 맞는 스터디 목록과 조회된 스터디 갯수를 함께 반환합니다. - * * @throws StudyHandler 좋아요 한 스터디가 없는 경우 Exception을 발생시킵니다. * @throws StudyHandler 조회된 스터디가 없을 경우 Exception을 발생시킵니다. - * */ @Override public StudyPreviewDTO findLikedStudies(Long memberId, Pageable pageable) { // 회원이 좋아요한 스터디 조회 List preferredStudyList = preferredStudyRepository.findByMemberIdAndStudyLikeStatusOrderByCreatedAtDesc( - memberId, StudyLikeStatus.LIKE, pageable); + memberId, StudyLikeStatus.LIKE, pageable); // 좋아요한 스터디가 없을 경우 - if (preferredStudyList.isEmpty()) + if (preferredStudyList.isEmpty()) { throw new StudyHandler(ErrorStatus._STUDY_LIKED_NOT_FOUND); + } // 좋아요한 스터디 목록 List studies = preferredStudyList.stream() - .map(PreferredStudy::getStudy) - .toList(); + .map(PreferredStudy::getStudy) + .toList(); // 전체 스터디 수 - if (studies.isEmpty()) + if (studies.isEmpty()) { throw new StudyHandler(ErrorStatus._STUDY_IS_NOT_MATCH); + } // 전체 스터디 수 long totalElements = preferredStudyRepository.countByMemberIdAndStudyLikeStatus(memberId, StudyLikeStatus.LIKE); @@ -664,22 +674,20 @@ public StudyPreviewDTO findLikedStudies(Long memberId, Pageable pageable) { * * @param pageable 페이지 정보를 입력 받습니다. * @param keyword 검색 키워드를 입력 받습니다. - * @param sortBy 정렬 기준을 입력 받습니다. - * + * @param sortBy 정렬 기준을 입력 받습니다. * @return 입력한 조건에 맞는 스터디 목록과 조회된 스터디 갯수를 함께 반환합니다. - * * @throws StudyHandler 조회된 스터디가 없을 경우 Exception을 발생시킵니다. - * */ @Override public StudyPreviewDTO findStudiesByKeyword(Pageable pageable, - String keyword, StudySortBy sortBy) { + String keyword, StudySortBy sortBy) { // 키워드로 스터디 조회 List studies = studyRepository.searchByTitle(keyword, sortBy, pageable); // 조회된 스터디가 없을 경우 - if (studies.isEmpty()) + if (studies.isEmpty()) { throw new StudyHandler(ErrorStatus._STUDY_IS_NOT_MATCH); + } // 전체 스터디 수 long totalElements = studyRepository.countAllByTitleContaining(keyword, sortBy); @@ -692,32 +700,31 @@ public StudyPreviewDTO findStudiesByKeyword(Pageable pageable, * @param pageable 페이지 정보를 입력 받습니다. * @param theme 테마를 입력 받습니다. * @param sortBy 정렬 기준을 입력 받습니다. - * * @return 입력한 조건에 맞는 스터디 목록과 조회된 스터디 갯수를 함께 반환합니다. - * * @throws StudyHandler 해당 테마에 해당하는 스터디가 존재하지 않을 경우 Exception을 발생시킵니다. * @throws StudyHandler 조회된 스터디가 없을 경우 Exception을 발생시킵니다. - * */ @Override public StudyPreviewDTO findStudiesByTheme(Pageable pageable, ThemeType theme, StudySortBy sortBy) { // 테마로 스터디 조회 Theme themeEntity = themeRepository.findByThemeType(theme) - .orElseThrow(() -> new StudyHandler(ErrorStatus._BAD_REQUEST)); + .orElseThrow(() -> new StudyHandler(ErrorStatus._BAD_REQUEST)); // 테마에 해당하는 스터디 테마 조회 List studyThemes = studyThemeRepository.findAllByTheme(themeEntity); // 해당 테마에 해당하는 스터디가 존재하지 않을 경우 - if (studyThemes.isEmpty()) + if (studyThemes.isEmpty()) { throw new StudyHandler(ErrorStatus._STUDY_THEME_NOT_EXIST); + } // 테마에 해당하는 스터디 조회 List studies = studyRepository.findByStudyTheme(studyThemes, sortBy, pageable); // 조회된 스터디가 없을 경우 - if (studies.isEmpty()) + if (studies.isEmpty()) { throw new StudyHandler(ErrorStatus._STUDY_IS_NOT_MATCH); + } // 전체 스터디 수 long totalElements = studyRepository.countStudyByStudyTheme(studyThemes, sortBy); @@ -729,22 +736,20 @@ public StudyPreviewDTO findStudiesByTheme(Pageable pageable, ThemeType theme, St * * @param pageable 페이지 정보를 입력 받습니다. * @param memberId 회원의 아이디를 입력 받습니다. - * * @return 입력한 조건에 맞는 스터디 목록과 조회된 스터디 갯수를 함께 반환합니다. - * * @throws StudyHandler 회원이 참가하고 있는 스터디가 없을 경우 Exception을 발생시킵니다. * @throws StudyHandler 조회된 스터디가 없을 경우 Exception을 발생시킵니다. - * */ @Override public StudyPreviewDTO findOngoingStudiesByMemberId(Pageable pageable, Long memberId) { // 회원이 참가하고 있는 스터디 ID 가져오기 List memberStudies = studyMemberRepository.findAllByMemberIdAndStatus( - memberId, StudyApplicationStatus.APPROVED); + memberId, StudyApplicationStatus.APPROVED); // 회원이 참가하고 있는 스터디가 없을 경우 - if (memberStudies.isEmpty()) + if (memberStudies.isEmpty()) { throw new StudyHandler(ErrorStatus._STUDY_NOT_PARTICIPATED); + } // 회원이 참가하고 있는 스터디 조회 List studies = studyRepository.findByMemberStudy(memberStudies, pageable); @@ -755,8 +760,9 @@ public StudyPreviewDTO findOngoingStudiesByMemberId(Pageable pageable, Long memb .toList(); // 조회된 스터디가 없을 경우 - if (studies.isEmpty()) + if (studies.isEmpty()) { throw new StudyHandler(ErrorStatus._STUDY_IS_NOT_MATCH); + } // 전체 스터디 수 long totalElements = studyRepository.countByMemberStudiesAndStatus(memberStudies, Status.ON); @@ -768,22 +774,20 @@ public StudyPreviewDTO findOngoingStudiesByMemberId(Pageable pageable, Long memb * * @param pageable 페이지 정보를 입력 받습니다. * @param memberId 회원의 아이디를 입력 받습니다. - * * @return 입력한 조건에 맞는 스터디 목록과 조회된 스터디 갯수를 함께 반환합니다. - * * @throws StudyHandler 회원이 신청한 스터디가 존재하지 않을 경우 Exception을 발생시킵니다. * @throws StudyHandler 조회된 스터디가 없을 경우 Exception을 발생시킵니다. - * */ @Override public StudyPreviewDTO findAppliedStudies(Pageable pageable, Long memberId) { // 회원이 신청한 스터디 조회 List memberStudies = studyMemberRepository.findAllByMemberIdAndStatus( - memberId, StudyApplicationStatus.APPLIED); + memberId, StudyApplicationStatus.APPLIED); // 회원이 신청한 스터디가 없을 경우 - if (memberStudies.isEmpty()) + if (memberStudies.isEmpty()) { throw new StudyHandler(ErrorStatus._STUDY_NOT_APPLIED); + } // 회원이 신청한 스터디 조회 List studies = studyRepository.findByMemberStudy(memberStudies, pageable); @@ -791,8 +795,9 @@ public StudyPreviewDTO findAppliedStudies(Pageable pageable, Long memberId) { studies.stream().filter(study -> study.getStatus().equals(Status.ON)).toList(); // 조회된 스터디가 없을 경우 - if (studies.isEmpty()) + if (studies.isEmpty()) { throw new StudyHandler(ErrorStatus._STUDY_IS_NOT_MATCH); + } // 전체 스터디 수 long totalElements = studyRepository.countByMemberStudiesAndStatus(memberStudies, Status.ON); @@ -804,12 +809,9 @@ public StudyPreviewDTO findAppliedStudies(Pageable pageable, Long memberId) { * * @param pageable 페이지 정보를 입력 받습니다. * @param memberId 회원의 아이디를 입력 받습니다. - * * @return 입력한 조건에 맞는 스터디 목록과 조회된 스터디 갯수를 함께 반환합니다. - * * @throws StudyHandler 회원이 모집 중인 스터디가 존재하지 않을 경우 Exception을 발생시킵니다. * @throws StudyHandler 조회된 스터디가 없을 경우 Exception을 발생시킵니다. - * */ @Override public StudyPreviewDTO findMyRecruitingStudies(Pageable pageable, Long memberId) { @@ -817,8 +819,9 @@ public StudyPreviewDTO findMyRecruitingStudies(Pageable pageable, Long memberId) List memberStudies = studyMemberRepository.findAllByMemberIdAndIsOwned(memberId, true); // 회원이 모집중인 스터디가 없을 경우 - if (memberStudies.isEmpty()) + if (memberStudies.isEmpty()) { throw new StudyHandler(ErrorStatus._RECRUITING_STUDY_IS_NOT_EXIST); + } // 회원이 모집중인 스터디 조회 List studies = studyRepository.findRecruitingStudiesByMemberStudy(memberStudies, pageable); @@ -828,8 +831,9 @@ public StudyPreviewDTO findMyRecruitingStudies(Pageable pageable, Long memberId) .toList(); // 조회된 스터디가 없을 경우 - if (studies.isEmpty()) + if (studies.isEmpty()) { throw new StudyHandler(ErrorStatus._STUDY_IS_NOT_MATCH); + } // 전체 스터디 수 long totalElements = studyRepository.countByMemberStudiesAndStatusAndIsOwned(memberStudies, Status.ON, true); @@ -841,25 +845,24 @@ public StudyPreviewDTO findMyRecruitingStudies(Pageable pageable, Long memberId) * 특정 회원이 참가하고 있는 스터디를 조회합니다. * * @param memberId 회원의 아이디를 입력 받습니다. - * * @return 입력한 조건에 맞는 스터디 목록과 조회된 스터디 갯수를 함께 반환합니다. - * * @throws MemberHandler 회원이 존재하지 않을 경우 Exception을 발생시킵니다. - * */ private List getOngoingStudyIds(Long memberId) { // 회원 조회 - if (!memberRepository.existsById(memberId)) + if (!memberRepository.existsById(memberId)) { throw new MemberHandler(ErrorStatus._MEMBER_NOT_FOUND); + } // 회원이 참가하고 있는 스터디 ID 가져오기 - List memberStudies = studyMemberRepository.findAllByMemberIdAndStatus(memberId, StudyApplicationStatus.APPROVED); + List memberStudies = studyMemberRepository.findAllByMemberIdAndStatus(memberId, + StudyApplicationStatus.APPROVED); return memberStudies.stream() - .filter(memberStudy -> memberStudy.getStatus().equals(StudyApplicationStatus.APPROVED)) + .filter(memberStudy -> memberStudy.getStatus().equals(StudyApplicationStatus.APPROVED)) .filter(memberStudy -> memberStudy.getStudy().getStatus().equals(Status.ON)) - .map(memberStudy -> memberStudy.getStudy().getId()) - .toList(); + .map(memberStudy -> memberStudy.getStudy().getId()) + .toList(); } @@ -867,23 +870,20 @@ private List getOngoingStudyIds(Long memberId) { * 검색 조건을 입력 받아 검색 조건 맵을 생성하는 메서드입니다. * * @param request 검색 조건을 입력 받습니다. - * * @return 검색 조건 맵을 반환합니다. - * */ private Map getSearchConditions(StudySearchRequestDTO request) { // 검색 조건 맵 생성 return getBasicStudyFilteringConditions(request.getGender(), request.getMinAge(), request.getMaxAge(), - request.getIsOnline(), request.getHasFee(), request.getMaxFee(), request.getMinFee(), request.getRegionCodes()); + request.getIsOnline(), request.getHasFee(), request.getMaxFee(), request.getMinFee(), + request.getRegionCodes()); } /** * 검색 조건을 입력 받아 검색 조건 맵을 생성하는 메서드입니다. * * @param request 검색 조건을 입력 받습니다. - * * @return 검색 조건 맵을 반환합니다. - * */ private Map getSearchConditionsWithTheme(StudySearchRequestWithThemeDTO request) { Map search = getBasicStudyFilteringConditions(request.getGender(), request.getMinAge(), @@ -898,41 +898,50 @@ private Map getSearchConditionsWithTheme(StudySearchRequestWithT } private Map getBasicStudyFilteringConditions(Gender gender, Integer minAge, Integer maxAge, - Boolean isOnline, Boolean hasFee, - Integer maxFee, Integer minFee, List regions) { + Boolean isOnline, Boolean hasFee, + Integer maxFee, Integer minFee, List regions) { Map search = new HashMap<>(); - if (gender != null) + if (gender != null) { search.put("gender", gender); - if (minAge != null) + } + if (minAge != null) { search.put("minAge", minAge); - if (maxAge != null) + } + if (maxAge != null) { search.put("maxAge", maxAge); - if (isOnline != null) + } + if (isOnline != null) { search.put("isOnline", isOnline); - if (hasFee != null) + } + if (hasFee != null) { search.put("hasFee", hasFee); - if (maxFee != null) + } + if (maxFee != null) { search.put("maxFee", maxFee); - if (minFee != null) + } + if (minFee != null) { search.put("minFee", minFee); + } // 지역 코드가 null 이거나 비어있으면 검색 조건에 추가하지 않음 - if (regions == null || regions.isEmpty()) + if (regions == null || regions.isEmpty()) { return search; + } List regionList = convertCodeToRegion(regions); - if (regionList != null && !regionList.isEmpty()) + if (regionList != null && !regionList.isEmpty()) { search.put("regions", regionList); + } return search; } private List convertCodeToRegion(List regions) { List regionList = regions.stream() - .map(regionCode -> regionRepository.findByCode(regionCode) - .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_REGION_NOT_EXIST))) - .toList(); + .map(regionCode -> regionRepository.findByCode(regionCode) + .orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_REGION_NOT_EXIST))) + .toList(); return regionList; } @@ -940,19 +949,19 @@ private List convertCodeToRegion(List regions) { /** * 스터디 목록을 DTO로 변환하는 메서드입니다. * - * @param studies 스터디 목록을 입력 받습니다. - * @param pageable 페이지 정보를 입력 받습니다. + * @param studies 스터디 목록을 입력 받습니다. + * @param pageable 페이지 정보를 입력 받습니다. * @param totalElements 전체 스터디 수를 입력 받습니다. - * @param memberId 회원의 아이디를 입력 받습니다. - * + * @param memberId 회원의 아이디를 입력 받습니다. * @return 스터디 목록을 DTO로 변환하여 반환합니다. */ private static SearchResponseDTO.StudyPreviewDTO getDTOs(List studies, Pageable pageable, long totalElements, - Long memberId) { + Long memberId) { // memberId == null 이면, 다른 생성자 사용 List stream = studies.stream() - .map((Study study) -> memberId == null ? new SearchStudyDTO(study) : new SearchStudyDTO(study, memberId)) - .toList(); + .map((Study study) -> memberId == null ? new SearchStudyDTO(study) + : new SearchStudyDTO(study, memberId)) + .toList(); Page page = new PageImpl<>(stream, pageable, totalElements); return new StudyPreviewDTO(page, stream, totalElements); } @@ -960,31 +969,29 @@ private static SearchResponseDTO.StudyPreviewDTO getDTOs(List studies, Pa /** * 테마 타입으로 저장된 테마를 조회합니다. * - * @param themes 테마 목록을 입력 받습니다. + * @param themes 테마 목록을 입력 받습니다. * @param themeType 테마 타입을 입력 받습니다. - * * @return 테마 타입에 해당하는 테마를 반환합니다. */ private Theme findThemeByType(List themes, ThemeType themeType) { return themes.stream() - .filter(t -> t.getThemeType().equals(themeType)) - .findFirst() - .orElseThrow(() -> new StudyHandler(ErrorStatus._BAD_REQUEST)); + .filter(t -> t.getThemeType().equals(themeType)) + .findFirst() + .orElseThrow(() -> new StudyHandler(ErrorStatus._BAD_REQUEST)); } /** * 지역 코드로 저장된 지역을 조회합니다. * - * @param regions 지역 목록을 입력 받습니다. + * @param regions 지역 목록을 입력 받습니다. * @param regionCode 지역 코드를 입력 받습니다. - * * @return 지역 코드에 해당하는 지역을 반환합니다. */ private Region findRegionByCode(List regions, String regionCode) { return regions.stream() - .filter(r -> r.getCode().equals(regionCode)) - .findFirst() - .orElseThrow(() -> new StudyHandler(ErrorStatus._BAD_REQUEST)); + .filter(r -> r.getCode().equals(regionCode)) + .findFirst() + .orElseThrow(() -> new StudyHandler(ErrorStatus._BAD_REQUEST)); } } diff --git a/src/main/java/com/example/spot/study/domain/association/Theme.java b/src/main/java/com/example/spot/study/domain/association/Theme.java index 9ee98da8..8f1f7bcc 100644 --- a/src/main/java/com/example/spot/study/domain/association/Theme.java +++ b/src/main/java/com/example/spot/study/domain/association/Theme.java @@ -1,16 +1,23 @@ package com.example.spot.study.domain.association; import com.example.spot.common.entity.BaseEntity; +import com.example.spot.member.domain.association.PreferredTheme; import com.example.spot.study.domain.enums.ThemeType; -import com.example.spot.member.domain.association.MemberTheme; -import jakarta.persistence.*; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import java.util.ArrayList; +import java.util.List; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; - -import java.util.ArrayList; -import java.util.List; import lombok.NoArgsConstructor; import org.hibernate.annotations.DynamicInsert; import org.hibernate.annotations.DynamicUpdate; @@ -36,18 +43,18 @@ public class Theme extends BaseEntity { //== 해당 테마를 선호하는 멤버 목록 ==// @Builder.Default @OneToMany(mappedBy = "theme", cascade = CascadeType.ALL) - private List memberThemeList = new ArrayList<>(); + private List preferredThemeList = new ArrayList<>(); //== 테마별 스터디 목록 ==// @Builder.Default @OneToMany(mappedBy = "theme", cascade = CascadeType.ALL) private List studyThemeList = new ArrayList<>(); -/* ----------------------------- 연관관계 메소드 ------------------------------------- */ + /* ----------------------------- 연관관계 메소드 ------------------------------------- */ - public void addMemberTheme(MemberTheme memberTheme) { - memberThemeList.add(memberTheme); - memberTheme.setTheme(this); + public void addMemberTheme(PreferredTheme preferredTheme) { + preferredThemeList.add(preferredTheme); + preferredTheme.setTheme(this); } public void addStudyTheme(StudyTheme studyTheme) { diff --git a/src/main/java/com/example/spot/study/domain/repository/RegionRepository.java b/src/main/java/com/example/spot/study/domain/repository/RegionRepository.java index 03be7a95..57a238b9 100644 --- a/src/main/java/com/example/spot/study/domain/repository/RegionRepository.java +++ b/src/main/java/com/example/spot/study/domain/repository/RegionRepository.java @@ -1,14 +1,21 @@ package com.example.spot.study.domain.repository; +import com.example.spot.common.api.code.status.ErrorStatus; +import com.example.spot.common.api.exception.GeneralException; import com.example.spot.study.domain.association.Region; +import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; -import java.util.Optional; - @Repository public interface RegionRepository extends JpaRepository { Optional findByCode(String code); + boolean existsByCode(String code); + + default Region getByCode(String code) { + return findByCode(code) + .orElseThrow(() -> new GeneralException(ErrorStatus._REGION_NOT_FOUND)); + } } diff --git a/src/main/java/com/example/spot/study/domain/repository/ThemeRepository.java b/src/main/java/com/example/spot/study/domain/repository/ThemeRepository.java index ad072298..3fbe57f6 100644 --- a/src/main/java/com/example/spot/study/domain/repository/ThemeRepository.java +++ b/src/main/java/com/example/spot/study/domain/repository/ThemeRepository.java @@ -1,16 +1,23 @@ package com.example.spot.study.domain.repository; +import com.example.spot.common.api.code.status.ErrorStatus; +import com.example.spot.common.api.exception.GeneralException; import com.example.spot.study.domain.association.Theme; import com.example.spot.study.domain.enums.ThemeType; +import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; -import java.util.Optional; - @Repository public interface ThemeRepository extends JpaRepository { Optional findByThemeType(ThemeType themeType); + boolean existsByThemeType(ThemeType themeType); + default Theme getByThemeType(ThemeType themeType) { + return findByThemeType(themeType) + .orElseThrow(() -> new GeneralException(ErrorStatus._THEME_NOT_FOUND)); + } + } diff --git a/src/main/java/com/example/spot/todo/application/impl/ManageToDoUseCaseImpl.java b/src/main/java/com/example/spot/todo/application/impl/ManageToDoUseCaseImpl.java index d428a9ab..2d45f13e 100644 --- a/src/main/java/com/example/spot/todo/application/impl/ManageToDoUseCaseImpl.java +++ b/src/main/java/com/example/spot/todo/application/impl/ManageToDoUseCaseImpl.java @@ -5,7 +5,7 @@ import com.example.spot.common.api.exception.handler.StudyHandler; import com.example.spot.common.security.utils.SecurityUtils; import com.example.spot.member.domain.Member; -import com.example.spot.member.domain.MemberRepository; +import com.example.spot.member.infrastructure.MemberRepository; import com.example.spot.study.domain.Study; import com.example.spot.study.domain.StudyRepository; import com.example.spot.study.domain.enums.StudyApplicationStatus; diff --git a/src/main/java/com/example/spot/todo/domain/ToDo.java b/src/main/java/com/example/spot/todo/domain/ToDo.java index 167f502a..3a6f0492 100644 --- a/src/main/java/com/example/spot/todo/domain/ToDo.java +++ b/src/main/java/com/example/spot/todo/domain/ToDo.java @@ -1,7 +1,7 @@ package com.example.spot.todo.domain; -import com.example.spot.member.domain.Member; import com.example.spot.common.entity.BaseEntity; +import com.example.spot.member.domain.Member; import com.example.spot.study.domain.Study; import jakarta.persistence.Column; import jakarta.persistence.Entity; @@ -17,7 +17,6 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; - import org.hibernate.annotations.DynamicInsert; import org.hibernate.annotations.DynamicUpdate; @@ -30,7 +29,8 @@ @NoArgsConstructor(access = AccessLevel.PROTECTED) public class ToDo extends BaseEntity { - @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) @@ -50,17 +50,21 @@ public class ToDo extends BaseEntity { @JoinColumn(name = "study_id") private Study study; - public void setToDoList(){ + public void setToDoList() { this.study.addToDoList(this); - this.member.addToDoList(this); } - public void check(){ + + public void check() { this.isDone = !this.isDone; } - public void update(String content, LocalDate date){ - if (content != null) this.content = content; - if (date != null) this.date = date; + public void update(String content, LocalDate date) { + if (content != null) { + this.content = content; + } + if (date != null) { + this.date = date; + } } } diff --git a/src/main/java/com/example/spot/vote/application/VoteCommandServiceImpl.java b/src/main/java/com/example/spot/vote/application/VoteCommandServiceImpl.java index 6066f836..d41e8406 100644 --- a/src/main/java/com/example/spot/vote/application/VoteCommandServiceImpl.java +++ b/src/main/java/com/example/spot/vote/application/VoteCommandServiceImpl.java @@ -5,7 +5,7 @@ import com.example.spot.common.api.exception.handler.StudyHandler; import com.example.spot.common.security.utils.SecurityUtils; import com.example.spot.member.domain.Member; -import com.example.spot.member.domain.MemberRepository; +import com.example.spot.member.infrastructure.MemberRepository; import com.example.spot.study.domain.Study; import com.example.spot.study.domain.StudyRepository; import com.example.spot.study.domain.enums.StudyApplicationStatus; @@ -18,12 +18,11 @@ import com.example.spot.vote.domain.repository.VoteParticipantRepository; import com.example.spot.vote.presentation.dto.request.StudyVoteRequestDTO; import com.example.spot.vote.presentation.dto.response.StudyVoteResponseDTO; +import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.List; - @Service @RequiredArgsConstructor @@ -40,6 +39,7 @@ public class VoteCommandServiceImpl implements VoteCommandService { /** * 스터디 투표를 생성하는 메서드입니다. + * * @param studyId 타겟 스터디의 아이디를 입력 받습니다. * @param voteDTO 생성할 투표의 제목, 항목 목록, 중복 선택 가능 여부, 종료 일시를 입력 받습니다. * @return 생성된 투표의 아이디와 제목을 반환합니다. @@ -74,16 +74,15 @@ public StudyVoteResponseDTO.VotePreviewDTO createVote(Long studyId, StudyVoteReq // Option 저장 vote = createOption(vote, voteDTO); // 연관관계 매핑 - loginMember.addVote(vote); study.addVote(vote); return StudyVoteResponseDTO.VotePreviewDTO.toDTO(vote); } /** - * 스터디 투표의 항목을 생성하는 메서드입니다. - * createVote 메서드 내부에서 사용되는 메서드입니다. - * @param vote 항목을 생성할 타겟 투표를 입력 받습니다. + * 스터디 투표의 항목을 생성하는 메서드입니다. createVote 메서드 내부에서 사용되는 메서드입니다. + * + * @param vote 항목을 생성할 타겟 투표를 입력 받습니다. * @param voteDTO 생성할 투표의 제목, 항목 목록, 중복 선택 가능 여부, 종료 일시를 입력 받습니다. * @return 투표 객체를 반환합니다. */ @@ -102,13 +101,15 @@ private Vote createOption(Vote vote, StudyVoteRequestDTO.VoteDTO voteDTO) { /** * 특정 항목에 투표하기 위한 메서드입니다. - * @param studyId 타겟 스터디의 아이디를 입력 받습니다. - * @param voteId 타겟 투표의 아이디를 입력 받습니다. + * + * @param studyId 타겟 스터디의 아이디를 입력 받습니다. + * @param voteId 타겟 투표의 아이디를 입력 받습니다. * @param votedOptionDTO 회원이 투표한 항목의 아이디 목록을 입력 받습니다. * @return 투표 아이디, 회원 아이디, 투표한 항목 목록을 반환합니다. */ @Override - public StudyVoteResponseDTO.VotedOptionDTO vote(Long studyId, Long voteId, StudyVoteRequestDTO.VotedOptionDTO votedOptionDTO) { + public StudyVoteResponseDTO.VotedOptionDTO vote(Long studyId, Long voteId, + StudyVoteRequestDTO.VotedOptionDTO votedOptionDTO) { //=== Exception ===// Long memberId = SecurityUtils.getCurrentUserId(); @@ -135,8 +136,9 @@ public StudyVoteResponseDTO.VotedOptionDTO vote(Long studyId, Long voteId, Study // 한 번 참여한 투표는 다시 참여할 수 없음 voteOptionRepository.findAllByVoteId(voteId) .forEach(option -> { - if (voteParticipantRepository.existsByMemberIdAndVoteOptionId(loginMember.getId(), option.getId())) { - throw new StudyHandler(ErrorStatus._STUDY_VOTE_RE_PARTICIPATION_INVALID); + if (voteParticipantRepository.existsByMemberIdAndVoteOptionId(loginMember.getId(), + option.getId())) { + throw new StudyHandler(ErrorStatus._STUDY_VOTE_RE_PARTICIPATION_INVALID); } }); @@ -154,7 +156,6 @@ public StudyVoteResponseDTO.VotedOptionDTO vote(Long studyId, Long voteId, Study .build(); voteParticipant = voteParticipantRepository.save(voteParticipant); - loginMember.addMemberVote(voteParticipant); votedVoteOption.addMemberVote(voteParticipant); return voteParticipant; @@ -166,13 +167,15 @@ public StudyVoteResponseDTO.VotedOptionDTO vote(Long studyId, Long voteId, Study /** * 투표를 편집하는 메서드입니다. + * * @param studyId 타겟 스터디의 아이디를 입력 받습니다. - * @param voteId 편집할 투표의 아이디를 입력 받습니다. + * @param voteId 편집할 투표의 아이디를 입력 받습니다. * @param voteDTO 편집된 투표의 제목, 항목 목록, 복수 선택 가능 여부, 종료 일시를 입력 받습니다. * @return 편집된 투표의 아이디와 제목을 반환합니다. */ @Override - public StudyVoteResponseDTO.VotePreviewDTO updateVote(Long studyId, Long voteId, StudyVoteRequestDTO.VoteUpdateDTO voteDTO) { + public StudyVoteResponseDTO.VotePreviewDTO updateVote(Long studyId, Long voteId, + StudyVoteRequestDTO.VoteUpdateDTO voteDTO) { //=== Exception ===// Long memberId = SecurityUtils.getCurrentUserId(); @@ -213,7 +216,6 @@ public StudyVoteResponseDTO.VotePreviewDTO updateVote(Long studyId, Long voteId, vote.updateVote(voteDTO.getTitle(), voteDTO.getIsMultipleChoice(), voteDTO.getFinishedAt()); vote = voteRepository.save(vote); - loginMember.updateVote(vote); study.updateVote(vote); return StudyVoteResponseDTO.VotePreviewDTO.toDTO(vote); @@ -221,8 +223,9 @@ public StudyVoteResponseDTO.VotePreviewDTO updateVote(Long studyId, Long voteId, /** * 투표를 삭제하는 메서드입니다. + * * @param studyId 타겟 스터디의 아이디를 입력 받습니다. - * @param voteId 삭제할 투표의 아이디를 입력 받습니다. + * @param voteId 삭제할 투표의 아이디를 입력 받습니다. * @return 삭제된 투표의 아이디와 제목을 반환합니다. */ @Override @@ -252,7 +255,6 @@ public StudyVoteResponseDTO.VotePreviewDTO deleteVote(Long studyId, Long voteId) //=== Feature ===// deleteOptions(voteId); - loginMember.deleteVote(vote); study.deleteVote(vote); voteRepository.delete(vote); @@ -260,8 +262,8 @@ public StudyVoteResponseDTO.VotePreviewDTO deleteVote(Long studyId, Long voteId) } /** - * 모든 투표 항목을 삭제하는 메서드입니다. - * deleteVote 메서드 내부에서 호출되는 메서드입니다. + * 모든 투표 항목을 삭제하는 메서드입니다. deleteVote 메서드 내부에서 호출되는 메서드입니다. + * * @param voteId 항목을 삭제할 타겟 투표의 아이디를 입력 받습니다. */ private void deleteOptions(Long voteId) { diff --git a/src/main/java/com/example/spot/vote/application/VoteQueryServiceImpl.java b/src/main/java/com/example/spot/vote/application/VoteQueryServiceImpl.java index e771f715..009faca4 100644 --- a/src/main/java/com/example/spot/vote/application/VoteQueryServiceImpl.java +++ b/src/main/java/com/example/spot/vote/application/VoteQueryServiceImpl.java @@ -4,7 +4,7 @@ import com.example.spot.common.api.exception.handler.StudyHandler; import com.example.spot.common.security.utils.SecurityUtils; import com.example.spot.member.domain.Member; -import com.example.spot.member.domain.MemberRepository; +import com.example.spot.member.infrastructure.MemberRepository; import com.example.spot.study.domain.Study; import com.example.spot.study.domain.StudyRepository; import com.example.spot.study.domain.enums.StudyApplicationStatus; @@ -14,13 +14,12 @@ import com.example.spot.vote.domain.association.VoteOption; import com.example.spot.vote.domain.repository.VoteParticipantRepository; import com.example.spot.vote.presentation.dto.response.StudyVoteResponseDTO; +import java.time.LocalDateTime; +import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.time.LocalDateTime; -import java.util.List; - @Service @Transactional(readOnly = true) @@ -36,6 +35,7 @@ public class VoteQueryServiceImpl implements VoteQueryService { /** * 스터디에 생성된 모든 투표 목록을 불러옵니다. + * * @param studyId 투표 목록을 불러올 타겟 스터디의 아이디를 입력 받습니다. * @return 스터디 아이디와 해당 스터디에서 진행중인 투표 목록, 마감된 투표 목록을 반환합니다. */ @@ -58,7 +58,8 @@ public StudyVoteResponseDTO.VoteListDTO getAllVotes(Long studyId) { //=== Feature ===// // 진행중인 투표 목록 - List votesInProgress = voteRepository.findAllByStudyIdAndFinishedAtAfter(studyId, LocalDateTime.now()).stream() + List votesInProgress = voteRepository.findAllByStudyIdAndFinishedAtAfter( + studyId, LocalDateTime.now()).stream() .map(vote -> { boolean isParticipated = isParticipated(vote, member); return StudyVoteResponseDTO.VoteInfoDTO.toDTO(vote, isParticipated); @@ -66,7 +67,8 @@ public StudyVoteResponseDTO.VoteListDTO getAllVotes(Long studyId) { .toList(); // 마감된 투표 목록 - List votesInCompletion = voteRepository.findAllByStudyIdAndFinishedAtBefore(studyId, LocalDateTime.now()).stream() + List votesInCompletion = voteRepository.findAllByStudyIdAndFinishedAtBefore( + studyId, LocalDateTime.now()).stream() .map(vote -> { boolean isParticipated = isParticipated(vote, member); return StudyVoteResponseDTO.VoteInfoDTO.toDTO(vote, isParticipated); @@ -77,9 +79,9 @@ public StudyVoteResponseDTO.VoteListDTO getAllVotes(Long studyId) { } /** - * 스터디 회원의 투표 참여 여부를 확인하는 메서드입니다. - * getAllVotes에서 사용되는 내부 메서드입니다. - * @param vote 스터디에서 생성한 투표의 아이디를 입력 받습니다. + * 스터디 회원의 투표 참여 여부를 확인하는 메서드입니다. getAllVotes에서 사용되는 내부 메서드입니다. + * + * @param vote 스터디에서 생성한 투표의 아이디를 입력 받습니다. * @param loginMember 로그인한 회원의 정보를 입력 받습니다. * @return 투표 참여 여부를 true or false로 반환합니다. */ @@ -95,8 +97,9 @@ private boolean isParticipated(Vote vote, Member loginMember) { } /** - * 입력 받은 스터디 투표가 종료되었는지 확인하는 메서드입니다. - * (클라이언트에서 투표 불러오기 API를 호출할 때 스터디 종료 여부에 따라 Response DTO가 바뀌어야 하기 때문에 필요한 메서드입니다) + * 입력 받은 스터디 투표가 종료되었는지 확인하는 메서드입니다. (클라이언트에서 투표 불러오기 API를 호출할 때 스터디 종료 여부에 따라 Response DTO가 바뀌어야 하기 때문에 필요한 + * 메서드입니다) + * * @param voteId 스터디에서 생성한 투표의 아이디를 입력 받습니다. * @return 투표 종료 여부를 true or false로 반환합니다. */ @@ -107,8 +110,9 @@ public Boolean getIsCompleted(Long voteId) { /** * 종료된 투표의 정보를 불러오는 메서드입니다. + * * @param studyId 타겟 스터디의 아이디를 입력 받습니다. - * @param voteId 스터디에서 생성한 투표의 아이디를 입력 받습니다. + * @param voteId 스터디에서 생성한 투표의 아이디를 입력 받습니다. * @return 종료된 투표의 아이디, 생성자, 제목, 항목별 투표 인원수, 전체 참여자 수, 종료 일시를 반환합니다. */ @Override @@ -140,8 +144,9 @@ public StudyVoteResponseDTO.CompletedVoteDTO getVoteInCompletion(Long studyId, L /** * 진행중인 투표의 정보를 불러오는 메서드입니다. + * * @param studyId 타겟 스터디의 아이디를 입력 받습니다. - * @param voteId 스터디에서 생성한 투표의 아이디를 입력 받습니다. + * @param voteId 스터디에서 생성한 투표의 아이디를 입력 받습니다. * @return 진행중인 투표의 아이디, 생성자, 제목, 항목 리스트, 복수 선택 가능 여부, 종료 일시, 로그인한 회원의 참여 여부를 반환합니다. */ @Override @@ -172,8 +177,9 @@ public StudyVoteResponseDTO.VoteDTO getVoteInProgress(Long studyId, Long voteId) /** * 마감된 투표에 대해 항목별 투표 현황을 불러오는 메서드입니다. + * * @param studyId 타겟 스터디의 아이디를 입력 받습니다. - * @param voteId 마감된 스터디 투표의 아이디를 입력 받습니다. + * @param voteId 마감된 스터디 투표의 아이디를 입력 받습니다. * @return 마감된 투표의 아이디와 제목, 항목별 투표 회원 목록을 반환합니다. */ @Override diff --git a/src/test/java/com/example/spot/service/post/GetPostUseCaseTest.java b/src/test/java/com/example/spot/service/post/GetPostUseCaseTest.java index 08915372..0681f7be 100644 --- a/src/test/java/com/example/spot/service/post/GetPostUseCaseTest.java +++ b/src/test/java/com/example/spot/service/post/GetPostUseCaseTest.java @@ -1,31 +1,39 @@ package com.example.spot.service.post; -import com.example.spot.common.api.exception.handler.PostHandler; -import com.example.spot.post.domain.association.LikedPost; -import com.example.spot.comment.domain.association.LikedPostComment; -import com.example.spot.member.domain.Member; -import com.example.spot.post.domain.Post; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.when; + import com.example.spot.comment.domain.PostComment; -import com.example.spot.post.domain.enums.Board; -import com.example.spot.post.domain.enums.PostStatus; -import com.example.spot.post.domain.association.MemberScrap; -import com.example.spot.comment.domain.association.LikedPostCommentRepository; -import com.example.spot.post.domain.association.LikedPostRepository; -import com.example.spot.member.domain.MemberRepository; -import com.example.spot.post.domain.association.MemberScrapRepository; import com.example.spot.comment.domain.PostCommentRepository; -import com.example.spot.report.domain.PostReportRepository; -import com.example.spot.post.domain.PostRepository; +import com.example.spot.comment.domain.association.LikedPostComment; +import com.example.spot.comment.domain.association.LikedPostCommentRepository; +import com.example.spot.comment.presentation.dto.CommentResponse; +import com.example.spot.common.api.exception.handler.PostHandler; +import com.example.spot.member.domain.Member; +import com.example.spot.member.infrastructure.MemberRepository; import com.example.spot.post.application.query.GetLikedPostCommentUseCase; import com.example.spot.post.application.query.GetLikedPostUseCase; import com.example.spot.post.application.query.impl.GetPostUseCaseImpl; -import com.example.spot.comment.presentation.dto.CommentResponse; +import com.example.spot.post.domain.Post; +import com.example.spot.post.domain.PostRepository; +import com.example.spot.post.domain.association.LikedPost; +import com.example.spot.post.domain.association.LikedPostRepository; +import com.example.spot.post.domain.association.MemberScrap; +import com.example.spot.post.domain.association.MemberScrapRepository; +import com.example.spot.post.domain.enums.Board; +import com.example.spot.post.domain.enums.PostStatus; import com.example.spot.post.presentation.dto.response.PostAnnouncementResponse; import com.example.spot.post.presentation.dto.response.PostBest5Response; import com.example.spot.post.presentation.dto.response.PostPagingResponse; import com.example.spot.post.presentation.dto.response.PostRepresentativeResponse; import com.example.spot.post.presentation.dto.response.PostSingleResponse; - +import com.example.spot.report.domain.PostReportRepository; +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.List; +import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -44,17 +52,6 @@ import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.when; - @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) class GetPostUseCaseTest { @@ -163,7 +160,6 @@ void setUp() { when(getLikedPostCommentUseCase.countByPostCommentIdAndIsLikedTrue(1L)).thenReturn(1L); when(getLikedPostCommentUseCase.countByPostCommentIdAndIsLikedTrue(2L)).thenReturn(0L); - // MemberScrap when(memberScrapRepository.countByPostId(1L)).thenReturn(1L); when(memberScrapRepository.countByPostId(2L)).thenReturn(1L); @@ -175,7 +171,7 @@ void setUp() { when(memberScrapRepository.existsByMemberIdAndPostId(2L, 1L)).thenReturn(true); } -/*-------------------------------------------------------- 게시글 단건 조회 ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- 게시글 단건 조회 ------------------------------------------------------------------------*/ @Test @DisplayName("게시글 단건 조회 - 일반 조회 (성공)") @@ -270,7 +266,7 @@ void getPostById_ReportedPost_Fail() { assertThrows(PostHandler.class, () -> postQueryService.getPostById(postId, false)); } -/*-------------------------------------------------------- 게시글 페이징 조회 ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- 게시글 페이징 조회 ------------------------------------------------------------------------*/ @Test @DisplayName("게시글 페이징 조회 - 전체 게시글 조회 (성공)") @@ -311,7 +307,8 @@ void getPagingPosts_Type_Success() { List posts = List.of(post2); postPage = new PageImpl<>(posts, pageable, 1); - when(postRepository.findByBoardAndPostReportListIsEmptyOrderByCreatedAtDesc(Board.INFORMATION_SHARING, pageable)).thenReturn(postPage); + when(postRepository.findByBoardAndPostReportListIsEmptyOrderByCreatedAtDesc(Board.INFORMATION_SHARING, + pageable)).thenReturn(postPage); // when PostPagingResponse result = postQueryService.getPagingPosts("INFORMATION_SHARING", pageable); @@ -342,7 +339,7 @@ void getPagingPosts_InvalidType_Fail() { } -/*-------------------------------------------------------- 인기 게시글 조회 ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- 인기 게시글 조회 ------------------------------------------------------------------------*/ @Test @DisplayName("인기 게시글 조회 - 실시간 인기 게시글 조회 (성공)") @@ -426,7 +423,7 @@ void getPostBest_InvalidFilter_Fail() { } -/*-------------------------------------------------------- 대표 게시글 조회 ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- 대표 게시글 조회 ------------------------------------------------------------------------*/ @Test @DisplayName("대표 게시글 조회 - (성공)") @@ -449,7 +446,7 @@ void getRepresentativePosts_Success() { assertThat(result.getResponses().get(1).getPostType()).isEqualTo("INFORMATION_SHARING"); } -/*-------------------------------------------------------- 최신 공지 조회 ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- 최신 공지 조회 ------------------------------------------------------------------------*/ @Test @DisplayName("최신 공지 조회 - (성공)") @@ -471,7 +468,7 @@ void getPostAnnouncements_Success() { assertThat(result.getResponses().get(0).getPostId()).isEqualTo(post1.getId()); } -/*-------------------------------------------------------- 댓글 목록 조회 ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- 댓글 목록 조회 ------------------------------------------------------------------------*/ @Test @DisplayName("댓글 목록 조회 - (성공)") @@ -508,7 +505,7 @@ void getCommentsByPostId_Fail() { assertThrows(PostHandler.class, () -> postQueryService.getCommentsByPostId(postId)); } -/*-------------------------------------------------------- 스크랩 게시글 페이징 조회 ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- 스크랩 게시글 페이징 조회 ------------------------------------------------------------------------*/ @Test @DisplayName("스크랩 게시글 페이징 조회 - 전체 게시글 조회 (성공)") @@ -580,11 +577,12 @@ void getScrapPagingPost_InvalidType_Fail() { -/*-------------------------------------------------------- Utils ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- Utils ------------------------------------------------------------------------*/ private static void getAuthentication(Long memberId) { String idString = String.valueOf(memberId); - Authentication authentication = new UsernamePasswordAuthenticationToken(idString, null, Collections.emptyList()); + Authentication authentication = new UsernamePasswordAuthenticationToken(idString, null, + Collections.emptyList()); SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); securityContext.setAuthentication(authentication); SecurityContextHolder.setContext(securityContext); @@ -594,20 +592,10 @@ private static void initMember() { member1 = Member.builder() .id(1L) .nickname("회원1") - .postList(new ArrayList<>()) - .likedPostList(new ArrayList<>()) - .memberScrapList(new ArrayList<>()) - .postCommentList(new ArrayList<>()) - .likedCommentList(new ArrayList<>()) .build(); member2 = Member.builder() .id(2L) .nickname("회원2") - .postList(new ArrayList<>()) - .likedPostList(new ArrayList<>()) - .memberScrapList(new ArrayList<>()) - .postCommentList(new ArrayList<>()) - .likedCommentList(new ArrayList<>()) .build(); } diff --git a/src/test/java/com/example/spot/service/post/PostCommandServiceTest.java b/src/test/java/com/example/spot/service/post/PostCommandServiceTest.java index aafe56d1..d8db1633 100644 --- a/src/test/java/com/example/spot/service/post/PostCommandServiceTest.java +++ b/src/test/java/com/example/spot/service/post/PostCommandServiceTest.java @@ -1,46 +1,50 @@ package com.example.spot.service.post; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import com.example.spot.comment.domain.PostComment; +import com.example.spot.comment.domain.PostCommentRepository; +import com.example.spot.comment.domain.association.LikedPostComment; +import com.example.spot.comment.domain.association.LikedPostCommentRepository; +import com.example.spot.comment.presentation.dto.CommentCreateRequest; +import com.example.spot.comment.presentation.dto.CommentCreateResponse; +import com.example.spot.comment.presentation.dto.CommentLikeResponse; import com.example.spot.common.api.exception.handler.PostHandler; -import com.example.spot.post.application.command.LikePostCommentUseCase; -import com.example.spot.post.application.command.LikePostUseCase; -import com.example.spot.post.application.command.ManagePostCommentUseCase; -import com.example.spot.post.application.command.ManagePostUseCase; -import com.example.spot.post.application.command.ScrapPostUseCase; +import com.example.spot.member.domain.Member; +import com.example.spot.member.infrastructure.MemberRepository; import com.example.spot.post.application.command.impl.LikePostCommentUseCaseImpl; import com.example.spot.post.application.command.impl.LikePostUseCaseImpl; import com.example.spot.post.application.command.impl.ManagePostCommentUseCaseImpl; import com.example.spot.post.application.command.impl.ManagePostUseCaseImpl; import com.example.spot.post.application.command.impl.ScrapPostUseCaseImpl; -import com.example.spot.post.domain.association.LikedPost; -import com.example.spot.comment.domain.association.LikedPostComment; -import com.example.spot.member.domain.Member; +import com.example.spot.post.application.query.GetLikedPostCommentUseCase; +import com.example.spot.post.application.query.GetLikedPostUseCase; import com.example.spot.post.domain.Post; -import com.example.spot.comment.domain.PostComment; -import com.example.spot.report.application.ReportCommandServiceImpl; -import com.example.spot.report.domain.PostReport; -import com.example.spot.post.domain.enums.Board; -import com.example.spot.post.domain.association.MemberScrap; -import com.example.spot.comment.domain.association.LikedPostCommentRepository; +import com.example.spot.post.domain.PostRepository; +import com.example.spot.post.domain.association.LikedPost; import com.example.spot.post.domain.association.LikedPostRepository; -import com.example.spot.member.domain.MemberRepository; +import com.example.spot.post.domain.association.MemberScrap; import com.example.spot.post.domain.association.MemberScrapRepository; -import com.example.spot.comment.domain.PostCommentRepository; -import com.example.spot.report.domain.PostReportRepository; -import com.example.spot.post.domain.PostRepository; -import com.example.spot.post.application.query.GetLikedPostCommentUseCase; -import com.example.spot.post.application.query.GetLikedPostUseCase; -import com.example.spot.comment.presentation.dto.CommentCreateRequest; -import com.example.spot.comment.presentation.dto.CommentCreateResponse; -import com.example.spot.comment.presentation.dto.CommentLikeResponse; +import com.example.spot.post.domain.enums.Board; import com.example.spot.post.presentation.dto.request.PostCreateRequest; -import com.example.spot.post.presentation.dto.response.PostCreateResponse; -import com.example.spot.post.presentation.dto.response.PostLikeResponse; -import com.example.spot.report.presentation.dto.PostReportDTO; import com.example.spot.post.presentation.dto.request.PostUpdateRequest; import com.example.spot.post.presentation.dto.request.ScrapAllDeleteRequest; +import com.example.spot.post.presentation.dto.response.PostCreateResponse; +import com.example.spot.post.presentation.dto.response.PostLikeResponse; import com.example.spot.post.presentation.dto.response.ScrapPostResponse; import com.example.spot.post.presentation.dto.response.ScrapsPostDeleteResponse; - +import com.example.spot.report.application.ReportCommandServiceImpl; +import com.example.spot.report.domain.PostReport; +import com.example.spot.report.domain.PostReportRepository; +import com.example.spot.report.presentation.dto.PostReportDTO; +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.List; +import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -55,18 +59,6 @@ import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.when; - @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) class PostCommandServiceTest { @@ -98,17 +90,23 @@ class PostCommandServiceTest { @Mock private GetLikedPostCommentUseCase getLikedPostCommentUseCase; - @InjectMocks private LikePostCommentUseCaseImpl likePostCommentUseCase; + @InjectMocks + private LikePostCommentUseCaseImpl likePostCommentUseCase; - @InjectMocks private LikePostUseCaseImpl likePostUseCase; + @InjectMocks + private LikePostUseCaseImpl likePostUseCase; - @InjectMocks private ManagePostCommentUseCaseImpl managePostCommentUseCase; + @InjectMocks + private ManagePostCommentUseCaseImpl managePostCommentUseCase; - @InjectMocks private ManagePostUseCaseImpl managePostUseCase; + @InjectMocks + private ManagePostUseCaseImpl managePostUseCase; - @InjectMocks private ScrapPostUseCaseImpl scrapPostUseCase; + @InjectMocks + private ScrapPostUseCaseImpl scrapPostUseCase; - @InjectMocks private ReportCommandServiceImpl reportCommandService; + @InjectMocks + private ReportCommandServiceImpl reportCommandService; private static Member member1; private static Member member2; @@ -186,7 +184,7 @@ void setUp() { when(memberScrapRepository.existsByMemberIdAndPostId(2L, 1L)).thenReturn(true); } -/*-------------------------------------------------------- 게시글 작성 ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- 게시글 작성 ------------------------------------------------------------------------*/ @Test @DisplayName("게시글 작성 - 일반 게시글 (성공)") @@ -261,7 +259,7 @@ void createPost_Announcement_Fail() { assertThrows(PostHandler.class, () -> managePostUseCase.createPost(memberId, postCreateRequest)); } -/*-------------------------------------------------------- 게시글 수정 ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- 게시글 수정 ------------------------------------------------------------------------*/ @Test @DisplayName("게시글 수정 - 공지 게시글 (성공)") @@ -373,7 +371,7 @@ void updatePost_NotExisted_Fail() { assertThrows(PostHandler.class, () -> managePostUseCase.updatePost(memberId, postId, postUpdateRequest)); } -/*-------------------------------------------------------- 게시글 삭제 ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- 게시글 삭제 ------------------------------------------------------------------------*/ @Test @DisplayName("게시글 삭제 - (성공)") @@ -414,7 +412,7 @@ void deletePost_NotExisted_Fail() { assertThrows(PostHandler.class, () -> managePostUseCase.deletePost(memberId, postId)); } -/*-------------------------------------------------------- 게시글 좋아요 ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- 게시글 좋아요 ------------------------------------------------------------------------*/ @Test @DisplayName("게시글 좋아요 - (성공)") @@ -468,7 +466,7 @@ void likePost_AlreadyLiked_Fail() { assertThrows(PostHandler.class, () -> likePostUseCase.likePost(postId, memberId)); } -/*-------------------------------------------------------- 게시글 좋아요 취소 ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- 게시글 좋아요 취소 ------------------------------------------------------------------------*/ @Test @DisplayName("게시글 좋아요 취소 - (성공)") @@ -521,7 +519,7 @@ void cancelPostLike_NotLiked_Fail() { assertThrows(PostHandler.class, () -> likePostUseCase.cancelPostLike(postId, memberId)); } -/*-------------------------------------------------------- 댓글 작성 ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- 댓글 작성 ------------------------------------------------------------------------*/ @Test @DisplayName("댓글 작성 - 상위 댓글 (성공)") @@ -593,7 +591,8 @@ void createComment_NotExisted_Fail() { .build(); // when & then - assertThrows(PostHandler.class, () -> managePostCommentUseCase.createComment(postId, memberId, commentCreateRequest)); + assertThrows(PostHandler.class, + () -> managePostCommentUseCase.createComment(postId, memberId, commentCreateRequest)); } @Test @@ -612,10 +611,11 @@ void createComment_InvalidParent_Fail() { .build(); // when & then - assertThrows(PostHandler.class, () -> managePostCommentUseCase.createComment(postId, memberId, commentCreateRequest)); + assertThrows(PostHandler.class, + () -> managePostCommentUseCase.createComment(postId, memberId, commentCreateRequest)); } -/*-------------------------------------------------------- 댓글 좋아요 ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- 댓글 좋아요 ------------------------------------------------------------------------*/ @Test @DisplayName("댓글 좋아요 - (성공)") @@ -672,7 +672,7 @@ void likeComment_AlreadyLiked_Fail() { assertThrows(PostHandler.class, () -> likePostCommentUseCase.likeComment(commentId, memberId)); } -/*-------------------------------------------------------- 댓글 좋아요 취소 ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- 댓글 좋아요 취소 ------------------------------------------------------------------------*/ @Test @DisplayName("댓글 좋아요 취소 - (성공)") @@ -708,7 +708,7 @@ void cancelCommentLike_NotExisted_Fail() { getAuthentication(memberId); // when & then - assertThrows(PostHandler.class, () ->likePostCommentUseCase.cancelCommentLike(commentId, memberId)); + assertThrows(PostHandler.class, () -> likePostCommentUseCase.cancelCommentLike(commentId, memberId)); } @Test @@ -724,10 +724,10 @@ void cancelCommentLike_NotLiked_Fail() { .thenReturn(Optional.empty()); // when & then - assertThrows(PostHandler.class, () ->likePostCommentUseCase.cancelCommentLike(commentId, memberId)); + assertThrows(PostHandler.class, () -> likePostCommentUseCase.cancelCommentLike(commentId, memberId)); } -/*-------------------------------------------------------- 댓글 싫어요 ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- 댓글 싫어요 ------------------------------------------------------------------------*/ @Test @DisplayName("댓글 싫어요 - (성공)") @@ -787,7 +787,7 @@ void dislikeComment_AlreadyDisliked_Fail() { assertThrows(PostHandler.class, () -> likePostCommentUseCase.dislikeComment(commentId, memberId)); } -/*-------------------------------------------------------- 댓글 싫어요 취소 ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- 댓글 싫어요 취소 ------------------------------------------------------------------------*/ @Test @DisplayName("댓글 싫어요 취소 - (성공)") @@ -844,7 +844,7 @@ void cancelCommentDislike_NotDisliked_Fail() { assertThrows(PostHandler.class, () -> likePostCommentUseCase.cancelCommentDislike(commentId, memberId)); } -/*-------------------------------------------------------- 게시글 스크랩 ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- 게시글 스크랩 ------------------------------------------------------------------------*/ @Test @DisplayName("게시글 스크랩 - (성공)") @@ -898,7 +898,7 @@ void scrapPost_AlreadyScraped_Fail() { assertThrows(PostHandler.class, () -> scrapPostUseCase.scrapPost(postId, memberId)); } -/*-------------------------------------------------------- 게시글 스크랩 취소 ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- 게시글 스크랩 취소 ------------------------------------------------------------------------*/ @Test @DisplayName("게시글 스크랩 취소 - (성공)") @@ -948,7 +948,7 @@ void cancelPostScrap_NotScraped_Fail() { assertThrows(PostHandler.class, () -> scrapPostUseCase.cancelPostScrap(postId, memberId)); } -/*-------------------------------------------------------- 게시글 스크랩 다중 취소 ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- 게시글 스크랩 다중 취소 ------------------------------------------------------------------------*/ @Test @DisplayName("게시글 스크랩 다중 취소 - (성공)") @@ -1012,7 +1012,7 @@ void cancelPostScraps_NotScraped_Fail() { assertThrows(PostHandler.class, () -> scrapPostUseCase.cancelPostScraps(scrapAllDeleteRequest)); } -/*-------------------------------------------------------- 게시글 신고 ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- 게시글 신고 ------------------------------------------------------------------------*/ @Test @DisplayName("게시글 신고 - (성공)") @@ -1073,11 +1073,12 @@ void reportPost_DuplicateReport_Fail() { } -/*-------------------------------------------------------- Utils ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- Utils ------------------------------------------------------------------------*/ private static void getAuthentication(Long memberId) { String idString = String.valueOf(memberId); - Authentication authentication = new UsernamePasswordAuthenticationToken(idString, null, Collections.emptyList()); + Authentication authentication = new UsernamePasswordAuthenticationToken(idString, null, + Collections.emptyList()); SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); securityContext.setAuthentication(authentication); SecurityContextHolder.setContext(securityContext); @@ -1088,21 +1089,11 @@ private static void initMember() { .id(1L) .nickname("회원1") .isAdmin(true) - .postList(new ArrayList<>()) - .likedPostList(new ArrayList<>()) - .memberScrapList(new ArrayList<>()) - .postCommentList(new ArrayList<>()) - .likedCommentList(new ArrayList<>()) .build(); member2 = Member.builder() .id(2L) .nickname("회원2") .isAdmin(false) - .postList(new ArrayList<>()) - .likedPostList(new ArrayList<>()) - .memberScrapList(new ArrayList<>()) - .postCommentList(new ArrayList<>()) - .likedCommentList(new ArrayList<>()) .build(); } diff --git a/src/test/java/com/example/spot/service/schedule/AttendanceCommandServiceTest.java b/src/test/java/com/example/spot/service/schedule/AttendanceCommandServiceTest.java index 1f339646..418daaa7 100644 --- a/src/test/java/com/example/spot/service/schedule/AttendanceCommandServiceTest.java +++ b/src/test/java/com/example/spot/service/schedule/AttendanceCommandServiceTest.java @@ -1,23 +1,35 @@ package com.example.spot.service.schedule; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + import com.example.spot.common.api.exception.handler.StudyHandler; import com.example.spot.member.domain.Member; +import com.example.spot.member.domain.enums.Gender; +import com.example.spot.member.infrastructure.MemberRepository; import com.example.spot.schedule.application.ScheduleCommandServiceImpl; import com.example.spot.schedule.domain.Schedule; +import com.example.spot.schedule.domain.ScheduleRepository; import com.example.spot.schedule.domain.association.Quiz; import com.example.spot.schedule.domain.association.QuizSubmission; import com.example.spot.schedule.domain.repository.QuizRepository; import com.example.spot.schedule.domain.repository.QuizSubmissionRepository; -import com.example.spot.schedule.domain.ScheduleRepository; +import com.example.spot.schedule.presentation.dto.request.QuizRequestDTO; +import com.example.spot.schedule.presentation.dto.response.QuizResponseDTO; +import com.example.spot.study.domain.Study; +import com.example.spot.study.domain.StudyRepository; import com.example.spot.study.domain.association.StudyMember; import com.example.spot.study.domain.enums.StudyApplicationStatus; -import com.example.spot.member.domain.enums.Gender; -import com.example.spot.study.domain.Study; -import com.example.spot.member.domain.MemberRepository; import com.example.spot.study.domain.repository.StudyMemberRepository; -import com.example.spot.study.domain.StudyRepository; -import com.example.spot.schedule.presentation.dto.request.QuizRequestDTO; -import com.example.spot.schedule.presentation.dto.response.QuizResponseDTO; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.List; +import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -32,17 +44,6 @@ import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.*; - @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) class AttendanceCommandServiceTest { @@ -102,9 +103,11 @@ void setUp() { when(studyMemberRepository.findByMemberIdAndStudyIdAndIsOwned(owner.getId(), 1L, Boolean.TRUE)) .thenReturn(Optional.of(ownerStudy)); - when(studyMemberRepository.findByMemberIdAndStudyIdAndStatus(member1.getId(), 1L, StudyApplicationStatus.APPROVED)) + when(studyMemberRepository.findByMemberIdAndStudyIdAndStatus(member1.getId(), 1L, + StudyApplicationStatus.APPROVED)) .thenReturn(Optional.of(member1Study)); - when(studyMemberRepository.findByMemberIdAndStudyIdAndStatus(owner.getId(), 1L, StudyApplicationStatus.APPROVED)) + when(studyMemberRepository.findByMemberIdAndStudyIdAndStatus(owner.getId(), 1L, + StudyApplicationStatus.APPROVED)) .thenReturn(Optional.of(ownerStudy)); when(studyMemberRepository.findAllByStudyIdAndStatus(1L, StudyApplicationStatus.APPROVED)) .thenReturn(List.of(member1Study, ownerStudy)); @@ -123,14 +126,16 @@ void createAttendanceQuiz_Success() { QuizRequestDTO.QuizDTO quizRequestDTO = getQuizDTO(owner); LocalDateTime startOfDay = quizRequestDTO.getCreatedAt().withHour(0).withMinute(0).withSecond(0).withNano(0); - LocalDateTime endOfDay = quizRequestDTO.getCreatedAt().withHour(23).withMinute(59).withSecond(59).withNano(999_999_000); + LocalDateTime endOfDay = quizRequestDTO.getCreatedAt().withHour(23).withMinute(59).withSecond(59) + .withNano(999_999_000); when(quizRepository.findAllByScheduleIdAndCreatedAtBetween(schedule.getId(), startOfDay, endOfDay)) .thenReturn(List.of()); when(quizRepository.save(any(Quiz.class))).thenReturn(quiz2); // when - QuizResponseDTO.QuestionDTO result = scheduleCommandService.createAttendanceQuiz(studyId, schedule.getId(), quizRequestDTO); + QuizResponseDTO.QuestionDTO result = scheduleCommandService.createAttendanceQuiz(studyId, schedule.getId(), + quizRequestDTO); // then assertThat(result).isNotNull(); @@ -147,14 +152,16 @@ void createAttendanceQuiz_StudyNotFound_Fail() { QuizRequestDTO.QuizDTO quizRequestDTO = getQuizDTO(owner); LocalDateTime startOfDay = quizRequestDTO.getCreatedAt().withHour(0).withMinute(0).withSecond(0).withNano(0); - LocalDateTime endOfDay = quizRequestDTO.getCreatedAt().withHour(23).withMinute(59).withSecond(59).withNano(999_999_000); + LocalDateTime endOfDay = quizRequestDTO.getCreatedAt().withHour(23).withMinute(59).withSecond(59) + .withNano(999_999_000); when(quizRepository.findAllByScheduleIdAndCreatedAtBetween(schedule.getId(), startOfDay, endOfDay)) .thenReturn(List.of()); when(quizRepository.save(any(Quiz.class))).thenReturn(quiz2); // when & then - assertThrows(StudyHandler.class, () -> scheduleCommandService.createAttendanceQuiz(studyId, schedule.getId(), quizRequestDTO)); + assertThrows(StudyHandler.class, + () -> scheduleCommandService.createAttendanceQuiz(studyId, schedule.getId(), quizRequestDTO)); } @Test @@ -166,14 +173,16 @@ void createAttendanceQuiz_NotStudyMember_Fail() { QuizRequestDTO.QuizDTO quizRequestDTO = getQuizDTO(member2); LocalDateTime startOfDay = quizRequestDTO.getCreatedAt().withHour(0).withMinute(0).withSecond(0).withNano(0); - LocalDateTime endOfDay = quizRequestDTO.getCreatedAt().withHour(23).withMinute(59).withSecond(59).withNano(999_999_000); + LocalDateTime endOfDay = quizRequestDTO.getCreatedAt().withHour(23).withMinute(59).withSecond(59) + .withNano(999_999_000); when(quizRepository.findAllByScheduleIdAndCreatedAtBetween(schedule.getId(), startOfDay, endOfDay)) .thenReturn(List.of()); when(quizRepository.save(any(Quiz.class))).thenReturn(quiz2); // when & then - assertThrows(StudyHandler.class, () -> scheduleCommandService.createAttendanceQuiz(studyId, schedule.getId(), quizRequestDTO)); + assertThrows(StudyHandler.class, + () -> scheduleCommandService.createAttendanceQuiz(studyId, schedule.getId(), quizRequestDTO)); } @Test @@ -185,14 +194,16 @@ void createAttendanceQuiz_NotOwner_Fail() { QuizRequestDTO.QuizDTO quizRequestDTO = getQuizDTO(member1); LocalDateTime startOfDay = quizRequestDTO.getCreatedAt().withHour(0).withMinute(0).withSecond(0).withNano(0); - LocalDateTime endOfDay = quizRequestDTO.getCreatedAt().withHour(23).withMinute(59).withSecond(59).withNano(999_999_000); + LocalDateTime endOfDay = quizRequestDTO.getCreatedAt().withHour(23).withMinute(59).withSecond(59) + .withNano(999_999_000); when(quizRepository.findAllByScheduleIdAndCreatedAtBetween(schedule.getId(), startOfDay, endOfDay)) .thenReturn(List.of()); when(quizRepository.save(any(Quiz.class))).thenReturn(quiz2); // when & then - assertThrows(StudyHandler.class, () -> scheduleCommandService.createAttendanceQuiz(studyId, schedule.getId(), quizRequestDTO)); + assertThrows(StudyHandler.class, + () -> scheduleCommandService.createAttendanceQuiz(studyId, schedule.getId(), quizRequestDTO)); } @Test @@ -207,14 +218,16 @@ void createAttendanceQuiz_ScheduleNotFound_Fail() { QuizRequestDTO.QuizDTO quizRequestDTO = getQuizDTO(member1); LocalDateTime startOfDay = quizRequestDTO.getCreatedAt().withHour(0).withMinute(0).withSecond(0).withNano(0); - LocalDateTime endOfDay = quizRequestDTO.getCreatedAt().withHour(23).withMinute(59).withSecond(59).withNano(999_999_000); + LocalDateTime endOfDay = quizRequestDTO.getCreatedAt().withHour(23).withMinute(59).withSecond(59) + .withNano(999_999_000); when(quizRepository.findAllByScheduleIdAndCreatedAtBetween(schedule.getId(), startOfDay, endOfDay)) .thenReturn(List.of(quiz1)); when(quizRepository.save(any(Quiz.class))).thenReturn(quiz2); // when & then - assertThrows(StudyHandler.class, () -> scheduleCommandService.createAttendanceQuiz(studyId, schedule.getId(), quizRequestDTO)); + assertThrows(StudyHandler.class, + () -> scheduleCommandService.createAttendanceQuiz(studyId, schedule.getId(), quizRequestDTO)); } @Test @@ -230,19 +243,23 @@ void attendantStudy_Success() { QuizRequestDTO.AttendanceDTO attendanceRequestDTO = getAttendanceDTO(member1, now); - LocalDateTime startOfDay = attendanceRequestDTO.getDateTime().withHour(0).withMinute(0).withSecond(0).withNano(0); - LocalDateTime endOfDay = attendanceRequestDTO.getDateTime().withHour(23).withMinute(59).withSecond(59).withNano(999_999_000); + LocalDateTime startOfDay = attendanceRequestDTO.getDateTime().withHour(0).withMinute(0).withSecond(0) + .withNano(0); + LocalDateTime endOfDay = attendanceRequestDTO.getDateTime().withHour(23).withMinute(59).withSecond(59) + .withNano(999_999_000); when(quizRepository.findAllByScheduleIdAndCreatedAtBetween(schedule.getId(), startOfDay, endOfDay)) .thenReturn(List.of(quiz1)); when(quizSubmissionRepository.save(any(QuizSubmission.class))).thenReturn(mockAttendance); - when(studyMemberRepository.findByMemberIdAndStudyIdAndStatus(member1.getId(), null, StudyApplicationStatus.APPROVED)) + when(studyMemberRepository.findByMemberIdAndStudyIdAndStatus(member1.getId(), null, + StudyApplicationStatus.APPROVED)) .thenReturn(Optional.of(member1Study)); when(quizSubmissionRepository.findByQuizIdAndMemberId(null, member1.getId())) .thenReturn(List.of(member1Attendance)); // when - QuizResponseDTO.AttendanceDTO result = scheduleCommandService.attendantStudy(studyId, scheduleId, attendanceRequestDTO); + QuizResponseDTO.AttendanceDTO result = scheduleCommandService.attendantStudy(studyId, scheduleId, + attendanceRequestDTO); // then assertThat(result).isNotNull(); @@ -266,8 +283,10 @@ void attendantStudy_NotStudyMember_Fail() { QuizRequestDTO.AttendanceDTO attendanceRequestDTO = getAttendanceDTO(); - LocalDateTime startOfDay = attendanceRequestDTO.getDateTime().withHour(0).withMinute(0).withSecond(0).withNano(0); - LocalDateTime endOfDay = attendanceRequestDTO.getDateTime().withHour(23).withMinute(59).withSecond(59).withNano(999_999_000); + LocalDateTime startOfDay = attendanceRequestDTO.getDateTime().withHour(0).withMinute(0).withSecond(0) + .withNano(0); + LocalDateTime endOfDay = attendanceRequestDTO.getDateTime().withHour(23).withMinute(59).withSecond(59) + .withNano(999_999_000); when(quizRepository.findAllByScheduleIdAndCreatedAtBetween(schedule.getId(), startOfDay, endOfDay)) .thenReturn(List.of(quiz1)); @@ -276,7 +295,8 @@ void attendantStudy_NotStudyMember_Fail() { .thenReturn(List.of()); // when & then - assertThrows(StudyHandler.class, () -> scheduleCommandService.attendantStudy(studyId, scheduleId, attendanceRequestDTO)); + assertThrows(StudyHandler.class, + () -> scheduleCommandService.attendantStudy(studyId, scheduleId, attendanceRequestDTO)); } @Test @@ -295,8 +315,10 @@ void attendantStudy_QuizNotFound_Fail() { QuizRequestDTO.AttendanceDTO attendanceRequestDTO = getAttendanceDTO(); - LocalDateTime startOfDay = attendanceRequestDTO.getDateTime().withHour(0).withMinute(0).withSecond(0).withNano(0); - LocalDateTime endOfDay = attendanceRequestDTO.getDateTime().withHour(23).withMinute(59).withSecond(59).withNano(999_999_000); + LocalDateTime startOfDay = attendanceRequestDTO.getDateTime().withHour(0).withMinute(0).withSecond(0) + .withNano(0); + LocalDateTime endOfDay = attendanceRequestDTO.getDateTime().withHour(23).withMinute(59).withSecond(59) + .withNano(999_999_000); when(quizRepository.findAllByScheduleIdAndCreatedAtBetween(schedule.getId(), startOfDay, endOfDay)) .thenReturn(List.of(quiz1)); @@ -307,7 +329,8 @@ void attendantStudy_QuizNotFound_Fail() { .thenReturn(List.of(member1Attendance)); // when & then - assertThrows(StudyHandler.class, () -> scheduleCommandService.attendantStudy(studyId, scheduleId, attendanceRequestDTO)); + assertThrows(StudyHandler.class, + () -> scheduleCommandService.attendantStudy(studyId, scheduleId, attendanceRequestDTO)); } @Test @@ -329,19 +352,23 @@ void attendantStudy_QuizTimeOver_Fail() { .answer("SPOT") .build(); - LocalDateTime startOfDay = attendanceRequestDTO.getDateTime().withHour(0).withMinute(0).withSecond(0).withNano(0); - LocalDateTime endOfDay = attendanceRequestDTO.getDateTime().withHour(23).withMinute(59).withSecond(59).withNano(999_999_000); + LocalDateTime startOfDay = attendanceRequestDTO.getDateTime().withHour(0).withMinute(0).withSecond(0) + .withNano(0); + LocalDateTime endOfDay = attendanceRequestDTO.getDateTime().withHour(23).withMinute(59).withSecond(59) + .withNano(999_999_000); when(quizRepository.findAllByScheduleIdAndCreatedAtBetween(schedule.getId(), startOfDay, endOfDay)) .thenReturn(List.of(quiz1)); when(quizSubmissionRepository.save(any(QuizSubmission.class))).thenReturn(mockAttendance); - when(studyMemberRepository.findByMemberIdAndStudyIdAndStatus(member1.getId(), null, StudyApplicationStatus.APPROVED)) + when(studyMemberRepository.findByMemberIdAndStudyIdAndStatus(member1.getId(), null, + StudyApplicationStatus.APPROVED)) .thenReturn(Optional.of(member1Study)); when(quizSubmissionRepository.findByQuizIdAndMemberId(null, member1.getId())) .thenReturn(List.of()); // when & then - assertThrows(StudyHandler.class, () -> scheduleCommandService.attendantStudy(studyId, scheduleId, attendanceRequestDTO)); + assertThrows(StudyHandler.class, + () -> scheduleCommandService.attendantStudy(studyId, scheduleId, attendanceRequestDTO)); } @Test @@ -358,19 +385,23 @@ void attendantStudy_QuizTryOver_Fail() { // 사용자 인증 정보 생성 QuizRequestDTO.AttendanceDTO attendanceRequestDTO = getAttendanceDTO(member1, now.plusMinutes(5).plusNanos(1)); - LocalDateTime startOfDay = attendanceRequestDTO.getDateTime().withHour(0).withMinute(0).withSecond(0).withNano(0); - LocalDateTime endOfDay = attendanceRequestDTO.getDateTime().withHour(23).withMinute(59).withSecond(59).withNano(999_999_000); + LocalDateTime startOfDay = attendanceRequestDTO.getDateTime().withHour(0).withMinute(0).withSecond(0) + .withNano(0); + LocalDateTime endOfDay = attendanceRequestDTO.getDateTime().withHour(23).withMinute(59).withSecond(59) + .withNano(999_999_000); when(quizRepository.findAllByScheduleIdAndCreatedAtBetween(schedule.getId(), startOfDay, endOfDay)) .thenReturn(List.of(quiz1)); when(quizSubmissionRepository.save(any(QuizSubmission.class))).thenReturn(mockAttendance); - when(studyMemberRepository.findByMemberIdAndStudyIdAndStatus(member1.getId(), null, StudyApplicationStatus.APPROVED)) + when(studyMemberRepository.findByMemberIdAndStudyIdAndStatus(member1.getId(), null, + StudyApplicationStatus.APPROVED)) .thenReturn(Optional.of(member1Study)); when(quizSubmissionRepository.findByQuizIdAndMemberId(null, member1.getId())) .thenReturn(List.of(member1Attendance, member1Attendance2, member1Attendance3)); // when & then - assertThrows(StudyHandler.class, () -> scheduleCommandService.attendantStudy(studyId, scheduleId, attendanceRequestDTO)); + assertThrows(StudyHandler.class, + () -> scheduleCommandService.attendantStudy(studyId, scheduleId, attendanceRequestDTO)); } @Test @@ -386,19 +417,23 @@ void attendantStudy_QuizAlreadyCorrect_Fail() { QuizRequestDTO.AttendanceDTO attendanceRequestDTO = getAttendanceDTO(owner, now.plusMinutes(5).plusNanos(1)); - LocalDateTime startOfDay = attendanceRequestDTO.getDateTime().withHour(0).withMinute(0).withSecond(0).withNano(0); - LocalDateTime endOfDay = attendanceRequestDTO.getDateTime().withHour(23).withMinute(59).withSecond(59).withNano(999_999_000); + LocalDateTime startOfDay = attendanceRequestDTO.getDateTime().withHour(0).withMinute(0).withSecond(0) + .withNano(0); + LocalDateTime endOfDay = attendanceRequestDTO.getDateTime().withHour(23).withMinute(59).withSecond(59) + .withNano(999_999_000); when(quizRepository.findAllByScheduleIdAndCreatedAtBetween(schedule.getId(), startOfDay, endOfDay)) .thenReturn(List.of(quiz1)); when(quizSubmissionRepository.save(any(QuizSubmission.class))).thenReturn(mockAttendance); - when(studyMemberRepository.findByMemberIdAndStudyIdAndStatus(owner.getId(), null, StudyApplicationStatus.APPROVED)) + when(studyMemberRepository.findByMemberIdAndStudyIdAndStatus(owner.getId(), null, + StudyApplicationStatus.APPROVED)) .thenReturn(Optional.of(ownerStudy)); when(quizSubmissionRepository.findByQuizIdAndMemberId(null, owner.getId())) .thenReturn(List.of(ownerAttendance)); // when & then - assertThrows(StudyHandler.class, () -> scheduleCommandService.attendantStudy(studyId, scheduleId, attendanceRequestDTO)); + assertThrows(StudyHandler.class, + () -> scheduleCommandService.attendantStudy(studyId, scheduleId, attendanceRequestDTO)); } @Test @@ -420,7 +455,8 @@ void deleteAttendanceQuiz_Success() { when(quizRepository.findAllByScheduleIdAndCreatedAtBetween(schedule.getId(), startOfDay, endOfDay)) .thenReturn(List.of(quiz1)); - when(studyMemberRepository.findByMemberIdAndStudyIdAndStatus(owner.getId(), null, StudyApplicationStatus.APPROVED)) + when(studyMemberRepository.findByMemberIdAndStudyIdAndStatus(owner.getId(), null, + StudyApplicationStatus.APPROVED)) .thenReturn(Optional.of(ownerStudy)); when(quizSubmissionRepository.findByQuizId(quiz1.getId())) .thenReturn(List.of(member1Attendance, member1Attendance2, member1Attendance3, ownerAttendance)); @@ -454,7 +490,8 @@ void deleteAttendanceQuiz_QuizNotFound_Fail() { when(quizRepository.findAllByScheduleIdAndCreatedAtBetween(2L, startOfDay, endOfDay)) .thenReturn(List.of()); - when(studyMemberRepository.findByMemberIdAndStudyIdAndStatus(owner.getId(), null, StudyApplicationStatus.APPROVED)) + when(studyMemberRepository.findByMemberIdAndStudyIdAndStatus(owner.getId(), null, + StudyApplicationStatus.APPROVED)) .thenReturn(Optional.of(ownerStudy)); when(quizSubmissionRepository.findByQuizId(quiz1.getId())) .thenReturn(List.of(member1Attendance, member1Attendance2, member1Attendance3, ownerAttendance)); @@ -508,7 +545,8 @@ void deleteAttendanceQuiz_NotStudyOwner_Fail() { when(quizRepository.findAllByScheduleIdAndCreatedAtBetween(scheduleId, startOfDay, endOfDay)) .thenReturn(List.of(quiz1)); - when(studyMemberRepository.findByMemberIdAndStudyIdAndStatus(owner.getId(), null, StudyApplicationStatus.APPROVED)) + when(studyMemberRepository.findByMemberIdAndStudyIdAndStatus(owner.getId(), null, + StudyApplicationStatus.APPROVED)) .thenReturn(Optional.of(member1Study)); when(quizSubmissionRepository.findByQuizId(quiz1.getId())) .thenReturn(List.of(member1Attendance, member1Attendance2, member1Attendance3, ownerAttendance)); @@ -517,20 +555,17 @@ void deleteAttendanceQuiz_NotStudyOwner_Fail() { assertThrows(StudyHandler.class, () -> scheduleCommandService.deleteAttendanceQuiz(studyId, scheduleId, date)); } -/*-------------------------------------------------------- Utils ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- Utils ------------------------------------------------------------------------*/ private static void initMember() { member1 = Member.builder() .id(1L) - .scheduleList(new ArrayList<>()) .build(); member2 = Member.builder() .id(2L) - .scheduleList(new ArrayList<>()) .build(); owner = Member.builder() .id(3L) - .scheduleList(new ArrayList<>()) .build(); } @@ -576,7 +611,6 @@ private static void initSchedule() { .member(owner) .build(); study.addSchedule(schedule); - owner.addSchedule(schedule); } private static void initQuiz() { @@ -619,7 +653,8 @@ private static void initMemberAttendance() { private static void getAuthentication(Long memberId) { String idString = String.valueOf(memberId); - Authentication authentication = new UsernamePasswordAuthenticationToken(idString, null, Collections.emptyList()); + Authentication authentication = new UsernamePasswordAuthenticationToken(idString, null, + Collections.emptyList()); SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); securityContext.setAuthentication(authentication); SecurityContextHolder.setContext(securityContext); diff --git a/src/test/java/com/example/spot/service/schedule/AttendanceQueryServiceTest.java b/src/test/java/com/example/spot/service/schedule/AttendanceQueryServiceTest.java index 9983656c..9ccb6a71 100644 --- a/src/test/java/com/example/spot/service/schedule/AttendanceQueryServiceTest.java +++ b/src/test/java/com/example/spot/service/schedule/AttendanceQueryServiceTest.java @@ -1,22 +1,30 @@ package com.example.spot.service.schedule; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.when; + import com.example.spot.common.api.exception.handler.StudyHandler; import com.example.spot.member.domain.Member; +import com.example.spot.member.domain.enums.Gender; +import com.example.spot.member.infrastructure.MemberRepository; import com.example.spot.schedule.application.ScheduleQueryServiceImpl; import com.example.spot.schedule.domain.Schedule; +import com.example.spot.schedule.domain.ScheduleRepository; import com.example.spot.schedule.domain.association.Quiz; import com.example.spot.schedule.domain.association.QuizSubmission; import com.example.spot.schedule.domain.repository.QuizRepository; import com.example.spot.schedule.domain.repository.QuizSubmissionRepository; -import com.example.spot.schedule.domain.ScheduleRepository; +import com.example.spot.schedule.presentation.dto.response.QuizResponseDTO; +import com.example.spot.study.domain.Study; +import com.example.spot.study.domain.StudyRepository; import com.example.spot.study.domain.association.StudyMember; import com.example.spot.study.domain.enums.StudyApplicationStatus; -import com.example.spot.member.domain.enums.Gender; -import com.example.spot.study.domain.Study; -import com.example.spot.member.domain.MemberRepository; import com.example.spot.study.domain.repository.StudyMemberRepository; -import com.example.spot.study.domain.StudyRepository; -import com.example.spot.schedule.presentation.dto.response.QuizResponseDTO; +import java.time.LocalDate; +import java.util.Collections; +import java.util.List; +import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -31,16 +39,6 @@ import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; -import java.time.LocalDate; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.when; - @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) class AttendanceQueryServiceTest { @@ -91,15 +89,18 @@ void setUp() { when(studyRepository.findById(1L)).thenReturn(Optional.of(study)); - when(studyMemberRepository.findByMemberIdAndStudyIdAndStatus(member1.getId(), 1L, StudyApplicationStatus.APPROVED)) + when(studyMemberRepository.findByMemberIdAndStudyIdAndStatus(member1.getId(), 1L, + StudyApplicationStatus.APPROVED)) .thenReturn(Optional.of(member1Study)); - when(studyMemberRepository.findByMemberIdAndStudyIdAndStatus(owner.getId(), 1L, StudyApplicationStatus.APPROVED)) + when(studyMemberRepository.findByMemberIdAndStudyIdAndStatus(owner.getId(), 1L, + StudyApplicationStatus.APPROVED)) .thenReturn(Optional.of(ownerStudy)); when(studyMemberRepository.findAllByStudyIdAndStatus(1L, StudyApplicationStatus.APPROVED)) .thenReturn(List.of(member1Study, ownerStudy)); when(scheduleRepository.findById(schedule.getId())).thenReturn(Optional.of(schedule)); - when(quizRepository.findAllByScheduleIdAndCreatedAtBetween(schedule.getId(), date.atStartOfDay(), date.atStartOfDay().plusDays(1))) + when(quizRepository.findAllByScheduleIdAndCreatedAtBetween(schedule.getId(), date.atStartOfDay(), + date.atStartOfDay().plusDays(1))) .thenReturn(List.of(quiz)); when(quizSubmissionRepository.findByQuizIdAndMemberId(quiz.getId(), member1.getId())) .thenReturn(List.of(member1Attendance)); @@ -118,7 +119,8 @@ void getAllAttendances_Success() { getAuthentication(member1.getId()); // when - QuizResponseDTO.AttendanceListDTO result = scheduleQueryService.getAllAttendances(studyId, schedule.getId(), date); + QuizResponseDTO.AttendanceListDTO result = scheduleQueryService.getAllAttendances(studyId, schedule.getId(), + date); // then assertThat(result).isNotNull(); @@ -234,20 +236,17 @@ void getAttendanceQuiz_ScheduleNotFound_Fail() { } -/*-------------------------------------------------------- Utils ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- Utils ------------------------------------------------------------------------*/ private static void initMember() { member1 = Member.builder() .id(1L) - .scheduleList(new ArrayList<>()) .build(); member2 = Member.builder() .id(2L) - .scheduleList(new ArrayList<>()) .build(); owner = Member.builder() .id(3L) - .scheduleList(new ArrayList<>()) .build(); } @@ -293,7 +292,6 @@ private static void initSchedule() { .member(owner) .build(); study.addSchedule(schedule); - owner.addSchedule(schedule); } private static void initQuiz() { @@ -321,7 +319,8 @@ private static void initMemberAttendance() { private static void getAuthentication(Long memberId) { String idString = String.valueOf(memberId); - Authentication authentication = new UsernamePasswordAuthenticationToken(idString, null, Collections.emptyList()); + Authentication authentication = new UsernamePasswordAuthenticationToken(idString, null, + Collections.emptyList()); SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); securityContext.setAuthentication(authentication); SecurityContextHolder.setContext(securityContext); diff --git a/src/test/java/com/example/spot/service/schedule/ScheduleCommandServiceTest.java b/src/test/java/com/example/spot/service/schedule/ScheduleCommandServiceTest.java index 747ac933..bda85814 100644 --- a/src/test/java/com/example/spot/service/schedule/ScheduleCommandServiceTest.java +++ b/src/test/java/com/example/spot/service/schedule/ScheduleCommandServiceTest.java @@ -1,22 +1,35 @@ package com.example.spot.service.schedule; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + import com.example.spot.common.api.exception.handler.StudyHandler; import com.example.spot.member.domain.Member; +import com.example.spot.member.domain.enums.Gender; +import com.example.spot.member.infrastructure.MemberRepository; import com.example.spot.notification.domain.Notification; +import com.example.spot.notification.domain.NotificationRepository; import com.example.spot.schedule.application.ScheduleCommandServiceImpl; import com.example.spot.schedule.domain.Schedule; +import com.example.spot.schedule.domain.ScheduleRepository; import com.example.spot.schedule.domain.enums.SchedulePeriod; +import com.example.spot.schedule.presentation.dto.request.ScheduleRequestDTO; +import com.example.spot.schedule.presentation.dto.response.ScheduleResponseDTO; +import com.example.spot.study.domain.Study; +import com.example.spot.study.domain.StudyRepository; import com.example.spot.study.domain.association.StudyMember; import com.example.spot.study.domain.enums.StudyApplicationStatus; -import com.example.spot.member.domain.enums.Gender; -import com.example.spot.study.domain.Study; -import com.example.spot.member.domain.MemberRepository; import com.example.spot.study.domain.repository.StudyMemberRepository; -import com.example.spot.notification.domain.NotificationRepository; -import com.example.spot.schedule.domain.ScheduleRepository; -import com.example.spot.study.domain.StudyRepository; -import com.example.spot.schedule.presentation.dto.request.ScheduleRequestDTO; -import com.example.spot.schedule.presentation.dto.response.ScheduleResponseDTO; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.Collections; +import java.util.List; +import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -31,18 +44,6 @@ import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.*; - @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) class ScheduleCommandServiceTest { @@ -146,14 +147,14 @@ private void addScheduleSuccess(SchedulePeriod schedulePeriod) { .build(); study1.addSchedule(schedule); - owner.addSchedule(schedule); // 사용자 인증 정보 생성 getAuthentication(memberId); when(memberRepository.findById(memberId)).thenReturn(Optional.of(member1)); when(studyRepository.findById(studyId)).thenReturn(Optional.of(study1)); - when(studyMemberRepository.findByMemberIdAndStudyIdAndStatus(memberId, studyId, StudyApplicationStatus.APPROVED)) + when(studyMemberRepository.findByMemberIdAndStudyIdAndStatus(memberId, studyId, + StudyApplicationStatus.APPROVED)) .thenReturn(Optional.of(studyMember2)); when(studyMemberRepository.findAllByStudyIdAndStatus(studyId, StudyApplicationStatus.APPROVED)) .thenReturn(List.of(studyMember1, studyMember2)); @@ -238,7 +239,6 @@ private void addScheduleFail(LocalDateTime startedAt, LocalDateTime finishedAt, .build(); study1.addSchedule(schedule); - owner.addSchedule(schedule); // 사용자 인증 정보 생성 getAuthentication(memberId); @@ -262,9 +262,11 @@ void modSchedule_Success() { Long studyId = 1L; Long scheduleId = 1L; - ScheduleRequestDTO.ScheduleDTO scheduleModDTO = getScheduleModDTO(memberId, scheduleId, studyId, member1, study1); + ScheduleRequestDTO.ScheduleDTO scheduleModDTO = getScheduleModDTO(memberId, scheduleId, studyId, member1, + study1); - when(studyMemberRepository.findByMemberIdAndStudyIdAndStatus(memberId, studyId, StudyApplicationStatus.APPROVED)) + when(studyMemberRepository.findByMemberIdAndStudyIdAndStatus(memberId, studyId, + StudyApplicationStatus.APPROVED)) .thenReturn(Optional.of(studyMember2)); when(scheduleRepository.findByIdAndMemberId(scheduleId, memberId)) .thenReturn(Optional.of(schedule)); @@ -273,13 +275,13 @@ void modSchedule_Success() { when(scheduleRepository.save(schedule)).thenReturn(schedule); // when - ScheduleResponseDTO.ScheduleDTO result = scheduleCommandService.modSchedule(studyId, scheduleId, scheduleModDTO); + ScheduleResponseDTO.ScheduleDTO result = scheduleCommandService.modSchedule(studyId, scheduleId, + scheduleModDTO); // then assertThat(result).isNotNull(); assertThat(result.getTitle()).isEqualTo("수정된 일정"); assertThat(study1.getSchedules()).isNotEmpty(); - assertThat(member1.getScheduleList()).isNotEmpty(); verify(scheduleRepository, times(1)).save(any(Schedule.class)); } @@ -292,7 +294,8 @@ void modSchedule_NotStudyMember_Fail() { Long studyId = 1L; Long scheduleId = 1L; - ScheduleRequestDTO.ScheduleDTO scheduleModDTO = getScheduleModDTO(memberId, scheduleId, studyId, member2, study1); + ScheduleRequestDTO.ScheduleDTO scheduleModDTO = getScheduleModDTO(memberId, scheduleId, studyId, member2, + study1); // when & then assertThrows(StudyHandler.class, () -> scheduleCommandService.modSchedule(studyId, scheduleId, scheduleModDTO)); @@ -307,7 +310,8 @@ void modSchedule_NotCreator_Fail() { Long studyId = 1L; Long scheduleId = 1L; - ScheduleRequestDTO.ScheduleDTO scheduleModDTO = getScheduleModDTO(memberId, scheduleId, studyId, member1, study1); + ScheduleRequestDTO.ScheduleDTO scheduleModDTO = getScheduleModDTO(memberId, scheduleId, studyId, member1, + study1); // when & then assertThrows(StudyHandler.class, () -> scheduleCommandService.modSchedule(studyId, scheduleId, scheduleModDTO)); @@ -322,7 +326,8 @@ void modSchedule_NotStudySchedule_Fail() { Long studyId = 2L; Long scheduleId = 1L; - ScheduleRequestDTO.ScheduleDTO scheduleModDTO = getScheduleModDTO(memberId, scheduleId, studyId, member1, study1); + ScheduleRequestDTO.ScheduleDTO scheduleModDTO = getScheduleModDTO(memberId, scheduleId, studyId, member1, + study1); // when & then assertThrows(StudyHandler.class, () -> scheduleCommandService.modSchedule(studyId, scheduleId, scheduleModDTO)); @@ -356,7 +361,6 @@ private ScheduleRequestDTO.ScheduleDTO getScheduleModDTO( .build(); study.addSchedule(schedule); - member.addSchedule(schedule); when(memberRepository.findById(memberId)).thenReturn(Optional.of(member)); when(studyRepository.findById(studyId)).thenReturn(Optional.of(study)); @@ -369,15 +373,12 @@ private ScheduleRequestDTO.ScheduleDTO getScheduleModDTO( private static void initMember() { member1 = Member.builder() .id(1L) - .scheduleList(new ArrayList<>()) .build(); member2 = Member.builder() .id(2L) - .scheduleList(new ArrayList<>()) .build(); owner = Member.builder() .id(3L) - .scheduleList(new ArrayList<>()) .build(); } @@ -418,7 +419,8 @@ private static void initMemberStudy() { private static void getAuthentication(Long memberId) { String idString = String.valueOf(memberId); - Authentication authentication = new UsernamePasswordAuthenticationToken(idString, null, Collections.emptyList()); + Authentication authentication = new UsernamePasswordAuthenticationToken(idString, null, + Collections.emptyList()); SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); securityContext.setAuthentication(authentication); SecurityContextHolder.setContext(securityContext); diff --git a/src/test/java/com/example/spot/service/schedule/ScheduleQueryServiceTest.java b/src/test/java/com/example/spot/service/schedule/ScheduleQueryServiceTest.java index e1a2e319..2de1cb46 100644 --- a/src/test/java/com/example/spot/service/schedule/ScheduleQueryServiceTest.java +++ b/src/test/java/com/example/spot/service/schedule/ScheduleQueryServiceTest.java @@ -1,20 +1,29 @@ package com.example.spot.service.schedule; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.when; + import com.example.spot.common.api.exception.GeneralException; import com.example.spot.common.api.exception.handler.StudyHandler; import com.example.spot.member.domain.Member; +import com.example.spot.member.domain.enums.Gender; +import com.example.spot.member.infrastructure.MemberRepository; import com.example.spot.schedule.application.ScheduleQueryServiceImpl; import com.example.spot.schedule.domain.Schedule; +import com.example.spot.schedule.domain.ScheduleRepository; import com.example.spot.schedule.domain.enums.SchedulePeriod; -import com.example.spot.study.domain.enums.StudyApplicationStatus; -import com.example.spot.member.domain.enums.Gender; -import com.example.spot.study.domain.association.StudyMember; +import com.example.spot.schedule.presentation.dto.response.ScheduleResponseDTO; import com.example.spot.study.domain.Study; -import com.example.spot.member.domain.MemberRepository; -import com.example.spot.study.domain.repository.StudyMemberRepository; -import com.example.spot.schedule.domain.ScheduleRepository; import com.example.spot.study.domain.StudyRepository; -import com.example.spot.schedule.presentation.dto.response.ScheduleResponseDTO; +import com.example.spot.study.domain.association.StudyMember; +import com.example.spot.study.domain.enums.StudyApplicationStatus; +import com.example.spot.study.domain.repository.StudyMemberRepository; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.Collections; +import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -30,18 +39,6 @@ import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Optional; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.when; - @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) class ScheduleQueryServiceTest { @@ -85,11 +82,14 @@ void setUp() { when(studyMemberRepository.findById(member1Study1.getId())).thenReturn(Optional.of(member1Study1)); when(studyMemberRepository.findById(ownerStudy1.getId())).thenReturn(Optional.of(ownerStudy1)); - when(studyMemberRepository.existsByMemberIdAndStudyIdAndStatus(member1.getId(), study1.getId(), StudyApplicationStatus.APPROVED)).thenReturn(true); - when(studyMemberRepository.existsByMemberIdAndStudyIdAndStatus(member2.getId(), study1.getId(), StudyApplicationStatus.APPROVED)).thenReturn(false); - when(studyMemberRepository.existsByMemberIdAndStudyIdAndStatus(owner.getId(), study1.getId(), StudyApplicationStatus.APPROVED)).thenReturn(true); + when(studyMemberRepository.existsByMemberIdAndStudyIdAndStatus(member1.getId(), study1.getId(), + StudyApplicationStatus.APPROVED)).thenReturn(true); + when(studyMemberRepository.existsByMemberIdAndStudyIdAndStatus(member2.getId(), study1.getId(), + StudyApplicationStatus.APPROVED)).thenReturn(false); + when(studyMemberRepository.existsByMemberIdAndStudyIdAndStatus(owner.getId(), study1.getId(), + StudyApplicationStatus.APPROVED)).thenReturn(true); when(studyMemberRepository.findByMemberIdAndStudyIdAndStatus( - member1.getId(), study1.getId(),StudyApplicationStatus.APPROVED) + member1.getId(), study1.getId(), StudyApplicationStatus.APPROVED) ).thenReturn(Optional.of(member1Study1)); when(scheduleRepository.findById(schedule1.getId())).thenReturn(Optional.of(schedule1)); @@ -180,11 +180,11 @@ void getSchedule_NotStudySchedule_Fail() { assertThrows(StudyHandler.class, () -> scheduleQueryService.getSchedule(scheduleId, studyId)); } -/* ------------------------------------------------ 스터디 모임 목록 조회 --------------------------------------------------- */ + /* ------------------------------------------------ 스터디 모임 목록 조회 --------------------------------------------------- */ @Test @DisplayName("스터디 모임 목록 조회 - 로그인 한 회원이 해당 스터디 회원이 아닌 경우") - void 스터디_모임_목록_조회_실패_1(){ + void 스터디_모임_목록_조회_실패_1() { // given Long memberId = 1L; @@ -199,7 +199,7 @@ void getSchedule_NotStudySchedule_Fail() { @Test @DisplayName("스터디 모임 목록 조회 - 스터디 모임 일정이 존재하지 않는 경우") - void 스터디_모임_목록_조회_실패_2(){ + void 스터디_모임_목록_조회_실패_2() { // given Long memberId = 1L; @@ -219,15 +219,12 @@ void getSchedule_NotStudySchedule_Fail() { private static void initMember() { member1 = Member.builder() .id(1L) - .scheduleList(new ArrayList<>()) .build(); member2 = Member.builder() .id(2L) - .scheduleList(new ArrayList<>()) .build(); owner = Member.builder() .id(3L) - .scheduleList(new ArrayList<>()) .build(); } @@ -293,7 +290,6 @@ private static void initSchedule() { .schedulePeriod(SchedulePeriod.NONE) .build(); study1.addSchedule(schedule1); - owner.addSchedule(schedule1); schedule2 = Schedule.builder() .id(2L) @@ -305,7 +301,6 @@ private static void initSchedule() { .schedulePeriod(SchedulePeriod.MONTHLY) .build(); study1.addSchedule(schedule2); - member1.addSchedule(schedule2); schedule3 = Schedule.builder() .id(3L) @@ -317,12 +312,12 @@ private static void initSchedule() { .schedulePeriod(SchedulePeriod.WEEKLY) .build(); study2.addSchedule(schedule3); - member2.addSchedule(schedule3); } private static void getAuthentication(Long memberId) { String idString = String.valueOf(memberId); - Authentication authentication = new UsernamePasswordAuthenticationToken(idString, null, Collections.emptyList()); + Authentication authentication = new UsernamePasswordAuthenticationToken(idString, null, + Collections.emptyList()); SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); securityContext.setAuthentication(authentication); SecurityContextHolder.setContext(securityContext); diff --git a/src/test/java/com/example/spot/service/story/StoryCommandServiceTest.java b/src/test/java/com/example/spot/service/story/StoryCommandServiceTest.java index 74851440..476bff85 100644 --- a/src/test/java/com/example/spot/service/story/StoryCommandServiceTest.java +++ b/src/test/java/com/example/spot/service/story/StoryCommandServiceTest.java @@ -1,33 +1,44 @@ package com.example.spot.service.story; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + import com.example.spot.common.api.exception.handler.StudyHandler; +import com.example.spot.common.application.s3.S3ImageService; import com.example.spot.member.domain.Member; +import com.example.spot.member.domain.enums.Gender; +import com.example.spot.member.infrastructure.MemberRepository; import com.example.spot.notification.domain.Notification; +import com.example.spot.notification.domain.NotificationRepository; import com.example.spot.report.domain.StoryReportRepository; +import com.example.spot.story.application.StoryCommandServiceImpl; import com.example.spot.story.domain.Story; import com.example.spot.story.domain.StoryRepository; import com.example.spot.story.domain.association.LikedStory; import com.example.spot.story.domain.association.LikedStoryComment; import com.example.spot.story.domain.association.StoryComment; +import com.example.spot.story.domain.enums.StoryCategory; import com.example.spot.story.domain.repository.LikedStoryCommentRepository; import com.example.spot.story.domain.repository.LikedStoryRepository; import com.example.spot.story.domain.repository.StoryCommentRepository; import com.example.spot.story.domain.repository.StoryImageRepository; -import com.example.spot.study.domain.association.StudyMember; -import com.example.spot.study.domain.enums.StudyApplicationStatus; -import com.example.spot.member.domain.enums.Gender; -import com.example.spot.story.domain.enums.StoryCategory; -import com.example.spot.study.domain.Study; -import com.example.spot.member.domain.MemberRepository; -import com.example.spot.study.domain.repository.StudyMemberRepository; -import com.example.spot.notification.domain.NotificationRepository; -import com.example.spot.study.domain.StudyRepository; -import com.example.spot.story.application.StoryCommandServiceImpl; -import com.example.spot.common.application.s3.S3ImageService; import com.example.spot.story.web.dto.request.StoryCommentRequestDTO; import com.example.spot.story.web.dto.request.StoryRequestDTO; import com.example.spot.story.web.dto.response.StoryCommentResponseDTO; import com.example.spot.story.web.dto.response.StoryResponseDTO; +import com.example.spot.study.domain.Study; +import com.example.spot.study.domain.StudyRepository; +import com.example.spot.study.domain.association.StudyMember; +import com.example.spot.study.domain.enums.StudyApplicationStatus; +import com.example.spot.study.domain.repository.StudyMemberRepository; +import java.util.Collections; +import java.util.List; +import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -43,16 +54,6 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.multipart.MultipartFile; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.*; - @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) class StoryCommandServiceTest { @@ -140,7 +141,7 @@ void setUp() { when(likedStoryRepository.existsByMemberIdAndStoryId(3L, 1L)) .thenReturn(true); - // Comment + // Comment when(storyCommentRepository.findAllByStoryId(1L)) .thenReturn(List.of(studyPost1Comment1, studyPost1Comment2)); when(storyCommentRepository.findById(1L)).thenReturn(Optional.of(studyPost1Comment1)); @@ -151,7 +152,7 @@ void setUp() { } -/*-------------------------------------------------------- 게시글 작성 ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- 게시글 작성 ------------------------------------------------------------------------*/ @Test @DisplayName("스터디 게시글 작성 - 공지 게시글 (성공)") @@ -308,7 +309,7 @@ void createPost_TitleOverflow_Fail() { } -/*-------------------------------------------------------- 게시글 삭제 ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- 게시글 삭제 ------------------------------------------------------------------------*/ @Test @DisplayName("스터디 게시글 삭제 - (성공)") @@ -377,7 +378,7 @@ void deletePost_NotAvailableMember_Fail() { assertThrows(StudyHandler.class, () -> studyPostCommandService.deletePost(studyId, postId)); } -/*-------------------------------------------------------- 게시글 좋아요 ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- 게시글 좋아요 ------------------------------------------------------------------------*/ @Test @DisplayName("스터디 게시글 좋아요 - (성공)") @@ -429,6 +430,7 @@ void likePost_NotStudyMember_Fail() { // when & then assertThrows(StudyHandler.class, () -> studyPostCommandService.likePost(studyId, postId)); } + @Test @DisplayName("스터디 게시글 좋아요 - 이미 좋아요를 누른 경우 (실패)") void likePost_AlreadyLiked_Fail() { @@ -452,7 +454,7 @@ void likePost_AlreadyLiked_Fail() { } -/*-------------------------------------------------------- 게시글 좋아요 취소 ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- 게시글 좋아요 취소 ------------------------------------------------------------------------*/ @Test @DisplayName("스터디 게시글 좋아요 취소 - (성공)") @@ -524,7 +526,7 @@ void cancelPostLike_NotLiked_Fail() { } -/*-------------------------------------------------------- 댓글 작성 ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- 댓글 작성 ------------------------------------------------------------------------*/ @Test @DisplayName("스터디 게시글 댓글 작성 - 익명 댓글 (성공)") @@ -621,7 +623,7 @@ void createComment_NotStudyMember_Fail() { -/*-------------------------------------------------------- 답글 작성 ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- 답글 작성 ------------------------------------------------------------------------*/ @Test @DisplayName("스터디 게시글 답글 작성 - 익명 댓글 (성공)") @@ -724,7 +726,8 @@ void createReply_NotStudyMember_Fail() { .thenReturn(List.of()); // when - assertThrows(StudyHandler.class, () -> studyPostCommandService.createReply(studyId, postId, commentId, commentDTO)); + assertThrows(StudyHandler.class, + () -> studyPostCommandService.createReply(studyId, postId, commentId, commentDTO)); } @Test @@ -757,14 +760,14 @@ void createReply_ParentCommentNotExist_Fail() { } -/*-------------------------------------------------------- 댓글 삭제 ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- 댓글 삭제 ------------------------------------------------------------------------*/ // @Test // @DisplayName("스터디 게시글 댓글 삭제") // void deleteComment() { // } -/*-------------------------------------------------------- 댓글 좋아요 ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- 댓글 좋아요 ------------------------------------------------------------------------*/ @Test @DisplayName("스터디 게시글 댓글 좋아요 - (성공)") @@ -785,7 +788,8 @@ void likeComment_Success() { when(likedStoryCommentRepository.save(any(LikedStoryComment.class))).thenReturn(likedStoryComment); // when - StoryCommentResponseDTO.CommentPreviewDTO result = studyPostCommandService.likeComment(studyId, postId, commentId); + StoryCommentResponseDTO.CommentPreviewDTO result = studyPostCommandService.likeComment(studyId, postId, + commentId); // then assertNotNull(result); @@ -839,7 +843,7 @@ void likeComment_AlreadyLiked_Fail() { } -/*-------------------------------------------------------- 댓글 싫어요 ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- 댓글 싫어요 ------------------------------------------------------------------------*/ @Test @DisplayName("스터디 게시글 댓글 싫어요 - (성공)") @@ -860,7 +864,8 @@ void dislikeComment_Success() { when(likedStoryCommentRepository.save(any(LikedStoryComment.class))).thenReturn(likedStoryComment); // when - StoryCommentResponseDTO.CommentPreviewDTO result = studyPostCommandService.dislikeComment(studyId, postId, commentId); + StoryCommentResponseDTO.CommentPreviewDTO result = studyPostCommandService.dislikeComment(studyId, postId, + commentId); // then assertNotNull(result); @@ -914,7 +919,7 @@ void dislikeComment_AlreadyDisliked_Fail() { assertThrows(StudyHandler.class, () -> studyPostCommandService.dislikeComment(studyId, postId, commentId)); } -/*-------------------------------------------------------- 댓글 좋아요 취소 ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- 댓글 좋아요 취소 ------------------------------------------------------------------------*/ @Test @DisplayName("스터디 게시글 댓글 좋아요 취소 - (성공)") @@ -928,7 +933,8 @@ void cancelCommentLike_Success() { getAuthentication(memberId); - when(likedStoryCommentRepository.findByMemberIdAndStoryCommentIdAndIsLiked(memberId, studyPost1Comment2.getId(), true)) + when(likedStoryCommentRepository.findByMemberIdAndStoryCommentIdAndIsLiked(memberId, studyPost1Comment2.getId(), + true)) .thenReturn(Optional.of(likedStoryComment)); when(storyCommentRepository.save(any(StoryComment.class))).thenReturn(studyPost1Comment2); @@ -955,7 +961,8 @@ void cancelCommentLike_NotStudyMember_Fail() { getAuthentication(memberId); - when(likedStoryCommentRepository.findByMemberIdAndStoryCommentIdAndIsLiked(memberId, studyPost1Comment2.getId(), true)) + when(likedStoryCommentRepository.findByMemberIdAndStoryCommentIdAndIsLiked(memberId, studyPost1Comment2.getId(), + true)) .thenReturn(Optional.of(likedStoryComment)); when(storyCommentRepository.save(any(StoryComment.class))).thenReturn(studyPost1Comment2); @@ -975,7 +982,8 @@ void cancelCommentLike_NotLiked_Fail() { getAuthentication(memberId); - when(likedStoryCommentRepository.findByMemberIdAndStoryCommentIdAndIsLiked(memberId, studyPost1Comment2.getId(), true)) + when(likedStoryCommentRepository.findByMemberIdAndStoryCommentIdAndIsLiked(memberId, studyPost1Comment2.getId(), + true)) .thenReturn(Optional.empty()); when(storyCommentRepository.save(any(StoryComment.class))).thenReturn(studyPost1Comment2); @@ -983,7 +991,7 @@ void cancelCommentLike_NotLiked_Fail() { assertThrows(StudyHandler.class, () -> studyPostCommandService.cancelCommentLike(studyId, postId, commentId)); } -/*-------------------------------------------------------- 댓글 싫어요 취소 ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- 댓글 싫어요 취소 ------------------------------------------------------------------------*/ @Test @DisplayName("스터디 게시글 댓글 싫어요 취소 - (성공)") @@ -997,7 +1005,8 @@ void cancelCommentDislike() { getAuthentication(memberId); - when(likedStoryCommentRepository.findByMemberIdAndStoryCommentIdAndIsLiked(memberId, studyPost1Comment2.getId(), false)) + when(likedStoryCommentRepository.findByMemberIdAndStoryCommentIdAndIsLiked(memberId, studyPost1Comment2.getId(), + false)) .thenReturn(Optional.of(studyDislikedComment)); when(storyCommentRepository.save(any(StoryComment.class))).thenReturn(studyPost1Comment2); @@ -1024,12 +1033,14 @@ void cancelCommentDislike_NotStudyMember_Fail() { getAuthentication(memberId); - when(likedStoryCommentRepository.findByMemberIdAndStoryCommentIdAndIsLiked(memberId, studyPost1Comment2.getId(), false)) + when(likedStoryCommentRepository.findByMemberIdAndStoryCommentIdAndIsLiked(memberId, studyPost1Comment2.getId(), + false)) .thenReturn(Optional.of(studyDislikedComment)); when(storyCommentRepository.save(any(StoryComment.class))).thenReturn(studyPost1Comment2); // when & then - assertThrows(StudyHandler.class, () -> studyPostCommandService.cancelCommentDislike(studyId, postId, commentId)); + assertThrows(StudyHandler.class, + () -> studyPostCommandService.cancelCommentDislike(studyId, postId, commentId)); } @Test @@ -1044,20 +1055,23 @@ void cancelCommentDislike_NotDisliked_Fail() { getAuthentication(memberId); - when(likedStoryCommentRepository.findByMemberIdAndStoryCommentIdAndIsLiked(memberId, studyPost1Comment2.getId(), false)) + when(likedStoryCommentRepository.findByMemberIdAndStoryCommentIdAndIsLiked(memberId, studyPost1Comment2.getId(), + false)) .thenReturn(Optional.empty()); when(storyCommentRepository.save(any(StoryComment.class))).thenReturn(studyPost1Comment2); // when & then - assertThrows(StudyHandler.class, () -> studyPostCommandService.cancelCommentDislike(studyId, postId, commentId)); + assertThrows(StudyHandler.class, + () -> studyPostCommandService.cancelCommentDislike(studyId, postId, commentId)); } -/*-------------------------------------------------------- Utils ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- Utils ------------------------------------------------------------------------*/ private static void getAuthentication(Long memberId) { String idString = String.valueOf(memberId); - Authentication authentication = new UsernamePasswordAuthenticationToken(idString, null, Collections.emptyList()); + Authentication authentication = new UsernamePasswordAuthenticationToken(idString, null, + Collections.emptyList()); SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); securityContext.setAuthentication(authentication); SecurityContextHolder.setContext(securityContext); @@ -1067,26 +1081,14 @@ private static void initMember() { member1 = Member.builder() .id(1L) .name("회원1") - .storyList(new ArrayList<>()) - .likedStoryList(new ArrayList<>()) - .storyCommentList(new ArrayList<>()) - .likedStoryCommentList(new ArrayList<>()) .build(); member2 = Member.builder() .id(2L) .name("회원2") - .storyList(new ArrayList<>()) - .likedStoryList(new ArrayList<>()) - .storyCommentList(new ArrayList<>()) - .likedStoryCommentList(new ArrayList<>()) .build(); owner = Member.builder() .id(3L) .name("회원3") - .storyList(new ArrayList<>()) - .likedStoryList(new ArrayList<>()) - .storyCommentList(new ArrayList<>()) - .likedStoryCommentList(new ArrayList<>()) .build(); } @@ -1116,7 +1118,6 @@ private static void initMemberStudy() { .member(owner) .study(study) .build(); - owner.addMemberStudy(ownerStudy); study.addMemberStudy(ownerStudy); member1Study = StudyMember.builder() @@ -1127,7 +1128,6 @@ private static void initMemberStudy() { .member(member1) .study(study) .build(); - member1.addMemberStudy(member1Study); study.addMemberStudy(member1Study); } @@ -1144,7 +1144,6 @@ private static void initStudyPost() { .likeNum(0) .commentNum(0) .build(); - member1.addStudyPost(story1); study.addStudyPost(story1); story2 = Story.builder() @@ -1159,7 +1158,6 @@ private static void initStudyPost() { .likeNum(0) .commentNum(0) .build(); - owner.addStudyPost(story2); study.addStudyPost(story2); story3 = Story.builder() @@ -1174,10 +1172,9 @@ private static void initStudyPost() { .likeNum(0) .commentNum(0) .build(); - owner.addStudyPost(story3); study.addStudyPost(story3); - for (int i=0; i<10; i++) { + for (int i = 0; i < 10; i++) { story1.plusHitNum(); } } @@ -1190,7 +1187,6 @@ private static void initStudyLikedPost() { .build(); story1.addLikedPost(likedStory); story1.plusLikeNum(); - owner.addStudyLikedPost(likedStory); } private static void initStudyPostComment() { @@ -1232,7 +1228,6 @@ private static void initStudyLikedComment() { .build(); studyPost1Comment2.addLikedComment(likedStoryComment); studyPost1Comment2.plusLikeCount(); - member1.addStudyLikedComment(likedStoryComment); studyDislikedComment = LikedStoryComment.builder() .id(2L) @@ -1242,6 +1237,5 @@ private static void initStudyLikedComment() { .build(); studyPost1Comment2.addLikedComment(studyDislikedComment); studyPost1Comment2.plusDislikeCount(); - owner.addStudyLikedComment(studyDislikedComment); } } \ No newline at end of file diff --git a/src/test/java/com/example/spot/service/story/StoryQueryServiceTest.java b/src/test/java/com/example/spot/service/story/StoryQueryServiceTest.java index 568a5228..258fd282 100644 --- a/src/test/java/com/example/spot/service/story/StoryQueryServiceTest.java +++ b/src/test/java/com/example/spot/service/story/StoryQueryServiceTest.java @@ -1,27 +1,36 @@ package com.example.spot.service.story; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.when; + import com.example.spot.common.api.exception.GeneralException; import com.example.spot.common.api.exception.handler.StudyHandler; import com.example.spot.member.domain.Member; +import com.example.spot.member.domain.enums.Gender; +import com.example.spot.member.infrastructure.MemberRepository; +import com.example.spot.story.application.StoryQueryServiceImpl; import com.example.spot.story.domain.Story; +import com.example.spot.story.domain.StoryRepository; import com.example.spot.story.domain.association.LikedStory; import com.example.spot.story.domain.association.LikedStoryComment; import com.example.spot.story.domain.association.StoryComment; import com.example.spot.story.domain.enums.StoryCategory; +import com.example.spot.story.domain.enums.StoryCategoryQuery; import com.example.spot.story.domain.repository.LikedStoryRepository; import com.example.spot.story.domain.repository.StoryCommentRepository; -import com.example.spot.story.domain.StoryRepository; +import com.example.spot.story.web.dto.response.StoryCommentResponseDTO; +import com.example.spot.story.web.dto.response.StoryResponseDTO; +import com.example.spot.study.domain.Study; +import com.example.spot.study.domain.StudyRepository; import com.example.spot.study.domain.association.StudyMember; import com.example.spot.study.domain.enums.StudyApplicationStatus; -import com.example.spot.member.domain.enums.Gender; -import com.example.spot.story.domain.enums.StoryCategoryQuery; -import com.example.spot.study.domain.Study; -import com.example.spot.member.domain.MemberRepository; import com.example.spot.study.domain.repository.StudyMemberRepository; -import com.example.spot.study.domain.StudyRepository; -import com.example.spot.story.application.StoryQueryServiceImpl; -import com.example.spot.story.web.dto.response.StoryCommentResponseDTO; -import com.example.spot.story.web.dto.response.StoryResponseDTO; +import java.util.Collections; +import java.util.List; +import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -37,15 +46,6 @@ import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.when; - @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) class StoryQueryServiceTest { @@ -129,7 +129,7 @@ void setUp() { } -/*-------------------------------------------------------- 게시글 목록 조회 ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- 게시글 목록 조회 ------------------------------------------------------------------------*/ @Test @DisplayName("스터디 게시글 목록 조회 - 전체 게시글 조회 (성공)") @@ -179,7 +179,8 @@ void getAllPosts_Theme_Success() { .thenReturn(List.of(story1, story3)); // when - StoryResponseDTO.StoryListDTO result = studyPostQueryService.getAllPosts(pageRequest, studyId, StoryCategoryQuery.FREE_TALK); + StoryResponseDTO.StoryListDTO result = studyPostQueryService.getAllPosts(pageRequest, studyId, + StoryCategoryQuery.FREE_TALK); // then assertNotNull(result); @@ -208,7 +209,8 @@ void getAllPosts_Announcements_Success() { .thenReturn(List.of(story1, story3)); // when - StoryResponseDTO.StoryListDTO result = studyPostQueryService.getAllPosts(pageRequest, studyId, StoryCategoryQuery.ANNOUNCEMENT); + StoryResponseDTO.StoryListDTO result = studyPostQueryService.getAllPosts(pageRequest, studyId, + StoryCategoryQuery.ANNOUNCEMENT); // then assertNotNull(result); @@ -259,11 +261,12 @@ void getAllPosts_NotCategorized_Fail() { .thenReturn(List.of(story1, story3)); // when & then - assertThrows(IllegalArgumentException.class, () -> studyPostQueryService.getAllPosts(pageRequest, studyId, StoryCategoryQuery.valueOf("Nothing"))); + assertThrows(IllegalArgumentException.class, + () -> studyPostQueryService.getAllPosts(pageRequest, studyId, StoryCategoryQuery.valueOf("Nothing"))); } -/*-------------------------------------------------------- 게시글 조회 ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- 게시글 조회 ------------------------------------------------------------------------*/ @Test @DisplayName("스터디 게시글 단건 조회 - 일반 조회 (성공)") @@ -353,7 +356,7 @@ void getPost_NotStudyMember_Fail() { .thenReturn(false); // when & then - assertThrows(StudyHandler.class, () ->studyPostQueryService.getPost(studyId, postId, false)); + assertThrows(StudyHandler.class, () -> studyPostQueryService.getPost(studyId, postId, false)); } @Test @@ -373,14 +376,14 @@ void getPost_NotStudyPost_Fail() { .thenReturn(List.of()); // when & then - assertThrows(StudyHandler.class, () ->studyPostQueryService.getPost(studyId, postId, false)); + assertThrows(StudyHandler.class, () -> studyPostQueryService.getPost(studyId, postId, false)); } -/* ------------------------------------------------ 스터디 공지사항 조회 --------------------------------------------------- */ + /* ------------------------------------------------ 스터디 공지사항 조회 --------------------------------------------------- */ @Test @DisplayName("스터디 공지사항 조회 - 성공") - void 스터디_공지사항_조회_성공(){ + void 스터디_공지사항_조회_성공() { // given long studyId = 1L; @@ -394,27 +397,30 @@ void getPost_NotStudyPost_Fail() { .build(); StudyMember studyMember = StudyMember.builder() - .introduction(title).study(study).member(owner).isOwned(true).status(StudyApplicationStatus.APPROVED).build(); + .introduction(title).study(study).member(owner).isOwned(true).status(StudyApplicationStatus.APPROVED) + .build(); when(storyRepository.findByStudyIdAndIsAnnouncement(studyId, true)).thenReturn(Optional.of(story)); - when(studyMemberRepository.findByMemberIdAndStudyIdAndStatus(1L, studyId, StudyApplicationStatus.APPROVED)).thenReturn( + when(studyMemberRepository.findByMemberIdAndStudyIdAndStatus(1L, studyId, + StudyApplicationStatus.APPROVED)).thenReturn( Optional.ofNullable(studyMember)); // when StoryResponseDTO.StoryContentDTO result = studyPostQueryService.findStudyAnnouncementPost(studyId); // then - assertEquals(title,result.getTitle()); + assertEquals(title, result.getTitle()); assertEquals(content, result.getContent()); } @Test @DisplayName("스터디 공지사항 조회 - 로그인 한 회원이 해당 스터디 회원이 아닌 경우") - void 스터디_공지사항_조회_실패_1(){ + void 스터디_공지사항_조회_실패_1() { // given long studyId = 1L; - when(studyMemberRepository.findByMemberIdAndStudyIdAndStatus(1L, studyId, StudyApplicationStatus.APPROVED)).thenReturn( + when(studyMemberRepository.findByMemberIdAndStudyIdAndStatus(1L, studyId, + StudyApplicationStatus.APPROVED)).thenReturn( Optional.empty()); // when & then @@ -423,15 +429,16 @@ void getPost_NotStudyPost_Fail() { @Test @DisplayName("스터디 공지사항 조회 - 스터디 공지 글이 없는 경우") - void 스터디_공지사항_조회_실패_2(){ + void 스터디_공지사항_조회_실패_2() { // given long studyId = 1L; StudyMember studyMember = StudyMember.builder() - .introduction("title").study(study).member(owner).isOwned(true).status(StudyApplicationStatus.APPROVED).build(); - + .introduction("title").study(study).member(owner).isOwned(true).status(StudyApplicationStatus.APPROVED) + .build(); - when(studyMemberRepository.findByMemberIdAndStudyIdAndStatus(1L, studyId, StudyApplicationStatus.APPROVED)).thenReturn( + when(studyMemberRepository.findByMemberIdAndStudyIdAndStatus(1L, studyId, + StudyApplicationStatus.APPROVED)).thenReturn( Optional.ofNullable(studyMember)); when(storyRepository.findByStudyIdAndIsAnnouncement(studyId, true)).thenReturn(Optional.empty()); @@ -513,11 +520,12 @@ void getAllComments_NotStudyPost_Fail() { } -/*-------------------------------------------------------- Utils ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- Utils ------------------------------------------------------------------------*/ private static void getAuthentication(Long memberId) { String idString = String.valueOf(memberId); - Authentication authentication = new UsernamePasswordAuthenticationToken(idString, null, Collections.emptyList()); + Authentication authentication = new UsernamePasswordAuthenticationToken(idString, null, + Collections.emptyList()); SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); securityContext.setAuthentication(authentication); SecurityContextHolder.setContext(securityContext); @@ -526,24 +534,12 @@ private static void getAuthentication(Long memberId) { private static void initMember() { member1 = Member.builder() .id(1L) - .storyList(new ArrayList<>()) - .likedStoryList(new ArrayList<>()) - .storyCommentList(new ArrayList<>()) - .likedStoryCommentList(new ArrayList<>()) .build(); member2 = Member.builder() .id(2L) - .storyList(new ArrayList<>()) - .likedStoryList(new ArrayList<>()) - .storyCommentList(new ArrayList<>()) - .likedStoryCommentList(new ArrayList<>()) .build(); owner = Member.builder() .id(3L) - .storyList(new ArrayList<>()) - .likedStoryList(new ArrayList<>()) - .storyCommentList(new ArrayList<>()) - .likedStoryCommentList(new ArrayList<>()) .build(); } @@ -573,7 +569,6 @@ private static void initMemberStudy() { .member(owner) .study(study) .build(); - owner.addMemberStudy(ownerStudy); study.addMemberStudy(ownerStudy); member1Study = StudyMember.builder() @@ -584,7 +579,6 @@ private static void initMemberStudy() { .member(member1) .study(study) .build(); - member1.addMemberStudy(member1Study); study.addMemberStudy(member1Study); } @@ -601,7 +595,6 @@ private static void initStudyPost() { .likeNum(0) .commentNum(0) .build(); - member1.addStudyPost(story1); study.addStudyPost(story1); story2 = Story.builder() @@ -616,7 +609,6 @@ private static void initStudyPost() { .likeNum(0) .commentNum(0) .build(); - owner.addStudyPost(story2); study.addStudyPost(story2); story3 = Story.builder() @@ -631,10 +623,9 @@ private static void initStudyPost() { .likeNum(0) .commentNum(0) .build(); - owner.addStudyPost(story3); study.addStudyPost(story3); - for (int i=0; i<10; i++) { + for (int i = 0; i < 10; i++) { story1.plusHitNum(); } } @@ -647,7 +638,6 @@ private static void initStudyLikedPost() { .build(); story1.addLikedPost(likedStory); story1.plusLikeNum(); - owner.addStudyLikedPost(likedStory); } private static void initStudyPostComment() { @@ -688,7 +678,6 @@ private static void initStudyLikedComment() { .build(); studyPost1Comment2.addLikedComment(likedStoryComment); studyPost1Comment2.plusLikeCount(); - member1.addStudyLikedComment(likedStoryComment); studyDislikedComment = LikedStoryComment.builder() .id(2L) @@ -698,6 +687,5 @@ private static void initStudyLikedComment() { .build(); studyPost1Comment2.addLikedComment(studyDislikedComment); studyPost1Comment2.plusDislikeCount(); - owner.addStudyLikedComment(studyDislikedComment); } } \ No newline at end of file diff --git a/src/test/java/com/example/spot/service/study/StudyCommandServiceTest.java b/src/test/java/com/example/spot/service/study/StudyCommandServiceTest.java index df242bf7..e193919d 100644 --- a/src/test/java/com/example/spot/service/study/StudyCommandServiceTest.java +++ b/src/test/java/com/example/spot/service/study/StudyCommandServiceTest.java @@ -1,32 +1,38 @@ package com.example.spot.service.study; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; -import com.example.spot.study.domain.association.StudyRegion; import com.example.spot.common.api.exception.handler.StudyHandler; import com.example.spot.member.domain.Member; +import com.example.spot.member.domain.enums.Gender; +import com.example.spot.member.infrastructure.MemberRepository; +import com.example.spot.study.application.StudyCommandServiceImpl; +import com.example.spot.study.domain.Study; +import com.example.spot.study.domain.StudyRepository; import com.example.spot.study.domain.association.Region; import com.example.spot.study.domain.association.StudyMember; +import com.example.spot.study.domain.association.StudyRegion; +import com.example.spot.study.domain.association.StudyTheme; import com.example.spot.study.domain.association.Theme; import com.example.spot.study.domain.enums.StudyApplicationStatus; -import com.example.spot.member.domain.enums.Gender; import com.example.spot.study.domain.enums.StudyState; import com.example.spot.study.domain.enums.ThemeType; -import com.example.spot.study.domain.association.StudyTheme; -import com.example.spot.study.domain.Study; -import com.example.spot.member.domain.MemberRepository; -import com.example.spot.study.domain.repository.StudyMemberRepository; import com.example.spot.study.domain.repository.RegionRepository; +import com.example.spot.study.domain.repository.StudyMemberRepository; import com.example.spot.study.domain.repository.StudyRegionRepository; -import com.example.spot.study.domain.StudyRepository; import com.example.spot.study.domain.repository.StudyThemeRepository; import com.example.spot.study.domain.repository.ThemeRepository; -import com.example.spot.study.application.StudyCommandServiceImpl; import com.example.spot.study.presentation.dto.request.StudyMemberRequestDTO; import com.example.spot.study.presentation.dto.response.StudyMemberResponseDTO; import com.example.spot.study.presentation.dto.response.StudyResponseDTO; +import java.util.Collections; +import java.util.List; +import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -41,11 +47,6 @@ import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Optional; - @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) class StudyCommandServiceTest { @@ -282,20 +283,17 @@ void registerStudy() { void likeStudy() { } -/*-------------------------------------------------------- Utils ------------------------------------------------------------------------*/ + /*-------------------------------------------------------- Utils ------------------------------------------------------------------------*/ private static void initMember() { member1 = Member.builder() .id(1L) - .scheduleList(new ArrayList<>()) .build(); member2 = Member.builder() .id(2L) - .scheduleList(new ArrayList<>()) .build(); owner = Member.builder() .id(3L) - .scheduleList(new ArrayList<>()) .build(); } @@ -349,7 +347,8 @@ private static void initTheme() { private static void getAuthentication(Long memberId) { String idString = String.valueOf(memberId); - Authentication authentication = new UsernamePasswordAuthenticationToken(idString, null, Collections.emptyList()); + Authentication authentication = new UsernamePasswordAuthenticationToken(idString, null, + Collections.emptyList()); SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); securityContext.setAuthentication(authentication); SecurityContextHolder.setContext(securityContext); diff --git a/src/test/java/com/example/spot/service/study/StudyQueryServiceTest.java b/src/test/java/com/example/spot/service/study/StudyQueryServiceTest.java index 63b6e4a3..b70f92c0 100644 --- a/src/test/java/com/example/spot/service/study/StudyQueryServiceTest.java +++ b/src/test/java/com/example/spot/service/study/StudyQueryServiceTest.java @@ -1,6 +1,8 @@ package com.example.spot.service.study; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.Mockito.mock; @@ -8,46 +10,45 @@ import static org.mockito.Mockito.when; import static org.mockito.internal.verification.VerificationModeFactory.times; -import com.example.spot.study.domain.association.StudyRegion; import com.example.spot.common.api.exception.handler.MemberHandler; import com.example.spot.common.api.exception.handler.StudyHandler; import com.example.spot.member.domain.Member; +import com.example.spot.member.domain.association.PreferredRegion; +import com.example.spot.member.domain.association.PreferredStudy; +import com.example.spot.member.domain.association.PreferredTheme; +import com.example.spot.member.domain.enums.Gender; +import com.example.spot.member.domain.enums.Status; +import com.example.spot.member.infrastructure.MemberRepository; +import com.example.spot.member.infrastructure.PreferredRegionRepository; +import com.example.spot.member.infrastructure.PreferredStudyRepository; +import com.example.spot.member.infrastructure.PreferredThemeRepository; +import com.example.spot.study.application.StudyQueryServiceImpl; +import com.example.spot.study.domain.Study; +import com.example.spot.study.domain.StudyRepository; import com.example.spot.study.domain.association.Region; import com.example.spot.study.domain.association.StudyMember; +import com.example.spot.study.domain.association.StudyRegion; +import com.example.spot.study.domain.association.StudyTheme; import com.example.spot.study.domain.association.Theme; -import com.example.spot.member.domain.enums.Gender; -import com.example.spot.member.domain.enums.Status; import com.example.spot.study.domain.enums.StudyApplicationStatus; import com.example.spot.study.domain.enums.StudyLikeStatus; import com.example.spot.study.domain.enums.StudySortBy; import com.example.spot.study.domain.enums.StudyState; import com.example.spot.study.domain.enums.ThemeType; -import com.example.spot.member.domain.association.MemberTheme; -import com.example.spot.member.domain.association.PreferredRegion; -import com.example.spot.member.domain.association.PreferredStudy; -import com.example.spot.study.domain.association.StudyTheme; -import com.example.spot.study.domain.Study; -import com.example.spot.member.domain.MemberRepository; import com.example.spot.study.domain.repository.StudyMemberRepository; -import com.example.spot.member.domain.association.MemberThemeRepository; -import com.example.spot.member.domain.association.PreferredRegionRepository; -import com.example.spot.member.domain.association.PreferredStudyRepository; import com.example.spot.study.domain.repository.StudyRegionRepository; -import com.example.spot.study.domain.StudyRepository; import com.example.spot.study.domain.repository.StudyThemeRepository; import com.example.spot.study.domain.repository.ThemeRepository; -import com.example.spot.study.application.StudyQueryServiceImpl; import com.example.spot.study.presentation.dto.request.StudySearchRequestDTO; import com.example.spot.study.presentation.dto.request.StudySearchRequestWithThemeDTO; import com.example.spot.study.presentation.dto.response.SearchResponseDTO.MyPageDTO; import com.example.spot.study.presentation.dto.response.SearchResponseDTO.StudyPreviewDTO; +import com.example.spot.study.presentation.dto.response.StudyResponseDTO; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; - -import com.example.spot.study.presentation.dto.response.StudyResponseDTO; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -85,7 +86,7 @@ class StudyQueryServiceTest { @Mock private StudyThemeRepository studyThemeRepository; @Mock - private MemberThemeRepository memberThemeRepository; + private PreferredThemeRepository preferredThemeRepository; // 지역 관련 조회 @Mock @@ -103,8 +104,8 @@ class StudyQueryServiceTest { private static Pageable pageable; private static Theme theme1; private static Theme theme2; - private static MemberTheme memberTheme1; - private static MemberTheme memberTheme2; + private static PreferredTheme preferredTheme1; + private static PreferredTheme preferredTheme2; private static StudyTheme studyTheme1; private static StudyTheme studyTheme2; private static StudySearchRequestDTO request; @@ -129,8 +130,8 @@ void setUp() { theme1 = getTheme(1L, ThemeType.어학); theme2 = getTheme(2L, ThemeType.공모전); - memberTheme1 = MemberTheme.builder().member(member).theme(theme1).build(); - memberTheme2 = MemberTheme.builder().member(member).theme(theme2).build(); + preferredTheme1 = PreferredTheme.builder().member(member).theme(theme1).build(); + preferredTheme2 = PreferredTheme.builder().member(member).theme(theme2).build(); studyTheme1 = new StudyTheme(theme1, study1); studyTheme2 = new StudyTheme(theme2, study2); @@ -147,7 +148,6 @@ void setUp() { preferredStudy1 = getPreferredStudy(member, study1); preferredStudy2 = getPreferredStudy(member, study2); - studyMember1 = getMemberStudy(member, study1); studyMember2 = getMemberStudy(member, study2); @@ -164,10 +164,11 @@ void setUp() { SecurityContextHolder.setContext(securityContext); when(memberRepository.existsById(member.getId())).thenReturn(true); - when(memberThemeRepository.findAllByMemberId(member.getId())).thenReturn(List.of(memberTheme1, memberTheme2)); + when(preferredThemeRepository.findAllByMemberId(member.getId())).thenReturn(List.of(preferredTheme1, + preferredTheme2)); when(studyThemeRepository.findAllByTheme(any())).thenReturn(List.of(studyTheme1, studyTheme2)); when(studyRepository.countStudyByConditionsAndThemeTypesAndNotInIds(any(), any(), any(), any())) - .thenReturn(2L); + .thenReturn(2L); when(studyRepository.findByStudyTheme(anyList())).thenReturn(List.of(study1, study2)); } @@ -243,8 +244,10 @@ void getMyPageStudyCount() { Long memberId = 1L; when(memberRepository.findById(memberId)).thenReturn(Optional.of(member)); - when(studyMemberRepository.countByMemberIdAndStatusAndStudy_Status(memberId, StudyApplicationStatus.APPLIED, Status.ON)).thenReturn(2L); - when(studyMemberRepository.countByMemberIdAndStatusAndStudy_Status(memberId, StudyApplicationStatus.APPROVED, Status.ON)).thenReturn(1L); + when(studyMemberRepository.countByMemberIdAndStatusAndStudy_Status(memberId, StudyApplicationStatus.APPLIED, + Status.ON)).thenReturn(2L); + when(studyMemberRepository.countByMemberIdAndStatusAndStudy_Status(memberId, StudyApplicationStatus.APPROVED, + Status.ON)).thenReturn(1L); when(studyMemberRepository.countByMemberIdAndIsOwnedAndStudy_Status(memberId, true, Status.ON)).thenReturn(3L); // when @@ -293,18 +296,18 @@ void getMyPageStudyCount() { @Test @DisplayName("검색 조건 없는 스터디 검색 - 페이징 테스트") - void 검색_조건_없는_스터디_검색_페이징(){ + void 검색_조건_없는_스터디_검색_페이징() { //given List studies = List.of(study1, study2); when(studyRepository.findAllStudy(any(), any())) - .thenReturn(studies); + .thenReturn(studies); when(studyRepository.count()) - .thenReturn(2L); + .thenReturn(2L); // when StudyPreviewDTO result = studyQueryService.findStudies( - PageRequest.of(0, 10), StudySortBy.ALL); + PageRequest.of(0, 10), StudySortBy.ALL); // then assertEquals(10, result.getSize()); @@ -334,7 +337,8 @@ void getMyPageStudyCount() { // given StudySearchRequestDTO request = getStudySearchRequestDTO(); Map conditions = getStringObjectMap(); - when(studyRepository.findAllStudyByConditions(conditions, StudySortBy.ALL, pageable)).thenReturn(List.of(study1, study2)); + when(studyRepository.findAllStudyByConditions(conditions, StudySortBy.ALL, pageable)).thenReturn( + List.of(study1, study2)); when(studyRepository.countStudyByConditions(conditions, StudySortBy.ALL)).thenReturn(2L); // when @@ -349,7 +353,7 @@ void getMyPageStudyCount() { @Test @DisplayName("검색 조건 있는 스터디 검색 - 페이징 테스트") - void 검색_조건_있는_스터디_검색_페이징(){ + void 검색_조건_있는_스터디_검색_페이징() { //given List studies = List.of(study1, study2); StudySearchRequestDTO request = getStudySearchRequestDTO(); @@ -361,7 +365,7 @@ void getMyPageStudyCount() { // when StudyPreviewDTO result = studyQueryService.findStudiesByConditions( - PageRequest.of(0, 10), request, StudySortBy.ALL); + PageRequest.of(0, 10), request, StudySortBy.ALL); // then assertEquals(10, result.getSize()); @@ -392,9 +396,11 @@ void getMyPageStudyCount() { void findRecommendStudies() { // given - // Mock the memberThemeRepository to return a list of MemberTheme - when(memberThemeRepository.findAllByMemberId(member.getId())).thenReturn(List.of(memberTheme1, memberTheme2)); - when(preferredRegionRepository.findAllByMemberId(member.getId())).thenReturn(List.of(preferredRegion1, preferredRegion2)); + // Mock the preferredThemeRepository to return a list of MemberTheme + when(preferredThemeRepository.findAllByMemberId(member.getId())).thenReturn(List.of(preferredTheme1, + preferredTheme2)); + when(preferredRegionRepository.findAllByMemberId(member.getId())).thenReturn( + List.of(preferredRegion1, preferredRegion2)); when(studyThemeRepository.findAllByTheme(theme1)).thenReturn(List.of(studyTheme1)); when(studyThemeRepository.findAllByTheme(theme2)).thenReturn(List.of(studyTheme2)); @@ -414,7 +420,7 @@ void findRecommendStudies() { // then assertNotNull(result); assertEquals(2, result.getSize()); // Assuming StudyPreviewDTO has a getStudies method - verify(memberThemeRepository).findAllByMemberId(member.getId()); + verify(preferredThemeRepository).findAllByMemberId(member.getId()); verify(studyThemeRepository, times(1)).findAllByTheme(theme1); verify(studyThemeRepository, times(1)).findAllByTheme(theme2); verify(studyRepository).findByStudyThemeAndNotInIds(anyList(), anyList()); @@ -426,8 +432,10 @@ void findRecommendStudies() { void findRecommendStudiesOnFail() { // given - when(memberThemeRepository.findAllByMemberId(member.getId())).thenReturn(List.of(memberTheme1, memberTheme2)); - when(preferredRegionRepository.findAllByMemberId(member.getId())).thenReturn(List.of(preferredRegion1, preferredRegion2)); + when(preferredThemeRepository.findAllByMemberId(member.getId())).thenReturn(List.of(preferredTheme1, + preferredTheme2)); + when(preferredRegionRepository.findAllByMemberId(member.getId())).thenReturn( + List.of(preferredRegion1, preferredRegion2)); when(studyThemeRepository.findAllByTheme(theme1)).thenReturn(List.of(studyTheme1)); when(studyThemeRepository.findAllByTheme(theme2)).thenReturn(List.of(studyTheme2)); @@ -445,7 +453,7 @@ void findRecommendStudiesOnFail() { assertThrows(StudyHandler.class, () -> { studyQueryService.findRecommendStudies(member.getId()); }); - verify(memberThemeRepository).findAllByMemberId(member.getId()); + verify(preferredThemeRepository).findAllByMemberId(member.getId()); verify(studyThemeRepository, times(1)).findAllByTheme(theme1); verify(studyThemeRepository, times(1)).findAllByTheme(theme2); verify(studyRepository).findByStudyThemeAndNotInIds(anyList(), anyList()); @@ -455,8 +463,10 @@ void findRecommendStudiesOnFail() { @DisplayName("추천 스터디 조회 - 회원의 관심 테마에 해당하는 스터디가 없는 경우") void 추천_스터디_조회_시_회원의_관심_테마에_해당하는_스터디가_없는_경우() { // given - when(memberThemeRepository.findAllByMemberId(member.getId())).thenReturn(List.of(memberTheme1, memberTheme2)); - when(preferredRegionRepository.findAllByMemberId(member.getId())).thenReturn(List.of(preferredRegion1, preferredRegion2)); + when(preferredThemeRepository.findAllByMemberId(member.getId())).thenReturn(List.of(preferredTheme1, + preferredTheme2)); + when(preferredRegionRepository.findAllByMemberId(member.getId())).thenReturn( + List.of(preferredRegion1, preferredRegion2)); when(studyThemeRepository.findAllByTheme(theme1)).thenReturn(List.of()); when(studyThemeRepository.findAllByTheme(theme2)).thenReturn(List.of()); @@ -469,7 +479,7 @@ void findRecommendStudiesOnFail() { studyQueryService.findRecommendStudies(member.getId()); }); - verify(memberThemeRepository).findAllByMemberId(member.getId()); + verify(preferredThemeRepository).findAllByMemberId(member.getId()); verify(studyThemeRepository, times(1)).findAllByTheme(theme1); verify(studyThemeRepository, times(1)).findAllByTheme(theme2); } @@ -478,8 +488,10 @@ void findRecommendStudiesOnFail() { @DisplayName("추천 스터디 조회 - 회원의 관심 지역에 해당하는 스터디가 없는 경우") void 추천_스터디_조회_시_회원의_관심_지역에_해당하는_스터디가_없는_경우() { // given - when(memberThemeRepository.findAllByMemberId(member.getId())).thenReturn(List.of(memberTheme1, memberTheme2)); - when(preferredRegionRepository.findAllByMemberId(member.getId())).thenReturn(List.of(preferredRegion1, preferredRegion2)); + when(preferredThemeRepository.findAllByMemberId(member.getId())).thenReturn(List.of(preferredTheme1, + preferredTheme2)); + when(preferredRegionRepository.findAllByMemberId(member.getId())).thenReturn( + List.of(preferredRegion1, preferredRegion2)); when(studyThemeRepository.findAllByTheme(theme1)).thenReturn(List.of(studyTheme1)); when(studyThemeRepository.findAllByTheme(theme2)).thenReturn(List.of(studyTheme2)); @@ -492,7 +504,7 @@ void findRecommendStudiesOnFail() { studyQueryService.findRecommendStudies(member.getId()); }); - verify(memberThemeRepository).findAllByMemberId(member.getId()); + verify(preferredThemeRepository).findAllByMemberId(member.getId()); verify(studyThemeRepository, times(1)).findAllByTheme(theme1); verify(studyThemeRepository, times(1)).findAllByTheme(theme2); } @@ -520,8 +532,8 @@ void findRecommendStudiesOnNoInterest() { Member member = getMember(); Long memberId = member.getId(); - // Mock the memberThemeRepository to return an empty list - when(memberThemeRepository.findAllByMemberId(memberId)).thenReturn(List.of()); + // Mock the preferredThemeRepository to return an empty list + when(preferredThemeRepository.findAllByMemberId(memberId)).thenReturn(List.of()); // when & then assertThrows(MemberHandler.class, () -> { @@ -549,10 +561,10 @@ void findRecommendStudiesOnNoRegion() { /* -------------------------------------------------------- 관심 Best 스터디 조회 ------------------------------------------------------------------------*/ @Test @DisplayName("관심 Best 스터디 조회 - 성공") - void 관심_BEST_스터디_조회_성공(){ + void 관심_BEST_스터디_조회_성공() { // given when(studyRepository.findAllStudyByConditions(any(), any(), any())) - .thenReturn(List.of(study1, study2)); + .thenReturn(List.of(study1, study2)); // when StudyPreviewDTO result = studyQueryService.findInterestedStudies(member.getId()); @@ -565,7 +577,7 @@ void findRecommendStudiesOnNoRegion() { @Test @DisplayName("관심 Best 스터디 조회 - 추천 스터디가 조회되지 않는 경우 ") - void 관심_BEST_스터디_조회_시_추천_스터디가_없는_경우(){ + void 관심_BEST_스터디_조회_시_추천_스터디가_없는_경우() { // given when(studyRepository.findAllStudyByConditions(any(), any(), any())) .thenReturn(List.of()); @@ -586,21 +598,21 @@ void findInterestStudiesByConditionsAllOnFail() { Map searchConditions = getStringObjectMap(); - when(memberThemeRepository.findAllByMemberId(member.getId())) - .thenReturn(List.of(memberTheme1, memberTheme2)); + when(preferredThemeRepository.findAllByMemberId(member.getId())) + .thenReturn(List.of(preferredTheme1, preferredTheme2)); when(studyThemeRepository.findAllByTheme(theme1)) - .thenReturn(List.of(studyTheme1)); + .thenReturn(List.of(studyTheme1)); when(studyThemeRepository.findAllByTheme(theme2)) - .thenReturn(List.of(studyTheme2)); + .thenReturn(List.of(studyTheme2)); when(studyRepository.countStudyByConditionsAndThemeTypesAndNotInIds( - searchConditions, List.of(studyTheme1, studyTheme2), sortBy, studyIds)) - .thenReturn(2L); + searchConditions, List.of(studyTheme1, studyTheme2), sortBy, studyIds)) + .thenReturn(2L); when(memberRepository.existsById(member.getId())).thenReturn(true); when(studyRepository.findStudyByConditionsAndThemeTypesAndNotInIds( - searchConditions, sortBy, pageable, List.of(studyTheme1, studyTheme2), studyIds)) - .thenReturn(List.of()); + searchConditions, sortBy, pageable, List.of(studyTheme1, studyTheme2), studyIds)) + .thenReturn(List.of()); // when & then assertThrows(StudyHandler.class, () -> { @@ -608,7 +620,7 @@ void findInterestStudiesByConditionsAllOnFail() { }); // then - verify(memberThemeRepository).findAllByMemberId(member.getId()); + verify(preferredThemeRepository).findAllByMemberId(member.getId()); verify(studyThemeRepository, times(1)).findAllByTheme(theme1); verify(studyThemeRepository, times(1)).findAllByTheme(theme2); } @@ -619,8 +631,8 @@ void findInterestStudiesByConditionsAllOnFail() { // given StudySortBy sortBy = StudySortBy.ALL; - when(memberThemeRepository.findAllByMemberId(member.getId())) - .thenReturn(List.of(memberTheme1, memberTheme2)); + when(preferredThemeRepository.findAllByMemberId(member.getId())) + .thenReturn(List.of(preferredTheme1, preferredTheme2)); when(studyThemeRepository.findAllByTheme(theme1)) .thenReturn(List.of()); when(studyThemeRepository.findAllByTheme(theme2)) @@ -632,7 +644,7 @@ void findInterestStudiesByConditionsAllOnFail() { }); // then - verify(memberThemeRepository).findAllByMemberId(member.getId()); + verify(preferredThemeRepository).findAllByMemberId(member.getId()); verify(studyThemeRepository, times(1)).findAllByTheme(theme1); verify(studyThemeRepository, times(1)).findAllByTheme(theme2); } @@ -649,54 +661,56 @@ void findInterestStudiesByConditionsAll() { // Mock conditions Map searchConditions = getStringObjectMap(); - when(memberThemeRepository.findAllByMemberId(member.getId())) - .thenReturn(List.of(memberTheme1, memberTheme2)); + when(preferredThemeRepository.findAllByMemberId(member.getId())) + .thenReturn(List.of(preferredTheme1, preferredTheme2)); when(studyThemeRepository.findAllByTheme(theme1)) - .thenReturn(List.of(studyTheme1)); + .thenReturn(List.of(studyTheme1)); when(studyThemeRepository.findAllByTheme(theme2)) - .thenReturn(List.of(studyTheme2)); + .thenReturn(List.of(studyTheme2)); when(studyRepository.countStudyByConditionsAndThemeTypesAndNotInIds( - searchConditions, List.of(studyTheme1, studyTheme2), sortBy, studyIds)) - .thenReturn(2L); + searchConditions, List.of(studyTheme1, studyTheme2), sortBy, studyIds)) + .thenReturn(2L); when(studyRepository.findStudyByConditionsAndThemeTypesAndNotInIds( - searchConditions, sortBy, pageable, List.of(studyTheme1, studyTheme2), studyIds)) - .thenReturn(List.of(study1, study2)); + searchConditions, sortBy, pageable, List.of(studyTheme1, studyTheme2), studyIds)) + .thenReturn(List.of(study1, study2)); when(memberRepository.existsById(member.getId())).thenReturn(true); // when - StudyPreviewDTO result = studyQueryService.findInterestStudiesByConditionsAll(pageable, member.getId(), request, sortBy); + StudyPreviewDTO result = studyQueryService.findInterestStudiesByConditionsAll(pageable, member.getId(), request, + sortBy); // then assertNotNull(result); assertEquals(2, result.getTotalElements()); // Assuming StudyPreviewDTO has a getSize method - verify(memberThemeRepository).findAllByMemberId(member.getId()); + verify(preferredThemeRepository).findAllByMemberId(member.getId()); verify(studyThemeRepository, times(1)).findAllByTheme(theme1); verify(studyThemeRepository, times(1)).findAllByTheme(theme2); - verify(studyRepository).countStudyByConditionsAndThemeTypesAndNotInIds(searchConditions, List.of(studyTheme1, studyTheme2), sortBy, studyIds); - verify(studyRepository).findStudyByConditionsAndThemeTypesAndNotInIds(searchConditions, sortBy, pageable, List.of(studyTheme1, studyTheme2), studyIds); + verify(studyRepository).countStudyByConditionsAndThemeTypesAndNotInIds(searchConditions, + List.of(studyTheme1, studyTheme2), sortBy, studyIds); + verify(studyRepository).findStudyByConditionsAndThemeTypesAndNotInIds(searchConditions, sortBy, pageable, + List.of(studyTheme1, studyTheme2), studyIds); } @Test @DisplayName("내 전체 관심사 스터디 조회 - 페이징 테스트") - void shouldReturnPagedStudies(){ + void shouldReturnPagedStudies() { //given List studies = List.of(study1, study2); - when(memberThemeRepository.findAllByMemberId(any())).thenReturn(List.of(memberTheme1, memberTheme2)); + when(preferredThemeRepository.findAllByMemberId(any())).thenReturn(List.of(preferredTheme1, preferredTheme2)); when(studyRepository.findStudyByConditionsAndThemeTypesAndNotInIds(any(), any(), any(), any(), any())) - .thenReturn(studies); + .thenReturn(studies); when(studyRepository.countStudyByConditionsAndThemeTypesAndNotInIds(any(), any(), any(), any())) - .thenReturn(2L); + .thenReturn(2L); when(memberRepository.existsById(any())).thenReturn(true); - // when StudyPreviewDTO result = studyQueryService.findInterestStudiesByConditionsAll( - PageRequest.of(0, 10), 1L, getStudySearchRequestDTO(), StudySortBy.ALL); + PageRequest.of(0, 10), 1L, getStudySearchRequestDTO(), StudySortBy.ALL); // then assertEquals(10, result.getSize()); @@ -707,22 +721,22 @@ void shouldReturnPagedStudies(){ @Test @DisplayName("내 전체 관심사 스터디 조회 - 검색 조건에 따른 스터디 필터링 테스트") - void shouldFilterStudiesBasedOnSearchConditions(){ + void shouldFilterStudiesBasedOnSearchConditions() { // given - when(memberRepository.existsById(member.getId())).thenReturn(true); - when(memberThemeRepository.findAllByMemberId(member.getId())).thenReturn(List.of(memberTheme1, memberTheme2)); + when(preferredThemeRepository.findAllByMemberId(member.getId())).thenReturn(List.of(preferredTheme1, + preferredTheme2)); when(studyThemeRepository.findAllByTheme(any())).thenReturn(List.of(studyTheme1, studyTheme2)); when(studyRepository.countStudyByConditionsAndThemeTypesAndNotInIds(any(), any(), any(), any())) - .thenReturn(1L); + .thenReturn(1L); when(studyRepository.findStudyByConditionsAndThemeTypesAndNotInIds(any(), any(), any(), any(), any())) - .thenReturn(List.of(study1)); + .thenReturn(List.of(study1)); // when // 검색 조건이 안맞는 경우, 검색 조건에 맞는 스터디가 조회 되면 안됨. StudyPreviewDTO result = studyQueryService.findInterestStudiesByConditionsAll( - pageable, member.getId(), getStudySearchRequestDTO(), StudySortBy.ALL); + pageable, member.getId(), getStudySearchRequestDTO(), StudySortBy.ALL); // then assertEquals(1, result.getTotalElements()); @@ -732,22 +746,23 @@ void shouldFilterStudiesBasedOnSearchConditions(){ @Test @DisplayName("내 전체 관심사 스터디 조회 - 정렬 조건에 따른 스터디 필터링 테스트(조회수 순)") - void shouldFilterStudiesBasedOnSortConditionsByHit(){ + void shouldFilterStudiesBasedOnSortConditionsByHit() { // given StudySortBy sortBy = StudySortBy.HIT; when(memberRepository.existsById(member.getId())).thenReturn(true); - when(memberThemeRepository.findAllByMemberId(member.getId())).thenReturn(List.of(memberTheme1, memberTheme2)); + when(preferredThemeRepository.findAllByMemberId(member.getId())).thenReturn(List.of(preferredTheme1, + preferredTheme2)); when(studyThemeRepository.findAllByTheme(any())).thenReturn(List.of(studyTheme1, studyTheme2)); when(studyRepository.countStudyByConditionsAndThemeTypesAndNotInIds(any(), any(), any(), any())) - .thenReturn(2L); + .thenReturn(2L); when(studyRepository.findStudyByConditionsAndThemeTypesAndNotInIds(any(), any(), any(), any(), any())) - .thenReturn(List.of(study1, study2)); + .thenReturn(List.of(study1, study2)); // when StudyPreviewDTO result = studyQueryService.findInterestStudiesByConditionsAll( - pageable, member.getId(), getStudySearchRequestDTO(), sortBy); + pageable, member.getId(), getStudySearchRequestDTO(), sortBy); // then assertEquals(2, result.getTotalElements()); @@ -758,21 +773,22 @@ void shouldFilterStudiesBasedOnSortConditionsByHit(){ @Test @DisplayName("내 전체 관심사 스터디 조회 - 정렬 조건에 따른 스터디 필터링 테스트(좋아요 순)") - void shouldFilterStudiesBasedOnSortConditionsByLiked(){ + void shouldFilterStudiesBasedOnSortConditionsByLiked() { // given StudySortBy sortBy = StudySortBy.LIKED; when(memberRepository.existsById(member.getId())).thenReturn(true); - when(memberThemeRepository.findAllByMemberId(member.getId())).thenReturn(List.of(memberTheme1, memberTheme2)); + when(preferredThemeRepository.findAllByMemberId(member.getId())).thenReturn(List.of(preferredTheme1, + preferredTheme2)); when(studyThemeRepository.findAllByTheme(any())).thenReturn(List.of(studyTheme1, studyTheme2)); when(studyRepository.countStudyByConditionsAndThemeTypesAndNotInIds(any(), any(), any(), any())) - .thenReturn(2L); + .thenReturn(2L); when(studyRepository.findStudyByConditionsAndThemeTypesAndNotInIds(any(), any(), any(), any(), any())) - .thenReturn(List.of(study2, study1)); + .thenReturn(List.of(study2, study1)); // when StudyPreviewDTO result = studyQueryService.findInterestStudiesByConditionsAll( - pageable, member.getId(), getStudySearchRequestDTO(), sortBy); + pageable, member.getId(), getStudySearchRequestDTO(), sortBy); // then assertEquals(2, result.getTotalElements()); @@ -789,7 +805,7 @@ void findInterestStudiesByConditionsAllOnNoInterest() { StudySortBy sortBy = StudySortBy.ALL; when(memberRepository.existsById(member.getId())).thenReturn(true); - when(memberThemeRepository.findAllByMemberId(member.getId())).thenReturn(List.of()); + when(preferredThemeRepository.findAllByMemberId(member.getId())).thenReturn(List.of()); // when & then assertThrows(MemberHandler.class, () -> { @@ -812,32 +828,35 @@ void findInterestStudiesByConditionsSpecific() { // Mock conditions Map searchConditions = getStringObjectMap(); - when(memberThemeRepository.findAllByMemberId(member.getId())) - .thenReturn(List.of(memberTheme1)); + when(preferredThemeRepository.findAllByMemberId(member.getId())) + .thenReturn(List.of(preferredTheme1)); when(studyThemeRepository.findAllByTheme(theme1)) - .thenReturn(List.of(studyTheme1)); + .thenReturn(List.of(studyTheme1)); // Only studyTheme1 should match when(studyRepository.countStudyByConditionsAndThemeTypesAndNotInIds( - searchConditions, List.of(studyTheme1), sortBy, studyIds)) - .thenReturn(1L); + searchConditions, List.of(studyTheme1), sortBy, studyIds)) + .thenReturn(1L); when(memberRepository.existsById(member.getId())).thenReturn(true); // Adjusting the mock to match the specific test data when(studyRepository.findStudyByConditionsAndThemeTypesAndNotInIds( - searchConditions, sortBy, pageable, List.of(studyTheme1), studyIds)) - .thenReturn(List.of(study1)); + searchConditions, sortBy, pageable, List.of(studyTheme1), studyIds)) + .thenReturn(List.of(study1)); // when - StudyPreviewDTO result = studyQueryService.findInterestStudiesByConditionsSpecific(pageable, member.getId(), request, themeType, sortBy); + StudyPreviewDTO result = studyQueryService.findInterestStudiesByConditionsSpecific(pageable, member.getId(), + request, themeType, sortBy); // then assertNotNull(result); assertEquals(1, result.getTotalElements()); // Verify the count matches expected result - verify(memberThemeRepository).findAllByMemberId(member.getId()); + verify(preferredThemeRepository).findAllByMemberId(member.getId()); verify(studyThemeRepository).findAllByTheme(theme1); // Ensure the correct theme is queried - verify(studyRepository).countStudyByConditionsAndThemeTypesAndNotInIds(searchConditions, List.of(studyTheme1), sortBy, studyIds); - verify(studyRepository).findStudyByConditionsAndThemeTypesAndNotInIds(searchConditions, sortBy, pageable, List.of(studyTheme1),studyIds); + verify(studyRepository).countStudyByConditionsAndThemeTypesAndNotInIds(searchConditions, List.of(studyTheme1), + sortBy, studyIds); + verify(studyRepository).findStudyByConditionsAndThemeTypesAndNotInIds(searchConditions, sortBy, pageable, + List.of(studyTheme1), studyIds); } @Test @@ -846,8 +865,8 @@ void findInterestStudiesByConditionsSpecific() { // given StudySortBy sortBy = StudySortBy.ALL; - when(memberThemeRepository.findAllByMemberId(member.getId())) - .thenReturn(List.of(memberTheme1, memberTheme2)); + when(preferredThemeRepository.findAllByMemberId(member.getId())) + .thenReturn(List.of(preferredTheme1, preferredTheme2)); when(studyThemeRepository.findAllByTheme(theme1)) .thenReturn(List.of()); when(studyThemeRepository.findAllByTheme(theme2)) @@ -855,11 +874,12 @@ void findInterestStudiesByConditionsSpecific() { // when & then assertThrows(StudyHandler.class, () -> { - studyQueryService.findInterestStudiesByConditionsSpecific(pageable, member.getId(), request, ThemeType.어학, sortBy); + studyQueryService.findInterestStudiesByConditionsSpecific(pageable, member.getId(), request, ThemeType.어학, + sortBy); }); // then - verify(memberThemeRepository).findAllByMemberId(member.getId()); + verify(preferredThemeRepository).findAllByMemberId(member.getId()); verify(studyThemeRepository, times(2)).findAllByTheme(any()); } @@ -876,51 +896,52 @@ void findInterestStudiesByConditionsSpecificOnFail() { // Mock conditions Map searchConditions = getStringObjectMap(); - when(memberThemeRepository.findAllByMemberId(member.getId())) - .thenReturn(List.of(memberTheme1)); + when(preferredThemeRepository.findAllByMemberId(member.getId())) + .thenReturn(List.of(preferredTheme1)); when(studyThemeRepository.findAllByTheme(theme1)) - .thenReturn(List.of(studyTheme1)); + .thenReturn(List.of(studyTheme1)); // Only studyTheme1 should match when(studyRepository.countStudyByConditionsAndThemeTypesAndNotInIds( - searchConditions, List.of(studyTheme1), sortBy, studyIds)) - .thenReturn(0L); + searchConditions, List.of(studyTheme1), sortBy, studyIds)) + .thenReturn(0L); // Adjusting the mock to match the specific test data when(studyRepository.findStudyByConditionsAndThemeTypesAndNotInIds( - searchConditions, sortBy, pageable, List.of(studyTheme1), studyIds)) - .thenReturn(List.of()); + searchConditions, sortBy, pageable, List.of(studyTheme1), studyIds)) + .thenReturn(List.of()); when(memberRepository.existsById(member.getId())).thenReturn(true); // when & then assertThrows(StudyHandler.class, () -> { - studyQueryService.findInterestStudiesByConditionsSpecific(pageable, member.getId(), request, themeType, sortBy); + studyQueryService.findInterestStudiesByConditionsSpecific(pageable, member.getId(), request, themeType, + sortBy); }); - verify(memberThemeRepository).findAllByMemberId(member.getId()); + verify(preferredThemeRepository).findAllByMemberId(member.getId()); verify(studyThemeRepository).findAllByTheme(theme1); // Ensure the correct theme is queried - verify(studyRepository).countStudyByConditionsAndThemeTypesAndNotInIds(searchConditions, List.of(studyTheme1), sortBy, studyIds); - verify(studyRepository).findStudyByConditionsAndThemeTypesAndNotInIds(searchConditions, sortBy, pageable, List.of(studyTheme1),studyIds); + verify(studyRepository).countStudyByConditionsAndThemeTypesAndNotInIds(searchConditions, List.of(studyTheme1), + sortBy, studyIds); + verify(studyRepository).findStudyByConditionsAndThemeTypesAndNotInIds(searchConditions, sortBy, pageable, + List.of(studyTheme1), studyIds); } @Test @DisplayName("내 특정 관심사 스터디 조회 - 페이징 테스트") - void shouldReturnPagedStudiesInSpecificTheme(){ + void shouldReturnPagedStudiesInSpecificTheme() { //given List studies = List.of(study1, study3); - - when(memberThemeRepository.findAllByMemberId(any())).thenReturn(List.of(memberTheme1)); + when(preferredThemeRepository.findAllByMemberId(any())).thenReturn(List.of(preferredTheme1)); when(studyRepository.findStudyByConditionsAndThemeTypesAndNotInIds(any(), any(), any(), any(), any())) - .thenReturn(studies); + .thenReturn(studies); when(studyRepository.countStudyByConditionsAndThemeTypesAndNotInIds(any(), any(), any(), any())) - .thenReturn(2L); + .thenReturn(2L); when(memberRepository.existsById(any())).thenReturn(true); - // when StudyPreviewDTO result = studyQueryService.findInterestStudiesByConditionsSpecific( - PageRequest.of(0, 10), 1L, getStudySearchRequestDTO(), ThemeType.어학, StudySortBy.ALL); + PageRequest.of(0, 10), 1L, getStudySearchRequestDTO(), ThemeType.어학, StudySortBy.ALL); // then assertEquals(10, result.getSize()); @@ -931,21 +952,21 @@ void shouldReturnPagedStudiesInSpecificTheme(){ @Test @DisplayName("내 특정 관심사 스터디 조회 - 검색 조건에 따른 스터디 필터링 테스트") - void shouldFilterStudiesBasedOnSearchConditionsInSpecificTheme(){ + void shouldFilterStudiesBasedOnSearchConditionsInSpecificTheme() { // given when(memberRepository.existsById(member.getId())).thenReturn(true); - when(memberThemeRepository.findAllByMemberId(member.getId())).thenReturn(List.of(memberTheme1)); + when(preferredThemeRepository.findAllByMemberId(member.getId())).thenReturn(List.of(preferredTheme1)); when(studyThemeRepository.findAllByTheme(any())).thenReturn(List.of(studyTheme1)); when(studyRepository.countStudyByConditionsAndThemeTypesAndNotInIds(any(), any(), any(), any())) - .thenReturn(2L); + .thenReturn(2L); when(studyRepository.findStudyByConditionsAndThemeTypesAndNotInIds(any(), any(), any(), any(), any())) - .thenReturn(List.of(study1, study3)); + .thenReturn(List.of(study1, study3)); // when // 검색 조건이 안맞는 경우, 검색 조건에 맞는 스터디가 조회 되면 안됨. StudyPreviewDTO result = studyQueryService.findInterestStudiesByConditionsSpecific( - pageable, member.getId(), getStudySearchRequestDTO(), ThemeType.어학, StudySortBy.ALL); + pageable, member.getId(), getStudySearchRequestDTO(), ThemeType.어학, StudySortBy.ALL); // then assertEquals(2, result.getTotalElements()); @@ -956,22 +977,22 @@ void shouldFilterStudiesBasedOnSearchConditionsInSpecificTheme(){ @Test @DisplayName("내 특정 관심사 스터디 조회 - 정렬 조건에 따른 스터디 필터링 테스트(조회수 순)") - void shouldFilterStudiesInSpecificThemeBasedOnSortConditionsByHit(){ + void shouldFilterStudiesInSpecificThemeBasedOnSortConditionsByHit() { // given StudySortBy sortBy = StudySortBy.HIT; - when(memberRepository.existsById(member.getId())).thenReturn(true); - when(memberThemeRepository.findAllByMemberId(member.getId())).thenReturn(List.of(memberTheme1, memberTheme2)); + when(preferredThemeRepository.findAllByMemberId(member.getId())).thenReturn(List.of(preferredTheme1, + preferredTheme2)); when(studyThemeRepository.findAllByTheme(any())).thenReturn(List.of(studyTheme1, studyTheme2)); when(studyRepository.countStudyByConditionsAndThemeTypesAndNotInIds(any(), any(), any(), any())) - .thenReturn(3L); + .thenReturn(3L); when(studyRepository.findStudyByConditionsAndThemeTypesAndNotInIds(any(), any(), any(), any(), any())) - .thenReturn(List.of(study1, study3, study2)); + .thenReturn(List.of(study1, study3, study2)); // when StudyPreviewDTO result = studyQueryService.findInterestStudiesByConditionsSpecific( - pageable, member.getId(), getStudySearchRequestDTO(), ThemeType.어학, sortBy); + pageable, member.getId(), getStudySearchRequestDTO(), ThemeType.어학, sortBy); // then assertEquals(3, result.getTotalElements()); @@ -983,22 +1004,22 @@ void shouldFilterStudiesInSpecificThemeBasedOnSortConditionsByHit(){ @Test @DisplayName("내 특정 관심사 스터디 조회 - 정렬 조건에 따른 스터디 필터링 테스트(좋아요 순)") - void shouldFilterStudiesInSpecificThemeBasedOnSortConditionsByLiked(){ + void shouldFilterStudiesInSpecificThemeBasedOnSortConditionsByLiked() { // given StudySortBy sortBy = StudySortBy.LIKED; - when(memberRepository.existsById(member.getId())).thenReturn(true); - when(memberThemeRepository.findAllByMemberId(member.getId())).thenReturn(List.of(memberTheme1, memberTheme2)); + when(preferredThemeRepository.findAllByMemberId(member.getId())).thenReturn(List.of(preferredTheme1, + preferredTheme2)); when(studyThemeRepository.findAllByTheme(any())).thenReturn(List.of(studyTheme1, studyTheme2)); when(studyRepository.countStudyByConditionsAndThemeTypesAndNotInIds(any(), any(), any(), any())) - .thenReturn(3L); + .thenReturn(3L); when(studyRepository.findStudyByConditionsAndThemeTypesAndNotInIds(any(), any(), any(), any(), any())) - .thenReturn(List.of(study2, study1, study3)); + .thenReturn(List.of(study2, study1, study3)); // when StudyPreviewDTO result = studyQueryService.findInterestStudiesByConditionsAll( - pageable, member.getId(), getStudySearchRequestDTO(), sortBy); + pageable, member.getId(), getStudySearchRequestDTO(), sortBy); // then assertEquals(3, result.getTotalElements()); @@ -1006,8 +1027,6 @@ void shouldFilterStudiesInSpecificThemeBasedOnSortConditionsByLiked(){ } - - // 만약 회원의 관심사에 해당하는 테마가 없다면 예외 처리 -> 입력한 테마와 회원이 등록한 테마가 다르면 에러 발생 @Test @DisplayName("내 특정 관심사 스터디 조회 - 회원의 관심사에 해당하는 테마가 없는 경우") @@ -1019,11 +1038,12 @@ void noThemeInMemberInterest() { StudySearchRequestDTO request = getStudySearchRequestDTO(); when(memberRepository.existsById(member.getId())).thenReturn(true); - when(memberThemeRepository.findAllByMemberId(member.getId())).thenReturn(List.of()); + when(preferredThemeRepository.findAllByMemberId(member.getId())).thenReturn(List.of()); // when & then assertThrows(MemberHandler.class, () -> { - studyQueryService.findInterestStudiesByConditionsSpecific(pageable, member.getId(), request, themeType, sortBy); + studyQueryService.findInterestStudiesByConditionsSpecific(pageable, member.getId(), request, themeType, + sortBy); }); } @@ -1040,24 +1060,25 @@ void findInterestRegionStudiesByConditionsAll() { Map searchConditions = getStringObjectMapWithThemeType(); when(preferredRegionRepository.findAllByMemberId(member.getId())) - .thenReturn(List.of(preferredRegion1, preferredRegion2)); + .thenReturn(List.of(preferredRegion1, preferredRegion2)); when(studyRegionRepository.findAllByRegion(region1)) - .thenReturn(List.of(studyRegion1)); + .thenReturn(List.of(studyRegion1)); when(studyRegionRepository.findAllByRegion(region2)) - .thenReturn(List.of(studyRegion2)); + .thenReturn(List.of(studyRegion2)); when(studyRepository.countStudyByConditionsAndRegionStudiesAndNotInIds( - searchConditions, List.of(studyRegion1, studyRegion2), sortBy, studyIds)) - .thenReturn(2L); + searchConditions, List.of(studyRegion1, studyRegion2), sortBy, studyIds)) + .thenReturn(2L); when(studyRepository.findStudyByConditionsAndRegionStudiesAndNotInIds( - searchConditions, sortBy, pageable, List.of(studyRegion1, studyRegion2), studyIds)) - .thenReturn(List.of(study1, study2)); + searchConditions, sortBy, pageable, List.of(studyRegion1, studyRegion2), studyIds)) + .thenReturn(List.of(study1, study2)); when(memberRepository.existsById(member.getId())).thenReturn(true); // when - StudyPreviewDTO result = studyQueryService.findInterestRegionStudiesByConditionsAll(pageable, member.getId(), requestWithTheme, sortBy); + StudyPreviewDTO result = studyQueryService.findInterestRegionStudiesByConditionsAll(pageable, member.getId(), + requestWithTheme, sortBy); // then assertNotNull(result); @@ -1065,8 +1086,10 @@ void findInterestRegionStudiesByConditionsAll() { verify(preferredRegionRepository).findAllByMemberId(member.getId()); verify(studyRegionRepository, times(1)).findAllByRegion(region1); verify(studyRegionRepository, times(1)).findAllByRegion(region2); - verify(studyRepository).countStudyByConditionsAndRegionStudiesAndNotInIds(searchConditions, List.of(studyRegion1, studyRegion2), sortBy, studyIds); - verify(studyRepository).findStudyByConditionsAndRegionStudiesAndNotInIds(searchConditions, sortBy, pageable, List.of(studyRegion1, studyRegion2), studyIds); + verify(studyRepository).countStudyByConditionsAndRegionStudiesAndNotInIds(searchConditions, + List.of(studyRegion1, studyRegion2), sortBy, studyIds); + verify(studyRepository).findStudyByConditionsAndRegionStudiesAndNotInIds(searchConditions, sortBy, pageable, + List.of(studyRegion1, studyRegion2), studyIds); } @Test @@ -1078,11 +1101,12 @@ void findInterestRegionStudiesByConditionsAll() { when(preferredRegionRepository.findAllByMemberId(member.getId())) .thenReturn(List.of(preferredRegion1, preferredRegion2)); - when(studyRegionRepository.findAllByRegion(any())).thenReturn(List.of( )); + when(studyRegionRepository.findAllByRegion(any())).thenReturn(List.of()); // when & then assertThrows(StudyHandler.class, () -> { - studyQueryService.findInterestRegionStudiesByConditionsAll(pageable, member.getId(), requestWithTheme, sortBy); + studyQueryService.findInterestRegionStudiesByConditionsAll(pageable, member.getId(), requestWithTheme, + sortBy); }); // then @@ -1102,20 +1126,21 @@ void findInterestRegionStudiesByConditionsAllOnFail() { Map searchConditions = getStringObjectMap(); when(preferredRegionRepository.findAllByMemberId(member.getId())) - .thenReturn(List.of(preferredRegion1, preferredRegion2)); + .thenReturn(List.of(preferredRegion1, preferredRegion2)); when(studyRegionRepository.findAllByRegion(any())).thenReturn(List.of(studyRegion1, studyRegion2)); when(studyRepository.countStudyByConditionsAndRegionStudiesAndNotInIds( - searchConditions, List.of(studyRegion1, studyRegion2), sortBy, studyIds)) - .thenReturn(2L); + searchConditions, List.of(studyRegion1, studyRegion2), sortBy, studyIds)) + .thenReturn(2L); when(memberRepository.existsById(member.getId())).thenReturn(true); when(studyRepository.findStudyByConditionsAndRegionStudiesAndNotInIds( - searchConditions, sortBy, pageable, List.of(studyRegion1, studyRegion2), studyIds)) - .thenReturn(List.of()); + searchConditions, sortBy, pageable, List.of(studyRegion1, studyRegion2), studyIds)) + .thenReturn(List.of()); // when & then assertThrows(StudyHandler.class, () -> { - studyQueryService.findInterestRegionStudiesByConditionsAll(pageable, member.getId(), requestWithTheme, sortBy); + studyQueryService.findInterestRegionStudiesByConditionsAll(pageable, member.getId(), requestWithTheme, + sortBy); }); // then @@ -1124,25 +1149,24 @@ void findInterestRegionStudiesByConditionsAllOnFail() { } - @Test @DisplayName("내 전체 관심 지역 스터디 조회 - 페이징 테스트") - void shouldReturnPagedStudiesByRegion(){ + void shouldReturnPagedStudiesByRegion() { //given List studies = List.of(study1, study2); - when(preferredRegionRepository.findAllByMemberId(any())).thenReturn(List.of(preferredRegion1, preferredRegion2)); + when(preferredRegionRepository.findAllByMemberId(any())).thenReturn( + List.of(preferredRegion1, preferredRegion2)); when(studyRegionRepository.findAllByRegion(any())).thenReturn(List.of(studyRegion1, studyRegion2)); when(studyRepository.findStudyByConditionsAndRegionStudiesAndNotInIds(any(), any(), any(), any(), any())) - .thenReturn(studies); + .thenReturn(studies); when(studyRepository.countStudyByConditionsAndRegionStudiesAndNotInIds(any(), any(), any(), any())) - .thenReturn(2L); + .thenReturn(2L); when(memberRepository.existsById(any())).thenReturn(true); - // when StudyPreviewDTO result = studyQueryService.findInterestRegionStudiesByConditionsAll( - PageRequest.of(0, 10), 1L, requestWithTheme, StudySortBy.ALL); + PageRequest.of(0, 10), 1L, requestWithTheme, StudySortBy.ALL); // then assertEquals(10, result.getSize()); @@ -1153,22 +1177,22 @@ void shouldReturnPagedStudiesByRegion(){ @Test @DisplayName("내 전체 관심 지역 스터디 조회 - 검색 조건에 따른 스터디 필터링 테스트") - void shouldFilterStudiesBasedOnSearchConditionsByRegion(){ + void shouldFilterStudiesBasedOnSearchConditionsByRegion() { // given - when(memberRepository.existsById(member.getId())).thenReturn(true); - when(preferredRegionRepository.findAllByMemberId(member.getId())).thenReturn(List.of(preferredRegion1, preferredRegion2)); + when(preferredRegionRepository.findAllByMemberId(member.getId())).thenReturn( + List.of(preferredRegion1, preferredRegion2)); when(studyRegionRepository.findAllByRegion(any())).thenReturn(List.of(studyRegion1, studyRegion2)); when(studyRepository.countStudyByConditionsAndRegionStudiesAndNotInIds(any(), any(), any(), any())) - .thenReturn(1L); + .thenReturn(1L); when(studyRepository.findStudyByConditionsAndRegionStudiesAndNotInIds(any(), any(), any(), any(), any())) - .thenReturn(List.of(study1)); + .thenReturn(List.of(study1)); // when // 검색 조건이 안맞는 경우, 검색 조건에 맞는 스터디가 조회 되면 안됨. StudyPreviewDTO result = studyQueryService.findInterestRegionStudiesByConditionsAll( - pageable, member.getId(), requestWithTheme, StudySortBy.ALL); + pageable, member.getId(), requestWithTheme, StudySortBy.ALL); // then assertEquals(1, result.getTotalElements()); @@ -1178,22 +1202,23 @@ void shouldFilterStudiesBasedOnSearchConditionsByRegion(){ @Test @DisplayName("내 전체 관심 지역 스터디 조회 - 정렬 조건에 따른 스터디 필터링 테스트(조회수 순)") - void shouldFilterRegionStudiesBasedOnSortConditionsByHit(){ + void shouldFilterRegionStudiesBasedOnSortConditionsByHit() { // given StudySortBy sortBy = StudySortBy.HIT; when(memberRepository.existsById(member.getId())).thenReturn(true); - when(preferredRegionRepository.findAllByMemberId(member.getId())).thenReturn(List.of(preferredRegion1, preferredRegion2)); + when(preferredRegionRepository.findAllByMemberId(member.getId())).thenReturn( + List.of(preferredRegion1, preferredRegion2)); when(studyRegionRepository.findAllByRegion(any())).thenReturn(List.of(studyRegion1, studyRegion2)); when(studyRepository.countStudyByConditionsAndRegionStudiesAndNotInIds(any(), any(), any(), any())) - .thenReturn(2L); + .thenReturn(2L); when(studyRepository.findStudyByConditionsAndRegionStudiesAndNotInIds(any(), any(), any(), any(), any())) - .thenReturn(List.of(study1, study2)); + .thenReturn(List.of(study1, study2)); // when StudyPreviewDTO result = studyQueryService.findInterestRegionStudiesByConditionsAll( - pageable, member.getId(), requestWithTheme, sortBy); + pageable, member.getId(), requestWithTheme, sortBy); // then assertEquals(2, result.getTotalElements()); @@ -1204,21 +1229,22 @@ void shouldFilterRegionStudiesBasedOnSortConditionsByHit(){ @Test @DisplayName("내 전체 관심 지역 스터디 조회 - 정렬 조건에 따른 스터디 필터링 테스트(좋아요 순)") - void shouldFilterRegionStudiesBasedOnSortConditionsByLiked(){ + void shouldFilterRegionStudiesBasedOnSortConditionsByLiked() { // given StudySortBy sortBy = StudySortBy.LIKED; when(memberRepository.existsById(member.getId())).thenReturn(true); - when(preferredRegionRepository.findAllByMemberId(member.getId())).thenReturn(List.of(preferredRegion1, preferredRegion2)); + when(preferredRegionRepository.findAllByMemberId(member.getId())).thenReturn( + List.of(preferredRegion1, preferredRegion2)); when(studyRegionRepository.findAllByRegion(any())).thenReturn(List.of(studyRegion1, studyRegion2)); when(studyRepository.countStudyByConditionsAndRegionStudiesAndNotInIds(any(), any(), any(), any())) - .thenReturn(2L); + .thenReturn(2L); when(studyRepository.findStudyByConditionsAndRegionStudiesAndNotInIds(any(), any(), any(), any(), any())) - .thenReturn(List.of(study2, study1)); + .thenReturn(List.of(study2, study1)); // when StudyPreviewDTO result = studyQueryService.findInterestRegionStudiesByConditionsAll( - pageable, member.getId(), requestWithTheme, sortBy); + pageable, member.getId(), requestWithTheme, sortBy); // then assertEquals(2, result.getTotalElements()); @@ -1239,7 +1265,8 @@ void findInterestRegionStudiesByConditionsAllOnNoInterest() { // when & then assertThrows(MemberHandler.class, () -> { - studyQueryService.findInterestRegionStudiesByConditionsAll(pageable, member.getId(), requestWithTheme, sortBy); + studyQueryService.findInterestRegionStudiesByConditionsAll(pageable, member.getId(), requestWithTheme, + sortBy); }); } @@ -1255,37 +1282,39 @@ void findInterestRegionStudiesByConditionsSpecific() { StudySortBy sortBy = StudySortBy.ALL; List studyIds = List.of(); - // Mock conditions Map searchConditions = getStringObjectMapWithThemeType(); when(preferredRegionRepository.findAllByMemberId(member.getId())) - .thenReturn(List.of(preferredRegion1)); + .thenReturn(List.of(preferredRegion1)); when(studyRegionRepository.findAllByRegion(region1)) - .thenReturn(List.of(studyRegion1)); + .thenReturn(List.of(studyRegion1)); // Only studyTheme1 should match when(studyRepository.countStudyByConditionsAndRegionStudiesAndNotInIds( - searchConditions, List.of(studyRegion1), sortBy, studyIds)) - .thenReturn(1L); + searchConditions, List.of(studyRegion1), sortBy, studyIds)) + .thenReturn(1L); // Adjusting the mock to match the specific test data when(studyRepository.findStudyByConditionsAndRegionStudiesAndNotInIds( - searchConditions, sortBy, pageable, List.of(studyRegion1), studyIds)) - .thenReturn(List.of(study1)); + searchConditions, sortBy, pageable, List.of(studyRegion1), studyIds)) + .thenReturn(List.of(study1)); when(memberRepository.existsById(member.getId())).thenReturn(true); // when - StudyPreviewDTO result = studyQueryService.findInterestRegionStudiesByConditionsSpecific(pageable, member.getId(), requestWithTheme, regionCode, sortBy); + StudyPreviewDTO result = studyQueryService.findInterestRegionStudiesByConditionsSpecific(pageable, + member.getId(), requestWithTheme, regionCode, sortBy); // then assertNotNull(result); assertEquals(1, result.getTotalElements()); // Verify the count matches expected result verify(preferredRegionRepository).findAllByMemberId(member.getId()); verify(studyRegionRepository).findAllByRegion(region1); // Ensure the correct theme is queried - verify(studyRepository).countStudyByConditionsAndRegionStudiesAndNotInIds(searchConditions, List.of(studyRegion1), sortBy, studyIds); - verify(studyRepository).findStudyByConditionsAndRegionStudiesAndNotInIds(searchConditions, sortBy, pageable, List.of(studyRegion1), studyIds); + verify(studyRepository).countStudyByConditionsAndRegionStudiesAndNotInIds(searchConditions, + List.of(studyRegion1), sortBy, studyIds); + verify(studyRepository).findStudyByConditionsAndRegionStudiesAndNotInIds(searchConditions, sortBy, pageable, + List.of(studyRegion1), studyIds); } @@ -1299,11 +1328,12 @@ void findInterestRegionStudiesByConditionsSpecific() { when(preferredRegionRepository.findAllByMemberId(member.getId())) .thenReturn(List.of(preferredRegion1, preferredRegion2)); - when(studyRegionRepository.findAllByRegion(any())).thenReturn(List.of( )); + when(studyRegionRepository.findAllByRegion(any())).thenReturn(List.of()); // when & then assertThrows(StudyHandler.class, () -> { - studyQueryService.findInterestRegionStudiesByConditionsSpecific(pageable, member.getId(), requestWithTheme, region1.getCode(), sortBy); + studyQueryService.findInterestRegionStudiesByConditionsSpecific(pageable, member.getId(), requestWithTheme, + region1.getCode(), sortBy); }); // then @@ -1324,49 +1354,51 @@ void findInterestRegionStudiesByConditionsSpecificOnFail() { Map searchConditions = getStringObjectMapWithThemeType(); when(preferredRegionRepository.findAllByMemberId(member.getId())) - .thenReturn(List.of(preferredRegion1)); + .thenReturn(List.of(preferredRegion1)); when(studyRegionRepository.findAllByRegion(region1)) - .thenReturn(List.of(studyRegion1)); + .thenReturn(List.of(studyRegion1)); when(studyRepository.countStudyByConditionsAndRegionStudiesAndNotInIds( - searchConditions, List.of(studyRegion1), sortBy, studyIds)) - .thenReturn(0L); + searchConditions, List.of(studyRegion1), sortBy, studyIds)) + .thenReturn(0L); when(studyRepository.findStudyByConditionsAndRegionStudiesAndNotInIds( - searchConditions, sortBy, pageable, List.of(studyRegion1), studyIds)) - .thenReturn(List.of()); + searchConditions, sortBy, pageable, List.of(studyRegion1), studyIds)) + .thenReturn(List.of()); when(memberRepository.existsById(member.getId())).thenReturn(true); // when & then assertThrows(StudyHandler.class, () -> { - studyQueryService.findInterestRegionStudiesByConditionsSpecific(pageable, member.getId(), requestWithTheme, regionCode, sortBy); + studyQueryService.findInterestRegionStudiesByConditionsSpecific(pageable, member.getId(), requestWithTheme, + regionCode, sortBy); }); verify(preferredRegionRepository).findAllByMemberId(member.getId()); verify(studyRegionRepository).findAllByRegion(region1); // Ensure the correct theme is queried - verify(studyRepository).countStudyByConditionsAndRegionStudiesAndNotInIds(searchConditions, List.of(studyRegion1), sortBy, studyIds); - verify(studyRepository).findStudyByConditionsAndRegionStudiesAndNotInIds(searchConditions, sortBy, pageable, List.of(studyRegion1),studyIds); + verify(studyRepository).countStudyByConditionsAndRegionStudiesAndNotInIds(searchConditions, + List.of(studyRegion1), sortBy, studyIds); + verify(studyRepository).findStudyByConditionsAndRegionStudiesAndNotInIds(searchConditions, sortBy, pageable, + List.of(studyRegion1), studyIds); } @Test @DisplayName("내 특정 관심 지역 스터디 조회 - 페이징 테스트") - void shouldReturnPagedStudiesInSpecificRegion(){ + void shouldReturnPagedStudiesInSpecificRegion() { //given List studies = List.of(study1, study3); - - when(preferredRegionRepository.findAllByMemberId(any())).thenReturn(List.of(preferredRegion1, preferredRegion2)); + when(preferredRegionRepository.findAllByMemberId(any())).thenReturn( + List.of(preferredRegion1, preferredRegion2)); when(studyRepository.findStudyByConditionsAndRegionStudiesAndNotInIds(any(), any(), any(), any(), any())) - .thenReturn(studies); + .thenReturn(studies); when(studyRegionRepository.findAllByRegion(any())).thenReturn(List.of(studyRegion1, studyRegion2)); when(studyRepository.countStudyByConditionsAndRegionStudiesAndNotInIds(any(), any(), any(), any())) - .thenReturn(2L); + .thenReturn(2L); when(memberRepository.existsById(any())).thenReturn(true); - // when StudyPreviewDTO result = studyQueryService.findInterestRegionStudiesByConditionsSpecific( - PageRequest.of(0, 10), 1L, requestWithTheme, region1.getCode(), StudySortBy.ALL); + PageRequest.of(0, 10), 1L, requestWithTheme, region1.getCode(), StudySortBy.ALL); // then assertEquals(10, result.getSize()); @@ -1377,21 +1409,21 @@ void shouldReturnPagedStudiesInSpecificRegion(){ @Test @DisplayName("내 특정 관심 지역 스터디 조회 - 검색 조건에 따른 스터디 필터링 테스트") - void shouldFilterStudiesBasedOnSearchConditionsInSpecificRegion(){ + void shouldFilterStudiesBasedOnSearchConditionsInSpecificRegion() { // given when(memberRepository.existsById(member.getId())).thenReturn(true); when(preferredRegionRepository.findAllByMemberId(member.getId())).thenReturn(List.of(preferredRegion1)); when(studyRegionRepository.findAllByRegion(any())).thenReturn(List.of(studyRegion1)); when(studyRepository.countStudyByConditionsAndRegionStudiesAndNotInIds(any(), any(), any(), any())) - .thenReturn(2L); + .thenReturn(2L); when(studyRepository.findStudyByConditionsAndRegionStudiesAndNotInIds(any(), any(), any(), any(), any())) - .thenReturn(List.of(study1, study3)); + .thenReturn(List.of(study1, study3)); // when // 검색 조건이 안맞는 경우, 검색 조건에 맞는 스터디가 조회 되면 안됨. StudyPreviewDTO result = studyQueryService.findInterestRegionStudiesByConditionsSpecific( - pageable, member.getId(), requestWithTheme, region1.getCode(), StudySortBy.ALL); + pageable, member.getId(), requestWithTheme, region1.getCode(), StudySortBy.ALL); // then assertEquals(2, result.getTotalElements()); @@ -1402,22 +1434,22 @@ void shouldFilterStudiesBasedOnSearchConditionsInSpecificRegion(){ @Test @DisplayName("내 특정 관심 지역 스터디 조회 - 정렬 조건에 따른 스터디 필터링 테스트(조회수 순)") - void shouldFilterStudiesInSpecificRegionBasedOnSortConditionsByHit(){ + void shouldFilterStudiesInSpecificRegionBasedOnSortConditionsByHit() { // given StudySortBy sortBy = StudySortBy.HIT; - when(memberRepository.existsById(member.getId())).thenReturn(true); - when(preferredRegionRepository.findAllByMemberId(member.getId())).thenReturn(List.of(preferredRegion1, preferredRegion2)); + when(preferredRegionRepository.findAllByMemberId(member.getId())).thenReturn( + List.of(preferredRegion1, preferredRegion2)); when(studyRegionRepository.findAllByRegion(any())).thenReturn(List.of(studyRegion1, studyRegion2)); when(studyRepository.countStudyByConditionsAndRegionStudiesAndNotInIds(any(), any(), any(), any())) - .thenReturn(3L); + .thenReturn(3L); when(studyRepository.findStudyByConditionsAndRegionStudiesAndNotInIds(any(), any(), any(), any(), any())) - .thenReturn(List.of(study1, study3, study2)); + .thenReturn(List.of(study1, study3, study2)); // when StudyPreviewDTO result = studyQueryService.findInterestRegionStudiesByConditionsSpecific( - pageable, member.getId(), requestWithTheme, region1.getCode(), sortBy); + pageable, member.getId(), requestWithTheme, region1.getCode(), sortBy); // then assertEquals(3, result.getTotalElements()); @@ -1429,22 +1461,21 @@ void shouldFilterStudiesInSpecificRegionBasedOnSortConditionsByHit(){ @Test @DisplayName("내 특정 관심 지역 스터디 조회 - 정렬 조건에 따른 스터디 필터링 테스트(좋아요 순)") - void shouldFilterStudiesInSpecificRegionBasedOnSortConditionsByLiked(){ + void shouldFilterStudiesInSpecificRegionBasedOnSortConditionsByLiked() { // given StudySortBy sortBy = StudySortBy.LIKED; - when(memberRepository.existsById(member.getId())).thenReturn(true); when(preferredRegionRepository.findAllByMemberId(member.getId())).thenReturn(List.of(preferredRegion1)); when(studyRegionRepository.findAllByRegion(any())).thenReturn(List.of(studyRegion1)); when(studyRepository.countStudyByConditionsAndRegionStudiesAndNotInIds(any(), any(), any(), any())) - .thenReturn(3L); + .thenReturn(3L); when(studyRepository.findStudyByConditionsAndRegionStudiesAndNotInIds(any(), any(), any(), any(), any())) - .thenReturn(List.of(study2, study1, study3)); + .thenReturn(List.of(study2, study1, study3)); // when StudyPreviewDTO result = studyQueryService.findInterestRegionStudiesByConditionsSpecific( - pageable, member.getId(), requestWithTheme,region1.getCode() ,sortBy); + pageable, member.getId(), requestWithTheme, region1.getCode(), sortBy); // then assertEquals(3, result.getTotalElements()); @@ -1452,8 +1483,6 @@ void shouldFilterStudiesInSpecificRegionBasedOnSortConditionsByLiked(){ } - - // 만약 회원의 관심사에 해당하는 테마가 없다면 예외 처리 -> 입력한 테마와 회원이 등록한 테마가 다르면 에러 발생 @Test @DisplayName("내 특정 관심 지역 스터디 조회 - 회원의 관심 지역에 해당하는 스터디가 없는 경우") @@ -1468,11 +1497,11 @@ void noThemeInMemberInterestRegion() { // when & then assertThrows(StudyHandler.class, () -> { - studyQueryService.findInterestRegionStudiesByConditionsSpecific(pageable, member.getId(), requestWithTheme, region1.getCode(), sortBy); + studyQueryService.findInterestRegionStudiesByConditionsSpecific(pageable, member.getId(), requestWithTheme, + region1.getCode(), sortBy); }); } - //------------------------------------ 모집 중 스터디 조회 ------------------------------------------------------ @@ -1485,9 +1514,9 @@ void findRecruitingStudiesByConditions() { Map searchConditions = getStringObjectMapWithThemeType(); when(studyRepository.findRecruitingStudyByConditions(searchConditions, sortBy, pageable)) - .thenReturn(List.of(study1, study2)); + .thenReturn(List.of(study1, study2)); when(studyRepository.countStudyByConditions(searchConditions, sortBy)) - .thenReturn(2L); + .thenReturn(2L); // SecurityContext와 Authentication을 모킹 Authentication authentication = mock(Authentication.class); @@ -1500,7 +1529,8 @@ void findRecruitingStudiesByConditions() { SecurityContextHolder.setContext(securityContext); // when - StudyPreviewDTO result = studyQueryService.findRecruitingStudiesByConditions(pageable, requestWithTheme, sortBy); + StudyPreviewDTO result = studyQueryService.findRecruitingStudiesByConditions(pageable, requestWithTheme, + sortBy); // then assertNotNull(result); @@ -1532,10 +1562,11 @@ void findLikedStudies() { // given Member member = getMember(); - when(preferredStudyRepository.findByMemberIdAndStudyLikeStatusOrderByCreatedAtDesc(member.getId(), StudyLikeStatus.LIKE, PageRequest.of(0, 10))) - .thenReturn(List.of(preferredStudy1, preferredStudy2)); + when(preferredStudyRepository.findByMemberIdAndStudyLikeStatusOrderByCreatedAtDesc(member.getId(), + StudyLikeStatus.LIKE, PageRequest.of(0, 10))) + .thenReturn(List.of(preferredStudy1, preferredStudy2)); when(preferredStudyRepository.countByMemberIdAndStudyLikeStatus(member.getId(), StudyLikeStatus.LIKE)) - .thenReturn(2L); + .thenReturn(2L); // when StudyPreviewDTO result = studyQueryService.findLikedStudies(member.getId(), PageRequest.of(0, 10)); @@ -1543,7 +1574,8 @@ void findLikedStudies() { // then assertNotNull(result); assertEquals(2, result.getTotalElements()); - verify(preferredStudyRepository).findByMemberIdAndStudyLikeStatusOrderByCreatedAtDesc(member.getId(), StudyLikeStatus.LIKE, PageRequest.of(0, 10)); + verify(preferredStudyRepository).findByMemberIdAndStudyLikeStatusOrderByCreatedAtDesc(member.getId(), + StudyLikeStatus.LIKE, PageRequest.of(0, 10)); } @@ -1553,7 +1585,8 @@ void findLikedStudies() { // given Member member = getMember(); - when(preferredStudyRepository.findByMemberIdAndStudyLikeStatusOrderByCreatedAtDesc(member.getId(), StudyLikeStatus.LIKE, PageRequest.of(0, 10))) + when(preferredStudyRepository.findByMemberIdAndStudyLikeStatusOrderByCreatedAtDesc(member.getId(), + StudyLikeStatus.LIKE, PageRequest.of(0, 10))) .thenReturn(List.of()); // when & then @@ -1574,9 +1607,9 @@ void findStudiesByKeyword() { StudySortBy sortBy = StudySortBy.ALL; when(studyRepository.searchByTitle(keyword, sortBy, pageable)) - .thenReturn(List.of(study1)); + .thenReturn(List.of(study1)); when(studyRepository.countAllByTitleContaining(keyword, sortBy)) - .thenReturn(1L); + .thenReturn(1L); // SecurityContext와 Authentication을 모킹 Authentication authentication = mock(Authentication.class); @@ -1591,8 +1624,6 @@ void findStudiesByKeyword() { // when StudyPreviewDTO result = studyQueryService.findStudiesByKeyword(pageable, keyword, sortBy); - - // then assertNotNull(result); assertEquals(1, result.getTotalElements()); @@ -1615,6 +1646,7 @@ void findStudiesByKeyword() { }); } + /* -------------------------------------------------------- 테마 별 스터디 검색 ------------------------------------------------------------------------*/ @Test @DisplayName("테마 별 스터디 검색 - 해당 테마에 해당하는 스터디가 있는 경우") @@ -1630,9 +1662,9 @@ void findStudiesByTheme() { when(studyThemeRepository.findAllByTheme(theme)).thenReturn(List.of(studyTheme)); when(studyRepository.findByStudyTheme(List.of(studyTheme), sortBy, pageable)) - .thenReturn(List.of(study1)); + .thenReturn(List.of(study1)); when(studyRepository.countStudyByStudyTheme(List.of(studyTheme), sortBy)) - .thenReturn(1L); + .thenReturn(1L); // SecurityContext와 Authentication을 모킹 Authentication authentication = mock(Authentication.class); SecurityContext securityContext = mock(SecurityContext.class); @@ -1669,7 +1701,6 @@ void findStudiesByTheme() { when(studyRepository.findByStudyTheme(List.of(studyTheme1), sortBy, pageable)) .thenReturn(List.of()); - // when & then assertThrows(StudyHandler.class, () -> { studyQueryService.findStudiesByTheme(pageable, themeType, sortBy); @@ -1685,11 +1716,11 @@ void findOngoingStudiesByMemberId() { // given when(studyMemberRepository.findAllByMemberIdAndStatus(member.getId(), StudyApplicationStatus.APPROVED)) - .thenReturn(List.of(studyMember1, studyMember2)); + .thenReturn(List.of(studyMember1, studyMember2)); when(studyRepository.findByMemberStudy(List.of(studyMember1, studyMember2), pageable)) - .thenReturn(List.of(study1, study2)); + .thenReturn(List.of(study1, study2)); when(studyRepository.countByMemberStudiesAndStatus(List.of(studyMember1, studyMember2), Status.ON)) - .thenReturn(2L); + .thenReturn(2L); // when StudyPreviewDTO result = studyQueryService.findOngoingStudiesByMemberId(pageable, member.getId()); @@ -1723,7 +1754,7 @@ void findOngoingStudiesByMemberId() { // given when(studyMemberRepository.findAllByMemberIdAndStatus(member.getId(), StudyApplicationStatus.APPROVED)) - .thenReturn(List.of(studyMember1, studyMember2)); + .thenReturn(List.of(studyMember1, studyMember2)); when(studyRepository.findByMemberStudy(List.of(studyMember1, studyMember2), pageable)) .thenReturn(List.of()); @@ -1735,7 +1766,6 @@ void findOngoingStudiesByMemberId() { } - /* -------------------------------------------------------- 내가 신청한 스터디 조회 ------------------------------------------------------------------------*/ @Test @DisplayName("내가 신청한 스터디 조회 - 신청한 스터디가 있는 경우") @@ -1743,11 +1773,11 @@ void findAppliedStudies() { // given when(studyMemberRepository.findAllByMemberIdAndStatus(member.getId(), StudyApplicationStatus.APPLIED)) - .thenReturn(List.of(studyMember1, studyMember2)); + .thenReturn(List.of(studyMember1, studyMember2)); when(studyRepository.findByMemberStudy(List.of(studyMember1, studyMember2), pageable)) - .thenReturn(List.of(study1, study2)); + .thenReturn(List.of(study1, study2)); when(studyRepository.countByMemberStudiesAndStatus(List.of(studyMember1, studyMember2), Status.ON)) - .thenReturn(2L); + .thenReturn(2L); // when StudyPreviewDTO result = studyQueryService.findAppliedStudies(pageable, member.getId()); @@ -1780,9 +1810,9 @@ void findAppliedStudies() { // given when(studyMemberRepository.findAllByMemberIdAndStatus(member.getId(), StudyApplicationStatus.APPLIED)) - .thenReturn(List.of(studyMember1, studyMember2)); + .thenReturn(List.of(studyMember1, studyMember2)); when(studyRepository.findByMemberStudy(List.of(studyMember1, studyMember2), pageable)) - .thenReturn(List.of()); + .thenReturn(List.of()); // when & then assertThrows(StudyHandler.class, () -> { @@ -1790,6 +1820,7 @@ void findAppliedStudies() { }); } + /* -------------------------------------------------------- 내가 모집 중인 스터디 조회 ------------------------------------------------------------------------*/ @Test @DisplayName("내가 모집중인 스터디 조회 - 모집중인 스터디가 있는 경우") @@ -1799,13 +1830,13 @@ void findMyRecruitingStudies() { Pageable pageable = PageRequest.of(0, 10); - when(studyMemberRepository.findAllByMemberIdAndIsOwned(member.getId(), true)) - .thenReturn(List.of(studyMember1, studyMember2)); + .thenReturn(List.of(studyMember1, studyMember2)); when(studyRepository.findRecruitingStudiesByMemberStudy(List.of(studyMember1, studyMember2), pageable)) - .thenReturn(List.of(study1, study2)); - when(studyRepository.countByMemberStudiesAndStatusAndIsOwned(List.of(studyMember1, studyMember2), Status.ON, true)) - .thenReturn(2L); + .thenReturn(List.of(study1, study2)); + when(studyRepository.countByMemberStudiesAndStatusAndIsOwned(List.of(studyMember1, studyMember2), Status.ON, + true)) + .thenReturn(2L); // when StudyPreviewDTO result = studyQueryService.findMyRecruitingStudies(pageable, member.getId()); @@ -1832,6 +1863,7 @@ void findMyRecruitingStudies() { }); } + @Test @DisplayName("내가 모집중인 스터디 조회 - 모집중인 스터디가 없는 경우 (회원이 모집중인 스터디가 종료 및 삭제 된 경우) ") void 내가_모집중인_스터디가_없는_경우_2() { @@ -1839,7 +1871,7 @@ void findMyRecruitingStudies() { Member member = getMember(); when(studyMemberRepository.findAllByMemberIdAndIsOwned(member.getId(), true)) - .thenReturn(List.of(studyMember1, studyMember2)); + .thenReturn(List.of(studyMember1, studyMember2)); when(studyRepository.findByMemberStudy(List.of(studyMember1, studyMember2), pageable)) .thenReturn(List.of()); @@ -1855,46 +1887,47 @@ void findMyRecruitingStudies() { private static Member getMember() { return Member.builder() - .id(1L) - .build(); + .id(1L) + .build(); } + private static void initStudy() { study1 = Study.builder() - .id(1L) - .gender(Gender.MALE) - .minAge(18) - .maxAge(35) - .fee(1000) - .profileImage("profile1.jpg") - .hasFee(true) - .isOnline(true) - .studyState(StudyState.RECRUITING) - .heartCount(0) - .status(Status.ON) - .hitNum(0L) - .goal("Learn English") - .introduction("This is an English study group") - .title("English Study Group") - .maxPeople(10L) - .build(); + .id(1L) + .gender(Gender.MALE) + .minAge(18) + .maxAge(35) + .fee(1000) + .profileImage("profile1.jpg") + .hasFee(true) + .isOnline(true) + .studyState(StudyState.RECRUITING) + .heartCount(0) + .status(Status.ON) + .hitNum(0L) + .goal("Learn English") + .introduction("This is an English study group") + .title("English Study Group") + .maxPeople(10L) + .build(); study2 = Study.builder() - .gender(Gender.FEMALE) - .minAge(25) - .maxAge(30) - .fee(2000) - .profileImage("profile2.jpg") - .hasFee(true) - .isOnline(false) - .studyState(StudyState.RECRUITING) - .heartCount(0) - .status(Status.ON) - .hitNum(0L) - .goal("Win a competition") - .introduction("This is a competition study group") - .title("Competition Study Group") - .maxPeople(15L) - .build(); + .gender(Gender.FEMALE) + .minAge(25) + .maxAge(30) + .fee(2000) + .profileImage("profile2.jpg") + .hasFee(true) + .isOnline(false) + .studyState(StudyState.RECRUITING) + .heartCount(0) + .status(Status.ON) + .hitNum(0L) + .goal("Win a competition") + .introduction("This is a competition study group") + .title("Competition Study Group") + .maxPeople(15L) + .build(); study3 = Study.builder() .gender(Gender.MALE) .minAge(18) @@ -1913,43 +1946,44 @@ private static void initStudy() { .maxPeople(10L) .build(); - study1.increaseHit(); study1.increaseHit(); + study1.increaseHit(); + study1.increaseHit(); study3.increaseHit(); study2.addPreferredStudy(getPreferredStudy(getMember(), study2)); } private static Theme getTheme(Long id, ThemeType themeType) { return Theme.builder() - .id(id) - .themeType(themeType) - .build(); + .id(id) + .themeType(themeType) + .build(); } private static Region getRegion(String neighborhood, String number) { return Region.builder() - .province("경기도") - .neighborhood(neighborhood) - .district("화성시") - .code(number) - .build(); + .province("경기도") + .neighborhood(neighborhood) + .district("화성시") + .code(number) + .build(); } private static PreferredStudy getPreferredStudy(Member member, Study study) { return PreferredStudy.builder() - .member(member) - .study(study) - .studyLikeStatus(StudyLikeStatus.LIKE) - .build(); + .member(member) + .study(study) + .studyLikeStatus(StudyLikeStatus.LIKE) + .build(); } private static StudyMember getMemberStudy(Member member, Study study) { return StudyMember.builder() - .member(member) - .study(study) + .member(member) + .study(study) .isOwned(true) .status(StudyApplicationStatus.APPROVED) - .build(); + .build(); } private static Map getStringObjectMap() { diff --git a/src/test/java/com/example/spot/service/study/studymember/StudyMemberCommandServiceTest.java b/src/test/java/com/example/spot/service/study/studymember/StudyMemberCommandServiceTest.java index 64d9e49f..c1c23b49 100644 --- a/src/test/java/com/example/spot/service/study/studymember/StudyMemberCommandServiceTest.java +++ b/src/test/java/com/example/spot/service/study/studymember/StudyMemberCommandServiceTest.java @@ -7,8 +7,8 @@ import com.example.spot.common.api.exception.handler.StudyHandler; import com.example.spot.member.domain.Member; -import com.example.spot.member.domain.MemberRepository; import com.example.spot.member.domain.enums.Status; +import com.example.spot.member.infrastructure.MemberRepository; import com.example.spot.study.application.StudyMemberCommandServiceImpl; import com.example.spot.study.domain.Study; import com.example.spot.study.domain.StudyRepository; diff --git a/src/test/java/com/example/spot/service/todo/ToDoCommandServiceTest.java b/src/test/java/com/example/spot/service/todo/ToDoCommandServiceTest.java index 1c826846..567d7def 100644 --- a/src/test/java/com/example/spot/service/todo/ToDoCommandServiceTest.java +++ b/src/test/java/com/example/spot/service/todo/ToDoCommandServiceTest.java @@ -11,7 +11,7 @@ import com.example.spot.common.api.exception.handler.StudyHandler; import com.example.spot.member.domain.Member; -import com.example.spot.member.domain.MemberRepository; +import com.example.spot.member.infrastructure.MemberRepository; import com.example.spot.study.domain.Study; import com.example.spot.study.domain.StudyRepository; import com.example.spot.study.domain.association.StudyMember;