Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] 회원 가입 수정 #530

Merged
merged 2 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,14 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

@Slf4j
@RestController
Expand All @@ -75,12 +72,11 @@ public class MemberController {
@ApiResponse(responseCode = "409", description = "이미 가입된 계정이 있는 경우, 닉네임이 중복되는 경우", content = @Content),
@ApiResponse(responseCode = "500", description = "서버측 에러", content = @Content)
})
@PostMapping(value = "/join",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@PostMapping(value = "/join")
public BaseResponse<JwtDto> join(
@RequestPart(value = "reqDto") @Valid MemberRequest.JoinDto joinDto,
@RequestPart(required = false) MultipartFile multipartFile) {
JwtDto jwtDto = memberLoginService.join(joinDto, multipartFile);
@RequestBody @Valid MemberRequest.JoinDto joinDto,
@RequestParam(required = false) String fileKey) {
JwtDto jwtDto = memberLoginService.join(joinDto, fileKey);
return BaseResponse.success(JOIN_SUCCESS, jwtDto);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import com.jeju.nanaland.domain.common.data.Language;
import com.jeju.nanaland.domain.common.data.Status;
import com.jeju.nanaland.domain.common.entity.ImageFile;
import com.jeju.nanaland.domain.common.service.FileService;
import com.jeju.nanaland.domain.common.service.ImageFileService;
import com.jeju.nanaland.domain.member.dto.MemberRequest;
import com.jeju.nanaland.domain.member.dto.MemberResponse.MemberInfoDto;
Expand All @@ -26,8 +25,10 @@
import com.jeju.nanaland.global.exception.ConflictException;
import com.jeju.nanaland.global.exception.NotFoundException;
import com.jeju.nanaland.global.exception.UnauthorizedException;
import com.jeju.nanaland.global.file.data.FileCategory;
import com.jeju.nanaland.global.file.service.FileUploadService;
import com.jeju.nanaland.global.image_upload.dto.S3ImageDto;
import com.jeju.nanaland.global.util.JwtUtil;
import java.io.File;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
Expand All @@ -36,7 +37,6 @@
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

@Service
@RequiredArgsConstructor
Expand All @@ -49,19 +49,19 @@ public class MemberLoginService {
private final MemberConsentService memberConsentService;
private final ImageFileService imageFileService;
private final FcmTokenService fcmTokenService;
private final FileService fileService;
private final MemberProfileService memberProfileService;
private final FileUploadService fileUploadService;

/**
* 회원 가입
*
* @param joinDto 회원 가입 정보
* @param multipartFile 프로필 사진
* @param joinDto 회원 가입 정보
* @param fileKey 파일 키
* @return JWT
* @throws ConflictException provider, providerId로 이미 가입된 회원이 존재하는 경우
*/
@Transactional
public JwtDto join(MemberRequest.JoinDto joinDto, MultipartFile multipartFile) {
public JwtDto join(MemberRequest.JoinDto joinDto, String fileKey) {
Optional<Member> savedMember = memberRepository.findByProviderAndProviderId(
Provider.valueOf(joinDto.getProvider()),
joinDto.getProviderId());
Expand All @@ -73,7 +73,7 @@ public JwtDto join(MemberRequest.JoinDto joinDto, MultipartFile multipartFile) {

String nickname = determineNickname(joinDto);
validateNickname(nickname);
ImageFile profileImageFile = memberProfileService.saveRandomProfileImageFile();
ImageFile profileImageFile = createProfileImageFile(fileKey);
Member member = createMember(joinDto, profileImageFile, nickname);

// GUEST가 아닌 경우, 이용약관 저장
Expand All @@ -86,15 +86,26 @@ public JwtDto join(MemberRequest.JoinDto joinDto, MultipartFile multipartFile) {
fcmTokenService.createFcmToken(member, joinDto.getFcmToken());
}

// 비동기 처리
if (multipartFile != null && !multipartFile.isEmpty()) {
File convertedFile = fileService.convertMultipartFileToFile(multipartFile);
imageFileService.uploadMemberProfileImage(member.getId(), convertedFile);
}

return getJwtDto(member);
}

/**
* 프로필 사진 업로드 및 저장.
* 프로필 사진이 없는 경우엔, 랜덤 프로필 사진 저장
*
* @param fileKey 파일 키
* @return 저장된 이미지 파일 또는 랜덤 프로필 사진 파일
*/
private ImageFile createProfileImageFile(String fileKey) {
if (fileKey == null) {
return memberProfileService.saveRandomProfileImageFile();
}
fileUploadService.validateFileExtension(fileKey, FileCategory.MEMBER_PROFILE);
S3ImageDto s3ImageDto = fileUploadService.getCloudImageUrls(fileKey);
return imageFileService.saveS3ImageFile(s3ImageDto);
}


/**
* 닉네임 설정. GUEST 유형의 경우 UUID를 사용하여 랜덤 닉네임 생성. GUEST가 아닌 경우, 제공된 닉네임을 반환.
*
Expand Down Expand Up @@ -202,7 +213,6 @@ private JwtDto getJwtDto(Member member) {
* @param member 회원
* @throws NotFoundException 존재하는 회원 탈퇴 정보가 없는 경우
*/
@Transactional
public void updateMemberActive(Member member) {
if (member.getStatus().equals(Status.INACTIVE)) {
member.updateStatus(Status.ACTIVE);
Expand All @@ -219,7 +229,6 @@ public void updateMemberActive(Member member) {
* @param loginDto 로그인 정보
* @param member 회원
*/
@Transactional
public void updateLanguageDifferent(MemberRequest.LoginDto loginDto, Member member) {
Language language = Language.valueOf(loginDto.getLocale());
if (!member.getLanguage().equals(language)) {
Expand Down Expand Up @@ -316,7 +325,6 @@ public void withdrawal(MemberInfoDto memberInfoDto, MemberRequest.WithdrawalDto
/**
* 매일 0시 0분 0초에 실행되는 회원 탈퇴 스케줄러. 비활성화 후 3개월이 지난 회원 탈퇴 처리
*/
@Transactional
@Scheduled(cron = "0 0 0 * * *")
public void deleteWithdrawalMemberInfo() {
List<Member> members = memberRepository.findAllInactiveMember();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ public enum ErrorCode {
SELF_REPORT_NOT_ALLOWED(BAD_REQUEST, "본인을 신고하는 요청은 유효하지 않습니다."),
ALREADY_REPORTED(BAD_REQUEST, "이미 신고되었습니다."),
NO_NOTIFICATION_CONSENT(BAD_REQUEST, "알림 동의를 하지 않은 유저입니다."),
INVALID_FILE_SIZE(BAD_REQUEST, "파일 크기가 유효하지 않습니다."),
INVALID_FILE_EXTENSION_TYPE(BAD_REQUEST, "해당 카테고리에서 지원하지 않는 파일 형식입니다."),
NO_FILE_EXTENSION(BAD_REQUEST, "파일 확장자가 없습니다."),

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import com.jeju.nanaland.domain.common.annotation.EnumValid;
import com.jeju.nanaland.global.file.data.FileCategory;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
Expand All @@ -23,10 +25,12 @@ public static class InitCommandDto {

@NotNull
@Schema(description = "파일 크기")
@Max(30 * 1024 * 1024)
private Long fileSize;

@NotNull
@Schema(description = "파일 파트 개수")
@Min(1)
private int partCount;

@EnumValid(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import static com.jeju.nanaland.global.exception.ErrorCode.FILE_S3_NOT_FOUNE;
import static com.jeju.nanaland.global.exception.ErrorCode.FILE_UPLOAD_FAIL;
import static com.jeju.nanaland.global.exception.ErrorCode.INVALID_FILE_EXTENSION_TYPE;
import static com.jeju.nanaland.global.exception.ErrorCode.INVALID_FILE_SIZE;
import static com.jeju.nanaland.global.exception.ErrorCode.NO_FILE_EXTENSION;

import com.amazonaws.HttpMethod;
Expand All @@ -29,7 +28,6 @@
import com.jeju.nanaland.global.image_upload.dto.S3VideoDto;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.net.URL;
import java.time.LocalDate;
import java.time.LocalDateTime;
Expand All @@ -51,7 +49,6 @@ public class FileUploadService {

private static final int MAX_IMAGE_COUNT = 5;
private static final int PRESIGNEDURL_EXPIRATION = 30;
private static final long MAX_FILE_SIZE = 30 * 1024 * 1024L;
private final AmazonS3 amazonS3;
private final AmazonS3Client amazonS3Client;
@Value("${cloud.aws.cloudfront.domain}")
Expand All @@ -68,9 +65,6 @@ public class FileUploadService {
private String claimReportDirectory;

public FileResponse.InitResultDto uploadInit(FileRequest.InitCommandDto initCommandDto) {
// 파일 크기 유효성 검사
validateFileSize(initCommandDto.getFileSize());

// 파일 형식 유효성 검사
String contentType = validateFileExtension(initCommandDto.getOriginalFileName(),
FileCategory.valueOf(initCommandDto.getFileCategory()));
Expand Down Expand Up @@ -121,12 +115,6 @@ public FileResponse.InitResultDto uploadInit(FileRequest.InitCommandDto initComm
}
}

private void validateFileSize(@NotNull Long fileSize) {
if (fileSize > MAX_FILE_SIZE) {
throw new BadRequestException(INVALID_FILE_SIZE.getMessage());
}
}

/**
* 파일 확장자 유효성 확인
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import com.jeju.nanaland.domain.common.data.Language;
import com.jeju.nanaland.domain.common.data.Status;
import com.jeju.nanaland.domain.common.entity.ImageFile;
import com.jeju.nanaland.domain.common.service.ImageFileService;
import com.jeju.nanaland.domain.member.dto.MemberRequest;
import com.jeju.nanaland.domain.member.dto.MemberRequest.JoinDto;
import com.jeju.nanaland.domain.member.dto.MemberResponse.MemberInfoDto;
Expand All @@ -32,6 +33,7 @@
import com.jeju.nanaland.global.exception.ErrorCode;
import com.jeju.nanaland.global.exception.NotFoundException;
import com.jeju.nanaland.global.exception.UnauthorizedException;
import com.jeju.nanaland.global.file.service.FileUploadService;
import com.jeju.nanaland.global.util.JwtUtil;
import java.time.LocalDate;
import java.util.ArrayList;
Expand All @@ -47,8 +49,6 @@
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile;


@ExtendWith(MockitoExtension.class)
Expand All @@ -71,6 +71,10 @@ class MemberLoginServiceTest {
private FcmTokenService fcmTokenService;
@Mock
private MemberProfileService memberProfileService;
@Mock
private FileUploadService fileUploadService;
@Mock
private ImageFileService imageFileService;
@InjectMocks
private MemberLoginService memberLoginService;

Expand Down Expand Up @@ -243,26 +247,22 @@ void joinSuccess_multipartFileExists() {
// given: 프로필 사진이 있는 경우
Language language = Language.KOREAN;
Member member = createMember(language, joinDto);
MultipartFile multipartFile = new MockMultipartFile("file", "test.jpg", "image/jpeg",
new byte[0]);
String fileKey = "test/1.png";
doReturn(Optional.empty())
.when(memberRepository).findByProviderAndProviderId(any(Provider.class), any(String.class));
doReturn(Optional.empty()).when(memberRepository).findByNickname(any(String.class));
doReturn(imageFile).when(memberProfileService).saveRandomProfileImageFile();
doReturn(member).when(memberRepository).save(any(Member.class));
doReturn("accessToken").when(jwtUtil).createAccessToken(any(String.class), anySet());
doReturn("refreshToken").when(jwtUtil).createRefreshToken(any(String.class), anySet());

// when: 회원 가입
JwtDto result = memberLoginService.join(joinDto, multipartFile);
JwtDto result = memberLoginService.join(joinDto, fileKey);

// then: JWT 생성 확인, 이용약관 생성 확인, 프로필 사진 확인
assertThat(result).isNotNull();
assertThat(result.getAccessToken()).isEqualTo("accessToken");
assertThat(result.getRefreshToken()).isEqualTo("refreshToken");
verify(memberConsentService).createMemberConsents(any(Member.class), anyList());
verify(memberRepository).save(argThat(savedMember ->
savedMember.getProfileImageFile().equals(imageFile)));
}

@Test
Expand All @@ -273,27 +273,22 @@ void joinSuccess_fcmTokenExists(){
JoinDto joinDto2 = createJoinDto("GOOGLE");
joinDto2.setFcmToken("fcmToken");
Member member = createMember(language, joinDto2);
MultipartFile multipartFile = new MockMultipartFile("file", "test.jpg", "image/jpeg",
new byte[0]);
doReturn(Optional.empty())
.when(memberRepository).findByProviderAndProviderId(any(Provider.class), any(String.class));
doReturn(Optional.empty()).when(memberRepository).findByNickname(any(String.class));
doReturn(imageFile).when(memberProfileService).saveRandomProfileImageFile();
doReturn(member).when(memberRepository).save(any(Member.class));
doReturn("accessToken").when(jwtUtil).createAccessToken(any(String.class), anySet());
doReturn("refreshToken").when(jwtUtil).createRefreshToken(any(String.class), anySet());

// when: 회원 가입
JwtDto result = memberLoginService.join(joinDto2, multipartFile);
JwtDto result = memberLoginService.join(joinDto2, null);

// then: JWT 생성 확인, 이용약관 생성 확인, fcmToken 생성 확인, 프로필 사진 확인
// then: JWT 생성 확인, 이용약관 생성 확인, fcmToken 생성 확인
assertThat(result).isNotNull();
assertThat(result.getAccessToken()).isEqualTo("accessToken");
assertThat(result.getRefreshToken()).isEqualTo("refreshToken");
verify(memberConsentService).createMemberConsents(any(Member.class), anyList());
verify(fcmTokenService).createFcmToken(any(Member.class), any(String.class));
verify(memberRepository).save(argThat(savedMember ->
savedMember.getProfileImageFile().equals(imageFile)));
}
}

Expand Down
Loading