diff --git a/admin/src/main/java/com/multictrl/common/constant/BusinessConstant.java b/admin/src/main/java/com/multictrl/common/constant/BusinessConstant.java index 35cc8ac..94e4a2d 100644 --- a/admin/src/main/java/com/multictrl/common/constant/BusinessConstant.java +++ b/admin/src/main/java/com/multictrl/common/constant/BusinessConstant.java @@ -81,4 +81,6 @@ public interface BusinessConstant { String DRC = "drc_"; String FILE_PATH = "file/"; String IMAGE_PATH = "image/"; + String VIDEO_PATH = "video/"; + String VIDEO_COVER_SUFFIX = "_cover.jpeg"; } diff --git a/admin/src/main/java/com/multictrl/modules/business/controller/FlightTaskController.java b/admin/src/main/java/com/multictrl/modules/business/controller/FlightTaskController.java index f70f4e0..369abaf 100644 --- a/admin/src/main/java/com/multictrl/modules/business/controller/FlightTaskController.java +++ b/admin/src/main/java/com/multictrl/modules/business/controller/FlightTaskController.java @@ -8,6 +8,8 @@ import com.multictrl.common.page.PageData; import com.multictrl.common.utils.Result; import com.multictrl.common.validator.AssertUtils; import com.multictrl.modules.business.dto.FlightTaskDTO; +import com.multictrl.modules.business.dto.MediaFileDTO; +import com.multictrl.modules.business.entity.SrsRecordEntity; import com.multictrl.modules.business.service.FlightTaskService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -38,11 +40,15 @@ public class FlightTaskController { @Operation(summary = "分页") @Parameters({ @Parameter(name = Constant.PAGE, description = "当前页码,从1开始"), - @Parameter(name = Constant.LIMIT, description = "每页显示记录数") + @Parameter(name = Constant.LIMIT, description = "每页显示记录数"), + @Parameter(name = "key", description = "机库/航线名称"), + @Parameter(name = "taskType", description = "任务类型 1:航线飞行 2:手动飞行 3:定时飞行"), + @Parameter(name = "routeType", description = "航线类型 航点waypoint"), + @Parameter(name = "taskStatus", description = "任务状态 0:进行中 -1:失败 1:完成 2:阻飞") }) @RequiresPermissions("bus:task:page") public Result> page(@Parameter(hidden = true) @RequestParam Map params) { - PageData page = flightTaskService.page(params); + PageData page = flightTaskService.pageList(params); return new Result>().ok(page); } @@ -76,4 +82,22 @@ public class FlightTaskController { return new Result>().ok(data); } + + @GetMapping("/getTaskMedia/{taskId}") + @Operation(summary = "媒体文件") + @RequiresPermissions("bus:task:taskMedia") + public Result> getTaskMedia(@PathVariable("taskId") String taskId) { + List data = flightTaskService.getTaskMedia(taskId); + + return new Result>().ok(data); + } + + @GetMapping("/getFlightVideo/{taskId}") + @Operation(summary = "飞行视频") + @RequiresPermissions("bus:task:flightVideo") + public Result> getFlightVideo(@PathVariable("taskId") String taskId) { + List data = flightTaskService.getFlightVideo(taskId); + + return new Result>().ok(data); + } } diff --git a/admin/src/main/java/com/multictrl/modules/business/controller/SrsController.java b/admin/src/main/java/com/multictrl/modules/business/controller/SrsController.java index 31de847..615da18 100644 --- a/admin/src/main/java/com/multictrl/modules/business/controller/SrsController.java +++ b/admin/src/main/java/com/multictrl/modules/business/controller/SrsController.java @@ -87,7 +87,7 @@ public class SrsController { @PostMapping("/on_dvr") public String onDvr(@RequestBody SrsCallBackDTO srsCallBack) { // log.info("on_dvr {}", srsCallBack); - + srsService.saveSrsRecord(srsCallBack); return "0"; } diff --git a/admin/src/main/java/com/multictrl/modules/business/dao/FlightTaskDao.java b/admin/src/main/java/com/multictrl/modules/business/dao/FlightTaskDao.java index ec650b5..9ac5d2d 100644 --- a/admin/src/main/java/com/multictrl/modules/business/dao/FlightTaskDao.java +++ b/admin/src/main/java/com/multictrl/modules/business/dao/FlightTaskDao.java @@ -7,10 +7,15 @@ import org.apache.ibatis.annotations.Mapper; /** * 飞行架次 * - * @author Sdy + * @author Sdy * @since 1.0.0 2026-05-02 */ @Mapper public interface FlightTaskDao extends BaseDao { - + + //获取最新的任务 + FlightTaskEntity getLatestRouteTask(String dockSn); + + //获取当前任务的上一条任务 + FlightTaskEntity getPreviousTask(String taskId, String dockSn); } \ No newline at end of file diff --git a/admin/src/main/java/com/multictrl/modules/business/dto/MediaFileDTO.java b/admin/src/main/java/com/multictrl/modules/business/dto/MediaFileDTO.java index fac905c..5aa9ab3 100644 --- a/admin/src/main/java/com/multictrl/modules/business/dto/MediaFileDTO.java +++ b/admin/src/main/java/com/multictrl/modules/business/dto/MediaFileDTO.java @@ -1,5 +1,6 @@ package com.multictrl.modules.business.dto; +import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @@ -67,4 +68,8 @@ public class MediaFileDTO implements Serializable { @Schema(description = "事件收到时间") private Date createTime; + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @Schema(description = "媒体路径") + private String url; } diff --git a/admin/src/main/java/com/multictrl/modules/business/entity/SrsRecordEntity.java b/admin/src/main/java/com/multictrl/modules/business/entity/SrsRecordEntity.java index 20d79b2..0e8e4d5 100644 --- a/admin/src/main/java/com/multictrl/modules/business/entity/SrsRecordEntity.java +++ b/admin/src/main/java/com/multictrl/modules/business/entity/SrsRecordEntity.java @@ -43,4 +43,11 @@ public class SrsRecordEntity { @TableField(fill = FieldFill.INSERT) private Date createDate; + + @TableField(exist = false) + private String url; + @TableField(exist = false) + private String fileName; + @TableField(exist = false) + private String cover; } \ No newline at end of file diff --git a/admin/src/main/java/com/multictrl/modules/business/service/FlightTaskService.java b/admin/src/main/java/com/multictrl/modules/business/service/FlightTaskService.java index 841499b..88426b8 100644 --- a/admin/src/main/java/com/multictrl/modules/business/service/FlightTaskService.java +++ b/admin/src/main/java/com/multictrl/modules/business/service/FlightTaskService.java @@ -1,12 +1,16 @@ package com.multictrl.modules.business.service; import cn.hutool.json.JSONObject; +import com.multictrl.common.page.PageData; import com.multictrl.common.service.CrudService; import com.multictrl.modules.business.dto.FlightTaskDTO; +import com.multictrl.modules.business.dto.MediaFileDTO; import com.multictrl.modules.business.entity.FlightTaskEntity; +import com.multictrl.modules.business.entity.SrsRecordEntity; import com.multictrl.modules.business.influxdb.UavReport; import java.util.List; +import java.util.Map; /** * 飞行架次 @@ -16,6 +20,9 @@ import java.util.List; */ public interface FlightTaskService extends CrudService { + //分页列表 + PageData pageList(Map params); + //新增架次信息 void addRouteTask(String taskId, String dockSn, Long routeId);//航线任务 @@ -29,4 +36,13 @@ public interface FlightTaskService extends CrudService getFlightTrack(String taskId); + + //获取架次媒体 + List getTaskMedia(String taskId); + + //飞行视频 + List getFlightVideo(String taskId); + + //获取上一条任务 + FlightTaskDTO getPreviousTask(String taskId, String dockSn); } \ No newline at end of file diff --git a/admin/src/main/java/com/multictrl/modules/business/service/SrsService.java b/admin/src/main/java/com/multictrl/modules/business/service/SrsService.java index 180fc26..56919dc 100644 --- a/admin/src/main/java/com/multictrl/modules/business/service/SrsService.java +++ b/admin/src/main/java/com/multictrl/modules/business/service/SrsService.java @@ -1,6 +1,7 @@ package com.multictrl.modules.business.service; import cn.hutool.json.JSONArray; +import com.multictrl.modules.business.dao.SrsRecordDao; import com.multictrl.modules.business.dto.SrsCallBackDTO; import com.multictrl.modules.business.entity.SrsRecordEntity; @@ -26,4 +27,7 @@ public interface SrsService { //获取飞机推流地址 String getUavPushLiveUrl(String dockSn); + + //获取dao + SrsRecordDao getDao(); } diff --git a/admin/src/main/java/com/multictrl/modules/business/service/impl/FlightTaskServiceImpl.java b/admin/src/main/java/com/multictrl/modules/business/service/impl/FlightTaskServiceImpl.java index 96aa82b..c1521c0 100644 --- a/admin/src/main/java/com/multictrl/modules/business/service/impl/FlightTaskServiceImpl.java +++ b/admin/src/main/java/com/multictrl/modules/business/service/impl/FlightTaskServiceImpl.java @@ -2,6 +2,7 @@ package com.multictrl.modules.business.service.impl; import cn.hutool.core.date.DateTime; import cn.hutool.core.date.DateUtil; +import cn.hutool.core.io.FileUtil; import cn.hutool.json.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.google.common.collect.Maps; @@ -9,20 +10,16 @@ import com.multictrl.common.constant.BusinessConstant; import com.multictrl.common.constant.FlightTaskStatus; import com.multictrl.common.exception.ErrorCode; import com.multictrl.common.exception.RenException; +import com.multictrl.common.page.PageData; import com.multictrl.common.service.impl.CrudServiceImpl; import cn.hutool.core.util.StrUtil; -import com.multictrl.common.utils.CacheUtils; -import com.multictrl.common.utils.ConvertUtils; -import com.multictrl.common.utils.DateUtils; -import com.multictrl.common.utils.Spatial4jUtils; +import com.multictrl.common.utils.*; import com.multictrl.modules.business.dao.DockDeviceDao; import com.multictrl.modules.business.dao.FlightTaskDao; import com.multictrl.modules.business.dto.FlightTaskDTO; +import com.multictrl.modules.business.dto.MediaFileDTO; import com.multictrl.modules.business.dto.RouteDTO; -import com.multictrl.modules.business.entity.DockDeviceEntity; -import com.multictrl.modules.business.entity.DockEntity; -import com.multictrl.modules.business.entity.FlightTaskEntity; -import com.multictrl.modules.business.entity.MediaFileEntity; +import com.multictrl.modules.business.entity.*; import com.multictrl.modules.business.influxdb.UavReport; import com.multictrl.modules.business.service.*; import com.multictrl.modules.security.user.SecurityUser; @@ -45,17 +42,44 @@ public class FlightTaskServiceImpl extends CrudServiceImpl getWrapper(Map params) { String id = (String) params.get("id"); + String key = (String) params.get("key"); + String taskType = (String) params.get("taskType"); + String routeType = (String) params.get("routeType"); + String taskStatus = (String) params.get("taskStatus"); QueryWrapper wrapper = new QueryWrapper<>(); wrapper.eq(StrUtil.isNotBlank(id), "id", id); + wrapper.and(StrUtil.isNotBlank(key), v -> + v.like("dock_name", key).or().like("route_name", key)); + if (StrUtil.isNotBlank(taskType)) { + wrapper.eq("task_type", Integer.parseInt(taskType)); + } + if (StrUtil.isNotBlank(taskStatus)) { + wrapper.eq("task_status", Integer.parseInt(taskStatus)); + } + wrapper.eq(StrUtil.isNotBlank(routeType), "route_type", routeType); + wrapper.orderByDesc("create_date"); return wrapper; } + @Override + public PageData pageList(Map params) { + PageData page = page(params); + for (FlightTaskDTO dto : page.getList()) { + if (dto.getMediaNum() == null || dto.getMediaNum() == 0) { + Long mediaNum = mediaFileService.getDao().selectCount(new QueryWrapper().eq("task_id", dto.getTaskId())); + dto.setMediaNum(mediaNum.intValue()); + } + } + return page; + } + @Override public void addRouteTask(String taskId, String dockSn, Long routeId) { FlightTaskEntity entity = new FlightTaskEntity(); @@ -221,6 +245,48 @@ public class FlightTaskServiceImpl extends CrudServiceImpl getTaskMedia(String taskId) { + Map params = new HashMap<>(); + params.put("taskId", taskId); + List list = mediaFileService.list(params); + for (MediaFileDTO dto : list) { + String objectKey = dto.getObjectKey(); + dto.setUrl(BusinessConstant.IMAGE_PATH + BusinessConstant.DOCK_MEDIA_BUCKET + "/" + objectKey); + } + return list; + } + + @Override + public List getFlightVideo(String taskId) { + FlightTaskEntity entity = baseDao.selectById(taskId); + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.eq("app", entity.getDockSn()); + FlightTaskDTO previousTask = this.getPreviousTask(taskId, entity.getDockSn()); + if (previousTask == null) { + wrapper.gt("record_date", DateUtils.getBeforeHour(entity.getCreateDate(), -8)); + } else { + wrapper.between("record_date", DateUtils.getBeforeHour(entity.getCreateDate(), -8), + DateUtils.getBeforeHour(previousTask.getCreateDate(), -8)); + } + wrapper.ne("stream", "dock"); + wrapper.orderByAsc("record_date"); + List list = srsService.getDao().selectList(wrapper); + for (SrsRecordEntity srsRecordEntity : list) { + srsRecordEntity.setRecordDate(DateUtils.getBeforeHour(srsRecordEntity.getRecordDate(), 8)); + srsRecordEntity.setUrl(BusinessConstant.VIDEO_PATH + srsRecordEntity.getFile()); + srsRecordEntity.setCover(BusinessConstant.IMAGE_PATH + getVideoCover(srsRecordEntity.getFile(), srsRecordEntity.getStreamUrl())); + } + + return list; + } + + @Override + public FlightTaskDTO getPreviousTask(String taskId, String dockSn) { + FlightTaskEntity previousTask = baseDao.getPreviousTask(taskId, dockSn); + return ConvertUtils.sourceToTarget(previousTask, FlightTaskDTO.class); + } + //添加dock信息 private void addDockInfo(FlightTaskEntity entity, String dockSn) { entity.setDockSn(dockSn); @@ -231,4 +297,13 @@ public class FlightTaskServiceImpl extends CrudServiceImpl getWrapper(Map params) { String id = (String) params.get("id"); + String taskId = (String) params.get("taskId"); QueryWrapper wrapper = new QueryWrapper<>(); wrapper.eq(StrUtil.isNotBlank(id), "id", id); + wrapper.eq(StrUtil.isNotBlank(taskId), "task_id", taskId); + wrapper.orderByDesc("created_time"); return wrapper; } diff --git a/admin/src/main/java/com/multictrl/modules/business/service/impl/SrsServiceImpl.java b/admin/src/main/java/com/multictrl/modules/business/service/impl/SrsServiceImpl.java index 0d48485..a988c26 100644 --- a/admin/src/main/java/com/multictrl/modules/business/service/impl/SrsServiceImpl.java +++ b/admin/src/main/java/com/multictrl/modules/business/service/impl/SrsServiceImpl.java @@ -36,14 +36,19 @@ public class SrsServiceImpl implements SrsService { @Override public void saveSrsRecord(SrsCallBackDTO srsCallBack) { try { - if (StrUtil.isNotBlank(srsCallBack.getFile()) && !srsCallBack.getFile().contains("/out") - && !srsCallBack.getFile().contains("/in")) { + String callBackFile = srsCallBack.getFile(); + if (StrUtil.isNotBlank(callBackFile) && + (callBackFile.contains("/dock") || callBackFile.contains("/uav"))) { SrsRecordEntity srsRecordEntity = new SrsRecordEntity(); srsRecordEntity.setApp(srsCallBack.getApp()); srsRecordEntity.setStream(srsCallBack.getStream()); - srsRecordEntity.setFile(srsCallBack.getFile()); + if (callBackFile.startsWith("/")) { + srsRecordEntity.setFile(callBackFile.substring(1)); + } else { + srsRecordEntity.setFile(callBackFile); + } srsRecordEntity.setStreamUrl(srsCallBack.getStream_url()); - String file = srsCallBack.getFile(); + String file = callBackFile; // /live_record/aaa/bbb/2025-04-03_16-33-43-404.mp4 String dateTime = (file = file.substring(file.lastIndexOf("/") + 1)) .substring(0, file.indexOf(".")); @@ -133,4 +138,9 @@ public class SrsServiceImpl implements SrsService { return RTMP_PROTOCOL + srsConfig.getIp() + ":" + srsConfig.getRtmpPort() + "/" + dockSn + "/uav?token=" + srsConfig.getRtmpToken(); } + + @Override + public SrsRecordDao getDao() { + return srsRecordDao; + } } diff --git a/admin/src/main/resources/mapper/business/FlightTaskDao.xml b/admin/src/main/resources/mapper/business/FlightTaskDao.xml new file mode 100644 index 0000000..41f32f2 --- /dev/null +++ b/admin/src/main/resources/mapper/business/FlightTaskDao.xml @@ -0,0 +1,22 @@ + + + + + + + + + \ No newline at end of file diff --git a/prj-deploy/deploy.sh b/prj-deploy/deploy.sh index 7065a20..6528e93 100644 --- a/prj-deploy/deploy.sh +++ b/prj-deploy/deploy.sh @@ -55,6 +55,16 @@ cp ./file/wait/wait-for-it.sh /root/dj_multictrl_data/wait/wait-for-it.sh mkdir -p /root/dj_multictrl_data/mongodb/data +mkdir -p /root/dj_multictrl_data/script +mkdir -p /root/dj_multictrl_data/script/logs +cp ./file/script/cleanup_srs_record_videos.sh /root/dj_multictrl_data/script/ +chmod +x /root/dj_multictrl_data/script/cleanup_srs_record_videos.sh +crontab -l > cron.cron +echo '*/30 * * * * /root/dj_multictrl_data/script/cleanup_srs_record_videos.sh' >> cron.cron +#echo '0 2 * * * /usr/bin/docker restart aros-api-srs' >> cron.cron +#echo '0 */1 * * * /usr/bin/pkill ffmpeg' >> cron.cron +crontab cron.cron + chmod -R 777 /root/dj_multictrl_data docker-compose --compatibility up \ No newline at end of file diff --git a/prj-deploy/file/script/cleanup_srs_record_videos.sh b/prj-deploy/file/script/cleanup_srs_record_videos.sh new file mode 100644 index 0000000..390368e --- /dev/null +++ b/prj-deploy/file/script/cleanup_srs_record_videos.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +# 配置项 +TARGET_DIR="/data/dj_multictrl_data/live_record" +MAX_SIZE_GB=5 +LOG_FILE="/root/dj_multictrl_data/script/logs/cleanup.log" + +# 转换为 KB (1GB = 1048576KB) +MAX_SIZE_KB=$((MAX_SIZE_GB * 1048576)) + +# 获取当前目录总大小(KB) +CURRENT_SIZE_KB=$(du -sk "$TARGET_DIR" | cut -f1) + +# 如果超过最大大小,开始清理 +if [ "$CURRENT_SIZE_KB" -gt "$MAX_SIZE_KB" ]; then + echo "$(date): 当前大小 ${CURRENT_SIZE_KB}KB 超过限制 ${MAX_SIZE_KB}KB,开始清理..." >> "$LOG_FILE" + + # 循环删除最旧的文件,直到空间达标 + while [ "$(du -sk "$TARGET_DIR" | cut -f1)" -gt "$MAX_SIZE_KB" ]; do + # 查找整个目录(包括子目录)中最旧的文件 + OLDEST_FILE=$(find "$TARGET_DIR" -type f -printf '%T+ %p\n' | sort | head -n 1 | cut -d' ' -f2-) + + if [ -n "$OLDEST_FILE" ]; then + echo "正在删除最旧的文件: $OLDEST_FILE" >> "$LOG_FILE" + rm -f "$OLDEST_FILE" + + # 检查是否删除成功 + if [ ! -f "$OLDEST_FILE" ]; then + echo "删除成功" >> "$LOG_FILE" + else + echo "删除失败,可能权限不足" >> "$LOG_FILE" + break # 避免无限循环 + fi + else + echo "没有可删除的文件(可能只剩下空目录)" >> "$LOG_FILE" + break + fi + done +else + echo "$(date): 当前大小 ${CURRENT_SIZE_KB}KB,未超过限制 ${MAX_SIZE_KB}KB" >> "$LOG_FILE" +fi \ No newline at end of file diff --git a/prj-deploy/file/script/data_cleanup.sh b/prj-deploy/file/script/data_cleanup.sh new file mode 100644 index 0000000..fdbc4af --- /dev/null +++ b/prj-deploy/file/script/data_cleanup.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# 要检查的目录 +TARGET_DIR="/data/dj_multictrl_data" + +# 最低剩余容量(单位 GiB) +THRESHOLD=50 + +# 当空间不足时,每次清理几个文件(按最旧) +DELETE_COUNT=5 + +# 当前剩余容量(GiB) +AVAILABLE=$(df -BG "$TARGET_DIR" | awk 'NR==2 {gsub("G","",$4); print $4}') + +# 日志文件 +LOG_FILE="/var/log/disk_cleanup.log" + +echo "$(date '+%F %T') 当前剩余空间: ${AVAILABLE}G" >> "$LOG_FILE" + +if [ "$AVAILABLE" -lt "$THRESHOLD" ]; then + echo "$(date '+%F %T') 空间低于 ${THRESHOLD}G,开始清理..." >> "$LOG_FILE" + + # 找出最旧的文件并删除 + find "$TARGET_DIR" -type f -printf "%T@ %p\n" \ + | sort -n \ + | head -n "$DELETE_COUNT" \ + | awk '{print $2}' \ + | while read FILE; do + echo "$(date '+%F %T') 删除文件: $FILE" >> "$LOG_FILE" + rm -f "$FILE" + done + + echo "$(date '+%F %T') 清理结束" >> "$LOG_FILE" +else + echo "$(date '+%F %T') 空间充足,无需清理" >> "$LOG_FILE" +fi