-
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
Conversation
server/src/main/java/com/talkka/server/bus/service/BusLocationService.java
Outdated
Show resolved
Hide resolved
@@ -7,7 +7,7 @@ | |||
@Getter | |||
public enum EndBus implements EnumCodeInterface { | |||
// "0 = RUNNING" or "1 = END" | |||
RUNNING("0"), END("1"), LENT_BUS_END("4"); | |||
RUNNING("0"), END("1"), END_BUS_CODE2("2"), LENT_BUS_END("4"); |
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.
눈물나는데 답이 따로 없네요. 나중에 찾아보고 변경해봅시다
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.
월요일에 전화한번 해보죠...
대체 이게 뭐냐고...
server/src/main/java/com/talkka/server/bus/dao/BusStatEntity.java
Outdated
Show resolved
Hide resolved
server/src/main/java/com/talkka/server/bus/service/BusLocationCollectService.java
Show resolved
Hide resolved
server/src/main/java/com/talkka/server/bus/service/LocationCollectingScheduler.java
Show resolved
Hide resolved
@@ -58,14 +57,10 @@ public List<BusRouteInfoBodyDto> getRouteInfo(String apiRouteId) throws ApiClien | |||
params.add("routeId", apiRouteId); | |||
try { | |||
URI uri = this.getOpenApiUri(path, params); |
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.
아래 로직으로 변경됨에 따라 삭제 필요
@Query(value = "select count(*) from bus_location l") | ||
Integer getRowNum(); | ||
|
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.
해당 쿼리에서 l 별칭이 필요 없어 보입니다!
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.
수정하겠습니다.
import com.talkka.server.bus.dao.BusStatEntity; | ||
import com.talkka.server.bus.dao.BusStatRepository; | ||
|
||
import jakarta.transaction.Transactional; |
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.
해당 어노테이션도 jakarta로 되어있어서 수정 필요
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.
수정하겠습니다.
@@ -121,4 +100,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, |
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.
리팩토링 진행 하면서 별도 클래스로 분리해 빈으로 등록하는것 고려중입니다.
final int MAX_ATTEMPTS = 10; | ||
final int RETRY_INTERVAL = 200; |
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.
MAX_ATTEMPS 3회정도면 충분해보입니다! 다른 파트에서 주입할 수 있으면 더 좋을듯...?
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.
이건 이번주말에 분리하도록 리팩토링할 예정이라 그때 수정하도록 하겠습니다.
// old logic | ||
// @Deprecated | ||
// @Transactional | ||
// public void process() { | ||
// List<Integer> apiCallNoList = busLocationRepository.getDistinctApiCallNoList(); | ||
// apiCallNoList.sort(Comparator.naturalOrder()); | ||
// Map<String, BusLocationEntity> prev = new HashMap<>(); | ||
// System.out.println(apiCallNoList); | ||
// for (Integer apiCallNo : apiCallNoList) { | ||
// Map<String, BusLocationEntity> cur = new HashMap<>(); | ||
// List<BusLocationEntity> locationList = busLocationRepository.findByApiCallNo(apiCallNo); | ||
// for (BusLocationEntity location : locationList) { | ||
// BusLocationEntity beforeLocation; | ||
// if ((beforeLocation = prev.get(location.getPlateNo())) != null | ||
// // 두정거장 이상 넘어가면 체크하지 않음 | ||
// && location.getStationSeq() - beforeLocation.getStationSeq() <= 1 && ( | ||
// location.getRemainSeatCount() != -1 && beforeLocation.getRemainSeatCount() != -1)) { | ||
// BusStatEntity statEntity = toBusStatEntity(beforeLocation, location); | ||
// busStatRepository.save(statEntity); | ||
// } | ||
// cur.put(location.getPlateNo(), location); | ||
// } | ||
// prev = cur; | ||
// } | ||
// } |
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.
보존 안해도되면 삭제해도 될 듯 싶습니다.
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.
혹시 몰라서 냅두겠습니다.
Integer dayOfWeek, | ||
Integer hour, | ||
Integer minute, |
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.
이 부분들은 나중에 Refactoring 할 때 조금 더 엄밀하게 VO 로 체크되면 좋을 것 같아요.
Long statId, | ||
Long routeId, | ||
Long stationId, | ||
String apiRouteId, | ||
String apiStationId, | ||
Integer beforeSeat, | ||
Integer afterSeat, | ||
Integer seatDiff, | ||
LocalDateTime beforeTime, | ||
LocalDateTime afterTime, | ||
String plateNo, | ||
PlateType plateType, | ||
Integer dayOfWeek, | ||
Integer hour, | ||
Integer minute, |
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.
너무 다 던지는 느낌이 있어서 (일단은 이것도 쓸수도...?) 나중에 필요한 부분만 빼다가 보여주는게 필요합니다
예를 들어,
7800 - 성균관대 - 현재시각(07:00) 의 "들어오는 좌석수"를 요청하면
06:30 ~ 07:30 의 "들어오는 좌석수" - "시간" 의 배열 반환이면 충분합니다.
이런 식으로 해당 정류장에서 몇 명이 타는지... 이런 데이터들을 표현해야합니다.
private final BusLocationProcessor busLocationProcessor; | ||
private final BusLocationRepository busLocationRepository; | ||
|
||
@PostMapping("/process/all") |
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.
admin 권한 필요 (bus쪽은 공개되어있습니다.)
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.
관리자페이지 완성 후 adminController로 옮길 예정입니다.
public class BusViewController { | ||
private final BusViewService busViewService; | ||
|
||
@GetMapping("/now") |
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.
View 라는 이름이 모호하다고 생각합니다.
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.
해당 부분은 다른 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 | ||
) { |
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.
해당 controller 임시로 둔 것으로 이해해도 될까요? (추후에 제가 수정하게 되는 컨트롤러)
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.
네 맞습니다. 프론트에 맞춰서 수정하시면 됩니다.
@@ -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 comment
The 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 comment
The reason will be displayed to describe this comment to others. Learn more.
추후 리팩토링 예정
@ManyToOne(fetch = FetchType.LAZY) | ||
@JoinColumn(name = "route_id", nullable = false) | ||
private BusRouteEntity route; | ||
|
||
@ManyToOne(fetch = FetchType.LAZY) | ||
@JoinColumn(name = "station_id", nullable = false) | ||
private BusStationEntity station; | ||
|
||
@Column(name = "station_seq", nullable = false) | ||
private Integer stationSeq; | ||
|
||
@Column(name = "empty_seat", nullable = false) | ||
private Integer emptySeat; | ||
|
||
@Column(name = "plate_no", nullable = false, length = 32) | ||
private String plateNo; | ||
|
||
@Column(name = "plate_type", nullable = false, length = 1) | ||
@Convert(converter = PlateTypeConverter.class) | ||
private PlateType plateType; | ||
|
||
@Column(name = "created_at", nullable = false) | ||
private LocalDateTime createdAt; | ||
|
||
@Column(name = "epoch_day", nullable = false) | ||
private Long epochDay; | ||
|
||
@Column(name = "time", nullable = false) | ||
private Integer time; | ||
|
||
@ManyToOne(fetch = FetchType.LAZY) | ||
@JoinColumn(name = "route_info_id") | ||
private BusPlateStatisticEntity routeInfo; |
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.
실제로 많이 가져올 일이 없어 LAZY 뒀다고 생각해도 될까요
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.
네 맞습니다. 보통 BusRemainSeatEntity에서 BusPlateStatisticEntity를 조회하는 경우가 별로 없어 LAZY로 설정했습니다.
@Getter | ||
@AllArgsConstructor | ||
private static class SeatInfo implements Serializable { | ||
LocalDateTime arrivedTime; | ||
Integer remainSeat; | ||
} |
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.
따로 이렇게 두신 이유가 궁금합니다..!
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.
Serializable은 삭제했습니다.
arrivedTime과 remainSeat을 한쌍으로 묶어 json으로 내보내기 위해 inner 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.
이후에 open api docs로 잘 빠지는지 확인 필요...
remainSeatList.add(new SeatInfo(seat.getCreatedAt(), seat.getEmptySeat())); | ||
} | ||
if (!seats.isEmpty()) { | ||
standardTime = seats.get(seats.size() >> 1).getCreatedAt(); |
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.
요기는 어떤 의미를 가지고 있는지 궁금합니다.
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.
요청한 정거장 기준으로 앞뒤로 빈좌석과 도착시간 등을 조회하게 되는데,
이때 기준이 된 정거장에 도착한 시간을 얻는 코드입니다.
총 정거장의 개수가 항상 홀수이고, 기준이 된 정거장이 중앙에 위치하기 때문에 size/2 중앙 정거장의 인덱스를 가져올 수 있습니다.
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.
seats
-> 변수명으로부터 list 임이 잘 와닿지 않아, 37번 라인의 의미 해석이 어렵습니다.- 비슷한 문제로
seats.size()
부분이 해당 배열의 크기를 구한다는 의미임을 처음에 인지하지 못하는 문제가 있었습니다. - shift 연산자로 표현하는 것이 가독성이 굉장히 좋지 않았습니다.
- 결과적으로
seats.get(seats.size() >> 1)
부분은배열의 중간 요소를 구한다
라는 의미의 method로 분리하는 것이 조금 더 잘 읽힐 것 같습니다. seats.size() >> 1
또한 왜 해당 행위를 해야하는 지에 대해서 의미적으로 잘 전달되지 않아, private method 화 하는 것이 어떨까 제안드립니다.
if (location.getRemainSeatCount() == -1) { | ||
continue; | ||
} | ||
try { | ||
if (locationMap.containsKey(location.getPlateNo())) { | ||
var dq = locationMap.get(location.getPlateNo()); | ||
if (!dq.isEmpty() && location.getStationSeq() < dq.peekLast().getStationSeq()) { | ||
process(dq); | ||
dq = new ArrayDeque<>(); | ||
locationMap.put(location.getPlateNo(), dq); | ||
} | ||
dq.add(location); | ||
} else { | ||
Deque<BusLocationEntity> dq = new ArrayDeque<>(); | ||
dq.add(location); | ||
locationMap.put(location.getPlateNo(), dq); | ||
} | ||
} catch (InvalidLocationNotFoundException e) { | ||
log.warn("버스 위치정보 가공 실패 locationId : {}, apiRouteId : {}, apiStationId : {}, plateNo : {} ", | ||
location.getBusLocationId(), | ||
location.getApiRouteId(), | ||
location.getApiStationId(), | ||
location.getPlateNo()); | ||
} | ||
} |
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.
나중에 리팩토링 꼭 필요합니다. depth가 너무 깊네요.
* 이 정보는 특정 버스에 대한 다양한 정거장에서의 위치 데이터를 포함합니다. | ||
*/ | ||
private void process(Deque<BusLocationEntity> locations) { | ||
assert locations.peek() != null; |
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.
이 부분 확실한 부분인가요?
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.
네 process 호출 전 locations가 empty인지 확인합니다.
@Converter(autoApply = true) | ||
public class TimeSlotTypeConverter extends EnumCodeConverter<TimeSlot> { | ||
public TimeSlotTypeConverter() { | ||
super(TimeSlot.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.
사용하는 Entity에서 왜 이렇게 저장하는지를 명시해주시면 좋을 것 같습니다.
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.
TimeSlot 별로 집계 할 때 사용했었는데 집계 방식이 변경되어 사용하지 않게 되었습니다.
Converter삭제 및 TimeSlot enum 롤백 했습니다.
remainSeatList.add(new SeatInfo(seat.getCreatedAt(), seat.getEmptySeat())); | ||
} | ||
if (!seats.isEmpty()) { | ||
standardTime = seats.get(seats.size() >> 1).getCreatedAt(); |
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.
seats
-> 변수명으로부터 list 임이 잘 와닿지 않아, 37번 라인의 의미 해석이 어렵습니다.- 비슷한 문제로
seats.size()
부분이 해당 배열의 크기를 구한다는 의미임을 처음에 인지하지 못하는 문제가 있었습니다. - shift 연산자로 표현하는 것이 가독성이 굉장히 좋지 않았습니다.
- 결과적으로
seats.get(seats.size() >> 1)
부분은배열의 중간 요소를 구한다
라는 의미의 method로 분리하는 것이 조금 더 잘 읽힐 것 같습니다. seats.size() >> 1
또한 왜 해당 행위를 해야하는 지에 대해서 의미적으로 잘 전달되지 않아, private method 화 하는 것이 어떨까 제안드립니다.
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.
LGTM
버스위치정보 수집 스케줄러 기능 추가
버스 통계 서비스 구현
Todo
close #89
close #113