rain
2024-06-14 8d9a2d656e4ae007590c622e5f7c228adacdca49
src/main/java/com/dji/sample/media/service/impl/MediaServiceImpl.java
@@ -1,36 +1,47 @@
package com.dji.sample.media.service.impl;
import com.dji.sample.common.error.CommonErrorEnum;
import com.aliyuncs.utils.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.dji.sample.common.model.Pagination;
import com.dji.sample.common.model.PaginationData;
import com.dji.sample.common.model.ResponseResult;
import com.dji.sample.component.mqtt.model.*;
import com.dji.sample.component.mqtt.model.ChannelName;
import com.dji.sample.component.mqtt.model.CommonTopicReceiver;
import com.dji.sample.component.mqtt.model.MapKeyConst;
import com.dji.sample.component.mqtt.service.IMessageSenderService;
import com.dji.sample.component.redis.RedisConst;
import com.dji.sample.component.redis.RedisOpsUtils;
import com.dji.sample.component.websocket.model.BizCodeEnum;
import com.dji.sample.component.websocket.model.CustomWebSocketMessage;
import com.dji.sample.component.websocket.service.ISendMessageService;
import com.dji.sample.component.websocket.service.IWebSocketManageService;
import com.dji.sample.manage.model.dto.DeviceDTO;
import com.dji.sample.manage.model.enums.UserTypeEnum;
import com.dji.sample.manage.model.receiver.OsdSubDeviceReceiver;
import com.dji.sample.manage.service.IDeviceRedisService;
import com.dji.sample.manage.service.IDeviceService;
import com.dji.sample.media.model.FileUploadCallback;
import com.dji.sample.media.model.FileUploadDTO;
import com.dji.sample.media.model.MediaFileCountDTO;
import com.dji.sample.media.model.MediaFileDTO;
import com.dji.sample.media.dao.IFileMapper;
import com.dji.sample.media.model.*;
import com.dji.sample.media.model.param.SearchMediaParam;
import com.dji.sample.media.service.IFileService;
import com.dji.sample.media.service.IMediaService;
import com.dji.sample.wayline.model.dto.WaylineJobDTO;
import com.dji.sample.wayline.service.IWaylineJobService;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.mqtt.support.MqttHeaders;
import org.springframework.messaging.MessageHeaders;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestParam;
import java.awt.*;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
/**
@@ -39,6 +50,7 @@
 * @date 2021/12/9
 */
