Skip to content
Merged
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
59 changes: 39 additions & 20 deletions ranking/src/main/java/youthfi/ranking/util/DebeziumParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,42 +22,61 @@ public static ExecutionRow parseExecution(String v){
JsonNode payload = root.path("payload");
if (payload.isMissingNode()) return null;

String op = payload.path("op").asText("");
if ("d".equals(op)) return null; // 삭제 이벤트 무시
// delete 이벤트 스킵
if ("d".equals(payload.path("op").asText(""))) return null;

long tsMs = payload.has("ts_ms") ? payload.path("ts_ms").asLong() : System.currentTimeMillis();

long fallbackTs = payload.has("ts_ms") ? payload.path("ts_ms").asLong() : System.currentTimeMillis();
JsonNode after = payload.path("after");
if (after.isMissingNode() || after.isNull()) return null;

String userId = after.path("user_id").asText(null);
String stockId = after.path("stock_id").asText(null);
// userId / stockId (snake/camel 모두 지원)
String userId = after.hasNonNull("user_id") ? after.path("user_id").asText()
: after.hasNonNull("userId") ? String.valueOf(after.path("userId").asLong())
: null;
String stockId = after.hasNonNull("stock_id") ? after.path("stock_id").asText()
: after.hasNonNull("stockId") ? after.path("stockId").asText()
: null;
if (userId == null || userId.isBlank() || stockId == null || stockId.isBlank()) return null;

// price decimal 안전 파싱
// is_buy / quantity
int isBuy = after.hasNonNull("is_buy") ? after.path("is_buy").asInt()
: after.hasNonNull("isBuy") ? after.path("isBuy").asInt() : 0;
long quantity = after.path("quantity").isNumber() ? after.path("quantity").longValue()
: Long.parseLong(after.path("quantity").asText("0"));
if (quantity <= 0) return null;

// price: 문자열/숫자 모두 처리
BigDecimal priceDec = after.path("price").isNumber()
? after.path("price").decimalValue()
: new BigDecimal(after.path("price").asText("0"));
double price = priceDec.doubleValue();
if (price <= 0) return null;

int isBuy = after.path("is_buy").asInt();

long qty = after.path("quantity").isNumber()
? after.path("quantity").longValue()
: Long.parseLong(after.path("quantity").asText("0"));
if (qty <= 0) return null;

long tsMs = fallbackTs;
String tradeAtStr = after.path("trade_at").asText(null);
if (tradeAtStr != null && !tradeAtStr.isBlank()) {
LocalDateTime ldt = LocalDateTime.parse(tradeAtStr, DT_MICRO);
// 필요 시 Asia/Seoul로 바꿔도 됨. 여기선 UTC 기준.
tsMs = ldt.toInstant(ZoneOffset.UTC).toEpochMilli();
// trade_at: µs(int64) 또는 문자열 → ms로 정규화
if (after.has("trade_at")) {
JsonNode t = after.path("trade_at");
if (t.isNumber()) {
long micros = t.asLong(); // e.g. 1760732159312309
tsMs = micros / 1000L; // µs → ms
} else {
String s = t.asText();
try {
// "2025-10-17 11:15:59[.n]" 같은 포맷 지원
LocalDateTime ldt = LocalDateTime.parse(s, DT_MICRO);
tsMs = ldt.toInstant(ZoneOffset.UTC).toEpochMilli();
} catch (Exception ignore) {
// 실패 시 payload.ts_ms 또는 현재시각 유지
}
}
} else if (after.has("tradeAt")) {
tsMs = after.path("tradeAt").asLong(); // ms 가정
}

return new ExecutionRow(userId, stockId, price, isBuy, qty, tsMs);
return new ExecutionRow(userId, stockId, price, isBuy, quantity, tsMs);
} catch (Exception e) {
return null;
}
}

}