test: centralize vehicle identity fixtures

This commit is contained in:
lingniu
2026-07-01 18:32:09 +08:00
parent 4eaaa88e9c
commit e06534d6a5
12 changed files with 95 additions and 150 deletions

View File

@@ -4,7 +4,7 @@
## 设计目标
- **协议接入统一抽象**GB/T 32960、JT/T 808、Yutong MQTT 是默认生产接入JT/T 1078、JSATL12 作为显式 profile 的可选能力;信达 Push 仅保留为废弃兼容模块
- **协议接入统一抽象**GB/T 32960、JT/T 808、Yutong MQTT 是默认生产接入JT/T 1078、JSATL12 作为显式 profile 的可选能力;信达 Push 已废弃并删除源码
- **原子能力化**:每个协议一个独立 Maven 模块 + 独立 AutoConfiguration + 独立配置开关
- **业务 / 实时彻底解耦**:协议接入应用只负责收、解析、校验、规整和投递 Kafka历史与统计由独立 Kafka 消费应用落库
- **高并发低延迟**Netty + Disruptor + Java 25 虚拟线程;目标单节点 ≥ 5 万 msg/sP99 < 50 ms
@@ -53,6 +53,8 @@ lingniu-vehicle-ingest/
│ │ ├── event-history-service/ Kafka 全字段事件消费 + 历史查询
│ │ ├── vehicle-state-service/ Kafka 全字段事件消费 + Redis 热状态查询optional-latest-state profile
│ │ └── vehicle-stat-service/ Kafka 全字段事件消费 + 可配置日统计
│ ├── testing/
│ │ └── vehicle-identity-test-support/ 协议测试共享身份解析夹具
│ └── apps/
│ ├── command-gateway/ 可选 HTTP → 设备下行命令optional-command-gateway profile
│ ├── gb32960-ingest-app/ GB32960 TCP 接入 + Kafka 投递

View File

