diff --git a/src/main/java/com/universe/uni/controller/AuthController.java b/src/main/java/com/universe/uni/controller/AuthController.java index 77e3b37..604da5f 100644 --- a/src/main/java/com/universe/uni/controller/AuthController.java +++ b/src/main/java/com/universe/uni/controller/AuthController.java @@ -35,10 +35,15 @@ public AuthTokenDto authByGoogle(@RequestBody AuthRequestDto request) { return authService.authWithGoogle(request.code()); } - @GetMapping("kakao/callback") - public AuthRequestDto redirectKakaoAuth(@RequestParam(name = "code") String authenticationCode) { - return new AuthRequestDto(authenticationCode); - } + @PostMapping("apple") + public AuthTokenDto authByApple(@RequestBody AuthRequestDto request) { + return authService.authWithAppleUser(request.code()); + } + + @GetMapping("kakao/callback") + public AuthRequestDto redirectKakaoAuth(@RequestParam(name = "code") String authenticationCode) { + return new AuthRequestDto(authenticationCode); + } @GetMapping("google/callback") public AuthRequestDto redirectGoogleAuth(@RequestParam(name = "code") String authenticationCode) { diff --git a/src/main/java/com/universe/uni/domain/ApplePayload.java b/src/main/java/com/universe/uni/domain/ApplePayload.java new file mode 100644 index 0000000..a08ccbf --- /dev/null +++ b/src/main/java/com/universe/uni/domain/ApplePayload.java @@ -0,0 +1,29 @@ +package com.universe.uni.domain; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public record ApplePayload( + @JsonProperty("aud") + String aud, + @JsonProperty("auth_time") + Long authTime, + @JsonProperty("c_hash") + String cHash, + @JsonProperty("email") + String email, + @JsonProperty("email_verified") + String emailVerified, + @JsonProperty("exp") + Long exp, + @JsonProperty("iat") + Long iat, + @JsonProperty("is_private_email") + String isPrivateEmail, + @JsonProperty("iss") + String iss, + @JsonProperty("nonce_supported") + Boolean nonceSupported, + @JsonProperty("sub") + String sub +) { +} diff --git a/src/main/java/com/universe/uni/domain/AppleTokenDecodeManager.java b/src/main/java/com/universe/uni/domain/AppleTokenDecodeManager.java new file mode 100644 index 0000000..68113a9 --- /dev/null +++ b/src/main/java/com/universe/uni/domain/AppleTokenDecodeManager.java @@ -0,0 +1,31 @@ +package com.universe.uni.domain; + +import java.util.Base64; + +import org.springframework.stereotype.Component; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.universe.uni.exception.UnauthorizedException; +import com.universe.uni.exception.dto.ErrorType; + +@Component +public class AppleTokenDecodeManager implements AppleTokenManager { + + @Override + public String decodeEmail(String token) { + String[] encodedToken = token.split("\\."); + String encodedPayload = encodedToken[1]; + Base64.Decoder decoder = Base64.getDecoder(); + String payLoad = new String(decoder.decode(encodedPayload)); + + ObjectMapper mapper = new ObjectMapper(); + ApplePayload applePayload = null; + try { + applePayload = mapper.readValue(payLoad, ApplePayload.class); + } catch (JsonProcessingException e) { + throw new UnauthorizedException(ErrorType.UNSUPPORTED_TOKEN); + } + return applePayload.email(); + } +} diff --git a/src/main/java/com/universe/uni/domain/AppleTokenManager.java b/src/main/java/com/universe/uni/domain/AppleTokenManager.java new file mode 100644 index 0000000..365c8e1 --- /dev/null +++ b/src/main/java/com/universe/uni/domain/AppleTokenManager.java @@ -0,0 +1,8 @@ +package com.universe.uni.domain; + +import com.fasterxml.jackson.core.JsonProcessingException; + +public interface AppleTokenManager { + + String decodeEmail(String token); +} diff --git a/src/main/java/com/universe/uni/dto/response/WishCouponResponseDto.java b/src/main/java/com/universe/uni/dto/response/WishCouponResponseDto.java index 903a64a..9a4da0f 100644 --- a/src/main/java/com/universe/uni/dto/response/WishCouponResponseDto.java +++ b/src/main/java/com/universe/uni/dto/response/WishCouponResponseDto.java @@ -5,10 +5,11 @@ import lombok.Builder; -@JsonPropertyOrder({"isMine", "wishCoupon"}) +@JsonPropertyOrder({"isMine", "nickname", "wishCoupon"}) @Builder public record WishCouponResponseDto( boolean isMine, + String nickname, WishCouponDto wishCoupon ) { } diff --git a/src/main/java/com/universe/uni/service/AuthService.java b/src/main/java/com/universe/uni/service/AuthService.java index bbd7979..d25a529 100644 --- a/src/main/java/com/universe/uni/service/AuthService.java +++ b/src/main/java/com/universe/uni/service/AuthService.java @@ -4,12 +4,14 @@ import org.springframework.stereotype.Service; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.universe.uni.domain.AppleTokenManager; import com.universe.uni.domain.SnsType; import com.universe.uni.domain.entity.User; import com.universe.uni.dto.AuthTokenDto; -import com.universe.uni.external.response.GoogleAccessTokenResponse; +import com.universe.uni.exception.ApiException; +import com.universe.uni.exception.dto.ErrorType; import com.universe.uni.external.response.GoogleUserInfoResponse; -import com.universe.uni.external.response.KakaoAuthResponse; import com.universe.uni.external.response.KakaoUserResponse; import com.universe.uni.repository.GoogleRepository; import com.universe.uni.repository.KakaoRepository; @@ -25,6 +27,7 @@ public class AuthService implements AuthServiceContract { private final KakaoRepository kakaoRepository; private final GoogleRepository googleRepository; private final UserRepository userRepository; + private final AppleTokenManager appleTokenManager; @Override @Transactional @@ -58,6 +61,22 @@ private User registerGoogleUser(GoogleUserInfoResponse googleUser) { .build(); } + @Override + @Transactional + public AuthTokenDto authWithAppleUser(String identityToken) { + final String userEmail = appleTokenManager.decodeEmail(identityToken); + final User user = userRepository.findBySnsAuthCode(userEmail) + .orElseGet(() -> userRepository.save(registerAppleUser(userEmail))); + return beIssuedAuthToken(user.getId()); + } + + private User registerAppleUser(String email) { + return User.builder() + .snsType(SnsType.APPLE) + .snsAuthCode(email) + .build(); + } + private AuthTokenDto beIssuedAuthToken(long userId) { return jwtManager.issueToken(userId); } diff --git a/src/main/java/com/universe/uni/service/AuthServiceContract.java b/src/main/java/com/universe/uni/service/AuthServiceContract.java index 0345002..094ff5c 100644 --- a/src/main/java/com/universe/uni/service/AuthServiceContract.java +++ b/src/main/java/com/universe/uni/service/AuthServiceContract.java @@ -10,4 +10,7 @@ public interface AuthServiceContract { @Transactional AuthTokenDto authWithGoogle(String accessToken); + + @Transactional + AuthTokenDto authWithAppleUser(String identityToken); } diff --git a/src/main/java/com/universe/uni/service/HomeService.java b/src/main/java/com/universe/uni/service/HomeService.java index 2de0fe6..ccffee2 100644 --- a/src/main/java/com/universe/uni/service/HomeService.java +++ b/src/main/java/com/universe/uni/service/HomeService.java @@ -1,7 +1,8 @@ package com.universe.uni.service; -import java.time.LocalDate; -import java.time.Period; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; import java.util.List; import org.springframework.stereotype.Service; @@ -73,9 +74,9 @@ private int calculateScore(List gameHistoryList, GameResult res } private int calculateDays(Couple couple) { - LocalDate today = LocalDate.now(); - Period period = Period.between(couple.getStartDate(), today); - return period.getDays() + 1; + ZonedDateTime localTime = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + long dDay = ChronoUnit.DAYS.between(couple.getStartDate(), localTime.toLocalDate()); + return (int)dDay + 1; } private CoupleDto fromCoupleToCoupleDtoMapper(Couple couple) { diff --git a/src/main/java/com/universe/uni/service/UserService.java b/src/main/java/com/universe/uni/service/UserService.java index 02ccbb0..a2261d9 100644 --- a/src/main/java/com/universe/uni/service/UserService.java +++ b/src/main/java/com/universe/uni/service/UserService.java @@ -75,7 +75,7 @@ public UserWishCouponResponseDto getUserWishCouponList(Long userId) { int availableWishCoupon = (int)wishCouponList.stream().filter(wishCoupon -> !wishCoupon.isUsed()).count(); - int newWishCoupon = (int)wishCouponList.stream().filter(WishCoupon::isVisible).count(); + int newWishCoupon = (int)wishCouponList.stream().filter(wishCoupon -> !wishCoupon.isVisible()).count(); List wishCouponDtoList = wishCouponList.stream() .sorted(Comparator.comparing(WishCoupon::getId).reversed()) @@ -84,6 +84,7 @@ public UserWishCouponResponseDto getUserWishCouponList(Long userId) { .stream() .flatMap(entry -> entry.getValue().stream()) .map(this::fromWishCouponToWishCouponDto) + .filter(wishCouponDto -> !wishCouponDto.content().isBlank()) .collect(Collectors.toList()); return UserWishCouponResponseDto.builder() diff --git a/src/main/java/com/universe/uni/service/WishCouponService.java b/src/main/java/com/universe/uni/service/WishCouponService.java index f6a575a..3520219 100644 --- a/src/main/java/com/universe/uni/service/WishCouponService.java +++ b/src/main/java/com/universe/uni/service/WishCouponService.java @@ -65,6 +65,7 @@ public WishCouponResponseDto getWishCoupon(Long wishCouponId) { return WishCouponResponseDto.builder() .isMine(isMine) + .nickname(user.getNickname()) .wishCoupon(fromWishCouponToWishCouponDto(wishCoupon)) .build(); }