From 17272dd48ee4083e9de6c27f601889834a3744e9 Mon Sep 17 00:00:00 2001 From: Maybaba Date: Wed, 7 Aug 2024 20:37:32 +0900 Subject: [PATCH 01/13] # sugn up, sign in transfer to jpa --- .../auth/controller/EmailController.java | 190 +++++----- .../domain/auth/dto/EmailCodeDto.java | 15 +- .../domain/auth/dto/EmailCodeStoreDto.java | 4 +- .../domain/auth/dto/EmailCustomerDto.java | 6 +- .../domain/auth/entity/EmailVerification.java | 27 +- .../domain/auth/mapper/EmailMapper.java | 17 +- .../auth/repository/EmailRepository.java | 49 +++ .../domain/auth/security/TokenProvider.java | 26 +- .../auth/security/filter/AuthJwtFilter.java | 1 + .../domain/auth/service/EmailService.java | 330 +++++++++--------- .../domain/auth/service/UserService.java | 39 ++- .../controller/CustomerController.java | 34 +- .../domain/customer/entity/Customer.java | 4 +- .../repository/CustomerRepository.java | 21 ++ .../customer/service/CustomerService.java | 134 ++----- .../store/controller/StoreController.java | 116 +++--- .../store/repository/StoreRepository.java | 13 + .../domain/store/service/StoreService.java | 155 ++++---- .../interceptor/AutoLoginInterceptor.java | 2 +- 19 files changed, 607 insertions(+), 576 deletions(-) create mode 100644 src/main/java/org/nmfw/foodietree/domain/auth/repository/EmailRepository.java diff --git a/src/main/java/org/nmfw/foodietree/domain/auth/controller/EmailController.java b/src/main/java/org/nmfw/foodietree/domain/auth/controller/EmailController.java index 69416e80..2c67bdf0 100644 --- a/src/main/java/org/nmfw/foodietree/domain/auth/controller/EmailController.java +++ b/src/main/java/org/nmfw/foodietree/domain/auth/controller/EmailController.java @@ -33,18 +33,17 @@ public class EmailController { private final EmailService emailService; private final UserService userService; - private final EmailMapper emailMapper; private final TokenProvider tokenProvider; - @GetMapping("/send-reset-email") - public String sendVerificationCode(@RequestParam String to, String userType) { - try { - emailService.sendResetVerificationCode(to, userType, "reset"); - return "Password reset email sent successfully"; - } catch (Exception e) { - return "Failed to send password reset email: " + e.getMessage(); - } - } +// @GetMapping("/send-reset-email") +// public String sendVerificationCode(@RequestParam String to, String userType) { +// try { +// emailService.sendResetVerificationCode(to, userType, "reset"); +// return "Password reset email sent successfully"; +// } catch (Exception e) { +// return "Failed to send password reset email: " + e.getMessage(); +// } +// } /* @GetMapping("/verify-reset-code") @@ -59,18 +58,18 @@ public String verifyResetCode(@RequestParam String email, @RequestParam String c */ // 인증 코드 전송 - @PostMapping("/sendVerificationCode") - public ResponseEntity sendVerificationCode(@RequestBody Map request) { - String email = request.get("email"); - String purpose = request.get("purpose"); - String userType = request.get("userType"); - try { - emailService.sendResetVerificationCode(email, purpose, userType); - return ResponseEntity.ok("Verification code sent"); - } catch (MessagingException e) { - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Failed to send verification code"); - } - } +// @PostMapping("/sendVerificationCode") +// public ResponseEntity sendVerificationCode(@RequestBody Map request) { +// String email = request.get("email"); +// String purpose = request.get("purpose"); +// String userType = request.get("userType"); +// try { +// emailService.sendResetVerificationCode(email, purpose, userType); +// return ResponseEntity.ok("Verification code sent"); +// } catch (MessagingException e) { +// return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Failed to send verification code"); +// } +// } // 인증 리다이렉션 링크 메일 전송 @PostMapping("/sendVerificationLink") @@ -97,96 +96,100 @@ public ResponseEntity sendVerificationLink(@RequestBody Map r @PostMapping("/verifyEmail") public ResponseEntity verifyEmail(@RequestBody Map request) { - log.info("Request Data: {}", request); + log.info("Request Data: {}", request); - String token = request.get("token"); - String refreshToken = request.get("refreshToken"); + String token = request.get("token"); + String refreshToken = request.get("refreshToken"); - log.info("access token 있는지 확인 {}", token); - log.info("refresh token 있는지 확인 {}", refreshToken); + log.info("access token 있는지 확인 {}", token); + log.info("refresh token 있는지 확인 {}", refreshToken); - try { - // access token 유효성 검사 - TokenUserInfo accessTokenUserInfo = tokenProvider.validateAndGetTokenInfo(token); - - log.info("Email extracted from token: {}", accessTokenUserInfo.getEmail()); - log.info("user Role (type) extracted from token: {}", accessTokenUserInfo.getRole()); - log.info("user access expire date : {}", accessTokenUserInfo.getTokenExpireDate().toString()); - - String email = accessTokenUserInfo.getEmail(); - String userType = accessTokenUserInfo.getRole(); - - // 이메일 dto 정보를 데이터베이스에서 조회 - EmailCodeDto emailCodeDto = emailMapper.findOneByEmail(email); - log.info("EmailCodeDto retrieved from database: {}", emailCodeDto); - - // 이메일 정보가 인증 테이블에 있을 경우 - if (emailCodeDto != null) { - emailCodeDto.setUserType(userType); - emailCodeDto.setEmailVerified(true); - - // 이메일 인증이 완료되지 않은 경우 - 실제 회원가입이 되지 않은 경우 - if (!emailCodeDto.isEmailVerified()) { - // 이메일 재전송 페이지로 리다이렉션 - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(Map.of("success", false, "message", "Email link is not verified! Please resend verification email.")); - } - - //실제 회원가입(테이블에 저장)이 되지 않은 경우 false - if (!(userService.findByEmail(emailCodeDto))) { - - log.info("실제 회원가입(테이블에 저장)이 되지 않은 경우 {}", emailCodeDto); - emailMapper.update(emailCodeDto); // 인증정보 true 업데이트 - return userService.saveUserInfo(emailCodeDto); - - } else { - // 실제 회원가입이 되어있는 경우, 로그인 하는데 access token 기간이 종료된 경우 - // 만료 기한 access, refresh 업데이트 - log.info("email이 실제 회원가입 되어있는 경우 dto {}", emailCodeDto); - return userService.updateUserInfo(emailCodeDto); - } - } + try { + // access token 유효성 검사 + TokenUserInfo accessTokenUserInfo = tokenProvider.validateAndGetTokenInfo(token); - // email table에 인증정보가 없을 경우 즉, access token이 만료되었을경우 - // 인증 정보는 상관없이 access token이 만료되었을 경우 - } catch (Exception e) { - log.info("access token 의 기한이 만료되었거나 위조되었습니다."); - log.info("JWT parsing error: {}", e.getMessage()); + log.info("Email extracted from token: {}", accessTokenUserInfo.getEmail()); + log.info("user Role (type) extracted from token: {}", accessTokenUserInfo.getRole()); + log.info("user access expire date : {}", accessTokenUserInfo.getTokenExpireDate().toString()); - try { - // 리프레시 토큰의 만료일자를 확인 - 서버에도 리프레시 토큰 저장 - TokenUserInfo refreshTokenUserInfo = tokenProvider.validateAndGetRefreshTokenInfo(refreshToken); + String email = accessTokenUserInfo.getEmail(); + String userType = accessTokenUserInfo.getRole(); - log.info("token provider tokenUserInfo로 리프레시토큰의 만료일자 확인하기 위한 값 : {} ", refreshTokenUserInfo); + // 이메일 dto 정보를 데이터베이스에서 조회 + EmailCodeDto emailCodeDto = emailService.findOneByEmail(email); - String email = refreshTokenUserInfo.getEmail(); - String userType = refreshTokenUserInfo.getRole(); + log.info("EmailCodeDto retrieved from database: {}", emailCodeDto); + + // 이메일 정보가 인증 테이블에 있을 경우 + if (emailCodeDto != null) { + emailCodeDto.setUserType(userType); + emailCodeDto.setEmailVerified(true); + + // 이메일 인증이 완료되지 않은 경우 - 실제 회원가입이 되지 않은 경우 + if (!emailService.existsByEmailInCustomerOrStore(emailCodeDto.getEmail())) { + + // 이메일 재전송 페이지로 리다이렉션 + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(Map.of("success", false, "message", "Email link is not verified! Please resend verification email.")); + } - LocalDateTime refreshTokenExpiryDate = userService.getRefreshTokenExpiryDate(email, userType); + //실제 회원가입(테이블에 저장)이 되지 않은 경우 false + if (!(userService.findByEmail(emailCodeDto))) { - log.info("리이이이이이이이이프레에에에에에시토오오오크크ㅡ으응으으응으ㅡㄴ!!! 서버 만료일자 {}", refreshTokenExpiryDate); + log.info("실제 회원가입(테이블에 저장)이 되지 않은 경우 {}", emailCodeDto); - if (refreshTokenExpiryDate == null || refreshTokenExpiryDate.isBefore(LocalDateTime.now())) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Map.of("success", false, "message", "Refresh token expired")); - } + emailService.updateEmailVerification(emailCodeDto); // 인증정보 true 업데이트 - // 새로운 액세스 토큰 발급 - EmailCodeDto emailCodeDto = EmailCodeDto.builder() - .email(email) - .userType(userType) - .build(); + return userService.saveUserInfo(emailCodeDto); - // Call updateUserInfo and capture its response + } else { + // 실제 회원가입이 되어있는 경우, 로그인 하는데 access token 기간이 종료된 경우 + // 만료 기한 access, refresh 업데이트 + log.info("email이 실제 회원가입 되어있는 경우 dto {}", emailCodeDto); return userService.updateUserInfo(emailCodeDto); + } + } + + // email table에 인증정보가 없을 경우 즉, access token이 만료되었을경우 + // 인증 정보는 상관없이 access token이 만료되었을 경우 + } catch (Exception e) { + log.info("access token 의 기한이 만료되었거나 위조되었습니다."); + log.info("JWT parsing error: {}", e.getMessage()); + + try { + // 리프레시 토큰의 만료일자를 확인 - 서버에도 리프레시 토큰 저장 + TokenUserInfo refreshTokenUserInfo = tokenProvider.validateAndGetRefreshTokenInfo(refreshToken); + + log.info("token provider tokenUserInfo로 리프레시토큰의 만료일자 확인하기 위한 값 : {} ", refreshTokenUserInfo); + + String email = refreshTokenUserInfo.getEmail(); + String userType = refreshTokenUserInfo.getRole(); + LocalDateTime refreshTokenExpiryDate = userService.getRefreshTokenExpiryDate(email, userType); - } catch (Exception ex) { - log.error("Refresh token parsing error: {}", ex.getMessage()); - return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Map.of("success", false, "message", "Invalid refresh token")); + log.info("리이이이이이이이이프레에에에에에시토오오오크크ㅡ으응으으응으ㅡㄴ!!! 서버 만료일자 {}", refreshTokenExpiryDate); + + if (refreshTokenExpiryDate == null || refreshTokenExpiryDate.isBefore(LocalDateTime.now())) { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Map.of("success", false, "message", "Refresh token expired")); } + + // 새로운 액세스 토큰 발급 + EmailCodeDto emailCodeDto = EmailCodeDto.builder() + .email(email) + .userType(userType) + .build(); + + // Call updateUserInfo and capture its response + return userService.updateUserInfo(emailCodeDto); + + + } catch (Exception ex) { + log.error("Refresh token parsing error: {}", ex.getMessage()); + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Map.of("success", false, "message", "Invalid refresh token")); } - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Map.of("success", false, "message", "An unexpected error occurred")); } + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Map.of("success", false, "message", "An unexpected error occurred")); } +} /* @@ -208,4 +211,3 @@ public ResponseEntity verifyCode(@RequestBody Map request, @R } */ - diff --git a/src/main/java/org/nmfw/foodietree/domain/auth/dto/EmailCodeDto.java b/src/main/java/org/nmfw/foodietree/domain/auth/dto/EmailCodeDto.java index 07b04f8a..fff6e5dd 100644 --- a/src/main/java/org/nmfw/foodietree/domain/auth/dto/EmailCodeDto.java +++ b/src/main/java/org/nmfw/foodietree/domain/auth/dto/EmailCodeDto.java @@ -1,7 +1,6 @@ package org.nmfw.foodietree.domain.auth.dto; import lombok.*; -import org.nmfw.foodietree.domain.auth.entity.EmailVerification; import java.time.LocalDateTime; import java.util.Date; @@ -18,17 +17,7 @@ public class EmailCodeDto{ @Setter private LocalDateTime expiryDate; // 인증번호 만료기간 @Setter - private boolean emailVerified; + private Boolean emailVerified; private String userType; - - public EmailVerification toEntity() { - return EmailVerification.builder() - .email(this.email) - .code(this.code) - .expiryDate(this.expiryDate) - .emailVerified(this.emailVerified) - .build(); - } - -} +} \ No newline at end of file diff --git a/src/main/java/org/nmfw/foodietree/domain/auth/dto/EmailCodeStoreDto.java b/src/main/java/org/nmfw/foodietree/domain/auth/dto/EmailCodeStoreDto.java index b44156e8..fb203807 100644 --- a/src/main/java/org/nmfw/foodietree/domain/auth/dto/EmailCodeStoreDto.java +++ b/src/main/java/org/nmfw/foodietree/domain/auth/dto/EmailCodeStoreDto.java @@ -15,5 +15,5 @@ public class EmailCodeStoreDto { private String storeId; private String userType; @Setter - private Date refreshTokenExpireDate; -} + private LocalDateTime refreshTokenExpireDate; +} \ No newline at end of file diff --git a/src/main/java/org/nmfw/foodietree/domain/auth/dto/EmailCustomerDto.java b/src/main/java/org/nmfw/foodietree/domain/auth/dto/EmailCustomerDto.java index 3bf1255d..f133b6c5 100644 --- a/src/main/java/org/nmfw/foodietree/domain/auth/dto/EmailCustomerDto.java +++ b/src/main/java/org/nmfw/foodietree/domain/auth/dto/EmailCustomerDto.java @@ -2,7 +2,7 @@ import lombok.*; -import java.util.Date; +import java.time.LocalDateTime; @Setter @Getter @@ -14,5 +14,5 @@ public class EmailCustomerDto { private String customerId; private String userType; @Setter - private Date refreshTokenExpireDate; -} + private LocalDateTime refreshTokenExpireDate; +} \ No newline at end of file diff --git a/src/main/java/org/nmfw/foodietree/domain/auth/entity/EmailVerification.java b/src/main/java/org/nmfw/foodietree/domain/auth/entity/EmailVerification.java index c1ba16ff..77669d30 100644 --- a/src/main/java/org/nmfw/foodietree/domain/auth/entity/EmailVerification.java +++ b/src/main/java/org/nmfw/foodietree/domain/auth/entity/EmailVerification.java @@ -1,18 +1,19 @@ package org.nmfw.foodietree.domain.auth.entity; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; +import lombok.*; import org.nmfw.foodietree.domain.customer.entity.Customer; import javax.persistence.*; import java.time.LocalDateTime; import java.util.Date; +@Entity @Getter +@Setter +@Builder +@AllArgsConstructor @NoArgsConstructor -@Entity +@EqualsAndHashCode(of = "id") @Table(name = "tbl_verification_code") public class EmailVerification { @@ -23,8 +24,8 @@ public class EmailVerification { @Column(name = "email", nullable = true, length = 50, unique = true) private String email; - @Column(name = "code", nullable = true, length = 255) - private String code; +// @Column(name = "code", nullable = true, length = 255) +// private String code; @Column(name = "expiry_date", nullable = false) private LocalDateTime expiryDate; @@ -34,16 +35,4 @@ public class EmailVerification { @Column(name = "user_type", nullable = true, length = 50) private String userType; - - - @Builder - public EmailVerification(Long id, String email, String code, LocalDateTime expiryDate, boolean emailVerified, String userType) { - - this.id = id; - this.email = email; - this.code = code; - this.expiryDate = expiryDate; - this.emailVerified = emailVerified; - this.userType = userType; - } } \ No newline at end of file diff --git a/src/main/java/org/nmfw/foodietree/domain/auth/mapper/EmailMapper.java b/src/main/java/org/nmfw/foodietree/domain/auth/mapper/EmailMapper.java index 71504aa1..23c0773e 100644 --- a/src/main/java/org/nmfw/foodietree/domain/auth/mapper/EmailMapper.java +++ b/src/main/java/org/nmfw/foodietree/domain/auth/mapper/EmailMapper.java @@ -11,12 +11,17 @@ public interface EmailMapper { void save(EmailCodeDto dto); - int findByEmail(String email); +// int findByEmail(String email); - default boolean isEmailExists(String email) { - return findByEmail(email) > 0; - } - EmailCodeDto findOneByEmail(String email); +// default boolean isEmailExists(String email) { +// return findByEmail(email) > 0; +// } +// EmailCodeDto findOneByEmail(String email); void update(EmailCodeDto emailCodeDto); -} + + EmailCodeDto findOneByEmail(String email); + + Boolean existsByEmail(String email); + +} \ No newline at end of file diff --git a/src/main/java/org/nmfw/foodietree/domain/auth/repository/EmailRepository.java b/src/main/java/org/nmfw/foodietree/domain/auth/repository/EmailRepository.java new file mode 100644 index 00000000..e6b87537 --- /dev/null +++ b/src/main/java/org/nmfw/foodietree/domain/auth/repository/EmailRepository.java @@ -0,0 +1,49 @@ +package org.nmfw.foodietree.domain.auth.repository; + +import org.apache.ibatis.annotations.Param; +import org.nmfw.foodietree.domain.auth.dto.EmailCodeDto; +import org.nmfw.foodietree.domain.auth.entity.EmailVerification; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; + +@Repository +public interface EmailRepository extends JpaRepository, EmailRepositoryCustom { + + @Modifying + @Transactional + @Query("UPDATE EmailVerification ev SET ev.expiryDate = :expiryDate, ev.emailVerified = :emailVerified WHERE ev.email = :email") + void updateEmailVerification(@Param("expiryDate") LocalDateTime expiryDate, + @Param("emailVerified") Boolean emailVerified, + @Param("email") String email); + + @Transactional + default void saveEmailVerification(EmailCodeDto dto) { + EmailVerification emailVerification = new EmailVerification(); + emailVerification.setEmail(dto.getEmail()); + emailVerification.setUserType(dto.getUserType()); + emailVerification.setExpiryDate(dto.getExpiryDate()); + emailVerification.setEmailVerified(dto.getEmailVerified() != null ? dto.getEmailVerified() : false); + + save(emailVerification); + } + + @Query("SELECT COUNT(ev) FROM EmailVerification ev WHERE ev.email = :email") + int countByEmail(@Param("email") String email); + + @Query("SELECT ev FROM EmailVerification ev WHERE ev.email = :email") + EmailCodeDto findOneByEmail(@Param("email") String email); + + @Query("SELECT COUNT(ev) > 0 FROM EmailVerification ev WHERE ev.email = :email") + Boolean existsByEmail(@Param("email") String email); + + @Query("SELECT CASE WHEN COUNT(c) > 0 OR COUNT(s) > 0 THEN TRUE ELSE FALSE END " + + "FROM Customer c LEFT JOIN Store s ON c.customerId = s.storeId " + + "WHERE c.customerId = :email OR s.storeId = :email") + Boolean existsByEmailInCustomerOrStore(@Param("email") String email); + +} \ No newline at end of file diff --git a/src/main/java/org/nmfw/foodietree/domain/auth/security/TokenProvider.java b/src/main/java/org/nmfw/foodietree/domain/auth/security/TokenProvider.java index f6931bc0..0cbe98c4 100644 --- a/src/main/java/org/nmfw/foodietree/domain/auth/security/TokenProvider.java +++ b/src/main/java/org/nmfw/foodietree/domain/auth/security/TokenProvider.java @@ -25,6 +25,7 @@ import java.util.Date; import java.util.List; + @Component @Slf4j public class TokenProvider { @@ -62,8 +63,6 @@ public String createToken(EmailCodeDto emailCodeDto) { .compact(); } - // refresh token : for long term life cycle and did not need to verify email link - // save at user's DB public String createRefreshToken(String email, String userType) { byte[] decodedKey = Base64.getDecoder().decode(REFRESH_SECRET_KEY); @@ -85,24 +84,31 @@ public String createRefreshToken(String email, String userType) { .compact(); } - public Date getExpirationDateFromRefreshToken(String refreshToken) { + + public LocalDateTime getExpirationDateFromRefreshToken(String refreshToken) { + +// public Date getExpirationDateFromRefreshToken(String refreshToken) { byte[] keyBytes = REFRESH_SECRET_KEY.getBytes(); Key key = Keys.hmacShaKeyFor(keyBytes); - return Jwts.parserBuilder() + Date expiration = Jwts.parserBuilder() .setSigningKey(key) .build() .parseClaimsJws(refreshToken) .getBody() - .getExpiration(); // 리프레시 토큰 만료일자 + .getExpiration(); + + return expiration.toInstant() + .atZone(ZoneId.systemDefault()) // 시스템 기본 시간대를 사용 + .toLocalDateTime(); } - public TokenUserInfo validateAndGetTokenInfo(String token) { + public TokenUserInfo validateAndGetTokenInfo(String token) { - if (token == null || token.isEmpty()) { - throw new IllegalArgumentException("JWT String argument cannot be null or empty."); - } + if (token == null || token.isEmpty()) { + throw new IllegalArgumentException("JWT String argument cannot be null or empty."); + } try { //토큰 발급 당시 서명 처리 @@ -230,4 +236,4 @@ public boolean isEnabled() { } } -} +} \ No newline at end of file diff --git a/src/main/java/org/nmfw/foodietree/domain/auth/security/filter/AuthJwtFilter.java b/src/main/java/org/nmfw/foodietree/domain/auth/security/filter/AuthJwtFilter.java index 71deb141..d9c697bb 100644 --- a/src/main/java/org/nmfw/foodietree/domain/auth/security/filter/AuthJwtFilter.java +++ b/src/main/java/org/nmfw/foodietree/domain/auth/security/filter/AuthJwtFilter.java @@ -77,6 +77,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse } catch (Exception e) { log.info("refresh token, access token 유효성 검증 통과 둘다 못함 ❌"); + log.warn("Token validation error"); e.printStackTrace(); } diff --git a/src/main/java/org/nmfw/foodietree/domain/auth/service/EmailService.java b/src/main/java/org/nmfw/foodietree/domain/auth/service/EmailService.java index 0ba4a0bc..2196a525 100644 --- a/src/main/java/org/nmfw/foodietree/domain/auth/service/EmailService.java +++ b/src/main/java/org/nmfw/foodietree/domain/auth/service/EmailService.java @@ -1,30 +1,21 @@ package org.nmfw.foodietree.domain.auth.service; -import io.jsonwebtoken.Claims; -import io.jsonwebtoken.Jwts; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.nmfw.foodietree.domain.auth.dto.EmailCodeDto; import org.nmfw.foodietree.domain.auth.entity.EmailVerification; import org.nmfw.foodietree.domain.auth.mapper.EmailMapper; -import org.nmfw.foodietree.domain.auth.security.CodeGenerator; +import org.nmfw.foodietree.domain.auth.repository.EmailRepository; import org.nmfw.foodietree.domain.auth.security.TokenProvider; -import org.nmfw.foodietree.domain.customer.entity.Customer; -import org.nmfw.foodietree.domain.customer.mapper.CustomerMapper; -import org.nmfw.foodietree.domain.store.mapper.StoreMapper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import javax.mail.MessagingException; import javax.mail.internet.MimeMessage; import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.HashMap; -import java.util.Map; @Service @Slf4j @@ -32,160 +23,24 @@ public class EmailService { private final JavaMailSender javaMailSender; - private final EmailMapper emailMapper; - + private final EmailRepository emailRepository; private final TokenProvider tokenProvider; - private static final Map signUpList = new HashMap<>(); - - public void sendResetVerificationCode(String to, String userType,String purpose) throws MessagingException { - String code = CodeGenerator.generateCode(); - - MimeMessage message = javaMailSender.createMimeMessage(); - MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8"); - helper.setTo(to); - - String subject = "FoodieTree 비밀번호 재설정 인증코드"; - if (purpose.equalsIgnoreCase("signup")) { - subject = "FoodieTree 회원가입 인증코드"; - } - helper.setSubject(subject); - - // 생성한 코드와 유효시간을 이메일 템플릿에 포함하여 HTML을 직접 작성 - String htmlContent = generateEmailHtml(code, purpose); - - helper.setText(htmlContent, true); - - javaMailSender.send(message); - - if (purpose.equalsIgnoreCase("signup")) { - EmailCodeDto build = EmailCodeDto.builder() - .code(code) - .email(to) - .expiryDate(LocalDateTime.now().plusMinutes(5)).build(); - signUpList.put(to, build); - log.info("{}", build); - return; - } - - // 데이터베이스에 코드와 만료 날짜를 저장 - saveVerificationCode(to, userType,code); + @Transactional + public EmailCodeDto findOneByEmail(String email) { + return emailRepository.findOneByEmail(email); } - /* - public boolean verifyCode(String email, String inputCode) { - EmailCodeDto verificationCode = emailMapper.findByEmail(email); - - if (verificationCode != null - && verificationCode.getCode().equals(inputCode) - && verificationCode.getExpiryDate().isAfter(LocalDateTime.now())) { - return true; - } - return false; - } - - */ - - public boolean verifyCodeForSignUp(String email, String inputCode) { - EmailCodeDto verificationCode = signUpList.get(email); - - if (verificationCode != null - && verificationCode.getCode().equals(inputCode) - && verificationCode.getExpiryDate().isAfter(LocalDateTime.now())) { - return true; - } - return false; + @Transactional + public void updateEmailVerification(EmailCodeDto dto) { + emailRepository.updateEmailVerification(dto.getExpiryDate(), dto.getEmailVerified(), dto.getEmail()); } - /** - * 이메일 인증 코드를 생성하여 이메일로 전송 - * - * @param code: 생성한 인증 코드 - * @param purpose : 이메일 인증 코드의 용도 (signup, reset) - * @return - */ - private String generateEmailHtml(String code, String purpose) { - LocalDateTime expiryDate = LocalDateTime.now().plusMinutes(5); // 제한 시간 5분 설정 - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일 HH시 mm분"); - - String title; - String headerMessage; - - if ("signup".equalsIgnoreCase(purpose)) { - title = "회원가입 인증"; - headerMessage = "회원가입 인증 코드 입니다."; - } else if ("reset".equalsIgnoreCase(purpose)) { - title = "비밀번호 재설정 인증"; - headerMessage = "비밀번호 재설정 인증 코드 입니다."; - } else { - throw new IllegalArgumentException("Unknown purpose: " + purpose); - } - - return "\n" + - "\n" + - "\n" + - " \n" + - " " + title + " 코드\n" + - " \n" + - "\n" + - "\n" + - "
\n" + - "
\n" + - "

