puzhibing
2026-04-10 ed838e62602c0a63a64ff4bd144a0d16cc9a7cf8
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
package com.xiaopeng.map.service.serviceimpl;
 
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.xiaopeng.api.feignClient.vehicleConvoy.XpVehicleClient;
import com.xiaopeng.api.model.operation.XpElectronicFence;
import com.xiaopeng.api.model.operation.XpElectronicFenceArea;
import com.xiaopeng.api.model.vehicleConvoy.XpVehicle;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.map.dto.AreaInput;
import com.xiaopeng.map.mapper.XpElectronicFenceAreaMapper;
import com.xiaopeng.map.mapper.XpElectronicFenceMapper;
import com.xiaopeng.map.service.AdcFencePushService;
import com.xiaopeng.map.service.AdcOssHandleService;
import com.xiaopeng.map.utils.XngpAreaRTreeFbsUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
 
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
 
/**
 * ADC 围栏推送编排服务实现。
 * <p>
 * 版本管理:围栏主表 xp_electronic_fence.xpu_version 字段,
 * 每次触发推送时更新为秒级时间戳(与OSS文件名中的version一致),车端初始化时比对该版本号。
 * <p>
 * 注意:AdcOssHandleService 使用 @ConditionalOnBean(RobotaxiOssService.class),
 * 如果 OSS 配置缺失则不会注册,因此这里用 required=false 注入,为空时跳过推送。
 */
@Slf4j
@Service
public class AdcFencePushServiceImpl implements AdcFencePushService {
 
    @Autowired(required = false)
    private AdcOssHandleService adcOssHandleService;
 
    @Autowired
    private XpVehicleClient xpVehicleClient;
 
    @Autowired
    private XpElectronicFenceAreaMapper xpElectronicFenceAreaMapper;
 
    @Autowired
    private XpElectronicFenceMapper xpElectronicFenceMapper;
 
    @Override
    public void pushFenceToVehicles(Long electronicId) {
        log.info("[ADC推送] pushFenceToVehicles 开始, electronicId={}", electronicId);
        if (!isOssAvailable()) return;
 
        List<AreaInput> areaInputs = queryAreaInputs(electronicId);
        if (areaInputs.isEmpty()) {
            log.info("[ADC推送] 围栏{}无启用区域,跳过推送", electronicId);
            return;
        }
        log.info("[ADC推送] 围栏{}查询到{}个启用区域", electronicId, areaInputs.size());
 
        List<XpVehicle> vehicles = queryBoundVehicles(electronicId);
        if (vehicles.isEmpty()) {
            log.info("[ADC推送] 围栏{}无绑定车辆,跳过推送", electronicId);
            return;
        }
        log.info("[ADC推送] 围栏{}查询到{}辆绑定车辆", electronicId, vehicles.size());
 
        // 先更新版本号
        long version = updateXpuVersion(electronicId);
 
        for (XpVehicle vehicle : vehicles) {
            pushToSingleVehicle(vehicle.getVehicleVin(), areaInputs, electronicId, version);
        }
        log.info("[ADC推送] pushFenceToVehicles 完成, electronicId={}", electronicId);
    }
 
    @Override
    public void deactivateFenceForVehicles(Long electronicId) {
        if (!isOssAvailable()) return;
 
        List<XpVehicle> vehicles = queryBoundVehicles(electronicId);
        if (vehicles.isEmpty()) return;
 
        // 更新版本号(秒级时间戳)
        long version = updateXpuVersion(electronicId);
 
        for (XpVehicle vehicle : vehicles) {
            try {
                if (StringUtils.hasText(vehicle.getVehicleVin())) {
                    adcOssHandleService.uploadEmptyFence(vehicle.getVehicleVin(), version);
                    log.info("[ADC推送] 围栏{}失效,已向车辆{}上传空FBS", electronicId, vehicle.getVehicleVin());
                }
            } catch (Exception e) {
                log.info("[ADC推送] 向车辆{}上传失效空FBS失败", vehicle.getVehicleVin(), e);
            }
        }
    }
 
    @Override
    public void pushFenceToSingleVehicle(String vehicleVin, Long electronicId) {
        if (!isOssAvailable()) return;
 
        List<AreaInput> areaInputs = queryAreaInputs(electronicId);
        if (areaInputs.isEmpty()) {
            log.info("[ADC推送] 围栏{}无启用区域,跳过推送至车辆{}", electronicId, vehicleVin);
            return;
        }
 
        // 更新版本号(秒级时间戳)
        long version = updateXpuVersion(electronicId);
 
        pushToSingleVehicle(vehicleVin, areaInputs, electronicId, version);
    }
 
    @Override
    public String getLatestPushVersion(String vehicleVin) {
        try {
            // 通过VIN查询车辆绑定的围栏,再返回围栏的 xpu_version
            R<XpVehicle> vehicleR = xpVehicleClient.findByVehicleVin(vehicleVin);
            if (vehicleR == null || vehicleR.getData() == null || vehicleR.getData().getElectronicId() == null) {
                log.info("[ADC推送] 车辆{}未绑定围栏,无版本号", vehicleVin);
                return null;
            }
            Long electronicId = vehicleR.getData().getElectronicId();
            XpElectronicFence fence = xpElectronicFenceMapper.selectById(electronicId);
            if (fence == null || fence.getXpuVersion() == null) {
                return null;
            }
            return String.valueOf(fence.getXpuVersion());
        } catch (Exception e) {
            log.info("[ADC推送] 查询车辆{}绑定围栏版本失败", vehicleVin, e);
        }
        return null;
    }
 
