Skip to content
Merged
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
16 changes: 12 additions & 4 deletions src/main/java/futsal/futsalMatch/config/ApplicationConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
import futsal.futsalMatch.config.platform.UrbanConfig;
import futsal.futsalMatch.config.platform.WithConfig;
import futsal.futsalMatch.service.PlatformRequester;
import futsal.futsalMatch.service.strategy.parsing.PlabParsingStrategy;
import futsal.futsalMatch.service.strategy.parsing.PuzzleParsingStrategy;
import futsal.futsalMatch.service.strategy.parsing.UrbanParsingStrategy;
import futsal.futsalMatch.service.strategy.parsing.WithParsingStrategy;
import futsal.futsalMatch.service.strategy.request.PlabRequestStrategy;
import futsal.futsalMatch.service.strategy.request.PuzzleRequestStrategy;
import futsal.futsalMatch.service.strategy.request.UrbanRequestStrategy;
Expand All @@ -21,39 +25,43 @@
@RequiredArgsConstructor
public class ApplicationConfig {
private final PlabRequestStrategy plabRequestStrategy;
private final PlabParsingStrategy plabParsingStrategy;
private final PlabTransformStrategy plabTransformStrategy;
private final PlabConfig plabConfig;

private final PuzzleRequestStrategy puzzleRequestStrategy;
private final PuzzleParsingStrategy puzzleParsingStrategy;
private final PuzzleTransformStrategy puzzleTransformStrategy;
private final PuzzleConfig puzzleConfig;

private final WithRequestStrategy withRequestStrategy;
private final WithParsingStrategy withParsingStrategy;
private final WithTransformStrategy withTransformStrategy;
private final WithConfig withConfig;

private final UrbanRequestStrategy urbanRequestStrategy;
private final UrbanParsingStrategy urbanParsingStrategy;
private final UrbanTransformStrategy urbanTransformStrategy;
private final UrbanConfig urbanConfig;

@Bean
public PlatformRequester plabRequester() {
return new PlatformRequester(plabRequestStrategy, plabTransformStrategy, plabConfig);
return new PlatformRequester(plabRequestStrategy, plabParsingStrategy, plabTransformStrategy, plabConfig);
}

@Bean
public PlatformRequester puzzleRequester() {
return new PlatformRequester(puzzleRequestStrategy, puzzleTransformStrategy, puzzleConfig);
return new PlatformRequester(puzzleRequestStrategy, puzzleParsingStrategy, puzzleTransformStrategy, puzzleConfig);
}

@Bean
public PlatformRequester withRequester() {
return new PlatformRequester(withRequestStrategy, withTransformStrategy, withConfig);
return new PlatformRequester(withRequestStrategy, withParsingStrategy, withTransformStrategy, withConfig);
}

@Bean
public PlatformRequester urbanRequester() {
return new PlatformRequester(urbanRequestStrategy, urbanTransformStrategy, urbanConfig);
return new PlatformRequester(urbanRequestStrategy, urbanParsingStrategy, urbanTransformStrategy, urbanConfig);
}


Expand Down
16 changes: 16 additions & 0 deletions src/main/java/futsal/futsalMatch/config/AsyncConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package futsal.futsalMatch.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Configuration
public class AsyncConfig {

@Bean
public ExecutorService ioThreadPool() {
return Executors.newFixedThreadPool(100);
}
}
20 changes: 0 additions & 20 deletions src/main/java/futsal/futsalMatch/config/WebConfig.java

This file was deleted.

19 changes: 0 additions & 19 deletions src/main/java/futsal/futsalMatch/controller/ResetController.java

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,33 @@
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;

@Service
@Slf4j
@RequiredArgsConstructor
public class PlatformRequestService {

private final List<PlatformRequester> requesters;
private final ExecutorService ioThreadPool;

public List<MatchInfo> fetchAllData(LocalDate date, Region region) {
List<MatchInfo> result = requesters.stream()
.map(requester -> CompletableFuture.supplyAsync(() -> {
try {
return requester.request(date, region);
} catch (Exception e) {
log.error("Error in {} - {}", requester.getClass().getSimpleName(), e.getMessage());
return List.<MatchInfo>of();
}
}))
.map(requester -> CompletableFuture
.supplyAsync(() -> requester.fetch(date, region), ioThreadPool)
.thenApplyAsync(fetchData -> requester.parse(fetchData, date))
.thenApply(requester::transform)
.exceptionally(e -> {
log.error("Error in {} - {}", requester.getClass().getSimpleName(), e.getMessage());
return List.of();
})
)
.parallel()
.map(CompletableFuture::join)
.flatMap(List::stream)
.filter(m -> isValidTime(m.getTime()))
.sorted(Comparator.comparing(m -> LocalTime.parse(m.getTime())))
.toList();

return removeExpiredMatch(date, result);
}

Expand Down
14 changes: 14 additions & 0 deletions src/main/java/futsal/futsalMatch/service/PlatformRequester.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import futsal.futsalMatch.config.platform.PlatformConfig;
import futsal.futsalMatch.domain.MatchInfo;
import futsal.futsalMatch.enums.Region;
import futsal.futsalMatch.service.strategy.parsing.ParsingStrategy;
import futsal.futsalMatch.service.strategy.request.RequestStrategy;
import futsal.futsalMatch.service.strategy.transform.TransformStrategy;
import lombok.RequiredArgsConstructor;
Expand All @@ -13,9 +14,22 @@
@RequiredArgsConstructor
public class PlatformRequester {
private final RequestStrategy requestStrategy;
private final ParsingStrategy parsingStrategy;
private final TransformStrategy transformStrategy;
private final PlatformConfig config;

public String fetch(LocalDate date, Region region) {
return requestStrategy.fetch(config, date, region);
}

public List<Object> parse(String fetchData, LocalDate date) {
return parsingStrategy.parse(fetchData, date);
}

public List<MatchInfo> transform(List<Object> matchList) {
return matchList.stream().map(data -> transformStrategy.transform(config, data)).toList();
}

public List<MatchInfo> request(LocalDate date, Region region) {
return requestStrategy.request(config, date, region)
.stream()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package futsal.futsalMatch.service.strategy.parsing;

import java.time.LocalDate;
import java.util.List;

public interface ParsingStrategy {
List<Object> parse(String fetchData, LocalDate date);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package futsal.futsalMatch.service.strategy.parsing;

import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.stereotype.Component;

import java.time.LocalDate;
import java.util.List;
import java.util.stream.IntStream;

@Component
public class PlabParsingStrategy implements ParsingStrategy {
@Override
public List<Object> parse(String fetchData, LocalDate date) {
JSONObject jsonData = new JSONObject(fetchData);

if(!jsonData.has("results")) {
return List.of(); //매치가 없을 때
}

JSONArray jsonArray = jsonData.getJSONArray("results");
return IntStream.range(0, jsonArray.length())
.mapToObj(jsonArray::get)
.toList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package futsal.futsalMatch.service.strategy.parsing;

import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.stereotype.Component;

import java.time.LocalDate;
import java.util.List;
import java.util.stream.IntStream;

@Component
public class PuzzleParsingStrategy implements ParsingStrategy {
@Override
public List<Object> parse(String fetchData, LocalDate date) {
JSONObject jsonData = new JSONObject(fetchData);

if(!jsonData.has("list")) {
return List.of(); //매치가 없을 때
}

JSONArray jsonArray = jsonData.getJSONArray("list");

// Note: JSONArray.toList()는 사용하지 말 것.
// 사용 시 JSONArray의 데이터를 List<Map>으로 변환하므로, 이후 JSONObject로 처리할 수 없음.
return IntStream.range(0, jsonArray.length())
.mapToObj(jsonArray::get)
.toList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package futsal.futsalMatch.service.strategy.parsing;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
import org.springframework.stereotype.Component;

import java.time.LocalDate;
import java.util.List;

@Component
public class UrbanParsingStrategy implements ParsingStrategy {
@Override
public List<Object> parse(String fetchData, LocalDate date) {
Document document = Jsoup.parse(fetchData);
Elements matchElements = document.select("ul.goods_table_item");

String dateElement = "<span class='date'>" + date + "</span>";

return matchElements.stream()
.map(matchElement -> (Object) (matchElement + dateElement))
.toList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package futsal.futsalMatch.service.strategy.parsing;

import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.stereotype.Component;

import java.time.LocalDate;
import java.util.List;
import java.util.stream.IntStream;

@Component
public class WithParsingStrategy implements ParsingStrategy {
@Override
public List<Object> parse(String fetchData, LocalDate date) {
JSONObject jsonData = new JSONObject(fetchData.substring(11)); //starts with "200 OK OK,{JsonData...}"

if(!jsonData.has("block_list")) {
return List.of(); //매치가 없을 때
}

JSONArray jsonArray = jsonData.getJSONArray("block_list");

// Note: JSONArray.toList()는 사용하지 말 것.
// 사용 시 JSONArray의 데이터를 List<Map>으로 변환하므로, 이후 JSONObject로 처리할 수 없음.
return IntStream.range(0, jsonArray.length())
.mapToObj(jsonArray::get)
.toList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,32 @@ public List<Object> request(PlatformConfig config, LocalDate date, Region region
return List.of();
}
}

@Override
public String fetch(PlatformConfig config, LocalDate date, Region region) {
String requestUrl = UriComponentsBuilder.fromHttpUrl(config.getRequestBaseURL())
.queryParam("sch", date)
.queryParam("region", "1") //TODO region 값 변환
.queryParam("page_size", "700")
.queryParam("ordering", "schedule")
.build().toString();

try {
ResponseEntity<String> response = restTemplate.exchange(
requestUrl,
HttpMethod.GET,
new HttpEntity<>(new HttpHeaders()),
String.class
);

if(response.getStatusCode() != HttpStatus.OK){
throw new UnexpectedResponseStatusException("Unexpected response status: " + response.getStatusCode());
}

return response.getBody();
} catch (Exception e){
log.error("플랩풋볼 요청 실패 : {} - {}", e.getClass().getSimpleName(), e.getMessage());
return "";
}
}
}
Loading