refactor: remove file identity runtime
This commit is contained in:
@@ -62,7 +62,7 @@ history 消费者会按协议和 raw/event 自动拆分 consumer group。设置
|
|||||||
- `vehicle-history-live-jt808-event`
|
- `vehicle-history-live-jt808-event`
|
||||||
- `vehicle-history-live-jt808-raw`
|
- `vehicle-history-live-jt808-raw`
|
||||||
|
|
||||||
JT808 身份绑定本地模板默认使用 `file`,可以在没有 MySQL 时持久化人工绑定,避免重启后退回 `unknown`。需要把 0x0100 注册信息维护到 MySQL 并支持真实 VIN 反写时,将 `com.lingniu.jt808.plist.template` 里的 `VEHICLE_IDENTITY_STORE` 改为 `mysql`,并提供 `VEHICLE_IDENTITY_MYSQL_*` 连接参数。服务会自动创建 `vehicle_identity_binding` 和 `vehicle_identity_binding_registration` 两张表:前者只保存 `plate`、`vin`,后者保存 0x0100 注册字段;服务按刷新周期用注册表里的车牌关联 VIN,后续 raw/event 会带真实 VIN。`memory` 仅适合一次性联调,不建议用于生产验证。
|
本地模板与 ECS 生产口径一致,身份绑定只使用 MySQL。启动前必须提供 `VEHICLE_IDENTITY_MYSQL_*` 连接参数;服务会自动创建 `vehicle_identity_binding` 和 `vehicle_identity_binding_registration` 两张表:前者只保存 `plate`、`vin`,后者保存注册字段;服务按刷新周期用注册表里的车牌关联 VIN,后续 raw/event 会带真实 VIN。
|
||||||
|
|
||||||
## 启停
|
## 启停
|
||||||
|
|
||||||
|
|||||||
@@ -37,9 +37,17 @@
|
|||||||
<key>SINK_ARCHIVE_PATH</key>
|
<key>SINK_ARCHIVE_PATH</key>
|
||||||
<string>__SHARED_ARCHIVE_PATH__</string>
|
<string>__SHARED_ARCHIVE_PATH__</string>
|
||||||
<key>VEHICLE_IDENTITY_STORE</key>
|
<key>VEHICLE_IDENTITY_STORE</key>
|
||||||
<string>file</string>
|
<string>mysql</string>
|
||||||
<key>VEHICLE_IDENTITY_FILE</key>
|
<key>VEHICLE_IDENTITY_MYSQL_TABLE</key>
|
||||||
<string>__PROJECT_ROOT__/data/vehicle-identity-gb32960.jsonl</string>
|
<string>vehicle_identity_binding</string>
|
||||||
|
<key>VEHICLE_IDENTITY_MYSQL_JDBC_URL</key>
|
||||||
|
<string>__VEHICLE_IDENTITY_MYSQL_JDBC_URL__</string>
|
||||||
|
<key>VEHICLE_IDENTITY_MYSQL_USERNAME</key>
|
||||||
|
<string>__VEHICLE_IDENTITY_MYSQL_USERNAME__</string>
|
||||||
|
<key>VEHICLE_IDENTITY_MYSQL_PASSWORD</key>
|
||||||
|
<string>__VEHICLE_IDENTITY_MYSQL_PASSWORD__</string>
|
||||||
|
<key>VEHICLE_IDENTITY_MYSQL_REFRESH_INTERVAL</key>
|
||||||
|
<string>__VEHICLE_IDENTITY_MYSQL_REFRESH_INTERVAL__</string>
|
||||||
<key>NACOS_CONFIG_ENABLED</key>
|
<key>NACOS_CONFIG_ENABLED</key>
|
||||||
<string>false</string>
|
<string>false</string>
|
||||||
<key>MANAGEMENT_HEALTH_REDIS_ENABLED</key>
|
<key>MANAGEMENT_HEALTH_REDIS_ENABLED</key>
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
<key>SINK_ARCHIVE_PATH</key>
|
<key>SINK_ARCHIVE_PATH</key>
|
||||||
<string>__SHARED_ARCHIVE_PATH__</string>
|
<string>__SHARED_ARCHIVE_PATH__</string>
|
||||||
<key>VEHICLE_IDENTITY_STORE</key>
|
<key>VEHICLE_IDENTITY_STORE</key>
|
||||||
<string>file</string>
|
<string>mysql</string>
|
||||||
<key>VEHICLE_IDENTITY_MYSQL_TABLE</key>
|
<key>VEHICLE_IDENTITY_MYSQL_TABLE</key>
|
||||||
<string>vehicle_identity_binding</string>
|
<string>vehicle_identity_binding</string>
|
||||||
<key>VEHICLE_IDENTITY_MYSQL_JDBC_URL</key>
|
<key>VEHICLE_IDENTITY_MYSQL_JDBC_URL</key>
|
||||||
|
|||||||
@@ -46,7 +46,10 @@ services:
|
|||||||
GB32960_PLATFORM_PWD_HYUNDAI: ${GB32960_PLATFORM_PWD_HYUNDAI:-}
|
GB32960_PLATFORM_PWD_HYUNDAI: ${GB32960_PLATFORM_PWD_HYUNDAI:-}
|
||||||
GB32960_PLATFORM_IP_HYUNDAI: ${GB32960_PLATFORM_IP_HYUNDAI:-}
|
GB32960_PLATFORM_IP_HYUNDAI: ${GB32960_PLATFORM_IP_HYUNDAI:-}
|
||||||
GB32960_TLS_ENABLED: ${GB32960_TLS_ENABLED:-false}
|
GB32960_TLS_ENABLED: ${GB32960_TLS_ENABLED:-false}
|
||||||
VEHICLE_IDENTITY_FILE: /data/vehicle-identity.jsonl
|
VEHICLE_IDENTITY_STORE: ${VEHICLE_IDENTITY_STORE:-mysql}
|
||||||
|
VEHICLE_IDENTITY_MYSQL_JDBC_URL: ${MYSQL_JDBC_URL:-}
|
||||||
|
VEHICLE_IDENTITY_MYSQL_USERNAME: ${MYSQL_USERNAME:-}
|
||||||
|
VEHICLE_IDENTITY_MYSQL_PASSWORD: ${MYSQL_PASSWORD:-}
|
||||||
ports:
|
ports:
|
||||||
- "${GB32960_HTTP_PORT:-20100}:20100"
|
- "${GB32960_HTTP_PORT:-20100}:20100"
|
||||||
- "${GB32960_TCP_PORT:-32960}:32960"
|
- "${GB32960_TCP_PORT:-32960}:32960"
|
||||||
|
|||||||
@@ -1,106 +0,0 @@
|
|||||||
package com.lingniu.ingest.identity;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import com.lingniu.ingest.api.ProtocolId;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.BufferedWriter;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.StandardOpenOption;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 文件型车辆身份绑定表。
|
|
||||||
*
|
|
||||||
* <p>写入采用 append-only JSONL,启动时顺序重放到内存索引。这样协议层只依赖 identity SPI,
|
|
||||||
* 不直接耦合业务库;后续替换成 DB/配置中心时只需新增同接口实现。
|
|
||||||
*/
|
|
||||||
public final class FileVehicleIdentityService implements VehicleIdentityResolver, VehicleIdentityRegistry {
|
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(FileVehicleIdentityService.class);
|
|
||||||
|
|
||||||
private final Path path;
|
|
||||||
private final ObjectMapper mapper;
|
|
||||||
private final InMemoryVehicleIdentityService delegate = new InMemoryVehicleIdentityService();
|
|
||||||
private final Object writeLock = new Object();
|
|
||||||
|
|
||||||
public FileVehicleIdentityService(Path path) {
|
|
||||||
this(path, new ObjectMapper());
|
|
||||||
}
|
|
||||||
|
|
||||||
public FileVehicleIdentityService(Path path, ObjectMapper mapper) {
|
|
||||||
this.path = path.toAbsolutePath();
|
|
||||||
this.mapper = mapper;
|
|
||||||
load();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bind(VehicleIdentityBinding binding) {
|
|
||||||
if (binding == null || !binding.hasResolvedVin()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
delegate.bind(binding);
|
|
||||||
synchronized (writeLock) {
|
|
||||||
try {
|
|
||||||
Files.createDirectories(path.getParent());
|
|
||||||
try (BufferedWriter writer = Files.newBufferedWriter(path, StandardCharsets.UTF_8,
|
|
||||||
StandardOpenOption.CREATE, StandardOpenOption.APPEND, StandardOpenOption.WRITE)) {
|
|
||||||
// append-only 允许运行时新增绑定;重启时按文件顺序 replay,后写覆盖内存索引中的旧映射。
|
|
||||||
writer.write(mapper.writeValueAsString(BindingLine.from(binding)));
|
|
||||||
writer.newLine();
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new IllegalStateException("vehicle identity binding persist failed: " + path, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public VehicleIdentity resolve(VehicleIdentityLookup lookup) {
|
|
||||||
return delegate.resolve(lookup);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void load() {
|
|
||||||
if (!Files.exists(path)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try (BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
|
|
||||||
String line;
|
|
||||||
while ((line = reader.readLine()) != null) {
|
|
||||||
if (line.isBlank()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
BindingLine binding = mapper.readValue(line, BindingLine.class);
|
|
||||||
// 单行损坏不影响其它绑定加载,避免一个坏配置拖垮全部接入。
|
|
||||||
delegate.bind(binding.toBinding());
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.warn("skip invalid vehicle identity binding line path={} line={}", path, line, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new IllegalStateException("vehicle identity binding load failed: " + path, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private record BindingLine(
|
|
||||||
ProtocolId protocol,
|
|
||||||
String vin,
|
|
||||||
String phone,
|
|
||||||
String deviceId,
|
|
||||||
String plate
|
|
||||||
) {
|
|
||||||
private static BindingLine from(VehicleIdentityBinding binding) {
|
|
||||||
return new BindingLine(
|
|
||||||
binding.protocol(), binding.vin(), binding.phone(), binding.deviceId(), binding.plate());
|
|
||||||
}
|
|
||||||
|
|
||||||
private VehicleIdentityBinding toBinding() {
|
|
||||||
return new VehicleIdentityBinding(protocol, vin, phone, deviceId, plate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
package com.lingniu.ingest.identity;
|
|
||||||
|
|
||||||
import com.lingniu.ingest.api.ProtocolId;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.junit.jupiter.api.io.TempDir;
|
|
||||||
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
class FileVehicleIdentityServiceTest {
|
|
||||||
|
|
||||||
@TempDir
|
|
||||||
Path tempDir;
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void persistsBindingsAndReloadsThemAfterRestart() throws Exception {
|
|
||||||
Path store = tempDir.resolve("vehicle-identity.jsonl");
|
|
||||||
FileVehicleIdentityService first = new FileVehicleIdentityService(store);
|
|
||||||
|
|
||||||
first.bind(new VehicleIdentityBinding(
|
|
||||||
ProtocolId.JT808, "LNVIN000000000099", "13900000099", "DEV099", "粤B09999"));
|
|
||||||
|
|
||||||
FileVehicleIdentityService restarted = new FileVehicleIdentityService(store);
|
|
||||||
|
|
||||||
VehicleIdentity byPhone = restarted.resolve(new VehicleIdentityLookup(
|
|
||||||
ProtocolId.JT808, "", "13900000099", "", ""));
|
|
||||||
VehicleIdentity byDevice = restarted.resolve(new VehicleIdentityLookup(
|
|
||||||
ProtocolId.JT808, "", "", "DEV099", ""));
|
|
||||||
VehicleIdentity byPlate = restarted.resolve(new VehicleIdentityLookup(
|
|
||||||
ProtocolId.JT808, "", "", "", "粤B09999"));
|
|
||||||
|
|
||||||
assertThat(byPhone.vin()).isEqualTo("LNVIN000000000099");
|
|
||||||
assertThat(byPhone.resolved()).isTrue();
|
|
||||||
assertThat(byPhone.source()).isEqualTo(VehicleIdentitySource.BOUND_PHONE);
|
|
||||||
assertThat(byDevice.vin()).isEqualTo("LNVIN000000000099");
|
|
||||||
assertThat(byPlate.vin()).isEqualTo("LNVIN000000000099");
|
|
||||||
assertThat(Files.readString(store)).contains("\"vin\":\"LNVIN000000000099\"");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user