liujie
2026-06-09 c96fbe6b0aeb413a17c429133ee6d81d138144b9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
package com.xiaopeng.map.service.serviceimpl;
 
import com.xiaopeng.map.config.VinSaltProperties;
import com.xiaopeng.map.dto.AreaInput;
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;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
 
import java.util.List;
 
/**
 * ADC OSS 处理服务实现,根据坐标点生成 RTree fbs 并上传至 OSS。
 */
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor_ = @Lazy)
@ConditionalOnBean(RobotaxiOssService.class)
public class AdcOssHandleServiceImpl implements AdcOssHandleService {
 
    private final RobotaxiOssService robotaxiOssService;
    private final VinSaltProperties vinSaltProperties;
 
    @Override
    public String uploadOddLayerFile(List<AreaInput> areas, String vin, long version) {
        log.info("[ADC-OSS] uploadOddLayerFile 开始, vin={}, version={}, areasCount={}", vin, version, areas == null ? 0 : areas.size());
        if (areas != null) {
            for (int i = 0; i < areas.size(); i++) {
                AreaInput a = areas.get(i);
                log.info("[ADC-OSS] area[{}]: areaId={}, isBlacklist={}, pointsCount={}",
                        i, a.getAreaId(), a.isBlacklist(), a.getPoints() == null ? 0 : a.getPoints().size());
            }
        }
        validateParams(areas, vin);
 
        String vehicleUuid = VehicleCodec.vinToUUID(vin, vinSaltProperties.getSalt1(), vinSaltProperties.getSalt2());
        log.info("[ADC-OSS] VIN={} -> UUID={}", vin, vehicleUuid);
 
        byte[] fbsBytes = XngpAreaRTreeFbsUtil.buildRTreeBytesFromMultiAreas(areas);
        if (fbsBytes == null || fbsBytes.length == 0) {
            log.info("[ADC-OSS] buildRTreeBytesFromMultiAreas 返回空, vin={}", vin);
            throw new IllegalStateException("Failed to build RTree fbs from areas");
        }
        log.info("[ADC-OSS] FBS构建成功, vin={}, fbsSize={}bytes", vin, fbsBytes.length);
 
        String binFileName = version + "_xngp_area_data.bin";
 
        String objectKey = robotaxiOssService.uploadOddLayerFile(fbsBytes, binFileName, vehicleUuid);
        log.info("[ADC-OSS] bin文件上传成功, vin={}, objectKey={}", vin, objectKey);
 
        byte[] pbBytes = MapAreaVersionProtoWriter.toByteArray((int) version, fbsBytes.length);
        robotaxiOssService.uploadOddLayerFile(pbBytes, "xngp_area_version.pb", vehicleUuid);
        log.info("[ADC-OSS] pb版本文件上传成功, vin={}, version={}", vin, version);
 
        return objectKey;
    }
 
    @Override
    public String uploadEmptyFence(String vin, long version) {
        log.info("[ADC-OSS] uploadEmptyFence 开始, vin={}, version={}", vin, version);
        if (!StringUtils.hasText(vin)) {
            throw new IllegalArgumentException("vin cannot be empty");
        }
 
        String vehicleUuid = VehicleCodec.vinToUUID(vin, vinSaltProperties.getSalt1(), vinSaltProperties.getSalt2());
        log.info("[ADC-OSS] VIN={} -> UUID={}", vin, vehicleUuid);
 
        byte[] fbsBytes = XngpAreaRTreeFbsUtil.buildEmptyRTreeBytes();
        if (fbsBytes == null || fbsBytes.length == 0) {
            log.info("[ADC-OSS] buildEmptyRTreeBytes 返回空, vin={}", vin);
            throw new IllegalStateException("Failed to build empty RTree fbs");
        }
        log.info("[ADC-OSS] 空FBS构建成功, vin={}, fbsSize={}bytes", vin, fbsBytes.length);
 
        String binFileName = version + "_xngp_area_data.bin";
        String objectKey = robotaxiOssService.uploadOddLayerFile(fbsBytes, binFileName, vehicleUuid);
        log.info("[ADC-OSS] 空FBS bin文件上传成功, vin={}, objectKey={}", vin, objectKey);
 
        byte[] pbBytes = MapAreaVersionProtoWriter.toByteArray((int) version, fbsBytes.length);
        robotaxiOssService.uploadOddLayerFile(pbBytes, "xngp_area_version.pb", vehicleUuid);
        log.info("[ADC-OSS] 空FBS pb版本文件上传成功, vin={}, version={}", vin, version);
 
        return objectKey;
    }
 
    private void validateParams(List<AreaInput> areas, String vin) {
        if (CollectionUtils.isEmpty(areas)) {
            log.info("[ADC-OSS] validateParams失败: areas为空, vin={}", vin);
            throw new IllegalArgumentException("areas cannot be empty");
        }
        for (int i = 0; i < areas.size(); i++) {
            AreaInput area = areas.get(i);
            if (CollectionUtils.isEmpty(area.getPoints())) {
                log.info("[ADC-OSS] validateParams失败: area[{}] areaId={} points为空, vin={}", i, area.getAreaId(), vin);
                throw new IllegalArgumentException("each area must have non-empty points, areaId=" + area.getAreaId());
            }
        }
        if (!StringUtils.hasText(vin)) {
            log.info("[ADC-OSS] validateParams失败: vin为空");
            throw new IllegalArgumentException("vin cannot be empty");
        }
    }
}