    // ==================== 内部方法 ====================
 
    private void pushToSingleVehicle(String vehicleVin, List<AreaInput> areaInputs, Long electronicId, long version) {
        if (!StringUtils.hasText(vehicleVin)) return;
        long start = System.currentTimeMillis();
        try {
            adcOssHandleService.uploadOddLayerFile(areaInputs, vehicleVin, version);
            long cost = System.currentTimeMillis() - start;
            log.info("[ADC推送] 围栏{}推送至车辆{}成功,区域数={},version={},耗时={}ms",
                    electronicId, vehicleVin, areaInputs.size(), version, cost);
        } catch (Exception e) {
            long cost = System.currentTimeMillis() - start;
            log.info("[ADC推送] 围栏{}推送至车辆{}失败,耗时={}ms", electronicId, vehicleVin, cost, e);
        }
    }
 
    /**
     * 将围栏的 xpu_version 更新为当前秒级时间戳(与 OSS 文件名中的 version 保持一致)
     *
     * @return 写入的版本号(秒级时间戳)
     */
    private long updateXpuVersion(Long electronicId) {
        long version = System.currentTimeMillis() / 1000;
        try {
            xpElectronicFenceMapper.update(null,
                    new LambdaUpdateWrapper<XpElectronicFence>()
                            .eq(XpElectronicFence::getId, electronicId)
                            .set(XpElectronicFence::getXpuVersion, version));
            log.info("[ADC推送] 围栏{}版本号已更新为{}", electronicId, version);
        } catch (Exception e) {
            log.info("[ADC推送] 围栏{}版本号更新失败", electronicId, e);
        }
        return version;
    }
 
    private List<AreaInput> queryAreaInputs(Long electronicId) {
        List<XpElectronicFenceArea> areas = xpElectronicFenceAreaMapper.selectList(
                new LambdaQueryWrapper<XpElectronicFenceArea>()
                        .eq(XpElectronicFenceArea::getElectronicId, electronicId)
                        .eq(XpElectronicFenceArea::getAreaStatus, 1)
        );
        if (CollectionUtils.isEmpty(areas)) {
            log.info("[ADC推送] 围栏{}查询启用区域结果为空", electronicId);
            return Collections.emptyList();
        }
        log.info("[ADC推送] 围栏{}查询到{}个启用区域记录", electronicId, areas.size());
        List<AreaInput> result = new java.util.ArrayList<>();
        for (XpElectronicFenceArea area : areas) {
            AreaInput input = convertToAreaInput(area);
            if (input != null) {
                result.add(input);
            }
        }
        log.info("[ADC推送] 围栏{}转换后有效AreaInput数量={}", electronicId, result.size());
        return result;
    }
 
    private AreaInput convertToAreaInput(XpElectronicFenceArea area) {
        log.info("[ADC推送] convertToAreaInput: areaId={}, areaType={}, areaStatus={}, fencePoints={}",
                area.getId(), area.getAreaType(), area.getAreaStatus(),
                area.getFencePoints() == null ? "null" :
                        (area.getFencePoints().length() > 200 ? area.getFencePoints().substring(0, 200) + "..." : area.getFencePoints()));
 
        if (!StringUtils.hasText(area.getFencePoints())) {
            log.info("[ADC推送] 区域{}的fencePoints为空,跳过", area.getId());
            return null;
        }
 
        List<XngpAreaRTreeFbsUtil.LonLat> points = XngpAreaRTreeFbsUtil.parseGcl(area.getFencePoints());
        log.info("[ADC推送] 区域{}解析坐标点数量={}", area.getId(), points == null ? 0 : points.size());
 
        if (CollectionUtils.isEmpty(points)) {
            log.info("[ADC推送] 区域{}解析后坐标点为空,fencePoints原始值={}", area.getId(), area.getFencePoints());
            return null;
        }
 
        return AreaInput.builder()
                .areaId(area.getId().intValue())
                .isBlacklist(area.getAreaType() == 2)
                .points(points)
                .build();
    }
 
    private List<XpVehicle> queryBoundVehicles(Long electronicId) {
        try {
            R<List<XpVehicle>> result = xpVehicleClient.listAllByFenceId(electronicId);
            if (result != null && result.getData() != null) {
                return result.getData();
            }
        } catch (Exception e) {
            log.info("[ADC推送] 查询围栏{}绑定车辆失败", electronicId, e);
        }
        return Collections.emptyList();
    }
 
    private boolean isOssAvailable() {
        if (adcOssHandleService == null) {
            log.info("[ADC推送] AdcOssHandleService未注册(OSS配置缺失),跳过推送");
            return false;
        }
        return true;
    }
}