Skip to content
3 changes: 3 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public class ConcertService {

private final TicketOfficeRepository ticketOfficeRepository;

private final UserRepository userRepository;
private final JsoupApiService jsoupApiService;

public List<ConcertItem> getConcertsList(Pageable pageable) {
return concertRepository.getConcertItems(pageable);
Expand Down Expand Up @@ -68,6 +68,7 @@ public List<ConcertItem> getConcertsList2(Pageable pageable) {
return concertItems;
}
*/

public List<TicketOfficeElement> getTicketOfficesList(long concertId) {
Concert concert = new Concert(concertId);
List<TicketOffice> ticketOffices = ticketOfficeRepository.getTicketOfficesByConcert(concert);
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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()
);
Expand All @@ -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);
Expand Down Expand Up @@ -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());
Expand All @@ -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()
);
Expand All @@ -235,8 +204,8 @@ public void updateConcertData() throws InterruptedException { // 1주일 단위
concertPlace,
concertDetail.getConcertDescription(),
"",
maxPrice,
minPrice
ticketPrice.maxPrice,
ticketPrice.minPrice
);
}

Expand Down Expand Up @@ -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()
Expand All @@ -332,6 +303,7 @@ private ConcertListResponse getConcertListResponse(String serviceKey, LocalDate
}


//콘서트 상세를 조회합니다.
private ConcertDetailResponse getConcertDetailResponse(String serviceKey, String concertApiId) {
ConcertDetailResponse concertDetailResponse;
concertDetailResponse = restClient.get()
Expand All @@ -345,6 +317,7 @@ private ConcertDetailResponse getConcertDetailResponse(String serviceKey, String
}


// 콘서트 장소 목록을 조회합니다.
private ConcertPlaceListResponse getConcertPlaceListResponse(String serviceKey, int page) {
return restClient.get()
.uri(uriBuilder ->
Expand All @@ -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 ->
Expand All @@ -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);
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -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;
}


}