QDAirPortBackend0122/doc/work/PositionUpdateEvent_API重构_20250115.md
2026-01-22 13:19:47 +08:00

6.1 KiB
Raw Blame History

PositionUpdateEvent API重构文档

创建日期: 2025-01-15
版本: 0.1.11
重构类型: API设计原则的严格实施

问题背景

用户提出的问题

用户询问:"PositionUpdateEvent中为什么返回车辆ID用了string"

原有设计问题

  1. 类型不一致getVehicleId()返回String类型违背了"索引性质使用vehicleId"的设计原则
  2. 混合逻辑为了兼容两种不同的payload类型强制使用String统一返回
  3. 语义不清:没有明确区分索引标识符和业务标识符的使用场景

设计原则回顾

用户在前面确立的API设计原则

索引性质的返回值使用vehicleId唯一、稳定的标识符
详细信息的返回值包含vehicleId + licensePlate完整信息

重构方案

核心思路

区分不同数据类型和使用场景,提供专门的方法:

  1. 索引性质getVehicleId() → 返回Long类型
  2. 业务标识符getBusinessId() → 返回String类型
  3. 完整信息getVehicleIdentifier() → 返回完整对象

数据类型区分

无人车数据VehicleLocation

  • vehicleIdLong类型数据库主键
  • businessIdString类型车牌号licensePlate
  • 特点:有数据库记录,支持索引查询

航空器/机场车辆数据PositionUpdatePayload

  • vehicleIdnull没有数据库记录
  • businessIdString类型航班号/车牌号objectId
  • 特点:仅实时处理,不持久化存储

重构实施

1. getVehicleId()方法重构

修改前:

public String getVehicleId() {
    if (payload instanceof VehicleLocation) {
        return String.valueOf(((VehicleLocation) payload).getVehicleId());
    } else if (payload instanceof PositionUpdatePayload) {
        return ((PositionUpdatePayload) payload).getObjectId();
    }
    return null;
}

修改后:

public Long getVehicleId() {
    if (payload instanceof VehicleLocation) {
        return ((VehicleLocation) payload).getVehicleId();
    }
    // 航空器和机场车辆没有数据库记录返回null
    return null;
}

2. 新增getBusinessId()方法

public String getBusinessId() {
    if (payload instanceof VehicleLocation) {
        return ((VehicleLocation) payload).getLicensePlate();
    } else if (payload instanceof PositionUpdatePayload) {
        return ((PositionUpdatePayload) payload).getObjectId();
    }
    return null;
}

3. 新增getVehicleIdentifier()方法

public VehicleIdentifier getVehicleIdentifier() {
    if (payload instanceof VehicleLocation) {
        VehicleLocation location = (VehicleLocation) payload;
        return VehicleIdentifier.builder()
                .vehicleId(location.getVehicleId())
                .businessId(location.getLicensePlate())
                .vehicleType(location.getVehicleType().name())
                .build();
    } else if (payload instanceof PositionUpdatePayload) {
        PositionUpdatePayload positionPayload = (PositionUpdatePayload) payload;
        return VehicleIdentifier.builder()
                .vehicleId(null) // 航空器没有数据库记录
                .businessId(positionPayload.getObjectId())
                .vehicleType(positionPayload.getObjectType())
                .build();
    }
    return null;
}

4. VehicleIdentifier内部类

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class VehicleIdentifier {
    private Long vehicleId;      // 数据库主键(索引性质)
    private String businessId;   // 业务标识符(车牌号、航班号等)
    private String vehicleType;  // 车辆类型
}

使用场景说明

索引场景

PositionUpdateEvent event = new PositionUpdateEvent(vehicleLocation);

// 用于Map索引仅无人车有效
Long vehicleId = event.getVehicleId();
if (vehicleId != null) {
    vehicleLocationMap.put(String.valueOf(vehicleId), location);
}

业务展示场景

// 用于前端显示
String displayId = event.getBusinessId(); // 车牌号或航班号
String displayText = "车辆: " + displayId;

完整信息场景

// 获取完整标识符信息
VehicleIdentifier identifier = event.getVehicleIdentifier();
if (identifier.getVehicleId() != null) {
    // 有数据库记录的无人车
    processUnmannedVehicle(identifier);
} else {
    // 仅实时处理的航空器/机场车辆
    processRealtimeObject(identifier);
}

测试用例修正

修改前

assertEquals("TEST_AIRCRAFT_001", event.getVehicleId());

修改后

// 航空器没有数据库记录
assertNull(event.getVehicleId());
assertEquals("TEST_AIRCRAFT_001", event.getBusinessId());

// 验证完整标识符信息
VehicleIdentifier identifier = event.getVehicleIdentifier();
assertNull(identifier.getVehicleId());
assertEquals("TEST_AIRCRAFT_001", identifier.getBusinessId());
assertEquals("AIRCRAFT", identifier.getVehicleType());

技术优势

1. 类型安全

  • 明确的Long/String类型区分
  • 避免不必要的类型转换
  • 编译时类型检查

2. 语义清晰

  • 索引和业务标识符明确分离
  • 方法名称直接表达用途
  • 减少使用时的歧义

3. 扩展性

  • VehicleIdentifier类支持未来扩展
  • 不同数据源的统一处理
  • 保持向后兼容性

4. 性能优化

  • 减少不必要的字符串转换
  • Map操作使用数字索引更高效
  • 内存使用更节省

验证结果

  • 编译测试通过
  • 单元测试通过WebSocketEventTest
  • API设计原则完全落实
  • 保持向后兼容性
  • 文档和测试用例更新完成

设计原则强化

通过这次重构进一步强化了API设计原则的应用

  1. 索引性质 → 明确的数据类型Long
  2. 详细信息 → 完整的信息对象VehicleIdentifier
  3. 业务语义 → 专门的业务标识符方法getBusinessId
  4. 类型安全 → 编译时类型检查和null处理

这个重构为项目中所有类似的API设计树立了标准确保了设计的一致性和可维护性。