From 3ff3de60e9274da9601fba4d2165fe46be4d7f07 Mon Sep 17 00:00:00 2001
From: linwei <872216696@qq.com>
Date: Mon, 22 Jun 2026 16:31:37 +0800
Subject: [PATCH] add : 吉安支持查询待验收的接口

---
 drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/service/impl/GdTaskResultServiceImpl.java |  383 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 379 insertions(+), 4 deletions(-)

diff --git a/drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/service/impl/GdTaskResultServiceImpl.java b/drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/service/impl/GdTaskResultServiceImpl.java
index 6ef0e65..66db34a 100644
--- a/drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/service/impl/GdTaskResultServiceImpl.java
+++ b/drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/service/impl/GdTaskResultServiceImpl.java
@@ -16,16 +16,45 @@
  */
 package org.sxkj.gd.workorder.service.impl;
 
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.TypeReference;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.extern.slf4j.Slf4j;
+import org.sxkj.gd.workorder.dto.GdTaskResultDTO;
+import org.sxkj.gd.workorder.entity.GdDeviceCallDetailEntity;
+import org.sxkj.gd.workorder.entity.GdDeviceCallEntity;
+import org.sxkj.gd.workorder.entity.GdManageDeviceEntity;
+import org.sxkj.gd.workorder.entity.GdPatrolTaskEntity;
 import org.sxkj.gd.workorder.entity.GdTaskResultEntity;
+import org.sxkj.gd.workorder.service.IGdDeviceCallDetailService;
+import org.sxkj.gd.workorder.service.IGdDeviceCallService;
+import org.sxkj.gd.workorder.service.IGdManageDeviceService;
+import org.sxkj.gd.workorder.service.IGdPatrolTaskService;
+import org.sxkj.gd.workorder.service.IGdTaskResultService;
 import org.sxkj.gd.workorder.vo.GdTaskResultVO;
 import org.sxkj.gd.workorder.excel.GdTaskResultExcel;
 import org.sxkj.gd.workorder.mapper.GdTaskResultMapper;
-import org.sxkj.gd.workorder.service.IGdTaskResultService;
+import org.sxkj.common.utils.HeaderUtils;
+import org.sxkj.common.utils.SpringContextUtil;
 import org.springframework.stereotype.Service;
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import org.springblade.core.mp.base.BaseServiceImpl;
-import java.util.List;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.core.tool.utils.StringUtil;
+import org.sxkj.gd.workorder.wrapper.GdTaskResultWrapper;
+import org.sxkj.system.cache.SysCache;
+import org.sxkj.system.cache.UserCache;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
 
 /**
  * 成果表 服务实现类
@@ -33,17 +62,41 @@
  * @author lw
  * @since 2026-01-14
  */