@@ -122,6 +122,7 @@ class MavenModuleProfileTest {
void xindaModulesAreRemovedFromBuildSurface() throws Exception {
Document pom = rootPom();
String rootPomText = Files.readString(repositoryRoot().resolve("pom.xml"));
String readme = Files.readString(repositoryRoot().resolve("README.md"));
assertThat(defaultModules(pom))
.doesNotContain("modules/inbound/inbound-xinda-push")
@@ -137,6 +138,10 @@ class MavenModuleProfileTest {
.doesNotContain("legacy-xinda")
.doesNotContain("inbound-xinda-push")
.doesNotContain("xinda-push-app");
assertThat(readme)
.contains("信达 Push 已废弃并删除源码")
.doesNotContain("信达 Push 仅保留")
.doesNotContain("废弃兼容模块");
}
@Test
@@ -187,6 +192,29 @@ class MavenModuleProfileTest {
}
}
@Test
void vehicleIdentityTestSupportIsCentralizedInsteadOfCopiedIntoProtocolTests() throws Exception {
Document pom = rootPom();
List<String> testSupportFixtures = javaFilesNamed("InMemoryVehicleIdentityService.java");
assertThat(defaultModules(pom))
.contains("modules/testing/vehicle-identity-test-support");
assertThat(rootDependencyManagementArtifacts(pom))
.contains("vehicle-identity-test-support");
assertThat(testSupportFixtures)
.containsExactly("modules/testing/vehicle-identity-test-support/src/main/java/com/lingniu/ingest/identity/InMemoryVehicleIdentityService.java");
for (String modulePom : List.of(
"modules/protocols/protocol-jt808/pom.xml",
"modules/inbound/inbound-mqtt/pom.xml",
"modules/protocols/protocol-jt1078/pom.xml",
"modules/protocols/protocol-jsatl12/pom.xml")) {
assertThat(hasDependency(modulePom(modulePom), "com.lingniu.ingest", "vehicle-identity-test-support"))
.as(modulePom)
.isTrue();
}
}
@Test
void readmeMatchesCurrentRuntimeVersionsAndSplitAppResponsibilities() throws Exception {
Document pom = rootPom();
@@ -798,6 +826,19 @@ class MavenModuleProfileTest {
}
}
private static List<String> javaFilesNamed(String fileName) throws Exception {
Path root = repositoryRoot();
try (Stream<Path> files = Files.walk(root.resolve("modules"))) {
return files
.filter(Files::isRegularFile)
.filter(path -> fileName.equals(path.getFileName().toString()))
.map(root::relativize)
.map(Path::toString)
.sorted()
.toList();
}
}
private static Element firstDirectChild(Element parent, String tagName) {
NodeList children = parent.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {

View File

@@ -24,6 +24,11 @@
<groupId>com.lingniu.ingest</groupId>
<artifactId>vehicle-identity</artifactId>
</dependency>
<dependency>
<groupId>com.lingniu.ingest</groupId>
<artifactId>vehicle-identity-test-support</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>

View File

@@ -31,6 +31,11 @@
<groupId>com.lingniu.ingest</groupId>
<artifactId>vehicle-identity</artifactId>
</dependency>
<dependency>
<groupId>com.lingniu.ingest</groupId>
<artifactId>vehicle-identity-test-support</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.lingniu.ingest</groupId>
<artifactId>sink-archive</artifactId>

View File

@@ -1,48 +0,0 @@
package com.lingniu.ingest.identity;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public final class InMemoryVehicleIdentityService implements VehicleIdentityResolver, VehicleIdentityRegistry {
private final List<VehicleIdentityBinding> bindings = new CopyOnWriteArrayList<>();
@Override
public VehicleIdentity resolve(VehicleIdentityLookup lookup) {
if (lookup == null) {
return new VehicleIdentity("unknown", false, VehicleIdentitySource.UNKNOWN);
}
if (!lookup.vin().isBlank() && !"unknown".equalsIgnoreCase(lookup.vin())) {
return new VehicleIdentity(lookup.vin(), true, VehicleIdentitySource.EXPLICIT_VIN);
}
for (VehicleIdentityBinding binding : bindings) {
if (binding.protocol() != lookup.protocol()) {
continue;
}
if (!lookup.phone().isBlank() && lookup.phone().equals(binding.phone())) {
return new VehicleIdentity(binding.vin(), true, VehicleIdentitySource.BOUND_PHONE);
}
if (!lookup.deviceId().isBlank() && lookup.deviceId().equals(binding.deviceId())) {
return new VehicleIdentity(binding.vin(), true, VehicleIdentitySource.BOUND_DEVICE_ID);
}
if (!lookup.plate().isBlank() && lookup.plate().equals(binding.plate())) {
return new VehicleIdentity(binding.vin(), true, VehicleIdentitySource.BOUND_PLATE);
}
}
if (!lookup.phone().isBlank()) {
return new VehicleIdentity("unknown", false, VehicleIdentitySource.FALLBACK_PHONE);
}
if (!lookup.deviceId().isBlank()) {
return new VehicleIdentity("unknown", false, VehicleIdentitySource.FALLBACK_DEVICE_ID);
}
if (!lookup.plate().isBlank()) {
return new VehicleIdentity("unknown", false, VehicleIdentitySource.FALLBACK_PLATE);
}
return new VehicleIdentity("unknown", false, VehicleIdentitySource.UNKNOWN);
}
@Override
public void bind(VehicleIdentityBinding binding) {
bindings.add(binding);
}
}

View File

@@ -31,6 +31,11 @@
<groupId>com.lingniu.ingest</groupId>
<artifactId>vehicle-identity</artifactId>
</dependency>
<dependency>
<groupId>com.lingniu.ingest</groupId>
<artifactId>vehicle-identity-test-support</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.lingniu.ingest</groupId>
<artifactId>sink-archive</artifactId>

View File

@@ -1,48 +0,0 @@
package com.lingniu.ingest.identity;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public final class InMemoryVehicleIdentityService implements VehicleIdentityResolver, VehicleIdentityRegistry {
private final List<VehicleIdentityBinding> bindings = new CopyOnWriteArrayList<>();
@Override
public VehicleIdentity resolve(VehicleIdentityLookup lookup) {
if (lookup == null) {
return new VehicleIdentity("unknown", false, VehicleIdentitySource.UNKNOWN);
}
if (!lookup.vin().isBlank() && !"unknown".equalsIgnoreCase(lookup.vin())) {
return new VehicleIdentity(lookup.vin(), true, VehicleIdentitySource.EXPLICIT_VIN);
}
for (VehicleIdentityBinding binding : bindings) {
if (binding.protocol() != lookup.protocol()) {
continue;
}
if (!lookup.phone().isBlank() && lookup.phone().equals(binding.phone())) {
return new VehicleIdentity(binding.vin(), true, VehicleIdentitySource.BOUND_PHONE);
}
if (!lookup.deviceId().isBlank() && lookup.deviceId().equals(binding.deviceId())) {
return new VehicleIdentity(binding.vin(), true, VehicleIdentitySource.BOUND_DEVICE_ID);
}
if (!lookup.plate().isBlank() && lookup.plate().equals(binding.plate())) {
return new VehicleIdentity(binding.vin(), true, VehicleIdentitySource.BOUND_PLATE);
}
}
if (!lookup.phone().isBlank()) {
return new VehicleIdentity("unknown", false, VehicleIdentitySource.FALLBACK_PHONE);
}
if (!lookup.deviceId().isBlank()) {
return new VehicleIdentity("unknown", false, VehicleIdentitySource.FALLBACK_DEVICE_ID);
}
if (!lookup.plate().isBlank()) {
return new VehicleIdentity("unknown", false, VehicleIdentitySource.FALLBACK_PLATE);
}
return new VehicleIdentity("unknown", false, VehicleIdentitySource.UNKNOWN);
}
@Override
public void bind(VehicleIdentityBinding binding) {
bindings.add(binding);
}
}

View File

@@ -32,6 +32,11 @@
<groupId>com.lingniu.ingest</groupId>
<artifactId>vehicle-identity</artifactId>
</dependency>
<dependency>
<groupId>com.lingniu.ingest</groupId>
<artifactId>vehicle-identity-test-support</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>

View File

@@ -1,48 +0,0 @@
package com.lingniu.ingest.identity;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public final class InMemoryVehicleIdentityService implements VehicleIdentityResolver, VehicleIdentityRegistry {
private final List<VehicleIdentityBinding> bindings = new CopyOnWriteArrayList<>();
@Override
public VehicleIdentity resolve(VehicleIdentityLookup lookup) {
if (lookup == null) {
return new VehicleIdentity("unknown", false, VehicleIdentitySource.UNKNOWN);
}
if (!lookup.vin().isBlank() && !"unknown".equalsIgnoreCase(lookup.vin())) {
return new VehicleIdentity(lookup.vin(), true, VehicleIdentitySource.EXPLICIT_VIN);
}
for (VehicleIdentityBinding binding : bindings) {
if (binding.protocol() != lookup.protocol()) {
continue;
}
if (!lookup.phone().isBlank() && lookup.phone().equals(binding.phone())) {
return new VehicleIdentity(binding.vin(), true, VehicleIdentitySource.BOUND_PHONE);
}
if (!lookup.deviceId().isBlank() && lookup.deviceId().equals(binding.deviceId())) {
return new VehicleIdentity(binding.vin(), true, VehicleIdentitySource.BOUND_DEVICE_ID);
}
if (!lookup.plate().isBlank() && lookup.plate().equals(binding.plate())) {
return new VehicleIdentity(binding.vin(), true, VehicleIdentitySource.BOUND_PLATE);
}
}
if (!lookup.phone().isBlank()) {
return new VehicleIdentity("unknown", false, VehicleIdentitySource.FALLBACK_PHONE);
}
if (!lookup.deviceId().isBlank()) {
return new VehicleIdentity("unknown", false, VehicleIdentitySource.FALLBACK_DEVICE_ID);
}
if (!lookup.plate().isBlank()) {
return new VehicleIdentity("unknown", false, VehicleIdentitySource.FALLBACK_PLATE);
}
return new VehicleIdentity("unknown", false, VehicleIdentitySource.UNKNOWN);
}
@Override
public void bind(VehicleIdentityBinding binding) {
bindings.add(binding);
}
}

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.lingniu.ingest</groupId>
<artifactId>lingniu-vehicle-ingest</artifactId>
<version>0.1.0-SNAPSHOT</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<artifactId>vehicle-identity-test-support</artifactId>
<name>vehicle-identity-test-support</name>
<description>车辆身份解析测试夹具,仅供协议和接入模块测试依赖。</description>
<dependencies>
<dependency>
<groupId>com.lingniu.ingest</groupId>
<artifactId>vehicle-identity</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -29,14 +29,14 @@ public final class InMemoryVehicleIdentityService implements VehicleIdentityReso
return new VehicleIdentity(binding.vin(), true, VehicleIdentitySource.BOUND_PLATE);
}
}
if (!lookup.phone().isBlank()) {
return new VehicleIdentity("unknown", false, VehicleIdentitySource.FALLBACK_PHONE);
}
if (!lookup.deviceId().isBlank()) {
return new VehicleIdentity("unknown", false, VehicleIdentitySource.FALLBACK_DEVICE_ID);
return new VehicleIdentity(lookup.deviceId(), false, VehicleIdentitySource.FALLBACK_DEVICE_ID);
}
if (!lookup.phone().isBlank()) {
return new VehicleIdentity(lookup.phone(), false, VehicleIdentitySource.FALLBACK_PHONE);
}
if (!lookup.plate().isBlank()) {
return new VehicleIdentity("unknown", false, VehicleIdentitySource.FALLBACK_PLATE);
return new VehicleIdentity(lookup.plate(), false, VehicleIdentitySource.FALLBACK_PLATE);
}
return new VehicleIdentity("unknown", false, VehicleIdentitySource.UNKNOWN);
}

View File

@@ -23,6 +23,7 @@
<module>modules/core/ingest-core</module>
<module>modules/core/session-core</module>
<module>modules/core/vehicle-identity</module>
<module>modules/testing/vehicle-identity-test-support</module>
<module>modules/core/observability</module>
<module>modules/sinks/sink-kafka</module>
<module>modules/sinks/sink-archive</module>
@@ -201,6 +202,11 @@
<artifactId>vehicle-identity</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.lingniu.ingest</groupId>
<artifactId>vehicle-identity-test-support</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.lingniu.ingest</groupId>
<artifactId>observability</artifactId>