package com.ruoyi.device.service.impl; import com.ruoyi.device.domain.api.IAircraftDomain; import com.ruoyi.device.domain.api.IDeviceDomain; import com.ruoyi.device.domain.api.IDockAircraftDomain; import com.ruoyi.device.domain.api.IDockDomain; import com.ruoyi.device.domain.api.IThingsBoardDomain; import com.ruoyi.device.domain.model.Aircraft; import com.ruoyi.device.domain.model.Device; import com.ruoyi.device.domain.model.Dock; import com.ruoyi.device.domain.model.DockAircraft; import com.ruoyi.device.domain.model.thingsboard.AttributeMap; import com.ruoyi.device.domain.model.thingsboard.DeviceInfo; import com.ruoyi.device.domain.model.thingsboard.constants.DeviceAttributes; import com.ruoyi.device.service.enums.DeviceType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import java.util.List; import java.util.Objects; import java.util.Optional; @Service public class SynService { private static final Logger log = LoggerFactory.getLogger(SynService.class); private final IThingsBoardDomain iThingsBoardDomain; @Autowired private IDeviceDomain deviceDomain; @Autowired private IDockDomain dockDomain; @Autowired private IAircraftDomain aircraftDomain; @Autowired private IDockAircraftDomain dockAircraftDomain; public SynService(IThingsBoardDomain iThingsBoardDomain) { this.iThingsBoardDomain = iThingsBoardDomain; } /** * 定时任务:同步基础表数据 * 执行时间:启动后1分钟开始,每2分钟执行一次(可通过配置文件修改) * 配置项:device.schedule.print-devices.initial-delay 初始延迟时间(毫秒) * device.schedule.print-devices.fixed-delay 执行间隔时间(毫秒) */ @Scheduled(initialDelayString = "${device.schedule.update-devices.initial-delay:60000}", fixedDelayString = "${device.schedule.update-devices.fixed-delay:120000}") public void updateDevicesScheduled() { try { log.info("========== 开始执行定时任务:同步基础表数据 =========="); Iterable> allDevices = iThingsBoardDomain.getAllDevices(); int totalCount = 0; for (List deviceBatch : allDevices) { for (DeviceInfo deviceInfo : deviceBatch) { try { // 获取设备属性 AttributeMap attributes = iThingsBoardDomain.getDeviceAttributes(deviceInfo.getId()); // 判断设备类型 DeviceType deviceType = determineDeviceType(deviceInfo, attributes); // 同步设备表 Long deviceId = syncDevice(deviceInfo, attributes, deviceType); // 根据设备类型进行不同的处理 if (deviceType == DeviceType.DOCK) { // 机场:同步机场表 syncDock(deviceId, deviceInfo.getName()); // 获取机场挂载的无人机SN号 Optional subDeviceSnOpt = attributes.get(DeviceAttributes.SUB_DEVICE_SN); if (subDeviceSnOpt.isPresent() && StringUtils.hasText(subDeviceSnOpt.get())) { String aircraftSn = subDeviceSnOpt.get(); // 通过SN号查找无人机设备 Device aircraftDevice = findDeviceBySn(aircraftSn); if (aircraftDevice != null) { // 同步机场无人机关联 syncDockAircraft(deviceId, aircraftDevice.getDeviceId()); } } } else if (deviceType == DeviceType.AIRCRAFT) { // 无人机:同步无人机表 syncAircraft(deviceId, deviceInfo.getName()); } // 网关类型不需要额外处理 totalCount++; } catch (Exception e) { log.error("同步设备失败: deviceId={}, deviceName={}, error={}", deviceInfo.getId(), deviceInfo.getName(), e.getMessage(), e); } } } log.info("========== 数据同步任务完成,共同步 {} 个设备 ==========", totalCount); } catch (Exception e) { log.error("数据同步任务执行失败: {}", e.getMessage(), e); } } /** * 判断设备类型 * 优化后的判断逻辑: * 1. 优先使用 ThingsBoard 标准的 additionalInfo.gateway 字段判断网关 * 2. 对于非网关设备,通过 dock_sn 属性区分机场和无人机 * * @param deviceInfo ThingsBoard设备信息 * @param attributes 设备属性 * @return 设备类型 */ private DeviceType determineDeviceType(DeviceInfo deviceInfo, AttributeMap attributes) { String deviceName = deviceInfo.getName(); // 1. 使用 ThingsBoard 标准方式判断网关:检查 additionalInfo 中的 gateway 字段 if (deviceInfo.isGateway()) { return DeviceType.GATEWAY; } // 2. 非网关设备:通过 dock_sn 属性区分机场和无人机 Optional dockSnOpt = attributes.get(DeviceAttributes.DOCK_SN); if (dockSnOpt.isPresent() && StringUtils.hasText(dockSnOpt.get())) { String dockSn = dockSnOpt.get(); // dock_sn 等于设备名称 -> 机场 // dock_sn 不等于设备名称 -> 无人机(挂载在该机场下) return deviceName.equals(dockSn) ? DeviceType.DOCK : DeviceType.AIRCRAFT; } // dock_sn 属性不存在或为空,无法判断设备类型 throw new IllegalStateException("无法确定设备类型:设备 " + deviceName + " 缺少 dock_sn 属性"); } /** * 同步设备数据 * * @param deviceInfo ThingsBoard设备信息 * @param attributes 设备属性 * @param deviceType 设备类型 * @return 设备主键ID */ private Long syncDevice(DeviceInfo deviceInfo, AttributeMap attributes, DeviceType deviceType) { String iotDeviceId = deviceInfo.getId(); String deviceName = deviceInfo.getName(); // 使用 ThingsBoard 标准方式获取网关设备ID(通过 EntityRelation) String gateway = iThingsBoardDomain.getDeviceGatewayId(iotDeviceId); String deviceSn = deviceName; // 使用设备名称作为SN号,网关其实是没有SN号的 // 查询设备是否已存在 Device existingDevice = deviceDomain.selectDeviceByIotDeviceId(iotDeviceId); if (existingDevice == null) { // 设备不存在,插入新设备 Device newDevice = new Device(); newDevice.setDeviceName(deviceName); newDevice.setIotDeviceId(iotDeviceId); newDevice.setDeviceType(deviceType.getCode()); newDevice.setDeviceSn(deviceSn); newDevice.setGateway(gateway); newDevice.setCreateBy("system"); deviceDomain.insertDevice(newDevice); log.info("插入新设备: iotDeviceId={}, deviceName={}, deviceType={}", iotDeviceId, deviceName, deviceType); return newDevice.getDeviceId(); } else { // 设备已存在,检查是否需要更新 boolean needUpdate = false; if (!Objects.equals(existingDevice.getDeviceName(), deviceName)) { existingDevice.setDeviceName(deviceName); needUpdate = true; } if (!Objects.equals(existingDevice.getDeviceType(), deviceType.getCode())) { existingDevice.setDeviceType(deviceType.getCode()); needUpdate = true; } if (!Objects.equals(existingDevice.getDeviceSn(), deviceSn)) { existingDevice.setDeviceSn(deviceSn); needUpdate = true; } if (!Objects.equals(existingDevice.getGateway(), gateway)) { existingDevice.setGateway(gateway); needUpdate = true; } if (needUpdate) { existingDevice.setUpdateBy("system"); deviceDomain.updateDevice(existingDevice); log.info("更新设备: iotDeviceId={}, deviceName={}, deviceType={}", iotDeviceId, deviceName, deviceType); } return existingDevice.getDeviceId(); } } /** * 同步机场数据 * * @param deviceId 设备主键ID * @param deviceName 设备名称 */ private void syncDock(Long deviceId, String deviceName) { // 查询机场是否已存在 Dock existingDock = dockDomain.selectDockByDeviceId(deviceId); if (existingDock == null) { // 机场不存在,插入新机场 Dock newDock = new Dock(); newDock.setDockName(deviceName); newDock.setDeviceId(deviceId); newDock.setCreateBy("system"); dockDomain.insertDock(newDock); log.info("插入新机场: deviceId={}, dockName={}", deviceId, deviceName); } // 机场已存在,无需更新 } /** * 同步无人机数据 * * @param deviceId 设备主键ID * @param deviceName 设备名称 * @return 无人机主键ID */ private Long syncAircraft(Long deviceId, String deviceName) { // 查询无人机是否已存在 Aircraft existingAircraft = aircraftDomain.selectAircraftByDeviceId(deviceId); if (existingAircraft == null) { // 无人机不存在,插入新无人机 Aircraft newAircraft = new Aircraft(); newAircraft.setAircraftName(deviceName); newAircraft.setDeviceId(deviceId); newAircraft.setCreateBy("system"); aircraftDomain.insertAircraft(newAircraft); log.info("插入新无人机: deviceId={}, aircraftName={}", deviceId, deviceName); return newAircraft.getAircraftId(); } // 无人机已存在,无需更新 return existingAircraft.getAircraftId(); } /** * 根据设备SN号查找设备 * * @param deviceSn 设备SN号 * @return 设备信息 */ private Device findDeviceBySn(String deviceSn) { Device queryDevice = new Device(); queryDevice.setDeviceSn(deviceSn); List devices = deviceDomain.selectDeviceList(queryDevice); return (devices != null && !devices.isEmpty()) ? devices.get(0) : null; } /** * 同步机场无人机关联数据 * 按照一个机场只会挂载一台无人机的逻辑进行处理 * * @param dockDeviceId 机场设备主键ID * @param aircraftDeviceId 无人机设备主键ID */ private void syncDockAircraft(Long dockDeviceId, Long aircraftDeviceId) { // 获取机场主键 Dock dock = dockDomain.selectDockByDeviceId(dockDeviceId); if (dock == null) { log.warn("机场不存在,无法同步机场无人机关联: dockDeviceId={}", dockDeviceId); return; } // 获取无人机主键 Aircraft aircraft = aircraftDomain.selectAircraftByDeviceId(aircraftDeviceId); if (aircraft == null) { log.warn("无人机不存在,无法同步机场无人机关联: aircraftDeviceId={}", aircraftDeviceId); return; } Long dockId = dock.getDockId(); Long aircraftId = aircraft.getAircraftId(); // 查询该机场是否已有关联 List existingRelations = dockAircraftDomain.selectDockAircraftByDockId(dockId); if (existingRelations == null || existingRelations.isEmpty()) { // 机场没有关联,插入新关联 DockAircraft newRelation = new DockAircraft(); newRelation.setDockId(dockId); newRelation.setAircraftId(aircraftId); newRelation.setCreateBy("system"); dockAircraftDomain.insertDockAircraft(newRelation); log.info("插入机场无人机关联: dockId={}, aircraftId={}", dockId, aircraftId); } else { // 机场已有关联,检查是否需要更新 DockAircraft existingRelation = existingRelations.get(0); if (!Objects.equals(existingRelation.getAircraftId(), aircraftId)) { // 无人机发生变化,更新关联 existingRelation.setAircraftId(aircraftId); existingRelation.setUpdateBy("system"); dockAircraftDomain.updateDockAircraft(existingRelation); log.info("更新机场无人机关联: dockId={}, aircraftId={}", dockId, aircraftId); } } } }