-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[FEAT] 버스위치정보 수집 스케줄러 및 버스 통계 서비스 구현 #114
Changes from all commits
b8f697b
68d1ce4
9df9f2d
bc69ecd
3f6907f
f9b4e32
5325d66
a1b6ccd
f5b8467
f2abdea
d5ec6de
e3a9e78
74bf4aa
108ad1d
a2d0031
64370f1
ad071bc
0077439
46e3c10
7e60395
2f3cadd
be61855
675d2a9
5dc73ce
b2a3724
2cc5b95
d7fead6
8d7fa40
0eaf961
3513910
5dd52e1
857adc1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,9 +7,13 @@ | |
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.retry.backoff.FixedBackOffPolicy; | ||
import org.springframework.retry.policy.SimpleRetryPolicy; | ||
import org.springframework.retry.support.RetryTemplate; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.util.LinkedMultiValueMap; | ||
import org.springframework.util.MultiValueMap; | ||
import org.springframework.web.client.RestClientException; | ||
import org.springframework.web.client.RestTemplate; | ||
import org.springframework.web.util.DefaultUriBuilderFactory; | ||
|
||
|
@@ -42,15 +46,10 @@ public List<BusRouteSearchBodyDto> getSearchedRouteInfo(String keyword) throws A | |
MultiValueMap<String, String> params = new LinkedMultiValueMap<>(); | ||
params.add("keyword", keyword); | ||
try { | ||
URI uri = this.getOpenApiUri(path, params); | ||
ResponseEntity<BusRouteSearchRespDto> resp = restTemplate.getForEntity(uri, BusRouteSearchRespDto.class); | ||
var body = resp.getBody().msgBody(); | ||
if (body == null) { | ||
throw new ApiClientException("결과가 없습니다."); | ||
} | ||
return body; | ||
} catch (Exception exception) { | ||
throw new ApiClientException(exception.getMessage()); | ||
ResponseEntity<BusRouteSearchRespDto> resp = apiCallWithRetry(path, params, BusRouteSearchRespDto.class); | ||
return resp.getBody().msgBody(); | ||
} catch (RestClientException exception) { | ||
throw new ApiClientException("결과가 없습니다."); | ||
} | ||
} | ||
|
||
|
@@ -60,15 +59,10 @@ public List<BusRouteInfoBodyDto> getRouteInfo(String apiRouteId) throws ApiClien | |
MultiValueMap<String, String> params = new LinkedMultiValueMap<>(); | ||
params.add("routeId", apiRouteId); | ||
try { | ||
URI uri = this.getOpenApiUri(path, params); | ||
ResponseEntity<BusRouteInfoRespDto> resp = restTemplate.getForEntity(uri, BusRouteInfoRespDto.class); | ||
var body = resp.getBody().msgBody(); | ||
if (body == null) { | ||
throw new ApiClientException("결과가 없습니다."); | ||
} | ||
return body; | ||
} catch (Exception exception) { | ||
throw new ApiClientException(exception.getMessage()); | ||
ResponseEntity<BusRouteInfoRespDto> resp = apiCallWithRetry(path, params, BusRouteInfoRespDto.class); | ||
return resp.getBody().msgBody(); | ||
} catch (RestClientException exception) { | ||
throw new ApiClientException("결과가 없습니다."); | ||
} | ||
} | ||
|
||
|
@@ -78,15 +72,10 @@ public List<BusRouteStationBodyDto> getRouteStationInfo(String apiRouteId) throw | |
MultiValueMap<String, String> params = new LinkedMultiValueMap<>(); | ||
params.add("routeId", apiRouteId); | ||
try { | ||
URI uri = this.getOpenApiUri(path, params); | ||
ResponseEntity<BusRouteStationRespDto> resp = restTemplate.getForEntity(uri, BusRouteStationRespDto.class); | ||
var body = resp.getBody().msgBody(); | ||
if (body == null) { | ||
throw new ApiClientException("결과가 없습니다."); | ||
} | ||
return body; | ||
} catch (Exception exception) { | ||
throw new ApiClientException(exception.getMessage()); | ||
ResponseEntity<BusRouteStationRespDto> resp = apiCallWithRetry(path, params, BusRouteStationRespDto.class); | ||
return resp.getBody().msgBody(); | ||
} catch (RestClientException exception) { | ||
throw new ApiClientException("결과가 없습니다."); | ||
} | ||
} | ||
|
||
|
@@ -96,15 +85,10 @@ public List<BusLocationBodyDto> getBusLocationInfo(String apiRouteId) throws Api | |
MultiValueMap<String, String> params = new LinkedMultiValueMap<>(); | ||
params.add("routeId", apiRouteId); | ||
try { | ||
URI uri = this.getOpenApiUri(path, params); | ||
ResponseEntity<BusLocationRespDto> resp = restTemplate.getForEntity(uri, BusLocationRespDto.class); | ||
var body = resp.getBody().msgBody(); | ||
if (body == null) { | ||
throw new ApiClientException("결과가 없습니다."); | ||
} | ||
return body; | ||
} catch (Exception exception) { | ||
throw new ApiClientException(exception.getMessage()); | ||
ResponseEntity<BusLocationRespDto> resp = apiCallWithRetry(path, params, BusLocationRespDto.class); | ||
return resp.getBody().msgBody(); | ||
} catch (RestClientException exception) { | ||
throw new ApiClientException("결과가 없습니다."); | ||
} | ||
} | ||
|
||
|
@@ -139,4 +123,31 @@ private URI getOpenApiUri(String path, MultiValueMap<String, String> params) { | |
.queryParams(params) | ||
.build(); | ||
} | ||
|
||
// 리트라이 로직을 포함한 api call | ||
private <T> ResponseEntity<T> apiCallWithRetry(String path, MultiValueMap<String, String> params, | ||
Class<T> type) throws RestClientException { | ||
|
||
final int MAX_ATTEMPTS = 10; | ||
final int RETRY_INTERVAL = 200; | ||
Comment on lines
+131
to
+132
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MAX_ATTEMPS 3회정도면 충분해보입니다! 다른 파트에서 주입할 수 있으면 더 좋을듯...? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이건 이번주말에 분리하도록 리팩토링할 예정이라 그때 수정하도록 하겠습니다. |
||
|
||
// 이후 bean 으로 등록하는것 고려 | ||
RetryTemplate retryTemplate = new RetryTemplate(); | ||
|
||
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(); | ||
retryPolicy.setMaxAttempts(MAX_ATTEMPTS); | ||
retryTemplate.setRetryPolicy(retryPolicy); | ||
|
||
FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy(); | ||
backOffPolicy.setBackOffPeriod(RETRY_INTERVAL); | ||
retryTemplate.setBackOffPolicy(backOffPolicy); | ||
|
||
// 재시도마다 새로운 api key 로 시도 | ||
// 파싱 실패시 RestClientException 터트림 | ||
return retryTemplate.execute(context -> { | ||
// 재시도마다 새로운 api key 로 시도 | ||
URI uri = this.getOpenApiUri(path, params); | ||
return restTemplate.getForEntity(uri, type); // 파싱 실패시 RestClientException 터트림 | ||
}); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package com.talkka.server.bus.controller; | ||
|
||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
import com.talkka.server.bus.dao.BusLocationRepository; | ||
import com.talkka.server.bus.service.BusLocationProcessor; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
|
||
@RestController | ||
@RequiredArgsConstructor | ||
@RequestMapping("/api/bus/stat") | ||
public class BusLocationProcessController { | ||
private final BusLocationProcessor busLocationProcessor; | ||
private final BusLocationRepository busLocationRepository; | ||
|
||
@PostMapping("/process/all") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. admin 권한 필요 (bus쪽은 공개되어있습니다.) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 관리자페이지 완성 후 adminController로 옮길 예정입니다. |
||
public String process() { | ||
busLocationProcessor.start(busLocationRepository.findAll()); | ||
return "success"; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package com.talkka.server.bus.controller; | ||
|
||
import java.time.LocalDateTime; | ||
|
||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RequestParam; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
import com.talkka.server.bus.dto.BusViewDto; | ||
import com.talkka.server.bus.service.BusViewService; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
|
||
// 테스트용 컨트롤러입니다. | ||
@RestController | ||
@RequiredArgsConstructor | ||
@RequestMapping("/api/bus/route-info") | ||
public class BusViewController { | ||
private final BusViewService busViewService; | ||
|
||
@GetMapping("/now") | ||
Comment on lines
+20
to
+23
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. View 라는 이름이 모호하다고 생각합니다. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 해당 부분은 다른 controller 를 통하게 될 것임. 테스트용 임시 컨트롤러로 유지하고 이후 작업에서 변경하도록 할 것임. |
||
public ResponseEntity<BusViewDto> getNow( | ||
@RequestParam(name = "routeStationId", required = false, defaultValue = "17499") Long routeStationId, | ||
@RequestParam(name = "stationNum", required = false, defaultValue = "5") Integer stationNum, | ||
@RequestParam(name = "timeRange", required = false, defaultValue = "45") Integer timeRange, | ||
@RequestParam(name = "week", required = false, defaultValue = "2") Long week | ||
) { | ||
Comment on lines
+24
to
+29
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 해당 controller 임시로 둔 것으로 이해해도 될까요? (추후에 제가 수정하게 되는 컨트롤러) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 네 맞습니다. 프론트에 맞춰서 수정하시면 됩니다. |
||
LocalDateTime time = LocalDateTime.now().plusHours(8); | ||
return ResponseEntity.ok(busViewService.getBusView(routeStationId, stationNum, time, timeRange, week)); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -42,7 +42,7 @@ public class BusLocationEntity { | |
private String apiStationId; | ||
|
||
@Column(name = "station_seq", nullable = false) | ||
private Short stationSeq; | ||
private Integer stationSeq; | ||
|
||
@Column(name = "end_bus", nullable = false, length = 1) | ||
@Convert(converter = EndBusConverter.class) | ||
|
@@ -60,7 +60,7 @@ public class BusLocationEntity { | |
private PlateType plateType; | ||
|
||
@Column(name = "remain_seat_count", nullable = false) | ||
private Short remainSeatCount; | ||
private Integer remainSeatCount; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 변경되면서 다른 쪽에서 터질 수 있을 것 같은데 염두에 두겠습니다! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 추후 리팩토링 예정 |
||
|
||
@Column(name = "api_call_no", nullable = false) | ||
private Integer apiCallNo; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,23 @@ | ||
package com.talkka.server.bus.dao; | ||
|
||
import java.time.LocalDateTime; | ||
import java.util.List; | ||
|
||
import org.springframework.data.jpa.repository.JpaRepository; | ||
import org.springframework.data.jpa.repository.Query; | ||
import org.springframework.stereotype.Repository; | ||
|
||
@Repository | ||
public interface BusLocationRepository extends JpaRepository<BusLocationEntity, Long> { | ||
} | ||
|
||
@Query(value = "select distinct l.apiCallNo from bus_location l") | ||
List<Integer> getDistinctApiCallNoList(); | ||
|
||
@Query(value = "select count(*) from bus_location") | ||
Integer getRowNum(); | ||
|
||
List<BusLocationEntity> findByApiCallNo(Integer apiCallNo); | ||
|
||
List<BusLocationEntity> findByCreatedAtBetween(LocalDateTime startTime, LocalDateTime endTime); | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package com.talkka.server.bus.dao; | ||
|
||
import java.util.List; | ||
|
||
import org.springframework.data.jpa.domain.support.AuditingEntityListener; | ||
|
||
import com.talkka.server.bus.enums.PlateType; | ||
|
||
import jakarta.persistence.CascadeType; | ||
import jakarta.persistence.Column; | ||
import jakarta.persistence.Entity; | ||
import jakarta.persistence.EntityListeners; | ||
import jakarta.persistence.GeneratedValue; | ||
import jakarta.persistence.GenerationType; | ||
import jakarta.persistence.Id; | ||
import jakarta.persistence.JoinColumn; | ||
import jakarta.persistence.ManyToOne; | ||
import jakarta.persistence.OneToMany; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
@Entity(name = "bus_plate_statistic") | ||
@Getter | ||
@Builder | ||
@NoArgsConstructor | ||
@AllArgsConstructor | ||
@EntityListeners(AuditingEntityListener.class) | ||
public class BusPlateStatisticEntity { | ||
@Id | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
private Long id; | ||
|
||
@ManyToOne | ||
@JoinColumn(name = "route_id", nullable = false) | ||
private BusRouteEntity route; | ||
|
||
@Column(name = "plate_type", nullable = false) | ||
private PlateType plateType; | ||
|
||
@Column(name = "plate_no", nullable = false) | ||
private String plateNo; | ||
|
||
@Column(name = "epoch_day", nullable = false) | ||
private Long epochDay; | ||
|
||
@Column(name = "start_time", nullable = false) | ||
private Integer startTime; | ||
|
||
@Column(name = "end_time", nullable = false) | ||
private Integer endTime; | ||
|
||
@OneToMany(mappedBy = "plateStatistic", cascade = CascadeType.PERSIST) | ||
private List<BusRemainSeatEntity> seats; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package com.talkka.server.bus.dao; | ||
|
||
import org.springframework.data.jpa.repository.JpaRepository; | ||
import org.springframework.stereotype.Repository; | ||
|
||
@Repository | ||
public interface BusPlateStatisticRepository extends JpaRepository<BusPlateStatisticEntity, Long> { | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 친구 Abstract class도 빼도 좋을 것 같아요.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
리팩토링 진행 하면서 별도 클래스로 분리해 빈으로 등록하는것 고려중입니다.