Skip to content

Commit

Permalink
feat: 토큰 검증 로직 추가 (#49)
Browse files Browse the repository at this point in the history
  • Loading branch information
ckkim817 committed Jan 22, 2025
1 parent 2c65e27 commit 8e1a4ae
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import com.acon.server.member.domain.enums.Cuisine;
import com.acon.server.member.domain.enums.DislikeFood;
import com.acon.server.member.domain.enums.FavoriteSpot;
import com.acon.server.member.domain.enums.SocialType;
import com.acon.server.member.domain.enums.SpotStyle;
import com.acon.server.spot.domain.enums.SpotType;
import jakarta.validation.Valid;
Expand Down Expand Up @@ -39,8 +40,10 @@ public class MemberController {
public ResponseEntity<LoginResponse> login(
@Valid @RequestBody final LoginRequest request
) {
SocialType socialType = SocialType.fromValue(request.socialType());

return ResponseEntity.ok(
memberService.login(request)
memberService.login(socialType, request.idToken())
);
}

Expand All @@ -51,8 +54,7 @@ public ResponseEntity<LoginResponse> login(
public ResponseEntity<MemberAreaResponse> postArea(
@Valid @RequestBody final MemberAreaRequest request
) {
// TODO: 토큰 검증 이후 MemberID 추출 필요
String area = memberService.createMemberArea(request.latitude(), request.longitude(), 1L);
String area = memberService.createMemberArea(request.latitude(), request.longitude());

return ResponseEntity.ok(new MemberAreaResponse(area));
}
Expand All @@ -61,15 +63,14 @@ public ResponseEntity<MemberAreaResponse> postArea(
public ResponseEntity<Void> postPreference(
@Valid @RequestBody final PreferenceRequest request
) {
// TODO: 토큰으로 memberId 가져오기
List<DislikeFood> dislikeFoodList = request.dislikeFoodList().stream().map(DislikeFood::fromValue).toList();
List<Cuisine> favoriteCuisineList = request.favoriteCuisineRank().stream().map(Cuisine::fromValue).toList();
SpotType favoriteSpotType = SpotType.fromValue(request.favoriteSpotType());
SpotStyle favoriteSpotStyle = SpotStyle.fromValue(request.favoriteSpotStyle());
List<FavoriteSpot> favoriteSpotRank = request.favoriteSpotRank().stream().map(FavoriteSpot::fromValue).toList();

memberService.createPreference(dislikeFoodList, favoriteCuisineList, favoriteSpotType, favoriteSpotStyle,
favoriteSpotRank, 1L);
favoriteSpotRank);

return ResponseEntity.ok().build();
}
Expand All @@ -79,17 +80,15 @@ public ResponseEntity<Void> postPreference(
public ResponseEntity<Void> postGuidedSpot(
@Valid @RequestBody final GuidedSpotRequest request
) {
// TODO: 토큰 검증 이후 MemberID 추출 필요
memberService.createGuidedSpot(request.spotId(), 1L);
memberService.createGuidedSpot(request.spotId());

return ResponseEntity.ok().build();
}

@GetMapping(path = "/member/acorn", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<AcornCountResponse> getAcornCount() {
// TODO: 토큰으로 memberId 가져오기
return ResponseEntity.ok(
memberService.fetchAcornCount(1L)
memberService.fetchAcornCount()
);
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package com.acon.server.member.api.request;

import com.acon.server.member.domain.enums.SocialType;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;

// TODO: 세부 메시지 추가
public record LoginRequest(
@NotNull
SocialType socialType,
@NotBlank(message = "소셜 로그인 종류는 공백일 수 없습니다.")
String socialType,
@NotNull
@NotBlank
String idToken
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package com.acon.server.member.application.service;

import com.acon.server.common.auth.jwt.JwtUtils;
import com.acon.server.global.auth.MemberAuthentication;
import com.acon.server.global.auth.PrincipalHandler;
import com.acon.server.global.auth.jwt.JwtTokenProvider;
import com.acon.server.global.exception.BusinessException;
import com.acon.server.global.exception.ErrorType;
import com.acon.server.global.external.NaverMapsAdapter;
import com.acon.server.member.api.request.LoginRequest;
import com.acon.server.member.api.response.AcornCountResponse;
import com.acon.server.member.api.response.LoginResponse;
import com.acon.server.member.application.mapper.GuidedSpotMapper;
Expand Down Expand Up @@ -51,47 +52,27 @@ public class MemberService {
private final MemberMapper memberMapper;
private final PreferenceMapper preferenceMapper;

private final JwtUtils jwtUtils;
private final JwtTokenProvider jwtTokenProvider;
private final PrincipalHandler principalHandler;
private final GoogleSocialService googleSocialService;

private final NaverMapsAdapter naverMapsAdapter;

// TODO: 메서드 순서 정리, TRANSACTION 설정, mapper 사용
// TODO: @Valid 거친 건 원시타입으로 받기

public void createPreference(
List<DislikeFood> dislikeFoodList,
List<Cuisine> favoriteCuisineList,
SpotType favoriteSpotType,
SpotStyle favoriteSpotStyle,
List<FavoriteSpot> favoriteSpotRank,
Long memberId
) {
Preference preference = Preference.builder()
.memberId(memberId)
.dislikeFoodList(dislikeFoodList)
.favoriteCuisineRank(favoriteCuisineList)
.favoriteSpotType(favoriteSpotType)
.favoriteSpotStyle(favoriteSpotStyle)
.favoriteSpotRank(favoriteSpotRank)
.build();

preferenceRepository.save(preferenceMapper.toEntity(preference));
}

@Transactional
public LoginResponse login(final LoginRequest request) {
String socialId = googleSocialService.login(request.idToken());
Long memberId = fetchMemberId(request.socialType(), socialId);
List<String> tokens = jwtUtils.createToken(memberId);
return LoginResponse.of(tokens.get(0), tokens.get(1));
}

public AcornCountResponse fetchAcornCount(final Long memberId) {
MemberEntity memberEntity = memberRepository.findByIdOrElseThrow(memberId);
int acornCount = memberEntity.getLeftAcornCount();
public LoginResponse login(
final SocialType socialType,
final String idToken
) {
String socialId = googleSocialService.login(idToken);
Long memberId = fetchMemberId(socialType, socialId);
MemberAuthentication memberAuthentication = new MemberAuthentication(memberId, null, null);
String accessToken = jwtTokenProvider.issueAccessToken(memberAuthentication);
String refreshToken = jwtTokenProvider.issueRefreshToken();

return new AcornCountResponse(acornCount);
return LoginResponse.of(accessToken, refreshToken);
}

private boolean isExistingMember(
Expand All @@ -101,8 +82,12 @@ private boolean isExistingMember(
return memberRepository.findBySocialTypeAndSocialId(socialType, socialId).isPresent();
}

protected Long fetchMemberId(final SocialType socialType, final String socialId) {
protected Long fetchMemberId(
final SocialType socialType,
final String socialId
) {
MemberEntity memberEntity;

if (isExistingMember(socialType, socialId)) {
memberEntity = memberRepository.findBySocialTypeAndSocialId(
socialType,
Expand All @@ -115,18 +100,62 @@ protected Long fetchMemberId(final SocialType socialType, final String socialId)
.leftAcornCount(25)
.build());
}

Member member = memberMapper.toDomain(memberEntity);

return member.getId();
}

@Transactional
public void createGuidedSpot(final Long spotId, final Long memberId) {
public String createMemberArea(
final Double latitude,
final Double longitude
) {
MemberEntity memberEntity = memberRepository.findByIdOrElseThrow(principalHandler.getUserIdFromPrincipal());
String legalDong = naverMapsAdapter.getReverseGeoCodingResult(latitude, longitude);

verifiedAreaRepository.save(
VerifiedAreaEntity.builder()
.name(legalDong)
.memberId(memberEntity.getId())
.verifiedDate(Collections.singletonList(LocalDate.now()))
.build()
);

return legalDong;
}

@Transactional
public void createPreference(
final List<DislikeFood> dislikeFoodList,
final List<Cuisine> favoriteCuisineList,
final SpotType favoriteSpotType,
final SpotStyle favoriteSpotStyle,
final List<FavoriteSpot> favoriteSpotRank
) {
MemberEntity memberEntity = memberRepository.findByIdOrElseThrow(principalHandler.getUserIdFromPrincipal());

Preference preference = Preference.builder()
.memberId(memberEntity.getId())
.dislikeFoodList(dislikeFoodList)
.favoriteCuisineRank(favoriteCuisineList)
.favoriteSpotType(favoriteSpotType)
.favoriteSpotStyle(favoriteSpotStyle)
.favoriteSpotRank(favoriteSpotRank)
.build();

preferenceRepository.save(preferenceMapper.toEntity(preference));
}

@Transactional
public void createGuidedSpot(final Long spotId) {
if (!spotRepository.existsById(spotId)) {
throw new BusinessException(ErrorType.NOT_FOUND_SPOT_ERROR);
}

Optional<GuidedSpotEntity> optionalGuidedSpotEntity = guidedSpotRepository.findByMemberIdAndSpotId(memberId,
spotId);
MemberEntity memberEntity = memberRepository.findByIdOrElseThrow(principalHandler.getUserIdFromPrincipal());
Optional<GuidedSpotEntity> optionalGuidedSpotEntity =
guidedSpotRepository.findByMemberIdAndSpotId(memberEntity.getId(), spotId);

optionalGuidedSpotEntity.ifPresentOrElse(
guidedSpotEntity -> {
Expand All @@ -136,25 +165,19 @@ public void createGuidedSpot(final Long spotId, final Long memberId) {
},
() -> guidedSpotRepository.save(
GuidedSpotEntity.builder()
.memberId(memberId)
.memberId(memberEntity.getId())
.spotId(spotId)
.build()
)
);
}

public String createMemberArea(final Double latitude, final Double longitude, final Long memberId) {
String legalDong = naverMapsAdapter.getReverseGeoCodingResult(latitude, longitude);

verifiedAreaRepository.save(
VerifiedAreaEntity.builder()
.name(legalDong)
.memberId(memberId)
.verifiedDate(Collections.singletonList(LocalDate.now()))
.build()
);
@Transactional(readOnly = true)
public AcornCountResponse fetchAcornCount() {
MemberEntity memberEntity = memberRepository.findByIdOrElseThrow(principalHandler.getUserIdFromPrincipal());
int acornCount = memberEntity.getLeftAcornCount();

return legalDong;
return new AcornCountResponse(acornCount);
}

// TODO: 최근 길 안내 장소 지우는 스케줄러 추가
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.acon.server.review.application.service;

import com.acon.server.global.auth.PrincipalHandler;
import com.acon.server.global.exception.BusinessException;
import com.acon.server.global.exception.ErrorType;
import com.acon.server.member.application.mapper.MemberMapper;
Expand Down Expand Up @@ -31,9 +32,11 @@ public class ReviewService {
private final ReviewMapper reviewMapper;
private final SpotMapper spotMapper;

private final PrincipalHandler principalHandler;

@Transactional
public void createReview(final long spotId, final int acornCount) {
MemberEntity memberEntity = memberRepository.findByIdOrElseThrow(1L);
MemberEntity memberEntity = memberRepository.findByIdOrElseThrow(principalHandler.getUserIdFromPrincipal());
SpotEntity spotEntity = spotRepository.findByIdOrElseThrow(spotId);

validateAcornAvailability(memberEntity.getLeftAcornCount(), acornCount);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package com.acon.server.spot.application.service;

import com.acon.server.global.auth.PrincipalHandler;
import com.acon.server.global.exception.BusinessException;
import com.acon.server.global.exception.ErrorType;
import com.acon.server.global.external.GeoCodingResponse;
import com.acon.server.global.external.NaverMapsAdapter;
import com.acon.server.member.infra.entity.MemberEntity;
import com.acon.server.member.infra.repository.GuidedSpotRepository;
import com.acon.server.member.infra.repository.MemberRepository;
import com.acon.server.spot.api.request.SpotListRequest;
import com.acon.server.spot.api.response.MenuListResponse;
import com.acon.server.spot.api.response.MenuResponse;
Expand Down Expand Up @@ -49,15 +52,18 @@ public class SpotService {
private static final int SUGGESTION_LIMIT = 5;
private static final int VERIFICATION_DISTANCE = 250;

private final SpotRepository spotRepository;
private final GuidedSpotRepository guidedSpotRepository;
private final MemberRepository memberRepository;
private final MenuRepository menuRepository;
private final OpeningHourRepository openingHourRepository;
private final SpotImageRepository spotImageRepository;
private final GuidedSpotRepository guidedSpotRepository;
private final SpotRepository spotRepository;

private final SpotDtoMapper spotDtoMapper;
private final SpotMapper spotMapper;

private final PrincipalHandler principalHandler;

private final NaverMapsAdapter naverMapsAdapter;

private static final List<String> IMAGE_URLS = List.of(
Expand Down Expand Up @@ -239,10 +245,11 @@ public MenuListResponse fetchMenus(final Long spotId) {

@Transactional(readOnly = true)
public SearchSuggestionListResponse fetchSearchSuggestions(final Double latitude, final Double longitude) {
// TODO: 토큰 검증 이후 MemberID 추출 로직 필요
MemberEntity memberEntity = memberRepository.findByIdOrElseThrow(principalHandler.getUserIdFromPrincipal());

List<SearchSuggestionResponse> recentSpotSuggestion =
// TODO: 250m 범위 내의 TOP5로 수정
guidedSpotRepository.findTopByMemberIdOrderByUpdatedAtDesc(1L)
guidedSpotRepository.findTopByMemberIdOrderByUpdatedAtDesc(memberEntity.getId())
.flatMap(recentGuidedSpot -> spotRepository.findById(recentGuidedSpot.getSpotId()))
.map(spotDtoMapper::toSearchSuggestionResponse)
.stream()
Expand Down

0 comments on commit 8e1a4ae

Please sign in to comment.