| | |
| | | */ |
| | | 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; |
| | | |
| | | /** |
| | | * 成果表 服务实现类 |
| | |
| | | * @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())); |
| | | } |
| | |
| | | // 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; |
| | | } |
| | | |
| | | /** |
| | |
| | | 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"; |
| | | } |
| | | |
| | | } |