안녕하세요.

\n" + - "

지구를 지키는 'FoodieTree'입니다

\n" + - "
\n" + - "

아래 코드를 " + title + " 창으로 돌아가 입력해주세요.

\n" + - "
\n" + - "

" + headerMessage + "

\n" + - "
" + code + "
\n" + - "
\n" + - "

유효 시간: " + expiryDate.format(formatter) + "

\n" + - "
\n" + - "\n" + - ""; + @Transactional + public Boolean existsByEmailInCustomerOrStore(String email) { + return emailRepository.existsByEmailInCustomerOrStore(email); } - private void saveVerificationCode(String email, String userType,String code) { - EmailCodeDto verificationCode = EmailCodeDto.builder() - .email(email) - .code(code) - .expiryDate(LocalDateTime.now().plusMinutes(5)) - .build(); - - emailMapper.save(verificationCode); - } // 이메일 인증 링크 전송 메서드 public void sendVerificationEmailLink(String email, String userType, EmailCodeDto emailCodeDto) throws MessagingException { @@ -199,11 +54,12 @@ public void sendVerificationEmailLink(String email, String userType, EmailCodeDt .email(email) .userType(userType) .expiryDate(LocalDateTime.now().plusMinutes(5)) // 엑세스 토큰 만료 시간 + .emailVerified(false) .build(); - if(!emailMapper.isEmailExists(email)) { - emailMapper.save(dto); - } else emailMapper.update(dto); + if(!emailRepository.existsByEmail(email)) { + emailRepository.saveEmailVerification(dto); + } else updateEmailVerification(dto); // 이메일에 포함될 링크 생성 String verificationLink = "http://localhost:3000/verifyEmail?token=" + token + "&refreshToken=" + refreshToken; @@ -283,4 +139,156 @@ private String generateEmailLinkHtml(String verificationLink) { ""; } -} +// private static final Map signUpList = new HashMap<>(); + +// public void sendResetVerificationCode(String to, String userType,String purpose) throws MessagingException { +// String code = CodeGenerator.generateCode(); +// +// MimeMessage message = javaMailSender.createMimeMessage(); +// MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8"); +// helper.setTo(to); +// +// String subject = "FoodieTree 비밀번호 재설정 인증코드"; +// if (purpose.equalsIgnoreCase("signup")) { +// subject = "FoodieTree 회원가입 인증코드"; +// } +// helper.setSubject(subject); +// +// // 생성한 코드와 유효시간을 이메일 템플릿에 포함하여 HTML을 직접 작성 +// String htmlContent = generateEmailHtml(code, purpose); +// +// helper.setText(htmlContent, true); +// +// javaMailSender.send(message); +// +// if (purpose.equalsIgnoreCase("signup")) { +// EmailCodeDto build = EmailCodeDto.builder() +// .code(code) +// .email(to) +// .expiryDate(LocalDateTime.now().plusMinutes(5)).build(); +// signUpList.put(to, build); +// log.info("{}", build); +// return; +// } +// +// // 데이터베이스에 코드와 만료 날짜를 저장 +// saveVerificationCode(to, userType,code); +// } + + /* + public boolean verifyCode(String email, String inputCode) { + EmailCodeDto verificationCode = emailMapper.findByEmail(email); + + if (verificationCode != null + && verificationCode.getCode().equals(inputCode) + && verificationCode.getExpiryDate().isAfter(LocalDateTime.now())) { + return true; + } + return false; + } + + */ + +// public boolean verifyCodeForSignUp(String email, String inputCode) { +// EmailCodeDto verificationCode = signUpList.get(email); +// +// if (verificationCode != null +// && verificationCode.getCode().equals(inputCode) +// && verificationCode.getExpiryDate().isAfter(LocalDateTime.now())) { +// return true; +// } +// return false; +// } + + /** + * 이메일 인증 코드를 생성하여 이메일로 전송 + * + * @param code: 생성한 인증 코드 + * @param purpose : 이메일 인증 코드의 용도 (signup, reset) + * @return + */ +// private String generateEmailHtml(String code, String purpose) { +// LocalDateTime expiryDate = LocalDateTime.now().plusMinutes(5); // 제한 시간 5분 설정 +// DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일 HH시 mm분"); +// +// String title; +// String headerMessage; +// +// if ("signup".equalsIgnoreCase(purpose)) { +// title = "회원가입 인증"; +// headerMessage = "회원가입 인증 코드 입니다."; +// } else if ("reset".equalsIgnoreCase(purpose)) { +// title = "비밀번호 재설정 인증"; +// headerMessage = "비밀번호 재설정 인증 코드 입니다."; +// } else { +// throw new IllegalArgumentException("Unknown purpose: " + purpose); +// } +// +// return "\n" + +// "\n" + +// "\n" + +// " \n" + +// " " + title + " 코드\n" + +// " \n" + +// "\n" + +// "\n" + +// "
\n" + +// "
\n" + +// "

안녕하세요.

\n" + +// "

지구를 지키는 'FoodieTree'입니다

\n" + +// "
\n" + +// "

아래 코드를 " + title + " 창으로 돌아가 입력해주세요.

\n" + +// "
\n" + +// "

" + headerMessage + "

\n" + +// "
" + code + "
\n" + +// "
\n" + +// "

유효 시간: " + expiryDate.format(formatter) + "

