Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature] 시군구 기반 맛집 목록 API #23

Open
wants to merge 20 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ HELP.md
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/main/**/generated/
!**/src/test/**/build/

src/main/resources/application.properties
Expand Down
29 changes: 22 additions & 7 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,28 @@ repositories {
}

dependencies {
// json
implementation 'org.json:json:20230227'

// swagger
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0'

// JWT
compileOnly group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.11.5'
runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.11.5'
runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.5'

// querydsl
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"

implementation 'com.querydsl:querydsl-spatial'
implementation 'org.hibernate:hibernate-core:6.1.7.Final'
implementation 'org.hibernate:hibernate-spatial:6.1.7.Final'
implementation 'org.locationtech.jts:jts-core'
Comment on lines +35 to +43
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

혹시 querydsl 의존성 8개가 다 필요하실까요..?


// json
implementation group: 'org.json', name: 'json', version: '20230227'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-validation'
Expand All @@ -36,13 +55,9 @@ dependencies {
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'

// querydsl
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"
}


tasks.named('test') {
useJUnitPlatform()
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
@SpringBootApplication
public class LocationBasedFoodieServiceApplication {

public static void main(String[] args) {
SpringApplication.run(LocationBasedFoodieServiceApplication.class, args);
}
public static void main(String[] args) {
SpringApplication.run(LocationBasedFoodieServiceApplication.class, args);
}
Comment on lines -11 to +13
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

크흡.. 요거는 깃이 스페이스랑 탭이랑 다르게 인식해서 그러는 것 같더라구요. 다음에 컨벤션 맞출 때는 이거도 신경 써야 겠네요 8ㅅ8


}
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@
@AllArgsConstructor
public enum CustomErrorCode {

// 예시 에러코드입니다.
USER_NOT_FOUND(HttpStatus.BAD_REQUEST.value(), "존재하지 않는 사용자입니다."),
USER_ALREADY_EXIST(HttpStatus.BAD_REQUEST.value(), "이미 존재하는 계정입니다."),
RESTAURANT_NOT_FOUND(HttpStatus.BAD_REQUEST.value(), "존재하지 않는 가게입니다."),
TOKEN_USER_MISMATCH(HttpStatus.BAD_REQUEST.value(), "계정에 잘못된 접근입니다.");
// 예시 에러코드입니다.
USER_NOT_FOUND(HttpStatus.BAD_REQUEST.value(), "존재하지 않는 사용자입니다."),
USER_ALREADY_EXIST(HttpStatus.BAD_REQUEST.value(), "이미 존재하는 계정입니다."),
TOKEN_USER_MISMATCH(HttpStatus.BAD_REQUEST.value(), "계정에 잘못된 접근입니다."),
INVALID_SIGUNGU_EXCEPTION(HttpStatus.BAD_REQUEST.value(), "잘못된 시군구 요청입니다"),
SIGUNGU_NOT_FOUND(HttpStatus.BAD_REQUEST.value(), "존재하지 않는 시군구입니다"),
RESTAURANT_NOT_FOUND(HttpStatus.BAD_REQUEST.value(), "존재하지 않는 가게입니다.");

private final int errorCode;
private final String errorMessage;
private final int errorCode;
private final String errorMessage;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.locationbasedfoodieservice.common.util;

import com.locationbasedfoodieservice.restaurant.dto.RestaurantDetailResponseDto;
import com.locationbasedfoodieservice.restaurant.entity.Restaurant;
import com.locationbasedfoodieservice.sigungu.entity.Sigungu;
import java.util.AbstractMap;
import java.util.AbstractMap.SimpleEntry;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import java.util.stream.Stream;


public class DistanceUtil {

public DistanceUtil(){

}
private double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
// 지구의 반경 (km 단위)
final int R = 6371;

double latDistance = Math.toRadians(lat2 - lat1);
double lonDistance = Math.toRadians(lon2 - lon1);
double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2)
+ Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2))
* Math.sin(lonDistance / 2) * Math.sin(lonDistance / 2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
double distance = R * c; // 거리 계산

return distance;
}

public List<RestaurantDetailResponseDto> restaurantListSortByDistance(List<Restaurant> restaurantList, Sigungu sigungu,
double range, boolean sortByRating) {
Stream<SimpleEntry<Restaurant, Double>> restaurants = restaurantList.stream()
// 거리 계산
.map(r -> new SimpleEntry<>(r,
calculateDistance(sigungu.getLat(), sigungu.getLon(), r.getLatitude(),
r.getLongitude())))
// calculateDistance 결과가 range 초과인 restaurant 제거
.filter(entry ->entry.getValue() <= range);

Stream<RestaurantDetailResponseDto> sortedRestaurant = sortByRating
? restaurants
.map(entry-> RestaurantDetailResponseDto.from(entry.getKey(),entry.getValue()))
:restaurants
// 거리에 따라 정렬
.sorted(Comparator.comparingDouble(Entry::getValue))
.map(entry-> RestaurantDetailResponseDto.from(entry.getKey(),entry.getValue()));

return sortedRestaurant.collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.locationbasedfoodieservice.common.util;

import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;

public class GeomUtil {

public static GeomUtil geomUtil = new GeomUtil();
Comment on lines +7 to +9
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아니 껌유틸이 뭐지 하고 한참 고민했는데 ㅋㅋㅋㅋㅋㅋ점유틸이냐구요 ㅋㅋㅋㅋ


public static Point createPoint(double lat, double lon) {
GeometryFactory gf = new GeometryFactory();
return gf.createPoint(new Coordinate(lat, lon));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.locationbasedfoodieservice.restaurant.controller;

import com.locationbasedfoodieservice.restaurant.dto.RestaurantsResponseDto;
import com.locationbasedfoodieservice.restaurant.service.RestaurantService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;

@Tag(name = "restaurantAPI", description = "시군구 기반 목록, 위치 기반 목록, 평가 및 상세 정보 API")
@RequiredArgsConstructor
@RestController
public class RestaurantController {

private final RestaurantService restaurantService;

@GetMapping("/api/restaurants/list/location")
@Operation(summary = "시군구 기반 맛집 리스트 api. 거리 단위 : km")
public ResponseEntity<?> getRestaurantsBySigungu(
@RequestParam() String sigungu,
@RequestParam(name = "sort", defaultValue = "거리순", required = false) String sort,
@RequestParam(defaultValue = "1.0", required = false) double range) {
RestaurantsResponseDto restaurants = restaurantService.getRestaurantsBySigungu(sigungu,
sort, range);
Comment on lines +30 to +31
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

약간 사소한 거기는 한데 맛집 목록 Get해오는 API에서는 맛집 상세 정보는 들어가지 않는다고 했던 것 같아요..!

return ResponseEntity.ok().body(restaurants);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.locationbasedfoodieservice.restaurant.dto;

import com.locationbasedfoodieservice.review.dto.response.ReviewResponseDto;
import java.util.List;

import com.locationbasedfoodieservice.restaurant.entity.Restaurant;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@AllArgsConstructor
@NoArgsConstructor
@Builder
@Getter
public class RestaurantDetailResponseDto {

private String city;
private String name;
private String licenseDate;
private String businessStatus;
private String type;
private String streetAddress;
private String lotNumberAddress;
private String postalCode;
private Double latitude;
private Double longitude;
private Double distance;
private String nameAddress;
private Double rating;
private List<ReviewResponseDto> reviewList;

public static RestaurantDetailResponseDto from(Restaurant restaurant,
List<ReviewResponseDto> reviewList) {
return RestaurantDetailResponseDto.builder()
.city(restaurant.getCity())
.name(restaurant.getName())
.licenseDate(restaurant.getLicenseDate())
.businessStatus(restaurant.getBusinessStatus())
.type(restaurant.getType())
.streetAddress(restaurant.getStreetAddress())
.lotNumberAddress(restaurant.getLotNumberAddress())
.postalCode(restaurant.getPostalCode())
.latitude(restaurant.getLatitude())
.longitude(restaurant.getLongitude())
.nameAddress(restaurant.getNameAddress())
.rating(restaurant.getRating())
.reviewList(reviewList)
.build();
}

public static RestaurantDetailResponseDto from(Restaurant restaurant, Double distanceFrom) {
return RestaurantDetailResponseDto.builder()
.city(restaurant.getCity())
.name(restaurant.getName())
.licenseDate(restaurant.getLicenseDate())
.businessStatus(restaurant.getBusinessStatus())
.type(restaurant.getType())
.streetAddress(restaurant.getStreetAddress())
.lotNumberAddress(restaurant.getLotNumberAddress())
.postalCode(restaurant.getPostalCode())
.latitude(restaurant.getLatitude())
.longitude(restaurant.getLongitude())
.nameAddress(restaurant.getNameAddress())
.rating(restaurant.getRating())
.distance(distanceFrom) //unit of distanceutil : kilometer
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.locationbasedfoodieservice.restaurant.dto;

import com.locationbasedfoodieservice.restaurant.entity.Restaurant;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@AllArgsConstructor
@NoArgsConstructor
@Builder
@Getter
public class RestaurantsResponseDto {

private List<RestaurantDetailResponseDto> data;

public static RestaurantsResponseDto from(List<RestaurantDetailResponseDto> list){
return RestaurantsResponseDto.builder()
.data(list)
.build();
}
}
Loading
Loading