Merge remote-tracking branch 'origin/dunhuan_jdk8' into dunhuang

This commit is contained in:
guoshengxiong 2025-12-20 16:39:12 +08:00
commit ff147038df
15 changed files with 436 additions and 9 deletions

View File

@ -1,5 +1,6 @@
package com.zhgd.xmgl.async;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@ -17,11 +18,14 @@ import com.zhgd.xmgl.modules.basicdata.entity.SystemUser;
import com.zhgd.xmgl.modules.basicdata.enums.SystemUserAccountTypeEnum;
import com.zhgd.xmgl.modules.basicdata.mapper.SystemUserMapper;
import com.zhgd.xmgl.modules.basicdata.service.ICompanyConfigService;
import com.zhgd.xmgl.modules.basicdata.service.ICompanyService;
import com.zhgd.xmgl.modules.basicdata.service.ISystemUserService;
import com.zhgd.xmgl.modules.basicdata.service.impl.DictionaryItemServiceImpl;
import com.zhgd.xmgl.modules.basicdata.service.impl.NoticeServiceImpl;
import com.zhgd.xmgl.modules.project.entity.EnableMessageDevRule;
import com.zhgd.xmgl.modules.project.entity.Project;
import com.zhgd.xmgl.modules.project.service.IEnableMessageDevRuleService;
import com.zhgd.xmgl.modules.project.service.IProjectService;
import com.zhgd.xmgl.modules.video.entity.AiAnalyseHardWareAlarmRecord;
import com.zhgd.xmgl.modules.worker.service.impl.WorkerInfoServiceImpl;
import com.zhgd.xmgl.modules.xz.entity.XzAiDeductRule;
@ -91,6 +95,9 @@ public class AsyncAiAnalyse {
@Lazy
@Autowired
private IXzAiDeductRuleService xzAiDeductRuleService;
@Lazy
@Autowired
IProjectService projectService;
/**
* @param record
@ -116,6 +123,12 @@ public class AsyncAiAnalyse {
}
// sendAppNotice(record.getProjectSn(), title, msg, systemUserList, "/pages/potentialRisk/potentialRisk?id=" + record.getId());
}
List<Long> companyUserIds = systemUserService.getCompanyUserIdsByProjectSn(record.getProjectSn());
Project project = projectService.getOne(new LambdaQueryWrapper<Project>()
.eq(Project::getProjectSn, record.getProjectSn()));
if (CollUtil.isNotEmpty(companyUserIds)) {
noticeService.addUsersNotice(companyUserIds, title, project.getProjectName() + "," + msg, "8");
}
sendMessage(typeName, record.getLocation(), record.getProjectSn(), record.getHardwareId(), record.getAlarmType());
} catch (Exception e) {
log.error("error", e);

View File

@ -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());
}
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,20 @@
package com.zhgd.xmgl.modules.basicdata.entity.bo;
import lombok.Data;
@Data
public class CompanySnsByProjectSn {
private String projectName;
private String projectSn;
private String companySn4;
private String companySn3;
private String companySn2;
private String companySn1;
}

View File

@ -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;
}

View File

@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wflow.bean.vo.OrgTreeVo;
import com.zhgd.jeecg.common.mybatis.EntityMap;
import com.zhgd.xmgl.modules.basicdata.entity.Company;
import com.zhgd.xmgl.modules.basicdata.entity.bo.CompanySnsByProjectSn;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
@ -125,7 +126,7 @@ public interface CompanyMapper extends BaseMapper<Company> {
* @param projectSn
* @return
*/
EntityMap getCompanyInfosByProjectSn(String projectSn);
CompanySnsByProjectSn getCompanyInfosByProjectSn(String projectSn);
/**
* 根据用户id查询企业

View File

@ -232,12 +232,13 @@
</where>
</select>
<select id="getCompanyInfosByProjectSn" resultType="com.zhgd.jeecg.common.mybatis.EntityMap">
<select id="getCompanyInfosByProjectSn" resultType="com.zhgd.xmgl.modules.basicdata.entity.bo.CompanySnsByProjectSn">
SELECT a.project_name project_name,
a.project_sn project_sn,
cp.company_sn company_sn4,
b.company_sn company_sn3,
f.company_sn company_sn2
f.company_sn company_sn2,
f.headquarters_sn company_sn1
FROM project a
INNER JOIN company cp ON a.company_sn = cp.company_sn
INNER JOIN company b ON cp.parent_id = b.company_id

View File

@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.zhgd.jeecg.common.mybatis.EntityMap;
import com.zhgd.xmgl.modules.basicdata.entity.Company;
import com.zhgd.xmgl.modules.basicdata.entity.bo.CompanySnsByProjectSn;
import com.zhgd.xmgl.modules.basicdata.entity.vo.GetOrgSnListVo;
import java.util.HashMap;
@ -215,4 +216,6 @@ public interface ICompanyService extends IService<Company> {
boolean hasProjectSnAccessBy4(Long userId, String sn);
List<String> getProjectSnsBySn(String sn, Integer accountType);
CompanySnsByProjectSn getCompanyInfosByProjectSn(String projectSn);
}

View File

@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
import com.zhgd.jeecg.common.api.vo.Result;
import com.zhgd.xmgl.entity.sj.JwtPayloadUserInfo;
import com.zhgd.xmgl.modules.basicdata.entity.SystemUser;
import com.zhgd.xmgl.modules.basicdata.entity.bo.CompanySnsByProjectSn;
import com.zhgd.xmgl.modules.basicdata.entity.dto.LoginInfoByTokenDto;
import java.util.HashMap;

View File

@ -27,6 +27,7 @@ import com.zhgd.xmgl.modules.basicdata.entity.Company;
import com.zhgd.xmgl.modules.basicdata.entity.CompanyConfig;
import com.zhgd.xmgl.modules.basicdata.entity.SystemUser;
import com.zhgd.xmgl.modules.basicdata.entity.XzSystemUserToCompanyProject;
import com.zhgd.xmgl.modules.basicdata.entity.bo.CompanySnsByProjectSn;
import com.zhgd.xmgl.modules.basicdata.entity.vo.GetOrgSnListVo;
import com.zhgd.xmgl.modules.basicdata.enums.CompanyTypeEnum;
import com.zhgd.xmgl.modules.basicdata.enums.SystemUserAccountTypeEnum;
@ -978,15 +979,15 @@ public class CompanyServiceImpl extends ServiceImpl<CompanyMapper, Company> impl
if (CollUtil.isEmpty(clJa)) {
return null;
}
EntityMap snMap = companyMapper.getCompanyInfosByProjectSn(project.getProjectSn());
CompanySnsByProjectSn snMap = companyMapper.getCompanyInfosByProjectSn(project.getProjectSn());
if (snMap == null) {
return null;
}
Map<String, Object> existSnMap = new HashMap<>(16);
existSnMap.put(snMap.get("companySn2"), 0);
existSnMap.put(snMap.get("companySn3"), 0);
existSnMap.put(snMap.get("companySn4"), 0);
existSnMap.put(snMap.get("projectSn"), 0);
existSnMap.put(snMap.getCompanySn2(), 0);
existSnMap.put(snMap.getCompanySn3(), 0);
existSnMap.put(snMap.getCompanySn4(), 0);
existSnMap.put(snMap.getProjectSn(), 0);
for (int i = 0; i < clJa.size(); i++) {
JSONObject clJo = clJa.getJSONObject(i);
recursionFilterData(existSnMap, clJo, rtJa);
@ -1083,6 +1084,11 @@ public class CompanyServiceImpl extends ServiceImpl<CompanyMapper, Company> impl
return baseMapper.getProjectSnsBySn(sn, accountType);
}
@Override
public CompanySnsByProjectSn getCompanyInfosByProjectSn(String projectSn) {
return baseMapper.getCompanyInfosByProjectSn(projectSn);
}
private void recursionFilterData(Map<String, Object> existSnMap, JSONObject clJo, JSONArray rtJa) {
String sn = getSn(clJo);
if (!existSnMap.containsKey(sn)) {

View File

@ -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;
}
}

View File

@ -134,4 +134,9 @@ public class PoliceCameraItemFile implements Serializable {
*/
@ApiModelProperty(value = "视频截图url")
private java.lang.String videoScreenshot;
/**
* 文件已删除,1删除0未删除
*/
@ApiModelProperty(value = "文件已删除,1删除0未删除")
private java.lang.Integer isFileDeleted;
}

View File

@ -113,7 +113,7 @@
,ifnull(round(sum(pcif.duration),2),0) duration
from police_camera_item pci
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 != ''">
and pci.project_sn = #{projectSn}
</if>

View File

@ -131,6 +131,7 @@ public class PoliceCameraItemFileServiceImpl extends ServiceImpl<PoliceCameraIte
throw new OpenAlertException("未找到对应实体");
}
baseMapper.deleteById(id);
FileUtil.del(PathUtil.getBasePath() + "/" + policeCameraItemFile.getFileUrl());
}
@Override

View 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();
}
}