Commit 93d6404befa8b43e3570bf1c8279f7531ec36164
1 parent
b3e721df
fix():添加历史回放视频上传和下载
Showing
12 changed files
with
713 additions
and
44 deletions
src/main/java/com/genersoft/iot/vmp/vmanager/jt1078/platform/Jt1078OfCarController.java
| ... | ... | @@ -5,6 +5,7 @@ |
| 5 | 5 | |
| 6 | 6 | package com.genersoft.iot.vmp.vmanager.jt1078.platform; |
| 7 | 7 | |
| 8 | +import cn.hutool.core.io.resource.InputStreamResource; | |
| 8 | 9 | import com.alibaba.fastjson2.JSON; |
| 9 | 10 | import com.alibaba.fastjson2.JSONArray; |
| 10 | 11 | import com.alibaba.fastjson2.JSONException; |
| ... | ... | @@ -25,17 +26,20 @@ import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; |
| 25 | 26 | import com.genersoft.iot.vmp.vmanager.bean.StreamContent; |
| 26 | 27 | import com.genersoft.iot.vmp.vmanager.bean.StreamPlayPath; |
| 27 | 28 | import com.genersoft.iot.vmp.vmanager.jt1078.platform.ben.HttpClientPostEntity; |
| 29 | +import com.genersoft.iot.vmp.vmanager.jt1078.platform.config.FtpConfigBean; | |
| 28 | 30 | import com.genersoft.iot.vmp.vmanager.jt1078.platform.config.Jt1078ConfigBean; |
| 29 | 31 | import com.genersoft.iot.vmp.vmanager.jt1078.platform.config.RtspConfigBean; |
| 30 | 32 | import com.genersoft.iot.vmp.vmanager.jt1078.platform.config.TuohuaConfigBean; |
| 33 | +import com.genersoft.iot.vmp.vmanager.jt1078.platform.domain.HistoryEnum; | |
| 31 | 34 | import com.genersoft.iot.vmp.vmanager.jt1078.platform.domain.HistoryRecord; |
| 32 | 35 | import com.genersoft.iot.vmp.vmanager.jt1078.platform.domain.PatrolDataReq; |
| 33 | 36 | import com.genersoft.iot.vmp.vmanager.jt1078.platform.handler.HttpClientUtil; |
| 34 | -import com.genersoft.iot.vmp.vmanager.jt1078.platform.service.FlowService; | |
| 35 | 37 | import com.genersoft.iot.vmp.vmanager.jt1078.platform.service.HisToryRecordService; |
| 36 | 38 | import com.genersoft.iot.vmp.vmanager.jt1078.platform.service.Jt1078OfService; |
| 37 | 39 | import com.genersoft.iot.vmp.vmanager.streamProxy.StreamProxyController; |
| 38 | 40 | import com.genersoft.iot.vmp.vmanager.streamPush.StreamPushController; |
| 41 | +import com.genersoft.iot.vmp.vmanager.util.FtpUtils; | |
| 42 | +import com.genersoft.iot.vmp.vmanager.util.MD5Util; | |
| 39 | 43 | import org.apache.commons.collections4.CollectionUtils; |
| 40 | 44 | import org.apache.commons.collections4.MapUtils; |
| 41 | 45 | import org.apache.commons.lang3.RandomUtils; |
| ... | ... | @@ -48,17 +52,23 @@ import org.slf4j.LoggerFactory; |
| 48 | 52 | import org.springframework.beans.factory.annotation.Autowired; |
| 49 | 53 | import org.springframework.beans.factory.annotation.Value; |
| 50 | 54 | import org.springframework.data.redis.core.RedisTemplate; |
| 55 | +import org.springframework.http.HttpHeaders; | |
| 56 | +import org.springframework.http.MediaType; | |
| 57 | +import org.springframework.http.ResponseEntity; | |
| 58 | +import org.springframework.scheduling.annotation.Scheduled; | |
| 51 | 59 | import org.springframework.util.Base64Utils; |
| 52 | 60 | import org.springframework.web.bind.annotation.*; |
| 53 | 61 | import sun.misc.Signal; |
| 54 | 62 | import sun.misc.SignalHandler; |
| 55 | 63 | |
| 64 | +import javax.annotation.PostConstruct; | |
| 56 | 65 | import javax.annotation.Resource; |
| 57 | 66 | import javax.crypto.Cipher; |
| 58 | 67 | import javax.servlet.http.HttpServletRequest; |
| 59 | 68 | import javax.validation.constraints.NotBlank; |
| 60 | 69 | import java.io.ByteArrayOutputStream; |
| 61 | 70 | import java.io.IOException; |
| 71 | +import java.io.InputStream; | |
| 62 | 72 | import java.net.URISyntaxException; |
| 63 | 73 | import java.security.Key; |
| 64 | 74 | import java.security.KeyFactory; |
| ... | ... | @@ -71,6 +81,7 @@ import java.util.concurrent.ArrayBlockingQueue; |
| 71 | 81 | import java.util.concurrent.ConcurrentHashMap; |
| 72 | 82 | import java.util.concurrent.ThreadPoolExecutor; |
| 73 | 83 | import java.util.concurrent.TimeUnit; |
| 84 | +import java.util.function.Function; | |
| 74 | 85 | import java.util.stream.Collectors; |
| 75 | 86 | |
| 76 | 87 | import static com.genersoft.iot.vmp.utils.DateUtil.formatter; |
| ... | ... | @@ -105,18 +116,22 @@ public class Jt1078OfCarController { |
| 105 | 116 | @Autowired |
| 106 | 117 | private IStreamProxyService streamProxyService; |
| 107 | 118 | @Resource |
| 108 | - private FlowService flowService; | |
| 109 | - @Resource | |
| 110 | 119 | private Jt1078OfService jt1078OfService; |
| 111 | 120 | @Resource |
| 112 | 121 | private HisToryRecordService hisToryRecordService; |
| 122 | + @Resource | |
| 123 | + private FtpConfigBean ftpConfigBean; | |
| 113 | 124 | //存储历史端口 key -->端口 value ---> sim-channel-startTime-endTime-port |
| 114 | 125 | public static final ConcurrentHashMap<Integer, Set<String>> map = new ConcurrentHashMap<>(); |
| 115 | 126 | |
| 127 | + public static ConcurrentHashMap<String, HistoryRecord> HISTORY_RECORD_MAP = new ConcurrentHashMap<>(); | |
| 128 | + | |
| 116 | 129 | private static final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 20, 40, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10)); |
| 117 | 130 | |
| 118 | 131 | @Value("${spring.profiles.active}") |
| 119 | 132 | private String profilesActive; |
| 133 | + @Autowired | |
| 134 | + private FtpUtils ftpUtils; | |
| 120 | 135 | |
| 121 | 136 | |
| 122 | 137 | public Jt1078OfCarController() { |
| ... | ... | @@ -153,7 +168,10 @@ public class Jt1078OfCarController { |
| 153 | 168 | return resultMap; |
| 154 | 169 | } |
| 155 | 170 | |
| 156 | - | |
| 171 | + /** | |
| 172 | + * 上传历史视频到FTP | |
| 173 | + * @param stream 流名称 | |
| 174 | + */ | |
| 157 | 175 | @GetMapping("/history/uploading/{stream}") |
| 158 | 176 | public void uploading(@PathVariable String stream) { |
| 159 | 177 | try { |
| ... | ... | @@ -161,11 +179,113 @@ public class Jt1078OfCarController { |
| 161 | 179 | String msg = jt1078ConfigBean.formatMessageHistoryUpload(stream); |
| 162 | 180 | HttpClientPostEntity httpClientPost = this.httpClientUtil.doPost(url, msg, (String) null); |
| 163 | 181 | chooseEntity(httpClientPost, url, false); |
| 182 | + String[] split = stream.split("_"); | |
| 183 | + String sim = split[0]; | |
| 184 | + String channel = split[1]; | |
| 185 | + String startTime = split[2]; | |
| 186 | + String endTime = split[3]; | |
| 187 | + hisToryRecordService.addOrUpdRecode( | |
| 188 | + HistoryRecord.builder().ip(UUID.randomUUID().toString()) | |
| 189 | + .name(stream) | |
| 190 | + .createTime(new Date()) | |
| 191 | + .sim(sim) | |
| 192 | + .channel(channel) | |
| 193 | + .startTime(startTime) | |
| 194 | + .endTime(endTime) | |
| 195 | + .status(HistoryEnum.UPLOADING.getCode()) | |
| 196 | + .ip(ftpConfigBean.getHost()) | |
| 197 | + .port(ftpConfigBean.getPort()) | |
| 198 | + .username(ftpConfigBean.getUsername()) | |
| 199 | + .password(MD5Util.encrypt(ftpConfigBean.getPassword())) | |
| 200 | + .path(StringUtils.join(ftpConfigBean.getBasePath(),"/",sim,"/channel_",channel)) | |
| 201 | + .build()); | |
| 202 | + } catch (Exception e) { | |
| 203 | + throw new RuntimeException(e); | |
| 204 | + } | |
| 205 | + } | |
| 206 | + | |
| 207 | + /** | |
| 208 | + * 定时更新 | |
| 209 | + */ | |
| 210 | + @Scheduled(cron = "0 */2 * * * *") | |
| 211 | + private void HistoryRecordScheduled(){ | |
| 212 | + updateHistoryRecord(); | |
| 213 | + } | |
| 214 | + | |
| 215 | + /** | |
| 216 | + * 项目启动更新 | |
| 217 | + */ | |
| 218 | + @PostConstruct | |
| 219 | + public void init() { | |
| 220 | + updateHistoryRecord(); | |
| 221 | + } | |
| 222 | + | |
| 223 | + /** | |
| 224 | + * 更新历史视频上传记录 | |
| 225 | + */ | |
| 226 | + @GetMapping("/history/updateHistoryRecord") | |
| 227 | + public void updateHistoryRecord(){ | |
| 228 | + List<HistoryRecord> list = hisToryRecordService.getHistoryRecordList(); | |
| 229 | + if (CollectionUtils.isNotEmpty(list)) { | |
| 230 | + List<HistoryRecord> expiredList = list.stream().filter(HistoryRecord::checkCreateTimeExpiration).collect(Collectors.toList()); | |
| 231 | + if (CollectionUtils.isNotEmpty(expiredList)) { | |
| 232 | + try { | |
| 233 | + expiredList = ftpUtils.batchDelete(expiredList); | |
| 234 | + hisToryRecordService.beachUpdRecode(expiredList); | |
| 235 | + } catch (IOException e) { | |
| 236 | + log.error("删除FTP文件失败"); | |
| 237 | + throw new RuntimeException(e); | |
| 238 | + } | |
| 239 | + } | |
| 240 | + List<HistoryRecord> uploadedList = list.stream().filter(HistoryRecord::checkCreateTimeNotExpiration) | |
| 241 | + .filter(ftpUtils::checkHistoryVideo).collect(Collectors.toList()); | |
| 242 | + if (CollectionUtils.isNotEmpty(expiredList)) { | |
| 243 | + hisToryRecordService.beachUpdRecode(uploadedList); | |
| 244 | + } | |
| 245 | + HISTORY_RECORD_MAP = hisToryRecordService.getHistoryRecordList().stream().collect(Collectors.toMap( | |
| 246 | + HistoryRecord::getName, // 使用名字作为键 | |
| 247 | + Function.identity(), // 用户对象作为值 | |
| 248 | + (existing, replacement) -> { | |
| 249 | + throw new IllegalArgumentException(String.format("Duplicate name found: %s", existing.getName())); | |
| 250 | + }, | |
| 251 | + ConcurrentHashMap::new // 保持插入顺序 | |
| 252 | + )); | |
| 253 | + } | |
| 254 | + } | |
| 255 | + | |
| 256 | + /** | |
| 257 | + * 下载历史视频 | |
| 258 | + * @param stream 流名称 | |
| 259 | + */ | |
| 260 | + @GetMapping("/history/download/{stream}") | |
| 261 | + public ResponseEntity<InputStreamResource> download(@PathVariable String stream) { | |
| 262 | + try { | |
| 263 | + HistoryRecord historyRecord = hisToryRecordService.getHistoryRecord(stream); | |
| 264 | + if (historyRecord == null) { | |
| 265 | + throw new RuntimeException("没有该流的上传记录,请先上传文件"); | |
| 266 | + } | |
| 267 | + if (!historyRecord.getStatus().equals(HistoryEnum.UPLOADED.getCode())) { | |
| 268 | + throw new RuntimeException("该文件未上传完成请稍后重试 !!!"); | |
| 269 | + } | |
| 270 | + InputStream inputStream = ftpUtils.downloadFile(historyRecord); | |
| 271 | + | |
| 272 | + // 设置HTTP头部信息 | |
| 273 | + HttpHeaders headers = new HttpHeaders(); | |
| 274 | + headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + extractFileNameFromPath(historyRecord.getPath())); | |
| 275 | + headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); | |
| 276 | + | |
| 277 | + return ResponseEntity.ok() | |
| 278 | + .headers(headers) | |
| 279 | + .body(new InputStreamResource(inputStream)); | |
| 164 | 280 | } catch (Exception e) { |
| 165 | 281 | throw new RuntimeException(e); |
| 166 | 282 | } |
| 167 | 283 | } |
| 168 | 284 | |
| 285 | + // 从路径中提取文件名的方法示例 | |
| 286 | + private String extractFileNameFromPath(String ftpFilePath) { | |
| 287 | + return ftpFilePath.substring(ftpFilePath.lastIndexOf('/') + 1); | |
| 288 | + } | |
| 169 | 289 | |
| 170 | 290 | /** |
| 171 | 291 | * redis清除在线车辆 | ... | ... |
src/main/java/com/genersoft/iot/vmp/vmanager/jt1078/platform/config/FtpConfigBean.java
src/main/java/com/genersoft/iot/vmp/vmanager/jt1078/platform/controller/HistoryRecordController.java
| ... | ... | @@ -27,8 +27,8 @@ public class HistoryRecordController { |
| 27 | 27 | * @param record 历史视频下载记录对象 |
| 28 | 28 | */ |
| 29 | 29 | @PostMapping("/addRecode") |
| 30 | - public String addRecode(@RequestBody HistoryRecord record) { | |
| 31 | - return hisToryRecordService.addRecode(record); | |
| 30 | + public String addOrUpdRecode(@RequestBody HistoryRecord record) { | |
| 31 | + return hisToryRecordService.addOrUpdRecode(record); | |
| 32 | 32 | } |
| 33 | 33 | |
| 34 | 34 | ... | ... |
src/main/java/com/genersoft/iot/vmp/vmanager/jt1078/platform/domain/HistoryEnum.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.vmanager.jt1078.platform.domain; | |
| 2 | + | |
| 3 | +import lombok.AllArgsConstructor; | |
| 4 | +import lombok.Getter; | |
| 5 | +import lombok.NoArgsConstructor; | |
| 6 | + | |
| 7 | +/** | |
| 8 | + * 历史视频枚举 | |
| 9 | + * | |
| 10 | + * @Author WangXin | |
| 11 | + * @Data 2025/2/23 | |
| 12 | + * @Version 1.0.0 | |
| 13 | + */ | |
| 14 | +@Getter | |
| 15 | +@AllArgsConstructor | |
| 16 | +@NoArgsConstructor | |
| 17 | +public enum HistoryEnum { | |
| 18 | + /** | |
| 19 | + * 未上传 | |
| 20 | + */ | |
| 21 | + NOT_UPLOADED("0","未上传"), | |
| 22 | + /** | |
| 23 | + * 上传中 | |
| 24 | + */ | |
| 25 | + UPLOADING("2","上传中"), | |
| 26 | + /** | |
| 27 | + * 已上传 | |
| 28 | + */ | |
| 29 | + UPLOADED("3","已上传"); | |
| 30 | + | |
| 31 | + /** | |
| 32 | + * code码 | |
| 33 | + */ | |
| 34 | + private String code; | |
| 35 | + /** | |
| 36 | + * 名称 | |
| 37 | + */ | |
| 38 | + private String name; | |
| 39 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/vmanager/jt1078/platform/domain/HistoryRecord.java
| 1 | 1 | package com.genersoft.iot.vmp.vmanager.jt1078.platform.domain; |
| 2 | 2 | |
| 3 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; |
| 4 | +import com.genersoft.iot.vmp.vmanager.jt1078.platform.config.FtpConfigBean; | |
| 5 | +import com.genersoft.iot.vmp.vmanager.util.FtpUtils; | |
| 4 | 6 | import lombok.AllArgsConstructor; |
| 5 | 7 | import lombok.Data; |
| 6 | 8 | import lombok.NoArgsConstructor; |
| 7 | 9 | import lombok.experimental.SuperBuilder; |
| 8 | 10 | import org.springframework.format.annotation.DateTimeFormat; |
| 9 | 11 | |
| 12 | +import java.time.LocalDate; | |
| 13 | +import java.time.ZoneId; | |
| 14 | +import java.time.temporal.ChronoUnit; | |
| 10 | 15 | import java.util.Date; |
| 11 | 16 | |
| 17 | +import static com.genersoft.iot.vmp.VManageBootstrap.getBean; | |
| 18 | + | |
| 12 | 19 | /** |
| 13 | 20 | * 历史视频下载记录类 |
| 14 | 21 | * |
| ... | ... | @@ -48,7 +55,7 @@ public class HistoryRecord { |
| 48 | 55 | /** |
| 49 | 56 | * FTP端口 |
| 50 | 57 | */ |
| 51 | - private String port; | |
| 58 | + private Integer port; | |
| 52 | 59 | /** |
| 53 | 60 | * FTP账号 |
| 54 | 61 | */ |
| ... | ... | @@ -58,6 +65,18 @@ public class HistoryRecord { |
| 58 | 65 | */ |
| 59 | 66 | private String password; |
| 60 | 67 | /** |
| 68 | + * FTP存储地址 | |
| 69 | + */ | |
| 70 | + private String path; | |
| 71 | + /** | |
| 72 | + * 结束时间 | |
| 73 | + */ | |
| 74 | + private String startTime; | |
| 75 | + /** | |
| 76 | + * 开始时间 | |
| 77 | + */ | |
| 78 | + private String endTime; | |
| 79 | + /** | |
| 61 | 80 | * 修改时间 |
| 62 | 81 | */ |
| 63 | 82 | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| ... | ... | @@ -82,4 +101,21 @@ public class HistoryRecord { |
| 82 | 101 | */ |
| 83 | 102 | private String remark; |
| 84 | 103 | |
| 104 | + public static boolean checkCreateTimeExpiration(HistoryRecord historyRecord) { | |
| 105 | + long l = calculateDaysBetween(historyRecord.createTime); | |
| 106 | + return l > getBean(FtpConfigBean.class).getExpirationTime(); | |
| 107 | + } | |
| 108 | + | |
| 109 | + public static boolean checkCreateTimeNotExpiration(HistoryRecord historyRecord) { | |
| 110 | + long l = calculateDaysBetween(historyRecord.createTime); | |
| 111 | + return l < getBean(FtpConfigBean.class).getExpirationTime(); | |
| 112 | + } | |
| 113 | + | |
| 114 | + | |
| 115 | + private static long calculateDaysBetween(Date startDate) { | |
| 116 | + // 将Date转换为LocalDate | |
| 117 | + LocalDate startLocalDate = startDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); | |
| 118 | + // 计算两个LocalDate对象之间的天数差异 | |
| 119 | + return ChronoUnit.DAYS.between(startLocalDate, LocalDate.now()); | |
| 120 | + } | |
| 85 | 121 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/vmanager/jt1078/platform/mapper/HisToryRecordMapper.java
| ... | ... | @@ -2,6 +2,9 @@ package com.genersoft.iot.vmp.vmanager.jt1078.platform.mapper; |
| 2 | 2 | |
| 3 | 3 | import com.genersoft.iot.vmp.vmanager.jt1078.platform.domain.HistoryRecord; |
| 4 | 4 | import org.apache.ibatis.annotations.Mapper; |
| 5 | +import org.apache.ibatis.annotations.Param; | |
| 6 | + | |
| 7 | +import java.util.List; | |
| 5 | 8 | |
| 6 | 9 | /** |
| 7 | 10 | * @Author WangXin |
| ... | ... | @@ -15,5 +18,17 @@ public interface HisToryRecordMapper { |
| 15 | 18 | * 添加历史下载记录 |
| 16 | 19 | * @param record 历史视频下载记录对象 |
| 17 | 20 | */ |
| 18 | - Integer addRecode(HistoryRecord record); | |
| 21 | + int addOrUpdRecode(HistoryRecord record); | |
| 22 | + /** | |
| 23 | + * 修改历史视频记录 | |
| 24 | + */ | |
| 25 | + int beachUpdRecode(@Param("list") List<HistoryRecord> record); | |
| 26 | + /** | |
| 27 | + * 历史视频上传记录列表 | |
| 28 | + */ | |
| 29 | + List<HistoryRecord> getHistoryRecordList(); | |
| 30 | + /** | |
| 31 | + * 根据stream获取历史视频对象 | |
| 32 | + */ | |
| 33 | + HistoryRecord getHistoryRecord(@Param("stream") String stream); | |
| 19 | 34 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/vmanager/jt1078/platform/service/HisToryRecordService.java
| ... | ... | @@ -2,6 +2,8 @@ package com.genersoft.iot.vmp.vmanager.jt1078.platform.service; |
| 2 | 2 | |
| 3 | 3 | import com.genersoft.iot.vmp.vmanager.jt1078.platform.domain.HistoryRecord; |
| 4 | 4 | |
| 5 | +import java.util.List; | |
| 6 | + | |
| 5 | 7 | /** |
| 6 | 8 | * @Author WangXin |
| 7 | 9 | * @Data 2025/2/22 |
| ... | ... | @@ -12,5 +14,18 @@ public interface HisToryRecordService { |
| 12 | 14 | * 添加历史下载记录 |
| 13 | 15 | * @param record 历史视频下载记录对象 |
| 14 | 16 | */ |
| 15 | - String addRecode(HistoryRecord record); | |
| 17 | + String addOrUpdRecode(HistoryRecord record); | |
| 18 | + /** | |
| 19 | + * 修改历史视频记录 | |
| 20 | + */ | |
| 21 | + String beachUpdRecode(List<HistoryRecord> record); | |
| 22 | + /** | |
| 23 | + * 历史视频上传记录列表 | |
| 24 | + */ | |
| 25 | + List<HistoryRecord> getHistoryRecordList(); | |
| 26 | + /** | |
| 27 | + * 根据stream获取历史视频对象 | |
| 28 | + */ | |
| 29 | + HistoryRecord getHistoryRecord(String stream); | |
| 30 | + | |
| 16 | 31 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/vmanager/jt1078/platform/service/impl/HisToryRecordServiceImpl.java
| ... | ... | @@ -4,8 +4,14 @@ import com.genersoft.iot.vmp.vmanager.jt1078.platform.domain.HistoryRecord; |
| 4 | 4 | import com.genersoft.iot.vmp.vmanager.jt1078.platform.mapper.HisToryRecordMapper; |
| 5 | 5 | import com.genersoft.iot.vmp.vmanager.jt1078.platform.service.HisToryRecordService; |
| 6 | 6 | import lombok.AllArgsConstructor; |
| 7 | +import org.springframework.data.redis.cache.RedisCache; | |
| 7 | 8 | import org.springframework.stereotype.Service; |
| 8 | 9 | |
| 10 | +import javax.annotation.Resource; | |
| 11 | +import java.util.Collections; | |
| 12 | +import java.util.Date; | |
| 13 | +import java.util.List; | |
| 14 | + | |
| 9 | 15 | /** |
| 10 | 16 | * @Author WangXin |
| 11 | 17 | * @Data 2025/2/22 |
| ... | ... | @@ -16,12 +22,35 @@ import org.springframework.stereotype.Service; |
| 16 | 22 | public class HisToryRecordServiceImpl implements HisToryRecordService { |
| 17 | 23 | |
| 18 | 24 | private final HisToryRecordMapper hisToryRecordMapper; |
| 25 | + @Resource | |
| 26 | + private RedisCache redisCache; | |
| 19 | 27 | /** |
| 20 | 28 | * 添加历史下载记录 |
| 21 | 29 | * @param record 历史视频下载记录对象 |
| 22 | 30 | */ |
| 23 | 31 | @Override |
| 24 | - public String addRecode(HistoryRecord record) { | |
| 25 | - return hisToryRecordMapper.addRecode(record) > 0 ? "success" : "fail"; | |
| 32 | + public String addOrUpdRecode(HistoryRecord record) { | |
| 33 | + return hisToryRecordMapper.addOrUpdRecode(record) > 0 ? "success" : "fail"; | |
| 34 | + } | |
| 35 | + /** | |
| 36 | + * 修改历史视频记录 | |
| 37 | + */ | |
| 38 | + @Override | |
| 39 | + public String beachUpdRecode(List<HistoryRecord> record) { | |
| 40 | + return hisToryRecordMapper.beachUpdRecode(record) > 0 ? "success" : "fail"; | |
| 41 | + } | |
| 42 | + /** | |
| 43 | + * 历史视频上传记录列表 | |
| 44 | + */ | |
| 45 | + @Override | |
| 46 | + public List<HistoryRecord> getHistoryRecordList() { | |
| 47 | + return hisToryRecordMapper.getHistoryRecordList(); | |
| 48 | + } | |
| 49 | + /** | |
| 50 | + * 根据stream获取历史视频对象 | |
| 51 | + */ | |
| 52 | + @Override | |
| 53 | + public HistoryRecord getHistoryRecord(String stream) { | |
| 54 | + return hisToryRecordMapper.getHistoryRecord(stream); | |
| 26 | 55 | } |
| 27 | 56 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/vmanager/util/FtpUtils.java
| ... | ... | @@ -2,6 +2,8 @@ package com.genersoft.iot.vmp.vmanager.util; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.extra.ftp.FtpException; |
| 4 | 4 | import com.genersoft.iot.vmp.vmanager.jt1078.platform.config.FtpConfigBean; |
| 5 | +import com.genersoft.iot.vmp.vmanager.jt1078.platform.domain.HistoryEnum; | |
| 6 | +import com.genersoft.iot.vmp.vmanager.jt1078.platform.domain.HistoryRecord; | |
| 5 | 7 | import lombok.RequiredArgsConstructor; |
| 6 | 8 | import lombok.extern.log4j.Log4j2; |
| 7 | 9 | import org.apache.commons.lang3.StringUtils; |
| ... | ... | @@ -10,11 +12,12 @@ import org.apache.commons.net.ftp.FTPFile; |
| 10 | 12 | import org.apache.commons.net.ftp.FTPReply; |
| 11 | 13 | import org.springframework.stereotype.Component; |
| 12 | 14 | |
| 15 | +import java.io.ByteArrayOutputStream; | |
| 13 | 16 | import java.io.IOException; |
| 14 | 17 | import java.io.InputStream; |
| 15 | 18 | import java.net.SocketException; |
| 16 | -import java.util.LinkedList; | |
| 17 | -import java.util.List; | |
| 19 | +import java.text.SimpleDateFormat; | |
| 20 | +import java.util.*; | |
| 18 | 21 | |
| 19 | 22 | /** |
| 20 | 23 | * FTP服务工具类 |
| ... | ... | @@ -214,6 +217,10 @@ public class FtpUtils { |
| 214 | 217 | return is; |
| 215 | 218 | } |
| 216 | 219 | |
| 220 | + public InputStream downloadFile(HistoryRecord historyRecord){ | |
| 221 | + return getInputStreamOfFtpFile(historyRecord.getPath()); | |
| 222 | + } | |
| 223 | + | |
| 217 | 224 | /** |
| 218 | 225 | * 删除ftp文件 |
| 219 | 226 | * @param ftpFilePath ftp文件路径,根目录开始 |
| ... | ... | @@ -233,5 +240,120 @@ public class FtpUtils { |
| 233 | 240 | return result; |
| 234 | 241 | } |
| 235 | 242 | |
| 243 | + /** | |
| 244 | + * 验证指定目录下是否存在至少一个文件。 | |
| 245 | + * | |
| 246 | + * @param directoryPath 目录路径 | |
| 247 | + * @return 如果目录中存在至少一个文件,则返回文件名;否则返回null | |
| 248 | + */ | |
| 249 | + public String checkDirectoryContainsFiles(String directoryPath){ | |
| 250 | + FTPClient ftpClient = getFTPClient(); | |
| 251 | + try { | |
| 252 | + // 改变工作目录 | |
| 253 | + if (ftpClient.changeWorkingDirectory(directoryPath)) { | |
| 254 | + // 列出目录中的所有文件和子目录 | |
| 255 | + FTPFile[] files = ftpClient.listFiles(); | |
| 256 | + for (FTPFile file : files) { | |
| 257 | + if (file.isFile()) { // 检查是否为文件 | |
| 258 | + return file.getName(); // 找到了至少一个文件 | |
| 259 | + } | |
| 260 | + } | |
| 261 | + } else { | |
| 262 | + log.error("无法进入目录:{}", directoryPath); | |
| 263 | + return null; | |
| 264 | + } | |
| 265 | + } catch (IOException e) { | |
| 266 | + log.error("发生错误:{}", e.getMessage()); | |
| 267 | + throw new FtpException(String.format("发生错误:{%s}", e.getMessage())); | |
| 268 | + } finally { | |
| 269 | + closeConnect(ftpClient); | |
| 270 | + } | |
| 271 | + return null; // 没有找到任何文件 | |
| 272 | + } | |
| 273 | + | |
| 274 | + public boolean checkHistoryVideo(HistoryRecord record) { | |
| 275 | + String fileName = checkDirectoryContainsFiles(record.getPath()); | |
| 276 | + boolean flag = fileName != null; | |
| 277 | + if (flag){ | |
| 278 | + record.setPath( String.join(record.getPath(),"/",fileName)); | |
| 279 | + record.setStatus(HistoryEnum.UPLOADED.getCode()); | |
| 280 | + } | |
| 281 | + return flag; | |
| 282 | + } | |
| 283 | + /** | |
| 284 | + * 批量删除指定路径下的文件和文件夹。 | |
| 285 | + */ | |
| 286 | + public List<HistoryRecord> batchDelete(List<HistoryRecord> historyRecordList) throws IOException { | |
| 287 | + HashMap<String, List<HistoryRecord>> recordMap = new HashMap<>(); | |
| 288 | + recordMap.put("true",new ArrayList<>(historyRecordList)); | |
| 289 | + recordMap.put("false",new ArrayList<>(historyRecordList)); | |
| 290 | + for (HistoryRecord historyRecord : historyRecordList) { | |
| 291 | + boolean b = deleteRecursive(historyRecord.getPath()); | |
| 292 | + if (!b) { | |
| 293 | + historyRecord.setPath(null); | |
| 294 | + historyRecord.setStatus(HistoryEnum.NOT_UPLOADED.getCode()); | |
| 295 | + historyRecord.setCreateTime(new Date()); | |
| 296 | + recordMap.get("true").add(historyRecord); | |
| 297 | + }else { | |
| 298 | + recordMap.get("false").add(historyRecord); | |
| 299 | + } | |
| 300 | + } | |
| 301 | + log.debug("====================== FTP 删除过期文件 ====================="); | |
| 302 | + String format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); | |
| 303 | + log.debug("==> {} 成功删除文件 {} 个 ", format,recordMap.get("true").size()); | |
| 304 | + log.debug("==> {} 未删除文件 {} 个", format,recordMap.get("false").size()); | |
| 305 | + log.debug("==========================================================="); | |
| 306 | + return recordMap.get("false"); | |
| 307 | + } | |
| 308 | + | |
| 309 | + /** | |
| 310 | + * 递归删除指定路径下的文件和文件夹。 | |
| 311 | + * | |
| 312 | + * @param path 文件或文件夹路径 | |
| 313 | + */ | |
| 314 | + private boolean deleteRecursive(String path) throws IOException { | |
| 315 | + FTPClient ftpClient = getFTPClient(); | |
| 316 | + FTPFile[] files = ftpClient.listFiles(path); | |
| 317 | + if (files == null || files.length == 0) { | |
| 318 | + // 如果路径不存在或者为空,尝试删除它 | |
| 319 | + if (!ftpClient.removeDirectory(path)) { | |
| 320 | + if (!ftpClient.deleteFile(path)) { | |
| 321 | + log.error("无法删除: {}", path); | |
| 322 | + return false; | |
| 323 | + } else { | |
| 324 | + log.info("已删除文件: {}", path); | |
| 325 | + return true; | |
| 326 | + } | |
| 327 | + } else { | |
| 328 | + log.info("已删除目录: {}", path); | |
| 329 | + return true; | |
| 330 | + } | |
| 331 | + } | |
| 332 | + // 删除文件夹中的所有文件和子文件夹 | |
| 333 | + for (FTPFile file : files) { | |
| 334 | + String filePath = path.endsWith("/") ? path + file.getName() : path + "/" + file.getName(); | |
| 335 | + if (file.isDirectory()) { | |
| 336 | + deleteRecursive(filePath); // 递归删除子文件夹 | |
| 337 | + } else { | |
| 338 | + if (!ftpClient.deleteFile(filePath)) { | |
| 339 | + log.error("无法删除文件: {}", filePath); | |
| 340 | + return false; | |
| 341 | + } else { | |
| 342 | + log.info("已删除目录: {}", path); | |
| 343 | + return true; | |
| 344 | + } | |
| 345 | + } | |
| 346 | + } | |
| 347 | + // 最后尝试删除当前文件夹(如果它是空的) | |
| 348 | + if (!ftpClient.removeDirectory(path)) { | |
| 349 | + log.error("无法删除目录: {}", path); | |
| 350 | + return false; | |
| 351 | + } else { | |
| 352 | + log.info("已删除目录: {}", path); | |
| 353 | + return true; | |
| 354 | + } | |
| 355 | + } | |
| 356 | + | |
| 357 | + | |
| 236 | 358 | } |
| 237 | 359 | ... | ... |
src/main/resources/mapper/HisToryRecordMapper.xml
| ... | ... | @@ -4,10 +4,142 @@ |
| 4 | 4 | "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
| 5 | 5 | <mapper namespace="com.genersoft.iot.vmp.vmanager.jt1078.platform.mapper.HisToryRecordMapper"> |
| 6 | 6 | |
| 7 | - <insert id="addRecode"> | |
| 7 | + <resultMap id="HISTORY_RECORD_LIST_MAP" type="com.genersoft.iot.vmp.vmanager.jt1078.platform.domain.HistoryRecord"> | |
| 8 | + <result property="id" column="id" /> | |
| 9 | + <result property="name" column="name" /> | |
| 10 | + <result property="sim" column="sim" /> | |
| 11 | + <result property="channel" column="channel" /> | |
| 12 | + <result property="startTime" column="start_time" /> | |
| 13 | + <result property="endTime" column="end_time" /> | |
| 14 | + <result property="status" column="status" /> | |
| 15 | + <result property="ip" column="ip" /> | |
| 16 | + <result property="port" column="port" /> | |
| 17 | + <result property="username" column="username" /> | |
| 18 | + <result property="password" column="password" /> | |
| 19 | + <result property="path" column="path" /> | |
| 20 | + <result property="createTime" column="create_time" /> | |
| 21 | + <result property="updateTime" column="update_time" /> | |
| 22 | + <result property="createBy" column="create_by" /> | |
| 23 | + <result property="updateBy" column="update_by" /> | |
| 24 | + <result property="remark" column="remark" /> | |
| 25 | + </resultMap> | |
| 26 | + | |
| 27 | + <sql id="HISTORY_RECORD_LIST_SQL"> | |
| 28 | + SELECT | |
| 29 | + id, | |
| 30 | + name, | |
| 31 | + sim, | |
| 32 | + channel, | |
| 33 | + start_time, | |
| 34 | + end_time, | |
| 35 | + status, | |
| 36 | + ip, | |
| 37 | + port, | |
| 38 | + username, | |
| 39 | + password, | |
| 40 | + path, | |
| 41 | + create_time, | |
| 42 | + update_time, | |
| 43 | + create_by, | |
| 44 | + update_by, | |
| 45 | + remark | |
| 46 | + FROM `wvp_history_record` | |
| 47 | + </sql> | |
| 48 | + | |
| 49 | + <insert id="addOrUpdRecode"> | |
| 8 | 50 | INSERT INTO `wvp_history_record` |
| 9 | - (`id`, `name`, `sim`, `channel`, `status`, `ip`, `port`, `username`, `password`, `create_time`, `update_time`, `create_by`, `update_by`, `remark`) | |
| 10 | - VALUES | |
| 11 | - (#{id}, #{name}, #{sim}, #{channel}, #{status}, #{ip}, #{port}, #{username}, #{password}, #{createTime}, #{updateTime}, #{createBy}, #{updateBy}, #{remark}) | |
| 51 | + <trim prefix="(" suffix=")" suffixOverrides=","> | |
| 52 | + <if test=" id != null and id != '' ">`id` ,</if> | |
| 53 | + <if test=" sim != null and sim != '' ">`sim` ,</if> | |
| 54 | + <if test=" channel != null and channel != '' ">`channel` ,</if> | |
| 55 | + <if test=" startTime != null and startTime != '' ">`strat_time` ,</if> | |
| 56 | + <if test=" endTime != null and endTime != '' ">`end_time` ,</if> | |
| 57 | + <if test=" status != null and status != '' ">`status` ,</if> | |
| 58 | + <if test=" ip != null and ip != '' ">`ip` ,</if> | |
| 59 | + <if test=" port != null ">`port` ,</if> | |
| 60 | + <if test=" username != null and username != '' ">`username` ,</if> | |
| 61 | + <if test=" password != null and password != '' ">`password` ,</if> | |
| 62 | + <if test=" path != null and path != '' ">`path` ,</if> | |
| 63 | + <if test=" createTime != null ">`create_time` ,</if> | |
| 64 | + <if test=" updateTime != null ">`update_time` ,</if> | |
| 65 | + <if test=" createBy != null and createBy != '' ">`create_by` ,</if> | |
| 66 | + <if test=" updateBy != null and updateBy != '' ">`update_by` ,</if> | |
| 67 | + <if test=" remark != null and remark != '' ">`remark` ,</if> | |
| 68 | + </trim> | |
| 69 | + VALUES | |
| 70 | + <trim prefix="(" suffix=")" suffixOverrides=","> | |
| 71 | + <if test=" id != null and id != '' "> #{id}, </if> | |
| 72 | + <if test=" name != null and name != '' "> #{name}, </if> | |
| 73 | + <if test=" sim != null and sim != '' "> #{sim}, </if> | |
| 74 | + <if test=" channel != null and channel != '' "> #{channel}, </if> | |
| 75 | + <if test=" startTime != null and startTime != '' "> #{startTime}, </if> | |
| 76 | + <if test=" endTime != null and endTime != '' "> #{endTime}, </if> | |
| 77 | + <if test=" status != null and status != '' "> #{status}, </if> | |
| 78 | + <if test=" ip != null and ip != '' "> #{ip}, </if> | |
| 79 | + <if test=" port != null "> #{port}, </if> | |
| 80 | + <if test=" username != null and username != '' "> #{username}, </if> | |
| 81 | + <if test=" password != null and password != '' "> #{password}, </if> | |
| 82 | + <if test=" path != null and path != '' "> #{path}, </if> | |
| 83 | + <if test=" createTime != null "> #{createTime} , </if> | |
| 84 | + <if test=" updateTime != null "> #{updateTime}, </if> | |
| 85 | + <if test=" createBy != null and createBy != '' "> #{createBy}, </if> | |
| 86 | + <if test=" updateBy != null and updateBy != '' "> #{updateBy}, </if> | |
| 87 | + <if test=" remark != null and remark != '' "> #{remark}, </if> | |
| 88 | + </trim> | |
| 89 | + ON DUPLICATE KEY UPDATE | |
| 90 | + <trim suffixOverrides=","> | |
| 91 | + <if test=" id != null and id != '' "> `id` = #{id}, </if> | |
| 92 | + <if test=" name != null and name != '' "> `name` = #{name}, </if> | |
| 93 | + <if test=" sim != null and sim != '' "> `sim` = #{sim}, </if> | |
| 94 | + <if test=" channel != null and channel != '' "> `channel` = #{channel}, </if> | |
| 95 | + <if test=" startTime != null and startTime != '' "> `strat_time` = #{startTime}, </if> | |
| 96 | + <if test=" endTime != null and endTime != '' "> `end_time` = #{endTime}, </if> | |
| 97 | + <if test=" status != null and status != '' "> `status` = #{status}, </if> | |
| 98 | + <if test=" ip != null and ip != '' "> `ip` = #{ip}, </if> | |
| 99 | + <if test=" port != null "> `port` = #{port}, </if> | |
| 100 | + <if test=" username != null and username != '' "> `username` = #{username}, </if> | |
| 101 | + <if test=" password != null and password != '' "> `password` = #{password}, </if> | |
| 102 | + <if test=" path != null and path != '' "> `path` = #{path}, </if> | |
| 103 | + <if test=" createTime != null "> `create_time` = #{createTime} , </if> | |
| 104 | + <if test=" updateTime != null "> `update_time` = #{updateTime}, </if> | |
| 105 | + <if test=" createBy != null and createBy != '' "> `create_by` = #{createBy}, </if> | |
| 106 | + <if test=" updateBy != null and updateBy != '' "> `update_by` = #{updateBy}, </if> | |
| 107 | + <if test=" remark != null and remark != '' "> `remark` = #{remark}, </if> | |
| 108 | + </trim> | |
| 12 | 109 | </insert> |
| 110 | + | |
| 111 | + <update id="beachUpdRecode"> | |
| 112 | + <foreach collection="list" item="item" separator=";"> | |
| 113 | + UPDATE `wvp_history_record` | |
| 114 | + <set> | |
| 115 | + <if test="item.sim != null and item.sim != '' ">`sim` = #{item.sim}, </if> | |
| 116 | + <if test="item.channel != null and item.channel != '' ">`channel` = #{item.channel}, </if> | |
| 117 | + <if test="item.startTime != null and item.startTime != '' ">`strat_time` = #{item.startTime}, </if> | |
| 118 | + <if test="item.endTime != null and item.endTime != '' ">`end_time` = #{item.endTime}, </if> | |
| 119 | + <if test="item.status != null and item.status != '' ">`status` = #{item.status}, </if> | |
| 120 | + <if test="item.ip != null and item.ip != '' ">`ip` = #{item.ip}, </if> | |
| 121 | + <if test="item.port != null ">`port` = #{item.port}, </if> | |
| 122 | + <if test="item.username != null and item.username != '' ">`username` = #{item.username}, </if> | |
| 123 | + <if test="item.password != null and item.password != '' ">`password` = #{item.password}, </if> | |
| 124 | + <if test="item.path != null and item.path != '' ">`path` = #{item.path}, </if> | |
| 125 | + <if test="item.path == null ">`path` = NULL, </if> | |
| 126 | + <if test="item.createTime != null ">`create_time` = #{item.createTime} , </if> | |
| 127 | + <if test="item.updateTime != null ">`update_time` = #{item.updateTime}, </if> | |
| 128 | + <if test="item.createBy != null and item.createBy != '' ">`create_by` = #{item.createBy}, </if> | |
| 129 | + <if test="item.updateBy != null and item.updateBy != '' ">`update_by` = #{item.updateBy}, </if> | |
| 130 | + <if test="item.remark != null and item.remark != '' ">`remark` = #{item.remark}, </if> | |
| 131 | + </set> | |
| 132 | + WHERE | |
| 133 | + `name` = #{item.name} | |
| 134 | + </foreach> | |
| 135 | + </update> | |
| 136 | + <select id="getHistoryRecordList" | |
| 137 | + resultMap="HISTORY_RECORD_LIST_MAP"> | |
| 138 | + <include refid="HISTORY_RECORD_LIST_SQL" /> | |
| 139 | + </select> | |
| 140 | + <select id="getHistoryRecord" | |
| 141 | + resultMap="HISTORY_RECORD_LIST_MAP"> | |
| 142 | + <include refid="HISTORY_RECORD_LIST_SQL" /> | |
| 143 | + WHERE `name` = #{stream} | |
| 144 | + </select> | |
| 13 | 145 | </mapper> | ... | ... |
web_src/src/components/HistoricalRecord.vue
| ... | ... | @@ -45,8 +45,11 @@ |
| 45 | 45 | </div> |
| 46 | 46 | </div> |
| 47 | 47 | </el-main> |
| 48 | - <el-footer style="width: 100%;height: 30%;"> | |
| 49 | - <historical-data :history-data="historyData" @click="clickHistoricalPlay" /> | |
| 48 | + <el-footer style="width: 100%;height: 30%;background-color: white"> | |
| 49 | + <history-search-table :table-data="historyData" | |
| 50 | + @playHistoryVideo="clickHistoricalPlay" | |
| 51 | + @downloadHistoryVideo="downloadFunction" | |
| 52 | + @uploadHistoryVideo="uploadHistoryVideo"/> | |
| 50 | 53 | </el-footer> |
| 51 | 54 | </el-container> |
| 52 | 55 | </el-container> |
| ... | ... | @@ -62,15 +65,18 @@ import {parseTime} from "../../utils/ruoyi"; |
| 62 | 65 | import HistoricalData from "./JT1078Components/historical/HistoricalDataTree.vue"; |
| 63 | 66 | import HistoryList from "./JT1078Components/HistoryData.vue"; |
| 64 | 67 | import Device1078Tree from "./JT1078Components/deviceList/Device1078Tree.vue"; |
| 68 | +import HistorySearchTable from "./JT1078Components/HistorySearchTable.vue"; | |
| 65 | 69 | |
| 66 | 70 | export default { |
| 67 | 71 | //import引入的组件需要注入到对象中才能使用" |
| 68 | - components: {HistoryList, Device1078Tree, HistoricalData, CarTree, player}, | |
| 72 | + components: {HistorySearchTable, HistoryList, Device1078Tree, HistoricalData, CarTree, player}, | |
| 69 | 73 | props: {}, |
| 70 | 74 | data() { |
| 71 | 75 | //这里存放数据" |
| 72 | 76 | return { |
| 73 | - historyData: [{name: "aaaa"},{name: "bbbb"}], | |
| 77 | + //历史视频列表定时器 | |
| 78 | + historyTimer: null, | |
| 79 | + historyData: [], | |
| 74 | 80 | targetValue: [], |
| 75 | 81 | //源列表数据 |
| 76 | 82 | sourceValue: [], |
| ... | ... | @@ -126,13 +132,13 @@ export default { |
| 126 | 132 | * 树点击事件 |
| 127 | 133 | */ |
| 128 | 134 | nodeClick(data, node) { |
| 129 | - if (data.children === undefined && data) { | |
| 130 | - let split = data.id.split("_"); | |
| 131 | - if (split.length === 3){ | |
| 132 | - this.sim_channel = split[1] + '_' + split[2] | |
| 133 | - } else { | |
| 134 | - console.log("node click ==> ",data) | |
| 135 | - } | |
| 135 | + if (data.children === undefined && data) { | |
| 136 | + let split = data.id.split("_"); | |
| 137 | + if (split.length === 3) { | |
| 138 | + this.sim_channel = split[1] + '_' + split[2] | |
| 139 | + } else { | |
| 140 | + console.log("node click ==> ", data) | |
| 141 | + } | |
| 136 | 142 | } |
| 137 | 143 | }, |
| 138 | 144 | /** |
| ... | ... | @@ -258,16 +264,50 @@ export default { |
| 258 | 264 | this.playHistoryItem(data) |
| 259 | 265 | }, |
| 260 | 266 | /** |
| 267 | + * 上传历史视频 | |
| 268 | + */ | |
| 269 | + uploadHistoryVideo(data){ | |
| 270 | + this.$axios({ | |
| 271 | + method: 'get', | |
| 272 | + url: '/api/jt1078/query/history/list/' + data.sim + '/' + data.channel + "/" + this.startTime + "/" + this.endTime | |
| 273 | + }).then(res => { | |
| 274 | + if (res.data.data.code === -1){ | |
| 275 | + this.$message.success("视频开始上传,请等待") | |
| 276 | + this.searchHistoryList() | |
| 277 | + } | |
| 278 | + }).cache(err => { | |
| 279 | + console.log(err) | |
| 280 | + this.$message.error("视频上传失败,请联系管理员") | |
| 281 | + }) | |
| 282 | + }, | |
| 283 | + /** | |
| 261 | 284 | * 下载历史视频 |
| 262 | 285 | */ |
| 263 | - downloadFunction() { | |
| 264 | - this.getDateTime(); | |
| 265 | - console.log(this.downloadURL); | |
| 266 | - if (this.isEmpty(this.downloadURL)) { | |
| 267 | - return; | |
| 268 | - } | |
| 269 | - window.open(this.downloadURL, "_download"); | |
| 286 | + downloadFunction(data) { | |
| 287 | + this.$axios({ | |
| 288 | + url: `/api/jt1078/query/history/download/${data.name}`, // 请求URL | |
| 289 | + method: 'GET', // 将FTP文件路径作为参数传递 | |
| 290 | + responseType: 'blob', // 告知axios以二进制数据流的形式处理响应 | |
| 291 | + }).then((response) => { | |
| 292 | + const blob = new Blob([response.data], { type: 'application/octet-stream' }); | |
| 293 | + const link = document.createElement('a'); | |
| 294 | + link.href = window.URL.createObjectURL(blob); | |
| 295 | + link.download = ftpFilePath.substring(ftpFilePath.lastIndexOf('/') + 1); // 设置下载文件名 | |
| 296 | + document.body.appendChild(link); | |
| 297 | + link.click(); | |
| 298 | + document.body.removeChild(link); | |
| 299 | + }).catch(error => { | |
| 300 | + console.error("下载失败", error); | |
| 301 | + }); | |
| 270 | 302 | }, |
| 303 | + // downloadFunction(data) { | |
| 304 | + // this.getDateTime(); | |
| 305 | + // console.log(this.downloadURL); | |
| 306 | + // if (this.isEmpty(this.downloadURL)) { | |
| 307 | + // return; | |
| 308 | + // } | |
| 309 | + // window.open(this.downloadURL, "_download"); | |
| 310 | + // }, | |
| 271 | 311 | /** |
| 272 | 312 | * 搜索历史视频 |
| 273 | 313 | */ |
| ... | ... | @@ -302,7 +342,6 @@ export default { |
| 302 | 342 | url: '/api/jt1078/query/history/list/' + sim + '/' + channel + "/" + this.startTime + "/" + this.endTime |
| 303 | 343 | }).then( |
| 304 | 344 | res => { |
| 305 | - console.log(res.data) | |
| 306 | 345 | let items = res.data.data.obj.data.items; |
| 307 | 346 | if (res && res.data && res.data.data && res.data.data.obj && res.data.data.code == 1 && res.data.data.obj.data && items) { |
| 308 | 347 | for (let i in items) { |
| ... | ... | @@ -311,16 +350,17 @@ export default { |
| 311 | 350 | items[i].name = items[i].startTime + '-' + items[i].endTime; |
| 312 | 351 | } |
| 313 | 352 | this.historyData = items; |
| 314 | - this.loading = false | |
| 353 | + clearInterval(this.historyTimer); | |
| 354 | + this.historyTimer = setInterval(() => { | |
| 355 | + this.searchHistoryList(); | |
| 356 | + }, 10000); // | |
| 315 | 357 | } else if (res && res.data && res.data.data && res.data.data.msg) { |
| 316 | 358 | this.$message.error(res.data.data.msg); |
| 317 | - this.loading = false | |
| 318 | 359 | } else if (items === undefined) { |
| 319 | 360 | this.historyData = []; |
| 320 | - this.loading = false | |
| 321 | - } else { | |
| 322 | - this.loading = false | |
| 361 | + this.$message.warning("搜索历史列表为空") | |
| 323 | 362 | } |
| 363 | + this.loading = false | |
| 324 | 364 | }).cache(res => { |
| 325 | 365 | this.$message.error(res.msg); |
| 326 | 366 | this.loading = false |
| ... | ... | @@ -332,8 +372,8 @@ export default { |
| 332 | 372 | getDateTime() { |
| 333 | 373 | let date = this.date; |
| 334 | 374 | let timeList = this.timeList; |
| 335 | - console.log("date ",date) | |
| 336 | - console.log("timeList ",timeList) | |
| 375 | + console.log("date ", date) | |
| 376 | + console.log("timeList ", timeList) | |
| 337 | 377 | if (this.isEmpty(date)) { |
| 338 | 378 | this.$message.error("请选择日期") |
| 339 | 379 | return false; |
| ... | ... | @@ -401,7 +441,6 @@ export default { |
| 401 | 441 | this.closeLoading(); |
| 402 | 442 | }) |
| 403 | 443 | }, |
| 404 | - | |
| 405 | 444 | /** |
| 406 | 445 | * 实时访问播放地址 |
| 407 | 446 | * @param url | ... | ... |
web_src/src/components/JT1078Components/HistorySearchTable.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-table | |
| 3 | + ref="singleTable" | |
| 4 | + :data="tableData" | |
| 5 | + highlight-current-row | |
| 6 | + @current-change="handleCurrentChange" | |
| 7 | + height="250" | |
| 8 | + style="width: 100%"> | |
| 9 | + <el-table-column | |
| 10 | + type="index" | |
| 11 | + fixed | |
| 12 | + width="100"> | |
| 13 | + </el-table-column> | |
| 14 | + <el-table-column | |
| 15 | + property="name" | |
| 16 | + label="名称"> | |
| 17 | + </el-table-column> | |
| 18 | + <el-table-column | |
| 19 | + property="date" | |
| 20 | + label="日期"> | |
| 21 | + </el-table-column> | |
| 22 | + <el-table-column | |
| 23 | + property="status" | |
| 24 | + label="状态"> | |
| 25 | + </el-table-column> | |
| 26 | + <el-table-column | |
| 27 | + fixed="right" | |
| 28 | + label="操作"> | |
| 29 | + <template slot-scope="scope"> | |
| 30 | + <el-button icon="el-icon-video-play" size="small" style="border: none" @click="playHistoryVideo(scope.row)"></el-button> | |
| 31 | + <el-button v-if="scope.row.status === '1'" icon="el-icon-upload" size="small" style="border: none" @click="uploadHistoryVideo(scope.row)"></el-button> | |
| 32 | + <el-button v-if="scope.row.status === '2'" icon="el-icon-download" size="small" style="border: none" @click="downloadHistoryVideo(scope.row)"></el-button> | |
| 33 | + </template> | |
| 34 | + </el-table-column> | |
| 35 | + </el-table> | |
| 36 | +</template> | |
| 37 | + | |
| 38 | +<script> | |
| 39 | +//这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等), | |
| 40 | +//例如:import 《组件名称》 from '《组件路径》, | |
| 41 | + export default { | |
| 42 | + //import引入的组件需要注入到对象中才能使用" | |
| 43 | + components: {}, | |
| 44 | + props: { | |
| 45 | + tableData: { | |
| 46 | + type: Array, | |
| 47 | + default: [] | |
| 48 | + } | |
| 49 | + }, | |
| 50 | + data() { | |
| 51 | + //这里存放数据" | |
| 52 | + return { | |
| 53 | + currentRow: null | |
| 54 | + }; | |
| 55 | + }, | |
| 56 | + //计算属性 类似于data概念", | |
| 57 | + computed: {}, | |
| 58 | + //监控data中的数据变化", | |
| 59 | + watch: {}, | |
| 60 | + //方法集合", | |
| 61 | + methods: { | |
| 62 | + /** | |
| 63 | + * 选中行 | |
| 64 | + * @param val | |
| 65 | + */ | |
| 66 | + handleCurrentChange(val) { | |
| 67 | + this.currentRow = val; | |
| 68 | + }, | |
| 69 | + setCurrent(row) { | |
| 70 | + this.$refs.singleTable.setCurrentRow(row); | |
| 71 | + }, | |
| 72 | + /** | |
| 73 | + * 播放历史视频 | |
| 74 | + * @param row | |
| 75 | + */ | |
| 76 | + playHistoryVideo(row){ | |
| 77 | + this.$emit("playHistoryVideo", row); | |
| 78 | + }, | |
| 79 | + /** | |
| 80 | + * 上传历史视频 | |
| 81 | + * @param row | |
| 82 | + */ | |
| 83 | + uploadHistoryVideo(row){ | |
| 84 | + this.$emit('uploadHistoryVideo', row); | |
| 85 | + }, | |
| 86 | + /** | |
| 87 | + * 下载历史视频 | |
| 88 | + * @param row | |
| 89 | + */ | |
| 90 | + downloadHistoryVideo(row){ | |
| 91 | + this.$emit('downloadHistoryVideo', row); | |
| 92 | + } | |
| 93 | + }, | |
| 94 | + //生命周期 - 创建完成(可以访问当前this实例)", | |
| 95 | + created() { | |
| 96 | + }, | |
| 97 | + //生命周期 - 挂载完成(可以访问DOM元素)", | |
| 98 | + mounted() { | |
| 99 | + }, | |
| 100 | + beforeCreate() { | |
| 101 | + }, //生命周期 - 创建之前", | |
| 102 | + beforeMount() { | |
| 103 | + }, //生命周期 - 挂载之前", | |
| 104 | + beforeUpdate() { | |
| 105 | + }, //生命周期 - 更新之前", | |
| 106 | + updated() { | |
| 107 | + }, //生命周期 - 更新之后", | |
| 108 | + beforeDestroy() { | |
| 109 | + }, //生命周期 - 销毁之前", | |
| 110 | + destroyed() { | |
| 111 | + }, //生命周期 - 销毁完成", | |
| 112 | + activated() { | |
| 113 | + } //如果页面有keep-alive缓存功能,这个函数会触发", | |
| 114 | + }; | |
| 115 | +</script> | |
| 116 | +<style scoped> | |
| 117 | + | |
| 118 | +</style> | ... | ... |