From 7b1fed3e569a6a7c4a4f0d80bae3643669d737d0 Mon Sep 17 00:00:00 2001 From: nayonsoso Date: Sun, 28 Jan 2024 07:27:26 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=20=ED=83=88=ED=87=B4?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SolidConnectionApplication.java | 2 ++ .../auth/controller/AuthController.java | 16 +++++---- .../auth/dto/SignUpResponseDto.java | 13 +++++++ .../auth/service/AuthService.java | 34 +++++++++++++++---- .../auth/service/KakaoOAuthService.java | 9 +++++ .../solidconnection/entity/SiteUser.java | 15 ++++---- .../CountryRepository.java | 2 +- .../InterestedCountyRepository.java | 2 +- .../InterestedRegionRepository.java | 2 +- .../RegionRepository.java | 2 +- .../scheduler/UserRemovalScheduler.java | 17 ++++++++++ .../repository/SiteUserRepository.java | 7 ++++ .../siteuser/service/SiteUserService.java | 21 ++++++++++++ 13 files changed, 119 insertions(+), 23 deletions(-) create mode 100644 src/main/java/com/example/solidconnection/auth/dto/SignUpResponseDto.java rename src/main/java/com/example/solidconnection/{country => repositories}/CountryRepository.java (88%) rename src/main/java/com/example/solidconnection/{country => repositories}/InterestedCountyRepository.java (88%) rename src/main/java/com/example/solidconnection/{region => repositories}/InterestedRegionRepository.java (88%) rename src/main/java/com/example/solidconnection/{region => repositories}/RegionRepository.java (88%) create mode 100644 src/main/java/com/example/solidconnection/scheduler/UserRemovalScheduler.java create mode 100644 src/main/java/com/example/solidconnection/siteuser/service/SiteUserService.java diff --git a/src/main/java/com/example/solidconnection/SolidConnectionApplication.java b/src/main/java/com/example/solidconnection/SolidConnectionApplication.java index 1e378189c..81e27f96b 100644 --- a/src/main/java/com/example/solidconnection/SolidConnectionApplication.java +++ b/src/main/java/com/example/solidconnection/SolidConnectionApplication.java @@ -2,7 +2,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableScheduling; +@EnableScheduling @SpringBootApplication public class SolidConnectionApplication { diff --git a/src/main/java/com/example/solidconnection/auth/controller/AuthController.java b/src/main/java/com/example/solidconnection/auth/controller/AuthController.java index ce79d85f1..348f7bf15 100644 --- a/src/main/java/com/example/solidconnection/auth/controller/AuthController.java +++ b/src/main/java/com/example/solidconnection/auth/controller/AuthController.java @@ -1,6 +1,7 @@ package com.example.solidconnection.auth.controller; import com.example.solidconnection.auth.dto.SignUpRequestDto; +import com.example.solidconnection.auth.dto.SignUpResponseDto; import com.example.solidconnection.auth.dto.kakao.KakaoCodeDto; import com.example.solidconnection.auth.dto.kakao.KakaoOauthResponseDto; import com.example.solidconnection.auth.service.AuthService; @@ -9,10 +10,7 @@ import com.example.solidconnection.custom.response.DataResponse; import com.example.solidconnection.custom.response.StatusResponse; import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import java.security.Principal; @@ -31,8 +29,8 @@ public CustomResponse kakaoOauth(@RequestBody KakaoCodeDto kakaoCodeDto) { @PostMapping("/sign-up") public CustomResponse signUp(@RequestBody SignUpRequestDto signUpRequestDto) { - boolean status = authService.signUp(signUpRequestDto); - return new StatusResponse(status); + SignUpResponseDto signUpResponseDto = authService.signUp(signUpRequestDto); + return new DataResponse<>(signUpResponseDto); } @PostMapping("/sign-out") @@ -40,4 +38,10 @@ public CustomResponse signOut(Principal principal) { boolean status = authService.signOut(principal.getName()); return new StatusResponse(status); } + + @PatchMapping("/quit") + public CustomResponse quit(Principal principal) { + boolean status = authService.quit(principal.getName()); + return new StatusResponse(status); + } } diff --git a/src/main/java/com/example/solidconnection/auth/dto/SignUpResponseDto.java b/src/main/java/com/example/solidconnection/auth/dto/SignUpResponseDto.java new file mode 100644 index 000000000..3db1c11f4 --- /dev/null +++ b/src/main/java/com/example/solidconnection/auth/dto/SignUpResponseDto.java @@ -0,0 +1,13 @@ +package com.example.solidconnection.auth.dto; + +import lombok.*; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class SignUpResponseDto { + private String accessToken; + private String refreshToken; +} diff --git a/src/main/java/com/example/solidconnection/auth/service/AuthService.java b/src/main/java/com/example/solidconnection/auth/service/AuthService.java index 6c50de055..d98e80d04 100644 --- a/src/main/java/com/example/solidconnection/auth/service/AuthService.java +++ b/src/main/java/com/example/solidconnection/auth/service/AuthService.java @@ -2,15 +2,16 @@ import com.example.solidconnection.auth.dto.SignUpRequestDto; +import com.example.solidconnection.auth.dto.SignUpResponseDto; import com.example.solidconnection.config.token.TokenService; import com.example.solidconnection.config.token.TokenType; import com.example.solidconnection.config.token.TokenValidator; -import com.example.solidconnection.country.CountryRepository; -import com.example.solidconnection.country.InterestedCountyRepository; import com.example.solidconnection.custom.exception.CustomException; import com.example.solidconnection.entity.*; -import com.example.solidconnection.region.InterestedRegionRepository; -import com.example.solidconnection.region.RegionRepository; +import com.example.solidconnection.repositories.CountryRepository; +import com.example.solidconnection.repositories.InterestedCountyRepository; +import com.example.solidconnection.repositories.InterestedRegionRepository; +import com.example.solidconnection.repositories.RegionRepository; import com.example.solidconnection.siteuser.repository.SiteUserRepository; import com.example.solidconnection.type.CountryCode; import com.example.solidconnection.type.RegionCode; @@ -18,6 +19,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.time.LocalDate; import java.time.format.DateTimeFormatter; @@ -29,6 +31,7 @@ import static com.example.solidconnection.custom.exception.ErrorCode.*; @Service +@Transactional @RequiredArgsConstructor public class AuthService { @@ -41,7 +44,7 @@ public class AuthService { private final CountryRepository countryRepository; private final InterestedCountyRepository interestedCountyRepository; - public boolean signUp(SignUpRequestDto signUpRequestDto) { + public SignUpResponseDto signUp(SignUpRequestDto signUpRequestDto) { tokenValidator.validateKakaoToken(signUpRequestDto.getKakaoOauthToken()); validateUserNotDuplicated(signUpRequestDto); validateNicknameDuplicated(signUpRequestDto.getNickname()); @@ -52,7 +55,16 @@ public boolean signUp(SignUpRequestDto signUpRequestDto) { saveInterestedRegion(signUpRequestDto, savedSiteUser); saveInterestedCountry(signUpRequestDto, savedSiteUser); - return true; + + String email = savedSiteUser.getEmail(); + String accessToken = tokenService.generateToken(email, TokenType.ACCESS); + String refreshToken = tokenService.generateToken(email, TokenType.REFRESH); + tokenService.saveToken(refreshToken, TokenType.REFRESH); + + return SignUpResponseDto.builder() + .accessToken(accessToken) + .refreshToken(refreshToken) + .build(); } public boolean signOut(String email){ @@ -65,6 +77,16 @@ public boolean signOut(String email){ return true; } + public boolean quit(String email){ + SiteUser siteUser = getValidatedUser(email); + siteUser.setQuitedAt(LocalDate.now().plusDays(1)); + return true; + } + + private SiteUser getValidatedUser(String email){ + return siteUserRepository.findByEmail(email).orElseThrow(() -> new CustomException(USER_NOT_FOUND)); + } + private void validateUserNotDuplicated(SignUpRequestDto signUpRequestDto){ String email = tokenService.getEmail(signUpRequestDto.getKakaoOauthToken()); if(siteUserRepository.existsByEmail(email)){ diff --git a/src/main/java/com/example/solidconnection/auth/service/KakaoOAuthService.java b/src/main/java/com/example/solidconnection/auth/service/KakaoOAuthService.java index 08d681a6c..4583802ba 100644 --- a/src/main/java/com/example/solidconnection/auth/service/KakaoOAuthService.java +++ b/src/main/java/com/example/solidconnection/auth/service/KakaoOAuthService.java @@ -5,11 +5,13 @@ import com.example.solidconnection.config.token.TokenService; import com.example.solidconnection.config.token.TokenType; import com.example.solidconnection.custom.exception.CustomException; +import com.example.solidconnection.entity.SiteUser; import com.example.solidconnection.siteuser.repository.SiteUserRepository; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.*; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; @@ -18,6 +20,7 @@ import static com.example.solidconnection.custom.exception.ErrorCode.*; @Service +@Transactional @RequiredArgsConstructor public class KakaoOAuthService { @@ -40,6 +43,7 @@ public KakaoOauthResponseDto processOauth(String code) throws CustomException { String email = kakaoUserInfoDto.getKakaoAccount().getEmail(); boolean isAlreadyRegistered = siteUserRepository.existsByEmail(email); if (isAlreadyRegistered) { + resetQuitedAt(email); return kakaoSignIn(email); } String kakaoOauthToken = tokenService.generateToken(email, TokenType.KAKAO_OAUTH); @@ -103,4 +107,9 @@ private SignInResponseDto kakaoSignIn(String email) { .refreshToken(refreshToken) .build(); } + + public void resetQuitedAt(String email){ + SiteUser siteUser = siteUserRepository.findByEmail(email).orElseThrow(() -> new CustomException(USER_NOT_FOUND)); + siteUser.setQuitedAt(null); + } } diff --git a/src/main/java/com/example/solidconnection/entity/SiteUser.java b/src/main/java/com/example/solidconnection/entity/SiteUser.java index 485e97ffa..f42d2c1b6 100644 --- a/src/main/java/com/example/solidconnection/entity/SiteUser.java +++ b/src/main/java/com/example/solidconnection/entity/SiteUser.java @@ -4,12 +4,9 @@ import com.example.solidconnection.type.PreparationStatus; import com.example.solidconnection.type.Role; import jakarta.persistence.*; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; +import lombok.*; -import java.time.LocalDateTime; +import java.time.LocalDate; import java.util.Set; @Entity @@ -26,9 +23,11 @@ public class SiteUser { private String email; @Column(nullable = false, length = 100) + @Setter private String nickname; @Column(length = 500) + @Setter private String profileImageUrl; @Column(nullable = false, length = 20) @@ -46,9 +45,11 @@ public class SiteUser { @Enumerated(EnumType.STRING) private Gender gender; - private LocalDateTime nicknameModifiedAt; + @Setter + private LocalDate nicknameModifiedAt; - private LocalDateTime quitedAt; + @Setter + private LocalDate quitedAt; // 연관관계 @OneToMany(mappedBy = "siteUser") diff --git a/src/main/java/com/example/solidconnection/country/CountryRepository.java b/src/main/java/com/example/solidconnection/repositories/CountryRepository.java similarity index 88% rename from src/main/java/com/example/solidconnection/country/CountryRepository.java rename to src/main/java/com/example/solidconnection/repositories/CountryRepository.java index 53657de3b..a61fc08d4 100644 --- a/src/main/java/com/example/solidconnection/country/CountryRepository.java +++ b/src/main/java/com/example/solidconnection/repositories/CountryRepository.java @@ -1,4 +1,4 @@ -package com.example.solidconnection.country; +package com.example.solidconnection.repositories; import com.example.solidconnection.entity.Country; import com.example.solidconnection.type.CountryCode; diff --git a/src/main/java/com/example/solidconnection/country/InterestedCountyRepository.java b/src/main/java/com/example/solidconnection/repositories/InterestedCountyRepository.java similarity index 88% rename from src/main/java/com/example/solidconnection/country/InterestedCountyRepository.java rename to src/main/java/com/example/solidconnection/repositories/InterestedCountyRepository.java index 907b304a1..fff21cf38 100644 --- a/src/main/java/com/example/solidconnection/country/InterestedCountyRepository.java +++ b/src/main/java/com/example/solidconnection/repositories/InterestedCountyRepository.java @@ -1,4 +1,4 @@ -package com.example.solidconnection.country; +package com.example.solidconnection.repositories; import com.example.solidconnection.entity.InterestedCountry; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/com/example/solidconnection/region/InterestedRegionRepository.java b/src/main/java/com/example/solidconnection/repositories/InterestedRegionRepository.java similarity index 88% rename from src/main/java/com/example/solidconnection/region/InterestedRegionRepository.java rename to src/main/java/com/example/solidconnection/repositories/InterestedRegionRepository.java index b7e3bb4ec..9b429b63c 100644 --- a/src/main/java/com/example/solidconnection/region/InterestedRegionRepository.java +++ b/src/main/java/com/example/solidconnection/repositories/InterestedRegionRepository.java @@ -1,4 +1,4 @@ -package com.example.solidconnection.region; +package com.example.solidconnection.repositories; import com.example.solidconnection.entity.InterestedRegion; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/com/example/solidconnection/region/RegionRepository.java b/src/main/java/com/example/solidconnection/repositories/RegionRepository.java similarity index 88% rename from src/main/java/com/example/solidconnection/region/RegionRepository.java rename to src/main/java/com/example/solidconnection/repositories/RegionRepository.java index 2c99b0e08..907295a13 100644 --- a/src/main/java/com/example/solidconnection/region/RegionRepository.java +++ b/src/main/java/com/example/solidconnection/repositories/RegionRepository.java @@ -1,4 +1,4 @@ -package com.example.solidconnection.region; +package com.example.solidconnection.repositories; import com.example.solidconnection.entity.Region; import com.example.solidconnection.type.RegionCode; diff --git a/src/main/java/com/example/solidconnection/scheduler/UserRemovalScheduler.java b/src/main/java/com/example/solidconnection/scheduler/UserRemovalScheduler.java new file mode 100644 index 000000000..07c67ca2c --- /dev/null +++ b/src/main/java/com/example/solidconnection/scheduler/UserRemovalScheduler.java @@ -0,0 +1,17 @@ +package com.example.solidconnection.scheduler; + +import com.example.solidconnection.siteuser.service.SiteUserService; +import lombok.RequiredArgsConstructor; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class UserRemovalScheduler { + private final SiteUserService siteUserService; + + @Scheduled(cron = "0 0 0 * * ?") // 매일 자정에 실행 + public void scheduledUserRemoval() { + siteUserService.deleteUsersNeverVisitedAfterQuited(); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/solidconnection/siteuser/repository/SiteUserRepository.java b/src/main/java/com/example/solidconnection/siteuser/repository/SiteUserRepository.java index df58d5ac2..f557becb9 100644 --- a/src/main/java/com/example/solidconnection/siteuser/repository/SiteUserRepository.java +++ b/src/main/java/com/example/solidconnection/siteuser/repository/SiteUserRepository.java @@ -2,8 +2,12 @@ import com.example.solidconnection.entity.SiteUser; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import java.time.LocalDate; +import java.util.List; import java.util.Optional; @Repository @@ -11,4 +15,7 @@ public interface SiteUserRepository extends JpaRepository { Optional findByEmail(String email); boolean existsByEmail(String email); boolean existsByNickname(String nickname); + + @Query("SELECT u FROM SiteUser u WHERE u.quitedAt <= :cutoffDate") + List findUsersToBeRemoved(@Param("cutoffDate") LocalDate cutoffDate); } \ No newline at end of file diff --git a/src/main/java/com/example/solidconnection/siteuser/service/SiteUserService.java b/src/main/java/com/example/solidconnection/siteuser/service/SiteUserService.java new file mode 100644 index 000000000..6d700d3ff --- /dev/null +++ b/src/main/java/com/example/solidconnection/siteuser/service/SiteUserService.java @@ -0,0 +1,21 @@ +package com.example.solidconnection.siteuser.service; + +import com.example.solidconnection.entity.SiteUser; +import com.example.solidconnection.siteuser.repository.SiteUserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.time.LocalDate; +import java.util.List; + +@Service +@RequiredArgsConstructor +public class SiteUserService { + private final SiteUserRepository siteUserRepository; + + public void deleteUsersNeverVisitedAfterQuited() { + LocalDate cutoffDate = LocalDate.now().minusDays(30); + List usersToRemove = siteUserRepository.findUsersToBeRemoved(cutoffDate); + siteUserRepository.deleteAll(usersToRemove); + } +} \ No newline at end of file