diff --git a/Dockerfile b/Dockerfile index 16afd3e..b36374b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ FROM openjdk:17-alpine WORKDIR /app -COPY ./closeUp-0.0.1-SNAPSHOT.jar ./app.jar # ./ 없으면 안됨 +COPY ./closeUp-0.0.1-SNAPSHOT.jar ./app.jar ENTRYPOINT ["java", "-jar", "app.jar"] \ No newline at end of file diff --git a/build.gradle b/build.gradle index 7f37d49..c05126a 100644 --- a/build.gradle +++ b/build.gradle @@ -62,6 +62,8 @@ dependencies { // aws implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' + // aws sdk javax + implementation 'javax.xml.bind:jaxb-api:2.3.1' } // QueryDSL diff --git a/src/main/java/farmSystem/closeUp/common/Result.java b/src/main/java/farmSystem/closeUp/common/Result.java index 1d47ee9..7b50057 100644 --- a/src/main/java/farmSystem/closeUp/common/Result.java +++ b/src/main/java/farmSystem/closeUp/common/Result.java @@ -46,7 +46,13 @@ public enum Result { // 포인트 LESS_THAN_MINIMUM_POINT(400, "5000원 이상부터 충전이 가능합니다."), NOT_EQUAL_AMOUNT(400, "결제 금액이 일치하지 않습니다."), - NOT_FOUND_POINTHISTORY(404, "포인트 체결 내역이 존재하지 않습니다."); + NOT_FOUND_POINTHISTORY(404, "포인트 체결 내역이 존재하지 않습니다."), + // 플랫폼 + NOTFOUND_PLATFORM(404, "해당 플랫폼이 존재하지 않습니다."), + + // 관심사 + NOTFOUND_INTEREST(404, "해당 관심사가 존재하지 않습니다."), FILE_UPLOAD_FAIL(404, "파일 업로드에 실패했습니다."),; + private final String message; private final int status; diff --git a/src/main/java/farmSystem/closeUp/config/oauth/handler/OAuth2LoginSuccessHandler.java b/src/main/java/farmSystem/closeUp/config/oauth/handler/OAuth2LoginSuccessHandler.java index 7d833e4..e6be0f4 100644 --- a/src/main/java/farmSystem/closeUp/config/oauth/handler/OAuth2LoginSuccessHandler.java +++ b/src/main/java/farmSystem/closeUp/config/oauth/handler/OAuth2LoginSuccessHandler.java @@ -54,6 +54,11 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo String redirectUrl = createToken(response, oAuth2User, targetUrl); log.info("크리에이터 팔로우 페이지로 이동"); getRedirectStrategy().sendRedirect(request, response, redirectUrl); + } else if (oAuth2User.getUserRole() == UserRole.SIGNUP_CREATOR) { // 크리에이터가 추가정보는 입력했는데, 플랫폼 정보 및 관심사 설정안한경우 + targetUrl = "http://localhost:8080/creator/interest"; + String redirectUrl = createToken(response, oAuth2User, targetUrl); + log.info("크리에이터 플랫폼 및 이미지 검증 및 관심사 설정 페이지로 이동"); + getRedirectStrategy().sendRedirect(request, response, redirectUrl); } } catch (Exception e) { throw e; diff --git a/src/main/java/farmSystem/closeUp/config/security/SecurityConfig.java b/src/main/java/farmSystem/closeUp/config/security/SecurityConfig.java index b3846ac..73c15a1 100644 --- a/src/main/java/farmSystem/closeUp/config/security/SecurityConfig.java +++ b/src/main/java/farmSystem/closeUp/config/security/SecurityConfig.java @@ -60,7 +60,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{ .requestMatchers("/health","/token/reissue", "/css/**","/images/**","/js/**","/favicon.ico","/h2-console/**").permitAll() .requestMatchers("/user/sign-up/**").hasAnyRole(String.valueOf(UserRole.GUEST), String.valueOf(UserRole.SIGNUP_USER), String.valueOf(UserRole.FOLLOWED_USER), String.valueOf(UserRole.INTERESTED_USER)) .requestMatchers("/user/**").hasRole(String.valueOf(UserRole.USER)) - .requestMatchers("/creator/**").hasRole("CREATOR") + .requestMatchers("/creator/sign-up/**").hasAnyRole(String.valueOf(UserRole.GUEST), String.valueOf(UserRole.SIGNUP_CREATOR)) + .requestMatchers("/creator/**").hasRole(String.valueOf(UserRole.CREATOR)) .anyRequest().authenticated() ) diff --git a/src/main/java/farmSystem/closeUp/controller/UserController.java b/src/main/java/farmSystem/closeUp/controller/UserController.java index 2ad6d2e..2c96c72 100644 --- a/src/main/java/farmSystem/closeUp/controller/UserController.java +++ b/src/main/java/farmSystem/closeUp/controller/UserController.java @@ -2,6 +2,8 @@ import farmSystem.closeUp.common.CommonResponse; +import farmSystem.closeUp.dto.creator.request.PostCreatorSettingRequest; +import farmSystem.closeUp.dto.user.request.PostCreatorInfoRequest; import farmSystem.closeUp.dto.user.request.UserFollowRequest; import farmSystem.closeUp.dto.user.request.UserInfoRequest; import farmSystem.closeUp.dto.user.request.UserInterestRequest; @@ -15,6 +17,7 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import java.util.List; @@ -59,4 +62,16 @@ public CommonResponse signUpFollowBulk(@RequestBody @Valid f public CommonResponse signUpInterestBulk(@RequestBody @Valid final UserInterestRequest userInterestRequest){ return CommonResponse.success(userService.interestBulk(userInterestRequest)); } + + // 크리에이터 회원가입 + @PostMapping(value = "/creator/sign-up") + public CommonResponse signUpCreator(@RequestPart @Valid final PostCreatorInfoRequest postCreatorInfoRequest, @RequestPart MultipartFile multipartFile){ + return CommonResponse.success(userService.signUpCreator(postCreatorInfoRequest, multipartFile)); + } + + // 크리에이터 플랫폼, 활동 장르 설정 및 본인인증 + @PostMapping(value = "/creator/sign-up/setting") + public CommonResponse creatorSetting(@RequestPart @Valid final PostCreatorSettingRequest postCreatorSettingRequest, @RequestPart MultipartFile multipartFile){ + return CommonResponse.success(userService.creatorSetting(postCreatorSettingRequest, multipartFile)); + } } diff --git a/src/main/java/farmSystem/closeUp/domain/User.java b/src/main/java/farmSystem/closeUp/domain/User.java index a88ac86..9203c99 100644 --- a/src/main/java/farmSystem/closeUp/domain/User.java +++ b/src/main/java/farmSystem/closeUp/domain/User.java @@ -89,6 +89,17 @@ public void update(Long id, String nickname, String address, String phoneNumber, this.userRole = userRole; } + public void update(String nickname, String address, String phoneNumber, String profileImageUrl, String gender, String birthDay, String profileComment, UserRole userRole) { + this.nickName = nickname; + this.address = address; + this.phoneNumber = phoneNumber; + this.profileImageUrl = profileImageUrl; + this.gender = gender; + this.birthDay = birthDay; + this.userRole = userRole; + this.profileComment = profileComment; + } + public void setPlatform(Platform platform) { this.platform = platform; } @@ -96,4 +107,12 @@ public void setPlatform(Platform platform) { public void minusPoint(Long point) { this.point -= point; } + + public void setProfileImageUrl(String fileName) { + this.profileImageUrl = fileName; + } + + public void setVerificationImageUrl(String fileName) { + this.verificationImageUrl = fileName; + } } diff --git a/src/main/java/farmSystem/closeUp/domain/UserRole.java b/src/main/java/farmSystem/closeUp/domain/UserRole.java index 9de9b9e..c7dbca2 100644 --- a/src/main/java/farmSystem/closeUp/domain/UserRole.java +++ b/src/main/java/farmSystem/closeUp/domain/UserRole.java @@ -6,6 +6,6 @@ @Getter @RequiredArgsConstructor public enum UserRole { - GUEST("ROLE_GUEST"), USER("ROLE_USER"), CREATOR("ROLE_CREATOR"), SIGNUP_USER("ROLE_SIGNUP"), FOLLOWED_USER("ROLE_FOLLOW"), INTERESTED_USER("ROLE_INTEREST"); // oauth 첫 로그인시에는 Guest, 이후 추가 회원가입시 User + GUEST("ROLE_GUEST"), USER("ROLE_USER"), SIGNUP_CREATOR("ROLE_SIGNUP_CREATOR"),CREATOR("ROLE_CREATOR"), SIGNUP_USER("ROLE_SIGNUP"), FOLLOWED_USER("ROLE_FOLLOW"), INTERESTED_USER("ROLE_INTEREST"); // oauth 첫 로그인시에는 Guest, 이후 추가 회원가입시 User private final String key; } diff --git a/src/main/java/farmSystem/closeUp/dto/creator/request/PostCreatorSettingRequest.java b/src/main/java/farmSystem/closeUp/dto/creator/request/PostCreatorSettingRequest.java new file mode 100644 index 0000000..69c7a0f --- /dev/null +++ b/src/main/java/farmSystem/closeUp/dto/creator/request/PostCreatorSettingRequest.java @@ -0,0 +1,11 @@ +package farmSystem.closeUp.dto.creator.request; + +import lombok.Getter; + +import java.util.List; + +@Getter +public class PostCreatorSettingRequest { + private List interestIds; + private Long platformId; +} diff --git a/src/main/java/farmSystem/closeUp/dto/user/request/PostCreatorInfoRequest.java b/src/main/java/farmSystem/closeUp/dto/user/request/PostCreatorInfoRequest.java new file mode 100644 index 0000000..c72dadd --- /dev/null +++ b/src/main/java/farmSystem/closeUp/dto/user/request/PostCreatorInfoRequest.java @@ -0,0 +1,21 @@ +package farmSystem.closeUp.dto.user.request; + +import jakarta.validation.constraints.NotBlank; +import lombok.Getter; + +@Getter +public class PostCreatorInfoRequest { + @NotBlank(message = "닉네임 입력은 필수입니다.") + private String nickname; + @NotBlank(message = "주소 입력은 필수입니다.") + private String address; + @NotBlank(message = "휴대전화번호 입력은 필수입니다.") + private String phoneNumber; + + @NotBlank(message = "성별 입력은 필수입니다.") + private String gender; + @NotBlank(message = "생일 입력은 필수입니다.") + private String birthday; + @NotBlank(message = "한줄소개는 필수입니다.") + private String profileComment; +} \ No newline at end of file diff --git a/src/main/java/farmSystem/closeUp/repository/InterestRepository.java b/src/main/java/farmSystem/closeUp/repository/InterestRepository.java new file mode 100644 index 0000000..2a57db6 --- /dev/null +++ b/src/main/java/farmSystem/closeUp/repository/InterestRepository.java @@ -0,0 +1,7 @@ +package farmSystem.closeUp.repository; + +import farmSystem.closeUp.domain.Interest; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface InterestRepository extends JpaRepository{ +} diff --git a/src/main/java/farmSystem/closeUp/service/S3UploadService.java b/src/main/java/farmSystem/closeUp/service/S3UploadService.java index a1d232d..523825f 100644 --- a/src/main/java/farmSystem/closeUp/service/S3UploadService.java +++ b/src/main/java/farmSystem/closeUp/service/S3UploadService.java @@ -1,15 +1,23 @@ package farmSystem.closeUp.service; +import com.amazonaws.AmazonServiceException; +import com.amazonaws.HttpMethod; import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest; import com.amazonaws.services.s3.model.ObjectMetadata; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; +import java.net.URL; +import java.net.URLDecoder; +import java.util.Date; @Service +@Slf4j @RequiredArgsConstructor public class S3UploadService { @@ -18,14 +26,47 @@ public class S3UploadService { @Value("${cloud.aws.s3.bucket}") private String bucket; - public String saveFile(MultipartFile multipartFile) throws IOException { - String originalFilename = multipartFile.getOriginalFilename(); + public String saveFile(MultipartFile multipartFile, String fileName) throws IOException { + ObjectMetadata metadata = new ObjectMetadata(); metadata.setContentLength(multipartFile.getSize()); metadata.setContentType(multipartFile.getContentType()); - amazonS3.putObject(bucket, originalFilename, multipartFile.getInputStream(), metadata); - return amazonS3.getUrl(bucket, originalFilename).toString(); + amazonS3.putObject(bucket, fileName, multipartFile.getInputStream(), metadata); + return URLDecoder.decode(amazonS3.getUrl(bucket, fileName).toString(), "utf-8"); + } + + /* 2. 파일 삭제 */ + public void delete (String keyName) { + try { + // deleteObject(버킷명, 키값)으로 객체 삭제 + amazonS3.deleteObject(bucket, keyName); + } catch (AmazonServiceException e) { + log.error(e.toString()); + } + } + + /* 3. 파일의 presigned URL 반환 */ + public String getPresignedURL (String keyName) { + String preSignedURL = ""; + // presigned URL이 유효하게 동작할 만료기한 설정 (2분) + Date expiration = new Date(); + Long expTimeMillis = expiration.getTime(); + expTimeMillis += 1000 * 60 * 2; + expiration.setTime(expTimeMillis); + + try { + // presigned URL 발급 + GeneratePresignedUrlRequest generatePresignedUrlRequest = new GeneratePresignedUrlRequest(bucket, keyName) + .withMethod(HttpMethod.GET) + .withExpiration(expiration); + URL url = amazonS3.generatePresignedUrl(generatePresignedUrlRequest); + preSignedURL = url.toString(); + } catch (Exception e) { + log.error(e.toString()); + } + + return preSignedURL; } } diff --git a/src/main/java/farmSystem/closeUp/service/UserService.java b/src/main/java/farmSystem/closeUp/service/UserService.java index 2098582..8432ab1 100644 --- a/src/main/java/farmSystem/closeUp/service/UserService.java +++ b/src/main/java/farmSystem/closeUp/service/UserService.java @@ -6,14 +6,18 @@ import farmSystem.closeUp.config.redis.RedisUtils; import farmSystem.closeUp.config.security.SecurityUtils; import farmSystem.closeUp.domain.*; +import farmSystem.closeUp.dto.creator.request.PostCreatorSettingRequest; +import farmSystem.closeUp.dto.user.request.PostCreatorInfoRequest; import farmSystem.closeUp.dto.user.request.UserFollowRequest; import farmSystem.closeUp.dto.user.request.UserInfoRequest; import farmSystem.closeUp.dto.user.request.UserInterestRequest; import farmSystem.closeUp.dto.user.response.GetSearchCreatorResponse; import farmSystem.closeUp.dto.user.response.PostSignUpResponse; import farmSystem.closeUp.dto.user.response.PostTokenReissueResponse; +import farmSystem.closeUp.repository.InterestRepository; import farmSystem.closeUp.repository.UserInterestRepository; import farmSystem.closeUp.repository.follow.FollowRepository; +import farmSystem.closeUp.repository.platform.PlatformRepository; import farmSystem.closeUp.repository.user.UserRepository; import farmSystem.closeUp.repository.user.UserRepositoryImpl; import lombok.RequiredArgsConstructor; @@ -24,11 +28,14 @@ import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; import javax.naming.AuthenticationException; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.UUID; @Service @Slf4j @@ -41,6 +48,9 @@ public class UserService { private final UserInterestRepository userInterestRepository; private final RedisUtils redisUtils; private final JwtService jwtService; + private final InterestRepository interestRepository; + private final PlatformRepository platformRepository; + private final S3UploadService s3UploadService; @Transactional public List searchCreatorByKeyword(String keyword){ @@ -166,6 +176,7 @@ public PostSignUpResponse signUp(UserInfoRequest userInfoRequest){ return PostSignUpResponse.of(user.getUserId(), user.getUserRole()); } + @Transactional public PostSignUpResponse followBulk(UserFollowRequest userFollowRequest){ Long userId = null; try { @@ -190,6 +201,7 @@ public PostSignUpResponse followBulk(UserFollowRequest userFollowRequest){ return PostSignUpResponse.of(user.getUserId(), user.getUserRole()); } + @Transactional public PostSignUpResponse interestBulk(UserInterestRequest userInterestRequest){ Long userId = null; try { @@ -201,7 +213,7 @@ public PostSignUpResponse interestBulk(UserInterestRequest userInterestRequest){ // 만약 유저 존재 안할 경우 에러 User user = userRepository.findById(userId).orElseThrow(() -> new CustomException(Result.NOTFOUND_USER)); for (Long interestId : userInterestRequest.getGenreId()) { - Interest interest = Interest.builder().interestId(interestId).build(); + Interest interest = interestRepository.findById(interestId).orElseThrow(() -> new CustomException(Result.NOTFOUND_INTEREST)); UserInterest userInterest = UserInterest.builder() .user(user) .interest(interest) @@ -210,9 +222,86 @@ public PostSignUpResponse interestBulk(UserInterestRequest userInterestRequest){ } user.update(userId, UserRole.USER); - userRepository.save(user); +// userRepository.save(user); + + return PostSignUpResponse.of(user.getUserId(), user.getUserRole()); + } + + @Transactional + // 크리에이터 회원가입(정보 입력) , multipart 없으면? 어캐댐? + public PostSignUpResponse signUpCreator(PostCreatorInfoRequest postCreatorInfoRequest, MultipartFile multipartFile) { + Long userId = null; + try { + userId = getCurrentUserId(); + } catch (AuthenticationException e) { + throw new CustomException(Result.INVALID_ACCESS); + } + + // 만약 유저 존재 안할 경우 에러 + User user = userRepository.findById(userId).orElseThrow(() -> new CustomException(Result.NOTFOUND_USER)); + + // 닉네임 중복 체크 + if(userRepository.existsByNickName(postCreatorInfoRequest.getNickname())){ + new CustomException(Result.USERNAME_DUPLICATION); + } + + String fileName = "creator_profile/"+ UUID.randomUUID()+ multipartFile.getOriginalFilename(); + + try { + s3UploadService.saveFile(multipartFile, fileName); + } catch (IOException e) { + new CustomException(Result.FILE_UPLOAD_FAIL); + } + + user.setProfileImageUrl(fileName); + + // 회원가입 레벨 통과 + user.update( + userId, + postCreatorInfoRequest.getNickname(), + postCreatorInfoRequest.getAddress(), + postCreatorInfoRequest.getPhoneNumber(), + postCreatorInfoRequest.getGender(), + postCreatorInfoRequest.getBirthday(), + postCreatorInfoRequest.getProfileComment(), + UserRole.SIGNUP_CREATOR + ); + log.info("sign up"); return PostSignUpResponse.of(user.getUserId(), user.getUserRole()); } + @Transactional + // 플랫폼 설정 및 관심사 설정 + public PostSignUpResponse creatorSetting(PostCreatorSettingRequest postCreatorSettingRequest, MultipartFile multipartFile) { + User user; + user = getCurrentUser(); + Platform platform = platformRepository.findById(postCreatorSettingRequest.getPlatformId()).orElseThrow(() -> new CustomException(Result.NOTFOUND_PLATFORM)); + user.setPlatform(platform); // 크리에이터 플랫폼 설정 + + // 크리에이터 장르(관심사) 설정 + for (Long interestId :postCreatorSettingRequest.getInterestIds()) { + Interest interest = interestRepository.findById(interestId).orElseThrow(() -> new CustomException(Result.NOTFOUND_INTEREST)); + UserInterest userInterest = UserInterest.builder() + .user(user) + .interest(interest) + .build(); + userInterestRepository.save(userInterest); + } + + // 크리에이터 본인인증 + String fileName = "creator_verification/"+ UUID.randomUUID()+ multipartFile.getOriginalFilename(); + + + try { + s3UploadService.saveFile(multipartFile, fileName); + } catch (IOException e) { + new CustomException(Result.FILE_UPLOAD_FAIL); + } + + user.setVerificationImageUrl(fileName); + user.authorizeUser(UserRole.CREATOR); + + return PostSignUpResponse.of(user.getUserId(), user.getUserRole()); + } }