每天巡检一下磁盘存储是否超过75%,超过了就删除存储时间最久的视频,循环检查,超过了就删,保证磁盘存储容量不超过75%
This commit is contained in:
parent
ee4ac20435
commit
dc39ae8141
@ -0,0 +1,36 @@
|
|||||||
|
package com.zhgd.xmgl.modules.basicdata.controller;
|
||||||
|
|
||||||
|
import com.zhgd.jeecg.common.api.vo.Result;
|
||||||
|
import com.zhgd.xmgl.modules.basicdata.entity.bo.CleanupResult;
|
||||||
|
import com.zhgd.xmgl.modules.basicdata.entity.bo.DiskStatus;
|
||||||
|
import com.zhgd.xmgl.modules.basicdata.service.impl.DiskCleanupService;
|
||||||
|
import io.swagger.annotations.Api;
|
||||||
|
import io.swagger.annotations.ApiOperation;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/xmgl/disk")
|
||||||
|
@Api(tags = "磁盘管理")
|
||||||
|
@Slf4j
|
||||||
|
public class DiskController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DiskCleanupService diskCleanupService;
|
||||||
|
|
||||||
|
@GetMapping("/status")
|
||||||
|
@ApiOperation("获取磁盘状态")
|
||||||
|
public Result getDiskStatus() {
|
||||||
|
try {
|
||||||
|
Double status = diskCleanupService.checkDiskStatus();
|
||||||
|
return Result.success(status);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("获取磁盘状态失败", e);
|
||||||
|
return Result.error(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
package com.zhgd.xmgl.modules.basicdata.entity.bo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
// 清理结果类
|
||||||
|
@Data
|
||||||
|
public class CleanupResult {
|
||||||
|
private boolean success;
|
||||||
|
private String errorMessage;
|
||||||
|
private int deletedCount;
|
||||||
|
private double finalDiskUsage;
|
||||||
|
private List<String> deletedFiles = new ArrayList<>();
|
||||||
|
|
||||||
|
public void incrementDeletedCount() {
|
||||||
|
deletedCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addDeletedFile(String fileName) {
|
||||||
|
deletedFiles.add(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
package com.zhgd.xmgl.modules.basicdata.entity.bo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
// 磁盘状态信息类
|
||||||
|
@Data
|
||||||
|
public class DiskStatus {
|
||||||
|
private String diskPath;
|
||||||
|
private double usagePercentage;
|
||||||
|
private boolean exceedThreshold;
|
||||||
|
private long totalVideos;
|
||||||
|
private Date oldestVideoTime;
|
||||||
|
private String oldestVideoName;
|
||||||
|
}
|
||||||
@ -0,0 +1,214 @@
|
|||||||
|
package com.zhgd.xmgl.modules.basicdata.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.zhgd.xmgl.modules.basicdata.entity.bo.CleanupResult;
|
||||||
|
import com.zhgd.xmgl.modules.basicdata.entity.bo.DiskStatus;
|
||||||
|
import com.zhgd.xmgl.modules.policecamera.entity.PoliceCameraItemFile;
|
||||||
|
import com.zhgd.xmgl.modules.policecamera.service.IPoliceCameraItemFileService;
|
||||||
|
import com.zhgd.xmgl.util.PathUtil;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.FileStore;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Slf4j
|
||||||
|
public class DiskCleanupService {
|
||||||
|
// 文件类型:3 表示视频
|
||||||
|
private static final int FILE_TYPE_VIDEO = 3;
|
||||||
|
@Lazy
|
||||||
|
@Autowired
|
||||||
|
private IPoliceCameraItemFileService policeCameraItemFileService;
|
||||||
|
// 磁盘路径,可以从配置文件中读取
|
||||||
|
@Value("${diskMonitorPath:/datadir/itbgpImage}")
|
||||||
|
private String diskPath;
|
||||||
|
// 磁盘使用率阈值(百分比)
|
||||||
|
@Value("${diskUsageThreshold:75}")
|
||||||
|
private int diskUsageThreshold;
|
||||||
|
// 每次检查删除的文件数量限制
|
||||||
|
@Value("${cleanupBatchSize:10}")
|
||||||
|
private int batchSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取磁盘使用率
|
||||||
|
*
|
||||||
|
* @param diskPath
|
||||||
|
*/
|
||||||
|
public static double getDiskUsagePercentage(String diskPath) throws IOException {
|
||||||
|
Path path = Paths.get(diskPath);
|
||||||
|
FileStore store = Files.getFileStore(path);
|
||||||
|
|
||||||
|
long totalSpace = store.getTotalSpace();
|
||||||
|
long usableSpace = store.getUsableSpace();
|
||||||
|
long usedSpace = totalSpace - usableSpace;
|
||||||
|
|
||||||
|
return (double) usedSpace / totalSpace * 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理旧的视频文件
|
||||||
|
*/
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void cleanupOldVideos() {
|
||||||
|
int deletedCount = 0;
|
||||||
|
double currentUsage;
|
||||||
|
|
||||||
|
try {
|
||||||
|
currentUsage = getDiskUsagePercentage(diskPath);
|
||||||
|
|
||||||
|
// 循环清理,直到磁盘使用率降到阈值以下
|
||||||
|
while (currentUsage > diskUsageThreshold) {
|
||||||
|
// 查询最旧的视频文件(按上传时间排序)
|
||||||
|
List<PoliceCameraItemFile> oldVideos = policeCameraItemFileService.list(
|
||||||
|
new LambdaQueryWrapper<PoliceCameraItemFile>()
|
||||||
|
.eq(PoliceCameraItemFile::getFileType, FILE_TYPE_VIDEO)
|
||||||
|
.eq(PoliceCameraItemFile::getIsFileDeleted, 0) // 只查询未删除的
|
||||||
|
.isNotNull(PoliceCameraItemFile::getUploadTime)
|
||||||
|
.isNotNull(PoliceCameraItemFile::getFileUrl)
|
||||||
|
.orderByAsc(PoliceCameraItemFile::getUploadTime) // 按上传时间升序,最旧的在前
|
||||||
|
.last("LIMIT " + batchSize) // 限制每次处理的记录数
|
||||||
|
);
|
||||||
|
|
||||||
|
if (oldVideos.isEmpty()) {
|
||||||
|
log.warn("没有找到可清理的视频文件");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除这批视频文件
|
||||||
|
for (PoliceCameraItemFile video : oldVideos) {
|
||||||
|
deleteVideoFile(video);
|
||||||
|
// 逻辑删除数据库记录
|
||||||
|
video.setIsFileDeleted(1);
|
||||||
|
video.setUpdateDate(new Date());
|
||||||
|
policeCameraItemFileService.updateById(video);
|
||||||
|
|
||||||
|
deletedCount++;
|
||||||
|
log.info("已删除视频文件: {}, 上传时间: {}",
|
||||||
|
video.getFileName(),
|
||||||
|
formatDate(video.getUploadTime()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重新计算磁盘使用率
|
||||||
|
currentUsage = getDiskUsagePercentage(diskPath);
|
||||||
|
log.info("清理后磁盘使用率: {}%, 已删除 {} 个文件",
|
||||||
|
String.format("%.2f", currentUsage), deletedCount);
|
||||||
|
|
||||||
|
// 防止无限循环,添加安全限制
|
||||||
|
if (deletedCount >= 1000) {
|
||||||
|
log.warn("已达到最大删除数量限制(1000),停止清理");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("视频清理完成,总共删除 {} 个文件,最终磁盘使用率: {}%",
|
||||||
|
deletedCount, String.format("%.2f", currentUsage));
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("清理视频文件时发生错误", e);
|
||||||
|
throw new RuntimeException("清理视频文件失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatDate(Date date) {
|
||||||
|
if (date == null) return "未知时间";
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||||
|
return sdf.format(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dailyDiskInspection() {
|
||||||
|
log.info("开始执行磁盘巡检任务...");
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 检查磁盘使用率
|
||||||
|
double usagePercentage = getDiskUsagePercentage(diskPath);
|
||||||
|
log.info("当前磁盘使用率: {}%", String.format("%.2f", usagePercentage));
|
||||||
|
|
||||||
|
// 如果使用率超过阈值,执行清理
|
||||||
|
if (usagePercentage > diskUsageThreshold) {
|
||||||
|
log.warn("磁盘使用率超过阈值({}%),开始清理旧视频文件...", diskUsageThreshold);
|
||||||
|
cleanupOldVideos();
|
||||||
|
} else {
|
||||||
|
log.info("磁盘使用率正常,无需清理");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("磁盘巡检任务执行失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除物理视频文件
|
||||||
|
*/
|
||||||
|
public boolean deleteVideoFile(PoliceCameraItemFile video) {
|
||||||
|
String fileUrl = video.getFileUrl();
|
||||||
|
fileUrl = PathUtil.getBasePath() + "/" + fileUrl;
|
||||||
|
if (StrUtil.isBlank(fileUrl)) {
|
||||||
|
log.warn("视频文件URL为空,文件ID: {}", video.getId());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 根据fileUrl构建文件路径
|
||||||
|
File file = new File(fileUrl);
|
||||||
|
|
||||||
|
if (!file.exists()) {
|
||||||
|
log.warn("视频文件不存在: {}", fileUrl);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 记录文件信息
|
||||||
|
long fileSize = file.length();
|
||||||
|
|
||||||
|
// 删除文件
|
||||||
|
if (file.delete()) {
|
||||||
|
log.info("成功删除物理文件: {}, 大小: {} KB",
|
||||||
|
fileUrl, fileSize / 1024);
|
||||||
|
|
||||||
|
// 如果有视频截图,也一并删除
|
||||||
|
String videoScreenshot = video.getVideoScreenshot();
|
||||||
|
videoScreenshot = PathUtil.getBasePath() + "/" + videoScreenshot;
|
||||||
|
if (StrUtil.isNotBlank(videoScreenshot)) {
|
||||||
|
File screenshot = new File(videoScreenshot);
|
||||||
|
if (screenshot.exists()) {
|
||||||
|
screenshot.delete();
|
||||||
|
log.info("删除视频截图: {}", videoScreenshot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
log.error("删除物理文件失败: {}", fileUrl);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (SecurityException e) {
|
||||||
|
log.error("没有权限删除文件: {}", fileUrl, e);
|
||||||
|
return false;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("删除文件时发生异常: {}", fileUrl, e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 立即执行磁盘检查(可用于手动触发)
|
||||||
|
*/
|
||||||
|
public Double checkDiskStatus() throws IOException {
|
||||||
|
double usagePercentage = getDiskUsagePercentage(diskPath);
|
||||||
|
return usagePercentage;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -134,4 +134,9 @@ public class PoliceCameraItemFile implements Serializable {
|
|||||||
*/
|
*/
|
||||||
@ApiModelProperty(value = "视频截图url")
|
@ApiModelProperty(value = "视频截图url")
|
||||||
private java.lang.String videoScreenshot;
|
private java.lang.String videoScreenshot;
|
||||||
|
/**
|
||||||
|
* 文件已删除,1删除0未删除
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "文件已删除,1删除0未删除")
|
||||||
|
private java.lang.Integer isFileDeleted;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -113,7 +113,7 @@
|
|||||||
,ifnull(round(sum(pcif.duration),2),0) duration
|
,ifnull(round(sum(pcif.duration),2),0) duration
|
||||||
from police_camera_item pci
|
from police_camera_item pci
|
||||||
join police_camera_item_file pcif on pci.item_id = pcif.item_id
|
join police_camera_item_file pcif on pci.item_id = pcif.item_id
|
||||||
where 1=1
|
where 1=1 and pcif.is_deleted = 0
|
||||||
<if test="projectSn != null and projectSn != ''">
|
<if test="projectSn != null and projectSn != ''">
|
||||||
and pci.project_sn = #{projectSn}
|
and pci.project_sn = #{projectSn}
|
||||||
</if>
|
</if>
|
||||||
|
|||||||
@ -131,6 +131,7 @@ public class PoliceCameraItemFileServiceImpl extends ServiceImpl<PoliceCameraIte
|
|||||||
throw new OpenAlertException("未找到对应实体");
|
throw new OpenAlertException("未找到对应实体");
|
||||||
}
|
}
|
||||||
baseMapper.deleteById(id);
|
baseMapper.deleteById(id);
|
||||||
|
FileUtil.del(PathUtil.getBasePath() + "/" + policeCameraItemFile.getFileUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
86
src/main/java/com/zhgd/xmgl/task/PoliceCameraTask.java
Normal file
86
src/main/java/com/zhgd/xmgl/task/PoliceCameraTask.java
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
package com.zhgd.xmgl.task;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.date.DateTime;
|
||||||
|
import cn.hutool.core.date.DateUtil;
|
||||||
|
import cn.hutool.core.util.NumberUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.alibaba.fastjson.JSONArray;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.zhgd.redis.lock.RedisRepository;
|
||||||
|
import com.zhgd.xmgl.base.HikvisionEventsPictureRq;
|
||||||
|
import com.zhgd.xmgl.call.HikvisionCall;
|
||||||
|
import com.zhgd.xmgl.call.api.CarManufacturer;
|
||||||
|
import com.zhgd.xmgl.call.factory.CarManufacturerFactory;
|
||||||
|
import com.zhgd.xmgl.modules.basicdata.entity.bo.CleanupResult;
|
||||||
|
import com.zhgd.xmgl.modules.basicdata.entity.bo.DiskStatus;
|
||||||
|
import com.zhgd.xmgl.modules.basicdata.service.impl.DiskCleanupService;
|
||||||
|
import com.zhgd.xmgl.modules.car.entity.CarInfo;
|
||||||
|
import com.zhgd.xmgl.modules.car.entity.CarInfoApprovalFlow;
|
||||||
|
import com.zhgd.xmgl.modules.car.entity.CarMeasureSpeedData;
|
||||||
|
import com.zhgd.xmgl.modules.car.entity.CarMeasureSpeedDev;
|
||||||
|
import com.zhgd.xmgl.modules.car.enums.CarInfoCarModuleTypeEnum;
|
||||||
|
import com.zhgd.xmgl.modules.car.mapper.CarInfoApprovalFlowMapper;
|
||||||
|
import com.zhgd.xmgl.modules.car.mapper.CarInfoMapper;
|
||||||
|
import com.zhgd.xmgl.modules.car.service.ICarMeasureSpeedDataService;
|
||||||
|
import com.zhgd.xmgl.modules.car.service.ICarMeasureSpeedDevService;
|
||||||
|
import com.zhgd.xmgl.modules.car.service.impl.CarInfoServiceImpl;
|
||||||
|
import com.zhgd.xmgl.modules.policecamera.entity.PoliceCameraItemFile;
|
||||||
|
import com.zhgd.xmgl.modules.policecamera.service.IPoliceCameraItemFileService;
|
||||||
|
import com.zhgd.xmgl.modules.project.entity.Project;
|
||||||
|
import com.zhgd.xmgl.modules.project.mapper.ProjectMapper;
|
||||||
|
import com.zhgd.xmgl.util.AsyncTaskUtil;
|
||||||
|
import com.zhgd.xmgl.util.HikvisionUtil;
|
||||||
|
import com.zhgd.xmgl.util.PathUtil;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import net.javacrumbs.shedlock.core.SchedulerLock;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.nio.file.FileStore;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@RequestMapping("/xmgl/task")
|
||||||
|
@RestController
|
||||||
|
public class PoliceCameraTask {
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Autowired
|
||||||
|
HikvisionCall hikvisionCall;
|
||||||
|
@Lazy
|
||||||
|
@Autowired
|
||||||
|
private DiskCleanupService diskCleanupService;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每天巡检一下磁盘存储是否超过75%,超过了就删除存储时间最久的视频,循环检查,超过了就删,保证磁盘存储容量不超过75%
|
||||||
|
*/
|
||||||
|
@Scheduled(cron = "0 0 3 * * ?")
|
||||||
|
@SchedulerLock(name = "monitorDiskSpace", lockAtMostFor = 1000 * 60, lockAtLeastFor = 1000 * 60)
|
||||||
|
@RequestMapping("monitorDiskSpace")
|
||||||
|
public void monitorDiskSpace() {
|
||||||
|
diskCleanupService.dailyDiskInspection();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user