diff --git a/build.gradle.kts b/build.gradle.kts index b0fea44e..3aa9d0d2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -70,6 +70,9 @@ dependencies { // XML 파싱 implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml") + // 정적 HTML 문서 기준 크롤링 + implementation("org.jsoup:jsoup:1.21.2") + // Spotify implementation("se.michaelthelin.spotify:spotify-web-api-java:8.4.1") } diff --git a/src/main/java/com/back/web7_9_codecrete_be/domain/concerts/service/ConcertService.java b/src/main/java/com/back/web7_9_codecrete_be/domain/concerts/service/ConcertService.java index 7ac6f001..b740f304 100644 --- a/src/main/java/com/back/web7_9_codecrete_be/domain/concerts/service/ConcertService.java +++ b/src/main/java/com/back/web7_9_codecrete_be/domain/concerts/service/ConcertService.java @@ -39,7 +39,7 @@ public class ConcertService { private final TicketOfficeRepository ticketOfficeRepository; - private final UserRepository userRepository; + private final JsoupApiService jsoupApiService; public List getConcertsList(Pageable pageable) { return concertRepository.getConcertItems(pageable); @@ -68,6 +68,7 @@ public List getConcertsList2(Pageable pageable) { return concertItems; } */ + public List getTicketOfficesList(long concertId) { Concert concert = new Concert(concertId); List ticketOffices = ticketOfficeRepository.getTicketOfficesByConcert(concert); diff --git a/src/main/java/com/back/web7_9_codecrete_be/domain/concerts/service/JsoupApiService.java b/src/main/java/com/back/web7_9_codecrete_be/domain/concerts/service/JsoupApiService.java new file mode 100644 index 00000000..fbd18aea --- /dev/null +++ b/src/main/java/com/back/web7_9_codecrete_be/domain/concerts/service/JsoupApiService.java @@ -0,0 +1,45 @@ +package com.back.web7_9_codecrete_be.domain.concerts.service; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; +import org.springframework.stereotype.Service; + +import java.io.IOException; + +@Slf4j +@Service +public class JsoupApiService { + + public String getHtmlByUrl(String url) { + StringBuilder sb = new StringBuilder(); + String res = ""; + try { + Document doc = Jsoup.connect(url).timeout(10*1000).get(); + Element nextData = doc.selectFirst("script#__NEXT_DATA__"); + log.info(doc.toString()); + String json = nextData.html(); + ObjectMapper mapper = new ObjectMapper(); + JsonNode root = mapper.readTree(json); + String timestamp = + root.path("props") + .path("pageProps") + .path("dehydratedState") + .path("queries") + .get(0) + .path("state") + .path("data") + .path("availableBeginTimestamp") + .asText(); + log.info("timestamp: {}", timestamp); + sb.append(timestamp); + } catch (IOException e){ + log.error(e.getMessage()); + } + return sb.toString(); + } +} diff --git a/src/main/java/com/back/web7_9_codecrete_be/domain/concerts/service/KopisApiService.java b/src/main/java/com/back/web7_9_codecrete_be/domain/concerts/service/KopisApiService.java index 95edcc8d..9f1c6115 100644 --- a/src/main/java/com/back/web7_9_codecrete_be/domain/concerts/service/KopisApiService.java +++ b/src/main/java/com/back/web7_9_codecrete_be/domain/concerts/service/KopisApiService.java @@ -104,23 +104,7 @@ public ConcertListResponse setConcertsList() throws InterruptedException { } //콘서트 최고 금액, 최저 금액 처리. - String price = concertDetail.getConcertPrice(); - String[] bits = price.split(" "); - int maxPrice = 0; - int minPrice = Integer.MAX_VALUE; - if (bits.length == 1) { - minPrice = 0; - } else { - for (String bit : bits) { - bit = bit.replaceAll(",", ""); - if (bit.endsWith("원")) { - bit = bit.replaceAll("원", ""); - maxPrice = Math.max(Integer.parseInt(bit), maxPrice); - minPrice = Math.min(Integer.parseInt(bit), minPrice); - } - } - } - + TicketPrice ticketPrice = new TicketPrice(concertDetail.getConcertPrice()); // 콘서트 저장 Concert concert = new Concert( @@ -130,8 +114,8 @@ public ConcertListResponse setConcertsList() throws InterruptedException { dateStringToDateTime(concertDetail.getStartDate()), dateStringToDateTime(concertDetail.getEndDate()), "", - maxPrice, - minPrice, + ticketPrice.maxPrice, + ticketPrice.minPrice, concertDetail.getPosterUrl(), concertDetail.getApiConcertId() ); @@ -149,13 +133,13 @@ public ConcertListResponse setConcertsList() throws InterruptedException { ticketOfficeRepository.save(to); } log.info("Concert saved: " + savedConcert); - Thread.sleep(300); } log.info(totalConcertsList.size() + "개의 공연 데이터 저장 완료!"); return plr; } + // 매주 월요일 새벽 2시 기준으로 데이터 갱신 @Scheduled(cron = "0 0 2 * * Mon") public void updateConcertData() throws InterruptedException { // 1주일 단위로 추가된 공연을 더하기 LocalDate weekBefore = LocalDate.now().minusDays(8); @@ -198,22 +182,7 @@ public void updateConcertData() throws InterruptedException { // 1주일 단위 } //콘서트 최고 금액, 최저 금액 처리. - String price = concertDetail.getConcertPrice(); - String[] bits = price.split(" "); - int maxPrice = 0; - int minPrice = Integer.MAX_VALUE; - if (bits.length == 1) { - minPrice = 0; - } else { - for (String bit : bits) { - bit = bit.replaceAll(",", ""); - if (bit.endsWith("원")) { - bit = bit.replaceAll("원", ""); - maxPrice = Math.max(Integer.parseInt(bit), maxPrice); - minPrice = Math.min(Integer.parseInt(bit), minPrice); - } - } - } + TicketPrice ticketPrice = new TicketPrice(concertDetail.getConcertPrice()); // 콘서트 저장 Concert concert = concertRepository.getConcertByApiConcertId(concertDetail.getApiConcertId()); @@ -225,8 +194,8 @@ public void updateConcertData() throws InterruptedException { // 1주일 단위 dateStringToDateTime(concertDetail.getStartDate()), dateStringToDateTime(concertDetail.getEndDate()), "", - maxPrice, - minPrice, + ticketPrice.maxPrice, + ticketPrice.minPrice, concertDetail.getPosterUrl(), concertDetail.getApiConcertId() ); @@ -235,8 +204,8 @@ public void updateConcertData() throws InterruptedException { // 1주일 단위 concertPlace, concertDetail.getConcertDescription(), "", - maxPrice, - minPrice + ticketPrice.maxPrice, + ticketPrice.minPrice ); } @@ -313,6 +282,8 @@ private ConcertListResponse getConcertListResponse(String serviceKey, LocalDate return performanceListResponse; } + + //콘서트 목록을 조회합니다. private ConcertListResponse getConcertListResponse(String serviceKey, LocalDate sdate, LocalDate edate, int page, LocalDate afterDate) { ConcertListResponse performanceListResponse; performanceListResponse = restClient.get() @@ -332,6 +303,7 @@ private ConcertListResponse getConcertListResponse(String serviceKey, LocalDate } + //콘서트 상세를 조회합니다. private ConcertDetailResponse getConcertDetailResponse(String serviceKey, String concertApiId) { ConcertDetailResponse concertDetailResponse; concertDetailResponse = restClient.get() @@ -345,6 +317,7 @@ private ConcertDetailResponse getConcertDetailResponse(String serviceKey, String } + // 콘서트 장소 목록을 조회합니다. private ConcertPlaceListResponse getConcertPlaceListResponse(String serviceKey, int page) { return restClient.get() .uri(uriBuilder -> @@ -356,6 +329,20 @@ private ConcertPlaceListResponse getConcertPlaceListResponse(String serviceKey, ).retrieve().body(ConcertPlaceListResponse.class); } + // 콘서트 장소 목록을 이름 기준으로 조회합니다. + private ConcertPlaceListResponse getConcertPlaceListResponseByName(String serviceKey, String name, int page){ + return restClient.get() + .uri(uriBuilder -> + uriBuilder.path("/prfplc") + .queryParam("service", serviceKey) + .queryParam("cpage", page) + .queryParam("rows", 100) + .queryParam("shprfnmfct", name) + .build() + ).retrieve().body(ConcertPlaceListResponse.class); + } + + //콘서트 장소 상세를 조회합니다. private ConcertPlaceDetailResponse getConcertPlaceDetailResponse(String serviceKey, String concertPlaceId) { return restClient.get() .uri(uriBuilder -> @@ -365,11 +352,38 @@ private ConcertPlaceDetailResponse getConcertPlaceDetailResponse(String serviceK ).retrieve().body(ConcertPlaceDetailResponse.class); } + // API에서 출력하는 날짜 문자열을 LocalDate 객체로 변환 private LocalDate dateStringToDateTime(String dateString) { + // "yyyy.MM.dd" 형식으로 들어옴 String [] split = dateString.split("\\."); int year = Integer.parseInt(split[0]); int month = Integer.parseInt(split[1]); int day = Integer.parseInt(split[2]); return LocalDate.of(year, month, day); } + + // 가격을 저장할 내부 객체 + private class TicketPrice{ + int maxPrice; + int minPrice; + + // 문자열 가격 정보를 받아서 변환 후 저장. + public TicketPrice(String price) { + String[] bits = price.split(" "); + int maxPrice = 0; + int minPrice = Integer.MAX_VALUE; + if (bits.length == 1) { + minPrice = 0; + } else { + for (String bit : bits) { + bit = bit.replaceAll(",", ""); + if (bit.endsWith("원")) { + bit = bit.replaceAll("원", ""); + this.maxPrice = Math.max(Integer.parseInt(bit), maxPrice); + this.minPrice = Math.min(Integer.parseInt(bit), minPrice); + } + } + } + } + } } diff --git a/src/main/java/com/back/web7_9_codecrete_be/domain/concerts/service/interparkApiService.java b/src/main/java/com/back/web7_9_codecrete_be/domain/concerts/service/interparkApiService.java new file mode 100644 index 00000000..644bb0a9 --- /dev/null +++ b/src/main/java/com/back/web7_9_codecrete_be/domain/concerts/service/interparkApiService.java @@ -0,0 +1,37 @@ +package com.back.web7_9_codecrete_be.domain.concerts.service; + +import com.back.web7_9_codecrete_be.domain.concerts.repository.ConcertPlaceRepository; +import com.back.web7_9_codecrete_be.domain.concerts.repository.ConcertRepository; +import com.back.web7_9_codecrete_be.domain.concerts.repository.TicketOfficeRepository; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestClient; + +import java.time.LocalDateTime; + +@Service +public class interparkApiService { + private final RestClient restClient; + + public interparkApiService(ConcertRepository concertRepository, ConcertPlaceRepository placeRepository, TicketOfficeRepository ticketOfficeRepository) { + this.restClient = RestClient.builder() + .baseUrl("https://api-ticketfront.interpark.com/v1/goods/") + .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .build(); + } + + public LocalDateTime getTicketOpenDate(String goodsId){ + restClient.get() + .uri(uriBuilder -> + uriBuilder.path("/"+goodsId) + .path("/summary") + .queryParam("goodsCode",goodsId) + .build() + ).retrieve(); + + return null; + } + + +}