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.common.core.utils.SensitiveDataCryptoUtil;
|
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) {
|
String vin = SensitiveDataCryptoUtil.decryptIfPossible(vehicle.getVehicleVin());
|
pushToSingleVehicle(vin, 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())) {
|
String vin = SensitiveDataCryptoUtil.decryptIfPossible(vehicle.getVehicleVin());
|
adcOssHandleService.uploadEmptyFence(vin, version);
|
log.info("[ADC推送] 围栏{}失效,已向车辆{}上传空FBS", electronicId, vin);
|
}
|
} 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;
|
}
|
}
|