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..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,25 +5,18 @@ 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; @RestController @RequiredArgsConstructor @@ -33,44 +26,20 @@ 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("/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"; - } + @GetMapping("/check") + @CrossOrigin + @ResponseBody + public ResponseEntity check(@RequestParam String email) { + log.info("이메일 중복체크 아이디 : {}", email); + boolean flag = emailService.existsByEmailInCustomerOrStore(email); + log.info("이메일 중복체크 결과 {}", flag); + return ResponseEntity + .ok() + .body(flag); } - */ - - // 인증 코드 전송 - @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,115 +66,94 @@ public ResponseEntity sendVerificationLink(@RequestBody Map r @PostMapping("/verifyEmail") public ResponseEntity verifyEmail(@RequestBody Map request) { - log.info("Request Data: {}", request); - - String token = request.get("token"); - String refreshToken = request.get("refreshToken"); + log.info("Request Data: {}", request); - log.info("access token 있는지 확인 {}", token); - log.info("refresh token 있는지 확인 {}", refreshToken); + String token = request.get("token"); + String refreshToken = request.get("refreshToken"); - try { - // access token 유효성 검사 - TokenUserInfo accessTokenUserInfo = tokenProvider.validateAndGetTokenInfo(token); + log.info("access token 있는지 확인 {}", token); + log.info("refresh token 있는지 확인 {}", refreshToken); - 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(); + 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(); + log.info("유효성 검증 후 email : {}", email); + String userType = accessTokenUserInfo.getRole(); + log.info("유효성 검증 후 userType : {}", userType); + + // 이메일 dto 정보를 데이터베이스에서 조회 + Optional emailVerificationOpt = emailService.findOneByEmail(email); + + if (emailVerificationOpt.isPresent()) { + EmailVerification emailVerification = emailVerificationOpt.get(); + EmailCodeDto emailCodeDto = EmailCodeDto.builder() + .email(emailVerification.getEmail()) + .expiryDate(emailVerification.getExpiryDate()) + .emailVerified(true) + .userType(userType) + .build(); - // 이메일 dto 정보를 데이터베이스에서 조회 - EmailCodeDto emailCodeDto = emailMapper.findOneByEmail(email); log.info("EmailCodeDto retrieved from database: {}", emailCodeDto); + // 이메일 정보가 인증 테이블에 있을 경우 if (emailCodeDto != null) { + + log.info("이메일 정보 테이블에 해당 이메일 있음 {}", email); 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.")); - } + if (!emailService.existsByEmailInCustomerOrStore(emailCodeDto.getEmail())) { - //실제 회원가입(테이블에 저장)이 되지 않은 경우 false - if (!(userService.findByEmail(emailCodeDto))) { + log.info("( customer, store 테이블에 저장)이 되지 않은 경우 {}", emailCodeDto); - log.info("실제 회원가입(테이블에 저장)이 되지 않은 경우 {}", emailCodeDto); - emailMapper.update(emailCodeDto); // 인증정보 true 업데이트 - return userService.saveUserInfo(emailCodeDto); + emailService.updateEmailVerification(emailCodeDto); // 인증정보 true 업데이트 + userService.saveUserInfo(emailCodeDto); } else { // 실제 회원가입이 되어있는 경우, 로그인 하는데 access token 기간이 종료된 경우 // 만료 기한 access, refresh 업데이트 - log.info("email이 실제 회원가입 되어있는 경우 dto {}", emailCodeDto); + log.info("customer, store에 email이 실제 회원가입 되어있는 경우 dto {}", emailCodeDto); return userService.updateUserInfo(emailCodeDto); } } + } + // email table에 인증정보가 없을 경우 즉, access token이 만료되었을경우 + // 인증 정보는 상관없이 access token이 만료되었을 경우 + } catch (JwtException e) { + log.warn("JWT parsing error: {}", e.getMessage(), e); + } + // 리프레시 토큰의 만료일자를 확인 - 서버에도 리프레시 토큰 저장 + TokenUserInfo refreshTokenUserInfo = tokenProvider.validateAndGetRefreshTokenInfo(refreshToken); - // 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); - - log.info("리이이이이이이이이프레에에에에에시토오오오크크ㅡ으응으으응으ㅡㄴ!!! 서버 만료일자 {}", refreshTokenExpiryDate); - - if (refreshTokenExpiryDate == null || refreshTokenExpiryDate.isBefore(LocalDateTime.now())) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Map.of("success", false, "message", "Refresh token expired")); - } + log.info("token provider tokenUserInfo로 리프레시토큰의 만료일자 확인하기 위한 값 : {} ", refreshTokenUserInfo); - // 새로운 액세스 토큰 발급 - EmailCodeDto emailCodeDto = EmailCodeDto.builder() - .email(email) - .userType(userType) - .build(); + String email = refreshTokenUserInfo.getEmail(); + String userType = refreshTokenUserInfo.getRole(); - // Call updateUserInfo and capture its response - return userService.updateUserInfo(emailCodeDto); + LocalDateTime refreshTokenExpiryDate = userService.getUserRefreshTokenExpiryDate(email, userType); + log.info(" 서버에서 리프레시 토큰 만료일자 {}", refreshTokenExpiryDate); - } 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")); + if (refreshTokenExpiryDate == null || refreshTokenExpiryDate.isBefore(LocalDateTime.now())) { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Map.of("success", false, "message", "Refresh token expired")); } - } - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Map.of("success", false, "message", "An unexpected error occurred")); - } - } + // 새로운 액세스 토큰 발급 + EmailCodeDto emailCodeDto = EmailCodeDto.builder() + .email(email) + .userType(userType) + .build(); - /* - @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"); + 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 07b04f8a..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 @@ -3,6 +3,7 @@ import lombok.*; import org.nmfw.foodietree.domain.auth.entity.EmailVerification; +import javax.persistence.Entity; import java.time.LocalDateTime; import java.util.Date; @@ -14,21 +15,8 @@ @Builder public class EmailCodeDto{ private String email; //nullable - private String code; //추후 refresh token 도입예정 - @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..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 @@ -1,49 +1,49 @@ 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.auth.dto.EmailCodeDto; 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 { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) private Long id; @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; @Column(name = "email_verified", nullable = true) - private boolean emailVerified; + private Boolean emailVerified; @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; + 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/mapper/EmailMapper.java b/src/main/java/org/nmfw/foodietree/domain/auth/mapper/EmailMapper.java index 71504aa1..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,12 +11,10 @@ public interface EmailMapper { void save(EmailCodeDto dto); - int findByEmail(String email); + void update(EmailCodeDto emailCodeDto); - default boolean isEmailExists(String email) { - return findByEmail(email) > 0; - } EmailCodeDto findOneByEmail(String email); - void update(EmailCodeDto emailCodeDto); + Boolean existsByEmail(String email); + } 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..0940a5a7 --- /dev/null +++ b/src/main/java/org/nmfw/foodietree/domain/auth/repository/EmailRepository.java @@ -0,0 +1,48 @@ +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; +import java.util.Optional; + +@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") + Optional 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 COUNT(c) > 0 FROM Customer c WHERE c.customerId = :email") + Boolean existsInCustomer(@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..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 @@ -25,6 +25,7 @@ import java.util.Date; import java.util.List; + @Component @Slf4j public class TokenProvider { @@ -62,18 +63,10 @@ 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); - 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 추가 @@ -85,24 +78,28 @@ public String createRefreshToken(String email, String userType) { .compact(); } - public Date getExpirationDateFromRefreshToken(String refreshToken) { + + public LocalDateTime 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."); - } + log.info("validateAndGetTokenInfo run!!!!"); try { //토큰 발급 당시 서명 처리 @@ -136,12 +133,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/security/filter/AuthJwtFilter.java b/src/main/java/org/nmfw/foodietree/domain/auth/security/filter/AuthJwtFilter.java index 71deb141..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 { @@ -77,6 +70,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse } catch (Exception e) { log.info("refresh token, access token 유효성 검증 통과 둘다 못함 ❌"); + log.warn("Token validation error"); e.printStackTrace(); } @@ -85,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/EmailService.java b/src/main/java/org/nmfw/foodietree/domain/auth/service/EmailService.java index 0ba4a0bc..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 @@ -1,30 +1,23 @@ 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.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; 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; +import java.util.Optional; @Service @Slf4j @@ -32,160 +25,41 @@ public class EmailService { private final JavaMailSender javaMailSender; - private final EmailMapper emailMapper; - + private final EmailRepository emailRepository; + private final CustomerRepository customerRepository; + private final StoreRepository storeRepository; 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); + public EmailCodeDto getEmailCodeDtoByEmail(String email) { + EmailVerification emailVerification = emailRepository.findOneByEmail(email) + .orElseThrow(() -> new RuntimeException("EmailVerification not found for email: " + email)); - 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); + return EmailCodeDto.builder() + .email(emailVerification.getEmail()) + .expiryDate(emailVerification.getExpiryDate()) + .emailVerified(emailVerification.getEmailVerified()) + .userType(emailVerification.getUserType()) + .build(); } - /* - 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; + @Transactional + public Optional findOneByEmail(String email) { + return emailRepository.findOneByEmail(email); } - */ - - 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) { + Boolean existsInCustomer = customerRepository.existsByCustomerId(email); + Boolean existsInStore = storeRepository.existsByStoreId(email); + return existsInCustomer || existsInStore; } - 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 +73,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; 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..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 @@ -7,15 +7,14 @@ 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; import java.io.Serializable; import java.time.LocalDateTime; -import java.util.Date; import java.util.Map; @Service @@ -23,8 +22,8 @@ @RequiredArgsConstructor public class UserService { - private final CustomerMapper customerMapper; - private final StoreMapper storeMapper; + private final CustomerService customerService; + private final StoreService storeService; private final TokenProvider tokenProvider; @@ -32,11 +31,13 @@ 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); - Date expirationDate = tokenProvider.getExpirationDateFromRefreshToken(refreshToken); + LocalDateTime expirationDate = tokenProvider.getExpirationDateFromRefreshToken(refreshToken); // 최초 회원 정보 저장 로직 : customer인지 store 인지 null 값으로 구분 if (emailCodeDtoUserType.equals("store")) { @@ -47,7 +48,7 @@ public class UserService { .refreshTokenExpireDate(expirationDate) .build(); - storeMapper.signUpSaveStore(emailCodeStoreDto); + storeService.signUpSaveStore(emailCodeStoreDto); } else if(emailCodeDtoUserType.equals("customer")) { @@ -57,15 +58,15 @@ public class UserService { .refreshTokenExpireDate(expirationDate) .build(); - customerMapper.signUpSaveCustomer(emailCodeCustomerDto); + customerService.signUpSaveCustomer(emailCodeCustomerDto); } return ResponseEntity.ok(Map.of( "success", true, "token", token, "refreshToken", refreshToken, - "email", emailCodeDtoUserType, - "role", emailCodeDtoEmail, + "email", emailCodeDtoEmail, + "role", emailCodeDtoUserType, "message", "Token reissued successfully." )); @@ -81,7 +82,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 +92,8 @@ public class UserService { .userType(emailCodeDtoUserType) .refreshTokenExpireDate(expirationDate) .build(); - storeMapper.signUpUpdateStore(emailCodeStoreDto); + + storeService.signUpUpdateStore(emailCodeStoreDto); // customer 일 경우 } else if (emailCodeDtoUserType.equals("customer")) { @@ -101,15 +104,16 @@ public class UserService { .refreshTokenExpireDate(expirationDate) .build(); - customerMapper.signUpUpdateCustomer(emailCodeCustomerDto); + customerService.signUpUpdateCustomer(emailCodeCustomerDto); + } return ResponseEntity.ok(Map.of( "success", true, "token", token, "refreshToken", refreshToken, - "email", emailCodeDtoEmail, - "role", emailCodeDtoUserType, + "email", emailCodeDtoEmail, // "email" 키에 email을 할당 + "role", emailCodeDtoUserType, // "role" 키에 userType을 할당 "message", "Token reissued successfully." )); } @@ -128,14 +132,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; } @@ -144,18 +150,37 @@ 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 = 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 { throw new IllegalArgumentException("Invalid user type"); } } -} + 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/controller/CustomerController.java b/src/main/java/org/nmfw/foodietree/domain/customer/controller/CustomerController.java index 1009b5b0..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 @@ -22,27 +22,27 @@ 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) { - log.info("{}", keyword); - - boolean flag = customerService.checkIdentifier(keyword); - 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) { @@ -55,27 +55,6 @@ public ResponseEntity> getMyLocation(@RequestBody Map> 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 73ef6752..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,34 +10,31 @@ @Entity -@Getter +@Getter @Setter @Builder @AllArgsConstructor @NoArgsConstructor -@EqualsAndHashCode(of = "id") +@EqualsAndHashCode(of = "idxCustomerId") @Table(name = "tbl_customer") public class Customer { - @Id + @Id // auto increment @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "idx_customer_id") - private Long 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") 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; @@ -57,12 +53,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; } 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 0eb6be19..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 @@ -1,8 +1,33 @@ package org.nmfw.foodietree.domain.customer.repository; +import org.apache.ibatis.annotations.Param; import org.nmfw.foodietree.domain.customer.entity.Customer; 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; +import java.util.Optional; + +@Repository +@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); + + @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 = :email") + boolean existsByCustomerId(@Param("email") String email); + + @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 5a9bbdf4..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 @@ -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,51 @@ @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()); + customerRepository.updateRefreshTokenExpireDate( + emailCodeCustomerDto.getRefreshTokenExpireDate(), + emailCodeCustomerDto.getCustomerId() + ); + } - // 3. DB에도 해당 쿠키값을 저장 - customerMapper.updateAutoLogin( - AutoLoginDto.builder() - .sessionId(sessionId) - .limitTime(LocalDateTime.now().plusDays(90)) - .id(customerId) - .build() - ); + @Transactional + public void signUpSaveCustomer(EmailCustomerDto emailCustomerDto) { + 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; // 예외를 다시 던져서 롤백 트리거 } - - maintainLoginState(session, foundCustomer); - - return LoginResult.SUCCESS; } - 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); - - session.setAttribute("login", new LoginUserInfoDto(foundCustomer)); + @Transactional(readOnly = true) + public boolean findOne(String email) { + return customerRepository.existsByCustomerId(email); } - // 아이디 중복 검사 - public boolean checkIdentifier(String keyword) { - return customerMapper.existsById(keyword); + @Transactional(readOnly = true) + public Customer getCustomerById(String customerId) { + return customerRepository.findByCustomerId(customerId) + .orElseThrow(() -> new RuntimeException("Customer not found with id: " + customerId)); } - 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() - ); - } + 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/StoreController.java b/src/main/java/org/nmfw/foodietree/domain/store/controller/StoreController.java index 95a8c5ae..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:/"; - } } 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 c33b1bd0..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 @@ -1,10 +1,26 @@ 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("refreshTokenExpireDate") LocalDateTime refreshTokenExpireDate, + @Param("storeId") String storeId + ); + + @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 871dcebd..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,8 @@ 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; import org.nmfw.foodietree.domain.customer.entity.Customer; @@ -15,8 +17,12 @@ 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.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; import org.springframework.web.util.WebUtils; import javax.servlet.http.HttpServletRequest; @@ -30,74 +36,41 @@ @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.getRefreshTokenExpireDate(), + emailCodeStoreDto.getStoreId() + ); } - 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 email) { + return storeRepository.existsByStoreId(email); } - 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 void updateStore(LocalDateTime date, String email) { + storeRepository.updateRefreshTokenExpireDate(date, email); } } 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..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 +} 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