package com.dji.sample.wayline.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.dji.sample.component.oss.model.OssConfiguration; import com.dji.sample.component.oss.service.impl.OssServiceContext; import com.dji.sample.wayline.dao.IWaylineFileMapper; import com.dji.sample.wayline.model.dto.KmzFileProperties; import com.dji.sample.wayline.model.dto.WaylineFileDTO; import com.dji.sample.wayline.model.entity.WaylineFileEntity; import com.dji.sample.wayline.model.entity.WaylineJobEntity; import com.dji.sample.wayline.service.IWaylineFileService; import com.dji.sdk.cloudapi.device.DeviceDomainEnum; import com.dji.sdk.cloudapi.device.DeviceEnum; import com.dji.sdk.cloudapi.device.DeviceSubTypeEnum; import com.dji.sdk.cloudapi.device.DeviceTypeEnum; import com.dji.sdk.cloudapi.wayline.GetWaylineListRequest; import com.dji.sdk.cloudapi.wayline.GetWaylineListResponse; import com.dji.sdk.cloudapi.wayline.WaylineTypeEnum; import com.dji.sdk.common.Pagination; import com.dji.sdk.common.PaginationData; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Node; import org.dom4j.io.SAXReader; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.DigestUtils; import org.springframework.util.StringUtils; import org.springframework.web.multipart.MultipartFile; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.nio.charset.StandardCharsets; import java.sql.SQLException; import java.util.*; import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import static com.dji.sample.wayline.model.dto.KmzFileProperties.WAYLINE_FILE_SUFFIX; /** * @author sean * @version 0.3 * @date 2021/12/22 */ @Service @Transactional public class WaylineFileServiceImpl implements IWaylineFileService { @Autowired private IWaylineFileMapper mapper; @Autowired private OssServiceContext ossService; @Override public PaginationData getWaylinesByParam(String workspaceId, GetWaylineListRequest param) { // Paging Query Page page = mapper.selectPage( new Page(param.getPage(), param.getPageSize()), new LambdaQueryWrapper() .eq(WaylineFileEntity::getWorkspaceId, workspaceId) .eq(Objects.nonNull(param.getFavorited()), WaylineFileEntity::getFavorited, param.getFavorited()) .and(param.getTemplateType() != null, wrapper -> { for (WaylineTypeEnum type : param.getTemplateType()) { wrapper.like(WaylineFileEntity::getTemplateTypes, type.getValue()).or(); } }) .and(param.getPayloadModelKey() != null, wrapper -> { for (DeviceEnum type : param.getPayloadModelKey()) { wrapper.like(WaylineFileEntity::getPayloadModelKeys, type.getType()).or(); } }) .and(param.getDroneModelKeys() != null, wrapper -> { for (DeviceEnum type : param.getDroneModelKeys()) { wrapper.eq(WaylineFileEntity::getDroneModelKey, type.getType()).or(); } }) .like(Objects.nonNull(param.getKey()), WaylineFileEntity::getName, param.getKey()) // There is a risk of SQL injection .last(Objects.nonNull(param.getOrderBy()), " order by " + param.getOrderBy().toString())); // Wrap the results of a paging query into a custom paging object. List records = page.getRecords() .stream() .map(this::entityConvertToDTO) .collect(Collectors.toList()); return new PaginationData<>(records, new Pagination(page.getCurrent(), page.getSize(), page.getTotal())); } @Override public PaginationData getWaylines(long pageIndex, long pageSize) { // Paging Query Page page = mapper.selectPage( new Page(pageIndex, pageSize), new LambdaQueryWrapper().orderByDesc(WaylineFileEntity::getId) // There is a risk of SQL injection ); // Wrap the results of a paging query into a custom paging object. List records = page.getRecords() .stream() .map(this::entityConvertToDTO) .collect(Collectors.toList()); return new PaginationData<>(records, new Pagination(page.getCurrent(), page.getSize(), page.getTotal())); } @Override public Optional getWaylineByWaylineId(String workspaceId, String waylineId) { return Optional.ofNullable( this.entityConvertToDTO( mapper.selectOne( new LambdaQueryWrapper() // .eq(WaylineFileEntity::getWorkspaceId, workspaceId) // .eq(WaylineFileEntity::getWaylineId, waylineId)))); .eq(WaylineFileEntity::getId, waylineId)))); } @Override public Optional getWaylineByWaylineId(String waylineId) { return Optional.ofNullable( this.entityConvertToDTO( mapper.selectOne( new LambdaQueryWrapper() .eq(WaylineFileEntity::getId, waylineId)))); } @Override public URL getObjectUrl(String workspaceId, String waylineId) throws SQLException { Optional waylineOpt = this.getWaylineByWaylineId(workspaceId, waylineId); if (waylineOpt.isEmpty()) { throw new SQLException(waylineId + " does not exist."); } URL url =ossService.getObjectUrl(OssConfiguration.bucket, waylineOpt.get().getObjectKey()); return url; } @Override public URL getObjectUrl(String objectKey) { return ossService.getObjectUrl(OssConfiguration.bucket, objectKey); } @Override public Integer saveWaylineFile(String workspaceId, WaylineFileDTO metadata) { WaylineFileEntity file = this.dtoConvertToEntity(metadata); file.setWaylineId(UUID.randomUUID().toString()); file.setWorkspaceId(workspaceId); if (!StringUtils.hasText(file.getSign())) { try (InputStream object = ossService.getObject(OssConfiguration.bucket, metadata.getObjectKey())) { if (object.available() == 0) { throw new RuntimeException("The file " + metadata.getObjectKey() + " does not exist in the bucket[" + OssConfiguration.bucket + "]."); } file.setSign(DigestUtils.md5DigestAsHex(object)); } catch (IOException e) { e.printStackTrace(); } } int insertId = mapper.insert(file); return insertId > 0 ? file.getId() : insertId; } @Override public Boolean markFavorite(String workspaceId, List waylineIds, Boolean isFavorite) { if (waylineIds.isEmpty()) { return false; } if (isFavorite == null) { return true; } return mapper.update(null, new LambdaUpdateWrapper() .set(WaylineFileEntity::getFavorited, isFavorite) .eq(WaylineFileEntity::getWorkspaceId, workspaceId) .in(WaylineFileEntity::getWaylineId, waylineIds)) > 0; } @Override public List getDuplicateNames(String workspaceId, List names) { return mapper.selectList(new LambdaQueryWrapper() .eq(WaylineFileEntity::getWorkspaceId, workspaceId) .in(WaylineFileEntity::getName, names)) .stream() .map(WaylineFileEntity::getName) .collect(Collectors.toList()); } @Override public Boolean deleteByWaylineId(String workspaceId, String waylineId) { Optional waylineOpt = this.getWaylineByWaylineId(workspaceId, waylineId); if (waylineOpt.isEmpty()) { return true; } GetWaylineListResponse wayline = waylineOpt.get(); boolean isDel = mapper.delete(new LambdaUpdateWrapper() .eq(WaylineFileEntity::getWorkspaceId, workspaceId) .eq(WaylineFileEntity::getWaylineId, waylineId)) > 0; if (!isDel) { return false; } return ossService.deleteObject(OssConfiguration.bucket, wayline.getObjectKey()); } @Override public void importKmzFile(MultipartFile file, String workspaceId, String creator) { Optional waylineFileOpt = validKmzFile(file); if (waylineFileOpt.isEmpty()) { throw new RuntimeException("The file format is incorrect."); } try { WaylineFileDTO waylineFile = waylineFileOpt.get(); waylineFile.setUsername(creator); ossService.putObject(OssConfiguration.bucket, waylineFile.getObjectKey(), file.getInputStream()); this.saveWaylineFile(workspaceId, waylineFile); } catch (IOException e) { e.printStackTrace(); } } private Optional validKmzFile(MultipartFile file) { String filename = file.getOriginalFilename(); try { InputStream fileInputStream = file.getInputStream(); // DeviceTypeEnum payloadType = DeviceTypeEnum.find(Integer.parseInt(payloadNode.valueOf(KmzFileProperties.TAG_WPML_PREFIX + KmzFileProperties.TAG_PAYLOAD_ENUM_VALUE))); // DeviceSubTypeEnum payloadSubType = DeviceSubTypeEnum.find(Integer.parseInt(payloadNode.valueOf(KmzFileProperties.TAG_WPML_PREFIX + KmzFileProperties.TAG_PAYLOAD_SUB_ENUM_VALUE))); // Node payloadNode = document.selectSingleNode("//" + KmzFileProperties.TAG_WPML_PREFIX + KmzFileProperties.TAG_PAYLOAD_INFO); return Optional.of(WaylineFileDTO.builder() // .objectKey(OssConfiguration.objectDirPrefix + File.separator + filename) .objectKey(OssConfiguration.objectDirPrefix + "/" + filename) .name(filename.substring(0, filename.lastIndexOf(WAYLINE_FILE_SUFFIX))) .sign(DigestUtils.md5DigestAsHex(fileInputStream)) .payloadModelKeys(null) .templateTypes(null) .build()); }catch(IOException e) { e.printStackTrace(); } return Optional.empty(); // if (Objects.isNull(filename) || !filename.endsWith(WAYLINE_FILE_SUFFIX)) { // throw new RuntimeException("The file format is incorrect."); // } // try (ZipInputStream unzipFile = new ZipInputStream(file.getInputStream(), StandardCharsets.UTF_8)) { // // ZipEntry nextEntry = unzipFile.getNextEntry(); // while (Objects.nonNull(nextEntry)) { // boolean isWaylines = (KmzFileProperties.FILE_DIR_FIRST + "/" + KmzFileProperties.FILE_DIR_SECOND_TEMPLATE).equals(nextEntry.getName()); // if (!isWaylines) { // nextEntry = unzipFile.getNextEntry(); // continue; // } // SAXReader reader = new SAXReader(); // Document document = reader.read(unzipFile); // if (!StandardCharsets.UTF_8.name().equals(document.getXMLEncoding())) { // throw new RuntimeException("The file encoding format is incorrect."); // } // // Node droneNode = document.selectSingleNode("//" + KmzFileProperties.TAG_WPML_PREFIX + KmzFileProperties.TAG_DRONE_INFO); // Node payloadNode = document.selectSingleNode("//" + KmzFileProperties.TAG_WPML_PREFIX + KmzFileProperties.TAG_PAYLOAD_INFO); // if (Objects.isNull(droneNode) || Objects.isNull(payloadNode)) { // throw new RuntimeException("The file format is incorrect."); // } // DeviceTypeEnum type = DeviceTypeEnum.find(Integer.parseInt(droneNode.valueOf(KmzFileProperties.TAG_WPML_PREFIX + KmzFileProperties.TAG_DRONE_ENUM_VALUE))); // DeviceSubTypeEnum subType = DeviceSubTypeEnum.find(Integer.parseInt(droneNode.valueOf(KmzFileProperties.TAG_WPML_PREFIX + KmzFileProperties.TAG_DRONE_SUB_ENUM_VALUE))); // DeviceTypeEnum payloadType = DeviceTypeEnum.find(Integer.parseInt(payloadNode.valueOf(KmzFileProperties.TAG_WPML_PREFIX + KmzFileProperties.TAG_PAYLOAD_ENUM_VALUE))); // DeviceSubTypeEnum payloadSubType = DeviceSubTypeEnum.find(Integer.parseInt(payloadNode.valueOf(KmzFileProperties.TAG_WPML_PREFIX + KmzFileProperties.TAG_PAYLOAD_SUB_ENUM_VALUE))); // String templateType = document.valueOf("//" + KmzFileProperties.TAG_WPML_PREFIX + KmzFileProperties.TAG_TEMPLATE_TYPE); // // return Optional.of(WaylineFileDTO.builder() // .droneModelKey(DeviceEnum.find(DeviceDomainEnum.DRONE, type, subType).getDevice()) // .payloadModelKeys(List.of(DeviceEnum.find(DeviceDomainEnum.PAYLOAD, payloadType, payloadSubType).getDevice())) // .objectKey(OssConfiguration.objectDirPrefix + File.separator + filename) // .name(filename.substring(0, filename.lastIndexOf(WAYLINE_FILE_SUFFIX))) // .sign(DigestUtils.md5DigestAsHex(file.getInputStream())) // .templateTypes(List.of(WaylineTypeEnum.find(templateType).getValue())) // .build()); // } // // } catch (IOException | DocumentException e) { // e.printStackTrace(); // } // return Optional.empty(); } /** * Convert database entity objects into wayline data transfer object. * @param entity * @return */ private GetWaylineListResponse entityConvertToDTO(WaylineFileEntity entity) { if (entity == null) { return null; } return new GetWaylineListResponse() // .setDroneModelKey(DeviceEnum.find(entity.getDroneModelKey())) // .setFavorited(entity.getFavorited()) .setName(entity.getName()) // .setPayloadModelKeys(entity.getPayloadModelKeys() != null ? // Arrays.stream(entity.getPayloadModelKeys().split(",")).map(DeviceEnum::find).collect(Collectors.toList()) : null) // .setTemplateTypes(Arrays.stream(entity.getTemplateTypes().split(",")) // .map(Integer::parseInt).map(WaylineTypeEnum::find) // .collect(Collectors.toList())) .setUsername(entity.getUsername()) .setObjectKey(entity.getObjectKey()) .setSign(entity.getSign()) .setUpdateTime(entity.getUpdateTime()) .setCreateTime(entity.getCreateTime()) .setId(entity.getWaylineId()); } /** * Convert the received wayline object into a database entity object. * @param file * @return */ private WaylineFileEntity dtoConvertToEntity(WaylineFileDTO file) { WaylineFileEntity.WaylineFileEntityBuilder builder = WaylineFileEntity.builder(); if (file != null) { builder.droneModelKey(file.getDroneModelKey()) .name(file.getName()) .username(file.getUsername()) .objectKey(file.getObjectKey()) // Separate multiple payload data with ",". // .payloadModelKeys(String.join(",", file.getPayloadModelKeys())) // .templateTypes(file.getTemplateTypes().stream() // .map(String::valueOf) // .collect(Collectors.joining(","))) // .favorited(file.getFavorited()) .sign(file.getSign()) .build(); } return builder.build(); } }