drone-common/src/main/java/org/sxkj/common/jaxt/JianXingTuApiClient.java
@@ -24,9 +24,13 @@ */ private static final String GET_DEVICE_LIST_URL_POST = "/webservice/webservice/devicePilot/queryList"; /** * 获设备列表-飞机 * 获设备列表-机巢 */ private static final String GET_PLANE_LIST_URL_POST = "/webservice/webservice/deviceAirport/queryList"; /** * 新增飞行任务 */ private static final String FLY_TASK_SAVE_URL_POST = "/webservice/webservice/flyTask/save"; /** * 获取登录接口路径 @@ -35,4 +39,28 @@ public static String getLoginUrl() { return BASE_URL + LOGIN_URL_POST; } /** * 获取设备列表-无人机接口路径 * @return 设备列表-无人机接口路径 */ public static String getDevicePilotListUrl() { return BASE_URL + GET_DEVICE_LIST_URL_POST; } /** * 获取设备列表-机巢接口路径 * @return 设备列表-机巢接口路径 */ public static String getDeviceAirportListUrl() { return BASE_URL + GET_PLANE_LIST_URL_POST; } /** * 获取新增飞行任务接口路径 * @return 新增飞行任务接口路径 */ public static String getFlyTaskSaveUrl() { return BASE_URL + FLY_TASK_SAVE_URL_POST; } } drone-service/drone-gd/src/main/java/org/sxkj/gd/GdApplication.java
@@ -22,6 +22,8 @@ import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.Bean; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.web.client.RestTemplate; /** @@ -31,6 +33,8 @@ @BladeCloudApplication @EnableFeignClients({"org.sxkj","org.springblade","com.dji.sample"}) @MapperScan({"org.springblade.**.mapper.**","org.sxkj.**.mapper.**"}) @EnableScheduling @EnableAsync public class GdApplication { public static void main(String[] args) { drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/controller/GdManageDeviceController.java
@@ -134,6 +134,17 @@ return R.status(gdManageDeviceService.deleteLogic(Func.toLongList(ids))); } /** * 同步星图设备(测试接口) */ @PostMapping("/sync-xingtu") @ApiOperationSupport(order = 8) @ApiOperation(value = "同步星图设备(测试)", notes = "同步无人机和机巢到设备表") public R syncXingtuDevice() throws Exception { int count = gdManageDeviceService.syncXingtuDevice(); return R.data(count); } /** * 导出数据 drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/dto/GdXingtuAirportListDTO.java
New file @@ -0,0 +1,148 @@ /* * 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.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.io.Serializable; /** * 星图接口-设备列表(机巢)数据对象 */ @Data public class GdXingtuAirportListDTO implements Serializable { private static final long serialVersionUID = 1L; @ApiModelProperty("主键id") private String id; @ApiModelProperty("名称") private String name; @ApiModelProperty("型号") private String type; @ApiModelProperty("区域编码") private String regionCode; @ApiModelProperty("部门id") private String deptId; @ApiModelProperty("部门名称") private String deptName; @ApiModelProperty("设备序列号") private String snCode; @ApiModelProperty("铭牌序列号") private String nameplateSnCode; @ApiModelProperty("经度") private String longitude; @ApiModelProperty("纬度") private String latitude; @ApiModelProperty("高度") private Double height; @ApiModelProperty("作业半径") private Double workRadius; @ApiModelProperty("安全返航高度") private Double safeReturnHeight; @ApiModelProperty("安全作业高度") private Double safeWorkHeight; @ApiModelProperty("是否允许飞行") private Integer isAllowFlight; @ApiModelProperty("负责人姓名") private String managerName; @ApiModelProperty("负责人电话") private String managerMobile; @ApiModelProperty("地址") private String address; @ApiModelProperty("附件id") private String fileId; @ApiModelProperty("附件地址") private String fileUrl; @ApiModelProperty("备注") private String remark; @ApiModelProperty("是否包含无人机") private Integer isHasPilot; @ApiModelProperty("是否四合一模块") private Integer isFourModule; @ApiModelProperty("负载序列号") private String loadSnCode; @ApiModelProperty("负载类型") private String loadType; @ApiModelProperty("无人机序列号") private String pilotSnCode; @ApiModelProperty("无人机类型") private String pilotType; @ApiModelProperty("是否保险") private Integer isInsurance; @ApiModelProperty("保险信息") private String insuranceInfo; @ApiModelProperty("状态") private String status; @ApiModelProperty("禁飞原因") private String noFlyReason; @ApiModelProperty("机巢卡号") private String airportSimCardNumber; @ApiModelProperty("机巢流量包") private String airportDataPackage; @ApiModelProperty("流量包开始时间") private String dataPackageValidityBegin; @ApiModelProperty("流量包结束时间") private String dataPackageValidityEnd; @ApiModelProperty("固件版本") private String firmwareVersion; @ApiModelProperty("工作空间id") private String workspaceId; @ApiModelProperty("当前控制用户id") private String currentControlUserId; @ApiModelProperty("负载序号") private String loadIndex; } drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/dto/GdXingtuFlyTaskSaveDTO.java
New file @@ -0,0 +1,125 @@ /* * 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.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.io.Serializable; import java.util.List; /** * 星图接口-新增飞行任务请求对象 */ @Data public class GdXingtuFlyTaskSaveDTO implements Serializable { private static final long serialVersionUID = 1L; @ApiModelProperty("任务名称") private String name; @ApiModelProperty("机场id") private String airportId; @ApiModelProperty("机场名称") private String airportName; @ApiModelProperty("巡检类型,字典") private String inspectionType; @ApiModelProperty("巡检类型,字典-数组格式") private List<String> inspectionTypeArr; @ApiModelProperty("任务类型:定时飞行、航线飞行") private String taskType; @ApiModelProperty("定时类型:单次执行、周期执行、自定义执行") private String timingType; @ApiModelProperty("单次任务执行时间") private String singleTime; @ApiModelProperty("周期(自定义)执行开始日期") private String startDate; @ApiModelProperty("周期(自定义)执行结束日期") private String endDate; @ApiModelProperty("周期(自定义)执行时间HH:mm:ss格式") private String timeOfDay; @ApiModelProperty("间隔周期数") private Integer interval; @ApiModelProperty("间隔单位:WEEK/MONTH") private String intervalUnit; @ApiModelProperty("周几(1-7)或几号(1-31)") private Integer dayOfPeriod; @ApiModelProperty("航线id") private String airlineId; @ApiModelProperty("航线名称") private String airlineName; @ApiModelProperty("部门id") private Long deptId; @ApiModelProperty("部门名称") private String deptName; @ApiModelProperty("地区编码,拼接") private String regionCode; @ApiModelProperty("安全返航高度") private Integer safeReturnHeight; @ApiModelProperty("是否云端录制 0否 1是") private Integer isCloudRecord; @ApiModelProperty("是否断点续飞 0否 1是") private Integer isContinueFly; @ApiModelProperty("是否开启AI识别 0否 1是") private Integer isAiIdentify; @ApiModelProperty("识别时机:实时、后置") private String identifyTiming; @ApiModelProperty("算法id,多个逗号分隔") private String algorithmId; @ApiModelProperty("识别间隔(秒/次)") private Integer identifyIntervals; @ApiModelProperty("是否自动生成工单 0否 1是") private Integer isAutoGenWorkOrder; @ApiModelProperty("异常动作配置-相机动作:镜头拍照、广角拍照、红外拍照、全景拍照") private String abnormalCameraAction; @ApiModelProperty("异常动作配置-变焦倍数") private Integer abnormalZoomPhotography; @ApiModelProperty("异常动作配置-悬停时长") private Integer abnormalHoverDuration; @ApiModelProperty("任务状态:草稿、待审核、待执行、执行中、已完成、已取消、审核不通过") private String status; } drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/dto/GdXingtuPilotListDTO.java
New file @@ -0,0 +1,154 @@ /* * 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.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.io.Serializable; /** * 星图接口-设备列表(无人机)数据对象 */ @Data public class GdXingtuPilotListDTO implements Serializable { private static final long serialVersionUID = 1L; @ApiModelProperty("主键id") private String id; @ApiModelProperty("删除标记") private String delFlag; @ApiModelProperty("名称") private String name; @ApiModelProperty("型号") private String type; @ApiModelProperty("区域编码") private String regionCode; @ApiModelProperty("部门id") private String deptId; @ApiModelProperty("部门名称") private String deptName; @ApiModelProperty("设备序列号") private String snCode; @ApiModelProperty("经度") private String longitude; @ApiModelProperty("纬度") private String latitude; @ApiModelProperty("固件版本") private String firmwareVersion; @ApiModelProperty("购买时间") private String buyTime; @ApiModelProperty("使用人id") private String userId; @ApiModelProperty("使用人名称") private String userName; @ApiModelProperty("保管人") private String keeper; @ApiModelProperty("保管人电话") private String keeperMobile; @ApiModelProperty("负载序列号") private String loadSnCode; @ApiModelProperty("负载类型") private String loadType; @ApiModelProperty("是否喷涂") private Integer isPainting; @ApiModelProperty("是否四合一模块") private Integer isFourModule; @ApiModelProperty("资质文件id") private String certificateFileId; @ApiModelProperty("资质文件地址") private String certificateFileUrl; @ApiModelProperty("附件id") private String fileId; @ApiModelProperty("附件地址") private String fileUrl; @ApiModelProperty("备注") private String remark; @ApiModelProperty("是否保险") private Integer isInsurance; @ApiModelProperty("保险信息") private String insuranceInfo; @ApiModelProperty("直播类型") private String liveType; @ApiModelProperty("直播地址") private String liveUrl; @ApiModelProperty("AI直播地址") private String liveUrlAi; @ApiModelProperty("机巢卡号") private String airportSimCardNumber; @ApiModelProperty("机巢流量包") private String airportDataPackage; @ApiModelProperty("流量包开始时间") private String dataPackageValidityBegin; @ApiModelProperty("流量包结束时间") private String dataPackageValidityEnd; @ApiModelProperty("工作空间id") private String workspaceId; @ApiModelProperty("状态") private String status; @ApiModelProperty("是否带机巢") private Integer isWithDock; @ApiModelProperty("父级序列号") private String parentSn; @ApiModelProperty("负载序号") private String loadIndex; @ApiModelProperty("可控范围") private Integer controlledScope; @ApiModelProperty("清晰度") private Integer liveClarity; } drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/entity/GdManageDeviceEntity.java
@@ -47,6 +47,17 @@ @ApiModelProperty(value = "设备编码") private String deviceSn; /** * 星图业务Id */ @ApiModelProperty(value = "设备Id(星图业务)") private String airportId; /** * 行政区域编号 */ @ApiModelProperty(value = "行政区域编号") @TableField("area_code") private String areaCode; /** * 设备型号 */ @ApiModelProperty(value = "设备型号") drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/service/IGdManageDeviceService.java
@@ -63,4 +63,10 @@ * @return */ boolean saveOrUpdateDevice(GdManageDeviceEntity gdManageDeviceEntity) throws Exception; /** * 同步星图设备(无人机、机巢) * @return 同步数量 */ int syncXingtuDevice() throws Exception; } drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/service/impl/GdManageDeviceServiceImpl.java
@@ -16,21 +16,33 @@ */ package org.sxkj.gd.workorder.service.impl; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Scheduled; 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.GdXingtuPilotListDTO; import org.sxkj.gd.workorder.entity.GdManageDeviceEntity; import org.sxkj.gd.workorder.param.GdManageDevicePageParam; 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.sxkj.system.cache.DictCache; import org.sxkj.system.enums.DictEnum; import org.springblade.core.tool.api.R; import org.springblade.core.tool.utils.StringUtil; import java.text.SimpleDateFormat; import java.util.Collections; import java.util.Date; import java.util.List; /** @@ -40,7 +52,10 @@ * @since 2026-01-14 */ @Service @AllArgsConstructor public class GdManageDeviceServiceImpl extends BaseServiceImpl<GdManageDeviceMapper, GdManageDeviceEntity> implements IGdManageDeviceService { private final JianXingtuApiService jianXingtuApiService; @Override public IPage<GdManageDeviceVO> selectGdManageDevicePage(IPage<GdManageDeviceVO> page, GdManageDevicePageParam gdManageDevice) { @@ -80,4 +95,199 @@ } return saveOrUpdate(gdManageDeviceEntity); } /** * 同步星图设备(无人机、机巢) * * @return 同步数量 */ @Override @Async @Scheduled(cron = "0 0 0 * * ?") public int syncXingtuDevice() throws Exception { int total = 0; total += syncPilotDevices(); total += syncAirportDevices(); return total; } 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)) { count++; } } return count; } 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; } private boolean saveOrUpdateByAirportId(GdManageDeviceEntity entity) throws Exception { if (StringUtil.isBlank(entity.getAirportId())) { return false; } GdManageDeviceEntity exist = lambdaQuery() .eq(GdManageDeviceEntity::getAirportId, entity.getAirportId()) .one(); 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()); } } return saveOrUpdateDevice(entity); } 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())); return entity; } 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())); return entity; } private Long parseModeCode(String status) { if ("在线".equals(status)) { return 0L; } if ("离线".equals(status)) { return 1L; } return null; } private Double parseDouble(String value) { if (StringUtil.isBlank(value)) { return null; } try { return Double.valueOf(value); } catch (Exception ignored) { return null; } } 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; } 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; } } 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); } } drone-service/drone-gd/src/main/java/org/sxkj/gd/xingtu/JianXingtuApiService.java
@@ -3,10 +3,15 @@ import lombok.SneakyThrows; import org.springblade.core.redis.cache.BladeRedis; import org.springblade.core.tool.api.R; import org.springblade.core.tool.utils.StringUtil; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import org.sxkj.common.jaxt.JianXingTuApiClient; import org.sxkj.gd.workorder.dto.GdXingtuFlyTaskSaveDTO; import java.util.HashMap; import java.util.Map; @@ -74,6 +79,64 @@ } /** * 设备列表-无人机 * @param name 名称 * @return 接口响应 */ public R getDevicePilotList(String name) { String url = JianXingTuApiClient.getDevicePilotListUrl(); return postWithAuthHeader(url, buildListParams(name)); } /** * 设备列表-机巢 * @param name 名称 * @return 接口响应 */ public R getDeviceAirportList(String name) { String url = JianXingTuApiClient.getDeviceAirportListUrl(); return postWithAuthHeader(url, buildListParams(name)); } /** * 新增飞行任务 * @param param 请求参数 * @return 接口响应 */ public R saveFlyTask(GdXingtuFlyTaskSaveDTO param) { String url = JianXingTuApiClient.getFlyTaskSaveUrl(); return postWithAuthHeader(url, param); } private Map<String, Object> buildListParams(String name) { Map<String, Object> params = new HashMap<>(); if (StringUtil.isNotBlank(name)) { params.put("name", name); } return params; } private R postWithAuthHeader(String url, Object body) { String token = getToken(); if (StringUtil.isBlank(token) || "获取token失败".equals(token)) { return R.fail("获取token失败"); } HttpHeaders headers = new HttpHeaders(); headers.set("authorization", token); HttpEntity<Object> entity = new HttpEntity<>(body, headers); ResponseEntity<R> response = restTemplate.exchange(url, HttpMethod.POST, entity, R.class); if (response.getStatusCode().is2xxSuccessful()) { R responseBody = response.getBody(); if (responseBody != null) { return responseBody; } return R.fail("接口返回为空"); } return R.fail("请求外部接口失败"); } /** * 解析JSON字符串为Map * @param input JSON字符串 * @return 解析后的Map