+@Slf4j
 @Service
 public class GdTaskResultServiceImpl extends BaseServiceImpl<GdTaskResultMapper, GdTaskResultEntity> implements IGdTaskResultService {
+
+	/**
+	 * 日志数据解析后的内部类
+	 */
+	@lombok.Data
+	private static class DeviceCallLogItem {
+		private String content;
+		private String createTime;
+		private String createUserName;
+		private String delFlag;
+		private String flyDataId;
+		private String id;
+		private String pilotSn;
+	}
 
 	@Override
 	public IPage<GdTaskResultVO> selectGdTaskResultPage(IPage<GdTaskResultVO> page, GdTaskResultVO gdTaskResult) {
 		return page.setRecords(baseMapper.selectGdTaskResultPage(page, gdTaskResult));
 	}
 
+	/**
+	 * 根据巡查任务ID查询成果列表
+	 *
+	 * @param patrolTaskId    巡查任务ID
+	 * @param attachmentTypes 附件类型列表(可选,为null或空时查询全部)
+	 * @return 成果列表
+	 */
 	@Override
-	public List<GdTaskResultVO> listByPatrolTaskId(Long patrolTaskId) {
-		List<GdTaskResultVO> gdTaskResultVOS = baseMapper.selectGdTaskResultListByPatrolTaskId(patrolTaskId);
+	public List<GdTaskResultVO> listByPatrolTaskId(Long patrolTaskId, List<Integer> attachmentTypes) {
+		// 步骤1:查询成果列表
+		List<GdTaskResultVO> gdTaskResultVOS = baseMapper.selectGdTaskResultListByPatrolTaskId(patrolTaskId, attachmentTypes);
+		// 步骤2:处理URL转义字符
 		for (GdTaskResultVO gdTaskResultVO : gdTaskResultVOS) {
 			gdTaskResultVO.setResultUrl(unescapeUrl(gdTaskResultVO.getResultUrl()));
 		}
@@ -58,6 +111,248 @@
 		//	gdTaskResult.setTypeName(DictCache.getValue(DictEnum.YES_NO, GdTaskResult.getType()));
 		//});
 		return gdTaskResultList;
+	}
+
+	@Override
+	public boolean saveBatchTaskResult(List<GdTaskResultDTO> gdTaskResults) {
+		if (gdTaskResults == null || gdTaskResults.isEmpty()) {
+			log.error("saveBatchTaskResult-批量新增-对外接口数据为空");
+			return false;
+		}
+
+		// 按dateType分组处理
+		Map<Integer, List<GdTaskResultDTO>> groupedByDateType = gdTaskResults.stream()
+				.collect(Collectors.groupingBy(dto -> dto.getDateType() != null ? dto.getDateType() : 0));
+
+		// 处理 dateType != 4 的数据,保存到成果表
+		List<GdTaskResultDTO> normalResults = groupedByDateType.entrySet().stream()
+				.filter(entry -> entry.getKey() != 4)
+				.flatMap(entry -> entry.getValue().stream())
+				.collect(Collectors.toList());
+
+		if (!normalResults.isEmpty()) {
+			saveNormalTaskResults(normalResults);
+		}
+
+		// 处理 dateType == 4 的数据,保存到设备调用表和设备调用详情表
+		List<GdTaskResultDTO> logResults = groupedByDateType.get(4);
+		if (logResults != null && !logResults.isEmpty()) {
+			saveDeviceCallData(logResults);
+		}
+
+		return true;
+	}
+
+	/**
+	 * 保存普通成果数据(dateType != 4)
+	 *
+	 * @param gdTaskResults 成果DTO列表
+	 */
+	private void saveNormalTaskResults(List<GdTaskResultDTO> gdTaskResults) {
+		List<GdTaskResultEntity> gdTaskResultEntities = GdTaskResultWrapper.build().listEntity(gdTaskResults);
+		IGdPatrolTaskService patrolTaskService = SpringContextUtil.getBean(IGdPatrolTaskService.class);
+		// 查询巡查任务信息
+		GdPatrolTaskEntity patrolTask = patrolTaskService.getById(gdTaskResultEntities.get(0).getPatrolTaskId());
+		if (patrolTask == null) {
+			log.warn("巡查任务不存在:" + patrolTask.getId());
+			return;
+		}
+		gdTaskResultEntities.forEach(gdTaskResult -> {
+  			String processedAreaCode = HeaderUtils.processAreaCode(gdTaskResult.getAreaCode());
+			gdTaskResult.setAreaCode(processedAreaCode);
+			// 如果resultCode为空,生成时间戳作为默认值
+			if (StringUtil.isEmpty(gdTaskResult.getResultCode())) {
+				gdTaskResult.setResultCode(String.valueOf(System.currentTimeMillis()));
+			}
+			if (gdTaskResult.getShootTime() == null) {
+				gdTaskResult.setShootTime(new Date());
+			}
+			gdTaskResult.setStatus(0);
+			gdTaskResult.setUpdateTime(new Date());
+			gdTaskResult.setCreateTime(new Date());
+			gdTaskResult.setIsDeleted(0);
+			gdTaskResult.setCreateDept(patrolTask.getCreateDept());
+			gdTaskResult.setUpdateUser(patrolTask.getCreateUser());
+			gdTaskResult.setDistributeStatus(0);
+		});
+
+		int i = baseMapper.insertBatch(gdTaskResultEntities);
+	}
+
+	/**
+	 * 保存设备调用数据(dateType == 4)
+	 * geojson字段解析后保存到设备调用详情表,同时创建一条设备调用主表记录
+	 *
+	 * @param logResults 日志类型的成果DTO列表
+	 */
+	private void saveDeviceCallData(List<GdTaskResultDTO> logResults) {
+		// 使用SpringContextUtil动态获取服务,避免循环依赖
+		IGdPatrolTaskService patrolTaskService = SpringContextUtil.getBean(IGdPatrolTaskService.class);
+		IGdDeviceCallService deviceCallService = SpringContextUtil.getBean(IGdDeviceCallService.class);
+		IGdDeviceCallDetailService deviceCallDetailService = SpringContextUtil.getBean(IGdDeviceCallDetailService.class);
+		IGdManageDeviceService manageDeviceService = SpringContextUtil.getBean(IGdManageDeviceService.class);
+
+		for (GdTaskResultDTO logResult : logResults) {
+			Long patrolTaskId = logResult.getPatrolTaskId();
+			String geojson = logResult.getGeojson();
+
+			if (patrolTaskId == null || StringUtil.isEmpty(geojson)) {
+				log.warn("设备调用数据不完整,patrolTaskId: {}, geojson: {}", patrolTaskId, geojson);
+				continue;
+			}
+
+			// 查询巡查任务信息
+			GdPatrolTaskEntity patrolTask = patrolTaskService.getById(patrolTaskId);
+			if (patrolTask == null) {
+				log.warn("巡查任务不存在:" + patrolTaskId);
+				continue;
+			}
+
+			// 解析geojson数据
+			List<DeviceCallLogItem> logItems = parseGeojson(geojson);
+			if (logItems == null || logItems.isEmpty()) {
+				log.warn("geojson解析结果为空:" + geojson);
+				continue;
+			}
+
+			// 保存设备调用主表记录
+			GdDeviceCallEntity callEntity = buildDeviceCallEntity(patrolTask, logItems, manageDeviceService);
+			boolean b = deviceCallService.saveOrUpdate(callEntity);
+
+			// 保存设备调用详情记录
+			List<GdDeviceCallDetailEntity> detailEntities = buildDeviceCallDetailEntities(callEntity.getId(), logItems);
+			boolean b1 = deviceCallDetailService.saveBatch(detailEntities);
+		}
+	}
+
+	/**
+	 * 解析geojson字段,提取设备调用日志列表
+	 *
+	 * @param geojson geojson字符串
+	 * @return 日志项列表
+	 */
+	private List<DeviceCallLogItem> parseGeojson(String geojson) {
+		try {
+			return JSON.parseObject(geojson, new TypeReference<List<DeviceCallLogItem>>() {});
+		} catch (Exception e) {
+			log.error("解析geojson失败: " + geojson, e);
+			return null;
+		}
+	}
+
+	/**
+	 * 构建设备调用主表实体
+	 *
+	 * @param patrolTask 巡查任务实体
+	 * @param logItems 日志项列表
+	 * @param manageDeviceService 设备管理服务
+	 * @return 设备调用实体
+	 */
+	private GdDeviceCallEntity buildDeviceCallEntity(GdPatrolTaskEntity patrolTask, List<DeviceCallLogItem> logItems,
+													IGdManageDeviceService manageDeviceService) {
+		GdDeviceCallEntity callEntity = new GdDeviceCallEntity();
+		callEntity.setPatrolTaskName(patrolTask.getPatrolTaskName());
+		callEntity.setTaskDepartment(SysCache.getDeptName(patrolTask.getCreateDept()));
+		callEntity.setTaskDepartmentId(patrolTask.getCreateDept());
+		callEntity.setTaskInitiator(String.valueOf(patrolTask.getCreateUser()));
+		callEntity.setTaskInitiatorId(patrolTask.getCreateUser());
+		callEntity.setPlanExecuteTime(patrolTask.getExecuteTime());
+
+		// 从日志项中获取设备SN
+		String deviceSn = logItems.stream()
+				.map(DeviceCallLogItem::getPilotSn)
+				.filter(StringUtil::hasText)
+				.findFirst()
+				.orElse(null);
+
+		if (StringUtil.hasText(deviceSn)) {
+			GdManageDeviceEntity device = manageDeviceService.getOne(
+					Wrappers.lambdaQuery(GdManageDeviceEntity.class)
+							.eq(GdManageDeviceEntity::getDeviceSn, deviceSn),
+					false
+			);
+			if (device != null) {
+				callEntity.setDeviceId(device.getId());
+				callEntity.setDeviceName(device.getDeviceName());
+			}
+		}
+
+		// 计算飞行时长(使用日志中最小时间和最大时间做差值计算)
+		if (!logItems.isEmpty()) {
+			try {
+				List<Date> validDates = logItems.stream()
+						.map(item -> parseDateTime(item.getCreateTime()))
+						.filter(Objects::nonNull)
+						.sorted()
+						.collect(Collectors.toList());
+
+				if (!validDates.isEmpty()) {
+					Date startTime = validDates.get(0);
+					Date endTime = validDates.get(validDates.size() - 1);
+					long durationSeconds = (endTime.getTime() - startTime.getTime()) / 1000;
+					callEntity.setFlightDuration(durationSeconds);
+					callEntity.setActualExecuteTime(startTime);
+				}
+			} catch (Exception e) {
+				log.warn("计算飞行时长失败", e);
+				callEntity.setFlightDuration(0L);
+			}
+		}
+
+		return callEntity;
+	}
+
+	/**
+	 * 构建设备调用详情实体列表
+	 *
+	 * @param patrolTaskId 巡查任务ID
+	 * @param logItems 日志项列表
+	 * @return 设备调用详情实体列表
+	 */
+	private List<GdDeviceCallDetailEntity> buildDeviceCallDetailEntities(Long patrolTaskId, List<DeviceCallLogItem> logItems) {
+		List<GdDeviceCallDetailEntity> detailEntities = new ArrayList<>();
+
+		int sort = 1;
+		for (DeviceCallLogItem logItem : logItems) {
+			GdDeviceCallDetailEntity detailEntity = new GdDeviceCallDetailEntity();
+			detailEntity.setCallId(patrolTaskId);
+			detailEntity.setDeviceStatus(logItem.getContent());
+			detailEntity.setOccurTime(parseDateTime(logItem.getCreateTime()));
+			detailEntity.setSort(sort++);
+			detailEntities.add(detailEntity);
+		}
+
+		return detailEntities;
+	}
+
+	/**
+	 * 解析日期时间字符串
+	 *
+	 * @param dateTimeStr 日期时间字符串
+	 * @return Date对象
+	 */
+	private Date parseDateTime(String dateTimeStr) {
+		if (StringUtil.isEmpty(dateTimeStr)) {
+			return null;
+		}
+		try {
+			if (dateTimeStr.contains("T")) {
+				dateTimeStr = dateTimeStr.replace("T", " ");
+			}
+			if (dateTimeStr.contains(".")) {
+				dateTimeStr = dateTimeStr.substring(0, dateTimeStr.indexOf("."));
+			}
+			return new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(dateTimeStr);
+		} catch (Exception e) {
+			log.warn("日期解析失败: " + dateTimeStr, e);
+			return null;
+		}
+	}
+
+	@Override
+	public boolean updateTaskResultById(GdTaskResultEntity taskResult) {
+		int result = baseMapper.updateTaskResultById(taskResult);
+		return result > 0;
 	}
 
 	/**
@@ -86,4 +381,84 @@
 		return result;
 	}
 
+	@Override
+	public void downloadResultFiles(String ids, HttpServletResponse response) {
+		// 根据ID列表查询成果记录
+		List<Long> idList = Func.toLongList(ids);
+		List<GdTaskResultEntity> resultList = listByIds(idList);
+
+		if (resultList == null || resultList.isEmpty()) {
+			throw new RuntimeException("未找到成果数据");
+		}
+
+		// 设置响应头
+		response.setContentType("application/zip");
+		response.setHeader("Content-Disposition", "attachment; filename=result_files.zip");
+
+		try (ZipOutputStream zos = new ZipOutputStream(response.getOutputStream())) {
+			for (GdTaskResultEntity result : resultList) {
+				String resultUrl = result.getResultUrl();
+				if (resultUrl == null || resultUrl.isEmpty()) {
+					continue;
+				}
+
+				// 处理转义字符
+				resultUrl = unescapeUrl(resultUrl);
+
+				try {
+					// 从URL下载文件
+					URL url = new URL(resultUrl);
+					URLConnection connection = url.openConnection();
+					connection.setConnectTimeout(5000);
+					connection.setReadTimeout(10000);
+
+					// 从URL中提取文件名
+					String fileName = extractFileName(resultUrl, result.getId());
+
+					// 添加到ZIP
+					ZipEntry zipEntry = new ZipEntry(fileName);
+					zos.putNextEntry(zipEntry);
+
+					try (InputStream is = connection.getInputStream()) {
+						byte[] buffer = new byte[4096];
+						int len;
+						while ((len = is.read(buffer)) > 0) {
+							zos.write(buffer, 0, len);
+						}
+					}
+
+					zos.closeEntry();
+				} catch (Exception e) {
+					// 单个文件下载失败,继续处理其他文件
+					// 可以记录日志
+				}
+			}
+		} catch (IOException e) {
+			throw new RuntimeException("下载文件失败", e);
+		}
+	}
+
+	/**
+	 * 从URL中提取文件名
+	 */
+	private String extractFileName(String url, Long resultId) {
+		if (url == null || url.isEmpty()) {
+			return resultId + "_unknown";
+		}
+
+		// 从URL中提取最后一部分作为文件名
+		int lastSlash = url.lastIndexOf('/');
+		if (lastSlash != -1 && lastSlash < url.length() - 1) {
+			String fileName = url.substring(lastSlash + 1);
+			// 处理可能的查询参数
+			int questionMark = fileName.indexOf('?');
+			if (questionMark != -1) {
+				fileName = fileName.substring(0, questionMark);
+			}
+			return resultId + "_" + fileName;
+		}
+
+		return resultId + "_file";
+	}
+
 }

--
Gitblit v1.9.3