diff --git a/build.gradle b/build.gradle index 451717b5..ef2405b9 100644 --- a/build.gradle +++ b/build.gradle @@ -50,6 +50,13 @@ dependencies { annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta" annotationProcessor "jakarta.annotation:jakarta.annotation-api" annotationProcessor "jakarta.persistence:jakarta.persistence-api" + + // aws s3 + implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' + +// https://mvnrepository.com/artifact/org.mockito/mockito-core + testImplementation 'org.mockito:mockito-core:5.6.0' + } tasks.named('test') { diff --git a/src/main/java/com/postgraduate/domain/auth/application/dto/res/AuthUserResponse.java b/src/main/java/com/postgraduate/domain/auth/application/dto/res/AuthUserResponse.java index 05caf118..8535bf9b 100644 --- a/src/main/java/com/postgraduate/domain/auth/application/dto/res/AuthUserResponse.java +++ b/src/main/java/com/postgraduate/domain/auth/application/dto/res/AuthUserResponse.java @@ -6,11 +6,13 @@ import lombok.Getter; import lombok.NoArgsConstructor; +import java.util.Optional; + @Builder @Getter @NoArgsConstructor @AllArgsConstructor public class AuthUserResponse { - private User user; + private Optional user; private Long socialId; } \ No newline at end of file diff --git a/src/main/java/com/postgraduate/domain/auth/application/mapper/AuthMapper.java b/src/main/java/com/postgraduate/domain/auth/application/mapper/AuthMapper.java index 4a6a1b7c..aec141d1 100644 --- a/src/main/java/com/postgraduate/domain/auth/application/mapper/AuthMapper.java +++ b/src/main/java/com/postgraduate/domain/auth/application/mapper/AuthMapper.java @@ -3,8 +3,10 @@ import com.postgraduate.domain.auth.application.dto.res.AuthUserResponse; import com.postgraduate.domain.user.domain.entity.User; +import java.util.Optional; + public class AuthMapper { - public static AuthUserResponse mapToAuthUser(User user, Long socialId) { + public static AuthUserResponse mapToAuthUser(Optional user, Long socialId) { return AuthUserResponse.builder() .user(user) .socialId(socialId) diff --git a/src/main/java/com/postgraduate/domain/auth/application/usecase/kakao/KakaoSignInUseCase.java b/src/main/java/com/postgraduate/domain/auth/application/usecase/kakao/KakaoSignInUseCase.java index b88057d5..e8e4f548 100644 --- a/src/main/java/com/postgraduate/domain/auth/application/usecase/kakao/KakaoSignInUseCase.java +++ b/src/main/java/com/postgraduate/domain/auth/application/usecase/kakao/KakaoSignInUseCase.java @@ -4,7 +4,7 @@ import com.postgraduate.domain.auth.application.dto.res.KakaoUserInfoResponse; import com.postgraduate.domain.auth.application.dto.req.SignUpRequest; import com.postgraduate.domain.auth.application.mapper.AuthMapper; -import com.postgraduate.domain.user.application.usecase.UserCheckUseCase; +import com.postgraduate.domain.user.application.mapper.UserMapper; import com.postgraduate.domain.user.domain.entity.User; import com.postgraduate.domain.user.domain.service.UserGetService; import com.postgraduate.domain.user.domain.service.UserSaveService; @@ -18,7 +18,6 @@ @RequiredArgsConstructor public class KakaoSignInUseCase { private final KakaoAccessTokenUseCase kakaoTokenUseCase; - private final UserCheckUseCase userCheckUseCase; private final UserSaveService userSaveService; private final UserGetService userGetService; @@ -27,17 +26,12 @@ public AuthUserResponse getUser(String token) { KakaoUserInfoResponse userInfo = kakaoTokenUseCase.getUserInfo(token); Long socialId = userInfo.getId(); Optional user = userGetService.bySocialId(socialId); - if (user.isEmpty()) { - return AuthMapper.mapToAuthUser(null, socialId); - } - return AuthMapper.mapToAuthUser(user.get(), null); + return AuthMapper.mapToAuthUser(user, socialId); } - public AuthUserResponse signUp(SignUpRequest request) { - if (userCheckUseCase.isDuplicatedNickName(request.getNickName())) { - throw new RuntimeException("중복예외"); - } - User user = userSaveService.saveUser(request); - return AuthMapper.mapToAuthUser(user, null); + public User signUp(SignUpRequest request) { + User user = UserMapper.mapToUser(request); + userSaveService.saveUser(user); + return user; } } \ No newline at end of file diff --git a/src/main/java/com/postgraduate/domain/auth/presentation/AuthController.java b/src/main/java/com/postgraduate/domain/auth/presentation/AuthController.java index 312fe82d..ed62a293 100644 --- a/src/main/java/com/postgraduate/domain/auth/presentation/AuthController.java +++ b/src/main/java/com/postgraduate/domain/auth/presentation/AuthController.java @@ -6,6 +6,7 @@ import com.postgraduate.domain.auth.application.dto.res.JwtTokenResponse; import com.postgraduate.domain.auth.application.usecase.jwt.JwtUseCase; import com.postgraduate.domain.auth.application.usecase.kakao.KakaoSignInUseCase; +import com.postgraduate.domain.user.domain.entity.User; import com.postgraduate.global.auth.AuthDetails; import com.postgraduate.global.dto.ResponseDto; import io.swagger.v3.oas.annotations.Operation; @@ -31,20 +32,20 @@ public class AuthController { @PostMapping("/login") @Operation(summary = "카카오 로그인", description = "회원인 경우 JWT를, 회원이 아닌 경우 socialId를 반환합니다(회원가입은 진행하지 않습니다).") - public ResponseDto getUserDetails(@RequestBody KakaoLoginRequest request) { + public ResponseDto authLogin(@RequestBody KakaoLoginRequest request) { AuthUserResponse authUser = kakaoSignInUseCase.getUser(request.getAccessToken()); - if (authUser.getSocialId() != null) { + if (authUser.getUser().isEmpty()) return ResponseDto.create(AUTH_NONE.getCode(), NOT_REGISTERED_USER_MESSAGE.getMessage(), authUser); - } - JwtTokenResponse jwtToken = jwtUseCase.signIn(authUser.getUser()); + + JwtTokenResponse jwtToken = jwtUseCase.signIn(authUser.getUser().get()); return ResponseDto.create(AUTH_ALREADY.getCode(), SUCCESS_AUTH_MESSAGE.getMessage(), jwtToken); } @PostMapping("/signup") @Operation(summary = "회원가입", description = "로그인 API에서 반환한 socialId와 닉네임을 함께 보내주세요.") public ResponseDto signUpUser(@RequestBody SignUpRequest request) { - AuthUserResponse authUser = kakaoSignInUseCase.signUp(request); - JwtTokenResponse jwtToken = jwtUseCase.signIn(authUser.getUser()); + User user = kakaoSignInUseCase.signUp(request); + JwtTokenResponse jwtToken = jwtUseCase.signIn(user); return ResponseDto.create(AUTH_CREATE.getCode(), SUCCESS_AUTH_MESSAGE.getMessage(), jwtToken); } diff --git a/src/main/java/com/postgraduate/domain/image/application/dto/PreSignedUrlRequest.java b/src/main/java/com/postgraduate/domain/image/application/dto/PreSignedUrlRequest.java new file mode 100644 index 00000000..e9f87da2 --- /dev/null +++ b/src/main/java/com/postgraduate/domain/image/application/dto/PreSignedUrlRequest.java @@ -0,0 +1,12 @@ +package com.postgraduate.domain.image.application.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@AllArgsConstructor +@NoArgsConstructor +public class PreSignedUrlRequest { + private String fileName; +} diff --git a/src/main/java/com/postgraduate/domain/image/application/dto/PreSignedUrlResponse.java b/src/main/java/com/postgraduate/domain/image/application/dto/PreSignedUrlResponse.java new file mode 100644 index 00000000..632421da --- /dev/null +++ b/src/main/java/com/postgraduate/domain/image/application/dto/PreSignedUrlResponse.java @@ -0,0 +1,14 @@ +package com.postgraduate.domain.image.application.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Builder +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class PreSignedUrlResponse { + private String preSignedUrl; +} diff --git a/src/main/java/com/postgraduate/domain/image/application/usecase/PreSignedUseCase.java b/src/main/java/com/postgraduate/domain/image/application/usecase/PreSignedUseCase.java new file mode 100644 index 00000000..d65b5896 --- /dev/null +++ b/src/main/java/com/postgraduate/domain/image/application/usecase/PreSignedUseCase.java @@ -0,0 +1,27 @@ +package com.postgraduate.domain.image.application.usecase; + +import com.postgraduate.domain.image.application.dto.PreSignedUrlRequest; +import com.postgraduate.domain.image.application.dto.PreSignedUrlResponse; +import com.postgraduate.global.config.s3.S3Service; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class PreSignedUseCase { + private final S3Service s3Service; + + public PreSignedUrlResponse getProfileUrl(PreSignedUrlRequest preSignedUrlRequest) { + if (preSignedUrlRequest.getFileName().isEmpty()) + throw new IllegalArgumentException(); //TODO : 빈값이 들어올 경우 예외 처리 + String preSignedUrl = s3Service.getProfilePreSignedUrl(preSignedUrlRequest.getFileName()); + return new PreSignedUrlResponse(preSignedUrl); + } + + public PreSignedUrlResponse getCertificationUrl(PreSignedUrlRequest preSignedUrlRequest) { + if (preSignedUrlRequest.getFileName().isEmpty()) + throw new IllegalArgumentException(); //TODO : 빈값이 들어올 경우 예외 처리 + String preSignedUrl = s3Service.getCertificationPreSignedUrl(preSignedUrlRequest.getFileName()); + return new PreSignedUrlResponse(preSignedUrl); + } +} diff --git a/src/main/java/com/postgraduate/domain/image/presentation/ImageController.java b/src/main/java/com/postgraduate/domain/image/presentation/ImageController.java new file mode 100644 index 00000000..a5718b77 --- /dev/null +++ b/src/main/java/com/postgraduate/domain/image/presentation/ImageController.java @@ -0,0 +1,38 @@ +package com.postgraduate.domain.image.presentation; + +import com.postgraduate.domain.image.application.dto.PreSignedUrlRequest; +import com.postgraduate.domain.image.application.dto.PreSignedUrlResponse; +import com.postgraduate.domain.image.application.usecase.PreSignedUseCase; +import com.postgraduate.global.dto.ResponseDto; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import static com.postgraduate.domain.image.presentation.constant.ImageResponseCode.IMAGE_CREATE; +import static com.postgraduate.domain.image.presentation.constant.ImageResponseMessage.ISSUE_URL; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/image") +@Tag(name = "IMAGE Controller") +public class ImageController { + private final PreSignedUseCase preSignedUseCase; + + @PostMapping("/url/profile") + @Operation(description = "USER Profile 등록 URL API - 이미지 풀네임으로 주세요 xxx.확장자, 이미지 이름을 유니크하게 만들어주세요 UUID+파일명 등등") + public ResponseDto getProfileUrl(@RequestBody PreSignedUrlRequest preSignedUrlRequest) { + PreSignedUrlResponse profileUrl = preSignedUseCase.getProfileUrl(preSignedUrlRequest); + return ResponseDto.create(IMAGE_CREATE.getCode(), ISSUE_URL.getMessage(), profileUrl); + } + + @PostMapping("/url/certification") + @Operation(description = "Senior 학생증 인증 등록 URL API - 이미지 풀네임으로 주세요 xxx.확장자, 이미지 이름을 유니크하게 만들어주세요 UUID+파일명 등등") + public ResponseDto getCertificationUrl(@RequestBody PreSignedUrlRequest preSignedUrlRequest) { + PreSignedUrlResponse certificationUrl = preSignedUseCase.getCertificationUrl(preSignedUrlRequest); + return ResponseDto.create(IMAGE_CREATE.getCode(), ISSUE_URL.getMessage(), certificationUrl); + } +} diff --git a/src/main/java/com/postgraduate/domain/image/presentation/constant/ImageResponseCode.java b/src/main/java/com/postgraduate/domain/image/presentation/constant/ImageResponseCode.java new file mode 100644 index 00000000..8596f472 --- /dev/null +++ b/src/main/java/com/postgraduate/domain/image/presentation/constant/ImageResponseCode.java @@ -0,0 +1,14 @@ +package com.postgraduate.domain.image.presentation.constant; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum ImageResponseCode { + IMAGE_FIND("IMG200"), + IMAGE_UPDATE("IMG201"), + IMAGE_CREATE("IMG202"), + IMAGE_DELETE("IMG203"); + private final String code; +} diff --git a/src/main/java/com/postgraduate/domain/image/presentation/constant/ImageResponseMessage.java b/src/main/java/com/postgraduate/domain/image/presentation/constant/ImageResponseMessage.java new file mode 100644 index 00000000..37186e1f --- /dev/null +++ b/src/main/java/com/postgraduate/domain/image/presentation/constant/ImageResponseMessage.java @@ -0,0 +1,11 @@ +package com.postgraduate.domain.image.presentation.constant; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum ImageResponseMessage { + ISSUE_URL("URL발급에 성공하였습니다."); + private final String message; +} diff --git a/src/main/java/com/postgraduate/domain/senior/application/dto/req/SeniorProfileRequest.java b/src/main/java/com/postgraduate/domain/senior/application/dto/req/SeniorProfileRequest.java index 64c4b7d3..6fc8e61e 100644 --- a/src/main/java/com/postgraduate/domain/senior/application/dto/req/SeniorProfileRequest.java +++ b/src/main/java/com/postgraduate/domain/senior/application/dto/req/SeniorProfileRequest.java @@ -17,6 +17,4 @@ public class SeniorProfileRequest { private String chatLink; @NotNull private String time; - @NotNull - private int term; } diff --git a/src/main/java/com/postgraduate/domain/senior/application/usecase/SeniorSignUpUseCase.java b/src/main/java/com/postgraduate/domain/senior/application/usecase/SeniorSignUpUseCase.java index 01c2eaf4..7dd3e828 100644 --- a/src/main/java/com/postgraduate/domain/senior/application/usecase/SeniorSignUpUseCase.java +++ b/src/main/java/com/postgraduate/domain/senior/application/usecase/SeniorSignUpUseCase.java @@ -1,6 +1,8 @@ package com.postgraduate.domain.senior.application.usecase; import com.postgraduate.domain.senior.application.dto.req.SeniorSignUpRequest; +import com.postgraduate.domain.senior.application.mapper.SeniorMapper; +import com.postgraduate.domain.senior.domain.entity.Senior; import com.postgraduate.domain.senior.domain.service.SeniorSaveService; import com.postgraduate.domain.user.domain.entity.User; import com.postgraduate.domain.user.domain.entity.constant.Role; @@ -22,6 +24,7 @@ public class SeniorSignUpUseCase { public void signUp(AuthDetails authDetails, SeniorSignUpRequest request) { User user = securityUtils.getLoggedInUser(authDetails); userUpdateService.updateRole(user.getUserId(), Role.SENIOR); - seniorSaveService.saveSenior(user, request); + Senior senior = SeniorMapper.mapToSenior(user, request); + seniorSaveService.saveSenior(senior); } } diff --git a/src/main/java/com/postgraduate/domain/senior/application/usecase/SeniorUpdateUseCase.java b/src/main/java/com/postgraduate/domain/senior/application/usecase/SeniorUpdateUseCase.java index 736c2343..964e6695 100644 --- a/src/main/java/com/postgraduate/domain/senior/application/usecase/SeniorUpdateUseCase.java +++ b/src/main/java/com/postgraduate/domain/senior/application/usecase/SeniorUpdateUseCase.java @@ -3,9 +3,8 @@ import com.postgraduate.domain.senior.application.dto.req.SeniorProfileRequest; import com.postgraduate.domain.senior.domain.entity.Senior; import com.postgraduate.domain.senior.domain.service.SeniorGetService; -import com.postgraduate.domain.senior.domain.service.SeniorSaveService; +import com.postgraduate.domain.senior.domain.service.SeniorUpdateService; import com.postgraduate.domain.user.domain.entity.User; -import com.postgraduate.domain.user.domain.service.UserGetService; import com.postgraduate.global.auth.AuthDetails; import com.postgraduate.global.config.security.util.SecurityUtils; import lombok.RequiredArgsConstructor; @@ -16,13 +15,13 @@ @Transactional @RequiredArgsConstructor public class SeniorUpdateUseCase { - private final SeniorSaveService seniorSaveService; private final SeniorGetService seniorGetService; + private final SeniorUpdateService seniorUpdateService; private final SecurityUtils securityUtils; - public void updateProfile(AuthDetails authDetails, SeniorProfileRequest request) { + public void updateProfile(AuthDetails authDetails, SeniorProfileRequest profileRequest) { User user = securityUtils.getLoggedInUser(authDetails); Senior senior = seniorGetService.byUser(user); - seniorSaveService.saveSenior(senior, request); + seniorUpdateService.updateSeniorProfile(senior, profileRequest); } } diff --git a/src/main/java/com/postgraduate/domain/senior/domain/entity/Senior.java b/src/main/java/com/postgraduate/domain/senior/domain/entity/Senior.java index 40946023..1fdd32c4 100644 --- a/src/main/java/com/postgraduate/domain/senior/domain/entity/Senior.java +++ b/src/main/java/com/postgraduate/domain/senior/domain/entity/Senior.java @@ -1,5 +1,6 @@ package com.postgraduate.domain.senior.domain.entity; +import com.postgraduate.domain.senior.application.dto.req.SeniorProfileRequest; import com.postgraduate.domain.user.domain.entity.User; import jakarta.persistence.*; import lombok.AllArgsConstructor; @@ -54,6 +55,7 @@ public class Senior { @Column(nullable = false) private String bank; + @Column(nullable = false) private String certification; @Column(nullable = false) @@ -65,11 +67,10 @@ public class Senior { @Column(nullable = false) private int hit; - public void updateProfile(String info, String target, String chatLink, String time, int term) { - this.info = info; - this.target = target; - this.chatLink = chatLink; - this.time = time; - this.term = term; + public void updateProfile(SeniorProfileRequest profileRequest) { + this.info = profileRequest.getInfo(); + this.target = profileRequest.getTarget(); + this.chatLink = profileRequest.getChatLink(); + this.time = profileRequest.getTime(); } } diff --git a/src/main/java/com/postgraduate/domain/senior/domain/service/SeniorSaveService.java b/src/main/java/com/postgraduate/domain/senior/domain/service/SeniorSaveService.java index f1b1e1e0..24c39a5e 100644 --- a/src/main/java/com/postgraduate/domain/senior/domain/service/SeniorSaveService.java +++ b/src/main/java/com/postgraduate/domain/senior/domain/service/SeniorSaveService.java @@ -1,11 +1,7 @@ package com.postgraduate.domain.senior.domain.service; -import com.postgraduate.domain.senior.application.dto.req.SeniorProfileRequest; -import com.postgraduate.domain.senior.application.dto.req.SeniorSignUpRequest; -import com.postgraduate.domain.senior.application.mapper.SeniorMapper; import com.postgraduate.domain.senior.domain.entity.Senior; import com.postgraduate.domain.senior.domain.entity.repository.SeniorRepository; -import com.postgraduate.domain.user.domain.entity.User; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -14,13 +10,7 @@ public class SeniorSaveService { private final SeniorRepository seniorRepository; - public Senior saveSenior(User user, SeniorSignUpRequest request) { - Senior senior = SeniorMapper.mapToSenior(user, request); - return seniorRepository.save(senior); - } - - public Senior saveSenior(Senior senior, SeniorProfileRequest request) { - senior.updateProfile(request.getInfo(), request.getTarget(), request.getChatLink(), request.getTime(), request.getTerm()); + public Senior saveSenior(Senior senior) { return seniorRepository.save(senior); } } diff --git a/src/main/java/com/postgraduate/domain/senior/domain/service/SeniorUpdateService.java b/src/main/java/com/postgraduate/domain/senior/domain/service/SeniorUpdateService.java new file mode 100644 index 00000000..2f940181 --- /dev/null +++ b/src/main/java/com/postgraduate/domain/senior/domain/service/SeniorUpdateService.java @@ -0,0 +1,15 @@ +package com.postgraduate.domain.senior.domain.service; + +import com.postgraduate.domain.senior.application.dto.req.SeniorProfileRequest; +import com.postgraduate.domain.senior.domain.entity.Senior; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@RequiredArgsConstructor +@Service +public class SeniorUpdateService { + + public void updateSeniorProfile(Senior senior, SeniorProfileRequest profileRequest) { + senior.updateProfile(profileRequest); + } +} diff --git a/src/main/java/com/postgraduate/domain/senior/presentation/SeniorController.java b/src/main/java/com/postgraduate/domain/senior/presentation/SeniorController.java index 7c22d95b..7b382edc 100644 --- a/src/main/java/com/postgraduate/domain/senior/presentation/SeniorController.java +++ b/src/main/java/com/postgraduate/domain/senior/presentation/SeniorController.java @@ -4,7 +4,6 @@ import com.postgraduate.domain.senior.application.dto.req.SeniorSignUpRequest; import com.postgraduate.domain.senior.application.usecase.SeniorSignUpUseCase; import com.postgraduate.domain.senior.application.usecase.SeniorUpdateUseCase; -import com.postgraduate.domain.senior.presentation.constant.SeniorResponseCode; import com.postgraduate.global.auth.AuthDetails; import com.postgraduate.global.dto.ResponseDto; import io.swagger.v3.oas.annotations.Operation; @@ -14,9 +13,9 @@ import org.springframework.web.bind.annotation.*; import static com.postgraduate.domain.senior.presentation.constant.SeniorResponseCode.SENIOR_CREATE; +import static com.postgraduate.domain.senior.presentation.constant.SeniorResponseCode.SENIOR_UPDATE; import static com.postgraduate.domain.senior.presentation.constant.SeniorResponseMessage.SUCCESS_SENIOR_SIGN_UP_MESSAGE; import static com.postgraduate.domain.senior.presentation.constant.SeniorResponseMessage.SUCCESS_UPDATE_PROFILE_MESSAGE; -import static org.springframework.http.HttpStatus.OK; @RestController @@ -28,18 +27,18 @@ public class SeniorController { private final SeniorUpdateUseCase updateUseCase; @PostMapping("/signup") - @Operation(summary = "대학원생 가입", description = "대학원생 회원가입") + @Operation(summary = "대학원생 가입 - 필수 과정만", description = "대학원생 회원가입 - 필수 과정만") public ResponseDto singUpSenior(@AuthenticationPrincipal AuthDetails authDetails, - @RequestBody SeniorSignUpRequest request) { - signUpUseCase.signUp(authDetails, request); + @RequestBody SeniorSignUpRequest signUpRequest) { + signUpUseCase.signUp(authDetails, signUpRequest); return ResponseDto.create(SENIOR_CREATE.getCode(), SUCCESS_SENIOR_SIGN_UP_MESSAGE.getMessage()); } @PatchMapping("/profile") @Operation(summary = "대학원생 프로필 등록", description = "소개글, 추천대상, 오픈채팅방 링크, 가능 시간대, 소통시간") public ResponseDto singUpSenior(@AuthenticationPrincipal AuthDetails authDetails, - @RequestBody SeniorProfileRequest request) { - updateUseCase.updateProfile(authDetails, request); - return ResponseDto.create(SENIOR_CREATE.getCode(), SUCCESS_UPDATE_PROFILE_MESSAGE.getMessage()); + @RequestBody SeniorProfileRequest profileRequest) { + updateUseCase.updateProfile(authDetails, profileRequest); + return ResponseDto.create(SENIOR_UPDATE.getCode(), SUCCESS_UPDATE_PROFILE_MESSAGE.getMessage()); } } diff --git a/src/main/java/com/postgraduate/domain/user/application/dto/req/UserProfileRequest.java b/src/main/java/com/postgraduate/domain/user/application/dto/req/UserProfileRequest.java new file mode 100644 index 00000000..cb087d96 --- /dev/null +++ b/src/main/java/com/postgraduate/domain/user/application/dto/req/UserProfileRequest.java @@ -0,0 +1,12 @@ +package com.postgraduate.domain.user.application.dto.req; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +public class UserProfileRequest { + private String profile; +} diff --git a/src/main/java/com/postgraduate/domain/user/application/usecase/UserCheckUseCase.java b/src/main/java/com/postgraduate/domain/user/application/usecase/UserCheckUseCase.java deleted file mode 100644 index 7c4b5f66..00000000 --- a/src/main/java/com/postgraduate/domain/user/application/usecase/UserCheckUseCase.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.postgraduate.domain.user.application.usecase; - -import com.postgraduate.domain.user.domain.entity.User; -import com.postgraduate.domain.user.domain.service.UserGetService; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.Optional; - -@Service -@Transactional -@RequiredArgsConstructor -public class UserCheckUseCase { - private final UserGetService userGetService; - public boolean isDuplicatedNickName(String nickName) { - Optional user = userGetService.byNickName(nickName); - return user.isPresent(); - } -} diff --git a/src/main/java/com/postgraduate/domain/user/application/usecase/UserMyPageUseCase.java b/src/main/java/com/postgraduate/domain/user/application/usecase/UserMyPageUseCase.java index 866013dd..6a35cd6d 100644 --- a/src/main/java/com/postgraduate/domain/user/application/usecase/UserMyPageUseCase.java +++ b/src/main/java/com/postgraduate/domain/user/application/usecase/UserMyPageUseCase.java @@ -24,11 +24,16 @@ public UserInfoResponse getUserInfo(AuthDetails authDetails) { return UserMapper.mapToInfo(user); } - public void updateUser(AuthDetails authDetails, String nickName) { + public void updateNickName(AuthDetails authDetails, String nickName) { User user = securityUtils.getLoggedInUser(authDetails); userUpdateService.updateNickName(user.getUserId(), nickName); } + public void updateProfile(AuthDetails authDetails, String profile) { + User user = securityUtils.getLoggedInUser(authDetails); + userUpdateService.updateProfile(user.getUserId(), profile); + } + public boolean duplicatedNickName(String nickName) { return userGetService.byNickName(nickName).isEmpty(); } diff --git a/src/main/java/com/postgraduate/domain/user/domain/entity/User.java b/src/main/java/com/postgraduate/domain/user/domain/entity/User.java index 1475e5d5..4844c03d 100644 --- a/src/main/java/com/postgraduate/domain/user/domain/entity/User.java +++ b/src/main/java/com/postgraduate/domain/user/domain/entity/User.java @@ -26,7 +26,7 @@ public class User { // @Column(unique = true) email은 여러 소셜을 사용하면 unique가 깨질 수 있음 private String email; - @Column(unique = true) + @Column(nullable = false, unique = true) private String nickName; @Column(nullable = false) @@ -55,4 +55,8 @@ public void updateNickName(String nickName) { public void updateRole(Role role) { this.role = role; } + + public void updateProfile(String profile) { + this.profile = profile; + } } diff --git a/src/main/java/com/postgraduate/domain/user/domain/service/UserSaveService.java b/src/main/java/com/postgraduate/domain/user/domain/service/UserSaveService.java index f0334551..98589896 100644 --- a/src/main/java/com/postgraduate/domain/user/domain/service/UserSaveService.java +++ b/src/main/java/com/postgraduate/domain/user/domain/service/UserSaveService.java @@ -1,7 +1,5 @@ package com.postgraduate.domain.user.domain.service; -import com.postgraduate.domain.auth.application.dto.req.SignUpRequest; -import com.postgraduate.domain.user.application.mapper.UserMapper; import com.postgraduate.domain.user.domain.entity.User; import com.postgraduate.domain.user.domain.repository.UserRepository; import lombok.RequiredArgsConstructor; @@ -12,8 +10,7 @@ public class UserSaveService { private final UserRepository userRepository; - public User saveUser(SignUpRequest request) { - User user = UserMapper.mapToUser(request); - return userRepository.save(user); + public void saveUser(User user) { + userRepository.save(user); } } diff --git a/src/main/java/com/postgraduate/domain/user/domain/service/UserUpdateService.java b/src/main/java/com/postgraduate/domain/user/domain/service/UserUpdateService.java index 07070090..00865680 100644 --- a/src/main/java/com/postgraduate/domain/user/domain/service/UserUpdateService.java +++ b/src/main/java/com/postgraduate/domain/user/domain/service/UserUpdateService.java @@ -20,4 +20,9 @@ public void updateRole(Long userId, Role role) { User user = userRepository.findById(userId).get(); user.updateRole(role); } + + public void updateProfile(Long userId, String profile) { + User user = userRepository.findById(userId).get(); + user.updateProfile(profile); + } } diff --git a/src/main/java/com/postgraduate/domain/user/presentation/UserController.java b/src/main/java/com/postgraduate/domain/user/presentation/UserController.java index 3c422eb1..8d67865a 100644 --- a/src/main/java/com/postgraduate/domain/user/presentation/UserController.java +++ b/src/main/java/com/postgraduate/domain/user/presentation/UserController.java @@ -1,6 +1,7 @@ package com.postgraduate.domain.user.presentation; import com.postgraduate.domain.user.application.dto.req.UserNickNameRequest; +import com.postgraduate.domain.user.application.dto.req.UserProfileRequest; import com.postgraduate.domain.user.application.dto.res.UserInfoResponse; import com.postgraduate.domain.user.application.usecase.UserMyPageUseCase; import com.postgraduate.domain.user.presentation.constant.UserResponseCode; @@ -15,7 +16,6 @@ import static com.postgraduate.domain.user.presentation.constant.UserResponseCode.USER_FIND; import static com.postgraduate.domain.user.presentation.constant.UserResponseCode.USER_UPDATE; import static com.postgraduate.domain.user.presentation.constant.UserResponseMessage.*; -import static org.springframework.http.HttpStatus.OK; @RestController @RequiredArgsConstructor @@ -34,7 +34,7 @@ public ResponseDto getUserInfo(@AuthenticationPrincipal AuthDe @PatchMapping("/nickname") @Operation(description = "사용자 닉네임 변경 및 업데이트") public ResponseDto updateNickName(@AuthenticationPrincipal AuthDetails authDetails, @RequestBody UserNickNameRequest userNickNameRequest) { - myPageUseCase.updateUser(authDetails, userNickNameRequest.getNickName()); + myPageUseCase.updateNickName(authDetails, userNickNameRequest.getNickName()); return ResponseDto.create(USER_UPDATE.getCode(), UPDATE_USER_INFO.getMessage()); } @@ -44,4 +44,11 @@ public ResponseDto duplicatedNickName(@RequestParam String nickName) { boolean checkDup = myPageUseCase.duplicatedNickName(nickName); return ResponseDto.create(USER_FIND.getCode(), GET_NICKNAME_CHECK.getMessage(), checkDup); } + + @PatchMapping("/profile") + @Operation(description = "사용자 프로필 사진 업데이트 - url을 주세요") + public ResponseDto updateProfile(@AuthenticationPrincipal AuthDetails authDetails, @RequestBody UserProfileRequest userProfileRequest) { + myPageUseCase.updateProfile(authDetails, userProfileRequest.getProfile()); + return ResponseDto.create(USER_UPDATE.getCode(), UPDATE_USER_INFO.getMessage()); + } } diff --git a/src/main/java/com/postgraduate/global/config/s3/S3Config.java b/src/main/java/com/postgraduate/global/config/s3/S3Config.java new file mode 100644 index 00000000..542e6560 --- /dev/null +++ b/src/main/java/com/postgraduate/global/config/s3/S3Config.java @@ -0,0 +1,31 @@ +package com.postgraduate.global.config.s3; + +import com.amazonaws.auth.AWSCredentials; +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class S3Config { + + @Value("${cloud.aws.credentials.access-key}") + public String accessKey; + @Value("${cloud.aws.credentials.secret-key}") + public String secretKey; + @Value("${cloud.aws.region.static}") + public String region; + + @Bean + public AmazonS3Client amazonS3Client() { + AWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey); + return (AmazonS3Client) + AmazonS3ClientBuilder.standard() + .withCredentials(new AWSStaticCredentialsProvider(credentials)) + .withRegion(region) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/postgraduate/global/config/s3/S3Service.java b/src/main/java/com/postgraduate/global/config/s3/S3Service.java new file mode 100644 index 00000000..c5a632f2 --- /dev/null +++ b/src/main/java/com/postgraduate/global/config/s3/S3Service.java @@ -0,0 +1,65 @@ +package com.postgraduate.global.config.s3; + +import com.amazonaws.HttpMethod; +import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.Headers; +import com.amazonaws.services.s3.model.CannedAccessControlList; +import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.net.URL; +import java.util.Date; + +@Service +@RequiredArgsConstructor +@Slf4j +public class S3Service { + private final AmazonS3Client amazonS3; + @Value("${cloud.aws.s3.bucket}") + private String bucket; + @Value("${cloud.aws.s3.prefix-profile}") + private String prefixProfile; + @Value("${cloud.aws.s3.prefix-certification}") + private String prefixCertification; + + /** + * @param fileName : 어떤 파일을 저장할 것인가 (파일명) + * @return + */ + public String getProfilePreSignedUrl(String fileName) { + fileName = prefixProfile + "/" + fileName; + GeneratePresignedUrlRequest generatePresignedUrlRequest = getGeneratePreSignedUrlRequest(bucket, fileName); + URL url = amazonS3.generatePresignedUrl(generatePresignedUrlRequest); + return url.toString(); + } + + public String getCertificationPreSignedUrl(String fileName) { + fileName = prefixCertification + "/" + fileName; + GeneratePresignedUrlRequest generatePresignedUrlRequest = getGeneratePreSignedUrlRequest(bucket, fileName); + URL url = amazonS3.generatePresignedUrl(generatePresignedUrlRequest); + return url.toString(); + } + + private GeneratePresignedUrlRequest getGeneratePreSignedUrlRequest(String bucket, String fileName) { + GeneratePresignedUrlRequest generatePresignedUrlRequest = + new GeneratePresignedUrlRequest(bucket, fileName) + .withMethod(HttpMethod.PUT) + .withExpiration(getPreSignedUrlExpiration()); + generatePresignedUrlRequest.addRequestParameter( + Headers.S3_CANNED_ACL, + CannedAccessControlList.PublicRead.toString()); + return generatePresignedUrlRequest; + } + + private Date getPreSignedUrlExpiration() { + Date expiration = new Date(); + long expTimeMillis = expiration.getTime(); + expTimeMillis += 1000 * 60 * 2; // 2분동안 가능하도록 + expiration.setTime(expTimeMillis); + log.info(expiration.toString()); + return expiration; + } +} diff --git a/src/test/java/com/postgraduate/domain/auth/application/usecase/kakao/KakaoSignInUseCaseTest.java b/src/test/java/com/postgraduate/domain/auth/application/usecase/kakao/KakaoSignInUseCaseTest.java index 56cb4dad..720a77c1 100644 --- a/src/test/java/com/postgraduate/domain/auth/application/usecase/kakao/KakaoSignInUseCaseTest.java +++ b/src/test/java/com/postgraduate/domain/auth/application/usecase/kakao/KakaoSignInUseCaseTest.java @@ -1,30 +1,46 @@ package com.postgraduate.domain.auth.application.usecase.kakao; -import com.postgraduate.domain.auth.application.dto.req.SignUpRequest; -import com.postgraduate.domain.user.application.usecase.UserCheckUseCase; +import com.postgraduate.domain.auth.application.dto.res.AuthUserResponse; +import com.postgraduate.domain.auth.application.dto.res.KakaoUserInfoResponse; +import com.postgraduate.domain.auth.application.dto.res.KakaoUserInfoResponse.KakaoAccount; +import com.postgraduate.domain.user.domain.entity.User; import com.postgraduate.domain.user.domain.service.UserGetService; -import com.postgraduate.domain.user.domain.service.UserSaveService; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import java.util.Optional; -class KakaoSignInUseCaseTest { +import static org.mockito.BDDMockito.given; +@ExtendWith(MockitoExtension.class) +class KakaoSignInUseCaseTest { + @Mock + UserGetService userGetService; + @Mock + KakaoAccessTokenUseCase kakaoAccessTokenUseCase; + @InjectMocks + KakaoSignInUseCase kakaoSignInUseCase; @Test - void 중복_아이디를_입력하면_예외를_발생시킨다() { - KakaoAccessTokenUseCase kakaoAccessTokenUseCase = mock(); - UserCheckUseCase userCheckUseCase = mock(); - UserSaveService userSaveService = mock(); - UserGetService userGetService = mock(); - - SignUpRequest request = new SignUpRequest(23423423L, "nickname"); - - when(userCheckUseCase.isDuplicatedNickName(request.getNickName())).thenReturn(true); - - KakaoSignInUseCase kakaoSignInUseCase = new KakaoSignInUseCase(kakaoAccessTokenUseCase, userCheckUseCase, userSaveService, userGetService); + void 첫_로그인_socialId_반환() { + String token = "abcd"; + given(kakaoAccessTokenUseCase.getUserInfo(token)).willReturn(new KakaoUserInfoResponse(10000L, new KakaoAccount())); + given(userGetService.bySocialId(10000L)).willReturn(Optional.ofNullable(null)); + AuthUserResponse authUserResponse = kakaoSignInUseCase.getUser(token); + Assertions.assertThat(authUserResponse.getSocialId()).isEqualTo(10000L); + Assertions.assertThat(authUserResponse.getUser().isEmpty()).isTrue(); + } - assertThrows(RuntimeException.class, () -> kakaoSignInUseCase.signUp(request)); + @Test + void 기존_유저_로그인_user반환() { + String token = "abcd"; + given(kakaoAccessTokenUseCase.getUserInfo(token)).willReturn(new KakaoUserInfoResponse(10000L, new KakaoAccount())); + given(userGetService.bySocialId(10000L)).willReturn(Optional.ofNullable(new User())); + AuthUserResponse authUserResponse = kakaoSignInUseCase.getUser(token); + Assertions.assertThat(authUserResponse.getSocialId()).isEqualTo(10000L); + Assertions.assertThat(authUserResponse.getUser().isEmpty()).isFalse(); } } \ No newline at end of file