diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 85fa530..2ba7bd5 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -65,6 +65,8 @@ jobs: export FCM_KEY_PATH=/app/firebase/${{ secrets.FCM_FILE_NAME }} + export KAKAO_MAP_API_KEY='${{ secrets.KAKAO_MAP_API_KEY }}' + # Spring Boot 앱만 pull docker compose -f docker-compose.yml pull spring-boot-app diff --git a/docker-compose.yml b/docker-compose.yml index 7df5846..06e8525 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,6 +18,7 @@ services: COOLSMS_API_KEY: ${COOLSMS_API_KEY} COOLSMS_API_SECRET: ${COOLSMS_API_SECRET} COOLSMS_SENDER: ${COOLSMS_SENDER} + KAKAO_MAP_API_KEY: ${KAKAO_MAP_API_KEY} volumes: - /home/ec2-user/app/firebase:/app/firebase:ro depends_on: diff --git a/src/main/java/com/meetkey/server/domain/match/dto/RecommendationResDTO.java b/src/main/java/com/meetkey/server/domain/match/dto/RecommendationResDTO.java index ddf78a5..f3dbf47 100644 --- a/src/main/java/com/meetkey/server/domain/match/dto/RecommendationResDTO.java +++ b/src/main/java/com/meetkey/server/domain/match/dto/RecommendationResDTO.java @@ -18,7 +18,9 @@ public record RecommendationResDTO( List interests, PersonalityDTO personality, List photoUrls, - String introduction + String introduction, + BadgeInfoDTO badge, + String location ) { @Builder public record LanguageDTO( @@ -36,4 +38,11 @@ public record PersonalityDTO( RelationType relationType ) { } + + @Builder + public record BadgeInfoDTO( + String level, + int score + ) { + } } diff --git a/src/main/java/com/meetkey/server/domain/match/service/MatchServiceImpl.java b/src/main/java/com/meetkey/server/domain/match/service/MatchServiceImpl.java index fa2b41f..2dc734c 100644 --- a/src/main/java/com/meetkey/server/domain/match/service/MatchServiceImpl.java +++ b/src/main/java/com/meetkey/server/domain/match/service/MatchServiceImpl.java @@ -1,5 +1,6 @@ package com.meetkey.server.domain.match.service; +import com.meetkey.server.domain.badge.enums.BadgeLevel; import com.meetkey.server.domain.chat.repository.ChatRoomMemberRepository; import com.meetkey.server.domain.match.dto.*; import com.meetkey.server.domain.match.entity.RecommendationQueue; @@ -43,6 +44,7 @@ public class MatchServiceImpl implements MatchService { private final PreferenceRepository preferenceRepository; private final RecommendationQueueRepository recommendationQueueRepository; private final ChatRoomMemberRepository chatRoomMemberRepository; + private final com.meetkey.server.domain.badge.respository.PointHistoryRepository pointHistoryRepository; @Transactional @Override @@ -324,6 +326,11 @@ private RecommendationResDTO convertToDTO(Member member) { private RecommendationResDTO convertToDTO(Member member, Preference pref, double distance) { int age = member.getBirthday() != null ? LocalDate.now().getYear() - member.getBirthday().getYear() + 1 : 0; + int totalScore = pointHistoryRepository.calculateTotalScore(member); + String badgeLevel = BadgeLevel.fromScore(totalScore).name(); + + String location = member.getLocation(); + RecommendationResDTO.PersonalityDTO personalityDTO = null; if (pref != null) { personalityDTO = RecommendationResDTO.PersonalityDTO.builder() @@ -359,6 +366,11 @@ private RecommendationResDTO convertToDTO(Member member, Preference pref, double .personality(personalityDTO) .photoUrls(Collections.emptyList()) // 플레이스홀더 .introduction(member.getBio()) + .badge(RecommendationResDTO.BadgeInfoDTO.builder() + .level(badgeLevel) + .score(totalScore) + .build()) + .location(location) .build(); } diff --git a/src/main/java/com/meetkey/server/domain/member/service/GeocodingService.java b/src/main/java/com/meetkey/server/domain/member/service/GeocodingService.java new file mode 100644 index 0000000..8192749 --- /dev/null +++ b/src/main/java/com/meetkey/server/domain/member/service/GeocodingService.java @@ -0,0 +1,46 @@ +package com.meetkey.server.domain.member.service; + +import com.meetkey.server.global.security.oauth.dto.KakaoMapDto; +import com.meetkey.server.global.security.oauth.kakao.KakaoMapClient; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor +public class GeocodingService { + + private final KakaoMapClient kakaoMapClient; + + @Value("${kakao.map-api-key}") + private String kakaoMapApiKey; + + public String getAddress(Double latitude, Double longitude) { + if (latitude == null || longitude == null) { + return null; + } + + try { + String authorization = "KakaoAK " + kakaoMapApiKey; + + KakaoMapDto response = kakaoMapClient.getRegionCode(authorization, longitude, latitude); + + if (response != null && response.getDocuments() != null && !response.getDocuments().isEmpty()) { + KakaoMapDto.Document doc = response.getDocuments().stream() + .filter(d -> "H".equals(d.getRegionType())) + .findFirst() + .orElse(response.getDocuments().get(0)); + + String city = doc.getRegion1depthName(); + String district = doc.getRegion2depthName(); + + return "대한민국, " + city; + } + } catch (Exception e) { + log.error("Geocoding failed for lat: {}, lon: {}", latitude, longitude, e); + } + return null; + } +} diff --git a/src/main/java/com/meetkey/server/domain/member/service/ProfileService.java b/src/main/java/com/meetkey/server/domain/member/service/ProfileService.java index 8825ab3..0ddf9a7 100644 --- a/src/main/java/com/meetkey/server/domain/member/service/ProfileService.java +++ b/src/main/java/com/meetkey/server/domain/member/service/ProfileService.java @@ -38,6 +38,7 @@ public class ProfileService { private final MemberLocationRepository memberLocationRepository; private final EvaluationRepository evaluationRepository; private final BadgeService badgeService; + private final GeocodingService geocodingService; public ProfileUpdateResponse updateProfile(Long memberId, ProfileUpdateRequest request) { Member member = getMember(memberId); @@ -54,6 +55,11 @@ public ProfileUpdateResponse updateProfile(Long memberId, ProfileUpdateRequest r } else { memberLocation.update(request.latitude(), request.longitude()); } + + String address = geocodingService.getAddress(request.latitude(), request.longitude()); + if (address != null) { + member.updateProfileInfo(address, request.bio(), request.first(), request.target(), request.level()); + } } badgeService.checkProfileCompletion(memberId); @@ -73,6 +79,11 @@ public void updateLocation(Long memberId, LocationUpdateRequest request) { } else { memberLocation.update(request.latitude(), request.longitude()); } + + String address = geocodingService.getAddress(request.latitude(), request.longitude()); + if (address != null) { + member.updateProfileInfo(address, member.getBio(), member.getFirstLanguage(), member.getTargetLanguage(), member.getTargetLanguageLevel()); + } } } diff --git a/src/main/java/com/meetkey/server/global/security/oauth/dto/KakaoMapDto.java b/src/main/java/com/meetkey/server/global/security/oauth/dto/KakaoMapDto.java new file mode 100644 index 0000000..2f86d79 --- /dev/null +++ b/src/main/java/com/meetkey/server/global/security/oauth/dto/KakaoMapDto.java @@ -0,0 +1,42 @@ +package com.meetkey.server.global.security.oauth.dto; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Getter +@NoArgsConstructor +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class KakaoMapDto { + private Meta meta; + private List documents; + + @Getter + @NoArgsConstructor + @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) + public static class Meta { + private int totalCount; + } + + @Getter + @NoArgsConstructor + @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) + public static class Document { + private String regionType; + private String addressName; + @com.fasterxml.jackson.annotation.JsonProperty("region_1depth_name") + private String region1depthName; // 시/도 + @com.fasterxml.jackson.annotation.JsonProperty("region_2depth_name") + private String region2depthName; // 시/군/구 + @com.fasterxml.jackson.annotation.JsonProperty("region_3depth_name") + private String region3depthName; // 읍/면/동 + @com.fasterxml.jackson.annotation.JsonProperty("region_4depth_name") + private String region4depthName; + private String code; + private double x; + private double y; + } +} diff --git a/src/main/java/com/meetkey/server/global/security/oauth/kakao/KakaoMapClient.java b/src/main/java/com/meetkey/server/global/security/oauth/kakao/KakaoMapClient.java new file mode 100644 index 0000000..45601e1 --- /dev/null +++ b/src/main/java/com/meetkey/server/global/security/oauth/kakao/KakaoMapClient.java @@ -0,0 +1,23 @@ +package com.meetkey.server.global.security.oauth.kakao; + +import com.meetkey.server.global.config.FeignConfig; +import com.meetkey.server.global.config.OauthConfig; +import com.meetkey.server.global.security.oauth.dto.KakaoMapDto; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestParam; + +@FeignClient( + name = "KakaoMapClient", + url = "https://dapi.kakao.com", + configuration = {OauthConfig.class, FeignConfig.class} +) +public interface KakaoMapClient { + @GetMapping("/v2/local/geo/coord2regioncode.json") + KakaoMapDto getRegionCode( + @RequestHeader("Authorization") String authorization, + @RequestParam("x") Double longitude, + @RequestParam("y") Double latitude + ); +} diff --git a/src/main/resources/application-local.yaml b/src/main/resources/application-local.yaml index b2e2106..e8d6094 100644 --- a/src/main/resources/application-local.yaml +++ b/src/main/resources/application-local.yaml @@ -33,6 +33,7 @@ admin: kakao: app-key: ${KAKAO_APP_KEY:} + map-api-key: ${KAKAO_MAP_API_KEY:} apple: app-key: ${APPLE_APP_KEY:} diff --git a/src/main/resources/application-prod.yaml b/src/main/resources/application-prod.yaml index e825d0c..07e0654 100644 --- a/src/main/resources/application-prod.yaml +++ b/src/main/resources/application-prod.yaml @@ -33,6 +33,7 @@ admin: kakao: app-key: ${KAKAO_APP_KEY:} + map-api-key: ${KAKAO_MAP_API_KEY:} apple: app-key: ${APPLE_APP_KEY:} \ No newline at end of file diff --git a/src/main/resources/application-test.yaml b/src/main/resources/application-test.yaml index 0872f09..6d107dd 100644 --- a/src/main/resources/application-test.yaml +++ b/src/main/resources/application-test.yaml @@ -37,6 +37,7 @@ admin: kakao: app-key: ${KAKAO_APP_KEY:} + map-api-key: ${KAKAO_MAP_API_KEY:} apple: app-key: ${APPLE_APP_KEY:}