Skip to content
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
12 changes: 12 additions & 0 deletions backend/.idea/dataSources.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions backend/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,6 @@ dependencies {
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-data-jpa-test'
testImplementation 'org.springframework.boot:spring-boot-starter-security-test'
testImplementation 'org.springframework.boot:spring-boot-testcontainers'
testImplementation 'org.testcontainers:testcontainers'
testImplementation 'org.testcontainers:junit-jupiter' // 어노테이션 담당
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import _team.onmyway.service.RecommendationService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Mono;

@RestController
@RequiredArgsConstructor
Expand All @@ -14,7 +15,7 @@ public class RecommendationController {

// 전체 카테고리 로드: 카테고리별 7개(대표 포함) + 메인용 대표 1개
@GetMapping("/recommend")
public AllCategoryRecommendationsDTO recommendAllCategories(
public Mono<AllCategoryRecommendationsDTO> recommendAllCategories(
@RequestParam double lat,
@RequestParam double lng
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;

import java.util.List;

@RestController
Expand All @@ -25,13 +27,13 @@ public class RouteController {
private final RecommendationService recommendationService;
private final ObjectMapper objectMapper;

@PostMapping("/slow")
public ResponseEntity<?> getSlowRoutes(@RequestBody List<PositionDTO> positions) {
@PostMapping("/findOut")
public ResponseEntity<?> getFindOutRoute(@RequestBody List<PositionDTO> positions) {
if (positions.isEmpty()) return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
PositionDTO start = positions.get(0);

RouteResponseDTO routing = routeService.slowRoute(positions).block();
AllCategoryRecommendationsDTO recommendations = recommendationService.recommendByRoute(routing, start.getLat(), start.getLon());
RouteResponseDTO routing = routeService.findOutRoute(positions).block();
Mono<AllCategoryRecommendationsDTO> recommendations = recommendationService.recommendByRoute(routing, start.getLat(), start.getLon());

ObjectNode response = objectMapper.createObjectNode();
response.set("route", objectMapper.valueToTree(routing));
Expand All @@ -46,7 +48,22 @@ public ResponseEntity<?> getRightRoute(@RequestBody List<PositionDTO> positions)
PositionDTO start = positions.get(0);

RouteResponseDTO routing = routeService.rightRoute(positions).block();
AllCategoryRecommendationsDTO recommendations = recommendationService.recommendByRoute(routing, start.getLat(), start.getLon());
Mono<AllCategoryRecommendationsDTO> recommendations = recommendationService.recommendByRoute(routing, start.getLat(), start.getLon());

ObjectNode response = objectMapper.createObjectNode();
response.set("route", objectMapper.valueToTree(routing));
response.set("recommendations", objectMapper.valueToTree(recommendations));

return new ResponseEntity<>(response, HttpStatus.OK);
}

@PostMapping("/slow")
public ResponseEntity<?> getRoute(@RequestBody List<PositionDTO> positions) {
if (positions.isEmpty()) return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
PositionDTO start = positions.get(0);

RouteResponseDTO routing = routeService.slowRoute(positions).block();
Mono<AllCategoryRecommendationsDTO> recommendations = recommendationService.recommendByRoute(routing, start.getLat(), start.getLon());

ObjectNode response = objectMapper.createObjectNode();
response.set("route", objectMapper.valueToTree(routing));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package _team.onmyway.controllers;
package _team.onmyway.controller;

import _team.onmyway.dto.PointDTO;
import _team.onmyway.service.SearchService;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package _team.onmyway.dto;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Getter;

import java.time.LocalTime;

@Getter
@AllArgsConstructor
public class PlaceRecommendationDTO {
Expand All @@ -11,4 +14,11 @@ public class PlaceRecommendationDTO {
private double lng;
private String category;
private int walkingMinutes;
private LocalTime openTime;
private LocalTime closeTime;

@JsonProperty("isOpen")
private boolean isOpen;

private String imageURL;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package _team.onmyway.dto;

import _team.onmyway.entity.Place;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Getter;

import java.time.LocalTime;
import java.util.List;
import java.util.stream.Collectors;

@Getter
@AllArgsConstructor
public class RecommendationResponseDTO {

private List<PlaceInfo> places;

public static RecommendationResponseDTO from(List<PlaceRecommendationDTO> recommendations, Double userlat, Double userlon) {
return new RecommendationResponseDTO(
recommendations.stream()
.map(p -> PlaceInfo.from(p, userlat, userlon))
.collect(Collectors.toList())
);
}

@Getter
@AllArgsConstructor
static class PlaceInfo {
private String name;
private double lat;
private double lng;
private String category;
private int walkingMinutes;
private LocalTime open;
private LocalTime close;

@JsonProperty("isOpen")
private boolean isOpen;

private String imageURL;

public static PlaceInfo from(PlaceRecommendationDTO p, double userLat, double userLng) {

double distance = calculateDistance(userLat, userLng, p.getLat(), p.getLng());
int minutes = (int) (distance / 80); // 80m = 1분

return new PlaceInfo(
p.getName(),
p.getLat(),
p.getLng(),
p.getCategory(),
minutes,
p.getOpenTime(),
p.getCloseTime(),
p.isOpen(),
p.getImageURL()
);
}

private static double calculateDistance(double lat1, double lng1, double lat2, double lng2) {
double dx = lat1 - lat2;
double dy = lng1 - lng2;
return Math.sqrt(dx * dx + dy * dy) * 111000;
}
}
}
4 changes: 4 additions & 0 deletions backend/src/main/java/_team/onmyway/entity/Photos.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,8 @@ public class Photos {
private Place place;

private String photoURL;

public String getPhotoURL() {
return photoURL;
}
}
11 changes: 11 additions & 0 deletions backend/src/main/java/_team/onmyway/entity/Place.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.BatchSize;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@Entity
@Table(indexes = {
Expand Down Expand Up @@ -53,4 +56,12 @@ public class Place {

@UpdateTimestamp
private LocalDateTime updatedAt;

@BatchSize(size = 100)
@OneToMany(mappedBy = "place") // 양방향 연결
private List<WorkingTime> workingTimes = new ArrayList<>();

@BatchSize(size = 100)
@OneToMany(mappedBy = "place")
private List<Photos> photos = new ArrayList<>();
}
5 changes: 5 additions & 0 deletions backend/src/main/java/_team/onmyway/entity/WorkingTime.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,19 @@
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.time.LocalTime;

@Entity
@Table(indexes = {
@Index(name="place_idx", columnList = "place_id")
})
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Getter
public class WorkingTime {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
@ExceptionHandler(Exception.class)
public ResponseEntity<String> handleException(Exception e) {
return ResponseEntity.status(500).body("NETWORK_ERROR");
}
package _team.onmyway.exception;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(RuntimeException.class)
public ResponseEntity<String> handleException(Exception e) {
return ResponseEntity.status(500).body("서버에 오류가 발생했습니다");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -188,10 +188,7 @@ private void openClose(Resource resource) throws Exception {
description = getValue(line, columnMap, "비고");
}

log.info(address+" "+name);
Place findPlace = placeMap.get(address+"_"+name);
log.info(findPlace.getName()+" "+findPlace.getAddress());

WorkingTime workingTime = WorkingTime.builder()
.dayOfWeek(dayweek)
.place(findPlace)
Expand All @@ -203,6 +200,7 @@ private void openClose(Resource resource) throws Exception {

openCloseEntries.add(workingTime);
}
System.out.println(openCloseEntries.size());
workingTimeRepository.saveAll(openCloseEntries);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package _team.onmyway.repository;

import _team.onmyway.entity.Photos;
import _team.onmyway.entity.Place;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

import java.util.List;

public interface PhotosRepository extends JpaRepository<Photos, Long> {
public List<Photos> findByPlace(Place place);
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package _team.onmyway.repository;

import _team.onmyway.entity.Place;
import _team.onmyway.entity.WorkingTime;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

public interface WorkingTimeRepository extends JpaRepository<WorkingTime, Integer> {
public List<WorkingTime> findByPlace(Place place);
}
53 changes: 53 additions & 0 deletions backend/src/main/java/_team/onmyway/service/ImageService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package _team.onmyway.service;

import _team.onmyway.entity.Photos;
import _team.onmyway.entity.Place;
import _team.onmyway.repository.PhotosRepository;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Map;

@Service
public class ImageService {

private final WebClient webClient;
private final PhotosRepository photosRepository;

public ImageService(PhotosRepository photosRepository) {
this.photosRepository = photosRepository;
this.webClient = WebClient.builder()
.baseUrl("https://openapi.naver.com")
.defaultHeader("X-Naver-Client-Id", "jSOzQhwW9F2Trv7pnGX4")
.defaultHeader("X-Naver-Client-Secret", "qzFMoZ_Cr2")
.build();
}

public Mono<String> getImageURL(Place p) {
List<Photos> dbPhotos = p.getPhotos();
if (dbPhotos.size() > 0) {
return Mono.just(dbPhotos.get(0).getPhotoURL());
} else {
return webClient.get()
.uri(uri -> uri
.path("/v1/search/image")
.queryParam("query",p.getName()+" 외관")
.queryParam("sort","sim")
.build())
.retrieve()
.bodyToMono(Map.class)
.map(response -> {
// items 리스트 추출
List<Map<String, Object>> list = (List<Map<String, Object>>) response.get("items");
if (list == null || list.isEmpty()) {
return ""; // 결과 없으면 빈 문자열
}
return (String) list.get(0).get("thumbnail");
})
.defaultIfEmpty("") // 혹시 모를 빈 응답 처리
.onErrorReturn(""); // API 에러 발생 시 빈 문자열 반환 (서비스 중단 방지)
}
}
}
Loading