Skip to content

Commit 2f58a5d

Browse files
authored
Merge 93dbcf9 into fe04a03
2 parents fe04a03 + 93dbcf9 commit 2f58a5d

File tree

9 files changed

+82
-42
lines changed

9 files changed

+82
-42
lines changed

build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ dependencies {
8181
implementation 'org.flywaydb:flyway-mysql'
8282
implementation 'com.github.ben-manes.caffeine:caffeine:3.1.8'
8383

84+
// circuit breaker dependencies
85+
implementation 'io.github.resilience4j:resilience4j-spring-boot3:2.2.0'
8486
}
8587

8688
dependencyManagement {

src/main/java/sevenstar/marineleisure/alert/controller/AlertController.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
package sevenstar.marineleisure.alert.controller;
22

33
import java.util.List;
4+
import java.util.Map;
5+
import java.util.Set;
46

57
import org.springframework.http.ResponseEntity;
68
import org.springframework.web.bind.annotation.GetMapping;
79
import org.springframework.web.bind.annotation.RequestMapping;
810
import org.springframework.web.bind.annotation.RestController;
911

1012
import lombok.RequiredArgsConstructor;
13+
import sevenstar.marineleisure.alert.dto.response.JellyfishResponse;
1114
import sevenstar.marineleisure.alert.dto.response.JellyfishResponseDto;
1215
import sevenstar.marineleisure.alert.dto.vo.JellyfishDetailVO;
1316
import sevenstar.marineleisure.alert.mapper.AlertMapper;
@@ -26,9 +29,11 @@ public class AlertController {
2629
* @return 해파리 발생 관련 정보
2730
*/
2831
@GetMapping("/jellyfish")
29-
public ResponseEntity<BaseResponse<JellyfishResponseDto>> getJellyfishList() {
32+
public ResponseEntity<BaseResponse<JellyfishResponse>> getJellyfishList() {
3033
List<JellyfishDetailVO> items = jellyfishService.search();
31-
JellyfishResponseDto result = alertMapper.toResponseDto(items);
34+
Map<String, Set<String>> map = jellyfishService.convert(items);
35+
36+
JellyfishResponse result = alertMapper.toResponseDto(items.getFirst().getReportDate(), map);
3237
return BaseResponse.success(result);
3338
}
3439

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package sevenstar.marineleisure.alert.dto.response;
2+
3+
import java.time.LocalDate;
4+
import java.util.Map;
5+
import java.util.Set;
6+
7+
import lombok.AllArgsConstructor;
8+
import lombok.Data;
9+
import lombok.NoArgsConstructor;
10+
11+
@Data
12+
@NoArgsConstructor
13+
@AllArgsConstructor
14+
public class JellyfishResponse {
15+
private LocalDate reposrtDate;
16+
private Map<String, Set<String>> jellyfish;
17+
}
Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,19 @@
11
package sevenstar.marineleisure.alert.mapper;
22

33
import java.time.LocalDate;
4-
import java.util.List;
4+
import java.util.Map;
5+
import java.util.Set;
56

67
import org.springframework.stereotype.Component;
78

89
import lombok.RequiredArgsConstructor;
9-
import sevenstar.marineleisure.alert.dto.response.JellyfishResponseDto;
10-
import sevenstar.marineleisure.alert.dto.vo.JellyfishDetailVO;
11-
import sevenstar.marineleisure.alert.dto.vo.JellyfishRegionVO;
12-
import sevenstar.marineleisure.alert.dto.vo.JellyfishSpeciesVO;
13-
import sevenstar.marineleisure.global.enums.DensityLevel;
14-
import sevenstar.marineleisure.global.enums.ToxicityLevel;
10+
import sevenstar.marineleisure.alert.dto.response.JellyfishResponse;
1511

1612
@Component
1713
@RequiredArgsConstructor
1814
public class AlertMapper {
1915

20-
public JellyfishResponseDto toResponseDto(List<JellyfishDetailVO> detailList) {
21-
if (detailList.isEmpty()) {
22-
return null;
23-
}
24-
LocalDate reportDate = detailList.get(0).getReportDate();
25-
26-
List<JellyfishRegionVO> regions = detailList.stream()
27-
.map(detail -> new JellyfishRegionVO(
28-
detail.getRegion(),
29-
new JellyfishSpeciesVO(
30-
detail.getSpecies(),
31-
ToxicityLevel.valueOf(detail.getToxicity()).getDescription(),
32-
DensityLevel.valueOf(detail.getDensityType()).getDescription()
33-
)
34-
))
35-
.toList();
36-
37-
return new JellyfishResponseDto(reportDate, regions);
16+
public JellyfishResponse toResponseDto(LocalDate reportDate, Map<String, Set<String>> map) {
17+
return new JellyfishResponse(reportDate, map);
3818
}
3919
}

src/main/java/sevenstar/marineleisure/alert/service/JellyfishService.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,13 @@
33
import java.io.File;
44
import java.io.IOException;
55
import java.time.LocalDate;
6+
import java.util.HashMap;
7+
import java.util.HashSet;
68
import java.util.List;
9+
import java.util.Map;
10+
import java.util.Set;
711

12+
import org.springframework.scheduling.annotation.Scheduled;
813
import org.springframework.stereotype.Service;
914
import org.springframework.transaction.annotation.Transactional;
1015
import org.springframework.web.client.RestTemplate;
@@ -56,7 +61,7 @@ public JellyfishSpecies searchByName(String name) {
5661
/**
5762
* 웹에서 크롤링 해 Pdf를 DB에 적재합니다.
5863
*/
59-
// @Scheduled(cron = "0 0 0 ? * FRI")
64+
@Scheduled(cron = "0 0 0 ? * FRI")
6065
// 금요일 00시에 동작합니다.
6166
@Transactional
6267
public void updateLatestReport() {
@@ -104,4 +109,15 @@ public void updateLatestReport() {
104109
}
105110
}
106111

112+
public Map<String, Set<String>> convert(List<JellyfishDetailVO> jellyfish) {
113+
Map<String, Set<String>> map = new HashMap<>();
114+
for (JellyfishDetailVO detail : jellyfish) {
115+
if (map.containsKey(detail.getSpecies())) {
116+
map.get(detail.getSpecies()).add(detail.getRegion());
117+
} else {
118+
map.put(detail.getSpecies(), new HashSet<>(List.of(detail.getRegion())));
119+
}
120+
}
121+
return map;
122+
}
107123
}

src/main/java/sevenstar/marineleisure/alert/util/JellyfishExtractor.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import com.fasterxml.jackson.core.type.TypeReference;
1010
import com.fasterxml.jackson.databind.ObjectMapper;
1111

12+
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
1213
import lombok.RequiredArgsConstructor;
1314
import lombok.extern.slf4j.Slf4j;
1415
import sevenstar.marineleisure.alert.dto.vo.ParsedJellyfishVO;
@@ -21,6 +22,7 @@ public class JellyfishExtractor {
2122
private final OpenAiChatModel chatModel;
2223
private final ObjectMapper objectMapper;
2324

25+
@CircuitBreaker(name = "openai-api", fallbackMethod = "fallbackExtractJellyfishData")
2426
public List<ParsedJellyfishVO> extractJellyfishData(String text) {
2527
try {
2628
String instruction = """
@@ -71,4 +73,9 @@ public List<ParsedJellyfishVO> extractJellyfishData(String text) {
7173
return List.of();
7274
}
7375
}
76+
77+
public List<ParsedJellyfishVO> fallbackExtractJellyfishData(String text, Throwable t) {
78+
log.error("OpenAI API 호출에 실패하여 fallback 메서드가 실행되었습니다. message: {}", t.getMessage());
79+
return List.of();
80+
}
7481
}

src/main/java/sevenstar/marineleisure/global/api/kakao/KakaoApiClient.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package sevenstar.marineleisure.global.api.kakao;
22

33
import java.net.URI;
4+
import java.util.List;
45

5-
import org.springframework.beans.factory.annotation.Qualifier;
66
import org.springframework.beans.factory.annotation.Value;
77
import org.springframework.http.HttpMethod;
88
import org.springframework.http.ResponseEntity;
@@ -11,18 +11,22 @@
1111
import org.springframework.util.MultiValueMap;
1212
import org.springframework.web.client.RestTemplate;
1313

14+
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
1415
import lombok.RequiredArgsConstructor;
16+
import lombok.extern.slf4j.Slf4j;
1517
import sevenstar.marineleisure.global.api.kakao.dto.RegionResponse;
1618
import sevenstar.marineleisure.global.utils.UriBuilder;
1719

1820
@Component
1921
@RequiredArgsConstructor
22+
@Slf4j
2023
public class KakaoApiClient {
2124
@Value("${kakao.map.uri}")
2225
private String kakaoMapUri;
2326

2427
private final RestTemplate kakaoRestTemplate;
2528

29+
@CircuitBreaker(name = "kakao-api", fallbackMethod = "fallbackKakaoApi")
2630
public ResponseEntity<RegionResponse> get(float latitude, float longitude) {
2731
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
2832
params.add("y", String.valueOf(latitude));
@@ -33,4 +37,13 @@ public ResponseEntity<RegionResponse> get(float latitude, float longitude) {
3337
return kakaoRestTemplate.exchange(uri, HttpMethod.GET, null, RegionResponse.class);
3438
}
3539

40+
public ResponseEntity<RegionResponse> fallbackKakaoApi(float latitude, float longitude, Throwable t) {
41+
log.error("Kakao API 호출에 실패하여 fallback 메서드가 실행되었습니다. message: {}", t.getMessage());
42+
RegionResponse fallbackResponse = new RegionResponse();
43+
RegionResponse.Document fallbackDocument = new RegionResponse.Document();
44+
fallbackDocument.setAddress_name("알 수 없는 지역");
45+
fallbackResponse.setDocuments(List.of(fallbackDocument));
46+
return ResponseEntity.ok(fallbackResponse);
47+
}
48+
3649
}

src/main/java/sevenstar/marineleisure/global/api/scheduler/SchedulerService.java

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,6 @@ public class SchedulerService {
2727

2828
private final Executor taskExecutor;
2929

30-
// public SchedulerService(
31-
// KhoaApiService khoaApiService,
32-
// OpenMeteoService openMeteoService,
33-
// PresetSchedulerService presetSchedulerService,
34-
// SpotViewQuartileRepository spotViewQuartileRepository,
35-
// @Qualifier("applicationTaskExecutor") Executor taskExecutor // ★ 여기
36-
// ) {
37-
// this.khoaApiService = khoaApiService;
38-
// this.openMeteoService = openMeteoService;
39-
// this.presetSchedulerService = presetSchedulerService;
40-
// this.spotViewQuartileRepository = spotViewQuartileRepository;
41-
// this.taskExecutor = taskExecutor;
42-
// }
4330
/**
4431
* 앞으로의 스케줄링 전략에 의해 수정될 부분입니다.
4532
* @author guwnoong

src/main/resources/application.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,16 @@ spring:
33
name: MarineLeisure
44
profiles:
55
active: prod
6+
7+
resilience4j.circuitbreaker:
8+
instances:
9+
openai-api:
10+
sliding-window-size: 10 # 최근 10개의 요청을 기반으로 실패율 계산
11+
failure-rate-threshold: 50 # 실패율이 50% 이상이면 서킷을 OPEN
12+
wait-duration-in-open-state: 10s # 서킷이 OPEN된 상태를 10초간 유지
13+
permitted-number-of-calls-in-half-open-state: 5 # HALF-OPEN 상태에서 5개의 테스트 요청을 허용
14+
kakao-api:
15+
sliding-window-size: 10 # 최근 10개의 요청을 기반으로 실패율 계산
16+
failure-rate-threshold: 50 # 실패율이 50% 이상이면 서킷을 OPEN
17+
wait-duration-in-open-state: 10s # 서킷이 OPEN된 상태를 10초간 유지
18+
permitted-number-of-calls-in-half-open-state: 5 # HALF-OPEN 상태에서 5개의 테스트 요청을 허용

0 commit comments

Comments
 (0)