| | |
| | | import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import io.minio.GetObjectArgs; |
| | | import io.minio.MinioClient; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.commons.lang3.StringUtils; |
| | | import org.apache.ibatis.annotations.Param; |
| | |
| | | import org.sxkj.common.enums.AttachResultTypeEnum; |
| | | import org.sxkj.common.enums.TypeOfOutcome; |
| | | import org.sxkj.common.func.Streams; |
| | | import org.sxkj.common.model.ResponseResult; |
| | | import org.sxkj.common.query.PaginationUtils; |
| | | import org.sxkj.common.redis.RedisOpsUtils; |
| | | import org.sxkj.common.utils.*; |
| | |
| | | import org.sxkj.resource.model.FileMetadataDTO; |
| | | import org.sxkj.resource.model.MinioPojo; |
| | | import org.sxkj.resource.model.PositionDTO; |
| | | import org.sxkj.resource.param.AttachPageParam; |
| | | import org.sxkj.resource.service.IAttachService; |
| | | import org.sxkj.resource.util.MinioFileDownloader; |
| | | import org.sxkj.resource.vo.*; |
| | |
| | | import org.sxkj.tools.model.TifConvertResult; |
| | | |
| | | import java.io.File; |
| | | import java.io.IOException; |
| | | import java.io.InputStream; |
| | | import java.io.OutputStream; |
| | | import java.time.LocalDateTime; |
| | | import java.time.LocalTime; |
| | | import java.time.ZoneId; |
| | |
| | | import java.util.concurrent.ExecutorService; |
| | | import java.util.concurrent.TimeUnit; |
| | | import java.util.stream.Collectors; |
| | | import java.util.zip.Deflater; |
| | | import java.util.zip.ZipEntry; |
| | | import java.util.zip.ZipOutputStream; |
| | | |
| | | /** |
| | | * 附件表 服务实现类 |
| | |
| | | |
| | | |
| | | @Override |
| | | public IPage<AttachVO> selectAttachPage(IPage<AttachVO> page, AttachVO attach) { |
| | | Integer resultType = attach.getResultType(); |
| | | List<AttachVO> attachVOS = Collections.emptyList(); |
| | | if (Objects.nonNull(resultType) && Attach.RESULT_TYPE_AI.equals(resultType)) { |
| | | attachVOS = baseMapper.selectEventAttachPage(page, attach); |
| | | } else { |
| | | attachVOS = baseMapper.selectAttachPage(page, attach); |
| | | } |
| | | |
| | | public IPage<AttachVO> selectAttachPage(IPage<AttachVO> page, AttachPageParam attach) { |
| | | List<AttachVO> attachVOS = baseMapper.selectAttachPage(page, attach); |
| | | return page.setRecords(attachVOS); |
| | | } |
| | | |
| | |
| | | |
| | | |
| | | @Override |
| | | public List<AnalysisAttachVo> contrastiveAnalysis(Long id) { |
| | | Attach attach = getById(id); |
| | | ValidUtil.assertNull(attach, "数据不能为空!"); |
| | | ValidUtil.assertNull(attach.getMetadata(), "媒体数据不存在经纬度!"); |
| | | FileMetadataDTO metadata = JSON.parseObject(attach.getMetadata().toString(), FileMetadataDTO.class); |
| | | PositionDTO shootPosition = metadata.getShootPosition(); |
| | | ValidUtil.assertNull(shootPosition, "媒体数据不存在经纬度!"); |
| | | Double lat = attach.getLatitude() != null && attach.getLatitude() > 0 ? attach.getLatitude() : shootPosition.getLat(); |
| | | Double lng = attach.getLongitude() != null && attach.getLongitude() > 0 ? attach.getLongitude() : shootPosition.getLng(); |
| | | Date createTime = attach.getCreateTime(); |
| | | // 转换为 LocalDateTime(保留时间部分) |
| | | LocalDateTime ldt = createTime.toInstant() |
| | | .atZone(ZoneId.systemDefault()) |
| | | .toLocalDateTime(); |
| | | // 计算前后两个月 |
| | | LocalDateTime twoMonthsBefore = ldt.minusMonths(2); // 前2个月 |
| | | LocalDateTime twoMonthsAfter = ldt.plusMonths(2); // 后2个月 |
| | | |
| | | // 如果需要转回 Date 类型 |
| | | Date beforeDate = Date.from(twoMonthsBefore.atZone(ZoneId.systemDefault()).toInstant()); |
| | | Date afterDate = Date.from(twoMonthsAfter.atZone(ZoneId.systemDefault()).toInstant()); |
| | | List<AnalysisAttachVo> pointAttachList = baseMapper.findPointAttach(lat, lng, beforeDate, afterDate, id); |
| | | return Streams.toList(pointAttachList, entity -> { |
| | | entity.setIsCheck(entity.getId().equals(id)); |
| | | return entity; |
| | | }); |
| | | |
| | | } |
| | | |
| | | @Override |
| | | public AttachInfoVO getAttachInfo(Long id) { |
| | | AttachInfoVO attachVO = baseMapper.getAttachInfo(id); |
| | | if (attachVO != null) { |
| | |
| | | PositionDTO shootPosition = metadata.getShootPosition(); |
| | | ValidUtil.assertNull(shootPosition, "媒体数据不存在经纬度!"); |
| | | Double lat = shootPosition.getLat(); |
| | | attachVO.setLatitude(lat); |
| | | Double lng = shootPosition.getLng(); |
| | | attachVO.setLongitude(lng); |
| | | } |
| | | // 文件类型,通过文件名称有_T,则是红外,包含_W|_V 则是可见光 |
| | | // if (attachVO.getName().contains("_T")) { |
| | | // attachVO.setPhotoType("ir"); |
| | | // } else if (attachVO.getName().contains("_W") || attachVO.getName().contains("_V")) { |
| | | // attachVO.setPhotoType("visible"); |
| | | // } |
| | | } |
| | | return attachVO; |
| | | } |
| | |
| | | FileMetadataDTO metadata = JSON.parseObject(item.getMetadata().toString(), FileMetadataDTO.class); |
| | | PositionDTO shootPosition = metadata.getShootPosition(); |
| | | Double lat = shootPosition.getLat(); |
| | | item.setLatitude(lat); |
| | | Double lng = shootPosition.getLng(); |
| | | item.setLongitude(lng); |
| | | } |
| | | }); |
| | | // 把result_type=0和把result_type=2数据进行用经度和纬度对比,如果有相同的,则把result_type=0的数据删除 |
| | | boolean removed = attachVO.removeIf(item -> { |
| | | return item.getResultType() == 0 && |
| | | attachVO.stream().anyMatch( |
| | | item1 -> item1.getResultType() == 2 |
| | | && item1.getLatitude().equals(item.getLatitude()) |
| | | && item1.getLongitude().equals(item.getLongitude()) |
| | | ); |
| | | }); |
| | | return attachVO; |
| | | } |
| | |
| | | return baseMapper.selectCount(Wrappers.<Attach>lambdaQuery() |
| | | .eq(Attach::getIsGenerateAiImg, 0) |
| | | .eq(Attach::getIsDeleted, 0) |
| | | .eq(Attach::getWayLineJobId, jobId).in(Attach::getResultType, AttachResultTypeEnum.getTheAttachmentType())); |
| | | .eq(Attach::getPatrolTaskId, jobId).in(Attach::getResultType, AttachResultTypeEnum.getTheAttachmentType())); |
| | | } |
| | | |
| | | @Override |
| | |
| | | return attachTypeStatisticsVOS; |
| | | } |
| | | |
| | | /** |
| | | * 下载附件 |
| | | * @param param 下载参数 |
| | | * @param outputStream 输出流 |
| | | * @return |
| | | * @throws IOException |
| | | */ |
| | | @Override |
| | | public Boolean downloadByByte(AttachmentDownloadParam param, OutputStream outputStream) throws IOException { |
| | | // 如果附件id集合并且任务id集合参数都为空,就判断起止时间是否都有为空,如果都为空,则返回错误 |
| | | param.setAreaCode(HeaderUtils.formatAreaCode(param.getAreaCode())); |
| | | if (CollectionUtils.isEmpty(param.getAttachIds()) && CollectionUtils.isEmpty(param.getWayLineJobIds())) { |
| | | // 判断起止时间是否为空 |
| | | if (StringUtils.isEmpty(param.getStartTime()) || StringUtils.isEmpty(param.getEndTime())) { |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | // 获取附件列表 |
| | | List<AttachVO> attachList = getAttachList(param); |
| | | if (CollectionUtils.isEmpty(attachList)) { |
| | | return false; |
| | | } |
| | | |
| | | try { |
| | | // 直接使用传入的输出流创建ZipOutputStream |
| | | ZipOutputStream zos = new ZipOutputStream(outputStream); |
| | | // 使用BEST_SPEED而不是BEST_COMPRESSION,提高速度 |
| | | zos.setLevel(Deflater.BEST_SPEED); |
| | | |
| | | // 使用较小的缓冲区,减少内存使用 |
| | | final int BUFFER_SIZE = 1024 * 1024; // 1MB |
| | | byte[] buffer = new byte[BUFFER_SIZE]; |
| | | |
| | | // 创建Minio客户端 |
| | | MinioClient minioClient = MinioClient.builder() |
| | | .endpoint(minioPojo.getPath()) |
| | | .credentials(minioPojo.getAccessKey(), minioPojo.getSecretKey()) |
| | | .build(); |
| | | |
| | | // 创建一个Set来跟踪已经使用的文件名,避免重名 |
| | | Set<String> usedFileNames = new HashSet<>(); |
| | | |
| | | // 直接遍历所有附件,不再按任务分组 |
| | | for (AttachVO attachVO : attachList) { |
| | | String objectName = attachVO.getName(); |
| | | try { |
| | | // 从MinIO获取文件流 |
| | | InputStream is = minioClient.getObject( |
| | | GetObjectArgs.builder() |
| | | .bucket(minioPojo.getBucket()) |
| | | .object(objectName) |
| | | .build()); |
| | | |
| | | // 提取文件名(不包含路径) |
| | | String fileName = objectName.substring(objectName.lastIndexOf("/") + 1); |
| | | |
| | | // 处理文件名冲突 |
| | | String uniqueFileName = fileName; |
| | | int counter = 1; |
| | | while (usedFileNames.contains(uniqueFileName)) { |
| | | // 如果文件名已存在,在文件名和扩展名之间添加计数器 |
| | | int dotIndex = fileName.lastIndexOf("."); |
| | | if (dotIndex > 0) { |
| | | uniqueFileName = fileName.substring(0, dotIndex) + "_" + counter + fileName.substring(dotIndex); |
| | | } else { |
| | | uniqueFileName = fileName + "_" + counter; |
| | | } |
| | | counter++; |
| | | } |
| | | |
| | | // 将文件名添加到已使用集合中 |
| | | usedFileNames.add(uniqueFileName); |
| | | |
| | | // 创建zip条目(直接放在根目录下) |
| | | ZipEntry zipEntry = new ZipEntry(uniqueFileName); |
| | | zos.putNextEntry(zipEntry); |
| | | |
| | | // 将文件内容写入zip |
| | | int length; |
| | | while ((length = is.read(buffer)) > 0) { |
| | | zos.write(buffer, 0, length); |
| | | // 每写入一块数据后刷新,确保数据及时发送到客户端 |
| | | zos.flush(); |
| | | } |
| | | |
| | | // 关闭资源 |
| | | zos.closeEntry(); |
| | | is.close(); |
| | | } catch (Exception e) { |
| | | log.error("处理文件 {} 失败: {}", objectName, e.getMessage()); |
| | | } |
| | | } |
| | | // 确保所有数据都被写入 |
| | | zos.finish(); |
| | | zos.flush(); |
| | | // 注意:不要关闭ZipOutputStream,因为它会关闭底层的OutputStream |
| | | // 让调用者负责关闭输出流 |
| | | |
| | | return true; |
| | | |
| | | } catch (Exception e) { |
| | | log.error("创建zip文件失败", e); |
| | | return false; |
| | | }finally { |
| | | if (outputStream != null) { |
| | | try { |
| | | outputStream.flush(); |
| | | } catch (IOException e) { |
| | | log.error("Error flushing output stream: {}", e.getMessage()); |
| | | } |
| | | try { |
| | | outputStream.close(); |
| | | } catch (IOException e) { |
| | | log.error("Error closing output stream: {}", e.getMessage()); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | } |