xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/AdcOssHandleServiceImpl.java
@@ -3,6 +3,7 @@ import com.xiaopeng.map.config.VinSaltProperties; import com.xiaopeng.map.service.AdcOssHandleService; import com.xiaopeng.map.service.RobotaxiOssService; import com.xiaopeng.map.utils.MapAreaVersionProtoWriter; import com.xiaopeng.map.utils.VehicleCodec; import com.xiaopeng.map.utils.XngpAreaRTreeFbsUtil; import lombok.RequiredArgsConstructor; @@ -12,8 +13,7 @@ import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import java.text.SimpleDateFormat; import java.util.Date; import java.io.IOException; import java.util.List; /** @@ -24,8 +24,6 @@ @RequiredArgsConstructor @ConditionalOnBean(RobotaxiOssService.class) public class AdcOssHandleServiceImpl implements AdcOssHandleService { private static final String FILE_NAME_FORMAT = "yyyyMMddHHmmss"; private final RobotaxiOssService robotaxiOssService; private final VinSaltProperties vinSaltProperties; @@ -41,9 +39,18 @@ throw new IllegalStateException("Failed to build RTree fbs from points"); } String fileName = new SimpleDateFormat(FILE_NAME_FORMAT).format(new Date()) + "_xngp_area_data.bin"; int version = (int) (System.currentTimeMillis() / 1000); String binFileName = version + "_xngp_area_data.bin"; String objectKey = robotaxiOssService.uploadOddLayerFile(fbsBytes, fileName, areaCode, vehicleUuid); String objectKey = robotaxiOssService.uploadOddLayerFile(fbsBytes, binFileName, areaCode, vehicleUuid); try { byte[] pbBytes = MapAreaVersionProtoWriter.toByteArray(version, fbsBytes.length); robotaxiOssService.uploadOddLayerFile(pbBytes, "xngp_area_version.pb", areaCode, vehicleUuid); } catch (IOException e) { throw new IllegalStateException("Failed to build or upload xngp_area_version.pb", e); } log.debug("AdcOssHandle uploaded ODD layer file, areaId={}, areaCode={}, objectKey={}", areaId, areaCode, objectKey); return objectKey; } xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/utils/MapAreaVersionProtoWriter.java
New file @@ -0,0 +1,32 @@ package com.xiaopeng.map.utils; import com.google.protobuf.CodedOutputStream; import java.io.IOException; /** * 将 MapAreaVersion 序列化为与 map_area_version.proto 兼容的 Protobuf 二进制格式。 */ public final class MapAreaVersionProtoWriter { private MapAreaVersionProtoWriter() { } /** * 生成 MapAreaVersion 的 protobuf 二进制 * * @param version Unix 秒级时间戳 * @param fileLength bin 文件字节长度 * @return protobuf 二进制 */ public static byte[] toByteArray(int version, int fileLength) throws IOException { int size = CodedOutputStream.computeUInt32Size(1, version) + CodedOutputStream.computeUInt32Size(2, fileLength); byte[] buf = new byte[size]; CodedOutputStream cos = CodedOutputStream.newInstance(buf); cos.writeUInt32(1, version); cos.writeUInt32(2, fileLength); cos.checkNoSpaceLeft(); return buf; } } xp-robotaxi-map-boot/src/test/java/com/xiaopeng/map/service/AdcOssHandleServiceTest.java
@@ -53,11 +53,19 @@ assertTrue(objectKey.contains("ROBOTAXI_ODD_layer") && objectKey.contains(areaCode), "返回 objectKey 应包含路径前缀和区域编码"); // 获取下载临时链接(Presigned URL) // 获取 bin 文件下载临时链接(Presigned URL) String downloadUrl = robotaxiOssService.generatePresignedDownloadUrl(objectKey, 3600); log.info("generatePresignedDownloadUrl downloadUrl={}", downloadUrl); log.info("generatePresignedDownloadUrl bin downloadUrl={}", downloadUrl); assertNotNull(downloadUrl); assertTrue(downloadUrl.contains("Expires") || downloadUrl.contains("Signature"), "下载临时链接应包含签名参数(Expires 或 Signature)"); // 获取 xngp_area_version.pb 下载临时链接(与 bin 同目录) String pbObjectKey = objectKey.substring(0, objectKey.lastIndexOf('/') + 1) + "xngp_area_version.pb"; String pbDownloadUrl = robotaxiOssService.generatePresignedDownloadUrl(pbObjectKey, 3600); log.info("generatePresignedDownloadUrl xngp_area_version.pb downloadUrl={}", pbDownloadUrl); assertNotNull(pbDownloadUrl); assertTrue(pbDownloadUrl.contains("Expires") || pbDownloadUrl.contains("Signature"), "xngp_area_version.pb 下载临时链接应包含签名参数"); } }