吉安感知网项目-后端
linwei
6 days ago 05fb356099b5af472ee23d9164bca61962d9c2ed
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/service/impl/GdTaskResultServiceImpl.java
@@ -16,16 +16,46 @@
 */
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.springblade.core.secure.utils.AuthUtil;
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 +63,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 +112,289 @@
      //   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列表
    * @return 保存成功后的成果ID列表
    */
   @Override
   public List<Long> batchSaveWithIds(List<GdTaskResultDTO> gdTaskResults) {
      // 步骤1:转换DTO为实体
      List<GdTaskResultEntity> gdTaskResultEntities = GdTaskResultWrapper.build().listEntity(gdTaskResults);
      // 步骤2:处理每个实体的属性
      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(Long.valueOf(AuthUtil.getDeptId()));
         gdTaskResult.setUpdateUser(AuthUtil.getUserId());
         gdTaskResult.setDistributeStatus(0);
      });
      // 步骤3:批量插入数据
      int insertCount = baseMapper.insertBatch(gdTaskResultEntities);
      // 步骤4:收集并返回保存后的ID列表
      return gdTaskResultEntities.stream()
            .map(GdTaskResultEntity::getId)
            .filter(Objects::nonNull)
            .collect(Collectors.toList());
   }
   /**
    * 保存普通成果数据(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 +423,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";
   }
}