zhaowenyu
2026-04-01 77ae8c8bf34aaafa58a230d65db5e0f9f2e23c74
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
package com.ard.sample.media.service.impl;
 
import com.ard.common.config.OssConfiguration;
import com.ard.oss.service.impl.OssServiceContext;
import com.ard.sample.utils.TimestampUtils;
import com.ard.sample.utils.VideoDurationUtilFFmpeg;
import com.ard.sample.wayline.model.dto.WaylineJobDTO;
import com.ard.sample.wayline.service.IWaylineJobService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ard.sample.common.model.Pagination;
import com.ard.sample.common.model.PaginationData;
import com.ard.sample.manage.model.dto.DeviceDictionaryDTO;
import com.ard.sample.manage.model.enums.DeviceDomainEnum;
import com.ard.sample.manage.service.IDeviceDictionaryService;
import com.ard.sample.media.mapper.IFileMapper;
import com.ard.sample.media.model.FileUploadDTO;
import com.ard.sample.media.model.MediaFileDTO;
import com.ard.sample.media.model.MediaFileEntity;
import com.ard.sample.media.service.IFileService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.net.URL;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
 
/**
 * @author sean
 * @version 0.2
 * @date 2021/12/9
 */
@Slf4j
@Service
@Transactional
public class FileServiceImpl implements IFileService {
 
    // 定义全局的日期时间格式化器(复用避免重复创建)
    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
 
    @Resource
    private IFileMapper mapper;
 
    @Autowired
    private IDeviceDictionaryService deviceDictionaryService;
 
    @Autowired
    private IWaylineJobService _waylineJobService;
 
    @Autowired
    private OssServiceContext ossService;
 
    @Autowired
    private VideoDurationUtilFFmpeg _videoDurationUtil;
 
    @Autowired
    private TimestampUtils _timestampUtils;
 
    private Optional<MediaFileEntity> getMediaByFingerprint(String fingerprint) {
        MediaFileEntity fileEntity = mapper.selectOne(new LambdaQueryWrapper<MediaFileEntity>()
                .eq(MediaFileEntity::getFingerprint, fingerprint));
        return Optional.ofNullable(fileEntity);
    }
 
    private Optional<MediaFileEntity> getMediaByFileId(String fileId) {
        MediaFileEntity fileEntity = mapper.selectOne(new LambdaQueryWrapper<MediaFileEntity>()
                .eq(MediaFileEntity::getFileId, fileId));
        return Optional.ofNullable(fileEntity);
    }
 
    @Override
    public Boolean checkExist(String fingerprint) {
        return this.getMediaByFingerprint(fingerprint).isPresent();
    }
 
    @Override
    public Integer saveFile(FileUploadDTO file) {
        MediaFileEntity fileEntity = this.fileUploadConvertToEntity(file);
        fileEntity.setFileId(UUID.randomUUID().toString());
        if(fileEntity.getFileName().contains(".mp4")) {
            log.info("mp4文件入库时读取视频时长,文件名:{},文件地址:{}",fileEntity.getFileName(),fileEntity.getFilePath());
            double duration = _videoDurationUtil.getRemoteMp4Duration(file.getPath());
            log.info("mp4视频文件时长为:{}",duration);
            fileEntity.setDuration(duration);
            log.info("视频创建时间:{}",fileEntity.getStartTime());
            long endTime=_timestampUtils.plusSecondsByMillis(fileEntity.getStartTime(), duration);
            log.info("视频创建时间+时长=结束时间:{}",endTime);
            fileEntity.setEndTime(endTime);
        }
 
        return mapper.insert(fileEntity);
    }
 
    @Override
    public List<MediaFileDTO> getAllFiles() {
        return mapper.selectList(new LambdaQueryWrapper<MediaFileEntity>())
                .stream()
                .map(this::entityConvertToDto)
                .collect(Collectors.toList());
    }
 
    /**
     * 根据任务id获取全部媒体库文件
     *
     * @param jobId 任务id
     * @return List<MediaFileDTO> 媒体库文件列表
     */
    @Override
    public PaginationData<MediaFileDTO> getJobFiles(long page, long pageSize,String jobId, String isType) {
        Page<MediaFileEntity> pageData = mapper.selectPage(
                new Page<MediaFileEntity>(page, pageSize),
                new LambdaQueryWrapper<MediaFileEntity>()
                        .eq(Objects.nonNull(jobId), MediaFileEntity::getJobId, jobId)
//                        .orderByDesc(MediaFileEntity::getId));
                        // isType=1 时匹配 V.jpeg
                        .like("1".equals(isType), MediaFileEntity::getFileName, "V.jpeg")
                        // isType=0 时匹配 V.mp4
                        .like("0".equals(isType), MediaFileEntity::getFileName, "V.mp4")
                        // 排序修改:按start_time正序(升序)排列
                        .orderByAsc(MediaFileEntity::getStartTime)); // 关键改动1:替换排序方式
 
        List<MediaFileDTO> records = pageData.getRecords()
                .stream()
                .map(this::entityConvertToDto)
                .collect(Collectors.toList());
 
        return new PaginationData<MediaFileDTO>(records, new Pagination(pageData));
    }
 
