feat: persist jt808 registration identity
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
@@ -22,6 +22,7 @@ public final class MySqlVehicleIdentityService implements VehicleIdentityResolve
|
||||
|
||||
private final DataSource dataSource;
|
||||
private final String table;
|
||||
private final String registrationTable;
|
||||
private final ScheduledExecutorService refresher;
|
||||
private volatile InMemoryVehicleIdentityService index;
|
||||
|
||||
@@ -35,6 +36,7 @@ public final class MySqlVehicleIdentityService implements VehicleIdentityResolve
|
||||
}
|
||||
this.dataSource = dataSource;
|
||||
this.table = sanitizeTable(table);
|
||||
this.registrationTable = this.table + "_registration";
|
||||
initializeSchema();
|
||||
this.index = loadIndex();
|
||||
this.refresher = startRefresher(refreshInterval);
|
||||
@@ -51,6 +53,22 @@ public final class MySqlVehicleIdentityService implements VehicleIdentityResolve
|
||||
index.bind(binding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(VehicleRegistrationBinding registration) {
|
||||
if (registration == null) {
|
||||
return;
|
||||
}
|
||||
upsertRegistration(registration);
|
||||
if (registration.hasResolvedVin()) {
|
||||
bind(new VehicleIdentityBinding(
|
||||
registration.protocol(),
|
||||
registration.vin(),
|
||||
registration.phone(),
|
||||
registration.deviceId(),
|
||||
registration.plate()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public VehicleIdentity resolve(VehicleIdentityLookup lookup) {
|
||||
if (lookup == null) {
|
||||
@@ -83,9 +101,30 @@ public final class MySqlVehicleIdentityService implements VehicleIdentityResolve
|
||||
KEY idx_vehicle_identity_vin (vin)
|
||||
)
|
||||
""".formatted(table);
|
||||
String registrationSql = """
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
protocol VARCHAR(32) NOT NULL,
|
||||
phone VARCHAR(64) NOT NULL,
|
||||
vin VARCHAR(64) NOT NULL DEFAULT 'unknown',
|
||||
device_id VARCHAR(128) NOT NULL DEFAULT '',
|
||||
plate VARCHAR(128) NOT NULL DEFAULT '',
|
||||
province INT NULL,
|
||||
city INT NULL,
|
||||
maker VARCHAR(64) NOT NULL DEFAULT '',
|
||||
device_type VARCHAR(128) NOT NULL DEFAULT '',
|
||||
plate_color INT NULL,
|
||||
last_registered_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (protocol, phone),
|
||||
KEY idx_vehicle_registration_vin (vin),
|
||||
KEY idx_vehicle_registration_device (protocol, device_id),
|
||||
KEY idx_vehicle_registration_plate (protocol, plate)
|
||||
)
|
||||
""".formatted(registrationTable);
|
||||
try (Connection connection = dataSource.getConnection();
|
||||
Statement statement = connection.createStatement()) {
|
||||
statement.execute(sql);
|
||||
statement.execute(registrationSql);
|
||||
} catch (SQLException e) {
|
||||
throw new IllegalStateException("vehicle identity mysql schema initialize failed: " + table, e);
|
||||
}
|
||||
@@ -105,12 +144,29 @@ public final class MySqlVehicleIdentityService implements VehicleIdentityResolve
|
||||
resultSet.getString("identifier_value"),
|
||||
resultSet.getString("vin"));
|
||||
}
|
||||
loadRegistrationIndex(loaded, connection);
|
||||
return loaded;
|
||||
} catch (SQLException e) {
|
||||
throw new IllegalStateException("vehicle identity mysql index load failed: " + table, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadRegistrationIndex(InMemoryVehicleIdentityService loaded, Connection connection) throws SQLException {
|
||||
String sql = "SELECT protocol, phone, device_id, plate, vin FROM " + registrationTable
|
||||
+ " WHERE vin IS NOT NULL AND vin <> '' AND LOWER(vin) <> 'unknown'";
|
||||
try (PreparedStatement statement = connection.prepareStatement(sql);
|
||||
ResultSet resultSet = statement.executeQuery()) {
|
||||
while (resultSet.next()) {
|
||||
loaded.bind(new VehicleIdentityBinding(
|
||||
protocol(resultSet.getString("protocol")),
|
||||
resultSet.getString("vin"),
|
||||
resultSet.getString("phone"),
|
||||
resultSet.getString("device_id"),
|
||||
resultSet.getString("plate")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void bindLoadedRow(InMemoryVehicleIdentityService loaded,
|
||||
ProtocolId protocol,
|
||||
String identifierType,
|
||||
@@ -173,6 +229,46 @@ public final class MySqlVehicleIdentityService implements VehicleIdentityResolve
|
||||
}
|
||||
}
|
||||
|
||||
private void upsertRegistration(VehicleRegistrationBinding registration) {
|
||||
String phone = normalize(registration.phone());
|
||||
if (phone.isBlank()) {
|
||||
return;
|
||||
}
|
||||
String vin = registration.hasResolvedVin() ? registration.vin().trim() : "unknown";
|
||||
String sql = "INSERT INTO " + registrationTable
|
||||
+ " (protocol, phone, vin, device_id, plate, province, city, maker, device_type, plate_color) "
|
||||
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) "
|
||||
+ "ON DUPLICATE KEY UPDATE "
|
||||
+ "vin = CASE WHEN VALUES(vin) <> 'unknown' THEN VALUES(vin) ELSE vin END, "
|
||||
+ "device_id = VALUES(device_id), plate = VALUES(plate), province = VALUES(province), "
|
||||
+ "city = VALUES(city), maker = VALUES(maker), device_type = VALUES(device_type), "
|
||||
+ "plate_color = VALUES(plate_color), last_registered_at = CURRENT_TIMESTAMP";
|
||||
try (Connection connection = dataSource.getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement(sql)) {
|
||||
statement.setString(1, protocolName(registration.protocol()));
|
||||
statement.setString(2, phone);
|
||||
statement.setString(3, vin);
|
||||
statement.setString(4, normalize(registration.deviceId()));
|
||||
statement.setString(5, normalize(registration.plate()));
|
||||
setInteger(statement, 6, registration.province());
|
||||
setInteger(statement, 7, registration.city());
|
||||
statement.setString(8, normalize(registration.maker()));
|
||||
statement.setString(9, normalize(registration.deviceType()));
|
||||
setInteger(statement, 10, registration.plateColor());
|
||||
statement.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
throw new IllegalStateException("vehicle identity mysql registration bind failed: " + registrationTable, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void setInteger(PreparedStatement statement, int index, Integer value) throws SQLException {
|
||||
if (value == null) {
|
||||
statement.setObject(index, null);
|
||||
} else {
|
||||
statement.setInt(index, value);
|
||||
}
|
||||
}
|
||||
|
||||
private static String sanitizeTable(String table) {
|
||||
String value = table == null || table.isBlank() ? "vehicle_identity_binding" : table.trim();
|
||||
if (!value.matches("[A-Za-z0-9_]+")) {
|
||||
|
||||
@@ -3,4 +3,7 @@ package com.lingniu.ingest.identity;
|
||||
public interface VehicleIdentityRegistry {
|
||||
|
||||
void bind(VehicleIdentityBinding binding);
|
||||
|
||||
default void register(VehicleRegistrationBinding registration) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.lingniu.ingest.identity;
|
||||
|
||||
import com.lingniu.ingest.api.ProtocolId;
|
||||
|
||||
public record VehicleRegistrationBinding(
|
||||
ProtocolId protocol,
|
||||
String vin,
|
||||
String phone,
|
||||
String deviceId,
|
||||
String plate,
|
||||
Integer province,
|
||||
Integer city,
|
||||
String maker,
|
||||
String deviceType,
|
||||
Integer plateColor
|
||||
) {
|
||||
public VehicleRegistrationBinding {
|
||||
vin = normalize(vin);
|
||||
phone = normalize(phone);
|
||||
deviceId = normalize(deviceId);
|
||||
plate = normalize(plate);
|
||||
maker = normalize(maker);
|
||||
deviceType = normalize(deviceType);
|
||||
if (phone.isBlank() && deviceId.isBlank() && plate.isBlank()) {
|
||||
throw new IllegalArgumentException("at least one registration identifier must not be blank");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasResolvedVin() {
|
||||
return !vin.isBlank() && !"unknown".equalsIgnoreCase(vin);
|
||||
}
|
||||
|
||||
private static String normalize(String value) {
|
||||
return value == null ? "" : value.trim();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user