diff --git a/src/main/java/project/backend/domain/culturalevent/service/CulturalEventBatchService.java b/src/main/java/project/backend/domain/culturalevent/service/CulturalEventBatchService.java index 3b05c19..a139ef9 100644 --- a/src/main/java/project/backend/domain/culturalevent/service/CulturalEventBatchService.java +++ b/src/main/java/project/backend/domain/culturalevent/service/CulturalEventBatchService.java @@ -23,7 +23,7 @@ import project.backend.domain.culturalevnetinfo.dto.CulturalEventInfoCreateDto; import project.backend.domain.culturalevnetinfo.entity.CulturalEventInfo; import project.backend.domain.culturalevnetinfo.service.CulturalEventInfoService; -import project.backend.domain.place.dto.PlaceCreateDto; +import project.backend.domain.place.dto.CrawlPlaceCreateDto; import project.backend.domain.place.entity.Place; import project.backend.domain.place.service.PlaceService; import project.backend.domain.ticketingsite.entity.TicketingSite; @@ -76,8 +76,8 @@ public CulturalEvent createCulturalEvent(String goodsCode, CulturalEventCreateDt culturalEvent = culturalEventMapper.culturalEventCreateDtoToCulturalEvent(culturalEventCreateDto); } // Place 연관관계 매핑 - PlaceCreateDto placeCreateDto = placeService.getPlaceCreateDtoFromPlaceCode(culturalEventCreateDto.getPlaceCode()); - Place place = placeService.createPlace(placeCreateDto); + CrawlPlaceCreateDto placeCreateDto = placeService.getCrawlPlaceCreateDtoFromPlaceCode(culturalEventCreateDto.getPlaceCode()); + Place place = placeService.createCrawlPlace(placeCreateDto); culturalEvent.setPlace(place); // CulturalEventCategory 연관관계 매핑 diff --git a/src/main/java/project/backend/domain/place/controller/PlacesController.java b/src/main/java/project/backend/domain/place/controller/PlacesController.java new file mode 100644 index 0000000..492a1db --- /dev/null +++ b/src/main/java/project/backend/domain/place/controller/PlacesController.java @@ -0,0 +1,31 @@ +package project.backend.domain.place.controller; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import project.backend.domain.place.dto.PlaceRetrieveDto; +import project.backend.domain.place.service.PlaceService; + +import java.util.List; + +@Api(tags = "Place - 장소") +@RestController +@RequestMapping("/api/places") +@RequiredArgsConstructor +public class PlacesController { + private final PlaceService placeService; + + @ApiOperation(value = "장소 리스트 조회") + @GetMapping + public ResponseEntity getPlaces( + @RequestParam String search, + @RequestParam(defaultValue = "1") int page, + @RequestParam(defaultValue = "10") int size + ) { + List placeRetrieveDtos = placeService.getKakaoPlaces(search, page, size); + return ResponseEntity.status(HttpStatus.OK).body(placeRetrieveDtos); + } +} diff --git a/src/main/java/project/backend/domain/place/dto/CrawlPlaceCreateDto.java b/src/main/java/project/backend/domain/place/dto/CrawlPlaceCreateDto.java new file mode 100644 index 0000000..a2b38b8 --- /dev/null +++ b/src/main/java/project/backend/domain/place/dto/CrawlPlaceCreateDto.java @@ -0,0 +1,21 @@ +package project.backend.domain.place.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.*; + +// Main Section +@Getter +@Setter +public class CrawlPlaceCreateDto { + @JsonProperty("placeName") + private String name; + + @JsonProperty("placeAddress") + private String address; + + @JsonProperty("latitude") + private Double latitude; + + @JsonProperty("longitude") + private Double longitude; +} \ No newline at end of file diff --git a/src/main/java/project/backend/domain/place/dto/PlaceCreateDto.java b/src/main/java/project/backend/domain/place/dto/PlaceCreateDto.java index fa8f96e..7e4616b 100644 --- a/src/main/java/project/backend/domain/place/dto/PlaceCreateDto.java +++ b/src/main/java/project/backend/domain/place/dto/PlaceCreateDto.java @@ -1,21 +1,15 @@ package project.backend.domain.place.dto; import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.*; +import lombok.Getter; +import lombok.Setter; // Main Section @Getter @Setter public class PlaceCreateDto { - @JsonProperty("placeName") private String name; - - @JsonProperty("placeAddress") private String address; - - @JsonProperty("latitude") private Double latitude; - - @JsonProperty("longitude") private Double longitude; } \ No newline at end of file diff --git a/src/main/java/project/backend/domain/place/dto/PlacePostRequestDto.java b/src/main/java/project/backend/domain/place/dto/PlacePostRequestDto.java deleted file mode 100644 index 4accd9b..0000000 --- a/src/main/java/project/backend/domain/place/dto/PlacePostRequestDto.java +++ /dev/null @@ -1,17 +0,0 @@ -package project.backend.domain.place.dto; - -import lombok.*; - -import javax.persistence.Column; - -@Getter -@Setter -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class PlacePostRequestDto { - private String title; - private String address; - private Double latitude; - private Double longitude; -} \ No newline at end of file diff --git a/src/main/java/project/backend/domain/place/dto/PlaceRetrieveDto.java b/src/main/java/project/backend/domain/place/dto/PlaceRetrieveDto.java index 55a7104..bec7610 100644 --- a/src/main/java/project/backend/domain/place/dto/PlaceRetrieveDto.java +++ b/src/main/java/project/backend/domain/place/dto/PlaceRetrieveDto.java @@ -7,7 +7,7 @@ @NoArgsConstructor @AllArgsConstructor public class PlaceRetrieveDto { - private String title; + private String name; private String address; private Double latitude; private Double longitude; diff --git a/src/main/java/project/backend/domain/place/mapper/PlaceMapper.java b/src/main/java/project/backend/domain/place/mapper/PlaceMapper.java index 7b28fb8..1561976 100644 --- a/src/main/java/project/backend/domain/place/mapper/PlaceMapper.java +++ b/src/main/java/project/backend/domain/place/mapper/PlaceMapper.java @@ -2,13 +2,15 @@ import org.mapstruct.Mapper; import org.mapstruct.ReportingPolicy; +import project.backend.domain.place.dto.CrawlPlaceCreateDto; import project.backend.domain.place.dto.PlaceCreateDto; import project.backend.domain.place.dto.PlaceRetrieveDto; import project.backend.domain.place.entity.Place; @Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface PlaceMapper { - Place placeCreateDtoToPlace(PlaceCreateDto placeCreateDto); + Place crawlPlaceCreateDtoToPlace(CrawlPlaceCreateDto crawlPlaceCreateDto); + Place PlaceCreateDtoToPlace(PlaceCreateDto placeCreateDto); PlaceRetrieveDto placeToPlaceResponseDto(Place place); diff --git a/src/main/java/project/backend/domain/place/service/PlaceService.java b/src/main/java/project/backend/domain/place/service/PlaceService.java index 5600d33..67fb01b 100644 --- a/src/main/java/project/backend/domain/place/service/PlaceService.java +++ b/src/main/java/project/backend/domain/place/service/PlaceService.java @@ -2,21 +2,32 @@ import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.reflect.TypeToken; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.client.RestTemplate; +import project.backend.domain.place.dto.CrawlPlaceCreateDto; import project.backend.domain.place.dto.PlaceCreateDto; +import project.backend.domain.place.dto.PlaceRetrieveDto; import project.backend.domain.place.entity.Place; import project.backend.domain.place.mapper.PlaceMapper; import project.backend.domain.place.repository.PlaceRepository; -import project.backend.domain.ticket.entity.Ticket; -import project.backend.global.error.exception.BusinessException; -import project.backend.global.error.exception.ErrorCode; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.Optional; @@ -24,29 +35,44 @@ @RequiredArgsConstructor @Transactional public class PlaceService { + + @Value("${jwt.kakao.client_id}") + private String kakaoClientId; + private final PlaceRepository placeRepository; private final PlaceMapper placeMapper; /** * Place 생성 * - * @param placeCreateDto + * @param crawlPlaceCreateDto * @return Place */ + public Place createCrawlPlace(CrawlPlaceCreateDto crawlPlaceCreateDto) { + if (crawlPlaceCreateDto != null) { + Place place = placeRepository + .findFirstByAddress(crawlPlaceCreateDto.getAddress()) + .orElseGet(() -> placeMapper.crawlPlaceCreateDtoToPlace(crawlPlaceCreateDto)); + placeRepository.save(place); + return place; + } else { + return null; + } + } + public Place createPlace(PlaceCreateDto placeCreateDto) { if (placeCreateDto != null) { Place place = placeRepository .findFirstByAddress(placeCreateDto.getAddress()) - .orElseGet(() -> placeMapper.placeCreateDtoToPlace(placeCreateDto)); + .orElseGet(() -> placeMapper.PlaceCreateDtoToPlace(placeCreateDto)); placeRepository.save(place); return place; } else { return null; } - } - public PlaceCreateDto getPlaceCreateDtoFromPlaceCode(String placeCode) { + public CrawlPlaceCreateDto getCrawlPlaceCreateDtoFromPlaceCode(String placeCode) { // URL 조회 RestTemplate restTemplate = new RestTemplate(); @@ -63,7 +89,50 @@ public PlaceCreateDto getPlaceCreateDtoFromPlaceCode(String placeCode) { Map data = (Map) responseBody.get("data"); ObjectMapper mapper = new ObjectMapper(); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - return mapper.convertValue(data, PlaceCreateDto.class); + return mapper.convertValue(data, CrawlPlaceCreateDto.class); } + public List getKakaoPlaces(String search, Integer page, Integer size) { + List places = new ArrayList<>(); + try { + String urlString = String.format("https://dapi.kakao.com/v2/local/search/keyword.json?query=%s&page=%d&size=%d", + URLEncoder.encode(search, "UTF-8"), page, size); + URL url = new URL(urlString); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("GET"); + conn.setRequestProperty("Authorization", "KakaoAK " + kakaoClientId); + + int responseCode = conn.getResponseCode(); + if (responseCode == 200) { // success + BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream())); + String inputLine; + StringBuffer response = new StringBuffer(); + + while ((inputLine = in.readLine()) != null) { + response.append(inputLine); + } + in.close(); + + // Parse JSON response + JsonObject jsonObject = JsonParser.parseString(response.toString()).getAsJsonObject(); + JsonArray documents = jsonObject.getAsJsonArray("documents"); + + for (int i = 0; i < documents.size(); i++) { + JsonObject document = documents.get(i).getAsJsonObject(); + PlaceRetrieveDto place = PlaceRetrieveDto.builder() + .name(document.get("place_name").getAsString()) + .address(document.get("road_address_name").getAsString()) + .latitude(document.get("y").getAsDouble()) + .longitude(document.get("x").getAsDouble()) + .build(); + places.add(place); + } + } else { + System.out.println("GET request not worked"); + } + } catch (Exception e) { + e.printStackTrace(); + } + return places; + } } diff --git a/src/main/java/project/backend/domain/ticket/dto/TicketCreateDto.java b/src/main/java/project/backend/domain/ticket/dto/TicketCreateDto.java index b04866f..f7ec4a0 100644 --- a/src/main/java/project/backend/domain/ticket/dto/TicketCreateDto.java +++ b/src/main/java/project/backend/domain/ticket/dto/TicketCreateDto.java @@ -3,6 +3,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.*; import project.backend.domain.media.dto.MediaDto; +import project.backend.domain.place.dto.PlaceCreateDto; import javax.validation.constraints.*; import java.time.LocalDate; @@ -14,8 +15,6 @@ @NoArgsConstructor @AllArgsConstructor public class TicketCreateDto { - // TODO(sprint4) : Category, place 연결 미구현 상태 - @NotBlank @NotNull(message = "문화생활의 제목을 입력해주세요") @Size(min = 1, max = 20, message = "1자 ~ 20자 사이만 입력 가능합니다.") @@ -51,4 +50,6 @@ public class TicketCreateDto { public Long ticketFolderId; public List medias; + + public PlaceCreateDto place; } diff --git a/src/main/java/project/backend/domain/ticket/dto/TicketRetrieveDto.java b/src/main/java/project/backend/domain/ticket/dto/TicketRetrieveDto.java index bb99f4d..9c76224 100644 --- a/src/main/java/project/backend/domain/ticket/dto/TicketRetrieveDto.java +++ b/src/main/java/project/backend/domain/ticket/dto/TicketRetrieveDto.java @@ -1,6 +1,7 @@ package project.backend.domain.ticket.dto; import lombok.*; +import project.backend.domain.place.dto.PlaceRetrieveDto; import project.backend.domain.ticketfolder.dto.TicketFolderRetrieveDto; import java.time.LocalDateTime; @@ -11,7 +12,6 @@ @NoArgsConstructor @AllArgsConstructor public class TicketRetrieveDto { - // TODO(sprint4) : place 연결 미구현 상태 public Long id; public String title; public String mainImageUrl; @@ -22,4 +22,5 @@ public class TicketRetrieveDto { private String review; public String mediasData; public TicketFolderRetrieveDto ticketFolder; + public PlaceRetrieveDto place; } diff --git a/src/main/java/project/backend/domain/ticket/service/TicketService.java b/src/main/java/project/backend/domain/ticket/service/TicketService.java index 313e657..1a9bd86 100644 --- a/src/main/java/project/backend/domain/ticket/service/TicketService.java +++ b/src/main/java/project/backend/domain/ticket/service/TicketService.java @@ -15,6 +15,8 @@ import project.backend.domain.media.service.MediaService; import project.backend.domain.member.entity.Member; import project.backend.domain.member.service.MemberJwtService; +import project.backend.domain.place.entity.Place; +import project.backend.domain.place.service.PlaceService; import project.backend.domain.ticket.dto.TicketCreateDto; import project.backend.domain.ticket.entity.Ticket; import project.backend.domain.ticket.repository.TicketRepository; @@ -34,6 +36,7 @@ public class TicketService { private final TicketFolderRepository ticketFolderRepository; private final MediaService mediaService; private final MemberJwtService memberJwtService; + private final PlaceService placeService; public Ticket createTicket(TicketCreateDto ticketCreateDto) { @@ -51,6 +54,10 @@ public Ticket createTicket(TicketCreateDto ticketCreateDto) { .price(ticketCreateDto.getPrice()) .build(); + // Place 연결 + Place place = placeService.createPlace(ticketCreateDto.getPlace()); + ticket.setPlace(place); + // Media 연결 for (MediaDto mediaDto : ticketCreateDto.medias) { Media media = mediaService.setMediaOrdering(mediaDto);