    /**
     * 分页获取媒体文件列表
     * 增加筛选条件
     * @param page 当前页码
     * @param pageSize 每页大小
     * @return 分页数据对象,包含媒体文件列表和分页信息
     */
    @Override
    public PaginationData<MediaFileDTO> getMediaFilesPagination(long page, long pageSize, String jobId, Long startTime, Long endTime) {
        Page<MediaFileEntity> pageData = mapper.selectPage(
                new Page<MediaFileEntity>(page, pageSize),
                new LambdaQueryWrapper<MediaFileEntity>()
                        .eq(jobId != null, MediaFileEntity::getJobId, jobId)
                        .ge(startTime != null, MediaFileEntity::getStartTime, startTime)
                        .le(endTime != null, MediaFileEntity::getStartTime, endTime)
                        .orderByDesc(MediaFileEntity::getId));
        List<MediaFileDTO> records = pageData.getRecords()
                .stream()
                .map(this::entityConvertToDto)
                .collect(Collectors.toList());
 
        return new PaginationData<MediaFileDTO>(records, new Pagination(pageData));
    }
 
    @Override
    public URL getObjectUrl(String fileId) {
        Optional<MediaFileEntity> mediaFileOpt = getMediaByFileId(fileId);
        if (mediaFileOpt.isEmpty()) {
            throw new IllegalArgumentException("{} doesn't exist.");
        }
 
        return ossService.getObjectUrl(OssConfiguration.bucket, mediaFileOpt.get().getObjectKey());
    }
 
    @Override
    public List<MediaFileDTO> getFilesByWorkspaceAndJobId(String jobId) {
        return mapper.selectList(new LambdaQueryWrapper<MediaFileEntity>()
                .eq(MediaFileEntity::getJobId, jobId))
                .stream()
                .map(this::entityConvertToDto).collect(Collectors.toList());
    }
 
    /**
     * 将接收到的文件对象转换为数据库实体对象。
     * @param file
     * @return
     */
    private MediaFileEntity fileUploadConvertToEntity(FileUploadDTO file) {
        MediaFileEntity.MediaFileEntityBuilder builder = MediaFileEntity.builder();
 
        if (file != null) {
            long creaeTime = 0;
            if(file.getMetadata().getCreatedTime() != null) {
                creaeTime = file.getMetadata().getCreatedTime().getTime();
            }
            builder.fileName(file.getName())
                    .filePath(file.getPath())
                    .fingerprint(file.getFingerprint())
                    .objectKey(file.getObjectKey())
                    .subFileType(file.getSubFileType())
                    .isOriginal(file.getExt().getIsOriginal())
                    .jobId(file.getExt().getFlightId())
                    .drone(file.getExt().getSn())
                    .startTime(creaeTime)
                    .tinnyFingerprint(file.getExt().getTinnyFingerprint());
 
            // domain-type-subType
            int[] payloadModel = Arrays.stream(file.getExt().getPayloadModelKey().split("-"))
                    .map(Integer::valueOf)
                    .mapToInt(Integer::intValue)
                    .toArray();
            Optional<DeviceDictionaryDTO> payloadDict = deviceDictionaryService
                    .getOneDictionaryInfoByTypeSubType(DeviceDomainEnum.PAYLOAD.getVal(), payloadModel[1], payloadModel[2]);
            payloadDict.ifPresent(payload -> builder.payload(payload.getDeviceName()));
        }
        return builder.build();
    }
 
    /**
     * 将数据库实体对象转换为文件数据传输对象。
     * @param entity
     * @return
     */
    private MediaFileDTO entityConvertToDto(MediaFileEntity entity) {
        MediaFileDTO.MediaFileDTOBuilder builder = MediaFileDTO.builder();
 
        if (entity != null) {
            builder.fileName(entity.getFileName())
                    .fileId(entity.getFileId())
                    .filePath(entity.getFilePath())
                    .isOriginal(entity.getIsOriginal())
                    .fingerprint(entity.getFingerprint())
                    .objectKey(entity.getObjectKey())
                    .tinnyFingerprint(entity.getTinnyFingerprint())
                    .payload(entity.getPayload())
                    .createTime(LocalDateTime.ofInstant(
                            Instant.ofEpochMilli(entity.getCreateTime()), ZoneId.systemDefault()).format(DATE_TIME_FORMATTER))
                    .updateTime(LocalDateTime.ofInstant(
                            Instant.ofEpochMilli(entity.getUpdateTime()), ZoneId.systemDefault()).format(DATE_TIME_FORMATTER))
                    .startTime(LocalDateTime.ofInstant(
                            Instant.ofEpochMilli(entity.getStartTime()), ZoneId.systemDefault()).format(DATE_TIME_FORMATTER))
                    .endTime(LocalDateTime.ofInstant(
                            Instant.ofEpochMilli(entity.getEndTime()), ZoneId.systemDefault()).format(DATE_TIME_FORMATTER))
                    .duration(entity.getDuration())
                    .drone(entity.getDrone())
                    .jobId(entity.getJobId())
                    .jobName(entity.getJobName());
 
        }
 
        return builder.build();
    }
 
}