/*
|
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
|
*
|
* Redistribution and use in source and binary forms, with or without
|
* modification, are permitted provided that the following conditions are met:
|
*
|
* Redistributions of source code must retain the above copyright notice,
|
* this list of conditions and the following disclaimer.
|
* Redistributions in binary form must reproduce the above copyright
|
* notice, this list of conditions and the following disclaimer in the
|
* documentation and/or other materials provided with the distribution.
|
* Neither the name of the dreamlu.net developer nor the names of its
|
* contributors may be used to endorse or promote products derived from
|
* this software without specific prior written permission.
|
* Author: Chill 庄骞 (smallchill@163.com)
|
*/
|
package org.sxkj.gd.workorder.service.impl;
|
|
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSONArray;
|
import com.alibaba.fastjson.JSONObject;
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
import lombok.extern.slf4j.Slf4j;
|
import org.sxkj.gd.utils.GdGeoAddressUtil;
|
import org.sxkj.gd.utils.GeomUtils;
|
import org.sxkj.gd.workorder.dto.GdXingtuAirportListDTO;
|
import org.sxkj.gd.workorder.dto.GdXingtuPayloadListDTO;
|
import org.sxkj.gd.workorder.dto.GdXingtuPilotListDTO;
|
import org.sxkj.gd.workorder.entity.GdManageDeviceEntity;
|
import org.sxkj.gd.workorder.entity.GdManageDevicePayloadEntity;
|
import org.sxkj.gd.workorder.param.GdManageDevicePageParam;
|
import org.sxkj.gd.workorder.service.IGdManageDevicePayloadService;
|
import org.sxkj.gd.workorder.vo.GdManageDeviceVO;
|
import org.sxkj.gd.workorder.excel.GdManageDeviceExcel;
|
import org.sxkj.gd.workorder.mapper.GdManageDeviceMapper;
|
import org.sxkj.gd.workorder.service.IGdManageDeviceService;
|
import org.sxkj.gd.xingtu.JianXingtuApiService;
|
import org.springframework.stereotype.Service;
|
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import lombok.AllArgsConstructor;
|
import org.springblade.core.mp.base.BaseServiceImpl;
|
import org.springblade.core.tool.api.R;
|
import org.springblade.core.tool.utils.StringUtil;
|
|
import java.text.SimpleDateFormat;
|
import java.util.*;
|
import java.util.stream.Collectors;
|
|
/**
|
* 设备信息 服务实现类
|
*
|
* @author lw
|
* @since 2026-01-14
|
*/
|
@Service
|
@AllArgsConstructor
|
@Slf4j
|
public class GdManageDeviceServiceImpl extends BaseServiceImpl<GdManageDeviceMapper, GdManageDeviceEntity> implements IGdManageDeviceService {
|
|
private final JianXingtuApiService jianXingtuApiService;
|
private final IGdManageDevicePayloadService iGdManageDevicePayloadService;
|
|
@Override
|
public IPage<GdManageDeviceVO> selectGdManageDevicePage(IPage<GdManageDeviceVO> page, GdManageDevicePageParam gdManageDevice) {
|
List<GdManageDeviceVO> gdManageDeviceVOS = baseMapper.selectGdManageDevicePage(page, gdManageDevice);
|
return page.setRecords(gdManageDeviceVOS);
|
}
|
|
@Override
|
public List<GdManageDeviceVO> selectGdManageDevice(GdManageDevicePageParam gdManageDevice) {
|
return baseMapper.selectGdManageDevice(gdManageDevice);
|
}
|
|
@Override
|
public List<GdManageDeviceExcel> exportGdManageDevice(Wrapper<GdManageDeviceEntity> queryWrapper) {
|
List<GdManageDeviceExcel> gdManageDeviceList = baseMapper.exportGdManageDevice(queryWrapper);
|
// gdManageDeviceList.forEach(gdManageDevice -> {
|
// gdManageDevice.setTypeName(DictCache.getValue(DictEnum.YES_NO, GdManageDevice.getType()));
|
//});
|
return gdManageDeviceList;
|
}
|
|
/**
|
* 保存或更新设备信息
|
*
|
* @param gdManageDeviceEntity
|
* @return
|
* @throws Exception
|
*/
|
@Override
|
public boolean saveOrUpdateDevice(GdManageDeviceEntity gdManageDeviceEntity) throws Exception {
|
// 获取设备位置 生成缓冲区
|
if (gdManageDeviceEntity.getLongitude() != null && gdManageDeviceEntity.getLatitude() != null) {
|
String bufferAroundPointAsString = GeomUtils.getBufferAroundPointAsString(gdManageDeviceEntity.getLongitude(), gdManageDeviceEntity.getLatitude(), 5 * 1000);
|
gdManageDeviceEntity.setGeom(bufferAroundPointAsString);
|
String location = GdGeoAddressUtil.getFormattedAddress(gdManageDeviceEntity.getLongitude(), gdManageDeviceEntity.getLatitude());
|
gdManageDeviceEntity.setLocation(location);
|
}
|
return saveOrUpdate(gdManageDeviceEntity);
|
}
|
|
/**
|
* 同步星图设备(无人机、机巢)
|
*
|
* @return 同步数量
|
*/
|
@Override
|
public int syncXingtuDevice() throws Exception {
|
int total = 0;
|
// 2.同步机巢设备
|
total += syncAirportDevices();
|
// 1.同步无人机设备
|
total += syncPilotDevices();
|
log.info("更新或新增设备" + total);
|
return total;
|
}
|
|
/**
|
* 同步星图无人机设备并入库
|
*
|
* @return 新增或更新数量
|
*/
|
private int syncPilotDevices() throws Exception {
|
R response = jianXingtuApiService.getDevicePilotList(null);
|
List<GdXingtuPilotListDTO> items = parseList(response, GdXingtuPilotListDTO.class);
|
if (items.isEmpty()) {
|
return 0;
|
}
|
int count = 0;
|
for (GdXingtuPilotListDTO item : items) {
|
GdManageDeviceEntity entity = buildFromPilot(item);
|
if (entity == null) {
|
continue;
|
}
|
if (saveOrUpdateByAirportId(entity)) {
|
// 同步负载设备
|
List<GdXingtuPayloadListDTO> loadList = item.getLoadList();
|
syncPayloadDevices(loadList);
|
count++;
|
}
|
}
|
return count;
|
}
|
|
/**
|
* 同步星图机巢设备并入库
|
*
|
* @return 新增或更新数量
|
*/
|
private int syncAirportDevices() throws Exception {
|
R response = jianXingtuApiService.getDeviceAirportList(null);
|
List<GdXingtuAirportListDTO> items = parseList(response, GdXingtuAirportListDTO.class);
|
if (items.isEmpty()) {
|
return 0;
|
}
|
int count = 0;
|
for (GdXingtuAirportListDTO item : items) {
|
GdManageDeviceEntity entity = buildFromAirport(item);
|
if (entity == null) {
|
continue;
|
}
|
if (saveOrUpdateByAirportId(entity)) {
|
count++;
|
}
|
}
|
return count;
|
}
|
|
/**
|
* 同步负载设备
|
*
|
* @param loadList 负载设备列表
|
*/
|
private void syncPayloadDevices(List<GdXingtuPayloadListDTO> loadList) throws Exception {
|
// 1. 收集所有负载设备的飞行员序列号,用于后续查询和删除操作
|
Set<String> allPilotSnCodes = new HashSet<>();
|
if (loadList != null && !loadList.isEmpty()) {
|
for (GdXingtuPayloadListDTO load : loadList) {
|
if (load.getPilotSnCode() != null) {
|
allPilotSnCodes.add(load.getPilotSnCode());
|
}
|
}
|
}
|
|
// 2. 获取当前数据库中这些飞行员序列号对应的所有负载设备
|
List<GdManageDevicePayloadEntity> existingPayloads = new ArrayList<>();
|
if (!allPilotSnCodes.isEmpty()) {
|
existingPayloads = iGdManageDevicePayloadService.list(
|
Wrappers.<GdManageDevicePayloadEntity>lambdaQuery().in(GdManageDevicePayloadEntity::getPilotSnCode, allPilotSnCodes)
|
);
|
}
|
|
// 3. 创建现有负载设备的序列号映射,用于快速查找
|
Map<String, GdManageDevicePayloadEntity> existingPayloadMap = new HashMap<>();
|
for (GdManageDevicePayloadEntity payload : existingPayloads) {
|
if (payload.getPilotSnCode() != null && payload.getSnCode() != null) {
|
existingPayloadMap.put(payload.getPilotSnCode() + "_" + payload.getSnCode() + "_" + payload.getName(), payload);
|
}
|
}
|
|
// 4. 处理负载设备列表
|
if (loadList != null && !loadList.isEmpty()) {
|
List<GdManageDevicePayloadEntity> toSaveOrUpdate = new ArrayList<>();
|
Set<String> currentPayloadKeys = new HashSet<>();
|
|
// 对负载设备列表进行去重处理,确保每个负载设备只被处理一次
|
Map<String, GdXingtuPayloadListDTO> uniqueLoadMap = new HashMap<>();
|
for (GdXingtuPayloadListDTO load : loadList) {
|
if (load.getSnCode() != null && load.getPilotSnCode() != null) {
|
String payloadKey = load.getPilotSnCode() + "_" + load.getSnCode() + "_" + load.getName();
|
uniqueLoadMap.put(payloadKey, load);
|
}
|
}
|
|
// 处理去重后的负载设备列表
|
for (GdXingtuPayloadListDTO load : uniqueLoadMap.values()) {
|
String snCode = load.getSnCode();
|
String pilotSnCode1 = load.getPilotSnCode();
|
String name = load.getName();
|
String payloadKey = pilotSnCode1 + "_" + snCode + "_" + name;
|
currentPayloadKeys.add(payloadKey);
|
|
// 检查是否已存在
|
GdManageDevicePayloadEntity existing = existingPayloadMap.remove(payloadKey);
|
if (existing != null) {
|
// 更新现有负载
|
existing.setName(load.getName());
|
existing.setType(load.getType());
|
toSaveOrUpdate.add(existing);
|
} else {
|
// 新增负载
|
GdManageDevicePayloadEntity newPayload = new GdManageDevicePayloadEntity();
|
newPayload.setName(load.getName());
|
newPayload.setType(load.getType());
|
newPayload.setSnCode(snCode);
|
newPayload.setPilotSnCode(pilotSnCode1);
|
// 可以根据需要设置区域编码等其他字段
|
toSaveOrUpdate.add(newPayload);
|
}
|
}
|
|
// 4. 批量保存或更新负载设备
|
if (!toSaveOrUpdate.isEmpty()) {
|
iGdManageDevicePayloadService.saveOrUpdateBatch(toSaveOrUpdate);
|
}
|
|
// 5. 删除数据库中存在但负载列表中不存在的负载设备
|
if (!existingPayloadMap.isEmpty()) {
|
List<Long> toDeleteIds = existingPayloadMap.values().stream()
|
.map(GdManageDevicePayloadEntity::getId)
|
.collect(Collectors.toList());
|
iGdManageDevicePayloadService.removeByIds(toDeleteIds);
|
}
|
} else {
|
// 如果负载列表为空,删除该飞行员序列号对应的所有负载设备
|
if (!existingPayloads.isEmpty()) {
|
List<Long> toDeleteIds = existingPayloads.stream()
|
.map(GdManageDevicePayloadEntity::getId)
|
.collect(Collectors.toList());
|
iGdManageDevicePayloadService.removeByIds(toDeleteIds);
|
}
|
}
|
}
|
|
/**
|
* 按机场ID保存或更新设备信息
|
*
|
* @param entity 设备信息
|
* @return 是否保存成功
|
*/
|
private boolean saveOrUpdateByAirportId(GdManageDeviceEntity entity) throws Exception {
|
if (StringUtil.isBlank(entity.getAirportId())) {
|
return false;
|
}
|
// 获取设备信息
|
GdManageDeviceEntity exist = lambdaQuery()
|
.eq(GdManageDeviceEntity::getAirportId, entity.getAirportId())
|
.one();
|
// 获取父级设备信息
|
GdManageDeviceEntity parent = getOne(Wrappers.<GdManageDeviceEntity>lambdaQuery().eq(GdManageDeviceEntity::getChildSn, entity.getDeviceSn()));
|
if (exist != null) {
|
entity.setId(exist.getId());
|
if (entity.getLongitude() == null) {
|
entity.setLongitude(exist.getLongitude());
|
}
|
if (entity.getLatitude() == null) {
|
entity.setLatitude(exist.getLatitude());
|
}
|
if (entity.getInsureExpiredTime() == null) {
|
entity.setInsureExpiredTime(exist.getInsureExpiredTime());
|
}
|
// 获取父级设备位置信息
|
if (parent != null) {
|
entity.setLongitude(parent.getLongitude());
|
entity.setLatitude(parent.getLatitude());
|
entity.setHeight(parent.getHeight());
|
entity.setModeCode(Long.valueOf(parent.getStatus()));
|
}
|
}
|
return saveOrUpdateDevice(entity);
|
}
|
|
/**
|
* 从星图无人机数据构建设备实体
|
*
|
* @param item 星图无人机数据
|
* @return 设备实体
|
*/
|
private GdManageDeviceEntity buildFromPilot(GdXingtuPilotListDTO item) {
|
if (item == null) {
|
return null;
|
}
|
GdManageDeviceEntity entity = new GdManageDeviceEntity();
|
entity.setAirportId(item.getId());
|
entity.setDeviceSn(item.getSnCode());
|
entity.setDeviceName(item.getName());
|
entity.setNickname(item.getName());
|
entity.setDeviceType(1);
|
entity.setLongitude(parseDouble(item.getLongitude()));
|
entity.setLatitude(parseDouble(item.getLatitude()));
|
entity.setFirmwareVersion(item.getFirmwareVersion());
|
entity.setModeCode(parseModeCode(item.getStatus()));
|
entity.setInsureExpiredTime(parseInsuranceDate(item.getInsuranceInfo()));
|
entity.setAreaCode(extractAreaCode(item.getRegionCode()));
|
entity.setIsWithDock(item.getIsWithDock());
|
entity.setCreateDept(item.getDeptId());
|
entity.setDevicePayload(
|
Optional.ofNullable(item.getLoadList())
|
.orElse(Collections.emptyList())
|
.stream()
|
.filter(Objects::nonNull)
|
.map(load -> load.getName())
|
.filter(Objects::nonNull)
|
.distinct() // 添加去重逻辑
|
.collect(Collectors.joining(","))
|
);
|
|
return entity;
|
}
|
|
/**
|
* 从星图机巢数据构建设备实体
|
*
|
* @param item 星图机巢数据
|
* @return 设备实体
|
*/
|
private GdManageDeviceEntity buildFromAirport(GdXingtuAirportListDTO item) {
|
if (item == null) {
|
return null;
|
}
|
GdManageDeviceEntity entity = new GdManageDeviceEntity();
|
entity.setAirportId(item.getId());
|
entity.setDeviceSn(item.getSnCode());
|
entity.setDeviceName(item.getName());
|
entity.setNickname(item.getName());
|
entity.setDeviceType(0);
|
entity.setLongitude(parseDouble(item.getLongitude()));
|
entity.setLatitude(parseDouble(item.getLatitude()));
|
entity.setFirmwareVersion(item.getFirmwareVersion());
|
entity.setChildSn(item.getPilotSnCode());
|
entity.setModeCode(parseModeCode(item.getStatus()));
|
entity.setInsureExpiredTime(parseInsuranceDate(item.getInsuranceInfo()));
|
entity.setAreaCode(extractAreaCode(item.getRegionCode()));
|
entity.setHeight(item.getHeight());
|
entity.setCreateDept(item.getDeptId());
|
return entity;
|
}
|
|
/**
|
* 将设备在线状态转换为模式码
|
*
|
* @param status 状态文本
|
* @return 模式码
|
*/
|
private Long parseModeCode(String status) {
|
if ("在线".equals(status)) {
|
return 0L;
|
}
|
if ("离线".equals(status)) {
|
return 1L;
|
}
|
return null;
|
}
|
|
/**
|
* 安全解析字符串为 Double
|
*
|
* @param value 字符串值
|
* @return Double 或 null
|
*/
|
private Double parseDouble(String value) {
|
if (StringUtil.isBlank(value)) {
|
return null;
|
}
|
try {
|
return Double.valueOf(value);
|
} catch (Exception ignored) {
|
return null;
|
}
|
}
|
|
/**
|
* 提取区域编码中的末级编码
|
*
|
* @param regionCode 区域编码
|
* @return 末级区域编码
|
*/
|
private String extractAreaCode(String regionCode) {
|
if (StringUtil.isBlank(regionCode)) {
|
return null;
|
}
|
String[] parts = regionCode.split(",");
|
if (parts.length == 1) {
|
parts = regionCode.split("-");
|
}
|
for (int i = parts.length - 1; i >= 0; i--) {
|
if (StringUtil.isNotBlank(parts[i])) {
|
return parts[i];
|
}
|
}
|
return regionCode;
|
}
|
|
/**
|
* 解析保险信息并获取最新保险日期
|
*
|
* @param insuranceInfo 保险信息JSON
|
* @return 保险日期
|
*/
|
private Date parseInsuranceDate(String insuranceInfo) {
|
if (StringUtil.isBlank(insuranceInfo)) {
|
return null;
|
}
|
try {
|
JSONArray array = JSON.parseArray(insuranceInfo);
|
if (array == null || array.isEmpty()) {
|
return null;
|
}
|
JSONObject last = array.getJSONObject(array.size() - 1);
|
if (last == null) {
|
return null;
|
}
|
Object dateValue = last.get("isInsuranceDate");
|
String dateStr = null;
|
if (dateValue instanceof JSONArray) {
|
JSONArray dateArray = (JSONArray) dateValue;
|
if (!dateArray.isEmpty()) {
|
dateStr = dateArray.getString(dateArray.size() - 1);
|
}
|
} else if (dateValue != null) {
|
dateStr = String.valueOf(dateValue);
|
}
|
if (StringUtil.isBlank(dateStr)) {
|
return null;
|
}
|
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
|
return format.parse(dateStr);
|
} catch (Exception ignored) {
|
return null;
|
}
|
}
|
|
/**
|
* 解析星图接口返回的列表数据
|
*
|
* @param response 接口响应
|
* @param clazz 目标类型
|
* @return 列表数据
|
*/
|
private <T> List<T> parseList(R response, Class<T> clazz) {
|
if (response == null || !response.isSuccess() || response.getData() == null) {
|
return Collections.emptyList();
|
}
|
Object data = response.getData();
|
if (data instanceof String && StringUtil.isBlank((String) data)) {
|
return Collections.emptyList();
|
}
|
return JSON.parseArray(JSON.toJSONString(data), clazz);
|
}
|
}
|