\n" + +// "
\n" + +// "\n" + +// ""; +// } + +// private void saveVerificationCode(String email, String userType,String code) { +// EmailCodeDto verificationCode = EmailCodeDto.builder() +// .email(email) +// .code(code) +// .expiryDate(LocalDateTime.now().plusMinutes(5)) +// .build(); +// +// emailMapper.save(verificationCode); +// } + + +} \ No newline at end of file diff --git a/src/main/java/org/nmfw/foodietree/domain/auth/service/UserService.java b/src/main/java/org/nmfw/foodietree/domain/auth/service/UserService.java index 5b3f5a07..02522871 100644 --- a/src/main/java/org/nmfw/foodietree/domain/auth/service/UserService.java +++ b/src/main/java/org/nmfw/foodietree/domain/auth/service/UserService.java @@ -7,9 +7,9 @@ import org.nmfw.foodietree.domain.auth.dto.EmailCodeStoreDto; import org.nmfw.foodietree.domain.auth.security.TokenProvider; import org.nmfw.foodietree.domain.customer.entity.Customer; -import org.nmfw.foodietree.domain.customer.mapper.CustomerMapper; +import org.nmfw.foodietree.domain.customer.service.CustomerService; import org.nmfw.foodietree.domain.store.entity.Store; -import org.nmfw.foodietree.domain.store.mapper.StoreMapper; +import org.nmfw.foodietree.domain.store.service.StoreService; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; @@ -23,8 +23,12 @@ @RequiredArgsConstructor public class UserService { - private final CustomerMapper customerMapper; - private final StoreMapper storeMapper; + //mapper.xml +// private final CustomerMapper customerMapper; +// private final StoreMapper storeMapper; + //jpa + private final CustomerService customerService; + private final StoreService storeService; private final TokenProvider tokenProvider; @@ -36,7 +40,7 @@ public class UserService { String token = tokenProvider.createToken(emailCodeDto); String refreshToken = tokenProvider.createRefreshToken(emailCodeDtoEmail, emailCodeDtoUserType); - Date expirationDate = tokenProvider.getExpirationDateFromRefreshToken(refreshToken); + LocalDateTime expirationDate = tokenProvider.getExpirationDateFromRefreshToken(refreshToken); // 최초 회원 정보 저장 로직 : customer인지 store 인지 null 값으로 구분 if (emailCodeDtoUserType.equals("store")) { @@ -47,7 +51,7 @@ public class UserService { .refreshTokenExpireDate(expirationDate) .build(); - storeMapper.signUpSaveStore(emailCodeStoreDto); + storeService.signUpSaveStore(emailCodeStoreDto); } else if(emailCodeDtoUserType.equals("customer")) { @@ -57,7 +61,7 @@ public class UserService { .refreshTokenExpireDate(expirationDate) .build(); - customerMapper.signUpSaveCustomer(emailCodeCustomerDto); + customerService.signUpSaveCustomer(emailCodeCustomerDto); } return ResponseEntity.ok(Map.of( @@ -81,7 +85,8 @@ public class UserService { log.info(" 새로운 토큰 재발급 ! {}",token); String refreshToken = tokenProvider.createRefreshToken(emailCodeDtoEmail, emailCodeDtoUserType); log.info("새로운 리프레시 토큰 재발급 ! {} ",refreshToken); - Date expirationDate = tokenProvider.getExpirationDateFromRefreshToken(refreshToken); + + LocalDateTime expirationDate = tokenProvider.getExpirationDateFromRefreshToken(refreshToken); if (emailCodeDtoUserType.equals("store")) { @@ -90,7 +95,8 @@ public class UserService { .userType(emailCodeDtoUserType) .refreshTokenExpireDate(expirationDate) .build(); - storeMapper.signUpUpdateStore(emailCodeStoreDto); + + storeService.signUpUpdateStore(emailCodeStoreDto); // customer 일 경우 } else if (emailCodeDtoUserType.equals("customer")) { @@ -101,7 +107,8 @@ public class UserService { .refreshTokenExpireDate(expirationDate) .build(); - customerMapper.signUpUpdateCustomer(emailCodeCustomerDto); + customerService.signUpUpdateCustomer(emailCodeCustomerDto); + } return ResponseEntity.ok(Map.of( @@ -128,14 +135,16 @@ public boolean findByEmail(EmailCodeDto emailCodeDto) { if ("store".equals(emailCodeDto.getUserType())) { log.info("store 타입 확인"); log.info("로그인 로직 확인 : 들어오는 유저타입 : {}", emailCodeDto.getUserType()); - if (storeMapper.findOne(emailCodeDto.getEmail()) != null) { + if (storeService.findOne(emailCodeDto.getEmail())) { + log.info("로그인 로직 확인 : 들어오는 유저타입 : {}, TRUE", emailCodeDto.getUserType()); result = true; } } else if ("customer".equals(emailCodeDto.getUserType())) { log.info("customer 타입 확인"); log.info("로그인 로직 확인 : 들어오는 유저타입 : {}", emailCodeDto.getUserType()); - if (customerMapper.findOne(emailCodeDto.getEmail()) != null) { + if (customerService.findOne(emailCodeDto.getEmail())) { + log.info("로그인 로직 확인 : 들어오는 유저타입 : {}, TRUE", emailCodeDto.getUserType()); result = true; } @@ -146,11 +155,12 @@ public boolean findByEmail(EmailCodeDto emailCodeDto) { public LocalDateTime getRefreshTokenExpiryDate(String email, String userType) { if ("customer".equals(userType)) { - Customer customer = customerMapper.findOne(email); + Customer customer = customerService.getCustomerById(email); log.info("customer object : {},customer email : {}, customer 의 리프레시 만료일자 인 서버 : {}", customer,customer.getCustomerId(), customer.getRefreshTokenExpireDate()); return customer != null ? customer.getRefreshTokenExpireDate() : null; } else if ("store".equals(userType)) { - Store store = storeMapper.findOne(email); + Store store = storeService.getStoreById(email); + log.info("store email : {}, store 의 리프레시 만료일자 인 서버 : {}", email, store.getRefreshTokenExpireDate()); return store != null ? store.getRefreshTokenExpireDate() : null; } else { @@ -158,4 +168,3 @@ public LocalDateTime getRefreshTokenExpiryDate(String email, String userType) { } } } - diff --git a/src/main/java/org/nmfw/foodietree/domain/customer/controller/CustomerController.java b/src/main/java/org/nmfw/foodietree/domain/customer/controller/CustomerController.java index 1009b5b0..0453ec0d 100644 --- a/src/main/java/org/nmfw/foodietree/domain/customer/controller/CustomerController.java +++ b/src/main/java/org/nmfw/foodietree/domain/customer/controller/CustomerController.java @@ -22,23 +22,22 @@ import java.util.List; -@RestController -@RequestMapping("/customer") @Slf4j +@RestController @RequiredArgsConstructor +@RequestMapping("/customer") public class CustomerController { private final CustomerService customerService; + private final CustomerMyPageService customerMyPageService; + private final CustomerEditService customerEditService; @GetMapping("/check") @CrossOrigin @ResponseBody - public ResponseEntity check( -// String type, - String keyword) { + public ResponseEntity check(String keyword) { log.info("{}", keyword); - - boolean flag = customerService.checkIdentifier(keyword); + boolean flag = customerService.findOne(keyword); return ResponseEntity .ok() .body(flag); @@ -55,27 +54,6 @@ public ResponseEntity> getMyLocation(@RequestBody Map ,CustomerRepositoryCustom { + @Query("SELECT c.refreshTokenExpireDate FROM Customer c WHERE c.customerId = :email") + Optional findRefreshDateById(@Param("email") String email); + + @Modifying + @Query("UPDATE Customer c SET c.refreshTokenExpireDate = :refreshTokenExpireDate WHERE c.customerId = :customerId") + void updateRefreshTokenExpireDate(@Param("refreshTokenExpireDate") LocalDateTime refreshTokenExpireDate, @Param("customerId") String customerId); + + @Query("SELECT COUNT(c) > 0 FROM Customer c WHERE c.customerId = :keyword") + boolean existsById(@Param("keyword") String keyword); + + Optional findByCustomerId(String customerId); } diff --git a/src/main/java/org/nmfw/foodietree/domain/customer/service/CustomerService.java b/src/main/java/org/nmfw/foodietree/domain/customer/service/CustomerService.java index 5a9bbdf4..155bf9db 100644 --- a/src/main/java/org/nmfw/foodietree/domain/customer/service/CustomerService.java +++ b/src/main/java/org/nmfw/foodietree/domain/customer/service/CustomerService.java @@ -2,15 +2,18 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.nmfw.foodietree.domain.auth.dto.EmailCustomerDto; import org.nmfw.foodietree.domain.customer.dto.request.AutoLoginDto; import org.nmfw.foodietree.domain.customer.dto.request.CustomerLoginDto; import org.nmfw.foodietree.domain.customer.dto.request.SignUpDto; import org.nmfw.foodietree.domain.customer.dto.resp.LoginUserInfoDto; import org.nmfw.foodietree.domain.customer.entity.Customer; import org.nmfw.foodietree.domain.customer.mapper.CustomerMapper; +import org.nmfw.foodietree.domain.customer.repository.CustomerRepository; import org.nmfw.foodietree.domain.customer.util.LoginUtil; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.web.util.WebUtils; import javax.servlet.http.Cookie; @@ -18,6 +21,7 @@ import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.time.LocalDateTime; +import java.util.Optional; import static org.nmfw.foodietree.domain.customer.util.LoginUtil.AUTO_LOGIN_COOKIE; @@ -27,120 +31,42 @@ @Slf4j public class CustomerService { - //고객 정보를 데이터베이스에 저장하거나 조회하는데 사용되는 객체 - private final CustomerMapper customerMapper; - //비밀번호 암호화 객체 - private final PasswordEncoder encoder; + private final CustomerRepository customerRepository; - //회원 가입 중간 처리 (저장 성공 여부 boolean 값으로 반환) - public boolean join(SignUpDto dto) { - Customer customer = dto.toEntity(); - - // 비밀번호를 인코딩(암호화) -// String encodedPassword = encoder.encode(dto.getCustomerPassword()); -// customer.setCustomerPassword(encodedPassword); //인코딩 된 비밀번호를 Customer에 주입 - - boolean saved = customerMapper.save(customer); //데이터에 저장 - -// if (saved && dto.getFood() != null) { -// customerMapper.savePreferredFoods(dto.getCustomerId(), dto.getFood()); -// } - -// System.out.println("\n save = " + saved); -// System.out.println(dto.getFood()); - - return saved; // 데이터 저장 결과 반환 + @Transactional(readOnly = true) + public Optional findRefreshDateById(String email) { + return customerRepository.findRefreshDateById(email); } + @Transactional + public void signUpUpdateCustomer(EmailCustomerDto emailCodeCustomerDto) { - //로그인 검증 처리 - public LoginResult authenticate(CustomerLoginDto dto, - HttpSession session, - HttpServletResponse response) { - - // 회원가입 여부 확인 - String customerId = dto.getCustomerId(); - System.out.println("\ncustomerId = " + customerId); - - Customer foundCustomer = customerMapper.findOne(customerId); //db에 있는 customerId 꺼내옴. - System.out.println("\nfoundCustomer = " + foundCustomer); - - if (foundCustomer == null) { - log.info("{} - 회원가입이 필요합니다.", customerId); - return LoginResult.NO_ID; - } - - // 비밀번호 일치 검사 - String inputPassword = dto.getCustomerPassword(); - String originPassword = foundCustomer.getCustomerPassword(); - - if (!encoder.matches(inputPassword, originPassword)) { - log.info("비밀번호가 일치하지 않습니다."); - return LoginResult.NO_PW; - } - - // 자동로그인 추가 처리 - if (dto.isAutoLogin()) { - // 1. 자동 로그인 쿠키 생성 - String sessionId = session.getId(); - - System.out.println("\nsessionId = " + sessionId); - Cookie autoLoginCookie = new Cookie(AUTO_LOGIN_COOKIE, sessionId); - System.out.println("\nautoLoginCookie = " + autoLoginCookie); - // 쿠키 설정 - autoLoginCookie.setPath("/"); // 쿠키를 사용할 경로 - autoLoginCookie.setMaxAge(60 * 60 * 24 * 90); // 자동로그인 유지 시간 - // 2. 쿠키를 클라이언트에 전송 - 응답바디에 실어보내야 함 - response.addCookie(autoLoginCookie); - log.debug("\nAuto login cookie set: " + autoLoginCookie.toString()); - - // 3. DB에도 해당 쿠키값을 저장 - customerMapper.updateAutoLogin( - AutoLoginDto.builder() - .sessionId(sessionId) - .limitTime(LocalDateTime.now().plusDays(90)) - .id(customerId) - .build() - ); - } - - - maintainLoginState(session, foundCustomer); - - return LoginResult.SUCCESS; + customerRepository.updateRefreshTokenExpireDate( + emailCodeCustomerDto.getRefreshTokenExpireDate(), + emailCodeCustomerDto.getCustomerId() + ); } - public static void maintainLoginState(HttpSession session, Customer foundCustomer) { - log.info("{}님 로그인 성공", foundCustomer.getCustomerId()); - - // 세션의 수명 : 설정된 시간 or 브라우저를 닫기 전까지 - int maxInactiveInterval = session.getMaxInactiveInterval(); - session.setMaxInactiveInterval(60 * 60); - log.debug("session time: {}", maxInactiveInterval); + @Transactional + public void signUpSaveCustomer(EmailCustomerDto emailCustomerDto) { + Customer customer = Customer.builder() + .customerId(emailCustomerDto.getCustomerId()) + .refreshTokenExpireDate(emailCustomerDto.getRefreshTokenExpireDate()) + .userType(emailCustomerDto.getUserType()) + .build(); - session.setAttribute("login", new LoginUserInfoDto(foundCustomer)); + customerRepository.save(customer); } - // 아이디 중복 검사 - public boolean checkIdentifier(String keyword) { - return customerMapper.existsById(keyword); + @Transactional(readOnly = true) + public boolean findOne(String customerId) { + return true; } - public void autoLoginClear(HttpServletRequest request, - HttpServletResponse response) { - - Cookie c = WebUtils.getCookie(request, AUTO_LOGIN_COOKIE); - if(c != null) { - c.setPath("/"); - c.setMaxAge(0); - response.addCookie(c); - customerMapper.updateAutoLogin( - AutoLoginDto.builder() - .sessionId("none") - .limitTime(LocalDateTime.now()) - .id(LoginUtil.getLoggedInUser(request.getSession())) - .build() - ); - } + @Transactional(readOnly = true) + public Customer getCustomerById(String customerId) { + return customerRepository.findByCustomerId(customerId) + .orElseThrow(() -> new RuntimeException("Customer not found with id: " + customerId)); } + } \ No newline at end of file diff --git a/src/main/java/org/nmfw/foodietree/domain/store/controller/StoreController.java b/src/main/java/org/nmfw/foodietree/domain/store/controller/StoreController.java index 95a8c5ae..68f506db 100644 --- a/src/main/java/org/nmfw/foodietree/domain/store/controller/StoreController.java +++ b/src/main/java/org/nmfw/foodietree/domain/store/controller/StoreController.java @@ -31,64 +31,64 @@ public class StoreController { private final StoreService storeService; - @GetMapping("/sign-in") - public String login(HttpSession session - , @RequestParam(required = false) String redirect) { - session.setAttribute("redirect", redirect); - return "sign-in"; - } +// @GetMapping("/sign-in") +// public String login(HttpSession session +// , @RequestParam(required = false) String redirect) { +// session.setAttribute("redirect", redirect); +// return "sign-in"; +// } - @PostMapping("/sign-in") - public String login(StoreLoginDto dto, - HttpSession session, - HttpServletResponse response) { +// @PostMapping("/sign-in") +// public String login(StoreLoginDto dto, +// HttpSession session, +// HttpServletResponse response) { +// +// StoreLoginResult result = storeService.authenticate(dto, session, response); +// if (result == SUCCESS) { +// String redirect = (String) session.getAttribute("redirect"); +// if (redirect != null) { +// session.removeAttribute("redirect"); +// return "redirect:" + redirect; +// } +// return "redirect:/"; +// } +// return "redirect:/store/sign-in?message=signin-fail"; +// } - StoreLoginResult result = storeService.authenticate(dto, session, response); - if (result == SUCCESS) { - String redirect = (String) session.getAttribute("redirect"); - if (redirect != null) { - session.removeAttribute("redirect"); - return "redirect:" + redirect; - } - return "redirect:/"; - } - return "redirect:/store/sign-in?message=signin-fail"; - } +// @GetMapping("/sign-up") +// public String StoreSignUpForm() { +// return "store/sign-up"; +// } +// +// /** +// * @param dto +// * @return StoreSignUpService에서 성공적으로 회원가입완료시 다음페이지로 이동 +// */ +// @PostMapping("/sign-up") +// public String storeSignUp(@Validated StoreSignUpDto dto, HttpSession session, BindingResult result) { +// log.info("/store-sign-up POST"); +// log.info("parameter:{}", dto); +// if (result.hasErrors()) { +// return "redirect:/store/sign-up?message=signup-fail"; +// } +// +// boolean flag = storeService.signUp(dto, session); +// if (!flag) { +// log.debug("회원가입 실패"); +// return "redirect:/store/sign-up?message=signup-fail"; +// } +// log.debug("회원가입 성공"); +// return "redirect:/store/approval"; +// } - @GetMapping("/sign-up") - public String StoreSignUpForm() { - return "store/sign-up"; - } - - /** - * @param dto - * @return StoreSignUpService에서 성공적으로 회원가입완료시 다음페이지로 이동 - */ - @PostMapping("/sign-up") - public String storeSignUp(@Validated StoreSignUpDto dto, HttpSession session, BindingResult result) { - log.info("/store-sign-up POST"); - log.info("parameter:{}", dto); - if (result.hasErrors()) { - return "redirect:/store/sign-up?message=signup-fail"; - } - - boolean flag = storeService.signUp(dto, session); - if (!flag) { - log.debug("회원가입 실패"); - return "redirect:/store/sign-up?message=signup-fail"; - } - log.debug("회원가입 성공"); - return "redirect:/store/approval"; - } - - @GetMapping("/sign-out") - public String signOut(HttpServletRequest request, HttpServletResponse response) { - HttpSession session = request.getSession(); - if (LoginUtil.isAutoLogin(request)) { - storeService.autoLoginClear(request, response); - } - session.removeAttribute("login"); - session.invalidate(); - return "redirect:/"; - } -} +// @GetMapping("/sign-out") +// public String signOut(HttpServletRequest request, HttpServletResponse response) { +// HttpSession session = request.getSession(); +// if (LoginUtil.isAutoLogin(request)) { +// storeService.autoLoginClear(request, response); +// } +// session.removeAttribute("login"); +// session.invalidate(); +// return "redirect:/"; +// } +} \ No newline at end of file diff --git a/src/main/java/org/nmfw/foodietree/domain/store/repository/StoreRepository.java b/src/main/java/org/nmfw/foodietree/domain/store/repository/StoreRepository.java index c33b1bd0..fdb114a5 100644 --- a/src/main/java/org/nmfw/foodietree/domain/store/repository/StoreRepository.java +++ b/src/main/java/org/nmfw/foodietree/domain/store/repository/StoreRepository.java @@ -1,10 +1,23 @@ package org.nmfw.foodietree.domain.store.repository; +import org.apache.ibatis.annotations.Param; import org.nmfw.foodietree.domain.store.entity.Store; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.transaction.annotation.Transactional; +import java.time.LocalDateTime; import java.util.Optional; public interface StoreRepository extends JpaRepository { Optional findByStoreId(String storeId); + + @Modifying + @Transactional + @Query("UPDATE Store s SET s.refreshTokenExpireDate = :refreshTokenExpireDate WHERE s.storeId = :storeId") + void updateRefreshTokenExpireDate( + @Param("storeId") String storeId, + @Param("refreshTokenExpireDate") LocalDateTime refreshTokenExpireDate + ); } diff --git a/src/main/java/org/nmfw/foodietree/domain/store/service/StoreService.java b/src/main/java/org/nmfw/foodietree/domain/store/service/StoreService.java index 871dcebd..0be6f5d5 100644 --- a/src/main/java/org/nmfw/foodietree/domain/store/service/StoreService.java +++ b/src/main/java/org/nmfw/foodietree/domain/store/service/StoreService.java @@ -5,6 +5,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.nmfw.foodietree.domain.auth.dto.EmailCodeStoreDto; import org.nmfw.foodietree.domain.customer.dto.request.AutoLoginDto; import org.nmfw.foodietree.domain.customer.dto.resp.LoginUserInfoDto; import org.nmfw.foodietree.domain.customer.entity.Customer; @@ -15,8 +16,10 @@ import org.nmfw.foodietree.domain.store.dto.request.StoreSignUpDto; import org.nmfw.foodietree.domain.store.entity.Store; import org.nmfw.foodietree.domain.store.mapper.StoreMapper; +import org.nmfw.foodietree.domain.store.repository.StoreRepository; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.web.util.WebUtils; import javax.servlet.http.HttpServletRequest; @@ -30,74 +33,106 @@ @RequiredArgsConstructor @Slf4j public class StoreService { - private final StoreMapper storeMapper; - private final PasswordEncoder encoder; + private final StoreRepository storeRepository; - public boolean signUp(StoreSignUpDto dto, HttpSession session) { - Store store = dto.toEntity(); -// String encodedPassword = encoder.encode(dto.getPassword()); -// store.setPassword(encodedPassword); - boolean flag = storeMapper.storeSave(store); - if (!flag) { - return false; - } - StoreService.maintainLoginState(session, store); - return true; + @Transactional + public void signUpUpdateStore(EmailCodeStoreDto emailCodeStoreDto) { + + storeRepository.updateRefreshTokenExpireDate( + emailCodeStoreDto.getStoreId(), + emailCodeStoreDto.getRefreshTokenExpireDate() + ); } - public StoreLoginResult authenticate(StoreLoginDto dto, HttpSession session, HttpServletResponse response) { - String storeId = dto.getStoreId(); - Store foundStore = storeMapper.findOne(storeId); - if (foundStore == null) { - log.info("{} - 회원가입이 필요합니다.", storeId); - return NO_ACC; - } + @Transactional + public void signUpSaveStore(EmailCodeStoreDto emailCodeStoreDto) { + Store store = Store.builder() + .storeId(emailCodeStoreDto.getStoreId()) + .userType(emailCodeStoreDto.getUserType()) + .refreshTokenExpireDate(emailCodeStoreDto.getRefreshTokenExpireDate()) + .build(); - String inputPassword = dto.getPassword(); -// String originPassword = foundStore.getPassword(); -// if (!encoder.matches(inputPassword, originPassword)) { -// log.info("비밀번호가 일치하지 않습니다"); -// return NO_PW; -// } + storeRepository.save(store); + } - if (dto.isAutoLogin()) { - String sessionId = session.getId(); - Cookie autoLoginCookie = new Cookie(LoginUtil.AUTO_LOGIN_COOKIE, sessionId); - autoLoginCookie.setPath("/"); // 쿠키를 사용할 경로 - autoLoginCookie.setMaxAge(60 * 60 * 24 * 90); // 자동로그인 유지 시간 - response.addCookie(autoLoginCookie); - storeMapper.updateAutoLogin( - AutoLoginDto.builder() - .sessionId(sessionId) - .limitTime(LocalDateTime.now().plusDays(90)) - .id(storeId) - .build() - ); - } - maintainLoginState(session, foundStore); - return SUCCESS; + @Transactional(readOnly = true) + public Store getStoreById(String storeId) { + return storeRepository.findByStoreId(storeId) + .orElseThrow(() -> new RuntimeException("Store not found with id: " + storeId)); } - public static void maintainLoginState(HttpSession session, Store found) { - session.setMaxInactiveInterval(60 * 60); - session.setAttribute("login", new LoginStoreInfoDto(found)); + @Transactional(readOnly = true) + public boolean findOne(String storeId) { + return true; } - public void autoLoginClear(HttpServletRequest request, - HttpServletResponse response) { - Cookie c = WebUtils.getCookie(request, AUTO_LOGIN_COOKIE); - if (c != null) { - c.setPath("/"); - c.setMaxAge(0); - response.addCookie(c); - storeMapper.updateAutoLogin( - AutoLoginDto.builder() - .sessionId("none") - .limitTime(LocalDateTime.now()) - .id(LoginUtil.getLoggedInUser(request.getSession())) - .build() - ); - } - } + +// public boolean signUp(StoreSignUpDto dto, HttpSession session) { +// Store store = dto.toEntity(); +//// String encodedPassword = encoder.encode(dto.getPassword()); +//// store.setPassword(encodedPassword); +// boolean flag = storeMapper.storeSave(store); +// if (!flag) { +// return false; +// } +// StoreService.maintainLoginState(session, store); +// return true; +// } +// +// public StoreLoginResult authenticate(StoreLoginDto dto, HttpSession session, HttpServletResponse response) { +// String storeId = dto.getStoreId(); +// Store foundStore = storeMapper.findOne(storeId); +// if (foundStore == null) { +// log.info("{} - 회원가입이 필요합니다.", storeId); +// return NO_ACC; +// } +// +// String inputPassword = dto.getPassword(); +//// String originPassword = foundStore.getPassword(); +//// if (!encoder.matches(inputPassword, originPassword)) { +//// log.info("비밀번호가 일치하지 않습니다"); +//// return NO_PW; +//// } +// +// if (dto.isAutoLogin()) { +// String sessionId = session.getId(); +// Cookie autoLoginCookie = new Cookie(LoginUtil.AUTO_LOGIN_COOKIE, sessionId); +// autoLoginCookie.setPath("/"); // 쿠키를 사용할 경로 +// autoLoginCookie.setMaxAge(60 * 60 * 24 * 90); // 자동로그인 유지 시간 +// response.addCookie(autoLoginCookie); +// storeMapper.updateAutoLogin( +// AutoLoginDto.builder() +// .sessionId(sessionId) +// .limitTime(LocalDateTime.now().plusDays(90)) +// .id(storeId) +// .build() +// ); +// } +// maintainLoginState(session, foundStore); +// return SUCCESS; +// } +// +// public static void maintainLoginState(HttpSession session, Store found) { +// session.setMaxInactiveInterval(60 * 60); +// session.setAttribute("login", new LoginStoreInfoDto(found)); +// } +// +// public void autoLoginClear(HttpServletRequest request, +// HttpServletResponse response) { +// +// Cookie c = WebUtils.getCookie(request, AUTO_LOGIN_COOKIE); +// if (c != null) { +// c.setPath("/"); +// c.setMaxAge(0); +// response.addCookie(c); +// storeMapper.updateAutoLogin( +// AutoLoginDto.builder() +// .sessionId("none") +// .limitTime(LocalDateTime.now()) +// .id(LoginUtil.getLoggedInUser(request.getSession())) +// .build() +// ); +// } +// } } diff --git a/src/main/java/org/nmfw/foodietree/global/interceptor/AutoLoginInterceptor.java b/src/main/java/org/nmfw/foodietree/global/interceptor/AutoLoginInterceptor.java index e75253a1..cc0b41a7 100644 --- a/src/main/java/org/nmfw/foodietree/global/interceptor/AutoLoginInterceptor.java +++ b/src/main/java/org/nmfw/foodietree/global/interceptor/AutoLoginInterceptor.java @@ -40,7 +40,7 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons if (foundCustomer != null && LocalDateTime.now().isBefore(foundCustomer.getLimitTime())) { - CustomerService.maintainLoginState(request.getSession(), foundCustomer); +// CustomerService.maintainLoginState(request.getSession(), foundCustomer); } } From dc7823dcec0c510630157c0f5a1888b96d5b4870 Mon Sep 17 00:00:00 2001 From: Maybaba <161430857+Maybaba@users.noreply.github.com> Date: Wed, 7 Aug 2024 23:49:14 +0900 Subject: [PATCH 02/13] Update EmailController.java --- .../auth/controller/EmailController.java | 55 +------------------ 1 file changed, 1 insertion(+), 54 deletions(-) diff --git a/src/main/java/org/nmfw/foodietree/domain/auth/controller/EmailController.java b/src/main/java/org/nmfw/foodietree/domain/auth/controller/EmailController.java index 2c67bdf0..ed98b2ca 100644 --- a/src/main/java/org/nmfw/foodietree/domain/auth/controller/EmailController.java +++ b/src/main/java/org/nmfw/foodietree/domain/auth/controller/EmailController.java @@ -35,41 +35,6 @@ public class EmailController { private final UserService userService; private final TokenProvider tokenProvider; -// @GetMapping("/send-reset-email") -// public String sendVerificationCode(@RequestParam String to, String userType) { -// try { -// emailService.sendResetVerificationCode(to, userType, "reset"); -// return "Password reset email sent successfully"; -// } catch (Exception e) { -// return "Failed to send password reset email: " + e.getMessage(); -// } -// } - - /* - @GetMapping("/verify-reset-code") - public String verifyResetCode(@RequestParam String email, @RequestParam String code) { - if (emailService.verifyCode(email, code)) { - return "Verification successful"; - } else { - return "Verification failed or code expired"; - } - } - - */ - - // 인증 코드 전송 -// @PostMapping("/sendVerificationCode") -// public ResponseEntity sendVerificationCode(@RequestBody Map request) { -// String email = request.get("email"); -// String purpose = request.get("purpose"); -// String userType = request.get("userType"); -// try { -// emailService.sendResetVerificationCode(email, purpose, userType); -// return ResponseEntity.ok("Verification code sent"); -// } catch (MessagingException e) { -// return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Failed to send verification code"); -// } -// } // 인증 리다이렉션 링크 메일 전송 @PostMapping("/sendVerificationLink") @@ -192,22 +157,4 @@ public ResponseEntity verifyEmail(@RequestBody Map request) { } - /* - @PostMapping("/verifyCode") - public ResponseEntity verifyCode(@RequestBody Map request, @RequestParam(required = false) String purpose) { - String email = request.get("email"); - String code = request.get("code"); - boolean isValid = false; - if (purpose != null && purpose.equals("signup")) { - isValid = emailService.verifyCodeForSignUp(email, code); - } else { - isValid = emailService.verifyCode(email, code); - } - if (isValid) { - return ResponseEntity.ok("success"); - } else { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("failure"); - } - } - - */ + From 155770c741b4c44ac76e2251f76181590c1e13ab Mon Sep 17 00:00:00 2001 From: Maybaba <161430857+Maybaba@users.noreply.github.com> Date: Wed, 7 Aug 2024 23:49:48 +0900 Subject: [PATCH 03/13] Update EmailMapper.java --- .../nmfw/foodietree/domain/auth/mapper/EmailMapper.java | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/main/java/org/nmfw/foodietree/domain/auth/mapper/EmailMapper.java b/src/main/java/org/nmfw/foodietree/domain/auth/mapper/EmailMapper.java index 23c0773e..0b38582c 100644 --- a/src/main/java/org/nmfw/foodietree/domain/auth/mapper/EmailMapper.java +++ b/src/main/java/org/nmfw/foodietree/domain/auth/mapper/EmailMapper.java @@ -11,17 +11,10 @@ public interface EmailMapper { void save(EmailCodeDto dto); -// int findByEmail(String email); - -// default boolean isEmailExists(String email) { -// return findByEmail(email) > 0; -// } -// EmailCodeDto findOneByEmail(String email); - void update(EmailCodeDto emailCodeDto); EmailCodeDto findOneByEmail(String email); Boolean existsByEmail(String email); -} \ No newline at end of file +} From f1c10d5323cba9a426d8624d2ccd7d7aaba2cd2a Mon Sep 17 00:00:00 2001 From: Maybaba <161430857+Maybaba@users.noreply.github.com> Date: Wed, 7 Aug 2024 23:50:22 +0900 Subject: [PATCH 04/13] Update TokenProvider.java --- .../nmfw/foodietree/domain/auth/security/TokenProvider.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/org/nmfw/foodietree/domain/auth/security/TokenProvider.java b/src/main/java/org/nmfw/foodietree/domain/auth/security/TokenProvider.java index 0cbe98c4..773d52bf 100644 --- a/src/main/java/org/nmfw/foodietree/domain/auth/security/TokenProvider.java +++ b/src/main/java/org/nmfw/foodietree/domain/auth/security/TokenProvider.java @@ -87,7 +87,6 @@ public String createRefreshToken(String email, String userType) { public LocalDateTime getExpirationDateFromRefreshToken(String refreshToken) { -// public Date getExpirationDateFromRefreshToken(String refreshToken) { byte[] keyBytes = REFRESH_SECRET_KEY.getBytes(); Key key = Keys.hmacShaKeyFor(keyBytes); @@ -236,4 +235,4 @@ public boolean isEnabled() { } } -} \ No newline at end of file +} From 5c3f66080c378095f19c14e7d2b57e59cc0aa82a Mon Sep 17 00:00:00 2001 From: Maybaba <161430857+Maybaba@users.noreply.github.com> Date: Wed, 7 Aug 2024 23:51:10 +0900 Subject: [PATCH 05/13] Update EmailService.java --- .../domain/auth/service/EmailService.java | 154 +----------------- 1 file changed, 1 insertion(+), 153 deletions(-) diff --git a/src/main/java/org/nmfw/foodietree/domain/auth/service/EmailService.java b/src/main/java/org/nmfw/foodietree/domain/auth/service/EmailService.java index 2196a525..2e4d9968 100644 --- a/src/main/java/org/nmfw/foodietree/domain/auth/service/EmailService.java +++ b/src/main/java/org/nmfw/foodietree/domain/auth/service/EmailService.java @@ -139,156 +139,4 @@ private String generateEmailLinkHtml(String verificationLink) { ""; } -// private static final Map signUpList = new HashMap<>(); - -// public void sendResetVerificationCode(String to, String userType,String purpose) throws MessagingException { -// String code = CodeGenerator.generateCode(); -// -// MimeMessage message = javaMailSender.createMimeMessage(); -// MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8"); -// helper.setTo(to); -// -// String subject = "FoodieTree 비밀번호 재설정 인증코드"; -// if (purpose.equalsIgnoreCase("signup")) { -// subject = "FoodieTree 회원가입 인증코드"; -// } -// helper.setSubject(subject); -// -// // 생성한 코드와 유효시간을 이메일 템플릿에 포함하여 HTML을 직접 작성 -// String htmlContent = generateEmailHtml(code, purpose); -// -// helper.setText(htmlContent, true); -// -// javaMailSender.send(message); -// -// if (purpose.equalsIgnoreCase("signup")) { -// EmailCodeDto build = EmailCodeDto.builder() -// .code(code) -// .email(to) -// .expiryDate(LocalDateTime.now().plusMinutes(5)).build(); -// signUpList.put(to, build); -// log.info("{}", build); -// return; -// } -// -// // 데이터베이스에 코드와 만료 날짜를 저장 -// saveVerificationCode(to, userType,code); -// } - - /* - public boolean verifyCode(String email, String inputCode) { - EmailCodeDto verificationCode = emailMapper.findByEmail(email); - - if (verificationCode != null - && verificationCode.getCode().equals(inputCode) - && verificationCode.getExpiryDate().isAfter(LocalDateTime.now())) { - return true; - } - return false; - } - - */ - -// public boolean verifyCodeForSignUp(String email, String inputCode) { -// EmailCodeDto verificationCode = signUpList.get(email); -// -// if (verificationCode != null -// && verificationCode.getCode().equals(inputCode) -// && verificationCode.getExpiryDate().isAfter(LocalDateTime.now())) { -// return true; -// } -// return false; -// } - - /** - * 이메일 인증 코드를 생성하여 이메일로 전송 - * - * @param code: 생성한 인증 코드 - * @param purpose : 이메일 인증 코드의 용도 (signup, reset) - * @return - */ -// private String generateEmailHtml(String code, String purpose) { -// LocalDateTime expiryDate = LocalDateTime.now().plusMinutes(5); // 제한 시간 5분 설정 -// DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일 HH시 mm분"); -// -// String title; -// String headerMessage; -// -// if ("signup".equalsIgnoreCase(purpose)) { -// title = "회원가입 인증"; -// headerMessage = "회원가입 인증 코드 입니다."; -// } else if ("reset".equalsIgnoreCase(purpose)) { -// title = "비밀번호 재설정 인증"; -// headerMessage = "비밀번호 재설정 인증 코드 입니다."; -// } else { -// throw new IllegalArgumentException("Unknown purpose: " + purpose); -// } -// -// return "\n" + -// "\n" + -// "\n" + -// " \n" + -// " " + title + " 코드\n" + -// " \n" + -// "\n" + -// "\n" + -// "
\n" + -// "
\n" + -// "

