137 lines
4.9 KiB
Java
137 lines
4.9 KiB
Java
package auctiora;
|
|
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
import lombok.AccessLevel;
|
|
import lombok.Getter;
|
|
import lombok.experimental.FieldDefaults;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
import java.io.IOException;
|
|
import java.sql.SQLException;
|
|
import java.util.concurrent.Executors;
|
|
import java.util.concurrent.ScheduledExecutorService;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
|
|
@Slf4j
|
|
public class TroostwijkMonitor {
|
|
|
|
private static final String LOT_API = "https://api.troostwijkauctions.com/lot/7/list";
|
|
|
|
RateLimitedHttpClient httpClient;
|
|
ObjectMapper objectMapper;
|
|
@Getter DatabaseService db;
|
|
NotificationService notifier;
|
|
ObjectDetectionService detector;
|
|
ImageProcessingService imageProcessor;
|
|
|
|
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(r -> {
|
|
var t = new Thread(r, "troostwijk-monitor-thread");
|
|
t.setDaemon(true);
|
|
return t;
|
|
});
|
|
|
|
public TroostwijkMonitor(String databasePath,
|
|
String notificationConfig,
|
|
String yoloCfgPath,
|
|
String yoloWeightsPath,
|
|
String classNamesPath)
|
|
throws SQLException, IOException {
|
|
|
|
httpClient = new RateLimitedHttpClient();
|
|
objectMapper = new ObjectMapper();
|
|
db = new DatabaseService(databasePath);
|
|
notifier = new NotificationService(notificationConfig);
|
|
detector = new ObjectDetectionService(yoloCfgPath, yoloWeightsPath, classNamesPath);
|
|
imageProcessor = new ImageProcessingService(db, detector, httpClient);
|
|
|
|
db.ensureSchema();
|
|
}
|
|
|
|
public void scheduleMonitoring() {
|
|
scheduler.scheduleAtFixedRate(this::monitorAllLots, 0, 1, TimeUnit.HOURS);
|
|
log.info("✓ Monitoring service started");
|
|
}
|
|
|
|
private void monitorAllLots() {
|
|
try {
|
|
var activeLots = db.getActiveLots();
|
|
log.info("Monitoring {} active lots …", activeLots.size());
|
|
for (var lot : activeLots) {
|
|
checkAndUpdateLot(lot);
|
|
}
|
|
} catch (SQLException e) {
|
|
log.error("Error during scheduled monitoring", e);
|
|
}
|
|
}
|
|
|
|
private void checkAndUpdateLot(Lot lot) {
|
|
refreshLotBid(lot);
|
|
|
|
var minutesLeft = lot.minutesUntilClose();
|
|
if (minutesLeft < 30) {
|
|
if (minutesLeft <= 5 && !lot.closingNotified()) {
|
|
notifier.sendNotification(
|
|
"Kavel " + lot.lotId() + " sluit binnen " + minutesLeft + " min.",
|
|
"Lot nearing closure", 1);
|
|
try {
|
|
db.updateLotNotificationFlags(lot.withClosingNotified(true));
|
|
} catch (SQLException e) {
|
|
throw new RuntimeException(e);
|
|
}
|
|
}
|
|
scheduler.schedule(() -> refreshLotBid(lot), 5, TimeUnit.MINUTES);
|
|
}
|
|
}
|
|
|
|
private void refreshLotBid(Lot lot) {
|
|
try {
|
|
var url = LOT_API +
|
|
"?batchSize=1&listType=7&offset=0&sortOption=0" +
|
|
"&saleID=" + lot.saleId() +
|
|
"&parentID=0&relationID=0&buildversion=201807311" +
|
|
"&lotID=" + lot.lotId();
|
|
|
|
var resp = httpClient.sendGet(url);
|
|
if (resp.statusCode() != 200) return;
|
|
|
|
var root = objectMapper.readTree(resp.body());
|
|
var results = root.path("results");
|
|
if (results.isArray() && results.size() > 0) {
|
|
var newBid = results.get(0).path("cb").asDouble();
|
|
if (Double.compare(newBid, lot.currentBid()) > 0) {
|
|
var previous = lot.currentBid();
|
|
var updatedLot = lot.withCurrentBid(newBid);
|
|
db.updateLotCurrentBid(updatedLot);
|
|
var msg = String.format(
|
|
"Nieuw bod op kavel %d: €%.2f (was €%.2f)",
|
|
lot.lotId(), newBid, previous);
|
|
notifier.sendNotification(msg, "Kavel bieding update", 0);
|
|
}
|
|
}
|
|
} catch (IOException | InterruptedException | SQLException e) {
|
|
log.warn("Failed to refresh bid for lot {}", lot.lotId(), e);
|
|
if (e instanceof InterruptedException) Thread.currentThread().interrupt();
|
|
}
|
|
}
|
|
|
|
public void printDatabaseStats() {
|
|
try {
|
|
var allLots = db.getAllLots();
|
|
var imageCount = db.getImageCount();
|
|
log.info("📊 Database Summary: total lots = {}, total images = {}",
|
|
allLots.size(), imageCount);
|
|
if (!allLots.isEmpty()) {
|
|
var sum = allLots.stream().mapToDouble(Lot::currentBid).sum();
|
|
log.info("Total current bids: €{:.2f}", sum);
|
|
}
|
|
} catch (SQLException e) {
|
|
log.warn("Could not retrieve database stats", e);
|
|
}
|
|
}
|
|
|
|
public void processPendingImages() {
|
|
imageProcessor.processPendingImages();
|
|
}
|
|
}
|