@Service
@Slf4j
public class MediaServiceImpl implements IMediaService {
    @Autowired
@@ -60,7 +72,15 @@
    private ISendMessageService sendMessageService;
    @Autowired
    private IWebSocketManageService webSocketManageService;
    private IDeviceRedisService deviceRedisService;
    @Autowired
    private IFileMapper mapper;
    @Value("${oss.out-net-file-address}")
    private String fileAddress;
    @Value("${oss.bucket}")
    private String bucket;
    @Override
    public Boolean fastUpload(String workspaceId, String fingerprint) {
@@ -91,65 +111,94 @@
    }
    @Override
    @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_FILE_UPLOAD_CALLBACK, outputChannel = ChannelName.OUTBOUND)
    public void handleFileUploadCallBack(CommonTopicReceiver receiver) {
    public PaginationData<MediaJobDTO> mediaPage(String workspaceId, SearchMediaParam param) {
        param.setFileAddress(fileAddress + "/" + bucket);
        Page<MediaJobDTO> waylineJobDTOPage = mapper.mediaPage(new Page<MediaJobDTO>(param.getPage(), param.getPageSize()), workspaceId, param);
        return new PaginationData<MediaJobDTO>(waylineJobDTOPage.getRecords(), new Pagination(waylineJobDTOPage));
    }
    @Override
    public PaginationData<MediaJobDTO> mediaDetail(String jobId, Long page, Long pageSize) {
        Page<MediaJobDTO> waylineJobDTOPage = mapper.mediaDetail(new Page<MediaJobDTO>(page, pageSize), jobId, fileAddress + "/" + bucket);
        return new PaginationData<MediaJobDTO>(waylineJobDTOPage.getRecords(), new Pagination(waylineJobDTOPage));
    }
    /**
     * Handle media files messages reported by dock.
     * 处理由dock报告的媒体文件消息。
     *
     * @param receiver
     * @return
     */
    @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_FILE_UPLOAD_CALLBACK, outputChannel = ChannelName.OUTBOUND_EVENTS)
    public CommonTopicReceiver handleFileUploadCallBack(CommonTopicReceiver receiver) throws IOException, FontFormatException {
        FileUploadCallback callback = objectMapper.convertValue(receiver.getData(), FileUploadCallback.class);
        String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + receiver.getGateway()
                + TopicConst.EVENTS_SUF + TopicConst._REPLY_SUF;
        CommonTopicResponse<RequestsReply> data = CommonTopicResponse.<RequestsReply>builder()
                .timestamp(System.currentTimeMillis())
                .method(EventsMethodEnum.FILE_UPLOAD_CALLBACK.getMethod())
                .data(RequestsReply.success())
                .tid(receiver.getTid())
                .bid(receiver.getBid())
                .build();
        if (callback.getResult() != ResponseResult.CODE_SUCCESS) {
            messageSenderService.publish(topic, data);
            return;
            log.error("媒体文件上传失败;Media file upload failed!");
            return null;
        }
        String jobId = callback.getFile().getExt().getFlightId();
        Optional<DeviceDTO> deviceOpt = deviceRedisService.getDeviceOnline(receiver.getGateway());
        MediaFileCountDTO mediaFileCount = (MediaFileCountDTO) RedisOpsUtils.hashGet(RedisConst.MEDIA_FILE_PREFIX + receiver.getGateway(), jobId);
        // duplicate data
        if (receiver.getBid().equals(mediaFileCount.getBid()) && receiver.getTid().equals(mediaFileCount.getTid())) {
            messageSenderService.publish(topic, data);
            return;
        if (deviceOpt.isEmpty()
                || (Objects.nonNull(mediaFileCount) && receiver.getBid().equals(mediaFileCount.getBid())
                && receiver.getTid().equals(mediaFileCount.getTid()))) {
            return receiver;
        }
        DeviceDTO device = (DeviceDTO) RedisOpsUtils.get(RedisConst.DEVICE_ONLINE_PREFIX + receiver.getGateway());
        DeviceDTO device = deviceOpt.get();
        Optional<WaylineJobDTO> jobOpt = waylineJobService.getJobByJobId(device.getWorkspaceId(), jobId);
        if (jobOpt.isPresent()) {
            boolean isSave = parseMediaFile(callback, jobOpt.get());
            if (!isSave) {
                data.setData(RequestsReply.error(CommonErrorEnum.ILLEGAL_ARGUMENT));
                log.error("保存文件到数据库失败,请手动检查数据;Failed to save the file to the database, please check the data manually.");
                return null;
            }
        } else if (!StringUtils.isEmpty(jobId)) { //一键起飞操作需要
            WaylineJobDTO waylineJobDTO = new WaylineJobDTO();
            waylineJobDTO.setWorkspaceId(device.getWorkspaceId());
            waylineJobDTO.setDockSn(device.getDeviceSn());
            boolean isSave = parseMediaFile(callback, waylineJobDTO);
            if (!isSave) {
                log.error("保存文件到数据库失败,请手动检查数据;Failed to save the file to the database, please check the data manually.");
                return null;
            }
        }
        messageSenderService.publish(topic, data);
        notifyUploadedCount(mediaFileCount, receiver, jobId);
        notifyUploadedCount(mediaFileCount, receiver, jobId, device);
        return receiver;
    }
    /**
     * update the uploaded count and notify web side
     * 更新上传的计数并通知web端
     *
     * @param mediaFileCount
     * @param receiver
     * @param jobId
     */
    private void notifyUploadedCount(MediaFileCountDTO mediaFileCount, CommonTopicReceiver receiver, String jobId) {
    private void notifyUploadedCount(MediaFileCountDTO mediaFileCount, CommonTopicReceiver receiver, String jobId, DeviceDTO dock) {
        // Do not notify when files that do not belong to the route are uploaded.
        //上传不属于该路线的文件时不进行通知。
        if (Objects.isNull(mediaFileCount)) {
            return;
        }
        mediaFileCount.setBid(receiver.getBid());
        mediaFileCount.setTid(receiver.getTid());
        mediaFileCount.setUploadedCount(mediaFileCount.getUploadedCount() + 1);
        String key = RedisConst.MEDIA_FILE_PREFIX + receiver.getGateway();
        // After all the files of the job are uploaded, delete the media file key.
        //待作业的所有文件上传完成后,删除媒体文件密钥。
        if (mediaFileCount.getUploadedCount() >= mediaFileCount.getMediaCount()) {
            RedisOpsUtils.hashDel(key, new String[]{jobId});
            // After uploading, delete the key with the highest priority.
            //上传完成后,删除优先级最高的密钥。
            String highestKey = RedisConst.MEDIA_HIGHEST_PRIORITY_PREFIX + receiver.getGateway();
            if (RedisOpsUtils.checkExist(highestKey) &&
                    jobId.equals(((MediaFileCountDTO) RedisOpsUtils.get(highestKey)).getJobId())) {
@@ -163,16 +212,12 @@
            RedisOpsUtils.hashSet(key, jobId, mediaFileCount);
        }
        DeviceDTO dock = (DeviceDTO) RedisOpsUtils.get(RedisConst.DEVICE_ONLINE_PREFIX + receiver.getGateway());
        sendMessageService.sendBatch(webSocketManageService.getValueWithWorkspaceAndUserType(dock.getWorkspaceId(), UserTypeEnum.WEB.getVal()),
                CustomWebSocketMessage.builder()
                        .bizCode(BizCodeEnum.FILE_UPLOAD_CALLBACK.getCode())
                        .timestamp(System.currentTimeMillis())
                        .data(mediaFileCount)
                        .build());
        //通过websocket把数据发送给web
        sendMessageService.sendBatch(dock.getWorkspaceId(), UserTypeEnum.WEB.getVal(),
                BizCodeEnum.FILE_UPLOAD_CALLBACK.getCode(), mediaFileCount);
    }
    private Boolean parseMediaFile(FileUploadCallback callback, WaylineJobDTO job) {
    private Boolean parseMediaFile(FileUploadCallback callback, WaylineJobDTO job) throws IOException, FontFormatException {
        // Set the drone sn that shoots the media
        Optional<DeviceDTO> dockDTO = deviceService.getDeviceBySn(job.getDockSn());
        dockDTO.ifPresent(dock -> callback.getFile().getExt().setSn(dock.getChildDeviceSn()));
@@ -180,26 +225,38 @@
        // set path
        String objectKey = callback.getFile().getObjectKey();
        callback.getFile().setPath(objectKey.substring(objectKey.indexOf("/") + 1, objectKey.lastIndexOf("/")));
        try {
            ExecutorService executor = Executors.newSingleThreadExecutor();
            executor.execute(() -> {
                try {
                    fileService.saveMarkFile(job.getWorkspaceId(), callback.getFile());
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            });
            executor.shutdown();
        } catch (Exception e) {
            log.error("方法执行有误==============: ", e);
            throw e;
        }
        return fileService.saveFile(job.getWorkspaceId(), callback.getFile()) > 0;
    }
    @Override
    @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_HIGHEST_PRIORITY_UPLOAD_FLIGHT_TASK_MEDIA, outputChannel = ChannelName.OUTBOUND)
    public void handleHighestPriorityUploadFlightTaskMedia(CommonTopicReceiver receiver, MessageHeaders headers) {
    /**
     * Handles the highest priority message about media uploads.
     *
     * @param receiver
     * @param headers
     * @return
     */
    @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_HIGHEST_PRIORITY_UPLOAD_FLIGHT_TASK_MEDIA, outputChannel = ChannelName.OUTBOUND_EVENTS)
    public CommonTopicReceiver handleHighestPriorityUploadFlightTaskMedia(CommonTopicReceiver receiver, MessageHeaders headers) {
        Map map = objectMapper.convertValue(receiver.getData(), Map.class);
        if (map.isEmpty() || !map.containsKey(MapKeyConst.FLIGHT_ID)) {
            return;
            return null;
        }
        messageSenderService.publish(headers.get(MqttHeaders.RECEIVED_TOPIC) + TopicConst._REPLY_SUF,
                CommonTopicResponse.builder()
                        .data(RequestsReply.success())
                        .method(receiver.getMethod())
                        .timestamp(System.currentTimeMillis())
                        .bid(receiver.getBid())
                        .tid(receiver.getTid())
                        .build());
        String dockSn = receiver.getGateway();
        String jobId = map.get(MapKeyConst.FLIGHT_ID).toString();
@@ -209,7 +266,7 @@
            countDTO = (MediaFileCountDTO) RedisOpsUtils.get(key);
            if (jobId.equals(countDTO.getJobId())) {
                RedisOpsUtils.setWithExpire(key, countDTO, RedisConst.DEVICE_ALIVE_SECOND * 5);
                return;
                return null;
            }
            countDTO.setPreJobId(countDTO.getJobId());
@@ -218,13 +275,13 @@
        RedisOpsUtils.setWithExpire(key, countDTO, RedisConst.DEVICE_ALIVE_SECOND * 5);
        DeviceDTO dock = (DeviceDTO) RedisOpsUtils.get(RedisConst.DEVICE_ONLINE_PREFIX + dockSn);
        sendMessageService.sendBatch(webSocketManageService.getValueWithWorkspaceAndUserType(dock.getWorkspaceId(), UserTypeEnum.WEB.getVal()),
                CustomWebSocketMessage.builder()
                        .timestamp(System.currentTimeMillis())
                        .bizCode(BizCodeEnum.HIGHEST_PRIORITY_UPLOAD_FLIGHT_TASK_MEDIA.getCode())
                        .data(countDTO)
                        .build());
        Optional<DeviceDTO> deviceOpt = deviceRedisService.getDeviceOnline(receiver.getGateway());
        if (deviceOpt.isEmpty()) {
            return null;
        }
        sendMessageService.sendBatch(deviceOpt.get().getWorkspaceId(), UserTypeEnum.WEB.getVal(),
                BizCodeEnum.HIGHEST_PRIORITY_UPLOAD_FLIGHT_TASK_MEDIA.getCode(), countDTO);
        return receiver;
    }
}