안녕하세요.

\n" + -// "

지구를 지키는 'FoodieTree'입니다

\n" + -// "
\n" + -// "

아래 코드를 " + title + " 창으로 돌아가 입력해주세요.

\n" + -// "
\n" + -// "

" + headerMessage + "

\n" + -// "
" + code + "
\n" + -// "
\n" + -// "

유효 시간: " + expiryDate.format(formatter) + "

\n" + -// "
\n" + -// "\n" + -// ""; -// } - -// private void saveVerificationCode(String email, String userType,String code) { -// EmailCodeDto verificationCode = EmailCodeDto.builder() -// .email(email) -// .code(code) -// .expiryDate(LocalDateTime.now().plusMinutes(5)) -// .build(); -// -// emailMapper.save(verificationCode); -// } - - -} \ No newline at end of file +} From 209ecbf88b4d146b9c55a865129161e2fa23e5cd Mon Sep 17 00:00:00 2001 From: Maybaba <161430857+Maybaba@users.noreply.github.com> Date: Wed, 7 Aug 2024 23:51:59 +0900 Subject: [PATCH 06/13] Update StoreController.java MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 사용하지 않는 주석 지우라고 하셔서 지웁니다. --- .../store/controller/StoreController.java | 62 +------------------ 1 file changed, 1 insertion(+), 61 deletions(-) diff --git a/src/main/java/org/nmfw/foodietree/domain/store/controller/StoreController.java b/src/main/java/org/nmfw/foodietree/domain/store/controller/StoreController.java index 68f506db..59b166a2 100644 --- a/src/main/java/org/nmfw/foodietree/domain/store/controller/StoreController.java +++ b/src/main/java/org/nmfw/foodietree/domain/store/controller/StoreController.java @@ -31,64 +31,4 @@ public class StoreController { private final StoreService storeService; -// @GetMapping("/sign-in") -// public String login(HttpSession session -// , @RequestParam(required = false) String redirect) { -// session.setAttribute("redirect", redirect); -// return "sign-in"; -// } - -// @PostMapping("/sign-in") -// public String login(StoreLoginDto dto, -// HttpSession session, -// HttpServletResponse response) { -// -// StoreLoginResult result = storeService.authenticate(dto, session, response); -// if (result == SUCCESS) { -// String redirect = (String) session.getAttribute("redirect"); -// if (redirect != null) { -// session.removeAttribute("redirect"); -// return "redirect:" + redirect; -// } -// return "redirect:/"; -// } -// return "redirect:/store/sign-in?message=signin-fail"; -// } - -// @GetMapping("/sign-up") -// public String StoreSignUpForm() { -// return "store/sign-up"; -// } -// -// /** -// * @param dto -// * @return StoreSignUpService에서 성공적으로 회원가입완료시 다음페이지로 이동 -// */ -// @PostMapping("/sign-up") -// public String storeSignUp(@Validated StoreSignUpDto dto, HttpSession session, BindingResult result) { -// log.info("/store-sign-up POST"); -// log.info("parameter:{}", dto); -// if (result.hasErrors()) { -// return "redirect:/store/sign-up?message=signup-fail"; -// } -// -// boolean flag = storeService.signUp(dto, session); -// if (!flag) { -// log.debug("회원가입 실패"); -// return "redirect:/store/sign-up?message=signup-fail"; -// } -// log.debug("회원가입 성공"); -// return "redirect:/store/approval"; -// } - -// @GetMapping("/sign-out") -// public String signOut(HttpServletRequest request, HttpServletResponse response) { -// HttpSession session = request.getSession(); -// if (LoginUtil.isAutoLogin(request)) { -// storeService.autoLoginClear(request, response); -// } -// session.removeAttribute("login"); -// session.invalidate(); -// return "redirect:/"; -// } -} \ No newline at end of file +} From 0a9b09372de6ba2be544f6e8a6104eb0ca8f133c Mon Sep 17 00:00:00 2001 From: Maybaba <161430857+Maybaba@users.noreply.github.com> Date: Wed, 7 Aug 2024 23:52:22 +0900 Subject: [PATCH 07/13] Update AutoLoginInterceptor.java --- .../foodietree/global/interceptor/AutoLoginInterceptor.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/org/nmfw/foodietree/global/interceptor/AutoLoginInterceptor.java b/src/main/java/org/nmfw/foodietree/global/interceptor/AutoLoginInterceptor.java index cc0b41a7..e5e2647b 100644 --- a/src/main/java/org/nmfw/foodietree/global/interceptor/AutoLoginInterceptor.java +++ b/src/main/java/org/nmfw/foodietree/global/interceptor/AutoLoginInterceptor.java @@ -40,10 +40,9 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons if (foundCustomer != null && LocalDateTime.now().isBefore(foundCustomer.getLimitTime())) { -// CustomerService.maintainLoginState(request.getSession(), foundCustomer); } } return true; } -} \ No newline at end of file +} From d0b8eb8ee8ae6c1afd73d8c1fe00cea49b127021 Mon Sep 17 00:00:00 2001 From: Maybaba <161430857+Maybaba@users.noreply.github.com> Date: Wed, 7 Aug 2024 23:53:10 +0900 Subject: [PATCH 08/13] Update StoreService.java --- .../domain/store/service/StoreService.java | 70 ------------------- 1 file changed, 70 deletions(-) diff --git a/src/main/java/org/nmfw/foodietree/domain/store/service/StoreService.java b/src/main/java/org/nmfw/foodietree/domain/store/service/StoreService.java index 0be6f5d5..f37216ed 100644 --- a/src/main/java/org/nmfw/foodietree/domain/store/service/StoreService.java +++ b/src/main/java/org/nmfw/foodietree/domain/store/service/StoreService.java @@ -65,74 +65,4 @@ public Store getStoreById(String storeId) { public boolean findOne(String storeId) { return true; } - - - -// public boolean signUp(StoreSignUpDto dto, HttpSession session) { -// Store store = dto.toEntity(); -//// String encodedPassword = encoder.encode(dto.getPassword()); -//// store.setPassword(encodedPassword); -// boolean flag = storeMapper.storeSave(store); -// if (!flag) { -// return false; -// } -// StoreService.maintainLoginState(session, store); -// return true; -// } -// -// public StoreLoginResult authenticate(StoreLoginDto dto, HttpSession session, HttpServletResponse response) { -// String storeId = dto.getStoreId(); -// Store foundStore = storeMapper.findOne(storeId); -// if (foundStore == null) { -// log.info("{} - 회원가입이 필요합니다.", storeId); -// return NO_ACC; -// } -// -// String inputPassword = dto.getPassword(); -//// String originPassword = foundStore.getPassword(); -//// if (!encoder.matches(inputPassword, originPassword)) { -//// log.info("비밀번호가 일치하지 않습니다"); -//// return NO_PW; -//// } -// -// if (dto.isAutoLogin()) { -// String sessionId = session.getId(); -// Cookie autoLoginCookie = new Cookie(LoginUtil.AUTO_LOGIN_COOKIE, sessionId); -// autoLoginCookie.setPath("/"); // 쿠키를 사용할 경로 -// autoLoginCookie.setMaxAge(60 * 60 * 24 * 90); // 자동로그인 유지 시간 -// response.addCookie(autoLoginCookie); -// storeMapper.updateAutoLogin( -// AutoLoginDto.builder() -// .sessionId(sessionId) -// .limitTime(LocalDateTime.now().plusDays(90)) -// .id(storeId) -// .build() -// ); -// } -// maintainLoginState(session, foundStore); -// return SUCCESS; -// } -// -// public static void maintainLoginState(HttpSession session, Store found) { -// session.setMaxInactiveInterval(60 * 60); -// session.setAttribute("login", new LoginStoreInfoDto(found)); -// } -// -// public void autoLoginClear(HttpServletRequest request, -// HttpServletResponse response) { -// -// Cookie c = WebUtils.getCookie(request, AUTO_LOGIN_COOKIE); -// if (c != null) { -// c.setPath("/"); -// c.setMaxAge(0); -// response.addCookie(c); -// storeMapper.updateAutoLogin( -// AutoLoginDto.builder() -// .sessionId("none") -// .limitTime(LocalDateTime.now()) -// .id(LoginUtil.getLoggedInUser(request.getSession())) -// .build() -// ); -// } -// } } From d9b49eaebcd7b4cc3d0900ad67b9e0ccb1d2aa50 Mon Sep 17 00:00:00 2001 From: Maybaba Date: Thu, 8 Aug 2024 12:31:06 +0900 Subject: [PATCH 09/13] # sign-up func bug fixed --- .../auth/controller/EmailController.java | 72 +++++++++++-------- .../domain/auth/dto/EmailCodeDto.java | 9 ++- .../domain/auth/entity/EmailVerification.java | 12 +++- .../auth/repository/EmailRepository.java | 3 +- .../domain/auth/security/TokenProvider.java | 6 +- .../domain/auth/service/EmailService.java | 19 ++++- .../domain/auth/service/UserService.java | 2 + .../controller/CustomerController.java | 3 +- .../repository/CustomerRepository.java | 2 +- .../customer/service/CustomerService.java | 4 +- .../store/repository/StoreRepository.java | 3 + .../domain/store/service/StoreService.java | 4 +- 12 files changed, 93 insertions(+), 46 deletions(-) diff --git a/src/main/java/org/nmfw/foodietree/domain/auth/controller/EmailController.java b/src/main/java/org/nmfw/foodietree/domain/auth/controller/EmailController.java index ed98b2ca..0f1bae56 100644 --- a/src/main/java/org/nmfw/foodietree/domain/auth/controller/EmailController.java +++ b/src/main/java/org/nmfw/foodietree/domain/auth/controller/EmailController.java @@ -24,6 +24,7 @@ import java.time.temporal.ChronoUnit; import java.util.Date; import java.util.Map; +import java.util.Optional; @RestController @RequiredArgsConstructor @@ -78,48 +79,57 @@ public ResponseEntity verifyEmail(@RequestBody Map request) { log.info("user access expire date : {}", accessTokenUserInfo.getTokenExpireDate().toString()); String email = accessTokenUserInfo.getEmail(); + log.info("유효성 검증 후 email : {}",email); String userType = accessTokenUserInfo.getRole(); + log.info("유효성 검증 후 userType : {}",userType); // 이메일 dto 정보를 데이터베이스에서 조회 - EmailCodeDto emailCodeDto = emailService.findOneByEmail(email); + Optional emailVerificationOpt = emailService.findOneByEmail(email); - log.info("EmailCodeDto retrieved from database: {}", emailCodeDto); - - // 이메일 정보가 인증 테이블에 있을 경우 - if (emailCodeDto != null) { - emailCodeDto.setUserType(userType); - emailCodeDto.setEmailVerified(true); - - // 이메일 인증이 완료되지 않은 경우 - 실제 회원가입이 되지 않은 경우 - if (!emailService.existsByEmailInCustomerOrStore(emailCodeDto.getEmail())) { - - // 이메일 재전송 페이지로 리다이렉션 - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(Map.of("success", false, "message", "Email link is not verified! Please resend verification email.")); - } - - //실제 회원가입(테이블에 저장)이 되지 않은 경우 false - if (!(userService.findByEmail(emailCodeDto))) { - - log.info("실제 회원가입(테이블에 저장)이 되지 않은 경우 {}", emailCodeDto); - - emailService.updateEmailVerification(emailCodeDto); // 인증정보 true 업데이트 - - return userService.saveUserInfo(emailCodeDto); + if (emailVerificationOpt.isPresent()) { + EmailVerification emailVerification = emailVerificationOpt.get(); + EmailCodeDto emailCodeDto = EmailCodeDto.builder() + .email(emailVerification.getEmail()) + .expiryDate(emailVerification.getExpiryDate()) + .emailVerified(true) + .userType(userType) + .build(); - } else { - // 실제 회원가입이 되어있는 경우, 로그인 하는데 access token 기간이 종료된 경우 - // 만료 기한 access, refresh 업데이트 - log.info("email이 실제 회원가입 되어있는 경우 dto {}", emailCodeDto); - return userService.updateUserInfo(emailCodeDto); + log.info("EmailCodeDto retrieved from database: {}", emailCodeDto); + + + // 이메일 정보가 인증 테이블에 있을 경우 + if (emailCodeDto != null) { + + log.info("이메일 정보 테이블에 해당 이메일 있음 {}", email); + emailCodeDto.setUserType(userType); + emailCodeDto.setEmailVerified(true); + + // 이메일 인증이 완료되지 않은 경우 - 실제 회원가입이 되지 않은 경우 + if (!emailService.existsByEmailInCustomerOrStore(emailCodeDto.getEmail())) { + // 이메일 재전송 페이지로 리다이렉션 +// return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(Map.of("success", false, "message", "Email link is not verified! Please resend verification email.")); +// } else { //실제 회원가입(테이블에 저장)이 되지 않은 경우 +// if (!(userService.findByEmail(emailCodeDto))) { + log.info("실제 회원가입(테이블에 저장)이 되지 않은 경우 {}", emailCodeDto); + + emailService.updateEmailVerification(emailCodeDto); // 인증정보 true 업데이트 +// return + userService.saveUserInfo(emailCodeDto); + } else { + // 실제 회원가입이 되어있는 경우, 로그인 하는데 access token 기간이 종료된 경우 + // 만료 기한 access, refresh 업데이트 + log.info("email이 실제 회원가입 되어있는 경우 dto {}", emailCodeDto); + return userService.updateUserInfo(emailCodeDto); + } } } - // email table에 인증정보가 없을 경우 즉, access token이 만료되었을경우 // 인증 정보는 상관없이 access token이 만료되었을 경우 + } catch (JwtException e) { + log.warn(" 토큰 검증에 실패함, JWT parsing error: {}", e.getMessage()); } catch (Exception e) { - log.info("access token 의 기한이 만료되었거나 위조되었습니다."); log.info("JWT parsing error: {}", e.getMessage()); - try { // 리프레시 토큰의 만료일자를 확인 - 서버에도 리프레시 토큰 저장 TokenUserInfo refreshTokenUserInfo = tokenProvider.validateAndGetRefreshTokenInfo(refreshToken); diff --git a/src/main/java/org/nmfw/foodietree/domain/auth/dto/EmailCodeDto.java b/src/main/java/org/nmfw/foodietree/domain/auth/dto/EmailCodeDto.java index fff6e5dd..785b72db 100644 --- a/src/main/java/org/nmfw/foodietree/domain/auth/dto/EmailCodeDto.java +++ b/src/main/java/org/nmfw/foodietree/domain/auth/dto/EmailCodeDto.java @@ -1,7 +1,9 @@ package org.nmfw.foodietree.domain.auth.dto; import lombok.*; +import org.nmfw.foodietree.domain.auth.entity.EmailVerification; +import javax.persistence.Entity; import java.time.LocalDateTime; import java.util.Date; @@ -13,11 +15,12 @@ @Builder public class EmailCodeDto{ private String email; //nullable - private String code; //추후 refresh token 도입예정 - @Setter +// private String code; //추후 refresh token 도입예정 private LocalDateTime expiryDate; // 인증번호 만료기간 - @Setter private Boolean emailVerified; private String userType; + public EmailVerification orElseThrow(Object o) { + return null; + } } \ No newline at end of file diff --git a/src/main/java/org/nmfw/foodietree/domain/auth/entity/EmailVerification.java b/src/main/java/org/nmfw/foodietree/domain/auth/entity/EmailVerification.java index 77669d30..8ae85c83 100644 --- a/src/main/java/org/nmfw/foodietree/domain/auth/entity/EmailVerification.java +++ b/src/main/java/org/nmfw/foodietree/domain/auth/entity/EmailVerification.java @@ -1,6 +1,7 @@ package org.nmfw.foodietree.domain.auth.entity; import lombok.*; +import org.nmfw.foodietree.domain.auth.dto.EmailCodeDto; import org.nmfw.foodietree.domain.customer.entity.Customer; import javax.persistence.*; @@ -31,8 +32,17 @@ public class EmailVerification { private LocalDateTime expiryDate; @Column(name = "email_verified", nullable = true) - private boolean emailVerified; + private Boolean emailVerified; @Column(name = "user_type", nullable = true, length = 50) private String userType; + + public EmailCodeDto toDto() { + return EmailCodeDto.builder() + .email(this.email) + .expiryDate(this.expiryDate) + .emailVerified(this.emailVerified) + .userType(this.userType) + .build(); + } } \ No newline at end of file diff --git a/src/main/java/org/nmfw/foodietree/domain/auth/repository/EmailRepository.java b/src/main/java/org/nmfw/foodietree/domain/auth/repository/EmailRepository.java index e6b87537..efa83e86 100644 --- a/src/main/java/org/nmfw/foodietree/domain/auth/repository/EmailRepository.java +++ b/src/main/java/org/nmfw/foodietree/domain/auth/repository/EmailRepository.java @@ -10,6 +10,7 @@ import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; +import java.util.Optional; @Repository public interface EmailRepository extends JpaRepository, EmailRepositoryCustom { @@ -36,7 +37,7 @@ default void saveEmailVerification(EmailCodeDto dto) { int countByEmail(@Param("email") String email); @Query("SELECT ev FROM EmailVerification ev WHERE ev.email = :email") - EmailCodeDto findOneByEmail(@Param("email") String email); + Optional findOneByEmail(@Param("email") String email); @Query("SELECT COUNT(ev) > 0 FROM EmailVerification ev WHERE ev.email = :email") Boolean existsByEmail(@Param("email") String email); diff --git a/src/main/java/org/nmfw/foodietree/domain/auth/security/TokenProvider.java b/src/main/java/org/nmfw/foodietree/domain/auth/security/TokenProvider.java index 773d52bf..1ac1a5b3 100644 --- a/src/main/java/org/nmfw/foodietree/domain/auth/security/TokenProvider.java +++ b/src/main/java/org/nmfw/foodietree/domain/auth/security/TokenProvider.java @@ -105,6 +105,8 @@ public LocalDateTime getExpirationDateFromRefreshToken(String refreshToken) { public TokenUserInfo validateAndGetTokenInfo(String token) { + log.info("validateAndGetTokenInfo run!!!!"); + if (token == null || token.isEmpty()) { throw new IllegalArgumentException("JWT String argument cannot be null or empty."); } @@ -141,12 +143,14 @@ public TokenUserInfo validateAndGetTokenInfo(String token) { } catch (JwtException e) { log.error("ACCESS Token validation error: {}", e.getMessage()); - throw e; // 또는 적절한 예외 처리 + throw e; } } public TokenUserInfo validateAndGetRefreshTokenInfo(String refreshToken) { + log.info("validateAndGetRefreshTokenInfo run!!!!"); + if (refreshToken == null || refreshToken.isEmpty()) { throw new IllegalArgumentException("JWT String argument cannot be null or empty."); } diff --git a/src/main/java/org/nmfw/foodietree/domain/auth/service/EmailService.java b/src/main/java/org/nmfw/foodietree/domain/auth/service/EmailService.java index 2e4d9968..75c54012 100644 --- a/src/main/java/org/nmfw/foodietree/domain/auth/service/EmailService.java +++ b/src/main/java/org/nmfw/foodietree/domain/auth/service/EmailService.java @@ -4,10 +4,9 @@ import lombok.extern.slf4j.Slf4j; import org.nmfw.foodietree.domain.auth.dto.EmailCodeDto; import org.nmfw.foodietree.domain.auth.entity.EmailVerification; -import org.nmfw.foodietree.domain.auth.mapper.EmailMapper; import org.nmfw.foodietree.domain.auth.repository.EmailRepository; import org.nmfw.foodietree.domain.auth.security.TokenProvider; -import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.config.ConfigDataResourceNotFoundException; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.stereotype.Service; @@ -16,6 +15,7 @@ import javax.mail.MessagingException; import javax.mail.internet.MimeMessage; import java.time.LocalDateTime; +import java.util.Optional; @Service @Slf4j @@ -26,8 +26,21 @@ public class EmailService { private final EmailRepository emailRepository; private final TokenProvider tokenProvider; + public EmailCodeDto getEmailCodeDtoByEmail(String email) { + EmailVerification emailVerification = emailRepository.findOneByEmail(email) + .orElseThrow(() -> new RuntimeException("EmailVerification not found for email: " + email)); + + return EmailCodeDto.builder() + .email(emailVerification.getEmail()) + .expiryDate(emailVerification.getExpiryDate()) + .emailVerified(emailVerification.getEmailVerified()) + .userType(emailVerification.getUserType()) + .build(); + } + + @Transactional - public EmailCodeDto findOneByEmail(String email) { + public Optional findOneByEmail(String email) { return emailRepository.findOneByEmail(email); } diff --git a/src/main/java/org/nmfw/foodietree/domain/auth/service/UserService.java b/src/main/java/org/nmfw/foodietree/domain/auth/service/UserService.java index 02522871..cae27bbd 100644 --- a/src/main/java/org/nmfw/foodietree/domain/auth/service/UserService.java +++ b/src/main/java/org/nmfw/foodietree/domain/auth/service/UserService.java @@ -36,7 +36,9 @@ public class UserService { public ResponseEntity> saveUserInfo(EmailCodeDto emailCodeDto) { String emailCodeDtoUserType = emailCodeDto.getUserType(); + log.info("emailcodedto에서 유저타입 찾기 {}", emailCodeDtoUserType); String emailCodeDtoEmail = emailCodeDto.getEmail(); + log.info("emailcodedto에서 이메일 찾기 {}", emailCodeDtoEmail); String token = tokenProvider.createToken(emailCodeDto); String refreshToken = tokenProvider.createRefreshToken(emailCodeDtoEmail, emailCodeDtoUserType); diff --git a/src/main/java/org/nmfw/foodietree/domain/customer/controller/CustomerController.java b/src/main/java/org/nmfw/foodietree/domain/customer/controller/CustomerController.java index 0453ec0d..d8e88ca9 100644 --- a/src/main/java/org/nmfw/foodietree/domain/customer/controller/CustomerController.java +++ b/src/main/java/org/nmfw/foodietree/domain/customer/controller/CustomerController.java @@ -36,8 +36,9 @@ public class CustomerController { @CrossOrigin @ResponseBody public ResponseEntity check(String keyword) { - log.info("{}", keyword); + log.info("customer 중복체크 아이디 : {}", keyword); boolean flag = customerService.findOne(keyword); + log.info("customer 중복체크 결과 {}", flag); return ResponseEntity .ok() .body(flag); diff --git a/src/main/java/org/nmfw/foodietree/domain/customer/repository/CustomerRepository.java b/src/main/java/org/nmfw/foodietree/domain/customer/repository/CustomerRepository.java index 93d75992..106ebc41 100644 --- a/src/main/java/org/nmfw/foodietree/domain/customer/repository/CustomerRepository.java +++ b/src/main/java/org/nmfw/foodietree/domain/customer/repository/CustomerRepository.java @@ -23,7 +23,7 @@ public interface CustomerRepository extends JpaRepository ,Custo void updateRefreshTokenExpireDate(@Param("refreshTokenExpireDate") LocalDateTime refreshTokenExpireDate, @Param("customerId") String customerId); @Query("SELECT COUNT(c) > 0 FROM Customer c WHERE c.customerId = :keyword") - boolean existsById(@Param("keyword") String keyword); + boolean existsByCustomerId(@Param("keyword") String keyword); Optional findByCustomerId(String customerId); } diff --git a/src/main/java/org/nmfw/foodietree/domain/customer/service/CustomerService.java b/src/main/java/org/nmfw/foodietree/domain/customer/service/CustomerService.java index 155bf9db..ba9fc21e 100644 --- a/src/main/java/org/nmfw/foodietree/domain/customer/service/CustomerService.java +++ b/src/main/java/org/nmfw/foodietree/domain/customer/service/CustomerService.java @@ -59,8 +59,8 @@ public void signUpSaveCustomer(EmailCustomerDto emailCustomerDto) { } @Transactional(readOnly = true) - public boolean findOne(String customerId) { - return true; + public boolean findOne(String keyword) { + return customerRepository.existsByCustomerId(keyword); } @Transactional(readOnly = true) diff --git a/src/main/java/org/nmfw/foodietree/domain/store/repository/StoreRepository.java b/src/main/java/org/nmfw/foodietree/domain/store/repository/StoreRepository.java index fdb114a5..753ab4f6 100644 --- a/src/main/java/org/nmfw/foodietree/domain/store/repository/StoreRepository.java +++ b/src/main/java/org/nmfw/foodietree/domain/store/repository/StoreRepository.java @@ -20,4 +20,7 @@ void updateRefreshTokenExpireDate( @Param("storeId") String storeId, @Param("refreshTokenExpireDate") LocalDateTime refreshTokenExpireDate ); + + @Query("SELECT COUNT(c) > 0 FROM Store c WHERE c.storeId = :keyword") + boolean existsByStoreId(@Param("keyword") String keyword); } diff --git a/src/main/java/org/nmfw/foodietree/domain/store/service/StoreService.java b/src/main/java/org/nmfw/foodietree/domain/store/service/StoreService.java index f37216ed..69828abc 100644 --- a/src/main/java/org/nmfw/foodietree/domain/store/service/StoreService.java +++ b/src/main/java/org/nmfw/foodietree/domain/store/service/StoreService.java @@ -62,7 +62,7 @@ public Store getStoreById(String storeId) { } @Transactional(readOnly = true) - public boolean findOne(String storeId) { - return true; + public boolean findOne(String keyword) { + return storeRepository.existsByStoreId(keyword); } } From f00f816516f2625b6bd89579c1bf656efda1a38c Mon Sep 17 00:00:00 2001 From: Maybaba Date: Thu, 8 Aug 2024 14:29:34 +0900 Subject: [PATCH 10/13] =?UTF-8?q?#=20=EC=9D=B4=EB=A9=94=EC=9D=BC=20?= =?UTF-8?q?=EC=A4=91=EB=B3=B5=EC=B2=B4=ED=81=AC=20EmailRepository.java=20?= =?UTF-8?q?=EC=BF=BC=EB=A6=AC=20RIGHTJOIN=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/controller/EmailController.java | 18 ++++++++++----- .../domain/auth/dto/EmailCodeDto.java | 4 ---- .../auth/repository/EmailRepository.java | 2 +- .../domain/auth/service/EmailService.java | 4 +++- .../domain/auth/service/UserService.java | 5 +---- .../controller/CustomerController.java | 22 +++++++++---------- .../domain/customer/entity/Customer.java | 8 ------- 7 files changed, 29 insertions(+), 34 deletions(-) diff --git a/src/main/java/org/nmfw/foodietree/domain/auth/controller/EmailController.java b/src/main/java/org/nmfw/foodietree/domain/auth/controller/EmailController.java index 0f1bae56..777b9de9 100644 --- a/src/main/java/org/nmfw/foodietree/domain/auth/controller/EmailController.java +++ b/src/main/java/org/nmfw/foodietree/domain/auth/controller/EmailController.java @@ -36,6 +36,18 @@ public class EmailController { private final UserService userService; private final TokenProvider tokenProvider; + @GetMapping("/check") + @CrossOrigin + @ResponseBody + public ResponseEntity check(String keyword) { + log.info("이메일 중복체크 아이디 : {}", keyword); + boolean flag = emailService.existsByEmailInCustomerOrStore(keyword); + log.info("이메일 중복체크 결과 {}", flag); + return ResponseEntity + .ok() + .body(flag); + } + // 인증 리다이렉션 링크 메일 전송 @PostMapping("/sendVerificationLink") @@ -107,10 +119,7 @@ public ResponseEntity verifyEmail(@RequestBody Map request) { // 이메일 인증이 완료되지 않은 경우 - 실제 회원가입이 되지 않은 경우 if (!emailService.existsByEmailInCustomerOrStore(emailCodeDto.getEmail())) { - // 이메일 재전송 페이지로 리다이렉션 -// return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(Map.of("success", false, "message", "Email link is not verified! Please resend verification email.")); -// } else { //실제 회원가입(테이블에 저장)이 되지 않은 경우 -// if (!(userService.findByEmail(emailCodeDto))) { + log.info("실제 회원가입(테이블에 저장)이 되지 않은 경우 {}", emailCodeDto); emailService.updateEmailVerification(emailCodeDto); // 인증정보 true 업데이트 @@ -153,7 +162,6 @@ public ResponseEntity verifyEmail(@RequestBody Map request) { .userType(userType) .build(); - // Call updateUserInfo and capture its response return userService.updateUserInfo(emailCodeDto); diff --git a/src/main/java/org/nmfw/foodietree/domain/auth/dto/EmailCodeDto.java b/src/main/java/org/nmfw/foodietree/domain/auth/dto/EmailCodeDto.java index 785b72db..107895b8 100644 --- a/src/main/java/org/nmfw/foodietree/domain/auth/dto/EmailCodeDto.java +++ b/src/main/java/org/nmfw/foodietree/domain/auth/dto/EmailCodeDto.java @@ -15,12 +15,8 @@ @Builder public class EmailCodeDto{ private String email; //nullable -// private String code; //추후 refresh token 도입예정 private LocalDateTime expiryDate; // 인증번호 만료기간 private Boolean emailVerified; private String userType; - public EmailVerification orElseThrow(Object o) { - return null; - } } \ No newline at end of file diff --git a/src/main/java/org/nmfw/foodietree/domain/auth/repository/EmailRepository.java b/src/main/java/org/nmfw/foodietree/domain/auth/repository/EmailRepository.java index efa83e86..2f057fbe 100644 --- a/src/main/java/org/nmfw/foodietree/domain/auth/repository/EmailRepository.java +++ b/src/main/java/org/nmfw/foodietree/domain/auth/repository/EmailRepository.java @@ -43,7 +43,7 @@ default void saveEmailVerification(EmailCodeDto dto) { Boolean existsByEmail(@Param("email") String email); @Query("SELECT CASE WHEN COUNT(c) > 0 OR COUNT(s) > 0 THEN TRUE ELSE FALSE END " + - "FROM Customer c LEFT JOIN Store s ON c.customerId = s.storeId " + + "FROM Customer c RIGHT JOIN Store s ON c.customerId = s.storeId " + "WHERE c.customerId = :email OR s.storeId = :email") Boolean existsByEmailInCustomerOrStore(@Param("email") String email); diff --git a/src/main/java/org/nmfw/foodietree/domain/auth/service/EmailService.java b/src/main/java/org/nmfw/foodietree/domain/auth/service/EmailService.java index 75c54012..c81ac002 100644 --- a/src/main/java/org/nmfw/foodietree/domain/auth/service/EmailService.java +++ b/src/main/java/org/nmfw/foodietree/domain/auth/service/EmailService.java @@ -51,7 +51,9 @@ public void updateEmailVerification(EmailCodeDto dto) { @Transactional public Boolean existsByEmailInCustomerOrStore(String email) { - return emailRepository.existsByEmailInCustomerOrStore(email); + Boolean flag = emailRepository.existsByEmailInCustomerOrStore(email); + log.info(" 실제 유저 테이블에 존재하는지 확인하는 값 flag : {}", flag); + return flag; } diff --git a/src/main/java/org/nmfw/foodietree/domain/auth/service/UserService.java b/src/main/java/org/nmfw/foodietree/domain/auth/service/UserService.java index cae27bbd..365bbe65 100644 --- a/src/main/java/org/nmfw/foodietree/domain/auth/service/UserService.java +++ b/src/main/java/org/nmfw/foodietree/domain/auth/service/UserService.java @@ -23,10 +23,6 @@ @RequiredArgsConstructor public class UserService { - //mapper.xml -// private final CustomerMapper customerMapper; -// private final StoreMapper storeMapper; - //jpa private final CustomerService customerService; private final StoreService storeService; @@ -169,4 +165,5 @@ public LocalDateTime getRefreshTokenExpiryDate(String email, String userType) { throw new IllegalArgumentException("Invalid user type"); } } + } diff --git a/src/main/java/org/nmfw/foodietree/domain/customer/controller/CustomerController.java b/src/main/java/org/nmfw/foodietree/domain/customer/controller/CustomerController.java index d8e88ca9..c457b07e 100644 --- a/src/main/java/org/nmfw/foodietree/domain/customer/controller/CustomerController.java +++ b/src/main/java/org/nmfw/foodietree/domain/customer/controller/CustomerController.java @@ -32,17 +32,17 @@ public class CustomerController { private final CustomerMyPageService customerMyPageService; private final CustomerEditService customerEditService; - @GetMapping("/check") - @CrossOrigin - @ResponseBody - public ResponseEntity check(String keyword) { - log.info("customer 중복체크 아이디 : {}", keyword); - boolean flag = customerService.findOne(keyword); - log.info("customer 중복체크 결과 {}", flag); - return ResponseEntity - .ok() - .body(flag); - } +// @GetMapping("/check") +// @CrossOrigin +// @ResponseBody +// public ResponseEntity check(String keyword) { +// log.info("customer 중복체크 아이디 : {}", keyword); +// boolean flag = customerService.findOne(keyword); +// log.info("customer 중복체크 결과 {}", flag); +// return ResponseEntity +// .ok() +// .body(flag); +// } @PostMapping("/myFavMap") public ResponseEntity> getMyLocation(@RequestBody Map payload) { diff --git a/src/main/java/org/nmfw/foodietree/domain/customer/entity/Customer.java b/src/main/java/org/nmfw/foodietree/domain/customer/entity/Customer.java index 2d766e0f..41672d62 100644 --- a/src/main/java/org/nmfw/foodietree/domain/customer/entity/Customer.java +++ b/src/main/java/org/nmfw/foodietree/domain/customer/entity/Customer.java @@ -57,12 +57,4 @@ public class Customer { @Column(name = "email_verified", nullable = true) private Boolean emailVerified; -// @OneToMany(mappedBy = "customer", cascade = CascadeType.ALL, orphanRemoval = true) -// private List emailVerifications = new ArrayList<>(); - -// @OneToMany(mappedBy = "customer", cascade = CascadeType.ALL, orphanRemoval = true) -// private List favFoods; -// -// @OneToMany(mappedBy = "customer", cascade = CascadeType.ALL, orphanRemoval = true) -// private List favAreas; } From 786fb732685e4eefe8eda6624a8430031cb02f0f Mon Sep 17 00:00:00 2001 From: Maybaba Date: Thu, 8 Aug 2024 16:06:45 +0900 Subject: [PATCH 11/13] =?UTF-8?q?#=20=EC=9D=B4=EB=A9=94=EC=9D=BC=20?= =?UTF-8?q?=EC=A4=91=EB=B3=B5=EC=B2=B4=ED=81=AC=20EmailRepository.java=20?= =?UTF-8?q?=EC=BF=BC=EB=A6=AC=20RIGHTJOIN=20=EB=B3=80=EA=B2=BD=20sign-up?= =?UTF-8?q?=20=EB=A1=9C=EC=A7=81=20=ED=99=95=EC=9D=B8=20=ED=9B=84=20?= =?UTF-8?q?=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85=20=EC=95=88=EB=90=98?= =?UTF-8?q?=EB=8A=94=20=EB=B2=84=EA=B7=B8=20=ED=94=BD=EC=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/auth/entity/EmailVerification.java | 1 + .../controller/CustomerController.java | 2 +- .../domain/customer/entity/Customer.java | 6 +++--- .../customer/service/CustomerService.java | 18 ++++++++++++------ 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/nmfw/foodietree/domain/auth/entity/EmailVerification.java b/src/main/java/org/nmfw/foodietree/domain/auth/entity/EmailVerification.java index 8ae85c83..e9650aaa 100644 --- a/src/main/java/org/nmfw/foodietree/domain/auth/entity/EmailVerification.java +++ b/src/main/java/org/nmfw/foodietree/domain/auth/entity/EmailVerification.java @@ -20,6 +20,7 @@ public class EmailVerification { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) private Long id; @Column(name = "email", nullable = true, length = 50, unique = true) diff --git a/src/main/java/org/nmfw/foodietree/domain/customer/controller/CustomerController.java b/src/main/java/org/nmfw/foodietree/domain/customer/controller/CustomerController.java index c457b07e..f3bf0962 100644 --- a/src/main/java/org/nmfw/foodietree/domain/customer/controller/CustomerController.java +++ b/src/main/java/org/nmfw/foodietree/domain/customer/controller/CustomerController.java @@ -98,7 +98,7 @@ public ResponseEntity> getReservations( @AuthenticationPrincipal TokenUserInfo userInfo ) { // 추후 토큰을 통해 고객 ID를 가져옴 - String customerId = userInfo.getEmail(); + String customerId = userInfo.getUsername(); List reservations = customerMyPageService.getReservationList(customerId); return ResponseEntity.ok(reservations); } diff --git a/src/main/java/org/nmfw/foodietree/domain/customer/entity/Customer.java b/src/main/java/org/nmfw/foodietree/domain/customer/entity/Customer.java index 41672d62..2c9e54c0 100644 --- a/src/main/java/org/nmfw/foodietree/domain/customer/entity/Customer.java +++ b/src/main/java/org/nmfw/foodietree/domain/customer/entity/Customer.java @@ -18,12 +18,12 @@ @Table(name = "tbl_customer") public class Customer { - @Id + @Id // auto increment @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "idx_customer_id") + @Column(name = "idx_customer_id", nullable = false) private Long idxCustomerId; - @Column(name = "customer_id", nullable = false, unique = true) + @Column(name = "customer_id", unique = true) private String customerId; @Column(name = "customer_password") diff --git a/src/main/java/org/nmfw/foodietree/domain/customer/service/CustomerService.java b/src/main/java/org/nmfw/foodietree/domain/customer/service/CustomerService.java index ba9fc21e..1b31cd8e 100644 --- a/src/main/java/org/nmfw/foodietree/domain/customer/service/CustomerService.java +++ b/src/main/java/org/nmfw/foodietree/domain/customer/service/CustomerService.java @@ -49,13 +49,19 @@ public void signUpUpdateCustomer(EmailCustomerDto emailCodeCustomerDto) { @Transactional public void signUpSaveCustomer(EmailCustomerDto emailCustomerDto) { - Customer customer = Customer.builder() - .customerId(emailCustomerDto.getCustomerId()) - .refreshTokenExpireDate(emailCustomerDto.getRefreshTokenExpireDate()) - .userType(emailCustomerDto.getUserType()) - .build(); + try { + Customer customer = Customer.builder() + .customerId(emailCustomerDto.getCustomerId()) + .refreshTokenExpireDate(emailCustomerDto.getRefreshTokenExpireDate()) + .userType(emailCustomerDto.getUserType()) + .build(); + + customerRepository.save(customer); + } catch (Exception e) { + log.error("Error saving customer: ", e); + throw e; // 예외를 다시 던져서 롤백 트리거 + } - customerRepository.save(customer); } @Transactional(readOnly = true) From 3da53fb6ca2c2da97ce7cf08406d8905e3df983b Mon Sep 17 00:00:00 2001 From: Maybaba Date: Thu, 8 Aug 2024 16:22:56 +0900 Subject: [PATCH 12/13] =?UTF-8?q?#=20=EC=A4=91=EB=B3=B5=EA=B2=80=EC=82=AC?= =?UTF-8?q?=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/auth/repository/EmailRepository.java | 13 +++++++++---- .../domain/auth/service/EmailService.java | 10 +++++++--- .../customer/repository/CustomerRepository.java | 3 +++ 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/nmfw/foodietree/domain/auth/repository/EmailRepository.java b/src/main/java/org/nmfw/foodietree/domain/auth/repository/EmailRepository.java index 2f057fbe..712d6a5c 100644 --- a/src/main/java/org/nmfw/foodietree/domain/auth/repository/EmailRepository.java +++ b/src/main/java/org/nmfw/foodietree/domain/auth/repository/EmailRepository.java @@ -42,9 +42,14 @@ default void saveEmailVerification(EmailCodeDto dto) { @Query("SELECT COUNT(ev) > 0 FROM EmailVerification ev WHERE ev.email = :email") Boolean existsByEmail(@Param("email") String email); - @Query("SELECT CASE WHEN COUNT(c) > 0 OR COUNT(s) > 0 THEN TRUE ELSE FALSE END " + - "FROM Customer c RIGHT JOIN Store s ON c.customerId = s.storeId " + - "WHERE c.customerId = :email OR s.storeId = :email") - Boolean existsByEmailInCustomerOrStore(@Param("email") String email); + @Query("SELECT COUNT(c) > 0 FROM Customer c WHERE c.customerId = :email") + Boolean existsInCustomer(@Param("email") String email); + + +// +// @Query("SELECT CASE WHEN (SELECT COUNT(c) FROM Customer c WHERE c.customerId = :email) > 0 " + +// "OR (SELECT COUNT(s) FROM Store s WHERE s.storeId = :email) > 0 " + +// "THEN TRUE ELSE FALSE END") +// Boolean existsByEmailInCustomerOrStore(@Param("email") String email); } \ No newline at end of file diff --git a/src/main/java/org/nmfw/foodietree/domain/auth/service/EmailService.java b/src/main/java/org/nmfw/foodietree/domain/auth/service/EmailService.java index c81ac002..e44999fc 100644 --- a/src/main/java/org/nmfw/foodietree/domain/auth/service/EmailService.java +++ b/src/main/java/org/nmfw/foodietree/domain/auth/service/EmailService.java @@ -6,6 +6,8 @@ import org.nmfw.foodietree.domain.auth.entity.EmailVerification; import org.nmfw.foodietree.domain.auth.repository.EmailRepository; import org.nmfw.foodietree.domain.auth.security.TokenProvider; +import org.nmfw.foodietree.domain.customer.repository.CustomerRepository; +import org.nmfw.foodietree.domain.store.repository.StoreRepository; import org.springframework.boot.context.config.ConfigDataResourceNotFoundException; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.MimeMessageHelper; @@ -24,6 +26,8 @@ public class EmailService { private final JavaMailSender javaMailSender; private final EmailRepository emailRepository; + private final CustomerRepository customerRepository; + private final StoreRepository storeRepository; private final TokenProvider tokenProvider; public EmailCodeDto getEmailCodeDtoByEmail(String email) { @@ -51,9 +55,9 @@ public void updateEmailVerification(EmailCodeDto dto) { @Transactional public Boolean existsByEmailInCustomerOrStore(String email) { - Boolean flag = emailRepository.existsByEmailInCustomerOrStore(email); - log.info(" 실제 유저 테이블에 존재하는지 확인하는 값 flag : {}", flag); - return flag; + Boolean existsInCustomer = customerRepository.existsByCustomerId(email); + Boolean existsInStore = storeRepository.existsByStoreId(email); + return existsInCustomer || existsInStore; } diff --git a/src/main/java/org/nmfw/foodietree/domain/customer/repository/CustomerRepository.java b/src/main/java/org/nmfw/foodietree/domain/customer/repository/CustomerRepository.java index 106ebc41..c34af2c8 100644 --- a/src/main/java/org/nmfw/foodietree/domain/customer/repository/CustomerRepository.java +++ b/src/main/java/org/nmfw/foodietree/domain/customer/repository/CustomerRepository.java @@ -15,6 +15,9 @@ @Transactional public interface CustomerRepository extends JpaRepository ,CustomerRepositoryCustom { + @Query("SELECT COUNT(s) > 0 FROM Store s WHERE s.storeId = :email") + Boolean existsInStore(@Param("email") String email); + @Query("SELECT c.refreshTokenExpireDate FROM Customer c WHERE c.customerId = :email") Optional findRefreshDateById(@Param("email") String email); From 1dbbd50b33cf342b615b34fd5eb2a0fbe896f7b1 Mon Sep 17 00:00:00 2001 From: Maybaba <161430857+Maybaba@users.noreply.github.com> Date: Fri, 9 Aug 2024 20:11:01 +0900 Subject: [PATCH 13/13] Feature/mainpage auth (#67) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feature/scrum 95 product main page depends on fav area (#60) * get preferredArea (w/dto) for mainPageRendering * customerId hardcoding for getPreferredAreas * get StoreList based on favArea * get StoreList based on favAreas Test files * # 찜 기능(FavStore 하트 처리) * # sign up logic edit - EmailController.java * # 주석 삭제 등 코드 정리 및 customer, store repository update date 쿼리 추가 * # 중복체크 버그 수정 * # getwriter deleted --------- Co-authored-by: Jeong-in Seo <162551171+munginisfree@users.noreply.github.com> Co-authored-by: hyerin11 --- .../auth/controller/EmailController.java | 45 ++++++------------ .../auth/repository/EmailRepository.java | 7 --- .../domain/auth/security/TokenProvider.java | 10 ---- .../auth/security/filter/AuthJwtFilter.java | 22 ++++----- .../domain/auth/service/UserService.java | 29 +++++++++--- .../domain/customer/entity/Customer.java | 6 +-- .../repository/CustomerRepository.java | 7 +-- .../repository/FavAreaRepositoryCustom.java | 13 ++++++ .../FavAreaRepositoryCustomImpl.java | 29 ++++++++++++ .../repository/FavStoreRepository.java | 8 ++++ .../customer/service/CustomerService.java | 7 ++- .../store/controller/FavStoreController.java | 41 +++++++++++++++++ .../store/controller/StoreListController.java | 6 ++- .../store/dto/request/FavStoreRequestDto.java | 21 +++++++++ .../store/repository/FavStoreRepository.java | 15 ++++++ .../repository/StoreListRepositoryCustom.java | 3 ++ .../StoreListRepositoryCustomImpl.java | 46 ++++++++++++++++++- .../store/repository/StoreRepository.java | 8 ++-- .../service/StoreList/FavStoreService.java | 41 +++++++++++++++++ .../service/StoreList/StoreListService.java | 17 ++++--- .../domain/store/service/StoreService.java | 16 +++++-- .../StoreListRepositoryCustomImplTest.java | 7 +++ .../StoreList/StoreListServiceTest.java | 32 +++++++++++++ 23 files changed, 342 insertions(+), 94 deletions(-) create mode 100644 src/main/java/org/nmfw/foodietree/domain/customer/repository/FavAreaRepositoryCustom.java create mode 100644 src/main/java/org/nmfw/foodietree/domain/customer/repository/FavAreaRepositoryCustomImpl.java create mode 100644 src/main/java/org/nmfw/foodietree/domain/store/controller/FavStoreController.java create mode 100644 src/main/java/org/nmfw/foodietree/domain/store/dto/request/FavStoreRequestDto.java create mode 100644 src/main/java/org/nmfw/foodietree/domain/store/repository/FavStoreRepository.java create mode 100644 src/main/java/org/nmfw/foodietree/domain/store/service/StoreList/FavStoreService.java create mode 100644 src/test/java/org/nmfw/foodietree/domain/store/repository/StoreListRepositoryCustomImplTest.java create mode 100644 src/test/java/org/nmfw/foodietree/domain/store/service/StoreList/StoreListServiceTest.java diff --git a/src/main/java/org/nmfw/foodietree/domain/auth/controller/EmailController.java b/src/main/java/org/nmfw/foodietree/domain/auth/controller/EmailController.java index 777b9de9..47a76ce9 100644 --- a/src/main/java/org/nmfw/foodietree/domain/auth/controller/EmailController.java +++ b/src/main/java/org/nmfw/foodietree/domain/auth/controller/EmailController.java @@ -5,24 +5,16 @@ import lombok.extern.slf4j.Slf4j; import org.nmfw.foodietree.domain.auth.dto.EmailCodeDto; import org.nmfw.foodietree.domain.auth.entity.EmailVerification; -import org.nmfw.foodietree.domain.auth.mapper.EmailMapper; import org.nmfw.foodietree.domain.auth.security.TokenProvider; import org.nmfw.foodietree.domain.auth.security.TokenProvider.TokenUserInfo; -import org.nmfw.foodietree.domain.auth.security.filter.AuthJwtFilter; import org.nmfw.foodietree.domain.auth.service.EmailService; import org.nmfw.foodietree.domain.auth.service.UserService; -import org.nmfw.foodietree.domain.customer.service.CustomerService; -import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import javax.mail.MessagingException; -import java.io.Serializable; -import java.time.Instant; import java.time.LocalDateTime; -import java.time.temporal.ChronoUnit; -import java.util.Date; import java.util.Map; import java.util.Optional; @@ -39,10 +31,10 @@ public class EmailController { @GetMapping("/check") @CrossOrigin @ResponseBody - public ResponseEntity check(String keyword) { - log.info("이메일 중복체크 아이디 : {}", keyword); - boolean flag = emailService.existsByEmailInCustomerOrStore(keyword); - log.info("이메일 중복체크 결과 {}", flag); + public ResponseEntity check(@RequestParam String email) { + log.info("이메일 중복체크 아이디 : {}", email); + boolean flag = emailService.existsByEmailInCustomerOrStore(email); + log.info("이메일 중복체크 결과 {}", flag); return ResponseEntity .ok() .body(flag); @@ -91,9 +83,9 @@ public ResponseEntity verifyEmail(@RequestBody Map request) { log.info("user access expire date : {}", accessTokenUserInfo.getTokenExpireDate().toString()); String email = accessTokenUserInfo.getEmail(); - log.info("유효성 검증 후 email : {}",email); + log.info("유효성 검증 후 email : {}", email); String userType = accessTokenUserInfo.getRole(); - log.info("유효성 검증 후 userType : {}",userType); + log.info("유효성 검증 후 userType : {}", userType); // 이메일 dto 정보를 데이터베이스에서 조회 Optional emailVerificationOpt = emailService.findOneByEmail(email); @@ -120,15 +112,15 @@ public ResponseEntity verifyEmail(@RequestBody Map request) { // 이메일 인증이 완료되지 않은 경우 - 실제 회원가입이 되지 않은 경우 if (!emailService.existsByEmailInCustomerOrStore(emailCodeDto.getEmail())) { - log.info("실제 회원가입(테이블에 저장)이 되지 않은 경우 {}", emailCodeDto); + log.info("( customer, store 테이블에 저장)이 되지 않은 경우 {}", emailCodeDto); emailService.updateEmailVerification(emailCodeDto); // 인증정보 true 업데이트 -// return + userService.saveUserInfo(emailCodeDto); } else { // 실제 회원가입이 되어있는 경우, 로그인 하는데 access token 기간이 종료된 경우 // 만료 기한 access, refresh 업데이트 - log.info("email이 실제 회원가입 되어있는 경우 dto {}", emailCodeDto); + log.info("customer, store에 email이 실제 회원가입 되어있는 경우 dto {}", emailCodeDto); return userService.updateUserInfo(emailCodeDto); } } @@ -136,10 +128,8 @@ public ResponseEntity verifyEmail(@RequestBody Map request) { // email table에 인증정보가 없을 경우 즉, access token이 만료되었을경우 // 인증 정보는 상관없이 access token이 만료되었을 경우 } catch (JwtException e) { - log.warn(" 토큰 검증에 실패함, JWT parsing error: {}", e.getMessage()); - } catch (Exception e) { - log.info("JWT parsing error: {}", e.getMessage()); - try { + log.warn("JWT parsing error: {}", e.getMessage(), e); + } // 리프레시 토큰의 만료일자를 확인 - 서버에도 리프레시 토큰 저장 TokenUserInfo refreshTokenUserInfo = tokenProvider.validateAndGetRefreshTokenInfo(refreshToken); @@ -148,9 +138,9 @@ public ResponseEntity verifyEmail(@RequestBody Map request) { String email = refreshTokenUserInfo.getEmail(); String userType = refreshTokenUserInfo.getRole(); - LocalDateTime refreshTokenExpiryDate = userService.getRefreshTokenExpiryDate(email, userType); + LocalDateTime refreshTokenExpiryDate = userService.getUserRefreshTokenExpiryDate(email, userType); - log.info("리이이이이이이이이프레에에에에에시토오오오크크ㅡ으응으으응으ㅡㄴ!!! 서버 만료일자 {}", refreshTokenExpiryDate); + log.info(" 서버에서 리프레시 토큰 만료일자 {}", refreshTokenExpiryDate); if (refreshTokenExpiryDate == null || refreshTokenExpiryDate.isBefore(LocalDateTime.now())) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Map.of("success", false, "message", "Refresh token expired")); @@ -163,16 +153,7 @@ public ResponseEntity verifyEmail(@RequestBody Map request) { .build(); return userService.updateUserInfo(emailCodeDto); - - - } catch (Exception ex) { - log.error("Refresh token parsing error: {}", ex.getMessage()); - return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Map.of("success", false, "message", "Invalid refresh token")); - } } - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Map.of("success", false, "message", "An unexpected error occurred")); } -} - diff --git a/src/main/java/org/nmfw/foodietree/domain/auth/repository/EmailRepository.java b/src/main/java/org/nmfw/foodietree/domain/auth/repository/EmailRepository.java index 712d6a5c..0940a5a7 100644 --- a/src/main/java/org/nmfw/foodietree/domain/auth/repository/EmailRepository.java +++ b/src/main/java/org/nmfw/foodietree/domain/auth/repository/EmailRepository.java @@ -45,11 +45,4 @@ default void saveEmailVerification(EmailCodeDto dto) { @Query("SELECT COUNT(c) > 0 FROM Customer c WHERE c.customerId = :email") Boolean existsInCustomer(@Param("email") String email); - -// -// @Query("SELECT CASE WHEN (SELECT COUNT(c) FROM Customer c WHERE c.customerId = :email) > 0 " + -// "OR (SELECT COUNT(s) FROM Store s WHERE s.storeId = :email) > 0 " + -// "THEN TRUE ELSE FALSE END") -// Boolean existsByEmailInCustomerOrStore(@Param("email") String email); - } \ No newline at end of file diff --git a/src/main/java/org/nmfw/foodietree/domain/auth/security/TokenProvider.java b/src/main/java/org/nmfw/foodietree/domain/auth/security/TokenProvider.java index 1ac1a5b3..ea18b269 100644 --- a/src/main/java/org/nmfw/foodietree/domain/auth/security/TokenProvider.java +++ b/src/main/java/org/nmfw/foodietree/domain/auth/security/TokenProvider.java @@ -65,14 +65,8 @@ public String createToken(EmailCodeDto emailCodeDto) { public String createRefreshToken(String email, String userType) { - byte[] decodedKey = Base64.getDecoder().decode(REFRESH_SECRET_KEY); - System.out.println("Decoded Key Length in Bytes: " + decodedKey.length); - System.out.println("Decoded Key Length in Bits: " + (decodedKey.length * 8)); - byte[] keyBytes = REFRESH_SECRET_KEY.getBytes(); Key key = Keys.hmacShaKeyFor(keyBytes); - System.out.println("Secret Key Length in Bytes: " + key.getEncoded().length); - System.out.println("Secret Key Length in Bits: " + (key.getEncoded().length * 8)); return Jwts.builder() .claim("role", userType) // role 클레임에 userType 추가 @@ -107,10 +101,6 @@ public TokenUserInfo validateAndGetTokenInfo(String token) { log.info("validateAndGetTokenInfo run!!!!"); - if (token == null || token.isEmpty()) { - throw new IllegalArgumentException("JWT String argument cannot be null or empty."); - } - try { //토큰 발급 당시 서명 처리 Claims claims = Jwts.parserBuilder() diff --git a/src/main/java/org/nmfw/foodietree/domain/auth/security/filter/AuthJwtFilter.java b/src/main/java/org/nmfw/foodietree/domain/auth/security/filter/AuthJwtFilter.java index d9c697bb..9dc1c5cc 100644 --- a/src/main/java/org/nmfw/foodietree/domain/auth/security/filter/AuthJwtFilter.java +++ b/src/main/java/org/nmfw/foodietree/domain/auth/security/filter/AuthJwtFilter.java @@ -1,17 +1,12 @@ package org.nmfw.foodietree.domain.auth.security.filter; -import io.jsonwebtoken.Claims; import io.jsonwebtoken.JwtException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.nmfw.foodietree.domain.auth.dto.EmailCodeDto; -import org.nmfw.foodietree.domain.auth.mapper.EmailMapper; import org.nmfw.foodietree.domain.auth.security.TokenProvider; import org.nmfw.foodietree.domain.auth.security.TokenProvider.TokenUserInfo; import org.nmfw.foodietree.domain.auth.service.UserService; -import org.nmfw.foodietree.domain.customer.mapper.CustomerMapper; -import org.nmfw.foodietree.domain.customer.repository.CustomerRepository; -import org.nmfw.foodietree.domain.store.mapper.StoreMapper; import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.authority.SimpleGrantedAuthority; @@ -27,8 +22,6 @@ import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.Date; import java.util.List; @Component @@ -65,7 +58,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse log.error("Refresh token parsing error: {}", ex.getMessage()); log.info("refresh token 기간 지나거나 위조됨 ❌"); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - response.getWriter().write("Invalid refresh token"); +// response.getWriter().write("Invalid refresh token"); return; } } else { @@ -86,15 +79,20 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse } private void handleRefreshToken(HttpServletRequest request, HttpServletResponse response, TokenUserInfo refreshTokenInfo) throws IOException { - String email = refreshTokenInfo.getEmail(); + String email = refreshTokenInfo.getUsername(); String userType = refreshTokenInfo.getRole(); - LocalDateTime refreshTokenExpiryDate = userService.getRefreshTokenExpiryDate(email, userType); + // DB 에 저장된 토큰 만료 일자 + LocalDateTime refreshTokenExpiryDate = userService.getUserRefreshTokenExpiryDate(email, userType); log.info("Refresh token expiry date from server: {}", refreshTokenExpiryDate); - if (refreshTokenExpiryDate == null || refreshTokenExpiryDate.isBefore(LocalDateTime.now())) { + if (refreshTokenExpiryDate.isAfter(LocalDateTime.now())) { + // 로그인 함과 동시에 리프레시 토큰 재발급 + userService.setUserRefreshTokenExpiryDate(email, userType); + log.info("리프레시토큰 재발급 ✅"); + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - response.getWriter().write("Refresh token expired"); +// response.getWriter().write("Refresh token expired"); return; } diff --git a/src/main/java/org/nmfw/foodietree/domain/auth/service/UserService.java b/src/main/java/org/nmfw/foodietree/domain/auth/service/UserService.java index 365bbe65..86a2b156 100644 --- a/src/main/java/org/nmfw/foodietree/domain/auth/service/UserService.java +++ b/src/main/java/org/nmfw/foodietree/domain/auth/service/UserService.java @@ -15,7 +15,6 @@ import java.io.Serializable; import java.time.LocalDateTime; -import java.util.Date; import java.util.Map; @Service @@ -66,8 +65,8 @@ public class UserService { "success", true, "token", token, "refreshToken", refreshToken, - "email", emailCodeDtoUserType, - "role", emailCodeDtoEmail, + "email", emailCodeDtoEmail, + "role", emailCodeDtoUserType, "message", "Token reissued successfully." )); @@ -113,8 +112,8 @@ public class UserService { "success", true, "token", token, "refreshToken", refreshToken, - "email", emailCodeDtoEmail, - "role", emailCodeDtoUserType, + "email", emailCodeDtoEmail, // "email" 키에 email을 할당 + "role", emailCodeDtoUserType, // "role" 키에 userType을 할당 "message", "Token reissued successfully." )); } @@ -151,7 +150,7 @@ public boolean findByEmail(EmailCodeDto emailCodeDto) { return result; } - public LocalDateTime getRefreshTokenExpiryDate(String email, String userType) { + public LocalDateTime getUserRefreshTokenExpiryDate(String email, String userType) { if ("customer".equals(userType)) { Customer customer = customerService.getCustomerById(email); log.info("customer object : {},customer email : {}, customer 의 리프레시 만료일자 인 서버 : {}", customer,customer.getCustomerId(), customer.getRefreshTokenExpireDate()); @@ -166,4 +165,22 @@ public LocalDateTime getRefreshTokenExpiryDate(String email, String userType) { } } + public void setUserRefreshTokenExpiryDate(String email, String userType) { + String newRefreshToken = tokenProvider.createRefreshToken(email, userType); + LocalDateTime newExpiryDate = tokenProvider.getExpirationDateFromRefreshToken(newRefreshToken); + + if ("customer".equals(userType)) { + Customer customer = customerService.getCustomerById(email); + if (customer != null) { + customerService.updateCustomer(newExpiryDate, email); + } + } else if ("store".equals(userType)) { + Store store = storeService.getStoreById(email); + if (store != null) { + storeService.updateStore(newExpiryDate, email); + } + } else { + throw new IllegalArgumentException("Invalid user type"); + } + } } diff --git a/src/main/java/org/nmfw/foodietree/domain/customer/entity/Customer.java b/src/main/java/org/nmfw/foodietree/domain/customer/entity/Customer.java index 2c9e54c0..c6183b3f 100644 --- a/src/main/java/org/nmfw/foodietree/domain/customer/entity/Customer.java +++ b/src/main/java/org/nmfw/foodietree/domain/customer/entity/Customer.java @@ -10,7 +10,7 @@ @Entity -@Getter +@Getter @Setter @Builder @AllArgsConstructor @NoArgsConstructor @@ -29,15 +29,12 @@ public class Customer { @Column(name = "customer_password") private String customerPassword; - @Setter @Column(name = "nickname") private String nickname; - @Setter @Column(name = "customer_phone_number") private String customerPhoneNumber; - @Setter @Column(name = "profile_image") private String profileImage; @@ -47,7 +44,6 @@ public class Customer { @Column(name = "limit_time") private LocalDateTime limitTime; - @Setter @Column(name = "refresh_token_expire_date", nullable = true) private LocalDateTime refreshTokenExpireDate; diff --git a/src/main/java/org/nmfw/foodietree/domain/customer/repository/CustomerRepository.java b/src/main/java/org/nmfw/foodietree/domain/customer/repository/CustomerRepository.java index c34af2c8..d9ffc3f9 100644 --- a/src/main/java/org/nmfw/foodietree/domain/customer/repository/CustomerRepository.java +++ b/src/main/java/org/nmfw/foodietree/domain/customer/repository/CustomerRepository.java @@ -25,8 +25,9 @@ public interface CustomerRepository extends JpaRepository ,Custo @Query("UPDATE Customer c SET c.refreshTokenExpireDate = :refreshTokenExpireDate WHERE c.customerId = :customerId") void updateRefreshTokenExpireDate(@Param("refreshTokenExpireDate") LocalDateTime refreshTokenExpireDate, @Param("customerId") String customerId); - @Query("SELECT COUNT(c) > 0 FROM Customer c WHERE c.customerId = :keyword") - boolean existsByCustomerId(@Param("keyword") String keyword); + @Query("SELECT COUNT(c) > 0 FROM Customer c WHERE c.customerId = :email") + boolean existsByCustomerId(@Param("email") String email); - Optional findByCustomerId(String customerId); + @Query + Optional findByCustomerId(@Param("customerId") String customerId); } diff --git a/src/main/java/org/nmfw/foodietree/domain/customer/repository/FavAreaRepositoryCustom.java b/src/main/java/org/nmfw/foodietree/domain/customer/repository/FavAreaRepositoryCustom.java new file mode 100644 index 00000000..30e1bcf8 --- /dev/null +++ b/src/main/java/org/nmfw/foodietree/domain/customer/repository/FavAreaRepositoryCustom.java @@ -0,0 +1,13 @@ +package org.nmfw.foodietree.domain.customer.repository; + +import org.nmfw.foodietree.domain.customer.dto.resp.UpdateAreaDto; +import org.nmfw.foodietree.domain.customer.entity.FavArea; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface FavAreaRepositoryCustom { + + List findFavAreaByCustomerId(String customerId); +} diff --git a/src/main/java/org/nmfw/foodietree/domain/customer/repository/FavAreaRepositoryCustomImpl.java b/src/main/java/org/nmfw/foodietree/domain/customer/repository/FavAreaRepositoryCustomImpl.java new file mode 100644 index 00000000..f12cb807 --- /dev/null +++ b/src/main/java/org/nmfw/foodietree/domain/customer/repository/FavAreaRepositoryCustomImpl.java @@ -0,0 +1,29 @@ +package org.nmfw.foodietree.domain.customer.repository; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.nmfw.foodietree.domain.customer.dto.resp.UpdateAreaDto; +import org.nmfw.foodietree.domain.customer.entity.FavArea; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.stream.Collectors; +@Repository +@RequiredArgsConstructor +@Slf4j +public class FavAreaRepositoryCustomImpl implements FavAreaRepositoryCustom { + + private final FavAreaRepository favAreaRepository; + + @Override + public List findFavAreaByCustomerId(String customerId) { + List byCustomerId = favAreaRepository.findByCustomerId(customerId); + return byCustomerId.stream() + .map(area -> UpdateAreaDto.builder() + .preferredArea(area.getPreferredArea()) + .alias(area.getAlias()) + .build()) + .collect(Collectors.toList()); + } + +} diff --git a/src/main/java/org/nmfw/foodietree/domain/customer/repository/FavStoreRepository.java b/src/main/java/org/nmfw/foodietree/domain/customer/repository/FavStoreRepository.java index 41bc8b4f..fb001d38 100644 --- a/src/main/java/org/nmfw/foodietree/domain/customer/repository/FavStoreRepository.java +++ b/src/main/java/org/nmfw/foodietree/domain/customer/repository/FavStoreRepository.java @@ -3,7 +3,15 @@ import org.nmfw.foodietree.domain.customer.entity.FavStore; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.List; + public interface FavStoreRepository extends JpaRepository { void deleteByCustomerIdAndStoreId(String customerId, String storeId); + + // 사용자가 누른 StoreId 조회 + FavStore findByCustomerIdAndStoreId(String customerId, String storeId); + // 사용자의 찜 가게 상태 조회 + List findByCustomerId(String customerId); + } diff --git a/src/main/java/org/nmfw/foodietree/domain/customer/service/CustomerService.java b/src/main/java/org/nmfw/foodietree/domain/customer/service/CustomerService.java index 1b31cd8e..ee322b4d 100644 --- a/src/main/java/org/nmfw/foodietree/domain/customer/service/CustomerService.java +++ b/src/main/java/org/nmfw/foodietree/domain/customer/service/CustomerService.java @@ -65,8 +65,8 @@ public void signUpSaveCustomer(EmailCustomerDto emailCustomerDto) { } @Transactional(readOnly = true) - public boolean findOne(String keyword) { - return customerRepository.existsByCustomerId(keyword); + public boolean findOne(String email) { + return customerRepository.existsByCustomerId(email); } @Transactional(readOnly = true) @@ -75,4 +75,7 @@ public Customer getCustomerById(String customerId) { .orElseThrow(() -> new RuntimeException("Customer not found with id: " + customerId)); } + public void updateCustomer(LocalDateTime date, String email) { + customerRepository.updateRefreshTokenExpireDate(date, email); + } } \ No newline at end of file diff --git a/src/main/java/org/nmfw/foodietree/domain/store/controller/FavStoreController.java b/src/main/java/org/nmfw/foodietree/domain/store/controller/FavStoreController.java new file mode 100644 index 00000000..2c3e8707 --- /dev/null +++ b/src/main/java/org/nmfw/foodietree/domain/store/controller/FavStoreController.java @@ -0,0 +1,41 @@ +package org.nmfw.foodietree.domain.store.controller; + +import org.nmfw.foodietree.domain.store.dto.request.FavStoreRequestDto; +import org.nmfw.foodietree.domain.store.service.StoreList.FavStoreService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.Map; + +@CrossOrigin +@RestController +@RequestMapping("/api/favorites") +public class FavStoreController { + + @Autowired + private FavStoreService favStoreService; + + @PostMapping("/{storeId}") + public ResponseEntity> toggleFavorite(@PathVariable String storeId, @RequestBody FavStoreRequestDto requestDto) { + String customerId = requestDto.getCustomerId(); + favStoreService.toggleFavorite(customerId, storeId); + + Map response = new HashMap<>(); + response.put("message", "Favorite toggled successfully"); + + return ResponseEntity.ok(response); + } + + @GetMapping("/{customerId}") + public ResponseEntity getFavorites(@PathVariable String customerId) { + try { + return ResponseEntity.ok(favStoreService.getFavoritesByCustomerId(customerId)); + } catch (Exception e) { + e.printStackTrace(); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Error retrieving favorites"); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/nmfw/foodietree/domain/store/controller/StoreListController.java b/src/main/java/org/nmfw/foodietree/domain/store/controller/StoreListController.java index d3c90875..50af5f0b 100644 --- a/src/main/java/org/nmfw/foodietree/domain/store/controller/StoreListController.java +++ b/src/main/java/org/nmfw/foodietree/domain/store/controller/StoreListController.java @@ -23,10 +23,14 @@ public class StoreListController { private final StoreListService storeListService; + // 현재 hardcoding 된 값 사용 + // 추후 변경 + String customerId = "test@gmail.com"; + // Store 전체 조회 요청! @GetMapping public ResponseEntity> getAllStores() { - List storeListDto = storeListService.getAllStores(); + List storeListDto = storeListService.getAllStores(customerId); return ResponseEntity.ok().body(storeListDto); } diff --git a/src/main/java/org/nmfw/foodietree/domain/store/dto/request/FavStoreRequestDto.java b/src/main/java/org/nmfw/foodietree/domain/store/dto/request/FavStoreRequestDto.java new file mode 100644 index 00000000..03fc61e7 --- /dev/null +++ b/src/main/java/org/nmfw/foodietree/domain/store/dto/request/FavStoreRequestDto.java @@ -0,0 +1,21 @@ +package org.nmfw.foodietree.domain.store.dto.request; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class FavStoreRequestDto { + private final String customerId; + private final String storeId; + + @JsonCreator + public FavStoreRequestDto( + @JsonProperty("customerId") String customerId, + @JsonProperty("storeId") String storeId) { + this.customerId = customerId; + this.storeId = storeId; + } +} diff --git a/src/main/java/org/nmfw/foodietree/domain/store/repository/FavStoreRepository.java b/src/main/java/org/nmfw/foodietree/domain/store/repository/FavStoreRepository.java new file mode 100644 index 00000000..c75c349c --- /dev/null +++ b/src/main/java/org/nmfw/foodietree/domain/store/repository/FavStoreRepository.java @@ -0,0 +1,15 @@ +//package org.nmfw.foodietree.domain.store.repository; +// +//import org.nmfw.foodietree.domain.customer.entity.FavStore; +//import org.springframework.data.jpa.repository.JpaRepository; +//import org.springframework.stereotype.Repository; +// +//import java.util.List; +// +//@Repository +//public interface FavStoreRepository extends JpaRepository { +// // 사용자가 누른 StoreId 조회 +// FavStore findByCustomerIdAndStoreId(String customerId, String storeId); +// // 사용자의 찜 가게 상태 조회 +// List findByCustomerId(String customerId); +//} diff --git a/src/main/java/org/nmfw/foodietree/domain/store/repository/StoreListRepositoryCustom.java b/src/main/java/org/nmfw/foodietree/domain/store/repository/StoreListRepositoryCustom.java index 37aa9e97..ee5ca410 100644 --- a/src/main/java/org/nmfw/foodietree/domain/store/repository/StoreListRepositoryCustom.java +++ b/src/main/java/org/nmfw/foodietree/domain/store/repository/StoreListRepositoryCustom.java @@ -1,5 +1,7 @@ package org.nmfw.foodietree.domain.store.repository; +import org.nmfw.foodietree.domain.customer.dto.resp.UpdateAreaDto; +import org.nmfw.foodietree.domain.customer.entity.FavArea; import org.nmfw.foodietree.domain.store.dto.resp.StoreListDto; import org.nmfw.foodietree.domain.store.entity.Store; import org.nmfw.foodietree.domain.store.entity.value.StoreCategory; @@ -10,4 +12,5 @@ public interface StoreListRepositoryCustom { List findStoresByCategory(StoreCategory category); + List findAllStoresByFavArea(List favouriteAreas); } diff --git a/src/main/java/org/nmfw/foodietree/domain/store/repository/StoreListRepositoryCustomImpl.java b/src/main/java/org/nmfw/foodietree/domain/store/repository/StoreListRepositoryCustomImpl.java index b639afdd..2bc88cd2 100644 --- a/src/main/java/org/nmfw/foodietree/domain/store/repository/StoreListRepositoryCustomImpl.java +++ b/src/main/java/org/nmfw/foodietree/domain/store/repository/StoreListRepositoryCustomImpl.java @@ -1,14 +1,16 @@ package org.nmfw.foodietree.domain.store.repository; +import com.querydsl.core.BooleanBuilder; import com.querydsl.jpa.impl.JPAQueryFactory; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.nmfw.foodietree.domain.customer.dto.resp.UpdateAreaDto; import org.nmfw.foodietree.domain.store.dto.resp.StoreListDto; -import org.nmfw.foodietree.domain.store.entity.QStore; import org.nmfw.foodietree.domain.store.entity.value.StoreCategory; import org.springframework.stereotype.Repository; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; import static org.nmfw.foodietree.domain.store.entity.QStore.store; @@ -19,6 +21,7 @@ public class StoreListRepositoryCustomImpl implements StoreListRepositoryCustom { private final JPAQueryFactory jpaQueryFactory; +// private final FavAreaRepository favAreaRepository; @Override public List findStoresByCategory(StoreCategory category) { @@ -30,4 +33,45 @@ public List findStoresByCategory(StoreCategory category) { .map(StoreListDto::fromEntity) .collect(Collectors.toList()); } + + @Override + public List findAllStoresByFavArea(List favouriteAreas) { + + // 선호지역 주소에서 도시 부분만 추출 + List favoriteCities = favouriteAreas.stream() + .map(UpdateAreaDto::getPreferredArea) + .map(this::extractCity) // 도시 부분 추출하는 helper method + .filter(Objects::nonNull) + .distinct() + .collect(Collectors.toList()); + log.info("Favorite cities: {}", favoriteCities); + + // 선호 도시들을 기반으로 가게를 조회하는 쿼리 작성 + BooleanBuilder booleanBuilder = new BooleanBuilder(); + for (String city : favoriteCities) { + booleanBuilder.or(store.address.contains(city)); + } + + List stores = jpaQueryFactory + .selectFrom(store) + .where(booleanBuilder) + .fetch() + .stream() + .map(StoreListDto::fromEntity) + .collect(Collectors.toList()); + + log.info("Stores found: {}", stores); + return stores; + } + + // 도시 부분을 추출하는 helper method - 현재는 데이터가 부족해 '시'로만 추출 + private String extractCity(String address) { + String[] parts = address.split(" "); + for (String part : parts) { + if (part.endsWith("시")) { + return part; + } + } + return null; + } } diff --git a/src/main/java/org/nmfw/foodietree/domain/store/repository/StoreRepository.java b/src/main/java/org/nmfw/foodietree/domain/store/repository/StoreRepository.java index 753ab4f6..8099fcdb 100644 --- a/src/main/java/org/nmfw/foodietree/domain/store/repository/StoreRepository.java +++ b/src/main/java/org/nmfw/foodietree/domain/store/repository/StoreRepository.java @@ -17,10 +17,10 @@ public interface StoreRepository extends JpaRepository { @Transactional @Query("UPDATE Store s SET s.refreshTokenExpireDate = :refreshTokenExpireDate WHERE s.storeId = :storeId") void updateRefreshTokenExpireDate( - @Param("storeId") String storeId, - @Param("refreshTokenExpireDate") LocalDateTime refreshTokenExpireDate + @Param("refreshTokenExpireDate") LocalDateTime refreshTokenExpireDate, + @Param("storeId") String storeId ); - @Query("SELECT COUNT(c) > 0 FROM Store c WHERE c.storeId = :keyword") - boolean existsByStoreId(@Param("keyword") String keyword); + @Query("SELECT COUNT(c) > 0 FROM Store c WHERE c.storeId = :email") + boolean existsByStoreId(@Param("email") String email); } diff --git a/src/main/java/org/nmfw/foodietree/domain/store/service/StoreList/FavStoreService.java b/src/main/java/org/nmfw/foodietree/domain/store/service/StoreList/FavStoreService.java new file mode 100644 index 00000000..566dd9e1 --- /dev/null +++ b/src/main/java/org/nmfw/foodietree/domain/store/service/StoreList/FavStoreService.java @@ -0,0 +1,41 @@ +package org.nmfw.foodietree.domain.store.service.StoreList; + +import org.nmfw.foodietree.domain.customer.entity.FavStore; +import org.nmfw.foodietree.domain.customer.repository.FavStoreRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class FavStoreService { + + @Autowired + private FavStoreRepository favStoreRepository; + + public void toggleFavorite(String customerId, String storeId) { + if (favStoreRepository == null) { + throw new IllegalStateException("FavStoreRepository is not initialized"); + } + FavStore existingFavStore = favStoreRepository.findByCustomerIdAndStoreId(customerId, storeId); + + if (existingFavStore == null) { + // 새로운 찜 상태 저장 + FavStore favStore = FavStore.builder() + .customerId(customerId) + .storeId(storeId) + .build(); + favStoreRepository.save(favStore); + } else { + // 기존 찜 상태 삭제 + favStoreRepository.delete(existingFavStore); + } + } + + public List getFavoritesByCustomerId(String customerId) { + if (favStoreRepository == null) { + throw new IllegalStateException("FavStoreRepository is not initialized"); + } + return favStoreRepository.findByCustomerId(customerId); + } +} diff --git a/src/main/java/org/nmfw/foodietree/domain/store/service/StoreList/StoreListService.java b/src/main/java/org/nmfw/foodietree/domain/store/service/StoreList/StoreListService.java index d37a8183..0f8e05e6 100644 --- a/src/main/java/org/nmfw/foodietree/domain/store/service/StoreList/StoreListService.java +++ b/src/main/java/org/nmfw/foodietree/domain/store/service/StoreList/StoreListService.java @@ -2,6 +2,11 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.nmfw.foodietree.domain.customer.dto.resp.UpdateAreaDto; +import org.nmfw.foodietree.domain.customer.entity.FavArea; +import org.nmfw.foodietree.domain.customer.repository.FavAreaRepository; +import org.nmfw.foodietree.domain.customer.repository.FavAreaRepositoryCustom; +import org.nmfw.foodietree.domain.customer.service.FavAreaService; import org.nmfw.foodietree.domain.store.dto.resp.StoreListDto; import org.nmfw.foodietree.domain.store.entity.Store; import org.nmfw.foodietree.domain.store.entity.value.StoreCategory; @@ -10,6 +15,7 @@ import org.springframework.stereotype.Service; import javax.transaction.Transactional; +import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; @@ -18,16 +24,13 @@ @Slf4j @Transactional public class StoreListService { - private final StoreListRepository storeListRepository; private final StoreListRepositoryCustom storeListRepositoryCustom; - + private final FavAreaRepositoryCustom favAreaRepositoryCustom; // 모든 가게 리스트 출력 - public List getAllStores() { - List stores = storeListRepository.findAll(); - return stores.stream() - .map(StoreListDto::fromEntity) - .collect(Collectors.toList()); + public List getAllStores(String customerId) { + List favouriteAreas = favAreaRepositoryCustom.findFavAreaByCustomerId(customerId); + return storeListRepositoryCustom.findAllStoresByFavArea(favouriteAreas); } // 해당 카테고리 별 리스트 출력 diff --git a/src/main/java/org/nmfw/foodietree/domain/store/service/StoreService.java b/src/main/java/org/nmfw/foodietree/domain/store/service/StoreService.java index 69828abc..f3029faa 100644 --- a/src/main/java/org/nmfw/foodietree/domain/store/service/StoreService.java +++ b/src/main/java/org/nmfw/foodietree/domain/store/service/StoreService.java @@ -5,6 +5,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.annotations.Param; import org.nmfw.foodietree.domain.auth.dto.EmailCodeStoreDto; import org.nmfw.foodietree.domain.customer.dto.request.AutoLoginDto; import org.nmfw.foodietree.domain.customer.dto.resp.LoginUserInfoDto; @@ -17,6 +18,8 @@ import org.nmfw.foodietree.domain.store.entity.Store; import org.nmfw.foodietree.domain.store.mapper.StoreMapper; import org.nmfw.foodietree.domain.store.repository.StoreRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -39,8 +42,8 @@ public class StoreService { public void signUpUpdateStore(EmailCodeStoreDto emailCodeStoreDto) { storeRepository.updateRefreshTokenExpireDate( - emailCodeStoreDto.getStoreId(), - emailCodeStoreDto.getRefreshTokenExpireDate() + emailCodeStoreDto.getRefreshTokenExpireDate(), + emailCodeStoreDto.getStoreId() ); } @@ -62,7 +65,12 @@ public Store getStoreById(String storeId) { } @Transactional(readOnly = true) - public boolean findOne(String keyword) { - return storeRepository.existsByStoreId(keyword); + public boolean findOne(String email) { + return storeRepository.existsByStoreId(email); + } + + + public void updateStore(LocalDateTime date, String email) { + storeRepository.updateRefreshTokenExpireDate(date, email); } } diff --git a/src/test/java/org/nmfw/foodietree/domain/store/repository/StoreListRepositoryCustomImplTest.java b/src/test/java/org/nmfw/foodietree/domain/store/repository/StoreListRepositoryCustomImplTest.java new file mode 100644 index 00000000..558e2760 --- /dev/null +++ b/src/test/java/org/nmfw/foodietree/domain/store/repository/StoreListRepositoryCustomImplTest.java @@ -0,0 +1,7 @@ +package org.nmfw.foodietree.domain.store.repository; + +import static org.junit.jupiter.api.Assertions.*; + +class StoreListRepositoryCustomImplTest { + +} \ No newline at end of file diff --git a/src/test/java/org/nmfw/foodietree/domain/store/service/StoreList/StoreListServiceTest.java b/src/test/java/org/nmfw/foodietree/domain/store/service/StoreList/StoreListServiceTest.java new file mode 100644 index 00000000..a61e9914 --- /dev/null +++ b/src/test/java/org/nmfw/foodietree/domain/store/service/StoreList/StoreListServiceTest.java @@ -0,0 +1,32 @@ +package org.nmfw.foodietree.domain.store.service.StoreList; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.nmfw.foodietree.domain.store.dto.resp.StoreListDto; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; +@SpringBootTest +@Transactional +class StoreListServiceTest { + + @Autowired + StoreListService storeListService; + + @Test + @DisplayName("선호지역에 기반한 가게 출력") + void getStoreByFavAreas() { + //given + String customerId="test@gmail.com"; + //when + List allStores = storeListService.getAllStores(customerId); + //then + System.out.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + System.out.println("allStores = " + allStores); + System.out.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + } +} \ No newline at end of file