diff --git a/src/main/java/com/perfact/be/domain/auth/controller/AuthController.java b/src/main/java/com/perfact/be/domain/auth/controller/AuthController.java index fb6b4be..e093ffa 100644 --- a/src/main/java/com/perfact/be/domain/auth/controller/AuthController.java +++ b/src/main/java/com/perfact/be/domain/auth/controller/AuthController.java @@ -48,6 +48,18 @@ public ApiResponse socialLogin( return ApiResponse.of(AuthSuccessStatus.SOCIAL_LOGIN_SUCCESS, response); } + @Operation( + summary = "게스트 로그인", + description = "UUID 기반 게스트 계정을 생성하고 엑세스/리프레시 토큰을 발급합니다." + ) + @PostMapping(value = "/guest-login", produces = "application/json") + public ApiResponse guestLogin( + ) { + AuthResponseDto.LoginResponse response = authService.guestLogin(); + return ApiResponse.of(AuthSuccessStatus.GUEST_LOGIN_SUCCESS, response); + } + + @Operation( summary = "엑세스 토큰 재발급", description = "리프레시 토큰을 이용해 엑세스 토큰을 재발급합니다." diff --git a/src/main/java/com/perfact/be/domain/auth/exception/status/AuthSuccessStatus.java b/src/main/java/com/perfact/be/domain/auth/exception/status/AuthSuccessStatus.java index e1f6cf3..e776a0c 100644 --- a/src/main/java/com/perfact/be/domain/auth/exception/status/AuthSuccessStatus.java +++ b/src/main/java/com/perfact/be/domain/auth/exception/status/AuthSuccessStatus.java @@ -13,7 +13,10 @@ public enum AuthSuccessStatus implements BaseCode { SOCIAL_LOGIN_SUCCESS(HttpStatus.OK, "AUTH2001", "소셜 로그인이 완료되었습니다."), DEV_TOKEN_ISSUED(HttpStatus.OK, "AUTH2002", "개발용 액세스 토큰 발급이 완료되었습니다."), LOGOUT_SUCCESS(HttpStatus.OK, "AUTH2003", "로그아웃이 완료되었습니다."), - AT_REFRESH_SUCCESS(HttpStatus.OK, "AUTH2004", "엑세스 토큰 재생성이 완료되었습니다."); + AT_REFRESH_SUCCESS(HttpStatus.OK, "AUTH2004", "엑세스 토큰 재생성이 완료되었습니다."), + GUEST_LOGIN_SUCCESS(HttpStatus.OK, "AUTH2005", "게스트 로그인이 완료되었습니다."), + + ; private final HttpStatus httpStatus; private final String code; diff --git a/src/main/java/com/perfact/be/domain/auth/service/AuthService.java b/src/main/java/com/perfact/be/domain/auth/service/AuthService.java index ac8e085..3e4665f 100644 --- a/src/main/java/com/perfact/be/domain/auth/service/AuthService.java +++ b/src/main/java/com/perfact/be/domain/auth/service/AuthService.java @@ -1,6 +1,7 @@ package com.perfact.be.domain.auth.service; import com.perfact.be.domain.auth.dto.AuthResponseDto; +import com.perfact.be.domain.auth.dto.AuthResponseDto.LoginResponse; import com.perfact.be.domain.auth.dto.AuthResponseDto.TokenResponse; import com.perfact.be.domain.user.entity.User; import jakarta.validation.constraints.NotNull; @@ -12,4 +13,6 @@ public interface AuthService { TokenResponse refreshAccessToken(User loginUser, String refreshToken); void logout(User loginUser, String refreshToken); + + AuthResponseDto.LoginResponse guestLogin(); } diff --git a/src/main/java/com/perfact/be/domain/auth/service/AuthServiceImpl.java b/src/main/java/com/perfact/be/domain/auth/service/AuthServiceImpl.java index f62791e..ff53cad 100644 --- a/src/main/java/com/perfact/be/domain/auth/service/AuthServiceImpl.java +++ b/src/main/java/com/perfact/be/domain/auth/service/AuthServiceImpl.java @@ -1,6 +1,7 @@ package com.perfact.be.domain.auth.service; import com.perfact.be.domain.auth.dto.AuthResponseDto; +import com.perfact.be.domain.auth.dto.AuthResponseDto.LoginResponse; import com.perfact.be.domain.auth.dto.AuthResponseDto.TokenResponse; import com.perfact.be.domain.auth.dto.NaverTokenResponse; import com.perfact.be.domain.auth.dto.NaverUserInfoResponse; @@ -85,6 +86,21 @@ public void logout(User loginUser, String refreshToken) { redisTemplate.delete(redisKey); } + @Override + public LoginResponse guestLogin() { + String deviceUuid = UUID.randomUUID().toString(); + User guestUser = userService.createGuestUser(deviceUuid); + + String accessToken = jwtProvider.generateAccessToken(guestUser.getId(), deviceUuid); + + String rtUuid = UUID.randomUUID().toString(); + String refreshToken = jwtProvider.generateRefreshToken(guestUser.getId(), deviceUuid, rtUuid); + + String redisKey = "RT:" + deviceUuid + ":" + rtUuid; + redisTemplate.opsForValue().set(redisKey, refreshToken, 3, TimeUnit.HOURS); + return new AuthResponseDto.LoginResponse(guestUser.getId(), guestUser.getEmail(), accessToken, refreshToken); + } + private String getAccessToken(String code, String state) { NaverTokenResponse tokenResponse = webClient diff --git a/src/main/java/com/perfact/be/domain/credit/entity/enums/PlanType.java b/src/main/java/com/perfact/be/domain/credit/entity/enums/PlanType.java index 5f053e0..ce2d614 100644 --- a/src/main/java/com/perfact/be/domain/credit/entity/enums/PlanType.java +++ b/src/main/java/com/perfact/be/domain/credit/entity/enums/PlanType.java @@ -1,5 +1,5 @@ package com.perfact.be.domain.credit.entity.enums; public enum PlanType { - FREE, STANDARD, PREMIUM + FREE, STANDARD, PREMIUM, GUEST } \ No newline at end of file diff --git a/src/main/java/com/perfact/be/domain/user/entity/User.java b/src/main/java/com/perfact/be/domain/user/entity/User.java index 20cab66..56ad388 100644 --- a/src/main/java/com/perfact/be/domain/user/entity/User.java +++ b/src/main/java/com/perfact/be/domain/user/entity/User.java @@ -27,7 +27,7 @@ public class User extends BaseEntity { private String socialId; @Enumerated(EnumType.STRING) private SocialType socialType; - @Column(name = "nickname", length = 10) + @Column(name = "nickname", length = 20) private String nickname; @Column(name = "email", length = 255) private String email; @@ -35,7 +35,6 @@ public class User extends BaseEntity { @Column(length = 20) private Role role; @Enumerated(EnumType.STRING) - @Builder.Default @Column(length = 20, nullable = false) private UserStatus status = UserStatus.ACTIVE; diff --git a/src/main/java/com/perfact/be/domain/user/entity/enums/Role.java b/src/main/java/com/perfact/be/domain/user/entity/enums/Role.java index c190676..71db7ef 100644 --- a/src/main/java/com/perfact/be/domain/user/entity/enums/Role.java +++ b/src/main/java/com/perfact/be/domain/user/entity/enums/Role.java @@ -1,5 +1,5 @@ package com.perfact.be.domain.user.entity.enums; public enum Role { - ROLE_USER, ROLE_ADMIN + ROLE_USER, ROLE_ADMIN, ROLE_GUEST; } diff --git a/src/main/java/com/perfact/be/domain/user/entity/enums/SocialType.java b/src/main/java/com/perfact/be/domain/user/entity/enums/SocialType.java index 97ca9ba..eea57fe 100644 --- a/src/main/java/com/perfact/be/domain/user/entity/enums/SocialType.java +++ b/src/main/java/com/perfact/be/domain/user/entity/enums/SocialType.java @@ -1,5 +1,5 @@ package com.perfact.be.domain.user.entity.enums; public enum SocialType { - NAVER, KAKAO + NAVER, KAKAO, GUEST } diff --git a/src/main/java/com/perfact/be/domain/user/service/UserService.java b/src/main/java/com/perfact/be/domain/user/service/UserService.java index 7eb4941..5f3d9c8 100644 --- a/src/main/java/com/perfact/be/domain/user/service/UserService.java +++ b/src/main/java/com/perfact/be/domain/user/service/UserService.java @@ -16,4 +16,6 @@ public interface UserService { void decreaseCredit(User user, int cost); NicknameResponse getNickname(User loginUser); + + User createGuestUser(String deviceUuid); } diff --git a/src/main/java/com/perfact/be/domain/user/service/UserServiceImpl.java b/src/main/java/com/perfact/be/domain/user/service/UserServiceImpl.java index c54abd5..e8b90a6 100644 --- a/src/main/java/com/perfact/be/domain/user/service/UserServiceImpl.java +++ b/src/main/java/com/perfact/be/domain/user/service/UserServiceImpl.java @@ -57,10 +57,23 @@ public SubscribeStatusResponse getSubscribeStatus(User loginUser) { String planName = plan != null ? plan.getName().toString() : "UNKNOWN"; boolean isFreePlan = "FREE".equals(planName); - String subscribeStatus = isFreePlan ? "무료 플랜 사용 중" : "유료 플랜 사용 중"; - String nextBillingDate = isFreePlan ? "무료 플랜 사용 중" : "미정"; + boolean isGuestPlan = "GUEST".equals(planName); + + String subscribeStatus; + String nextBillingDate; Long dailyCredit = plan != null ? plan.getDailyCredit() : 0L; + if (isGuestPlan) { + subscribeStatus = "게스트 모드 사용 중"; + nextBillingDate = "게스트 모드는 결제/갱신 없음"; + } else if (isFreePlan) { + subscribeStatus = "무료 플랜 사용 중"; + nextBillingDate = "무료 플랜 사용 중"; + } else { + subscribeStatus = "유료 플랜 사용 중"; + nextBillingDate = "미정"; // TODO : 유료 결제 로직 붙으면 수정 + } + LocalDateTime startOfToday = LocalDate.now().atStartOfDay(); LocalDateTime startOfTomorrow = startOfToday.plusDays(1); @@ -70,20 +83,20 @@ public SubscribeStatusResponse getSubscribeStatus(User loginUser) { // 오늘 사용량 Long todayUsage = Optional.ofNullable( - creditLogRepository.sumUsedCreditByUserAndTypeAndCreatedAtBetween( - loginUser, - CreditLogType.REPORT_CREATE, - startOfToday, - startOfTomorrow)) + creditLogRepository.sumUsedCreditByUserAndTypeAndCreatedAtBetween( + loginUser, + CreditLogType.REPORT_CREATE, + startOfToday, + startOfTomorrow)) .map(Math::abs).orElse(0L); // 이번 달 사용량 Long thisMonthUsage = Optional.ofNullable( - creditLogRepository.sumUsedCreditByUserAndTypeAndCreatedAtBetween( - loginUser, - CreditLogType.REPORT_CREATE, - startOfMonth, - startOfNextMonth)) + creditLogRepository.sumUsedCreditByUserAndTypeAndCreatedAtBetween( + loginUser, + CreditLogType.REPORT_CREATE, + startOfMonth, + startOfNextMonth)) .map(Math::abs).orElse(0L); return new SubscribeStatusResponse( planName, @@ -91,7 +104,8 @@ public SubscribeStatusResponse getSubscribeStatus(User loginUser) { nextBillingDate, dailyCredit, todayUsage, - thisMonthUsage); + thisMonthUsage + ); } @Override @@ -99,6 +113,34 @@ public NicknameResponse getNickname(User loginUser) { return new NicknameResponse(loginUser.getNickname()); } + @Override + @Transactional + public User createGuestUser(String deviceUuid) { + return userRepository.findBySocialIdAndSocialType(deviceUuid, SocialType.GUEST) + .orElseGet(() -> { + // 테스트로 지정하기 + SubscriptionPlans testPlan = subscriptionPlansRepository.findByName(PlanType.GUEST) + .orElseThrow(() -> new UserHandler(UserErrorStatus.PLAN_NOT_FOUND)); + + String nick = "게스트-" + deviceUuid.substring(0, 8); + + User newGuest = User.builder() + .email(null) // 게스트는 이메일 없음.. 기획 보고 나중에 수정해야 될 수도 있음. + .nickname(nick) + .socialId(deviceUuid) + .socialType(SocialType.GUEST) + .role(Role.ROLE_GUEST) + .status(UserStatus.ACTIVE) + .plan(testPlan) + .isSubscribe(false) + .isNotificationAgreed(false) + .build(); + + return userRepository.save(newGuest); + }); + } + + @Override @Transactional public void decreaseCredit(User user, int amount) { diff --git a/src/main/java/com/perfact/be/global/config/SecurityConfig.java b/src/main/java/com/perfact/be/global/config/SecurityConfig.java index ce4d9b6..2d41173 100644 --- a/src/main/java/com/perfact/be/global/config/SecurityConfig.java +++ b/src/main/java/com/perfact/be/global/config/SecurityConfig.java @@ -39,6 +39,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { "/v3/api-docs/**", "/swagger-ui.html", "/api/auth/social-login/**", + "/api/auth/guest-login/**", "/dev/auth/**" ).permitAll() .anyRequest().authenticated()