From f1a1ce75400a64b84198fa4a8228d2fb8b92b318 Mon Sep 17 00:00:00 2001 From: minahkim03 Date: Tue, 26 Nov 2024 22:06:03 +0900 Subject: [PATCH] =?UTF-8?q?:sparkles:=20=ED=9A=8C=EC=9B=90=20=ED=83=88?= =?UTF-8?q?=ED=87=B4=20soft=20delete=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/controller/MemberController.java | 31 +++--- .../domain/member/dto/MemberRequestDto.java | 27 +++-- .../simter/domain/member/entity/Member.java | 7 +- .../domain/member/service/MemberService.java | 98 +++++-------------- 4 files changed, 66 insertions(+), 97 deletions(-) diff --git a/src/main/java/com/simter/domain/member/controller/MemberController.java b/src/main/java/com/simter/domain/member/controller/MemberController.java index 439469a..d77bd5f 100644 --- a/src/main/java/com/simter/domain/member/controller/MemberController.java +++ b/src/main/java/com/simter/domain/member/controller/MemberController.java @@ -16,6 +16,7 @@ import io.swagger.v3.oas.annotations.Operation; import jakarta.mail.MessagingException; import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; @@ -23,39 +24,41 @@ 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.RestController; @RequiredArgsConstructor @RestController +@RequestMapping("/api/v1") public class MemberController { private final MemberService memberService; private final JwtTokenProvider jwtTokenProvider; @Operation(summary = "회원가입 API", description = "이메일, 로그인 타입, 비밀번호, 닉네임을 저장해 회원가입하는 API") - @PostMapping("/api/v1/register/general") - public ApiResponse register(@RequestBody RegisterDto registerDto) { + @PostMapping("/register/general") + public ApiResponse register(@Valid @RequestBody RegisterDto registerDto) { memberService.register(registerDto); return ApiResponse.onSuccess(null); } @Operation(summary = "회원가입 API", description = "이메일, 로그인 타입, 비밀번호, 닉네임을 저장해 회원가입하는 API") - @PostMapping("/api/v1/register/social") - public ApiResponse registerSocial(@RequestBody SocialRegisterDto socialRegisterDto) { + @PostMapping("/register/social") + public ApiResponse registerSocial(@Valid @RequestBody SocialRegisterDto socialRegisterDto) { memberService.register(socialRegisterDto); return ApiResponse.onSuccess(null); } @Operation(summary = "이메일 중복체크 API", description = "이메일이 이미 가입되어 있는지 조회하는 API") - @GetMapping("/api/v1/register/general/check") + @GetMapping("/register/general/check") public ApiResponse checkRegister(@RequestParam String email) { EmailValidationResponseDto emailValidationResponseDto = memberService.validateDuplicate(email); return ApiResponse.onSuccess(emailValidationResponseDto); } @Operation(summary = "일반 로그인 API", description = "이메일, 비밀번호를 입력하여 토큰을 생성하는 API") - @PostMapping("/api/v1/login/general") + @PostMapping("/login/general") public ApiResponse login(@RequestBody LoginRequestDto loginDto) { String email = loginDto.getEmail(); String password = loginDto.getPassword(); @@ -64,7 +67,7 @@ public ApiResponse login(@RequestBody LoginRequestDto loginDto } @Operation(summary = "로그아웃 API", description = "리프레시토큰을 파괴하는 API") - @DeleteMapping("/api/v1/logout") + @DeleteMapping("/logout") public ApiResponse logout(HttpServletRequest request) { String token = jwtTokenProvider.resolveToken(request).getAccessToken(); memberService.logout(token); @@ -72,7 +75,7 @@ public ApiResponse logout(HttpServletRequest request) { } @Operation(summary = "토큰 재발급 API", description = "리프레시토큰과 액세스 토큰을 재발급하는 API") - @GetMapping("/api/v1/reissue") + @GetMapping("/reissue") public ApiResponse reissue(HttpServletRequest request) { JwtTokenDto token = jwtTokenProvider.resolveToken(request); String email = jwtTokenProvider.getEmail(token.getRefreshToken()); @@ -81,7 +84,7 @@ public ApiResponse reissue(HttpServletRequest request) { } @Operation(summary = "비밀번호 재발송 API", description = "비밃번호를 재생성해서 유저에게 메일 발송하는 API") - @PatchMapping("/api/v1/login/temp-pw") + @PatchMapping("/login/temp-pw") public ApiResponse tempPw(@RequestBody PasswordReissueDto passwordReissueDto) throws MessagingException { memberService.tempPw(passwordReissueDto.getEmail()); @@ -89,7 +92,7 @@ public ApiResponse tempPw(@RequestBody PasswordReissueDto passwordReissueD } @Operation(summary = "메인화면 API", description = "닉네임, 비행기 유무, 편지 알림여부를 보내는 API") - @GetMapping("/api/v1/main") + @GetMapping("/main") public ApiResponse main(HttpServletRequest request) { JwtTokenDto token = jwtTokenProvider.resolveToken(request); String email = jwtTokenProvider.getEmail(token.getAccessToken()); @@ -98,7 +101,7 @@ public ApiResponse main(HttpServletRequest request) { } @Operation(summary = "새 편지 알림 끄기 API", description = "새 편지 알림을 끄는 API") - @PatchMapping("/api/v1/main/update-mail-alert") + @PatchMapping("/main/update-mail-alert") public ApiResponse turnOffMailAlert(HttpServletRequest request, @RequestBody String mailAlert) { JwtTokenDto token = jwtTokenProvider.resolveToken(request); String email = jwtTokenProvider.getEmail(token.getAccessToken()); @@ -107,7 +110,7 @@ public ApiResponse turnOffMailAlert(HttpServletRequest request, @RequestBo } @Operation(summary = "닉네임 변경 API", description = "닉네임을 변경하는 API") - @PatchMapping("/api/v1/setting/nickname") + @PatchMapping("/setting/nickname") public ApiResponse changeNickname(HttpServletRequest request, @RequestBody NicknameChangeDto nicknameChangeDto) { JwtTokenDto token = jwtTokenProvider.resolveToken(request); @@ -117,7 +120,7 @@ public ApiResponse changeNickname(HttpServletRequest request, @RequestBody } @Operation(summary = "비밀번호 변경 API", description = "비밀번호를 변경하는 API") - @PatchMapping("/api/v1/setting/password") + @PatchMapping("/setting/password") public ApiResponse changePassword(HttpServletRequest request, @RequestBody PasswordChangeDto passwordChangeDto) { JwtTokenDto token = jwtTokenProvider.resolveToken(request); @@ -127,7 +130,7 @@ public ApiResponse changePassword(HttpServletRequest request, @RequestBody } @Operation(summary = "회원 탈퇴 API", description = "상태를 비활성화로 바꾸고 날짜를 저장하는 API") - @PatchMapping("/api/v1/setting/delete-account") + @PatchMapping("/setting/delete-account") public ApiResponse deleteAccount(HttpServletRequest request) { JwtTokenDto token = jwtTokenProvider.resolveToken(request); String email = jwtTokenProvider.getEmail(token.getAccessToken()); diff --git a/src/main/java/com/simter/domain/member/dto/MemberRequestDto.java b/src/main/java/com/simter/domain/member/dto/MemberRequestDto.java index 79fd0df..2561515 100644 --- a/src/main/java/com/simter/domain/member/dto/MemberRequestDto.java +++ b/src/main/java/com/simter/domain/member/dto/MemberRequestDto.java @@ -1,5 +1,8 @@ package com.simter.domain.member.dto; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Builder; @@ -14,13 +17,17 @@ public class MemberRequestDto { @AllArgsConstructor(access = AccessLevel.PROTECTED) @NoArgsConstructor(access = AccessLevel.PROTECTED) public static class RegisterDto { - @NotNull + @NotBlank(message = "이메일은 필수 항목입니다.") + @Email(message = "유효한 이메일 주소를 입력해주세요.") private String email; - @NotNull + @NotBlank(message = "비밀번호는 필수 항목입니다.") + @Pattern(regexp = "^(?=.*[a-zA-Z])(?=.*\\d)(?=.*[!@#$%^&*()_+\\-=\\[\\]{};':\"\\\\|,.<>\\/?]).{8,16}$", + message = "비밀번호는 영문자, 숫자, 특수문자를 포함하여 8~16자로 입력해주세요.") private String password; - @NotNull + @NotBlank(message = "닉네임은 필수 항목입니다.") + @Pattern(regexp = "^[가-힣a-zA-Z]{1,10}$", message = "닉네임은 한글과 영문자만 사용하여 1~10자로 입력해주세요.") private String nickname; @NotNull @@ -32,10 +39,10 @@ public static class RegisterDto { @AllArgsConstructor(access = AccessLevel.PROTECTED) @NoArgsConstructor(access = AccessLevel.PROTECTED) public static class SocialRegisterDto { - @NotNull private String email; - @NotNull + @NotBlank(message = "닉네임은 필수 항목입니다.") + @Pattern(regexp = "^[가-힣a-zA-Z]{1,10}$", message = "닉네임은 한글과 영문자만 사용하여 1~10자로 입력해주세요.") private String nickname; @NotNull @@ -70,7 +77,9 @@ public static class LoginRequestDto { @AllArgsConstructor(access = AccessLevel.PROTECTED) @NoArgsConstructor(access = AccessLevel.PROTECTED) public static class NicknameChangeDto { - @NotNull String nickname; + @NotBlank(message = "닉네임은 필수 항목입니다.") + @Pattern(regexp = "^[가-힣a-zA-Z]{1,10}$", message = "닉네임은 한글과 영문자만 사용하여 1~10자로 입력해주세요.") + String nickname; } @Builder @@ -79,7 +88,11 @@ public static class NicknameChangeDto { @NoArgsConstructor(access = AccessLevel.PROTECTED) public static class PasswordChangeDto { @NotNull String oldPassword; - @NotNull String newPassword; + + @NotBlank(message = "비밀번호는 필수 항목입니다.") + @Pattern(regexp = "^(?=.*[a-zA-Z])(?=.*\\d)(?=.*[!@#$%^&*()_+\\-=\\[\\]{};':\"\\\\|,.<>\\/?]).{8,16}$", + message = "비밀번호는 영문자, 숫자, 특수문자를 포함하여 8~16자로 입력해주세요.") + String newPassword; } @Builder diff --git a/src/main/java/com/simter/domain/member/entity/Member.java b/src/main/java/com/simter/domain/member/entity/Member.java index bcb1d87..8672c6e 100644 --- a/src/main/java/com/simter/domain/member/entity/Member.java +++ b/src/main/java/com/simter/domain/member/entity/Member.java @@ -5,7 +5,6 @@ import java.util.Collection; import java.util.List; import lombok.*; -import org.hibernate.annotations.ColumnDefault; import java.time.LocalDateTime; import org.springframework.security.core.GrantedAuthority; @@ -114,8 +113,8 @@ public void setNickname(String nickname) { this.nickname = nickname; } - public void changeStatusToInactive() { - this.status = false; + public void changeStatus() { + this.status = !this.status; } public void setInactiveDate(LocalDateTime inactiveDate) { @@ -125,4 +124,6 @@ public void setInactiveDate(LocalDateTime inactiveDate) { public void setChatbot(String chatbot) { this.chatbot = chatbot; } + + public void setLoginType(String loginType) {this.loginType = loginType;} } \ No newline at end of file diff --git a/src/main/java/com/simter/domain/member/service/MemberService.java b/src/main/java/com/simter/domain/member/service/MemberService.java index 551d8c2..c83b6cd 100644 --- a/src/main/java/com/simter/domain/member/service/MemberService.java +++ b/src/main/java/com/simter/domain/member/service/MemberService.java @@ -3,14 +3,6 @@ import com.simter.apiPayload.code.status.ErrorStatus; import com.simter.apiPayload.exception.handler.ErrorHandler; import com.simter.config.JwtTokenProvider; -import com.simter.domain.airplane.repository.AirplaneRepository; -import com.simter.domain.calendar.entity.Calendars; -import com.simter.domain.calendar.repository.CalendarsRepository; -import com.simter.domain.chatbot.entity.CounselingLog; -import com.simter.domain.chatbot.repository.ChatbotRepository; -import com.simter.domain.chatbot.repository.CounselingLogRepository; -import com.simter.domain.chatbot.repository.SolutionRepository; -import com.simter.domain.mail.repository.MailRepository; import com.simter.domain.member.converter.MemberConverter; import com.simter.domain.member.dto.JwtTokenDto; import com.simter.domain.member.dto.MainDto; @@ -24,11 +16,9 @@ import jakarta.mail.MessagingException; import jakarta.mail.internet.MimeMessage; import jakarta.transaction.Transactional; -import java.util.Calendar; -import java.util.List; +import java.time.LocalDateTime; import java.util.Optional; import java.util.Random; -import java.util.regex.Pattern; import lombok.RequiredArgsConstructor; import org.apache.commons.lang3.RandomStringUtils; import org.springframework.mail.javamail.JavaMailSender; @@ -49,12 +39,6 @@ public class MemberService extends DefaultOAuth2UserService { private final AuthenticationManager authenticationManager; private final JwtTokenProvider jwtTokenProvider; private final JavaMailSender mailSender; - private final MailRepository mailRepository; - private final AirplaneRepository airplaneRepository; - private final CalendarsRepository calendarsRepository; - private final CounselingLogRepository counselingLogRepository; - private final SolutionRepository solutionRepository; - private final ChatbotRepository chatbotRepository; //회원가입 public void register(RegisterDto registerRequestDto) { @@ -62,7 +46,6 @@ public void register(RegisterDto registerRequestDto) { String password = registerRequestDto.getPassword(); String nickname = registerRequestDto.getNickname(); String loginType = registerRequestDto.getLoginType(); - validateRegister(email, password, nickname); String encryptedPassword = encoder.encode(password); RegisterDto newRegisterDto = RegisterDto.builder() .email(email) @@ -73,6 +56,9 @@ public void register(RegisterDto registerRequestDto) { Member member = MemberConverter.convertToEntity(newRegisterDto); if (!memberRepository.existsByEmail(email)) { memberRepository.save(member); + } else { + Member orgMember = memberRepository.findByEmail(email).orElseThrow(); + registerOrgMember(orgMember, newRegisterDto, "general"); } } @@ -82,7 +68,6 @@ public void register(SocialRegisterDto socialRegisterDto) { String nickname = socialRegisterDto.getNickname(); String loginType = socialRegisterDto.getLoginType(); JwtTokenDto token = socialRegisterDto.getToken(); - validateNickname(nickname); RegisterDto newRegisterDto = RegisterDto.builder() .email(email) .password("") @@ -93,7 +78,24 @@ public void register(SocialRegisterDto socialRegisterDto) { if (!memberRepository.existsByEmail(email)) { member.setRefreshToken(token.getRefreshToken()); memberRepository.save(member); + } else { + Member orgMember = memberRepository.findByEmail(email).orElseThrow(); + registerOrgMember(orgMember, newRegisterDto, "social"); + } + } + + //기존 회원 가입 + public void registerOrgMember(Member member, RegisterDto registerDto, String registerType) { + member.setNickname(registerDto.getNickname()); + if (registerType.equals("general")) { + member.setPassword(registerDto.getPassword()); + } else { + member.setPassword(""); } + member.setLoginType(registerDto.getLoginType()); + member.changeStatus(); + member.setInactiveDate(null); + memberRepository.saveAndFlush(member); } //로그인 @@ -182,7 +184,6 @@ public void turnOffMailAlert(String email, String mailAlert){ public void changeNickname(String email, String nickname) { Member member = memberRepository.findByEmail(email) .orElseThrow(() -> new ErrorHandler(ErrorStatus.MEMBER_NOT_FOUND)); - validateNickname(nickname); member.setNickname(nickname); memberRepository.save(member); } @@ -196,7 +197,6 @@ public void changePassword(String email, PasswordChangeDto passwordChangeDto) { if (!encoder.matches(oldPw, member.getPassword())) { throw new ErrorHandler(ErrorStatus.WRONG_PASSWORD); } - validatePassword(newPw); member.setPassword(encoder.encode(newPw)); memberRepository.save(member); } @@ -205,20 +205,15 @@ public void changePassword(String email, PasswordChangeDto passwordChangeDto) { public void deleteAccount(String email) { Member member = memberRepository.findByEmail(email) .orElseThrow(() -> new ErrorHandler(ErrorStatus.MEMBER_NOT_FOUND)); - deleteAll(member); - } - - //닉네임, 비밀번호, 이메일 유효 검증 - public void validateRegister(String email, String password, String nickname) { - validateEmail(email); - validatePassword(password); - validateNickname(nickname); + member.changeStatus(); + member.setInactiveDate(LocalDateTime.now()); + memberRepository.saveAndFlush(member); } //이메일 중복 조회 public EmailValidationResponseDto validateDuplicate(String email) { Optional findMember = memberRepository.findByEmail(email); - if (findMember.isEmpty()) { + if (findMember.isEmpty() || !findMember.get().isStatus()) { return EmailValidationResponseDto.builder() .isValid(true) .build(); @@ -228,47 +223,4 @@ public EmailValidationResponseDto validateDuplicate(String email) { .build(); } } - - //이메일 형식 확인 - public void validateEmail(String email) { - if (!Pattern.matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,6}$", email)) { - throw new ErrorHandler(ErrorStatus.INVALID_EMAIL_FORMAT); - } - } - - //비밀번호 형식 확인 - public void validatePassword(String password) { - if (!Pattern.matches("^(?=.*[a-zA-Z])(?=.*\\d)(?=.*[!@#$%^&*()_+\\-=\\[\\]{};':\"\\\\|,.<>\\/?]).{8,16}$", password)) { - throw new ErrorHandler(ErrorStatus.INVALID_PASSWORD_FORMAT); - } - } - - //닉네임 형식 확인 - public void validateNickname(String nickname) { - if (!Pattern.matches("^[가-힣a-zA-Z]{1,10}$", nickname)) { - throw new ErrorHandler(ErrorStatus.INVALID_NICKNAME_FORMAT); - } - } - - //회원 탈퇴 시 관련 데이터 모두 삭제 - public void deleteAll(Member member) { - mailRepository.deleteByMember(member); - airplaneRepository.deleteByReceiverId(member); - airplaneRepository.deleteBySenderId(member); - List calendars = calendarsRepository.findByUserId(member); - for (Calendars calendar : calendars) { - List logs = counselingLogRepository.findByCalendars(calendar); - - for (CounselingLog log : logs) { - solutionRepository.deleteByCounselingLogId(log.getId()); - chatbotRepository.deleteByCounselingLogId(log.getId()); - } - - counselingLogRepository.deleteByUserId(member); - } - calendarsRepository.deleteByUserId(member); - memberRepository.delete(member); - } - - } \ No newline at end of file